зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central on a CLOSED TREE
This commit is contained in:
Коммит
894a28d90b
|
@ -658,12 +658,15 @@ AccessibleWrap::get_accFocus(
|
||||||
if (IsDefunct())
|
if (IsDefunct())
|
||||||
return CO_E_OBJNOTCONNECTED;
|
return CO_E_OBJNOTCONNECTED;
|
||||||
|
|
||||||
// TODO make this work with proxies.
|
|
||||||
if (IsProxy())
|
|
||||||
return E_NOTIMPL;
|
|
||||||
|
|
||||||
// Return the current IAccessible child that has focus
|
// Return the current IAccessible child that has focus
|
||||||
Accessible* focusedAccessible = FocusedChild();
|
Accessible* focusedAccessible;
|
||||||
|
if (IsProxy()) {
|
||||||
|
ProxyAccessible* proxy = Proxy()->FocusedChild();
|
||||||
|
focusedAccessible = proxy ? WrapperFor(proxy) : nullptr;
|
||||||
|
} else {
|
||||||
|
focusedAccessible = FocusedChild();
|
||||||
|
}
|
||||||
|
|
||||||
if (focusedAccessible == this) {
|
if (focusedAccessible == this) {
|
||||||
pvarChild->vt = VT_I4;
|
pvarChild->vt = VT_I4;
|
||||||
pvarChild->lVal = CHILDID_SELF;
|
pvarChild->lVal = CHILDID_SELF;
|
||||||
|
@ -830,7 +833,17 @@ AccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren)
|
||||||
|
|
||||||
if (IsSelect()) {
|
if (IsSelect()) {
|
||||||
nsAutoTArray<Accessible*, 10> selectedItems;
|
nsAutoTArray<Accessible*, 10> selectedItems;
|
||||||
SelectedItems(&selectedItems);
|
if (IsProxy()) {
|
||||||
|
nsTArray<ProxyAccessible*> proxies;
|
||||||
|
Proxy()->SelectedItems(&proxies);
|
||||||
|
|
||||||
|
uint32_t selectedCount = proxies.Length();
|
||||||
|
for (uint32_t i = 0; i < selectedCount; i++) {
|
||||||
|
selectedItems.AppendElement(WrapperFor(proxies[i]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SelectedItems(&selectedItems);
|
||||||
|
}
|
||||||
|
|
||||||
// 1) Create and initialize the enumeration
|
// 1) Create and initialize the enumeration
|
||||||
RefPtr<AccessibleEnumerator> pEnum = new AccessibleEnumerator(selectedItems);
|
RefPtr<AccessibleEnumerator> pEnum = new AccessibleEnumerator(selectedItems);
|
||||||
|
@ -864,12 +877,13 @@ AccessibleWrap::get_accDefaultAction(
|
||||||
if (xpAccessible->IsDefunct())
|
if (xpAccessible->IsDefunct())
|
||||||
return CO_E_OBJNOTCONNECTED;
|
return CO_E_OBJNOTCONNECTED;
|
||||||
|
|
||||||
// TODO make this work with proxies.
|
|
||||||
if (xpAccessible->IsProxy())
|
|
||||||
return E_NOTIMPL;
|
|
||||||
|
|
||||||
nsAutoString defaultAction;
|
nsAutoString defaultAction;
|
||||||
xpAccessible->ActionNameAt(0, defaultAction);
|
if (xpAccessible->IsProxy()) {
|
||||||
|
xpAccessible->Proxy()->ActionNameAt(0, defaultAction);
|
||||||
|
} else {
|
||||||
|
xpAccessible->ActionNameAt(0, defaultAction);
|
||||||
|
}
|
||||||
|
|
||||||
*pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
|
*pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
|
||||||
defaultAction.Length());
|
defaultAction.Length());
|
||||||
return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
|
return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
|
||||||
|
@ -895,23 +909,42 @@ AccessibleWrap::accSelect(
|
||||||
if (xpAccessible->IsDefunct())
|
if (xpAccessible->IsDefunct())
|
||||||
return CO_E_OBJNOTCONNECTED;
|
return CO_E_OBJNOTCONNECTED;
|
||||||
|
|
||||||
// TODO make this work with proxies.
|
if (flagsSelect & SELFLAG_TAKEFOCUS) {
|
||||||
if (xpAccessible->IsProxy())
|
if (xpAccessible->IsProxy()) {
|
||||||
return E_NOTIMPL;
|
xpAccessible->Proxy()->TakeFocus();
|
||||||
|
} else {
|
||||||
if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION))
|
|
||||||
{
|
|
||||||
if (flagsSelect & SELFLAG_TAKEFOCUS)
|
|
||||||
xpAccessible->TakeFocus();
|
xpAccessible->TakeFocus();
|
||||||
|
}
|
||||||
|
|
||||||
if (flagsSelect & SELFLAG_TAKESELECTION)
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flagsSelect & SELFLAG_TAKESELECTION) {
|
||||||
|
if (xpAccessible->IsProxy()) {
|
||||||
|
xpAccessible->Proxy()->TakeSelection();
|
||||||
|
} else {
|
||||||
xpAccessible->TakeSelection();
|
xpAccessible->TakeSelection();
|
||||||
|
}
|
||||||
|
|
||||||
if (flagsSelect & SELFLAG_ADDSELECTION)
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flagsSelect & SELFLAG_ADDSELECTION) {
|
||||||
|
if (xpAccessible->IsProxy()) {
|
||||||
|
xpAccessible->Proxy()->SetSelected(true);
|
||||||
|
} else {
|
||||||
xpAccessible->SetSelected(true);
|
xpAccessible->SetSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (flagsSelect & SELFLAG_REMOVESELECTION)
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flagsSelect & SELFLAG_REMOVESELECTION) {
|
||||||
|
if (xpAccessible->IsProxy()) {
|
||||||
|
xpAccessible->Proxy()->SetSelected(false);
|
||||||
|
} else {
|
||||||
xpAccessible->SetSelected(false);
|
xpAccessible->SetSelected(false);
|
||||||
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -1063,11 +1096,15 @@ AccessibleWrap::accHitTest(
|
||||||
if (IsDefunct())
|
if (IsDefunct())
|
||||||
return CO_E_OBJNOTCONNECTED;
|
return CO_E_OBJNOTCONNECTED;
|
||||||
|
|
||||||
// TODO make this work with proxies.
|
Accessible* accessible = nullptr;
|
||||||
if (IsProxy())
|
if (IsProxy()) {
|
||||||
return E_NOTIMPL;
|
ProxyAccessible* proxy = Proxy()->ChildAtPoint(xLeft, yTop, eDirectChild);
|
||||||
|
if (proxy) {
|
||||||
Accessible* accessible = ChildAtPoint(xLeft, yTop, eDirectChild);
|
accessible = WrapperFor(proxy);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
accessible = ChildAtPoint(xLeft, yTop, eDirectChild);
|
||||||
|
}
|
||||||
|
|
||||||
// if we got a child
|
// if we got a child
|
||||||
if (accessible) {
|
if (accessible) {
|
||||||
|
@ -1107,7 +1144,7 @@ AccessibleWrap::accDoDefaultAction(
|
||||||
|
|
||||||
// TODO make this work with proxies.
|
// TODO make this work with proxies.
|
||||||
if (xpAccessible->IsProxy())
|
if (xpAccessible->IsProxy())
|
||||||
return E_NOTIMPL;
|
return xpAccessible->Proxy()->DoAction(0) ? S_OK : E_INVALIDARG;
|
||||||
|
|
||||||
return xpAccessible->DoAction(0) ? S_OK : E_INVALIDARG;
|
return xpAccessible->DoAction(0) ? S_OK : E_INVALIDARG;
|
||||||
|
|
||||||
|
|
|
@ -1446,11 +1446,19 @@ pref("plain_text.wrap_long_lines", true);
|
||||||
pref("dom.debug.propagate_gesture_events_through_content", false);
|
pref("dom.debug.propagate_gesture_events_through_content", false);
|
||||||
|
|
||||||
// The request URL of the GeoLocation backend.
|
// The request URL of the GeoLocation backend.
|
||||||
|
#ifdef RELEASE_BUILD
|
||||||
|
pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%GOOGLE_API_KEY%");
|
||||||
|
#else
|
||||||
pref("geo.wifi.uri", "https://location.services.mozilla.com/v1/geolocate?key=%MOZILLA_API_KEY%");
|
pref("geo.wifi.uri", "https://location.services.mozilla.com/v1/geolocate?key=%MOZILLA_API_KEY%");
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
|
#ifdef RELEASE_BUILD
|
||||||
|
pref("geo.provider.use_corelocation", false);
|
||||||
|
#else
|
||||||
pref("geo.provider.use_corelocation", true);
|
pref("geo.provider.use_corelocation", true);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
pref("geo.provider.ms-windows-location", false);
|
pref("geo.provider.ms-windows-location", false);
|
||||||
|
|
|
@ -2062,492 +2062,6 @@ FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const
|
||||||
return mAttrsAndChildren.IndexOfChild(aPossibleChild);
|
return mAttrsAndChildren.IndexOfChild(aPossibleChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to keep the size of StringBuilder close to a jemalloc bucket size.
|
|
||||||
#define STRING_BUFFER_UNITS 1020
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// We put StringBuilder in the anonymous namespace to prevent anything outside
|
|
||||||
// this file from accidentally being linked against it.
|
|
||||||
|
|
||||||
class StringBuilder
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
class Unit
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
|
|
||||||
{
|
|
||||||
MOZ_COUNT_CTOR(StringBuilder::Unit);
|
|
||||||
}
|
|
||||||
~Unit()
|
|
||||||
{
|
|
||||||
if (mType == eString || mType == eStringWithEncode) {
|
|
||||||
delete mString;
|
|
||||||
}
|
|
||||||
MOZ_COUNT_DTOR(StringBuilder::Unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Type
|
|
||||||
{
|
|
||||||
eUnknown,
|
|
||||||
eAtom,
|
|
||||||
eString,
|
|
||||||
eStringWithEncode,
|
|
||||||
eLiteral,
|
|
||||||
eTextFragment,
|
|
||||||
eTextFragmentWithEncode,
|
|
||||||
};
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
nsIAtom* mAtom;
|
|
||||||
const char* mLiteral;
|
|
||||||
nsAutoString* mString;
|
|
||||||
const nsTextFragment* mTextFragment;
|
|
||||||
};
|
|
||||||
Type mType;
|
|
||||||
uint32_t mLength;
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
StringBuilder() : mLast(this), mLength(0)
|
|
||||||
{
|
|
||||||
MOZ_COUNT_CTOR(StringBuilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
~StringBuilder()
|
|
||||||
{
|
|
||||||
MOZ_COUNT_DTOR(StringBuilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Append(nsIAtom* aAtom)
|
|
||||||
{
|
|
||||||
Unit* u = AddUnit();
|
|
||||||
u->mAtom = aAtom;
|
|
||||||
u->mType = Unit::eAtom;
|
|
||||||
uint32_t len = aAtom->GetLength();
|
|
||||||
u->mLength = len;
|
|
||||||
mLength += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int N>
|
|
||||||
void Append(const char (&aLiteral)[N])
|
|
||||||
{
|
|
||||||
Unit* u = AddUnit();
|
|
||||||
u->mLiteral = aLiteral;
|
|
||||||
u->mType = Unit::eLiteral;
|
|
||||||
uint32_t len = N - 1;
|
|
||||||
u->mLength = len;
|
|
||||||
mLength += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int N>
|
|
||||||
void Append(char (&aLiteral)[N])
|
|
||||||
{
|
|
||||||
Unit* u = AddUnit();
|
|
||||||
u->mLiteral = aLiteral;
|
|
||||||
u->mType = Unit::eLiteral;
|
|
||||||
uint32_t len = N - 1;
|
|
||||||
u->mLength = len;
|
|
||||||
mLength += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Append(const nsAString& aString)
|
|
||||||
{
|
|
||||||
Unit* u = AddUnit();
|
|
||||||
u->mString = new nsAutoString(aString);
|
|
||||||
u->mType = Unit::eString;
|
|
||||||
uint32_t len = aString.Length();
|
|
||||||
u->mLength = len;
|
|
||||||
mLength += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Append(nsAutoString* aString)
|
|
||||||
{
|
|
||||||
Unit* u = AddUnit();
|
|
||||||
u->mString = aString;
|
|
||||||
u->mType = Unit::eString;
|
|
||||||
uint32_t len = aString->Length();
|
|
||||||
u->mLength = len;
|
|
||||||
mLength += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
|
|
||||||
{
|
|
||||||
Unit* u = AddUnit();
|
|
||||||
u->mString = aString;
|
|
||||||
u->mType = Unit::eStringWithEncode;
|
|
||||||
u->mLength = aLen;
|
|
||||||
mLength += aLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Append(const nsTextFragment* aTextFragment)
|
|
||||||
{
|
|
||||||
Unit* u = AddUnit();
|
|
||||||
u->mTextFragment = aTextFragment;
|
|
||||||
u->mType = Unit::eTextFragment;
|
|
||||||
uint32_t len = aTextFragment->GetLength();
|
|
||||||
u->mLength = len;
|
|
||||||
mLength += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
|
|
||||||
{
|
|
||||||
Unit* u = AddUnit();
|
|
||||||
u->mTextFragment = aTextFragment;
|
|
||||||
u->mType = Unit::eTextFragmentWithEncode;
|
|
||||||
u->mLength = aLen;
|
|
||||||
mLength += aLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ToString(nsAString& aOut)
|
|
||||||
{
|
|
||||||
if (!aOut.SetCapacity(mLength, fallible)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (StringBuilder* current = this; current; current = current->mNext) {
|
|
||||||
uint32_t len = current->mUnits.Length();
|
|
||||||
for (uint32_t i = 0; i < len; ++i) {
|
|
||||||
Unit& u = current->mUnits[i];
|
|
||||||
switch (u.mType) {
|
|
||||||
case Unit::eAtom:
|
|
||||||
aOut.Append(nsDependentAtomString(u.mAtom));
|
|
||||||
break;
|
|
||||||
case Unit::eString:
|
|
||||||
aOut.Append(*(u.mString));
|
|
||||||
break;
|
|
||||||
case Unit::eStringWithEncode:
|
|
||||||
EncodeAttrString(*(u.mString), aOut);
|
|
||||||
break;
|
|
||||||
case Unit::eLiteral:
|
|
||||||
aOut.AppendASCII(u.mLiteral, u.mLength);
|
|
||||||
break;
|
|
||||||
case Unit::eTextFragment:
|
|
||||||
u.mTextFragment->AppendTo(aOut);
|
|
||||||
break;
|
|
||||||
case Unit::eTextFragmentWithEncode:
|
|
||||||
EncodeTextFragment(u.mTextFragment, aOut);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
MOZ_CRASH("Unknown unit type?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
Unit* AddUnit()
|
|
||||||
{
|
|
||||||
if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
|
|
||||||
new StringBuilder(this);
|
|
||||||
}
|
|
||||||
return mLast->mUnits.AppendElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit StringBuilder(StringBuilder* aFirst)
|
|
||||||
: mLast(nullptr), mLength(0)
|
|
||||||
{
|
|
||||||
MOZ_COUNT_CTOR(StringBuilder);
|
|
||||||
aFirst->mLast->mNext = this;
|
|
||||||
aFirst->mLast = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
|
|
||||||
{
|
|
||||||
const char16_t* c = aValue.BeginReading();
|
|
||||||
const char16_t* end = aValue.EndReading();
|
|
||||||
while (c < end) {
|
|
||||||
switch (*c) {
|
|
||||||
case '"':
|
|
||||||
aOut.AppendLiteral(""");
|
|
||||||
break;
|
|
||||||
case '&':
|
|
||||||
aOut.AppendLiteral("&");
|
|
||||||
break;
|
|
||||||
case 0x00A0:
|
|
||||||
aOut.AppendLiteral(" ");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
aOut.Append(*c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
|
|
||||||
{
|
|
||||||
uint32_t len = aValue->GetLength();
|
|
||||||
if (aValue->Is2b()) {
|
|
||||||
const char16_t* data = aValue->Get2b();
|
|
||||||
for (uint32_t i = 0; i < len; ++i) {
|
|
||||||
const char16_t c = data[i];
|
|
||||||
switch (c) {
|
|
||||||
case '<':
|
|
||||||
aOut.AppendLiteral("<");
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
aOut.AppendLiteral(">");
|
|
||||||
break;
|
|
||||||
case '&':
|
|
||||||
aOut.AppendLiteral("&");
|
|
||||||
break;
|
|
||||||
case 0x00A0:
|
|
||||||
aOut.AppendLiteral(" ");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
aOut.Append(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const char* data = aValue->Get1b();
|
|
||||||
for (uint32_t i = 0; i < len; ++i) {
|
|
||||||
const unsigned char c = data[i];
|
|
||||||
switch (c) {
|
|
||||||
case '<':
|
|
||||||
aOut.AppendLiteral("<");
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
aOut.AppendLiteral(">");
|
|
||||||
break;
|
|
||||||
case '&':
|
|
||||||
aOut.AppendLiteral("&");
|
|
||||||
break;
|
|
||||||
case 0x00A0:
|
|
||||||
aOut.AppendLiteral(" ");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
aOut.Append(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
|
|
||||||
nsAutoPtr<StringBuilder> mNext;
|
|
||||||
StringBuilder* mLast;
|
|
||||||
// mLength is used only in the first StringBuilder object in the linked list.
|
|
||||||
uint32_t mLength;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
static void
|
|
||||||
AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
|
|
||||||
{
|
|
||||||
uint32_t extraSpaceNeeded = 0;
|
|
||||||
uint32_t len = aText->GetLength();
|
|
||||||
if (aText->Is2b()) {
|
|
||||||
const char16_t* data = aText->Get2b();
|
|
||||||
for (uint32_t i = 0; i < len; ++i) {
|
|
||||||
const char16_t c = data[i];
|
|
||||||
switch (c) {
|
|
||||||
case '<':
|
|
||||||
extraSpaceNeeded += ArrayLength("<") - 2;
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
extraSpaceNeeded += ArrayLength(">") - 2;
|
|
||||||
break;
|
|
||||||
case '&':
|
|
||||||
extraSpaceNeeded += ArrayLength("&") - 2;
|
|
||||||
break;
|
|
||||||
case 0x00A0:
|
|
||||||
extraSpaceNeeded += ArrayLength(" ") - 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const char* data = aText->Get1b();
|
|
||||||
for (uint32_t i = 0; i < len; ++i) {
|
|
||||||
const unsigned char c = data[i];
|
|
||||||
switch (c) {
|
|
||||||
case '<':
|
|
||||||
extraSpaceNeeded += ArrayLength("<") - 2;
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
extraSpaceNeeded += ArrayLength(">") - 2;
|
|
||||||
break;
|
|
||||||
case '&':
|
|
||||||
extraSpaceNeeded += ArrayLength("&") - 2;
|
|
||||||
break;
|
|
||||||
case 0x00A0:
|
|
||||||
extraSpaceNeeded += ArrayLength(" ") - 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extraSpaceNeeded) {
|
|
||||||
aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
|
|
||||||
} else {
|
|
||||||
aBuilder.Append(aText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
|
|
||||||
{
|
|
||||||
const char16_t* c = aValue->BeginReading();
|
|
||||||
const char16_t* end = aValue->EndReading();
|
|
||||||
|
|
||||||
uint32_t extraSpaceNeeded = 0;
|
|
||||||
while (c < end) {
|
|
||||||
switch (*c) {
|
|
||||||
case '"':
|
|
||||||
extraSpaceNeeded += ArrayLength(""") - 2;
|
|
||||||
break;
|
|
||||||
case '&':
|
|
||||||
extraSpaceNeeded += ArrayLength("&") - 2;
|
|
||||||
break;
|
|
||||||
case 0x00A0:
|
|
||||||
extraSpaceNeeded += ArrayLength(" ") - 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++c;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extraSpaceNeeded) {
|
|
||||||
aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
|
|
||||||
} else {
|
|
||||||
aBuilder.Append(aValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
StartElement(Element* aContent, StringBuilder& aBuilder)
|
|
||||||
{
|
|
||||||
nsIAtom* localName = aContent->NodeInfo()->NameAtom();
|
|
||||||
int32_t tagNS = aContent->GetNameSpaceID();
|
|
||||||
|
|
||||||
aBuilder.Append("<");
|
|
||||||
if (aContent->IsHTMLElement() || aContent->IsSVGElement() ||
|
|
||||||
aContent->IsMathMLElement()) {
|
|
||||||
aBuilder.Append(localName);
|
|
||||||
} else {
|
|
||||||
aBuilder.Append(aContent->NodeName());
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t count = aContent->GetAttrCount();
|
|
||||||
for (int32_t i = count; i > 0;) {
|
|
||||||
--i;
|
|
||||||
const nsAttrName* name = aContent->GetAttrNameAt(i);
|
|
||||||
int32_t attNs = name->NamespaceID();
|
|
||||||
nsIAtom* attName = name->LocalName();
|
|
||||||
|
|
||||||
// Filter out any attribute starting with [-|_]moz
|
|
||||||
nsDependentAtomString attrNameStr(attName);
|
|
||||||
if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
|
|
||||||
StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAutoString* attValue = new nsAutoString();
|
|
||||||
aContent->GetAttr(attNs, attName, *attValue);
|
|
||||||
|
|
||||||
// Filter out special case of <br type="_moz*"> used by the editor.
|
|
||||||
// Bug 16988. Yuck.
|
|
||||||
if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
|
|
||||||
attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
|
|
||||||
StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
|
|
||||||
delete attValue;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
aBuilder.Append(" ");
|
|
||||||
|
|
||||||
if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
|
|
||||||
(attNs == kNameSpaceID_XMLNS &&
|
|
||||||
attName == nsGkAtoms::xmlns)) {
|
|
||||||
// Nothing else required
|
|
||||||
} else if (attNs == kNameSpaceID_XML) {
|
|
||||||
aBuilder.Append("xml:");
|
|
||||||
} else if (attNs == kNameSpaceID_XMLNS) {
|
|
||||||
aBuilder.Append("xmlns:");
|
|
||||||
} else if (attNs == kNameSpaceID_XLink) {
|
|
||||||
aBuilder.Append("xlink:");
|
|
||||||
} else {
|
|
||||||
nsIAtom* prefix = name->GetPrefix();
|
|
||||||
if (prefix) {
|
|
||||||
aBuilder.Append(prefix);
|
|
||||||
aBuilder.Append(":");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aBuilder.Append(attName);
|
|
||||||
aBuilder.Append("=\"");
|
|
||||||
AppendEncodedAttributeValue(attValue, aBuilder);
|
|
||||||
aBuilder.Append("\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
aBuilder.Append(">");
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Per HTML spec we should append one \n if the first child of
|
|
||||||
// pre/textarea/listing is a textnode and starts with a \n.
|
|
||||||
// But because browsers haven't traditionally had that behavior,
|
|
||||||
// we're not changing our behavior either - yet.
|
|
||||||
if (aContent->IsHTMLElement()) {
|
|
||||||
if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
|
|
||||||
localName == nsGkAtoms::listing) {
|
|
||||||
nsIContent* fc = aContent->GetFirstChild();
|
|
||||||
if (fc &&
|
|
||||||
(fc->NodeType() == nsIDOMNode::TEXT_NODE ||
|
|
||||||
fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
|
|
||||||
const nsTextFragment* text = fc->GetText();
|
|
||||||
if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
|
|
||||||
aBuilder.Append("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
ShouldEscape(nsIContent* aParent)
|
|
||||||
{
|
|
||||||
if (!aParent || !aParent->IsHTMLElement()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const nsIAtom* nonEscapingElements[] = {
|
|
||||||
nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
|
|
||||||
nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
|
|
||||||
nsGkAtoms::plaintext,
|
|
||||||
// Per the current spec noscript should be escaped in case
|
|
||||||
// scripts are disabled or if document doesn't have
|
|
||||||
// browsing context. However the latter seems to be a spec bug
|
|
||||||
// and Gecko hasn't traditionally done the former.
|
|
||||||
nsGkAtoms::noscript
|
|
||||||
};
|
|
||||||
static mozilla::BloomFilter<12, nsIAtom> sFilter;
|
|
||||||
static bool sInitialized = false;
|
|
||||||
if (!sInitialized) {
|
|
||||||
sInitialized = true;
|
|
||||||
for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
|
|
||||||
sFilter.add(nonEscapingElements[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsIAtom* tag = aParent->NodeInfo()->NameAtom();
|
|
||||||
if (sFilter.mightContain(tag)) {
|
|
||||||
for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
|
|
||||||
if (tag == nonEscapingElements[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
IsVoidTag(nsIAtom* aTag)
|
IsVoidTag(nsIAtom* aTag)
|
||||||
{
|
{
|
||||||
|
@ -2580,15 +2094,6 @@ IsVoidTag(nsIAtom* aTag)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
|
||||||
IsVoidTag(Element* aElement)
|
|
||||||
{
|
|
||||||
if (!aElement->IsHTMLElement()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return IsVoidTag(aElement->NodeInfo()->NameAtom());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
bool
|
bool
|
||||||
FragmentOrElement::IsHTMLVoid(nsIAtom* aLocalName)
|
FragmentOrElement::IsHTMLVoid(nsIAtom* aLocalName)
|
||||||
|
@ -2596,112 +2101,6 @@ FragmentOrElement::IsHTMLVoid(nsIAtom* aLocalName)
|
||||||
return aLocalName && IsVoidTag(aLocalName);
|
return aLocalName && IsVoidTag(aLocalName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
Serialize(FragmentOrElement* aRoot, bool aDescendentsOnly, nsAString& aOut)
|
|
||||||
{
|
|
||||||
nsINode* current = aDescendentsOnly ?
|
|
||||||
nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
|
|
||||||
|
|
||||||
if (!current) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder builder;
|
|
||||||
nsIContent* next;
|
|
||||||
while (true) {
|
|
||||||
bool isVoid = false;
|
|
||||||
switch (current->NodeType()) {
|
|
||||||
case nsIDOMNode::ELEMENT_NODE: {
|
|
||||||
Element* elem = current->AsElement();
|
|
||||||
StartElement(elem, builder);
|
|
||||||
isVoid = IsVoidTag(elem);
|
|
||||||
if (!isVoid &&
|
|
||||||
(next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
|
|
||||||
current = next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case nsIDOMNode::TEXT_NODE:
|
|
||||||
case nsIDOMNode::CDATA_SECTION_NODE: {
|
|
||||||
const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
|
|
||||||
nsIContent* parent = current->GetParent();
|
|
||||||
if (ShouldEscape(parent)) {
|
|
||||||
AppendEncodedCharacters(text, builder);
|
|
||||||
} else {
|
|
||||||
builder.Append(text);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case nsIDOMNode::COMMENT_NODE: {
|
|
||||||
builder.Append("<!--");
|
|
||||||
builder.Append(static_cast<nsIContent*>(current)->GetText());
|
|
||||||
builder.Append("-->");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case nsIDOMNode::DOCUMENT_TYPE_NODE: {
|
|
||||||
builder.Append("<!DOCTYPE ");
|
|
||||||
builder.Append(current->NodeName());
|
|
||||||
builder.Append(">");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
|
|
||||||
builder.Append("<?");
|
|
||||||
builder.Append(current->NodeName());
|
|
||||||
builder.Append(" ");
|
|
||||||
builder.Append(static_cast<nsIContent*>(current)->GetText());
|
|
||||||
builder.Append(">");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
|
|
||||||
builder.Append("</");
|
|
||||||
nsIContent* elem = static_cast<nsIContent*>(current);
|
|
||||||
if (elem->IsHTMLElement() || elem->IsSVGElement() ||
|
|
||||||
elem->IsMathMLElement()) {
|
|
||||||
builder.Append(elem->NodeInfo()->NameAtom());
|
|
||||||
} else {
|
|
||||||
builder.Append(current->NodeName());
|
|
||||||
}
|
|
||||||
builder.Append(">");
|
|
||||||
}
|
|
||||||
isVoid = false;
|
|
||||||
|
|
||||||
if (current == aRoot) {
|
|
||||||
return builder.ToString(aOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((next = current->GetNextSibling())) {
|
|
||||||
current = next;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = current->GetParentNode();
|
|
||||||
|
|
||||||
// Handle template element. If the parent is a template's content,
|
|
||||||
// then adjust the parent to be the template element.
|
|
||||||
if (current != aRoot &&
|
|
||||||
current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
|
|
||||||
DocumentFragment* frag = static_cast<DocumentFragment*>(current);
|
|
||||||
nsIContent* fragHost = frag->GetHost();
|
|
||||||
if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
|
|
||||||
current = fragHost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aDescendentsOnly && current == aRoot) {
|
|
||||||
return builder.ToString(aOut);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
|
FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
|
||||||
{
|
{
|
||||||
|
@ -2709,7 +2108,7 @@ FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
|
||||||
|
|
||||||
nsIDocument* doc = OwnerDoc();
|
nsIDocument* doc = OwnerDoc();
|
||||||
if (IsInHTMLDocument()) {
|
if (IsInHTMLDocument()) {
|
||||||
Serialize(this, !aIncludeSelf, aMarkup);
|
nsContentUtils::SerializeNodeToMarkup(this, !aIncludeSelf, aMarkup);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,11 +48,21 @@ NodeIsInTraversalRange(nsINode* aNode, bool aIsPreMode,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a chardata node contains an end point of the traversal range, it is
|
// If a leaf node contains an end point of the traversal range, it is
|
||||||
// always in the traversal range.
|
// always in the traversal range.
|
||||||
if (aNode->IsNodeOfType(nsINode::eDATA_NODE) &&
|
if (aNode == aStartNode || aNode == aEndNode) {
|
||||||
(aNode == aStartNode || aNode == aEndNode)) {
|
if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
|
||||||
return true;
|
return true; // text node or something
|
||||||
|
}
|
||||||
|
if (!aNode->HasChildren()) {
|
||||||
|
MOZ_ASSERT(aNode != aStartNode || !aStartOffset,
|
||||||
|
"aStartNode doesn't have children and not a data node, "
|
||||||
|
"aStartOffset should be 0");
|
||||||
|
MOZ_ASSERT(aNode != aEndNode || !aEndOffset,
|
||||||
|
"aStartNode doesn't have children and not a data node, "
|
||||||
|
"aStartOffset should be 0");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsINode* parent = aNode->GetParentNode();
|
nsINode* parent = aNode->GetParentNode();
|
||||||
|
@ -341,12 +351,14 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
||||||
// character in the cdata node, should we set mFirst to
|
// character in the cdata node, should we set mFirst to
|
||||||
// the next sibling?
|
// the next sibling?
|
||||||
|
|
||||||
if (!startIsData) {
|
// If the node has no child, the child may be <br> or something.
|
||||||
|
// So, we shouldn't skip the empty node if the start offset is 0.
|
||||||
|
// In other words, if the offset is 1, the node should be ignored.
|
||||||
|
if (!startIsData && startIndx) {
|
||||||
mFirst = GetNextSibling(startNode);
|
mFirst = GetNextSibling(startNode);
|
||||||
|
|
||||||
// Does mFirst node really intersect the range? The range could be
|
// Does mFirst node really intersect the range? The range could be
|
||||||
// 'degenerate', i.e., not collapsed but still contain no content.
|
// 'degenerate', i.e., not collapsed but still contain no content.
|
||||||
|
|
||||||
if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, startNode,
|
if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, startNode,
|
||||||
startIndx, endNode, endIndx)) {
|
startIndx, endNode, endIndx)) {
|
||||||
mFirst = nullptr;
|
mFirst = nullptr;
|
||||||
|
|
|
@ -197,6 +197,7 @@
|
||||||
#include "mozIThirdPartyUtil.h"
|
#include "mozIThirdPartyUtil.h"
|
||||||
#include "nsICookieService.h"
|
#include "nsICookieService.h"
|
||||||
#include "mozilla/EnumSet.h"
|
#include "mozilla/EnumSet.h"
|
||||||
|
#include "mozilla/BloomFilter.h"
|
||||||
|
|
||||||
#include "nsIBidiKeyboard.h"
|
#include "nsIBidiKeyboard.h"
|
||||||
|
|
||||||
|
@ -8259,3 +8260,609 @@ nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal,
|
||||||
|
|
||||||
return access;
|
return access;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// We put StringBuilder in the anonymous namespace to prevent anything outside
|
||||||
|
// this file from accidentally being linked against it.
|
||||||
|
|
||||||
|
class StringBuilder
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Try to keep the size of StringBuilder close to a jemalloc bucket size.
|
||||||
|
static const uint32_t STRING_BUFFER_UNITS = 1020;
|
||||||
|
class Unit
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
|
||||||
|
{
|
||||||
|
MOZ_COUNT_CTOR(StringBuilder::Unit);
|
||||||
|
}
|
||||||
|
~Unit()
|
||||||
|
{
|
||||||
|
if (mType == eString || mType == eStringWithEncode) {
|
||||||
|
delete mString;
|
||||||
|
}
|
||||||
|
MOZ_COUNT_DTOR(StringBuilder::Unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
eUnknown,
|
||||||
|
eAtom,
|
||||||
|
eString,
|
||||||
|
eStringWithEncode,
|
||||||
|
eLiteral,
|
||||||
|
eTextFragment,
|
||||||
|
eTextFragmentWithEncode,
|
||||||
|
};
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
nsIAtom* mAtom;
|
||||||
|
const char* mLiteral;
|
||||||
|
nsAutoString* mString;
|
||||||
|
const nsTextFragment* mTextFragment;
|
||||||
|
};
|
||||||
|
Type mType;
|
||||||
|
uint32_t mLength;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
StringBuilder() : mLast(this), mLength(0)
|
||||||
|
{
|
||||||
|
MOZ_COUNT_CTOR(StringBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
~StringBuilder()
|
||||||
|
{
|
||||||
|
MOZ_COUNT_DTOR(StringBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Append(nsIAtom* aAtom)
|
||||||
|
{
|
||||||
|
Unit* u = AddUnit();
|
||||||
|
u->mAtom = aAtom;
|
||||||
|
u->mType = Unit::eAtom;
|
||||||
|
uint32_t len = aAtom->GetLength();
|
||||||
|
u->mLength = len;
|
||||||
|
mLength += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int N>
|
||||||
|
void Append(const char (&aLiteral)[N])
|
||||||
|
{
|
||||||
|
Unit* u = AddUnit();
|
||||||
|
u->mLiteral = aLiteral;
|
||||||
|
u->mType = Unit::eLiteral;
|
||||||
|
uint32_t len = N - 1;
|
||||||
|
u->mLength = len;
|
||||||
|
mLength += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int N>
|
||||||
|
void Append(char (&aLiteral)[N])
|
||||||
|
{
|
||||||
|
Unit* u = AddUnit();
|
||||||
|
u->mLiteral = aLiteral;
|
||||||
|
u->mType = Unit::eLiteral;
|
||||||
|
uint32_t len = N - 1;
|
||||||
|
u->mLength = len;
|
||||||
|
mLength += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Append(const nsAString& aString)
|
||||||
|
{
|
||||||
|
Unit* u = AddUnit();
|
||||||
|
u->mString = new nsAutoString(aString);
|
||||||
|
u->mType = Unit::eString;
|
||||||
|
uint32_t len = aString.Length();
|
||||||
|
u->mLength = len;
|
||||||
|
mLength += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Append(nsAutoString* aString)
|
||||||
|
{
|
||||||
|
Unit* u = AddUnit();
|
||||||
|
u->mString = aString;
|
||||||
|
u->mType = Unit::eString;
|
||||||
|
uint32_t len = aString->Length();
|
||||||
|
u->mLength = len;
|
||||||
|
mLength += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
|
||||||
|
{
|
||||||
|
Unit* u = AddUnit();
|
||||||
|
u->mString = aString;
|
||||||
|
u->mType = Unit::eStringWithEncode;
|
||||||
|
u->mLength = aLen;
|
||||||
|
mLength += aLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Append(const nsTextFragment* aTextFragment)
|
||||||
|
{
|
||||||
|
Unit* u = AddUnit();
|
||||||
|
u->mTextFragment = aTextFragment;
|
||||||
|
u->mType = Unit::eTextFragment;
|
||||||
|
uint32_t len = aTextFragment->GetLength();
|
||||||
|
u->mLength = len;
|
||||||
|
mLength += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
|
||||||
|
{
|
||||||
|
Unit* u = AddUnit();
|
||||||
|
u->mTextFragment = aTextFragment;
|
||||||
|
u->mType = Unit::eTextFragmentWithEncode;
|
||||||
|
u->mLength = aLen;
|
||||||
|
mLength += aLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToString(nsAString& aOut)
|
||||||
|
{
|
||||||
|
if (!aOut.SetCapacity(mLength, fallible)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (StringBuilder* current = this; current; current = current->mNext) {
|
||||||
|
uint32_t len = current->mUnits.Length();
|
||||||
|
for (uint32_t i = 0; i < len; ++i) {
|
||||||
|
Unit& u = current->mUnits[i];
|
||||||
|
switch (u.mType) {
|
||||||
|
case Unit::eAtom:
|
||||||
|
aOut.Append(nsDependentAtomString(u.mAtom));
|
||||||
|
break;
|
||||||
|
case Unit::eString:
|
||||||
|
aOut.Append(*(u.mString));
|
||||||
|
break;
|
||||||
|
case Unit::eStringWithEncode:
|
||||||
|
EncodeAttrString(*(u.mString), aOut);
|
||||||
|
break;
|
||||||
|
case Unit::eLiteral:
|
||||||
|
aOut.AppendASCII(u.mLiteral, u.mLength);
|
||||||
|
break;
|
||||||
|
case Unit::eTextFragment:
|
||||||
|
u.mTextFragment->AppendTo(aOut);
|
||||||
|
break;
|
||||||
|
case Unit::eTextFragmentWithEncode:
|
||||||
|
EncodeTextFragment(u.mTextFragment, aOut);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unknown unit type?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Unit* AddUnit()
|
||||||
|
{
|
||||||
|
if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
|
||||||
|
new StringBuilder(this);
|
||||||
|
}
|
||||||
|
return mLast->mUnits.AppendElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit StringBuilder(StringBuilder* aFirst)
|
||||||
|
: mLast(nullptr), mLength(0)
|
||||||
|
{
|
||||||
|
MOZ_COUNT_CTOR(StringBuilder);
|
||||||
|
aFirst->mLast->mNext = this;
|
||||||
|
aFirst->mLast = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
|
||||||
|
{
|
||||||
|
const char16_t* c = aValue.BeginReading();
|
||||||
|
const char16_t* end = aValue.EndReading();
|
||||||
|
while (c < end) {
|
||||||
|
switch (*c) {
|
||||||
|
case '"':
|
||||||
|
aOut.AppendLiteral(""");
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
aOut.AppendLiteral("&");
|
||||||
|
break;
|
||||||
|
case 0x00A0:
|
||||||
|
aOut.AppendLiteral(" ");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
aOut.Append(*c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
|
||||||
|
{
|
||||||
|
uint32_t len = aValue->GetLength();
|
||||||
|
if (aValue->Is2b()) {
|
||||||
|
const char16_t* data = aValue->Get2b();
|
||||||
|
for (uint32_t i = 0; i < len; ++i) {
|
||||||
|
const char16_t c = data[i];
|
||||||
|
switch (c) {
|
||||||
|
case '<':
|
||||||
|
aOut.AppendLiteral("<");
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
aOut.AppendLiteral(">");
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
aOut.AppendLiteral("&");
|
||||||
|
break;
|
||||||
|
case 0x00A0:
|
||||||
|
aOut.AppendLiteral(" ");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
aOut.Append(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const char* data = aValue->Get1b();
|
||||||
|
for (uint32_t i = 0; i < len; ++i) {
|
||||||
|
const unsigned char c = data[i];
|
||||||
|
switch (c) {
|
||||||
|
case '<':
|
||||||
|
aOut.AppendLiteral("<");
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
aOut.AppendLiteral(">");
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
aOut.AppendLiteral("&");
|
||||||
|
break;
|
||||||
|
case 0x00A0:
|
||||||
|
aOut.AppendLiteral(" ");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
aOut.Append(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
|
||||||
|
nsAutoPtr<StringBuilder> mNext;
|
||||||
|
StringBuilder* mLast;
|
||||||
|
// mLength is used only in the first StringBuilder object in the linked list.
|
||||||
|
uint32_t mLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
static void
|
||||||
|
AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
|
||||||
|
{
|
||||||
|
uint32_t extraSpaceNeeded = 0;
|
||||||
|
uint32_t len = aText->GetLength();
|
||||||
|
if (aText->Is2b()) {
|
||||||
|
const char16_t* data = aText->Get2b();
|
||||||
|
for (uint32_t i = 0; i < len; ++i) {
|
||||||
|
const char16_t c = data[i];
|
||||||
|
switch (c) {
|
||||||
|
case '<':
|
||||||
|
extraSpaceNeeded += ArrayLength("<") - 2;
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
extraSpaceNeeded += ArrayLength(">") - 2;
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
extraSpaceNeeded += ArrayLength("&") - 2;
|
||||||
|
break;
|
||||||
|
case 0x00A0:
|
||||||
|
extraSpaceNeeded += ArrayLength(" ") - 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const char* data = aText->Get1b();
|
||||||
|
for (uint32_t i = 0; i < len; ++i) {
|
||||||
|
const unsigned char c = data[i];
|
||||||
|
switch (c) {
|
||||||
|
case '<':
|
||||||
|
extraSpaceNeeded += ArrayLength("<") - 2;
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
extraSpaceNeeded += ArrayLength(">") - 2;
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
extraSpaceNeeded += ArrayLength("&") - 2;
|
||||||
|
break;
|
||||||
|
case 0x00A0:
|
||||||
|
extraSpaceNeeded += ArrayLength(" ") - 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extraSpaceNeeded) {
|
||||||
|
aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
|
||||||
|
} else {
|
||||||
|
aBuilder.Append(aText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
|
||||||
|
{
|
||||||
|
const char16_t* c = aValue->BeginReading();
|
||||||
|
const char16_t* end = aValue->EndReading();
|
||||||
|
|
||||||
|
uint32_t extraSpaceNeeded = 0;
|
||||||
|
while (c < end) {
|
||||||
|
switch (*c) {
|
||||||
|
case '"':
|
||||||
|
extraSpaceNeeded += ArrayLength(""") - 2;
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
extraSpaceNeeded += ArrayLength("&") - 2;
|
||||||
|
break;
|
||||||
|
case 0x00A0:
|
||||||
|
extraSpaceNeeded += ArrayLength(" ") - 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extraSpaceNeeded) {
|
||||||
|
aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
|
||||||
|
} else {
|
||||||
|
aBuilder.Append(aValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
StartElement(Element* aContent, StringBuilder& aBuilder)
|
||||||
|
{
|
||||||
|
nsIAtom* localName = aContent->NodeInfo()->NameAtom();
|
||||||
|
int32_t tagNS = aContent->GetNameSpaceID();
|
||||||
|
|
||||||
|
aBuilder.Append("<");
|
||||||
|
if (aContent->IsHTMLElement() || aContent->IsSVGElement() ||
|
||||||
|
aContent->IsMathMLElement()) {
|
||||||
|
aBuilder.Append(localName);
|
||||||
|
} else {
|
||||||
|
aBuilder.Append(aContent->NodeName());
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t count = aContent->GetAttrCount();
|
||||||
|
for (int32_t i = count; i > 0;) {
|
||||||
|
--i;
|
||||||
|
const nsAttrName* name = aContent->GetAttrNameAt(i);
|
||||||
|
int32_t attNs = name->NamespaceID();
|
||||||
|
nsIAtom* attName = name->LocalName();
|
||||||
|
|
||||||
|
// Filter out any attribute starting with [-|_]moz
|
||||||
|
nsDependentAtomString attrNameStr(attName);
|
||||||
|
if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
|
||||||
|
StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoString* attValue = new nsAutoString();
|
||||||
|
aContent->GetAttr(attNs, attName, *attValue);
|
||||||
|
|
||||||
|
// Filter out special case of <br type="_moz*"> used by the editor.
|
||||||
|
// Bug 16988. Yuck.
|
||||||
|
if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
|
||||||
|
attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
|
||||||
|
StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
|
||||||
|
delete attValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
aBuilder.Append(" ");
|
||||||
|
|
||||||
|
if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
|
||||||
|
(attNs == kNameSpaceID_XMLNS &&
|
||||||
|
attName == nsGkAtoms::xmlns)) {
|
||||||
|
// Nothing else required
|
||||||
|
} else if (attNs == kNameSpaceID_XML) {
|
||||||
|
aBuilder.Append("xml:");
|
||||||
|
} else if (attNs == kNameSpaceID_XMLNS) {
|
||||||
|
aBuilder.Append("xmlns:");
|
||||||
|
} else if (attNs == kNameSpaceID_XLink) {
|
||||||
|
aBuilder.Append("xlink:");
|
||||||
|
} else {
|
||||||
|
nsIAtom* prefix = name->GetPrefix();
|
||||||
|
if (prefix) {
|
||||||
|
aBuilder.Append(prefix);
|
||||||
|
aBuilder.Append(":");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aBuilder.Append(attName);
|
||||||
|
aBuilder.Append("=\"");
|
||||||
|
AppendEncodedAttributeValue(attValue, aBuilder);
|
||||||
|
aBuilder.Append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
aBuilder.Append(">");
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Per HTML spec we should append one \n if the first child of
|
||||||
|
// pre/textarea/listing is a textnode and starts with a \n.
|
||||||
|
// But because browsers haven't traditionally had that behavior,
|
||||||
|
// we're not changing our behavior either - yet.
|
||||||
|
if (aContent->IsHTMLElement()) {
|
||||||
|
if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
|
||||||
|
localName == nsGkAtoms::listing) {
|
||||||
|
nsIContent* fc = aContent->GetFirstChild();
|
||||||
|
if (fc &&
|
||||||
|
(fc->NodeType() == nsIDOMNode::TEXT_NODE ||
|
||||||
|
fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
|
||||||
|
const nsTextFragment* text = fc->GetText();
|
||||||
|
if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
|
||||||
|
aBuilder.Append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
ShouldEscape(nsIContent* aParent)
|
||||||
|
{
|
||||||
|
if (!aParent || !aParent->IsHTMLElement()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const nsIAtom* nonEscapingElements[] = {
|
||||||
|
nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
|
||||||
|
nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
|
||||||
|
nsGkAtoms::plaintext,
|
||||||
|
// Per the current spec noscript should be escaped in case
|
||||||
|
// scripts are disabled or if document doesn't have
|
||||||
|
// browsing context. However the latter seems to be a spec bug
|
||||||
|
// and Gecko hasn't traditionally done the former.
|
||||||
|
nsGkAtoms::noscript
|
||||||
|
};
|
||||||
|
static mozilla::BloomFilter<12, nsIAtom> sFilter;
|
||||||
|
static bool sInitialized = false;
|
||||||
|
if (!sInitialized) {
|
||||||
|
sInitialized = true;
|
||||||
|
for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
|
||||||
|
sFilter.add(nonEscapingElements[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIAtom* tag = aParent->NodeInfo()->NameAtom();
|
||||||
|
if (sFilter.mightContain(tag)) {
|
||||||
|
for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
|
||||||
|
if (tag == nonEscapingElements[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsVoidTag(Element* aElement)
|
||||||
|
{
|
||||||
|
if (!aElement->IsHTMLElement()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return FragmentOrElement::IsHTMLVoid(aElement->NodeInfo()->NameAtom());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsContentUtils::SerializeNodeToMarkup(nsINode* aRoot,
|
||||||
|
bool aDescendentsOnly,
|
||||||
|
nsAString& aOut)
|
||||||
|
{
|
||||||
|
// If you pass in a DOCUMENT_NODE, you must pass aDescendentsOnly as true
|
||||||
|
MOZ_ASSERT(aDescendentsOnly ||
|
||||||
|
aRoot->NodeType() != nsIDOMNode::DOCUMENT_NODE);
|
||||||
|
|
||||||
|
nsINode* current = aDescendentsOnly ?
|
||||||
|
nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
|
||||||
|
|
||||||
|
if (!current) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder builder;
|
||||||
|
nsIContent* next;
|
||||||
|
while (true) {
|
||||||
|
bool isVoid = false;
|
||||||
|
switch (current->NodeType()) {
|
||||||
|
case nsIDOMNode::ELEMENT_NODE: {
|
||||||
|
Element* elem = current->AsElement();
|
||||||
|
StartElement(elem, builder);
|
||||||
|
isVoid = IsVoidTag(elem);
|
||||||
|
if (!isVoid &&
|
||||||
|
(next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
|
||||||
|
current = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case nsIDOMNode::TEXT_NODE:
|
||||||
|
case nsIDOMNode::CDATA_SECTION_NODE: {
|
||||||
|
const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
|
||||||
|
nsIContent* parent = current->GetParent();
|
||||||
|
if (ShouldEscape(parent)) {
|
||||||
|
AppendEncodedCharacters(text, builder);
|
||||||
|
} else {
|
||||||
|
builder.Append(text);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case nsIDOMNode::COMMENT_NODE: {
|
||||||
|
builder.Append("<!--");
|
||||||
|
builder.Append(static_cast<nsIContent*>(current)->GetText());
|
||||||
|
builder.Append("-->");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case nsIDOMNode::DOCUMENT_TYPE_NODE: {
|
||||||
|
builder.Append("<!DOCTYPE ");
|
||||||
|
builder.Append(current->NodeName());
|
||||||
|
builder.Append(">");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
|
||||||
|
builder.Append("<?");
|
||||||
|
builder.Append(current->NodeName());
|
||||||
|
builder.Append(" ");
|
||||||
|
builder.Append(static_cast<nsIContent*>(current)->GetText());
|
||||||
|
builder.Append(">");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
|
||||||
|
builder.Append("</");
|
||||||
|
nsIContent* elem = static_cast<nsIContent*>(current);
|
||||||
|
if (elem->IsHTMLElement() || elem->IsSVGElement() ||
|
||||||
|
elem->IsMathMLElement()) {
|
||||||
|
builder.Append(elem->NodeInfo()->NameAtom());
|
||||||
|
} else {
|
||||||
|
builder.Append(current->NodeName());
|
||||||
|
}
|
||||||
|
builder.Append(">");
|
||||||
|
}
|
||||||
|
isVoid = false;
|
||||||
|
|
||||||
|
if (current == aRoot) {
|
||||||
|
return builder.ToString(aOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((next = current->GetNextSibling())) {
|
||||||
|
current = next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current->GetParentNode();
|
||||||
|
|
||||||
|
// Handle template element. If the parent is a template's content,
|
||||||
|
// then adjust the parent to be the template element.
|
||||||
|
if (current != aRoot &&
|
||||||
|
current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
|
||||||
|
DocumentFragment* frag = static_cast<DocumentFragment*>(current);
|
||||||
|
nsIContent* fragHost = frag->GetHost();
|
||||||
|
if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
|
||||||
|
current = fragHost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aDescendentsOnly && current == aRoot) {
|
||||||
|
return builder.ToString(aOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2561,6 +2561,13 @@ public:
|
||||||
*/
|
*/
|
||||||
static StorageAccess StorageAllowedForPrincipal(nsIPrincipal* aPrincipal);
|
static StorageAccess StorageAllowedForPrincipal(nsIPrincipal* aPrincipal);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Serializes a HTML nsINode into its markup representation.
|
||||||
|
*/
|
||||||
|
static bool SerializeNodeToMarkup(nsINode* aRoot,
|
||||||
|
bool aDescendentsOnly,
|
||||||
|
nsAString& aOut);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool InitializeEventTable();
|
static bool InitializeEventTable();
|
||||||
|
|
||||||
|
|
|
@ -2311,16 +2311,11 @@ GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult,
|
||||||
uint64_t* aContentLength, nsACString& aContentType,
|
uint64_t* aContentLength, nsACString& aContentType,
|
||||||
nsACString& aCharset)
|
nsACString& aCharset)
|
||||||
{
|
{
|
||||||
aContentType.AssignLiteral("application/xml");
|
|
||||||
nsCOMPtr<nsIDocument> doc(do_QueryInterface(aDoc));
|
nsCOMPtr<nsIDocument> doc(do_QueryInterface(aDoc));
|
||||||
NS_ENSURE_STATE(doc);
|
NS_ENSURE_STATE(doc);
|
||||||
aCharset.AssignLiteral("UTF-8");
|
aCharset.AssignLiteral("UTF-8");
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
nsCOMPtr<nsIDOMSerializer> serializer =
|
|
||||||
do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIStorageStream> storStream;
|
nsCOMPtr<nsIStorageStream> storStream;
|
||||||
rv = NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storStream));
|
rv = NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storStream));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
@ -2329,9 +2324,32 @@ GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult,
|
||||||
rv = storStream->GetOutputStream(0, getter_AddRefs(output));
|
rv = storStream->GetOutputStream(0, getter_AddRefs(output));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// Make sure to use the encoding we'll send
|
if (doc->IsHTMLDocument()) {
|
||||||
rv = serializer->SerializeToStream(aDoc, output, aCharset);
|
aContentType.AssignLiteral("text/html");
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
nsString serialized;
|
||||||
|
if (!nsContentUtils::SerializeNodeToMarkup(doc, true, serialized)) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
NS_ConvertUTF16toUTF8 utf8Serialized(serialized);
|
||||||
|
|
||||||
|
uint32_t written;
|
||||||
|
rv = output->Write(utf8Serialized.get(), utf8Serialized.Length(), &written);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
MOZ_ASSERT(written == utf8Serialized.Length());
|
||||||
|
} else {
|
||||||
|
aContentType.AssignLiteral("application/xml");
|
||||||
|
|
||||||
|
nsCOMPtr<nsIDOMSerializer> serializer =
|
||||||
|
do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
// Make sure to use the encoding we'll send
|
||||||
|
rv = serializer->SerializeToStream(aDoc, output, aCharset);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
output->Close();
|
output->Close();
|
||||||
|
|
||||||
|
|
|
@ -4848,6 +4848,9 @@ HTMLInputElement::ChooseDirectory(ErrorResult& aRv)
|
||||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Script can call this method directly, so even though we don't show the
|
||||||
|
// "Pick Folder..." button on platforms that don't have a directory picker
|
||||||
|
// we have to redirect to the file picker here.
|
||||||
InitFilePicker(
|
InitFilePicker(
|
||||||
#if defined(ANDROID) || defined(MOZ_B2G)
|
#if defined(ANDROID) || defined(MOZ_B2G)
|
||||||
// No native directory picker - redirect to plain file picker
|
// No native directory picker - redirect to plain file picker
|
||||||
|
|
|
@ -113,8 +113,6 @@ public:
|
||||||
StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
|
StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
|
||||||
|
|
||||||
#if defined(PR_LOGGING)
|
#if defined(PR_LOGGING)
|
||||||
PRLogModuleInfo* gStateWatchingLog;
|
|
||||||
PRLogModuleInfo* gMozPromiseLog;
|
|
||||||
PRLogModuleInfo* gMediaTimerLog;
|
PRLogModuleInfo* gMediaTimerLog;
|
||||||
PRLogModuleInfo* gMediaSampleLog;
|
PRLogModuleInfo* gMediaSampleLog;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -819,9 +819,8 @@ WebMTrackDemuxer::Seek(media::TimeUnit aTime)
|
||||||
|
|
||||||
// Check what time we actually seeked to.
|
// Check what time we actually seeked to.
|
||||||
if (mSamples.GetSize() > 0) {
|
if (mSamples.GetSize() > 0) {
|
||||||
RefPtr<MediaRawData> sample(mSamples.PopFront());
|
const RefPtr<MediaRawData>& sample = mSamples.First();
|
||||||
seekTime = media::TimeUnit::FromMicroseconds(sample->mTime);
|
seekTime = media::TimeUnit::FromMicroseconds(sample->mTime);
|
||||||
mSamples.PushFront(sample);
|
|
||||||
}
|
}
|
||||||
SetNextKeyFrameTime();
|
SetNextKeyFrameTime();
|
||||||
|
|
||||||
|
@ -876,40 +875,40 @@ WebMTrackDemuxer::SetNextKeyFrameTime()
|
||||||
mNextKeyframeTime.reset();
|
mNextKeyframeTime.reset();
|
||||||
|
|
||||||
MediaRawDataQueue skipSamplesQueue;
|
MediaRawDataQueue skipSamplesQueue;
|
||||||
RefPtr<MediaRawData> sample;
|
|
||||||
bool foundKeyframe = false;
|
bool foundKeyframe = false;
|
||||||
while (!foundKeyframe && mSamples.GetSize()) {
|
while (!foundKeyframe && mSamples.GetSize()) {
|
||||||
sample = mSamples.PopFront();
|
RefPtr<MediaRawData> sample = mSamples.PopFront();
|
||||||
if (sample->mKeyframe) {
|
if (sample->mKeyframe) {
|
||||||
frameTime = sample->mTime;
|
frameTime = sample->mTime;
|
||||||
foundKeyframe = true;
|
foundKeyframe = true;
|
||||||
}
|
}
|
||||||
skipSamplesQueue.Push(sample);
|
skipSamplesQueue.Push(sample.forget());
|
||||||
}
|
}
|
||||||
Maybe<int64_t> startTime;
|
Maybe<int64_t> startTime;
|
||||||
if (skipSamplesQueue.GetSize()) {
|
if (skipSamplesQueue.GetSize()) {
|
||||||
sample = skipSamplesQueue.PopFront();
|
const RefPtr<MediaRawData>& sample = skipSamplesQueue.First();
|
||||||
startTime.emplace(sample->mTimecode);
|
startTime.emplace(sample->mTimecode);
|
||||||
skipSamplesQueue.PushFront(sample);
|
|
||||||
}
|
}
|
||||||
// Demux and buffer frames until we find a keyframe.
|
// Demux and buffer frames until we find a keyframe.
|
||||||
|
RefPtr<MediaRawData> sample;
|
||||||
while (!foundKeyframe && (sample = NextSample())) {
|
while (!foundKeyframe && (sample = NextSample())) {
|
||||||
if (sample->mKeyframe) {
|
if (sample->mKeyframe) {
|
||||||
frameTime = sample->mTime;
|
frameTime = sample->mTime;
|
||||||
foundKeyframe = true;
|
foundKeyframe = true;
|
||||||
}
|
}
|
||||||
skipSamplesQueue.Push(sample);
|
int64_t sampleTimecode = sample->mTimecode;
|
||||||
|
skipSamplesQueue.Push(sample.forget());
|
||||||
if (!startTime) {
|
if (!startTime) {
|
||||||
startTime.emplace(sample->mTimecode);
|
startTime.emplace(sampleTimecode);
|
||||||
} else if (!foundKeyframe &&
|
} else if (!foundKeyframe &&
|
||||||
sample->mTimecode > startTime.ref() + MAX_LOOK_AHEAD) {
|
sampleTimecode > startTime.ref() + MAX_LOOK_AHEAD) {
|
||||||
WEBM_DEBUG("Couldn't find keyframe in a reasonable time, aborting");
|
WEBM_DEBUG("Couldn't find keyframe in a reasonable time, aborting");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We may have demuxed more than intended, so ensure that all frames are kept
|
// We may have demuxed more than intended, so ensure that all frames are kept
|
||||||
// in the right order.
|
// in the right order.
|
||||||
mSamples.PushFront(skipSamplesQueue);
|
mSamples.PushFront(Move(skipSamplesQueue));
|
||||||
|
|
||||||
if (frameTime != -1) {
|
if (frameTime != -1) {
|
||||||
mNextKeyframeTime.emplace(media::TimeUnit::FromMicroseconds(frameTime));
|
mNextKeyframeTime.emplace(media::TimeUnit::FromMicroseconds(frameTime));
|
||||||
|
@ -972,7 +971,7 @@ WebMTrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold)
|
||||||
if (sample->mKeyframe && sample->mTime >= aTimeThreshold.ToMicroseconds()) {
|
if (sample->mKeyframe && sample->mTime >= aTimeThreshold.ToMicroseconds()) {
|
||||||
found = true;
|
found = true;
|
||||||
mSamples.Reset();
|
mSamples.Reset();
|
||||||
mSamples.PushFront(sample);
|
mSamples.PushFront(sample.forget());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetNextKeyFrameTime();
|
SetNextKeyFrameTime();
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "MediaDataDemuxer.h"
|
#include "MediaDataDemuxer.h"
|
||||||
#include "NesteggPacketHolder.h"
|
#include "NesteggPacketHolder.h"
|
||||||
|
#include "mozilla/Move.h"
|
||||||
|
|
||||||
typedef struct nestegg nestegg;
|
typedef struct nestegg nestegg;
|
||||||
|
|
||||||
|
@ -27,16 +28,22 @@ class MediaRawDataQueue {
|
||||||
mQueue.push_back(aItem);
|
mQueue.push_back(aItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Push(const MediaRawDataQueue& aOther) {
|
void Push(already_AddRefed<MediaRawData>&& aItem) {
|
||||||
mQueue.insert(mQueue.end(), aOther.mQueue.begin(), aOther.mQueue.end());
|
mQueue.push_back(Move(aItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushFront(MediaRawData* aItem) {
|
void PushFront(MediaRawData* aItem) {
|
||||||
mQueue.push_front(aItem);
|
mQueue.push_front(aItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushFront(const MediaRawDataQueue& aOther) {
|
void PushFront(already_AddRefed<MediaRawData>&& aItem) {
|
||||||
mQueue.insert(mQueue.begin(), aOther.mQueue.begin(), aOther.mQueue.end());
|
mQueue.push_front(Move(aItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushFront(MediaRawDataQueue&& aOther) {
|
||||||
|
while (!aOther.mQueue.empty()) {
|
||||||
|
Push(aOther.PopFront());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<MediaRawData> PopFront() {
|
already_AddRefed<MediaRawData> PopFront() {
|
||||||
|
|
|
@ -54,7 +54,7 @@ dictionary MozXMLHttpRequestParameters
|
||||||
// c = new(window.ActiveXObject || XMLHttpRequest)("Microsoft.XMLHTTP")
|
// c = new(window.ActiveXObject || XMLHttpRequest)("Microsoft.XMLHTTP")
|
||||||
// To handle that, we need a constructor that takes a string.
|
// To handle that, we need a constructor that takes a string.
|
||||||
Constructor(DOMString ignored),
|
Constructor(DOMString ignored),
|
||||||
Exposed=(Window,Worker)]
|
Exposed=(Window,DedicatedWorker,SharedWorker)]
|
||||||
interface XMLHttpRequest : XMLHttpRequestEventTarget {
|
interface XMLHttpRequest : XMLHttpRequestEventTarget {
|
||||||
// event handler
|
// event handler
|
||||||
attribute EventHandler onreadystatechange;
|
attribute EventHandler onreadystatechange;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* liability, trademark and document use rules apply.
|
* liability, trademark and document use rules apply.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[Exposed=(Window,Worker)]
|
[Exposed=(Window,DedicatedWorker,SharedWorker)]
|
||||||
interface XMLHttpRequestEventTarget : EventTarget {
|
interface XMLHttpRequestEventTarget : EventTarget {
|
||||||
// event handlers
|
// event handlers
|
||||||
[SetterThrows=Workers, GetterThrows=Workers]
|
[SetterThrows=Workers, GetterThrows=Workers]
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* liability, trademark and document use rules apply.
|
* liability, trademark and document use rules apply.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[Exposed=(Window,Worker)]
|
[Exposed=(Window,DedicatedWorker,SharedWorker)]
|
||||||
interface XMLHttpRequestUpload : XMLHttpRequestEventTarget {
|
interface XMLHttpRequestUpload : XMLHttpRequestEventTarget {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -195,12 +195,6 @@ var interfaceNamesInGlobalScope =
|
||||||
"TextDecoder",
|
"TextDecoder",
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
"TextEncoder",
|
"TextEncoder",
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
|
||||||
"XMLHttpRequest",
|
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
|
||||||
"XMLHttpRequestEventTarget",
|
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
|
||||||
"XMLHttpRequestUpload",
|
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
"URL",
|
"URL",
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
|
|
|
@ -57,6 +57,21 @@ private:
|
||||||
bool mReneuter;
|
bool mReneuter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MOZ_RAII SuppressedNeuteringRegion
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SuppressedNeuteringRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
|
||||||
|
~SuppressedNeuteringRegion();
|
||||||
|
|
||||||
|
static inline bool IsNeuteringSuppressed() { return sSuppressNeutering; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
bool mReenable;
|
||||||
|
|
||||||
|
static bool sSuppressNeutering;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "nsServiceManagerUtils.h"
|
#include "nsServiceManagerUtils.h"
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nsIXULAppInfo.h"
|
#include "nsIXULAppInfo.h"
|
||||||
|
#include "nsWindowsDllInterceptor.h"
|
||||||
#include "WinUtils.h"
|
#include "WinUtils.h"
|
||||||
|
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
|
@ -436,6 +437,93 @@ ProcessOrDeferMessage(HWND hwnd,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It is bad to subclass a window when neutering is active because you'll end
|
||||||
|
* up subclassing the *neutered* window procedure instead of the real window
|
||||||
|
* procedure. Since CreateWindow* fires WM_CREATE (and could thus trigger
|
||||||
|
* neutering), we intercept these calls and suppress neutering for the duration
|
||||||
|
* of the call. This ensures that any subsequent subclassing replaces the
|
||||||
|
* correct window procedure.
|
||||||
|
*/
|
||||||
|
WindowsDllInterceptor sUser32Interceptor;
|
||||||
|
typedef HWND (WINAPI *CreateWindowExWPtr)(DWORD,LPCWSTR,LPCWSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
|
||||||
|
typedef HWND (WINAPI *CreateWindowExAPtr)(DWORD,LPCSTR,LPCSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
|
||||||
|
typedef HWND (WINAPI *CreateWindowWPtr)(LPCWSTR,LPCWSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
|
||||||
|
typedef HWND (WINAPI *CreateWindowAPtr)(LPCSTR,LPCSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
|
||||||
|
|
||||||
|
CreateWindowExWPtr sCreateWindowExWStub = nullptr;
|
||||||
|
CreateWindowExAPtr sCreateWindowExAStub = nullptr;
|
||||||
|
CreateWindowWPtr sCreateWindowWStub = nullptr;
|
||||||
|
CreateWindowAPtr sCreateWindowAStub = nullptr;
|
||||||
|
|
||||||
|
HWND WINAPI
|
||||||
|
CreateWindowExWHook(DWORD aExStyle, LPCWSTR aClassName, LPCWSTR aWindowName,
|
||||||
|
DWORD aStyle, int aX, int aY, int aWidth, int aHeight,
|
||||||
|
HWND aParent, HMENU aMenu, HINSTANCE aInstance,
|
||||||
|
LPVOID aParam)
|
||||||
|
{
|
||||||
|
SuppressedNeuteringRegion doNotNeuterThisWindowYet;
|
||||||
|
return sCreateWindowExWStub(aExStyle, aClassName, aWindowName, aStyle, aX, aY,
|
||||||
|
aWidth, aHeight, aParent, aMenu, aInstance, aParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND WINAPI
|
||||||
|
CreateWindowExAHook(DWORD aExStyle, LPCSTR aClassName, LPCSTR aWindowName,
|
||||||
|
DWORD aStyle, int aX, int aY, int aWidth, int aHeight,
|
||||||
|
HWND aParent, HMENU aMenu, HINSTANCE aInstance,
|
||||||
|
LPVOID aParam)
|
||||||
|
{
|
||||||
|
SuppressedNeuteringRegion doNotNeuterThisWindowYet;
|
||||||
|
return sCreateWindowExAStub(aExStyle, aClassName, aWindowName, aStyle, aX, aY,
|
||||||
|
aWidth, aHeight, aParent, aMenu, aInstance, aParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND WINAPI
|
||||||
|
CreateWindowWHook(LPCWSTR aClassName, LPCWSTR aWindowName, DWORD aStyle, int aX,
|
||||||
|
int aY, int aWidth, int aHeight, HWND aParent, HMENU aMenu,
|
||||||
|
HINSTANCE aInstance, LPVOID aParam)
|
||||||
|
{
|
||||||
|
SuppressedNeuteringRegion doNotNeuterThisWindowYet;
|
||||||
|
return sCreateWindowWStub(aClassName, aWindowName, aStyle, aX, aY, aWidth,
|
||||||
|
aHeight, aParent, aMenu, aInstance, aParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND WINAPI
|
||||||
|
CreateWindowAHook(LPCSTR aClassName, LPCSTR aWindowName, DWORD aStyle, int aX,
|
||||||
|
int aY, int aWidth, int aHeight, HWND aParent, HMENU aMenu,
|
||||||
|
HINSTANCE aInstance, LPVOID aParam)
|
||||||
|
{
|
||||||
|
SuppressedNeuteringRegion doNotNeuterThisWindowYet;
|
||||||
|
return sCreateWindowAStub(aClassName, aWindowName, aStyle, aX, aY, aWidth,
|
||||||
|
aHeight, aParent, aMenu, aInstance, aParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InitCreateWindowHook()
|
||||||
|
{
|
||||||
|
sUser32Interceptor.Init("user32.dll");
|
||||||
|
if (!sCreateWindowExWStub) {
|
||||||
|
sUser32Interceptor.AddHook("CreateWindowExW",
|
||||||
|
reinterpret_cast<intptr_t>(CreateWindowExWHook),
|
||||||
|
(void**) &sCreateWindowExWStub);
|
||||||
|
}
|
||||||
|
if (!sCreateWindowExAStub) {
|
||||||
|
sUser32Interceptor.AddHook("CreateWindowExA",
|
||||||
|
reinterpret_cast<intptr_t>(CreateWindowExAHook),
|
||||||
|
(void**) &sCreateWindowExAStub);
|
||||||
|
}
|
||||||
|
if (!sCreateWindowWStub) {
|
||||||
|
sUser32Interceptor.AddHook("CreateWindowW",
|
||||||
|
reinterpret_cast<intptr_t>(CreateWindowWHook),
|
||||||
|
(void**) &sCreateWindowWStub);
|
||||||
|
}
|
||||||
|
if (!sCreateWindowAStub) {
|
||||||
|
sUser32Interceptor.AddHook("CreateWindowA",
|
||||||
|
reinterpret_cast<intptr_t>(CreateWindowAHook),
|
||||||
|
(void**) &sCreateWindowAStub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// We need the pointer value of this in PluginInstanceChild.
|
// We need the pointer value of this in PluginInstanceChild.
|
||||||
|
@ -610,7 +698,9 @@ CallWindowProcedureHook(int nCode,
|
||||||
|
|
||||||
HWND hWnd = reinterpret_cast<CWPSTRUCT*>(lParam)->hwnd;
|
HWND hWnd = reinterpret_cast<CWPSTRUCT*>(lParam)->hwnd;
|
||||||
|
|
||||||
if (!gNeuteredWindows->Contains(hWnd) && NeuterWindowProcedure(hWnd)) {
|
if (!gNeuteredWindows->Contains(hWnd) &&
|
||||||
|
!SuppressedNeuteringRegion::IsNeuteringSuppressed() &&
|
||||||
|
NeuterWindowProcedure(hWnd)) {
|
||||||
if (!gNeuteredWindows->AppendElement(hWnd)) {
|
if (!gNeuteredWindows->AppendElement(hWnd)) {
|
||||||
NS_ERROR("Out of memory!");
|
NS_ERROR("Out of memory!");
|
||||||
RestoreWindowProcedure(hWnd);
|
RestoreWindowProcedure(hWnd);
|
||||||
|
@ -710,6 +800,8 @@ InitUIThread()
|
||||||
gCOMWindow = FindCOMWindow();
|
gCOMWindow = FindCOMWindow();
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(gWinEventHook);
|
MOZ_ASSERT(gWinEventHook);
|
||||||
|
|
||||||
|
InitCreateWindowHook();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace windows
|
} // namespace windows
|
||||||
|
@ -943,6 +1035,26 @@ DeneuteredWindowRegion::~DeneuteredWindowRegion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SuppressedNeuteringRegion::SuppressedNeuteringRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
|
||||||
|
: mReenable(::gUIThreadId == ::GetCurrentThreadId() && ::gWindowHook)
|
||||||
|
{
|
||||||
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
|
if (mReenable) {
|
||||||
|
MOZ_ASSERT(!sSuppressNeutering);
|
||||||
|
sSuppressNeutering = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SuppressedNeuteringRegion::~SuppressedNeuteringRegion()
|
||||||
|
{
|
||||||
|
if (mReenable) {
|
||||||
|
MOZ_ASSERT(sSuppressNeutering);
|
||||||
|
sSuppressNeutering = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SuppressedNeuteringRegion::sSuppressNeutering = false;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages)
|
MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages)
|
||||||
{
|
{
|
||||||
|
@ -997,6 +1109,8 @@ MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages)
|
||||||
NS_ASSERTION(timerId, "SetTimer failed!");
|
NS_ASSERTION(timerId, "SetTimer failed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NeuteredWindowRegion neuteredRgn(true);
|
||||||
|
|
||||||
{
|
{
|
||||||
while (1) {
|
while (1) {
|
||||||
MSG msg = { 0 };
|
MSG msg = { 0 };
|
||||||
|
|
|
@ -123,6 +123,11 @@ class ModuleCompiler
|
||||||
bool finishGeneratingFunction(AsmFunction& func, CodeGenerator& codegen,
|
bool finishGeneratingFunction(AsmFunction& func, CodeGenerator& codegen,
|
||||||
const AsmJSFunctionLabels& labels)
|
const AsmJSFunctionLabels& labels)
|
||||||
{
|
{
|
||||||
|
// If we have hit OOM then invariants which we assert below may not
|
||||||
|
// hold, so abort now.
|
||||||
|
if (masm().oom())
|
||||||
|
return false;
|
||||||
|
|
||||||
// Code range
|
// Code range
|
||||||
unsigned line = func.lineno();
|
unsigned line = func.lineno();
|
||||||
unsigned column = func.column();
|
unsigned column = func.column();
|
||||||
|
|
|
@ -641,11 +641,8 @@ ArrayMetaTypeDescr::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
|
||||||
if (!args.isConstructing()) {
|
if (!ThrowIfNotConstructing(cx, args, "ArrayType"))
|
||||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
|
|
||||||
JSMSG_NOT_FUNCTION, "ArrayType");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
RootedObject arrayTypeGlobal(cx, &args.callee());
|
RootedObject arrayTypeGlobal(cx, &args.callee());
|
||||||
|
|
||||||
|
@ -998,11 +995,8 @@ StructMetaTypeDescr::construct(JSContext* cx, unsigned int argc, Value* vp)
|
||||||
{
|
{
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
|
||||||
if (!args.isConstructing()) {
|
if (!ThrowIfNotConstructing(cx, args, "StructType"))
|
||||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
|
|
||||||
JSMSG_NOT_FUNCTION, "StructType");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (args.length() >= 1 && args[0].isObject()) {
|
if (args.length() >= 1 && args[0].isObject()) {
|
||||||
RootedObject metaTypeDescr(cx, &args.callee());
|
RootedObject metaTypeDescr(cx, &args.callee());
|
||||||
|
|
|
@ -85,10 +85,8 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||||
// Based on our "Set" implementation instead of the more general ES6 steps.
|
// Based on our "Set" implementation instead of the more general ES6 steps.
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
|
||||||
if (!args.isConstructing()) {
|
if (!ThrowIfNotConstructing(cx, args, "WeakSet"))
|
||||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, "WeakSet");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!args.get(0).isNullOrUndefined()) {
|
if (!args.get(0).isNullOrUndefined()) {
|
||||||
RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject());
|
RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject());
|
||||||
|
|
|
@ -9,7 +9,7 @@ function evalWithCache(code, ctx) {
|
||||||
|
|
||||||
// We create a new global ...
|
// We create a new global ...
|
||||||
if (!("global" in ctx))
|
if (!("global" in ctx))
|
||||||
ctx.global = newGlobal();
|
ctx.global = newGlobal({ cloneSingletons: true });
|
||||||
|
|
||||||
if (!("isRunOnce" in ctx))
|
if (!("isRunOnce" in ctx))
|
||||||
ctx.isRunOnce = true;
|
ctx.isRunOnce = true;
|
||||||
|
|
|
@ -8,6 +8,7 @@ test = (function () {
|
||||||
evalWithCache(test, {});
|
evalWithCache(test, {});
|
||||||
function evalWithCache(code, ctx) {
|
function evalWithCache(code, ctx) {
|
||||||
code = cacheEntry(code);
|
code = cacheEntry(code);
|
||||||
|
ctx.global = newGlobal({ cloneSingletons: true });
|
||||||
ctx.isRunOnce = true;
|
ctx.isRunOnce = true;
|
||||||
var res1 = evaluate(code, Object.create(ctx, {saveBytecode: { value: true } }));
|
var res1 = evaluate(code, Object.create(ctx, {saveBytecode: { value: true } }));
|
||||||
var res2 = evaluate(code, Object.create(ctx, {loadBytecode: { value: true }, saveBytecode: { value: true } }));
|
var res2 = evaluate(code, Object.create(ctx, {loadBytecode: { value: true }, saveBytecode: { value: true } }));
|
||||||
|
|
|
@ -6,6 +6,7 @@ test = (function () {
|
||||||
evalWithCache(test, {});
|
evalWithCache(test, {});
|
||||||
function evalWithCache(code, ctx) {
|
function evalWithCache(code, ctx) {
|
||||||
code = cacheEntry(code);
|
code = cacheEntry(code);
|
||||||
|
ctx.global = newGlobal({ cloneSingletons: true });
|
||||||
var res1 = evaluate(code, Object.create(ctx, {saveBytecode: { value: true } }));
|
var res1 = evaluate(code, Object.create(ctx, {saveBytecode: { value: true } }));
|
||||||
}
|
}
|
||||||
if (typeof assertThrowsInstanceOf === 'undefined') {
|
if (typeof assertThrowsInstanceOf === 'undefined') {
|
||||||
|
|
|
@ -5,7 +5,7 @@ gczeal(2);
|
||||||
(function() {
|
(function() {
|
||||||
evaluate(cacheEntry((function() {
|
evaluate(cacheEntry((function() {
|
||||||
return "".toSource()
|
return "".toSource()
|
||||||
})()), Object.create({}, {
|
})()), Object.create({ global: newGlobal({ cloneSingletons: true }) }, {
|
||||||
saveBytecode: {
|
saveBytecode: {
|
||||||
value: true
|
value: true
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
var caught = false;
|
||||||
|
try {
|
||||||
|
evaluate(cacheEntry(""), {saveBytecode: {value: true}, global: this});
|
||||||
|
[[0]];
|
||||||
|
} catch (err) {
|
||||||
|
caught = true;
|
||||||
|
assertEq(err.message, "compartment cannot save singleton anymore.");
|
||||||
|
}
|
||||||
|
assertEq(caught, true);
|
|
@ -150,7 +150,7 @@ test = `
|
||||||
evalWithCache(test, { assertEqBytecode: true, assertEqResult: true });
|
evalWithCache(test, { assertEqBytecode: true, assertEqResult: true });
|
||||||
|
|
||||||
// And more of the same, in a slightly different way
|
// And more of the same, in a slightly different way
|
||||||
var g1 = newGlobal();
|
var g1 = newGlobal({ cloneSingletons: true });
|
||||||
var g2 = newGlobal();
|
var g2 = newGlobal();
|
||||||
var res = "function f(){}";
|
var res = "function f(){}";
|
||||||
var code = cacheEntry(res + "; f();");
|
var code = cacheEntry(res + "; f();");
|
||||||
|
|
|
@ -2179,7 +2179,7 @@ Assembler::as_BranchPool(uint32_t value, RepatchLabel* label, ARMBuffer::PoolEnt
|
||||||
if (label->bound()) {
|
if (label->bound()) {
|
||||||
BufferOffset dest(label);
|
BufferOffset dest(label);
|
||||||
as_b(dest.diffB<BOffImm>(ret), c, ret);
|
as_b(dest.diffB<BOffImm>(ret), c, ret);
|
||||||
} else {
|
} else if (!oom()) {
|
||||||
label->use(ret.getOffset());
|
label->use(ret.getOffset());
|
||||||
}
|
}
|
||||||
#ifdef JS_DISASM_ARM
|
#ifdef JS_DISASM_ARM
|
||||||
|
@ -2379,14 +2379,12 @@ Assembler::as_b(BOffImm off, Condition c, Label* documentation)
|
||||||
BufferOffset
|
BufferOffset
|
||||||
Assembler::as_b(Label* l, Condition c)
|
Assembler::as_b(Label* l, Condition c)
|
||||||
{
|
{
|
||||||
if (m_buffer.oom()) {
|
|
||||||
BufferOffset ret;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (l->bound()) {
|
if (l->bound()) {
|
||||||
// Note only one instruction is emitted here, the NOP is overwritten.
|
// Note only one instruction is emitted here, the NOP is overwritten.
|
||||||
BufferOffset ret = allocBranchInst();
|
BufferOffset ret = allocBranchInst();
|
||||||
|
if (oom())
|
||||||
|
return BufferOffset();
|
||||||
|
|
||||||
as_b(BufferOffset(l).diffB<BOffImm>(ret), c, ret);
|
as_b(BufferOffset(l).diffB<BOffImm>(ret), c, ret);
|
||||||
#ifdef JS_DISASM_ARM
|
#ifdef JS_DISASM_ARM
|
||||||
spewBranch(m_buffer.getInstOrNull(ret), l);
|
spewBranch(m_buffer.getInstOrNull(ret), l);
|
||||||
|
@ -2394,6 +2392,9 @@ Assembler::as_b(Label* l, Condition c)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oom())
|
||||||
|
return BufferOffset();
|
||||||
|
|
||||||
int32_t old;
|
int32_t old;
|
||||||
BufferOffset ret;
|
BufferOffset ret;
|
||||||
if (l->used()) {
|
if (l->used()) {
|
||||||
|
@ -2410,6 +2411,10 @@ Assembler::as_b(Label* l, Condition c)
|
||||||
BOffImm inv;
|
BOffImm inv;
|
||||||
ret = as_b(inv, c, l);
|
ret = as_b(inv, c, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oom())
|
||||||
|
return BufferOffset();
|
||||||
|
|
||||||
DebugOnly<int32_t> check = l->use(ret.getOffset());
|
DebugOnly<int32_t> check = l->use(ret.getOffset());
|
||||||
MOZ_ASSERT(check == old);
|
MOZ_ASSERT(check == old);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2446,14 +2451,12 @@ Assembler::as_bl(BOffImm off, Condition c, Label* documentation)
|
||||||
BufferOffset
|
BufferOffset
|
||||||
Assembler::as_bl(Label* l, Condition c)
|
Assembler::as_bl(Label* l, Condition c)
|
||||||
{
|
{
|
||||||
if (m_buffer.oom()) {
|
|
||||||
BufferOffset ret;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (l->bound()) {
|
if (l->bound()) {
|
||||||
// Note only one instruction is emitted here, the NOP is overwritten.
|
// Note only one instruction is emitted here, the NOP is overwritten.
|
||||||
BufferOffset ret = allocBranchInst();
|
BufferOffset ret = allocBranchInst();
|
||||||
|
if (oom())
|
||||||
|
return BufferOffset();
|
||||||
|
|
||||||
as_bl(BufferOffset(l).diffB<BOffImm>(ret), c, ret);
|
as_bl(BufferOffset(l).diffB<BOffImm>(ret), c, ret);
|
||||||
#ifdef JS_DISASM_ARM
|
#ifdef JS_DISASM_ARM
|
||||||
spewBranch(m_buffer.getInstOrNull(ret), l);
|
spewBranch(m_buffer.getInstOrNull(ret), l);
|
||||||
|
@ -2461,6 +2464,9 @@ Assembler::as_bl(Label* l, Condition c)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oom())
|
||||||
|
return BufferOffset();
|
||||||
|
|
||||||
int32_t old;
|
int32_t old;
|
||||||
BufferOffset ret;
|
BufferOffset ret;
|
||||||
// See if the list was empty :(
|
// See if the list was empty :(
|
||||||
|
@ -2478,6 +2484,10 @@ Assembler::as_bl(Label* l, Condition c)
|
||||||
BOffImm inv;
|
BOffImm inv;
|
||||||
ret = as_bl(inv, c, l);
|
ret = as_bl(inv, c, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oom())
|
||||||
|
return BufferOffset();
|
||||||
|
|
||||||
DebugOnly<int32_t> check = l->use(ret.getOffset());
|
DebugOnly<int32_t> check = l->use(ret.getOffset());
|
||||||
MOZ_ASSERT(check == old);
|
MOZ_ASSERT(check == old);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -152,8 +152,13 @@ class AssemblerBuffer
|
||||||
|
|
||||||
bool ensureSpace(int size) {
|
bool ensureSpace(int size) {
|
||||||
// Space can exist in the most recent Slice.
|
// Space can exist in the most recent Slice.
|
||||||
if (tail && tail->length() + size <= tail->Capacity())
|
if (tail && tail->length() + size <= tail->Capacity()) {
|
||||||
|
// Simulate allocation failure even when we don't need a new slice.
|
||||||
|
if (js::oom::ShouldFailWithOOM())
|
||||||
|
return fail_oom();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Otherwise, a new Slice must be added.
|
// Otherwise, a new Slice must be added.
|
||||||
Slice* slice = newSlice(lifoAlloc_);
|
Slice* slice = newSlice(lifoAlloc_);
|
||||||
|
|
|
@ -704,6 +704,8 @@ struct AssemblerBufferWithConstantPools : public AssemblerBuffer<SliceSize, Inst
|
||||||
// Mark and emit the guard branch.
|
// Mark and emit the guard branch.
|
||||||
markNextAsBranch();
|
markNextAsBranch();
|
||||||
this->putBytes(guardSize_ * InstSize, nullptr);
|
this->putBytes(guardSize_ * InstSize, nullptr);
|
||||||
|
if (this->oom())
|
||||||
|
return;
|
||||||
BufferOffset afterPool = this->nextOffset();
|
BufferOffset afterPool = this->nextOffset();
|
||||||
Asm::WritePoolGuard(branch, this->getInst(branch), afterPool);
|
Asm::WritePoolGuard(branch, this->getInst(branch), afterPool);
|
||||||
|
|
||||||
|
@ -864,7 +866,7 @@ struct AssemblerBufferWithConstantPools : public AssemblerBuffer<SliceSize, Inst
|
||||||
}
|
}
|
||||||
|
|
||||||
inhibitNops_ = true;
|
inhibitNops_ = true;
|
||||||
while (sizeExcludingCurrentPool() & (alignment - 1))
|
while ((sizeExcludingCurrentPool() & (alignment - 1)) && !this->oom())
|
||||||
putInt(alignFillInst_);
|
putInt(alignFillInst_);
|
||||||
inhibitNops_ = false;
|
inhibitNops_ = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,8 +77,7 @@ MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 1, JSEXN_TYPEERR, "bad surrogate characte
|
||||||
MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large")
|
MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large")
|
||||||
MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}")
|
MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}")
|
||||||
MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 1, JSEXN_TYPEERR, "wrong constructor called for {0}")
|
MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 1, JSEXN_TYPEERR, "wrong constructor called for {0}")
|
||||||
MSG_DEF(JSMSG_BUILTIN_CTOR_NO_NEW, 1, JSEXN_NONE, "calling a builtin {0} constructor without new is deprecated and will be forbidden in ES6")
|
MSG_DEF(JSMSG_BUILTIN_CTOR_NO_NEW, 1, JSEXN_TYPEERR, "calling a builtin {0} constructor without new is forbidden")
|
||||||
MSG_DEF(JSMSG_BUILTIN_CTOR_NO_NEW_FATAL, 1, JSEXN_TYPEERR, "calling a builtin {0} constructor without new is forbidden")
|
|
||||||
MSG_DEF(JSMSG_PROTO_SETTING_SLOW, 0, JSEXN_NONE, "mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create")
|
MSG_DEF(JSMSG_PROTO_SETTING_SLOW, 0, JSEXN_NONE, "mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create")
|
||||||
MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 1, JSEXN_TYPEERR, "yield from closing generator {0}")
|
MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 1, JSEXN_TYPEERR, "yield from closing generator {0}")
|
||||||
MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
|
MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
|
||||||
|
|
|
@ -1242,10 +1242,8 @@ js::proxy(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
|
||||||
if (!args.isConstructing()) {
|
if (!ThrowIfNotConstructing(cx, args, "Proxy"))
|
||||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, "Proxy");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
return NewScriptedProxy(cx, args, "Proxy");
|
return NewScriptedProxy(cx, args, "Proxy");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1245,12 +1245,14 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
|
||||||
{
|
{
|
||||||
if (saveBytecode) {
|
if (saveBytecode) {
|
||||||
if (!JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates()) {
|
if (!JS::CompartmentOptionsRef(cx).cloneSingletons()) {
|
||||||
JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
|
JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
|
||||||
JSSMSG_CACHE_SINGLETON_FAILED);
|
JSSMSG_CACHE_SINGLETON_FAILED);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
JS::CompartmentOptionsRef(cx).setCloneSingletons(true);
|
|
||||||
|
// cloneSingletons implies that singletons are used as template objects.
|
||||||
|
MOZ_ASSERT(JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadBytecode) {
|
if (loadBytecode) {
|
||||||
|
@ -3805,6 +3807,11 @@ NewGlobal(JSContext* cx, unsigned argc, Value* vp)
|
||||||
if (v.isBoolean())
|
if (v.isBoolean())
|
||||||
options.setInvisibleToDebugger(v.toBoolean());
|
options.setInvisibleToDebugger(v.toBoolean());
|
||||||
|
|
||||||
|
if (!JS_GetProperty(cx, opts, "cloneSingletons", &v))
|
||||||
|
return false;
|
||||||
|
if (v.isBoolean())
|
||||||
|
options.setCloneSingletons(v.toBoolean());
|
||||||
|
|
||||||
if (!JS_GetProperty(cx, opts, "principal", &v))
|
if (!JS_GetProperty(cx, opts, "principal", &v))
|
||||||
return false;
|
return false;
|
||||||
if (!v.isUndefined()) {
|
if (!v.isUndefined()) {
|
||||||
|
|
|
@ -589,7 +589,7 @@ ThrowIfNotConstructing(JSContext *cx, const CallArgs &args, const char *builtinN
|
||||||
if (args.isConstructing())
|
if (args.isConstructing())
|
||||||
return true;
|
return true;
|
||||||
return JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, GetErrorMessage, nullptr,
|
return JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, GetErrorMessage, nullptr,
|
||||||
JSMSG_BUILTIN_CTOR_NO_NEW_FATAL, builtinName);
|
JSMSG_BUILTIN_CTOR_NO_NEW, builtinName);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
|
@ -29,11 +29,11 @@ namespace js {
|
||||||
*
|
*
|
||||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||||
*/
|
*/
|
||||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 313;
|
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 314;
|
||||||
static const uint32_t XDR_BYTECODE_VERSION =
|
static const uint32_t XDR_BYTECODE_VERSION =
|
||||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||||
|
|
||||||
static_assert(JSErr_Limit == 419,
|
static_assert(JSErr_Limit == 418,
|
||||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||||
"removed MSG_DEFs from js.msg, you should increment "
|
"removed MSG_DEFs from js.msg, you should increment "
|
||||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||||
|
|
|
@ -1254,6 +1254,8 @@ XRE_XPCShellMain(int argc, char** argv, char** envp)
|
||||||
|
|
||||||
NS_LogInit();
|
NS_LogInit();
|
||||||
|
|
||||||
|
mozilla::LogModule::Init();
|
||||||
|
|
||||||
// A initializer to initialize histogram collection
|
// A initializer to initialize histogram collection
|
||||||
// used by telemetry.
|
// used by telemetry.
|
||||||
UniquePtr<base::StatisticsRecorder> telStats =
|
UniquePtr<base::StatisticsRecorder> telStats =
|
||||||
|
|
|
@ -127,9 +127,14 @@ nsFileControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
|
nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
|
||||||
|
|
||||||
|
#if defined(ANDROID) || defined(MOZ_B2G)
|
||||||
|
bool isDirPicker = false;
|
||||||
|
#else
|
||||||
nsIContent* content = GetContent();
|
nsIContent* content = GetContent();
|
||||||
bool isDirPicker = Preferences::GetBool("dom.input.dirpicker", false) &&
|
bool isDirPicker =
|
||||||
content && content->HasAttr(kNameSpaceID_None, nsGkAtoms::directory);
|
Preferences::GetBool("dom.input.dirpicker", false) &&
|
||||||
|
content && content->HasAttr(kNameSpaceID_None, nsGkAtoms::directory);
|
||||||
|
#endif
|
||||||
|
|
||||||
RefPtr<HTMLInputElement> fileContent = HTMLInputElement::FromContentOrNull(mContent);
|
RefPtr<HTMLInputElement> fileContent = HTMLInputElement::FromContentOrNull(mContent);
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,6 @@
|
||||||
'./src/mediapipeline',
|
'./src/mediapipeline',
|
||||||
'./src/peerconnection',
|
'./src/peerconnection',
|
||||||
'./src/sdp/sipcc',
|
'./src/sdp/sipcc',
|
||||||
'../../../xpcom/base',
|
|
||||||
'../../../dom/base',
|
'../../../dom/base',
|
||||||
'../../../dom/media',
|
'../../../dom/media',
|
||||||
'../../../media/mtransport',
|
'../../../media/mtransport',
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
#include "nsIObserverService.h"
|
#include "nsIObserverService.h"
|
||||||
#include "nsIObserver.h"
|
#include "nsIObserver.h"
|
||||||
#include "mozilla/Services.h"
|
#include "mozilla/Services.h"
|
||||||
#include "StaticPtr.h"
|
#include "mozilla/StaticPtr.h"
|
||||||
|
|
||||||
#include "gmp-video-decode.h" // GMP_API_VIDEO_DECODER
|
#include "gmp-video-decode.h" // GMP_API_VIDEO_DECODER
|
||||||
#include "gmp-video-encode.h" // GMP_API_VIDEO_ENCODER
|
#include "gmp-video-encode.h" // GMP_API_VIDEO_ENCODER
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
#include "StaticPtr.h"
|
#include "mozilla/StaticPtr.h"
|
||||||
#include "PeerConnectionImpl.h"
|
#include "PeerConnectionImpl.h"
|
||||||
#include "mozIGeckoMediaPluginService.h"
|
#include "mozIGeckoMediaPluginService.h"
|
||||||
#include "nsIRunnable.h"
|
#include "nsIRunnable.h"
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/* -*- 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
|
||||||
|
|
||||||
|
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
|
|
@ -90,7 +90,6 @@ EventTokenBucket::EventTokenBucket(uint32_t eventsPerSecond,
|
||||||
, mFineGrainResetTimerArmed(false)
|
, mFineGrainResetTimerArmed(false)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(EventTokenBucket);
|
|
||||||
mLastUpdate = TimeStamp::Now();
|
mLastUpdate = TimeStamp::Now();
|
||||||
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
@ -112,7 +111,6 @@ EventTokenBucket::~EventTokenBucket()
|
||||||
SOCKET_LOG(("EventTokenBucket::dtor %p events=%d\n",
|
SOCKET_LOG(("EventTokenBucket::dtor %p events=%d\n",
|
||||||
this, mEvents.GetSize()));
|
this, mEvents.GetSize()));
|
||||||
|
|
||||||
MOZ_COUNT_DTOR(EventTokenBucket);
|
|
||||||
if (mTimer && mTimerArmed)
|
if (mTimer && mTimerArmed)
|
||||||
mTimer->Cancel();
|
mTimer->Cancel();
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#ifndef NetEventTokenBucket_h__
|
#ifndef NetEventTokenBucket_h__
|
||||||
#define NetEventTokenBucket_h__
|
#define NetEventTokenBucket_h__
|
||||||
|
|
||||||
|
#include "ARefBase.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsDeque.h"
|
#include "nsDeque.h"
|
||||||
#include "nsITimer.h"
|
#include "nsITimer.h"
|
||||||
|
@ -59,7 +60,7 @@ namespace net {
|
||||||
|
|
||||||
class EventTokenBucket;
|
class EventTokenBucket;
|
||||||
|
|
||||||
class ATokenBucketEvent
|
class ATokenBucketEvent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void OnTokenBucketAdmitted() = 0;
|
virtual void OnTokenBucketAdmitted() = 0;
|
||||||
|
@ -67,10 +68,8 @@ public:
|
||||||
|
|
||||||
class TokenBucketCancelable;
|
class TokenBucketCancelable;
|
||||||
|
|
||||||
class EventTokenBucket : public nsITimerCallback
|
class EventTokenBucket : public nsITimerCallback, public ARefBase
|
||||||
{
|
{
|
||||||
virtual ~EventTokenBucket();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
NS_DECL_NSITIMERCALLBACK
|
NS_DECL_NSITIMERCALLBACK
|
||||||
|
@ -93,6 +92,8 @@ public:
|
||||||
nsresult SubmitEvent(ATokenBucketEvent *event, nsICancelable **cancelable);
|
nsresult SubmitEvent(ATokenBucketEvent *event, nsICancelable **cancelable);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
virtual ~EventTokenBucket();
|
||||||
|
|
||||||
friend class RunNotifyEvent;
|
friend class RunNotifyEvent;
|
||||||
friend class SetTimerEvent;
|
friend class SetTimerEvent;
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ nsHttpConnectionMgr::PrintDiagnostics()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgPrintDiagnostics(int32_t, void *)
|
nsHttpConnectionMgr::OnMsgPrintDiagnostics(int32_t, ARefBase *)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "prinrval.h"
|
#include "prinrval.h"
|
||||||
#include "TunnelUtils.h"
|
#include "TunnelUtils.h"
|
||||||
#include "mozilla/Mutex.h"
|
#include "mozilla/Mutex.h"
|
||||||
|
#include "ARefBase.h"
|
||||||
|
|
||||||
#include "nsIAsyncInputStream.h"
|
#include "nsIAsyncInputStream.h"
|
||||||
#include "nsIAsyncOutputStream.h"
|
#include "nsIAsyncOutputStream.h"
|
||||||
|
@ -43,6 +44,7 @@ class nsHttpConnection final : public nsAHttpSegmentReader
|
||||||
, public nsITransportEventSink
|
, public nsITransportEventSink
|
||||||
, public nsIInterfaceRequestor
|
, public nsIInterfaceRequestor
|
||||||
, public NudgeTunnelCallback
|
, public NudgeTunnelCallback
|
||||||
|
, public ARefBase
|
||||||
{
|
{
|
||||||
virtual ~nsHttpConnection();
|
virtual ~nsHttpConnection();
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsStringFwd.h"
|
#include "nsStringFwd.h"
|
||||||
#include "mozilla/Logging.h"
|
#include "mozilla/Logging.h"
|
||||||
|
#include "ARefBase.h"
|
||||||
|
|
||||||
extern PRLogModuleInfo *gHttpLog;
|
extern PRLogModuleInfo *gHttpLog;
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ extern PRLogModuleInfo *gHttpLog;
|
||||||
|
|
||||||
namespace mozilla { namespace net {
|
namespace mozilla { namespace net {
|
||||||
|
|
||||||
class nsHttpConnectionInfo
|
class nsHttpConnectionInfo: public ARefBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
nsHttpConnectionInfo(const nsACString &originHost,
|
nsHttpConnectionInfo(const nsACString &originHost,
|
||||||
|
@ -162,7 +163,7 @@ private:
|
||||||
bool mUsingConnect; // if will use CONNECT with http proxy
|
bool mUsingConnect; // if will use CONNECT with http proxy
|
||||||
nsCString mNPNToken;
|
nsCString mNPNToken;
|
||||||
|
|
||||||
// for nsRefPtr
|
// for RefPtr
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHttpConnectionInfo)
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHttpConnectionInfo)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -147,12 +147,25 @@ nsHttpConnectionMgr::Init(uint16_t maxConns,
|
||||||
return EnsureSocketThreadTarget();
|
return EnsureSocketThreadTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BoolWrapper : public ARefBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BoolWrapper() : mBool(false) {}
|
||||||
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BoolWrapper)
|
||||||
|
|
||||||
|
public: // intentional!
|
||||||
|
bool mBool;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual ~BoolWrapper() {}
|
||||||
|
};
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::Shutdown()
|
nsHttpConnectionMgr::Shutdown()
|
||||||
{
|
{
|
||||||
LOG(("nsHttpConnectionMgr::Shutdown\n"));
|
LOG(("nsHttpConnectionMgr::Shutdown\n"));
|
||||||
|
|
||||||
bool shutdown = false;
|
RefPtr<BoolWrapper> shutdownWrapper = new BoolWrapper();
|
||||||
{
|
{
|
||||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||||
|
|
||||||
|
@ -161,7 +174,7 @@ nsHttpConnectionMgr::Shutdown()
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgShutdown,
|
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgShutdown,
|
||||||
0, &shutdown);
|
0, shutdownWrapper);
|
||||||
|
|
||||||
// release our reference to the STS to prevent further events
|
// release our reference to the STS to prevent further events
|
||||||
// from being posted. this is how we indicate that we are
|
// from being posted. this is how we indicate that we are
|
||||||
|
@ -176,14 +189,41 @@ nsHttpConnectionMgr::Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for shutdown event to complete
|
// wait for shutdown event to complete
|
||||||
while (!shutdown)
|
while (!shutdownWrapper->mBool) {
|
||||||
NS_ProcessNextEvent(NS_GetCurrentThread());
|
NS_ProcessNextEvent(NS_GetCurrentThread());
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ConnEvent : public nsRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConnEvent(nsHttpConnectionMgr *mgr,
|
||||||
|
nsConnEventHandler handler, int32_t iparam, ARefBase *vparam)
|
||||||
|
: mMgr(mgr)
|
||||||
|
, mHandler(handler)
|
||||||
|
, mIParam(iparam)
|
||||||
|
, mVParam(vparam) {}
|
||||||
|
|
||||||
|
NS_IMETHOD Run()
|
||||||
|
{
|
||||||
|
(mMgr->*mHandler)(mIParam, mVParam);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual ~ConnEvent() {}
|
||||||
|
|
||||||
|
RefPtr<nsHttpConnectionMgr> mMgr;
|
||||||
|
nsConnEventHandler mHandler;
|
||||||
|
int32_t mIParam;
|
||||||
|
RefPtr<ARefBase> mVParam;
|
||||||
|
};
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, int32_t iparam, void *vparam)
|
nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler,
|
||||||
|
int32_t iparam, ARefBase *vparam)
|
||||||
{
|
{
|
||||||
EnsureSocketThreadTarget();
|
EnsureSocketThreadTarget();
|
||||||
|
|
||||||
|
@ -195,7 +235,7 @@ nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, int32_t iparam, void
|
||||||
rv = NS_ERROR_NOT_INITIALIZED;
|
rv = NS_ERROR_NOT_INITIALIZED;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
nsCOMPtr<nsIRunnable> event = new nsConnEvent(this, handler, iparam, vparam);
|
nsCOMPtr<nsIRunnable> event = new ConnEvent(this, handler, iparam, vparam);
|
||||||
rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
|
rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -293,37 +333,22 @@ nsresult
|
||||||
nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, int32_t priority)
|
nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, int32_t priority)
|
||||||
{
|
{
|
||||||
LOG(("nsHttpConnectionMgr::AddTransaction [trans=%p %d]\n", trans, priority));
|
LOG(("nsHttpConnectionMgr::AddTransaction [trans=%p %d]\n", trans, priority));
|
||||||
|
return PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
|
||||||
NS_ADDREF(trans);
|
|
||||||
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
|
|
||||||
if (NS_FAILED(rv))
|
|
||||||
NS_RELEASE(trans);
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, int32_t priority)
|
nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, int32_t priority)
|
||||||
{
|
{
|
||||||
LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%p %d]\n", trans, priority));
|
LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%p %d]\n", trans, priority));
|
||||||
|
return PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority, trans);
|
||||||
NS_ADDREF(trans);
|
|
||||||
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority, trans);
|
|
||||||
if (NS_FAILED(rv))
|
|
||||||
NS_RELEASE(trans);
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
|
nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
|
||||||
{
|
{
|
||||||
LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%p reason=%x]\n", trans, reason));
|
LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%p reason=%x]\n", trans, reason));
|
||||||
|
return PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction,
|
||||||
NS_ADDREF(trans);
|
static_cast<int32_t>(reason), trans);
|
||||||
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction,
|
|
||||||
static_cast<int32_t>(reason), trans);
|
|
||||||
if (NS_FAILED(rv))
|
|
||||||
NS_RELEASE(trans);
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
@ -350,30 +375,18 @@ nsHttpConnectionMgr::VerifyTraffic()
|
||||||
return PostEvent(&nsHttpConnectionMgr::OnMsgVerifyTraffic);
|
return PostEvent(&nsHttpConnectionMgr::OnMsgVerifyTraffic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *aCI)
|
nsHttpConnectionMgr::DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *aCI)
|
||||||
{
|
{
|
||||||
RefPtr<nsHttpConnectionInfo> connInfo(aCI);
|
return PostEvent(&nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup,
|
||||||
|
0, aCI);
|
||||||
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup,
|
|
||||||
0, connInfo);
|
|
||||||
if (NS_SUCCEEDED(rv))
|
|
||||||
unused << connInfo.forget();
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpeculativeConnectArgs
|
class SpeculativeConnectArgs : public ARefBase
|
||||||
{
|
{
|
||||||
virtual ~SpeculativeConnectArgs() {}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SpeculativeConnectArgs() { mOverridesOK = false; }
|
SpeculativeConnectArgs() { mOverridesOK = false; }
|
||||||
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SpeculativeConnectArgs)
|
||||||
// Added manually so we can use nsRefPtr without inheriting from
|
|
||||||
// nsISupports
|
|
||||||
NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
|
|
||||||
NS_IMETHOD_(MozExternalRefCountType) Release(void);
|
|
||||||
|
|
||||||
public: // intentional!
|
public: // intentional!
|
||||||
RefPtr<NullHttpTransaction> mTrans;
|
RefPtr<NullHttpTransaction> mTrans;
|
||||||
|
@ -385,16 +398,11 @@ public: // intentional!
|
||||||
bool mIsFromPredictor;
|
bool mIsFromPredictor;
|
||||||
bool mAllow1918;
|
bool mAllow1918;
|
||||||
|
|
||||||
// As above, added manually so we can use nsRefPtr without inheriting from
|
private:
|
||||||
// nsISupports
|
virtual ~SpeculativeConnectArgs() {}
|
||||||
protected:
|
|
||||||
ThreadSafeAutoRefCnt mRefCnt;
|
|
||||||
NS_DECL_OWNINGTHREAD
|
NS_DECL_OWNINGTHREAD
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_IMPL_ADDREF(SpeculativeConnectArgs)
|
|
||||||
NS_IMPL_RELEASE(SpeculativeConnectArgs)
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
|
nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
|
||||||
nsIInterfaceRequestor *callbacks,
|
nsIInterfaceRequestor *callbacks,
|
||||||
|
@ -444,11 +452,7 @@ nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
|
||||||
overrider->GetAllow1918(&args->mAllow1918);
|
overrider->GetAllow1918(&args->mAllow1918);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv =
|
return PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, args);
|
||||||
PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, args);
|
|
||||||
if (NS_SUCCEEDED(rv))
|
|
||||||
unused << args.forget();
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
@ -465,58 +469,49 @@ nsresult
|
||||||
nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
|
nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
|
||||||
{
|
{
|
||||||
LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%p]\n", conn));
|
LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%p]\n", conn));
|
||||||
|
return PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
|
||||||
NS_ADDREF(conn);
|
|
||||||
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
|
|
||||||
if (NS_FAILED(rv))
|
|
||||||
NS_RELEASE(conn);
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A structure used to marshall 2 pointers across the various necessary
|
// A structure used to marshall 2 pointers across the various necessary
|
||||||
// threads to complete an HTTP upgrade.
|
// threads to complete an HTTP upgrade.
|
||||||
class nsCompleteUpgradeData
|
class nsCompleteUpgradeData : public ARefBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
nsCompleteUpgradeData(nsAHttpConnection *aConn,
|
nsCompleteUpgradeData(nsAHttpConnection *aConn,
|
||||||
nsIHttpUpgradeListener *aListener)
|
nsIHttpUpgradeListener *aListener)
|
||||||
: mConn(aConn), mUpgradeListener(aListener) {}
|
: mConn(aConn)
|
||||||
|
, mUpgradeListener(aListener) { }
|
||||||
|
|
||||||
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsCompleteUpgradeData)
|
||||||
|
|
||||||
RefPtr<nsAHttpConnection> mConn;
|
RefPtr<nsAHttpConnection> mConn;
|
||||||
nsCOMPtr<nsIHttpUpgradeListener> mUpgradeListener;
|
nsCOMPtr<nsIHttpUpgradeListener> mUpgradeListener;
|
||||||
|
private:
|
||||||
|
virtual ~nsCompleteUpgradeData() { }
|
||||||
};
|
};
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::CompleteUpgrade(nsAHttpConnection *aConn,
|
nsHttpConnectionMgr::CompleteUpgrade(nsAHttpConnection *aConn,
|
||||||
nsIHttpUpgradeListener *aUpgradeListener)
|
nsIHttpUpgradeListener *aUpgradeListener)
|
||||||
{
|
{
|
||||||
nsCompleteUpgradeData *data =
|
RefPtr<nsCompleteUpgradeData> data =
|
||||||
new nsCompleteUpgradeData(aConn, aUpgradeListener);
|
new nsCompleteUpgradeData(aConn, aUpgradeListener);
|
||||||
nsresult rv;
|
return PostEvent(&nsHttpConnectionMgr::OnMsgCompleteUpgrade, 0, data);
|
||||||
rv = PostEvent(&nsHttpConnectionMgr::OnMsgCompleteUpgrade, 0, data);
|
|
||||||
if (NS_FAILED(rv))
|
|
||||||
delete data;
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::UpdateParam(nsParamName name, uint16_t value)
|
nsHttpConnectionMgr::UpdateParam(nsParamName name, uint16_t value)
|
||||||
{
|
{
|
||||||
uint32_t param = (uint32_t(name) << 16) | uint32_t(value);
|
uint32_t param = (uint32_t(name) << 16) | uint32_t(value);
|
||||||
return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam, 0,
|
return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam,
|
||||||
(void *)(uintptr_t) param);
|
static_cast<int32_t>(param), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
|
nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
|
||||||
{
|
{
|
||||||
LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
|
LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
|
||||||
|
return PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
|
||||||
NS_ADDREF(ci);
|
|
||||||
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
|
|
||||||
if (NS_FAILED(rv))
|
|
||||||
NS_RELEASE(ci);
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
@ -527,25 +522,19 @@ nsHttpConnectionMgr::ProcessPendingQ()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket(int32_t, void *param)
|
nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket(int32_t, ARefBase *param)
|
||||||
{
|
{
|
||||||
RefPtr<EventTokenBucket> tokenBucket =
|
EventTokenBucket *tokenBucket = static_cast<EventTokenBucket *>(param);
|
||||||
dont_AddRef(static_cast<EventTokenBucket *>(param));
|
|
||||||
gHttpHandler->SetRequestTokenBucket(tokenBucket);
|
gHttpHandler->SetRequestTokenBucket(tokenBucket);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::UpdateRequestTokenBucket(EventTokenBucket *aBucket)
|
nsHttpConnectionMgr::UpdateRequestTokenBucket(EventTokenBucket *aBucket)
|
||||||
{
|
{
|
||||||
RefPtr<EventTokenBucket> bucket(aBucket);
|
|
||||||
|
|
||||||
// Call From main thread when a new EventTokenBucket has been made in order
|
// Call From main thread when a new EventTokenBucket has been made in order
|
||||||
// to post the new value to the socket thread.
|
// to post the new value to the socket thread.
|
||||||
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket,
|
return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket,
|
||||||
0, bucket);
|
0, aBucket);
|
||||||
if (NS_SUCCEEDED(rv))
|
|
||||||
unused << bucket.forget();
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PLDHashOperator
|
PLDHashOperator
|
||||||
|
@ -1196,7 +1185,7 @@ nsHttpConnectionMgr::SupportsPipelining(nsHttpConnectionInfo *ci)
|
||||||
|
|
||||||
// nsHttpPipelineFeedback used to hold references across events
|
// nsHttpPipelineFeedback used to hold references across events
|
||||||
|
|
||||||
class nsHttpPipelineFeedback
|
class nsHttpPipelineFeedback : public ARefBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
nsHttpPipelineFeedback(nsHttpConnectionInfo *ci,
|
nsHttpPipelineFeedback(nsHttpConnectionInfo *ci,
|
||||||
|
@ -1209,14 +1198,14 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~nsHttpPipelineFeedback()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<nsHttpConnectionInfo> mConnInfo;
|
RefPtr<nsHttpConnectionInfo> mConnInfo;
|
||||||
RefPtr<nsHttpConnection> mConn;
|
RefPtr<nsHttpConnection> mConn;
|
||||||
nsHttpConnectionMgr::PipelineFeedbackInfoType mInfo;
|
nsHttpConnectionMgr::PipelineFeedbackInfoType mInfo;
|
||||||
uint32_t mData;
|
uint32_t mData;
|
||||||
|
private:
|
||||||
|
~nsHttpPipelineFeedback() {}
|
||||||
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHttpPipelineFeedback)
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1230,18 +1219,13 @@ nsHttpConnectionMgr::PipelineFeedbackInfo(nsHttpConnectionInfo *ci,
|
||||||
|
|
||||||
// Post this to the socket thread if we are not running there already
|
// Post this to the socket thread if we are not running there already
|
||||||
if (PR_GetCurrentThread() != gSocketThread) {
|
if (PR_GetCurrentThread() != gSocketThread) {
|
||||||
nsHttpPipelineFeedback *fb = new nsHttpPipelineFeedback(ci, info,
|
RefPtr<nsHttpPipelineFeedback> fb =
|
||||||
conn, data);
|
new nsHttpPipelineFeedback(ci, info, conn, data);
|
||||||
|
PostEvent(&nsHttpConnectionMgr::OnMsgProcessFeedback, 0, fb);
|
||||||
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessFeedback,
|
|
||||||
0, fb);
|
|
||||||
if (NS_FAILED(rv))
|
|
||||||
delete fb;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsConnectionEntry *ent = mCT.Get(ci->HashKey());
|
nsConnectionEntry *ent = mCT.Get(ci->HashKey());
|
||||||
|
|
||||||
if (ent)
|
if (ent)
|
||||||
ent->OnPipelineFeedbackInfo(info, conn, data);
|
ent->OnPipelineFeedbackInfo(info, conn, data);
|
||||||
}
|
}
|
||||||
|
@ -1924,6 +1908,44 @@ nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// ConnectionHandle
|
||||||
|
//
|
||||||
|
// thin wrapper around a real connection, used to keep track of references
|
||||||
|
// to the connection to determine when the connection may be reused. the
|
||||||
|
// transaction (or pipeline) owns a reference to this handle. this extra
|
||||||
|
// layer of indirection greatly simplifies consumer code, avoiding the
|
||||||
|
// need for consumer code to know when to give the connection back to the
|
||||||
|
// connection manager.
|
||||||
|
//
|
||||||
|
class ConnectionHandle : public nsAHttpConnection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
|
NS_DECL_NSAHTTPCONNECTION(mConn)
|
||||||
|
|
||||||
|
explicit ConnectionHandle(nsHttpConnection *conn) { NS_ADDREF(mConn = conn); }
|
||||||
|
nsHttpConnection *mConn;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual ~ConnectionHandle();
|
||||||
|
};
|
||||||
|
|
||||||
|
nsAHttpConnection *
|
||||||
|
nsHttpConnectionMgr::MakeConnectionHandle(nsHttpConnection *aWrapped)
|
||||||
|
{
|
||||||
|
return new ConnectionHandle(aWrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionHandle::~ConnectionHandle()
|
||||||
|
{
|
||||||
|
if (mConn) {
|
||||||
|
gHttpHandler->ReclaimConnection(mConn);
|
||||||
|
NS_RELEASE(mConn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS0(ConnectionHandle)
|
||||||
|
|
||||||
// Use this method for dispatching nsAHttpTransction's. It can only safely be
|
// Use this method for dispatching nsAHttpTransction's. It can only safely be
|
||||||
// used upon first use of a connection when NPN has not negotiated SPDY vs
|
// used upon first use of a connection when NPN has not negotiated SPDY vs
|
||||||
|
@ -1963,7 +1985,7 @@ nsHttpConnectionMgr::DispatchAbstractTransaction(nsConnectionEntry *ent,
|
||||||
transaction = aTrans;
|
transaction = aTrans;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<nsConnectionHandle> handle = new nsConnectionHandle(conn);
|
RefPtr<ConnectionHandle> handle = new ConnectionHandle(conn);
|
||||||
|
|
||||||
// give the transaction the indirect reference to the connection.
|
// give the transaction the indirect reference to the connection.
|
||||||
transaction->SetConnection(handle);
|
transaction->SetConnection(handle);
|
||||||
|
@ -2253,7 +2275,7 @@ nsHttpConnectionMgr::ProcessSpdyPendingQCB(const nsACString &key,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ(int32_t, void *)
|
nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ(int32_t, ARefBase *)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ\n"));
|
LOG(("nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ\n"));
|
||||||
|
@ -2294,7 +2316,7 @@ nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgShutdown(int32_t, void *param)
|
nsHttpConnectionMgr::OnMsgShutdown(int32_t, ARefBase *param)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
|
LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
|
||||||
|
@ -2317,41 +2339,40 @@ nsHttpConnectionMgr::OnMsgShutdown(int32_t, void *param)
|
||||||
|
|
||||||
// signal shutdown complete
|
// signal shutdown complete
|
||||||
nsCOMPtr<nsIRunnable> runnable =
|
nsCOMPtr<nsIRunnable> runnable =
|
||||||
new nsConnEvent(this, &nsHttpConnectionMgr::OnMsgShutdownConfirm,
|
new ConnEvent(this, &nsHttpConnectionMgr::OnMsgShutdownConfirm,
|
||||||
0, param);
|
0, param);
|
||||||
NS_DispatchToMainThread(runnable);
|
NS_DispatchToMainThread(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgShutdownConfirm(int32_t priority, void *param)
|
nsHttpConnectionMgr::OnMsgShutdownConfirm(int32_t priority, ARefBase *param)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgShutdownConfirm\n"));
|
LOG(("nsHttpConnectionMgr::OnMsgShutdownConfirm\n"));
|
||||||
|
|
||||||
bool *shutdown = static_cast<bool*>(param);
|
BoolWrapper *shutdown = static_cast<BoolWrapper *>(param);
|
||||||
*shutdown = true;
|
shutdown->mBool = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgNewTransaction(int32_t priority, void *param)
|
nsHttpConnectionMgr::OnMsgNewTransaction(int32_t priority, ARefBase *param)
|
||||||
{
|
{
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
|
LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
|
||||||
|
|
||||||
nsHttpTransaction *trans = (nsHttpTransaction *) param;
|
nsHttpTransaction *trans = static_cast<nsHttpTransaction *>(param);
|
||||||
trans->SetPriority(priority);
|
trans->SetPriority(priority);
|
||||||
nsresult rv = ProcessNewTransaction(trans);
|
nsresult rv = ProcessNewTransaction(trans);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
trans->Close(rv); // for whatever its worth
|
trans->Close(rv); // for whatever its worth
|
||||||
NS_RELEASE(trans);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, void *param)
|
nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, ARefBase *param)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgReschedTransaction [trans=%p]\n", param));
|
LOG(("nsHttpConnectionMgr::OnMsgReschedTransaction [trans=%p]\n", param));
|
||||||
|
|
||||||
nsHttpTransaction *trans = (nsHttpTransaction *) param;
|
nsHttpTransaction *trans = static_cast<nsHttpTransaction *>(param);
|
||||||
trans->SetPriority(priority);
|
trans->SetPriority(priority);
|
||||||
|
|
||||||
nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
|
nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
|
||||||
|
@ -2364,19 +2385,16 @@ nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, void *param)
|
||||||
InsertTransactionSorted(ent->mPendingQ, trans);
|
InsertTransactionSorted(ent->mPendingQ, trans);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_RELEASE(trans);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, void *param)
|
nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, ARefBase *param)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
|
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
|
||||||
|
|
||||||
nsresult closeCode = static_cast<nsresult>(reason);
|
nsresult closeCode = static_cast<nsresult>(reason);
|
||||||
RefPtr<nsHttpTransaction> trans =
|
nsHttpTransaction *trans = static_cast<nsHttpTransaction *>(param);
|
||||||
dont_AddRef(static_cast<nsHttpTransaction *>(param));
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// if the transaction owns a connection and the transaction is not done,
|
// if the transaction owns a connection and the transaction is not done,
|
||||||
|
@ -2394,7 +2412,7 @@ nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, void *param)
|
||||||
int32_t index = ent->mPendingQ.IndexOf(trans);
|
int32_t index = ent->mPendingQ.IndexOf(trans);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]"
|
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]"
|
||||||
" found in pending queue\n", trans.get()));
|
" found in pending queue\n", trans));
|
||||||
ent->mPendingQ.RemoveElementAt(index);
|
ent->mPendingQ.RemoveElementAt(index);
|
||||||
nsHttpTransaction *temp = trans;
|
nsHttpTransaction *temp = trans;
|
||||||
NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
|
NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
|
||||||
|
@ -2429,7 +2447,7 @@ nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, void *param)
|
||||||
if (liveTransaction && liveTransaction->IsNullTransaction()) {
|
if (liveTransaction && liveTransaction->IsNullTransaction()) {
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p] "
|
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p] "
|
||||||
"also canceling Null Transaction %p on conn %p\n",
|
"also canceling Null Transaction %p on conn %p\n",
|
||||||
trans.get(), liveTransaction, activeConn));
|
trans, liveTransaction, activeConn));
|
||||||
activeConn->CloseTransaction(liveTransaction, closeCode);
|
activeConn->CloseTransaction(liveTransaction, closeCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2437,10 +2455,10 @@ nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, void *param)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgProcessPendingQ(int32_t, void *param)
|
nsHttpConnectionMgr::OnMsgProcessPendingQ(int32_t, ARefBase *param)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param;
|
nsHttpConnectionInfo *ci = static_cast<nsHttpConnectionInfo *>(param);
|
||||||
|
|
||||||
if (!ci) {
|
if (!ci) {
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=nullptr]\n"));
|
LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=nullptr]\n"));
|
||||||
|
@ -2459,30 +2477,22 @@ nsHttpConnectionMgr::OnMsgProcessPendingQ(int32_t, void *param)
|
||||||
// for the specified connection info. walk the connection table...
|
// for the specified connection info. walk the connection table...
|
||||||
mCT.Enumerate(ProcessOneTransactionCB, this);
|
mCT.Enumerate(ProcessOneTransactionCB, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_RELEASE(ci);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::CancelTransactions(nsHttpConnectionInfo *aCI, nsresult code)
|
nsHttpConnectionMgr::CancelTransactions(nsHttpConnectionInfo *ci, nsresult code)
|
||||||
{
|
{
|
||||||
RefPtr<nsHttpConnectionInfo> ci(aCI);
|
|
||||||
LOG(("nsHttpConnectionMgr::CancelTransactions %s\n",ci->HashKey().get()));
|
LOG(("nsHttpConnectionMgr::CancelTransactions %s\n",ci->HashKey().get()));
|
||||||
|
|
||||||
int32_t intReason = static_cast<int32_t>(code);
|
int32_t intReason = static_cast<int32_t>(code);
|
||||||
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransactions, intReason, ci);
|
return PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransactions, intReason, ci);
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
unused << ci.forget();
|
|
||||||
}
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgCancelTransactions(int32_t code, void *param)
|
nsHttpConnectionMgr::OnMsgCancelTransactions(int32_t code, ARefBase *param)
|
||||||
{
|
{
|
||||||
nsresult reason = static_cast<nsresult>(code);
|
nsresult reason = static_cast<nsresult>(code);
|
||||||
RefPtr<nsHttpConnectionInfo> ci =
|
nsHttpConnectionInfo *ci = static_cast<nsHttpConnectionInfo *>(param);
|
||||||
dont_AddRef(static_cast<nsHttpConnectionInfo *>(param));
|
|
||||||
nsConnectionEntry *ent = mCT.Get(ci->HashKey());
|
nsConnectionEntry *ent = mCT.Get(ci->HashKey());
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p\n",
|
LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p\n",
|
||||||
ci->HashKey().get(), ent));
|
ci->HashKey().get(), ent));
|
||||||
|
@ -2502,7 +2512,7 @@ nsHttpConnectionMgr::OnMsgCancelTransactions(int32_t code, void *param)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgPruneDeadConnections(int32_t, void *)
|
nsHttpConnectionMgr::OnMsgPruneDeadConnections(int32_t, ARefBase *)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
|
LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
|
||||||
|
@ -2517,7 +2527,7 @@ nsHttpConnectionMgr::OnMsgPruneDeadConnections(int32_t, void *)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgPruneNoTraffic(int32_t, void *)
|
nsHttpConnectionMgr::OnMsgPruneNoTraffic(int32_t, ARefBase *)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgPruneNoTraffic\n"));
|
LOG(("nsHttpConnectionMgr::OnMsgPruneNoTraffic\n"));
|
||||||
|
@ -2529,7 +2539,7 @@ nsHttpConnectionMgr::OnMsgPruneNoTraffic(int32_t, void *)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgVerifyTraffic(int32_t, void *)
|
nsHttpConnectionMgr::OnMsgVerifyTraffic(int32_t, ARefBase *)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgVerifyTraffic\n"));
|
LOG(("nsHttpConnectionMgr::OnMsgVerifyTraffic\n"));
|
||||||
|
@ -2561,13 +2571,12 @@ nsHttpConnectionMgr::OnMsgVerifyTraffic(int32_t, void *)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup(int32_t, void *param)
|
nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup(int32_t, ARefBase *param)
|
||||||
{
|
{
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup\n"));
|
LOG(("nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup\n"));
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
|
|
||||||
RefPtr<nsHttpConnectionInfo> ci =
|
nsHttpConnectionInfo *ci = static_cast<nsHttpConnectionInfo *>(param);
|
||||||
dont_AddRef(static_cast<nsHttpConnectionInfo *>(param));
|
|
||||||
|
|
||||||
mCT.Enumerate(ClosePersistentConnectionsCB, this);
|
mCT.Enumerate(ClosePersistentConnectionsCB, this);
|
||||||
if (ci)
|
if (ci)
|
||||||
|
@ -2575,12 +2584,12 @@ nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup(int32_t, void *param)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgReclaimConnection(int32_t, void *param)
|
nsHttpConnectionMgr::OnMsgReclaimConnection(int32_t, ARefBase *param)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
|
LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
|
||||||
|
|
||||||
nsHttpConnection *conn = (nsHttpConnection *) param;
|
nsHttpConnection *conn = static_cast<nsHttpConnection *>(param);
|
||||||
|
|
||||||
//
|
//
|
||||||
// 1) remove the connection from the active list
|
// 1) remove the connection from the active list
|
||||||
|
@ -2601,8 +2610,7 @@ nsHttpConnectionMgr::OnMsgReclaimConnection(int32_t, void *param)
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(ent);
|
MOZ_ASSERT(ent);
|
||||||
nsHttpConnectionInfo *ci = nullptr;
|
RefPtr<nsHttpConnectionInfo> ci(ent->mConnInfo);
|
||||||
NS_ADDREF(ci = ent->mConnInfo);
|
|
||||||
|
|
||||||
// If the connection is in the active list, remove that entry
|
// If the connection is in the active list, remove that entry
|
||||||
// and the reference held by the mActiveConns list.
|
// and the reference held by the mActiveConns list.
|
||||||
|
@ -2668,15 +2676,14 @@ nsHttpConnectionMgr::OnMsgReclaimConnection(int32_t, void *param)
|
||||||
conn->Close(NS_ERROR_ABORT);
|
conn->Close(NS_ERROR_ABORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
OnMsgProcessPendingQ(0, ci); // releases |ci|
|
OnMsgProcessPendingQ(0, ci);
|
||||||
NS_RELEASE(conn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgCompleteUpgrade(int32_t, void *param)
|
nsHttpConnectionMgr::OnMsgCompleteUpgrade(int32_t, ARefBase *param)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
nsCompleteUpgradeData *data = (nsCompleteUpgradeData *) param;
|
nsCompleteUpgradeData *data = static_cast<nsCompleteUpgradeData *>(param);
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgCompleteUpgrade "
|
LOG(("nsHttpConnectionMgr::OnMsgCompleteUpgrade "
|
||||||
"this=%p conn=%p listener=%p\n", this, data->mConn.get(),
|
"this=%p conn=%p listener=%p\n", this, data->mConn.get(),
|
||||||
data->mUpgradeListener.get()));
|
data->mUpgradeListener.get()));
|
||||||
|
@ -2694,14 +2701,14 @@ nsHttpConnectionMgr::OnMsgCompleteUpgrade(int32_t, void *param)
|
||||||
data->mUpgradeListener->OnTransportAvailable(socketTransport,
|
data->mUpgradeListener->OnTransportAvailable(socketTransport,
|
||||||
socketIn,
|
socketIn,
|
||||||
socketOut);
|
socketOut);
|
||||||
delete data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgUpdateParam(int32_t, void *param)
|
nsHttpConnectionMgr::OnMsgUpdateParam(int32_t inParam, ARefBase *)
|
||||||
{
|
{
|
||||||
uint16_t name = (NS_PTR_TO_INT32(param) & 0xFFFF0000) >> 16;
|
uint32_t param = static_cast<uint32_t>(inParam);
|
||||||
uint16_t value = NS_PTR_TO_INT32(param) & 0x0000FFFF;
|
uint16_t name = ((param) & 0xFFFF0000) >> 16;
|
||||||
|
uint16_t value = param & 0x0000FFFF;
|
||||||
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case MAX_CONNECTIONS:
|
case MAX_CONNECTIONS:
|
||||||
|
@ -2735,13 +2742,11 @@ nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgProcessFeedback(int32_t, void *param)
|
nsHttpConnectionMgr::OnMsgProcessFeedback(int32_t, ARefBase *param)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
nsHttpPipelineFeedback *fb = (nsHttpPipelineFeedback *)param;
|
nsHttpPipelineFeedback *fb = static_cast<nsHttpPipelineFeedback *>(param);
|
||||||
|
|
||||||
PipelineFeedbackInfo(fb->mConnInfo, fb->mInfo, fb->mConn, fb->mData);
|
PipelineFeedbackInfo(fb->mConnInfo, fb->mInfo, fb->mConn, fb->mData);
|
||||||
delete fb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read Timeout Tick handlers
|
// Read Timeout Tick handlers
|
||||||
|
@ -2854,19 +2859,6 @@ nsHttpConnectionMgr::TimeoutTickCB(const nsACString &key,
|
||||||
return PL_DHASH_NEXT;
|
return PL_DHASH_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// nsHttpConnectionMgr::nsConnectionHandle
|
|
||||||
|
|
||||||
nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
|
|
||||||
{
|
|
||||||
if (mConn) {
|
|
||||||
gHttpHandler->ReclaimConnection(mConn);
|
|
||||||
NS_RELEASE(mConn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
|
|
||||||
|
|
||||||
// GetOrCreateConnectionEntry finds a ent for a particular CI for use in
|
// GetOrCreateConnectionEntry finds a ent for a particular CI for use in
|
||||||
// dispatching a transaction according to these rules
|
// dispatching a transaction according to these rules
|
||||||
// 1] use an ent that matches the ci that can be dispatched immediately
|
// 1] use an ent that matches the ci that can be dispatched immediately
|
||||||
|
@ -2907,36 +2899,34 @@ nsHttpConnectionMgr::GetOrCreateConnectionEntry(nsHttpConnectionInfo *specificCI
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::nsConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
|
ConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
|
||||||
nsHttpRequestHead *req,
|
nsHttpRequestHead *req,
|
||||||
nsHttpResponseHead *resp,
|
nsHttpResponseHead *resp,
|
||||||
bool *reset)
|
bool *reset)
|
||||||
{
|
{
|
||||||
return mConn->OnHeadersAvailable(trans, req, resp, reset);
|
return mConn->OnHeadersAvailable(trans, req, resp, reset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
|
ConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
|
||||||
{
|
{
|
||||||
mConn->CloseTransaction(trans, reason);
|
mConn->CloseTransaction(trans, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::
|
ConnectionHandle::TakeTransport(nsISocketTransport **aTransport,
|
||||||
nsConnectionHandle::TakeTransport(nsISocketTransport **aTransport,
|
nsIAsyncInputStream **aInputStream,
|
||||||
nsIAsyncInputStream **aInputStream,
|
nsIAsyncOutputStream **aOutputStream)
|
||||||
nsIAsyncOutputStream **aOutputStream)
|
|
||||||
{
|
{
|
||||||
return mConn->TakeTransport(aTransport, aInputStream, aOutputStream);
|
return mConn->TakeTransport(aTransport, aInputStream, aOutputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param)
|
nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, ARefBase *param)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||||
|
|
||||||
RefPtr<SpeculativeConnectArgs> args =
|
SpeculativeConnectArgs *args = static_cast<SpeculativeConnectArgs *>(param);
|
||||||
dont_AddRef(static_cast<SpeculativeConnectArgs *>(param));
|
|
||||||
|
|
||||||
LOG(("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s]\n",
|
LOG(("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s]\n",
|
||||||
args->mTrans->ConnectionInfo()->HashKey().get()));
|
args->mTrans->ConnectionInfo()->HashKey().get()));
|
||||||
|
@ -2980,25 +2970,25 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
|
ConnectionHandle::IsPersistent()
|
||||||
{
|
{
|
||||||
return mConn->IsPersistent();
|
return mConn->IsPersistent();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsHttpConnectionMgr::nsConnectionHandle::IsReused()
|
ConnectionHandle::IsReused()
|
||||||
{
|
{
|
||||||
return mConn->IsReused();
|
return mConn->IsReused();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpConnectionMgr::nsConnectionHandle::DontReuse()
|
ConnectionHandle::DontReuse()
|
||||||
{
|
{
|
||||||
mConn->DontReuse();
|
mConn->DontReuse();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, uint32_t bufLen)
|
ConnectionHandle::PushBack(const char *buf, uint32_t bufLen)
|
||||||
{
|
{
|
||||||
return mConn->PushBack(buf, bufLen);
|
return mConn->PushBack(buf, bufLen);
|
||||||
}
|
}
|
||||||
|
@ -3451,10 +3441,7 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||||
// otherwise just put this in the persistent connection pool
|
// otherwise just put this in the persistent connection pool
|
||||||
LOG(("nsHalfOpenSocket::OnOutputStreamReady no transaction match "
|
LOG(("nsHalfOpenSocket::OnOutputStreamReady no transaction match "
|
||||||
"returning conn %p to pool\n", conn.get()));
|
"returning conn %p to pool\n", conn.get()));
|
||||||
RefPtr<nsHttpConnection> copy(conn);
|
gHttpHandler->ConnMgr()->OnMsgReclaimConnection(0, conn);
|
||||||
// forget() to effectively addref because onmsg*() will drop a ref
|
|
||||||
gHttpHandler->ConnMgr()->OnMsgReclaimConnection(
|
|
||||||
0, conn.forget().take());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3569,7 +3556,7 @@ nsHttpConnectionMgr::nsHalfOpenSocket::GetInterface(const nsIID &iid,
|
||||||
|
|
||||||
|
|
||||||
nsHttpConnection *
|
nsHttpConnection *
|
||||||
nsHttpConnectionMgr::nsConnectionHandle::TakeHttpConnection()
|
ConnectionHandle::TakeHttpConnection()
|
||||||
{
|
{
|
||||||
// return our connection object to the caller and clear it internally
|
// return our connection object to the caller and clear it internally
|
||||||
// do not drop our reference - the caller now owns it.
|
// do not drop our reference - the caller now owns it.
|
||||||
|
@ -3581,19 +3568,19 @@ nsHttpConnectionMgr::nsConnectionHandle::TakeHttpConnection()
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
nsHttpConnectionMgr::nsConnectionHandle::CancelPipeline(nsresult reason)
|
ConnectionHandle::CancelPipeline(nsresult reason)
|
||||||
{
|
{
|
||||||
// no pipeline to cancel
|
// no pipeline to cancel
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAHttpTransaction::Classifier
|
nsAHttpTransaction::Classifier
|
||||||
nsHttpConnectionMgr::nsConnectionHandle::Classification()
|
ConnectionHandle::Classification()
|
||||||
{
|
{
|
||||||
if (mConn)
|
if (mConn)
|
||||||
return mConn->Classification();
|
return mConn->Classification();
|
||||||
|
|
||||||
LOG(("nsConnectionHandle::Classification this=%p "
|
LOG(("ConnectionHandle::Classification this=%p "
|
||||||
"has null mConn using CLASS_SOLO default", this));
|
"has null mConn using CLASS_SOLO default", this));
|
||||||
return nsAHttpTransaction::CLASS_SOLO;
|
return nsAHttpTransaction::CLASS_SOLO;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "mozilla/TimeStamp.h"
|
#include "mozilla/TimeStamp.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
#include "AlternateServices.h"
|
#include "AlternateServices.h"
|
||||||
|
#include "ARefBase.h"
|
||||||
|
|
||||||
#include "nsIObserver.h"
|
#include "nsIObserver.h"
|
||||||
#include "nsITimer.h"
|
#include "nsITimer.h"
|
||||||
|
@ -31,6 +32,10 @@ struct HttpRetParams;
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// message handlers have this signature
|
||||||
|
class nsHttpConnectionMgr;
|
||||||
|
typedef void (nsHttpConnectionMgr:: *nsConnEventHandler)(int32_t, ARefBase *);
|
||||||
|
|
||||||
class nsHttpConnectionMgr final : public nsIObserver
|
class nsHttpConnectionMgr final : public nsIObserver
|
||||||
, public AltSvcCache
|
, public AltSvcCache
|
||||||
{
|
{
|
||||||
|
@ -389,32 +394,9 @@ private:
|
||||||
void ResetIPFamilyPreference();
|
void ResetIPFamilyPreference();
|
||||||
};
|
};
|
||||||
|
|
||||||
// nsConnectionHandle
|
|
||||||
//
|
|
||||||
// thin wrapper around a real connection, used to keep track of references
|
|
||||||
// to the connection to determine when the connection may be reused. the
|
|
||||||
// transaction (or pipeline) owns a reference to this handle. this extra
|
|
||||||
// layer of indirection greatly simplifies consumer code, avoiding the
|
|
||||||
// need for consumer code to know when to give the connection back to the
|
|
||||||
// connection manager.
|
|
||||||
//
|
|
||||||
class nsConnectionHandle : public nsAHttpConnection
|
|
||||||
{
|
|
||||||
virtual ~nsConnectionHandle();
|
|
||||||
|
|
||||||
public:
|
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
|
||||||
NS_DECL_NSAHTTPCONNECTION(mConn)
|
|
||||||
|
|
||||||
explicit nsConnectionHandle(nsHttpConnection *conn) { NS_ADDREF(mConn = conn); }
|
|
||||||
|
|
||||||
nsHttpConnection *mConn;
|
|
||||||
};
|
|
||||||
public:
|
public:
|
||||||
static nsAHttpConnection *MakeConnectionHandle(nsHttpConnection *aWrapped)
|
static nsAHttpConnection *MakeConnectionHandle(nsHttpConnection *aWrapped);
|
||||||
{
|
|
||||||
return new nsConnectionHandle(aWrapped);
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// nsHalfOpenSocket is used to hold the state of an opening TCP socket
|
// nsHalfOpenSocket is used to hold the state of an opening TCP socket
|
||||||
|
@ -522,7 +504,7 @@ private:
|
||||||
uint16_t mMaxRequestDelay; // in seconds
|
uint16_t mMaxRequestDelay; // in seconds
|
||||||
uint16_t mMaxPipelinedRequests;
|
uint16_t mMaxPipelinedRequests;
|
||||||
uint16_t mMaxOptimisticPipelinedRequests;
|
uint16_t mMaxOptimisticPipelinedRequests;
|
||||||
bool mIsShuttingDown;
|
Atomic<bool, mozilla::Relaxed> mIsShuttingDown;
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
// NOTE: these members are only accessed on the socket transport thread
|
// NOTE: these members are only accessed on the socket transport thread
|
||||||
|
@ -594,72 +576,30 @@ private:
|
||||||
const nsACString &key, nsAutoPtr<nsConnectionEntry> &ent,
|
const nsACString &key, nsAutoPtr<nsConnectionEntry> &ent,
|
||||||
void *closure);
|
void *closure);
|
||||||
|
|
||||||
// message handlers have this signature
|
// used to marshall events to the socket transport thread.
|
||||||
typedef void (nsHttpConnectionMgr:: *nsConnEventHandler)(int32_t, void *);
|
|
||||||
|
|
||||||
// nsConnEvent
|
|
||||||
//
|
|
||||||
// subclass of nsRunnable used to marshall events to the socket transport
|
|
||||||
// thread. this class is used to implement PostEvent.
|
|
||||||
//
|
|
||||||
class nsConnEvent;
|
|
||||||
friend class nsConnEvent;
|
|
||||||
class nsConnEvent : public nsRunnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
nsConnEvent(nsHttpConnectionMgr *mgr,
|
|
||||||
nsConnEventHandler handler,
|
|
||||||
int32_t iparam,
|
|
||||||
void *vparam)
|
|
||||||
: mMgr(mgr)
|
|
||||||
, mHandler(handler)
|
|
||||||
, mIParam(iparam)
|
|
||||||
, mVParam(vparam)
|
|
||||||
{
|
|
||||||
NS_ADDREF(mMgr);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHOD Run()
|
|
||||||
{
|
|
||||||
(mMgr->*mHandler)(mIParam, mVParam);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual ~nsConnEvent()
|
|
||||||
{
|
|
||||||
NS_RELEASE(mMgr);
|
|
||||||
}
|
|
||||||
|
|
||||||
nsHttpConnectionMgr *mMgr;
|
|
||||||
nsConnEventHandler mHandler;
|
|
||||||
int32_t mIParam;
|
|
||||||
void *mVParam;
|
|
||||||
};
|
|
||||||
|
|
||||||
nsresult PostEvent(nsConnEventHandler handler,
|
nsresult PostEvent(nsConnEventHandler handler,
|
||||||
int32_t iparam = 0,
|
int32_t iparam = 0,
|
||||||
void *vparam = nullptr);
|
ARefBase *vparam = nullptr);
|
||||||
|
|
||||||
// message handlers
|
// message handlers
|
||||||
void OnMsgShutdown (int32_t, void *);
|
void OnMsgShutdown (int32_t, ARefBase *);
|
||||||
void OnMsgShutdownConfirm (int32_t, void *);
|
void OnMsgShutdownConfirm (int32_t, ARefBase *);
|
||||||
void OnMsgNewTransaction (int32_t, void *);
|
void OnMsgNewTransaction (int32_t, ARefBase *);
|
||||||
void OnMsgReschedTransaction (int32_t, void *);
|
void OnMsgReschedTransaction (int32_t, ARefBase *);
|
||||||
void OnMsgCancelTransaction (int32_t, void *);
|
void OnMsgCancelTransaction (int32_t, ARefBase *);
|
||||||
void OnMsgCancelTransactions (int32_t, void *);
|
void OnMsgCancelTransactions (int32_t, ARefBase *);
|
||||||
void OnMsgProcessPendingQ (int32_t, void *);
|
void OnMsgProcessPendingQ (int32_t, ARefBase *);
|
||||||
void OnMsgPruneDeadConnections (int32_t, void *);
|
void OnMsgPruneDeadConnections (int32_t, ARefBase *);
|
||||||
void OnMsgSpeculativeConnect (int32_t, void *);
|
void OnMsgSpeculativeConnect (int32_t, ARefBase *);
|
||||||
void OnMsgReclaimConnection (int32_t, void *);
|
void OnMsgReclaimConnection (int32_t, ARefBase *);
|
||||||
void OnMsgCompleteUpgrade (int32_t, void *);
|
void OnMsgCompleteUpgrade (int32_t, ARefBase *);
|
||||||
void OnMsgUpdateParam (int32_t, void *);
|
void OnMsgUpdateParam (int32_t, ARefBase *);
|
||||||
void OnMsgDoShiftReloadConnectionCleanup (int32_t, void *);
|
void OnMsgDoShiftReloadConnectionCleanup (int32_t, ARefBase *);
|
||||||
void OnMsgProcessFeedback (int32_t, void *);
|
void OnMsgProcessFeedback (int32_t, ARefBase *);
|
||||||
void OnMsgProcessAllSpdyPendingQ (int32_t, void *);
|
void OnMsgProcessAllSpdyPendingQ (int32_t, ARefBase *);
|
||||||
void OnMsgUpdateRequestTokenBucket (int32_t, void *);
|
void OnMsgUpdateRequestTokenBucket (int32_t, ARefBase *);
|
||||||
void OnMsgVerifyTraffic (int32_t, void *);
|
void OnMsgVerifyTraffic (int32_t, ARefBase *);
|
||||||
void OnMsgPruneNoTraffic (int32_t, void *);
|
void OnMsgPruneNoTraffic (int32_t, ARefBase *);
|
||||||
|
|
||||||
// Total number of active connections in all of the ConnectionEntry objects
|
// Total number of active connections in all of the ConnectionEntry objects
|
||||||
// that are accessed from mCT connection table.
|
// that are accessed from mCT connection table.
|
||||||
|
@ -711,7 +651,7 @@ private:
|
||||||
void *closure);
|
void *closure);
|
||||||
|
|
||||||
// For diagnostics
|
// For diagnostics
|
||||||
void OnMsgPrintDiagnostics(int32_t, void *);
|
void OnMsgPrintDiagnostics(int32_t, ARefBase *);
|
||||||
static PLDHashOperator PrintDiagnosticsCB(const nsACString &key,
|
static PLDHashOperator PrintDiagnosticsCB(const nsACString &key,
|
||||||
nsAutoPtr<nsConnectionEntry> &ent,
|
nsAutoPtr<nsConnectionEntry> &ent,
|
||||||
void *closure);
|
void *closure);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "TimingStruct.h"
|
#include "TimingStruct.h"
|
||||||
#include "Http2Push.h"
|
#include "Http2Push.h"
|
||||||
#include "mozilla/net/DNS.h"
|
#include "mozilla/net/DNS.h"
|
||||||
|
#include "ARefBase.h"
|
||||||
|
|
||||||
#ifdef MOZ_WIDGET_GONK
|
#ifdef MOZ_WIDGET_GONK
|
||||||
#include "nsINetworkInterface.h"
|
#include "nsINetworkInterface.h"
|
||||||
|
@ -45,6 +46,7 @@ class nsHttpTransaction final : public nsAHttpTransaction
|
||||||
, public ATokenBucketEvent
|
, public ATokenBucketEvent
|
||||||
, public nsIInputStreamCallback
|
, public nsIInputStreamCallback
|
||||||
, public nsIOutputStreamCallback
|
, public nsIOutputStreamCallback
|
||||||
|
, public ARefBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
|
|
|
@ -5,24 +5,32 @@
|
||||||
|
|
||||||
__version__ = '1.0.0'
|
__version__ = '1.0.0'
|
||||||
|
|
||||||
from .marionette_test import MarionetteTestCase, MarionetteJSTestCase, CommonTestCase, expectedFailure, skip, SkipTest
|
from .marionette_test import (
|
||||||
from .runner import (
|
CommonTestCase,
|
||||||
B2GTestCaseMixin,
|
expectedFailure,
|
||||||
B2GTestResultMixin,
|
MarionetteJSTestCase,
|
||||||
BaseMarionetteArguments,
|
MarionetteTestCase,
|
||||||
BaseMarionetteTestRunner,
|
skip,
|
||||||
BrowserMobProxyTestCaseMixin,
|
SkipTest,
|
||||||
EnduranceArguments,
|
skip_unless_protocol,
|
||||||
EnduranceTestCaseMixin,
|
)
|
||||||
HTMLReportingArguments,
|
from .runner import (
|
||||||
HTMLReportingTestResultMixin,
|
B2GTestCaseMixin,
|
||||||
HTMLReportingTestRunnerMixin,
|
B2GTestResultMixin,
|
||||||
Marionette,
|
BaseMarionetteArguments,
|
||||||
MarionetteTest,
|
BaseMarionetteTestRunner,
|
||||||
MarionetteTestResult,
|
BrowserMobProxyTestCaseMixin,
|
||||||
MarionetteTextTestRunner,
|
EnduranceArguments,
|
||||||
MemoryEnduranceTestCaseMixin,
|
EnduranceTestCaseMixin,
|
||||||
TestManifest,
|
HTMLReportingArguments,
|
||||||
TestResult,
|
HTMLReportingTestResultMixin,
|
||||||
TestResultCollection
|
HTMLReportingTestRunnerMixin,
|
||||||
|
Marionette,
|
||||||
|
MarionetteTest,
|
||||||
|
MarionetteTestResult,
|
||||||
|
MarionetteTextTestRunner,
|
||||||
|
MemoryEnduranceTestCaseMixin,
|
||||||
|
TestManifest,
|
||||||
|
TestResult,
|
||||||
|
TestResultCollection,
|
||||||
)
|
)
|
||||||
|
|
|
@ -56,9 +56,7 @@ class _UnexpectedSuccess(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def skip(reason):
|
def skip(reason):
|
||||||
"""
|
"""Unconditionally skip a test."""
|
||||||
Unconditionally skip a test.
|
|
||||||
"""
|
|
||||||
def decorator(test_item):
|
def decorator(test_item):
|
||||||
if not isinstance(test_item, (type, types.ClassType)):
|
if not isinstance(test_item, (type, types.ClassType)):
|
||||||
@functools.wraps(test_item)
|
@functools.wraps(test_item)
|
||||||
|
@ -81,12 +79,18 @@ def expectedFailure(func):
|
||||||
raise _UnexpectedSuccess
|
raise _UnexpectedSuccess
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
def skip_if_desktop(target):
|
||||||
|
def wrapper(self, *args, **kwargs):
|
||||||
|
if self.marionette.session_capabilities.get('b2g') is None:
|
||||||
|
raise SkipTest('skipping due to desktop')
|
||||||
|
return target(self, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
def skip_if_b2g(target):
|
def skip_if_b2g(target):
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
if self.marionette.session_capabilities.get('b2g') == True:
|
if self.marionette.session_capabilities.get('b2g') == True:
|
||||||
raise SkipTest('skipping due to b2g')
|
raise SkipTest('skipping due to b2g')
|
||||||
return target(self, *args, **kwargs)
|
return target(self, *args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def skip_if_e10s(target):
|
def skip_if_e10s(target):
|
||||||
|
@ -104,6 +108,19 @@ def skip_if_e10s(target):
|
||||||
return target(self, *args, **kwargs)
|
return target(self, *args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
def skip_unless_protocol(predicate):
|
||||||
|
"""Given a predicate passed the current protocol level, skip the
|
||||||
|
test if the predicate does not match."""
|
||||||
|
def decorator(test_item):
|
||||||
|
@functools.wraps(test_item)
|
||||||
|
def skip_wrapper(self):
|
||||||
|
level = self.marionette.client.protocol
|
||||||
|
if not predicate(level):
|
||||||
|
raise SkipTest('skipping because protocol level is %s' % level)
|
||||||
|
return self
|
||||||
|
return skip_wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
def parameterized(func_suffix, *args, **kwargs):
|
def parameterized(func_suffix, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
A decorator that can generate methods given a base method and some data.
|
A decorator that can generate methods given a base method and some data.
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# 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/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
from marionette import MarionetteTestCase
|
from unittest import skip
|
||||||
from marionette_driver.errors import MarionetteException
|
|
||||||
|
from marionette.marionette_test import MarionetteTestCase, skip_if_desktop, skip_unless_protocol
|
||||||
|
from marionette_driver.errors import MarionetteException, JavascriptException
|
||||||
|
|
||||||
|
|
||||||
class TestEmulatorContent(MarionetteTestCase):
|
class TestEmulatorContent(MarionetteTestCase):
|
||||||
|
@skip_if_desktop
|
||||||
def test_emulator_cmd(self):
|
def test_emulator_cmd(self):
|
||||||
self.marionette.set_script_timeout(10000)
|
self.marionette.set_script_timeout(10000)
|
||||||
expected = ["<build>",
|
expected = ["<build>",
|
||||||
|
@ -17,6 +19,7 @@ class TestEmulatorContent(MarionetteTestCase):
|
||||||
""");
|
""");
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
@skip_if_desktop
|
||||||
def test_emulator_shell(self):
|
def test_emulator_shell(self):
|
||||||
self.marionette.set_script_timeout(10000)
|
self.marionette.set_script_timeout(10000)
|
||||||
expected = ["Hello World!"]
|
expected = ["Hello World!"]
|
||||||
|
@ -25,6 +28,7 @@ class TestEmulatorContent(MarionetteTestCase):
|
||||||
""");
|
""");
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
@skip_if_desktop
|
||||||
def test_emulator_order(self):
|
def test_emulator_order(self):
|
||||||
self.marionette.set_script_timeout(10000)
|
self.marionette.set_script_timeout(10000)
|
||||||
self.assertRaises(MarionetteException,
|
self.assertRaises(MarionetteException,
|
||||||
|
@ -35,21 +39,17 @@ class TestEmulatorContent(MarionetteTestCase):
|
||||||
|
|
||||||
|
|
||||||
class TestEmulatorChrome(TestEmulatorContent):
|
class TestEmulatorChrome(TestEmulatorContent):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestEmulatorChrome, self).setUp()
|
super(TestEmulatorChrome, self).setUp()
|
||||||
self.marionette.set_context("chrome")
|
self.marionette.set_context("chrome")
|
||||||
|
|
||||||
|
|
||||||
class TestEmulatorScreen(MarionetteTestCase):
|
class TestEmulatorScreen(MarionetteTestCase):
|
||||||
|
@skip_if_desktop
|
||||||
def setUp(self):
|
def test_emulator_orientation(self):
|
||||||
MarionetteTestCase.setUp(self)
|
|
||||||
|
|
||||||
self.screen = self.marionette.emulator.screen
|
self.screen = self.marionette.emulator.screen
|
||||||
self.screen.initialize()
|
self.screen.initialize()
|
||||||
|
|
||||||
def test_emulator_orientation(self):
|
|
||||||
self.assertEqual(self.screen.orientation, self.screen.SO_PORTRAIT_PRIMARY,
|
self.assertEqual(self.screen.orientation, self.screen.SO_PORTRAIT_PRIMARY,
|
||||||
'Orientation has been correctly initialized.')
|
'Orientation has been correctly initialized.')
|
||||||
|
|
||||||
|
@ -68,3 +68,70 @@ class TestEmulatorScreen(MarionetteTestCase):
|
||||||
self.screen.orientation = self.screen.SO_PORTRAIT_PRIMARY
|
self.screen.orientation = self.screen.SO_PORTRAIT_PRIMARY
|
||||||
self.assertEqual(self.screen.orientation, self.screen.SO_PORTRAIT_PRIMARY,
|
self.assertEqual(self.screen.orientation, self.screen.SO_PORTRAIT_PRIMARY,
|
||||||
'Orientation has been set to portrait-primary')
|
'Orientation has been set to portrait-primary')
|
||||||
|
|
||||||
|
|
||||||
|
class TestEmulatorCallbacks(MarionetteTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
MarionetteTestCase.setUp(self)
|
||||||
|
self.original_emulator_cmd = self.marionette._emulator_cmd
|
||||||
|
self.original_emulator_shell = self.marionette._emulator_shell
|
||||||
|
self.marionette._emulator_cmd = self.mock_emulator_cmd
|
||||||
|
self.marionette._emulator_shell = self.mock_emulator_shell
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.marionette._emulator_cmd = self.original_emulator_cmd
|
||||||
|
self.marionette._emulator_shell = self.original_emulator_shell
|
||||||
|
|
||||||
|
def mock_emulator_cmd(self, *args):
|
||||||
|
return self.marionette._send_emulator_result("cmd response")
|
||||||
|
|
||||||
|
def mock_emulator_shell(self, *args):
|
||||||
|
return self.marionette._send_emulator_result("shell response")
|
||||||
|
|
||||||
|
def _execute_emulator(self, action, args):
|
||||||
|
script = "%s(%s, function(res) { marionetteScriptFinished(res); })" % (action, args)
|
||||||
|
return self.marionette.execute_async_script(script)
|
||||||
|
|
||||||
|
def emulator_cmd(self, cmd):
|
||||||
|
return self._execute_emulator("runEmulatorCmd", escape(cmd))
|
||||||
|
|
||||||
|
def emulator_shell(self, *args):
|
||||||
|
js_args = ", ".join(map(escape, args))
|
||||||
|
js_args = "[%s]" % js_args
|
||||||
|
return self._execute_emulator("runEmulatorShell", js_args)
|
||||||
|
|
||||||
|
def test_emulator_cmd_content(self):
|
||||||
|
with self.marionette.using_context("content"):
|
||||||
|
res = self.emulator_cmd("yo")
|
||||||
|
self.assertEqual("cmd response", res)
|
||||||
|
|
||||||
|
def test_emulator_shell_content(self):
|
||||||
|
with self.marionette.using_context("content"):
|
||||||
|
res = self.emulator_shell("first", "second")
|
||||||
|
self.assertEqual("shell response", res)
|
||||||
|
|
||||||
|
@skip_unless_protocol(lambda level: level >= 3)
|
||||||
|
def test_emulator_result_error_content(self):
|
||||||
|
with self.marionette.using_context("content"):
|
||||||
|
with self.assertRaisesRegexp(JavascriptException, "TypeError"):
|
||||||
|
self.marionette.execute_async_script("runEmulatorCmd()")
|
||||||
|
|
||||||
|
def test_emulator_cmd_chrome(self):
|
||||||
|
with self.marionette.using_context("chrome"):
|
||||||
|
res = self.emulator_cmd("yo")
|
||||||
|
self.assertEqual("cmd response", res)
|
||||||
|
|
||||||
|
def test_emulator_shell_chrome(self):
|
||||||
|
with self.marionette.using_context("chrome"):
|
||||||
|
res = self.emulator_shell("first", "second")
|
||||||
|
self.assertEqual("shell response", res)
|
||||||
|
|
||||||
|
@skip_unless_protocol(lambda level: level >= 3)
|
||||||
|
def test_emulator_result_error_chrome(self):
|
||||||
|
with self.marionette.using_context("chrome"):
|
||||||
|
with self.assertRaisesRegexp(JavascriptException, "TypeError"):
|
||||||
|
self.marionette.execute_async_script("runEmulatorCmd()")
|
||||||
|
|
||||||
|
|
||||||
|
def escape(word):
|
||||||
|
return "'%s'" % word
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
from marionette import MarionetteTestCase
|
from marionette import MarionetteTestCase
|
||||||
|
from marionette_driver.by import By
|
||||||
from marionette_driver.errors import (JavascriptException,
|
from marionette_driver.errors import (JavascriptException,
|
||||||
NoSuchFrameException)
|
NoSuchFrameException)
|
||||||
|
|
||||||
|
@ -134,3 +135,56 @@ class TestSwitchFrame(MarionetteTestCase):
|
||||||
test_html = self.marionette.absolute_url("test.html")
|
test_html = self.marionette.absolute_url("test.html")
|
||||||
self.marionette.navigate(test_html)
|
self.marionette.navigate(test_html)
|
||||||
self.assertEqual("Marionette Test", self.marionette.title)
|
self.assertEqual("Marionette Test", self.marionette.title)
|
||||||
|
|
||||||
|
def test_switch_to_parent_frame(self):
|
||||||
|
frame_html = self.marionette.absolute_url("frameset.html")
|
||||||
|
self.marionette.navigate(frame_html)
|
||||||
|
frame = self.marionette.find_element("name", "third")
|
||||||
|
self.marionette.switch_to_frame(frame)
|
||||||
|
|
||||||
|
# If we don't find the following element we aren't on the right page
|
||||||
|
self.marionette.find_element(By.ID, "checky")
|
||||||
|
form_page_title = self.marionette.execute_script("return document.title")
|
||||||
|
self.assertEqual("We Leave From Here", form_page_title)
|
||||||
|
|
||||||
|
self.marionette.switch_to_parent_frame()
|
||||||
|
|
||||||
|
current_page_title = self.marionette.execute_script("return document.title")
|
||||||
|
self.assertEqual("Unique title", current_page_title)
|
||||||
|
|
||||||
|
def test_switch_to_parent_frame_from_default_context_is_a_noop(self):
|
||||||
|
formpage = self.marionette.absolute_url("formPage.html")
|
||||||
|
self.marionette.navigate(formpage)
|
||||||
|
|
||||||
|
self.marionette.switch_to_parent_frame()
|
||||||
|
|
||||||
|
form_page_title = self.marionette.execute_script("return document.title")
|
||||||
|
self.assertEqual("We Leave From Here", form_page_title)
|
||||||
|
|
||||||
|
def test_should_be_able_to_switch_to_parent_from_second_level(self):
|
||||||
|
frame_html = self.marionette.absolute_url("frameset.html")
|
||||||
|
self.marionette.navigate(frame_html)
|
||||||
|
frame = self.marionette.find_element(By.NAME, "fourth")
|
||||||
|
self.marionette.switch_to_frame(frame)
|
||||||
|
|
||||||
|
second_level = self.marionette.find_element(By.NAME, "child1")
|
||||||
|
self.marionette.switch_to_frame(second_level)
|
||||||
|
self.marionette.find_element(By.NAME, "myCheckBox")
|
||||||
|
|
||||||
|
self.marionette.switch_to_parent_frame()
|
||||||
|
|
||||||
|
second_level = self.marionette.find_element(By.NAME, "child1")
|
||||||
|
|
||||||
|
def test_should_be_able_to_switch_to_parent_from_iframe(self):
|
||||||
|
frame_html = self.marionette.absolute_url("test_iframe.html")
|
||||||
|
self.marionette.navigate(frame_html)
|
||||||
|
frame = self.marionette.find_element(By.ID, "test_iframe")
|
||||||
|
self.marionette.switch_to_frame(frame)
|
||||||
|
|
||||||
|
current_page_title = self.marionette.execute_script("return document.title")
|
||||||
|
self.assertEqual("Marionette Test", current_page_title)
|
||||||
|
|
||||||
|
self.marionette.switch_to_parent_frame()
|
||||||
|
|
||||||
|
parent_page_title = self.marionette.execute_script("return document.title")
|
||||||
|
self.assertEqual("Marionette IFrame Test", parent_page_title)
|
||||||
|
|
|
@ -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/.
|
||||||
|
|
||||||
|
import json
|
||||||
|
from marionette import MarionetteTestCase, skip_unless_protocol
|
||||||
|
from marionette_transport import (
|
||||||
|
Command,
|
||||||
|
Proto2Command,
|
||||||
|
Proto2Response,
|
||||||
|
Response
|
||||||
|
)
|
||||||
|
|
||||||
|
get_current_url = ("getCurrentUrl", None)
|
||||||
|
execute_script = ("executeScript", {"script": "return 42"})
|
||||||
|
|
||||||
|
|
||||||
|
class TestMessageSequencing(MarionetteTestCase):
|
||||||
|
@property
|
||||||
|
def last_id(self):
|
||||||
|
return self.marionette.client.last_id
|
||||||
|
|
||||||
|
@last_id.setter
|
||||||
|
def last_id(self, new_id):
|
||||||
|
self.marionette.client.last_id = new_id
|
||||||
|
|
||||||
|
def send(self, name, params):
|
||||||
|
self.last_id = self.last_id + 1
|
||||||
|
cmd = Command(self.last_id, name, params)
|
||||||
|
self.marionette.client.send(cmd)
|
||||||
|
return self.last_id
|
||||||
|
|
||||||
|
@skip_unless_protocol(lambda level: level >= 3)
|
||||||
|
def test_discard_older_messages(self):
|
||||||
|
first = self.send(*get_current_url)
|
||||||
|
second = self.send(*execute_script)
|
||||||
|
resp = self.marionette.client.receive()
|
||||||
|
self.assertEqual(second, resp.id)
|
||||||
|
|
||||||
|
@skip_unless_protocol(lambda level: level >= 3)
|
||||||
|
def test_last_id_incremented(self):
|
||||||
|
before = self.last_id
|
||||||
|
self.send(*get_current_url)
|
||||||
|
self.assertGreater(self.last_id, before)
|
||||||
|
|
||||||
|
|
||||||
|
class MessageTestCase(MarionetteTestCase):
|
||||||
|
def assert_attr(self, obj, attr):
|
||||||
|
self.assertTrue(hasattr(obj, attr),
|
||||||
|
"object does not have attribute %s" % attr)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCommand(MessageTestCase):
|
||||||
|
def create(self, msgid="msgid", name="name", params="params"):
|
||||||
|
return Command(msgid, name, params)
|
||||||
|
|
||||||
|
def test_initialise(self):
|
||||||
|
cmd = self.create()
|
||||||
|
self.assert_attr(cmd, "id")
|
||||||
|
self.assert_attr(cmd, "name")
|
||||||
|
self.assert_attr(cmd, "params")
|
||||||
|
self.assertEqual("msgid", cmd.id)
|
||||||
|
self.assertEqual("name", cmd.name)
|
||||||
|
self.assertEqual("params", cmd.params)
|
||||||
|
|
||||||
|
def test_stringify(self):
|
||||||
|
cmd = self.create()
|
||||||
|
string = str(cmd)
|
||||||
|
self.assertIn("Command", string)
|
||||||
|
self.assertIn("id=msgid", string)
|
||||||
|
self.assertIn("name=name", string)
|
||||||
|
self.assertIn("params=params", string)
|
||||||
|
|
||||||
|
def test_to_msg(self):
|
||||||
|
cmd = self.create()
|
||||||
|
msg = json.loads(cmd.to_msg())
|
||||||
|
self.assertEquals(msg[0], Command.TYPE)
|
||||||
|
self.assertEquals(msg[1], "msgid")
|
||||||
|
self.assertEquals(msg[2], "name")
|
||||||
|
self.assertEquals(msg[3], "params")
|
||||||
|
|
||||||
|
def test_from_msg(self):
|
||||||
|
msg = [Command.TYPE, "msgid", "name", "params"]
|
||||||
|
payload = json.dumps(msg)
|
||||||
|
cmd = Command.from_msg(payload)
|
||||||
|
self.assertEquals(msg[1], cmd.id)
|
||||||
|
self.assertEquals(msg[2], cmd.name)
|
||||||
|
self.assertEquals(msg[3], cmd.params)
|
||||||
|
|
||||||
|
|
||||||
|
class TestResponse(MessageTestCase):
|
||||||
|
def create(self, msgid="msgid", error="error", result="result"):
|
||||||
|
return Response(msgid, error, result)
|
||||||
|
|
||||||
|
def test_initialise(self):
|
||||||
|
resp = self.create()
|
||||||
|
self.assert_attr(resp, "id")
|
||||||
|
self.assert_attr(resp, "error")
|
||||||
|
self.assert_attr(resp, "result")
|
||||||
|
self.assertEqual("msgid", resp.id)
|
||||||
|
self.assertEqual("error", resp.error)
|
||||||
|
self.assertEqual("result", resp.result)
|
||||||
|
|
||||||
|
def test_stringify(self):
|
||||||
|
resp = self.create()
|
||||||
|
string = str(resp)
|
||||||
|
self.assertIn("Response", string)
|
||||||
|
self.assertIn("id=msgid", string)
|
||||||
|
self.assertIn("error=error", string)
|
||||||
|
self.assertIn("result=result", string)
|
||||||
|
|
||||||
|
def test_to_msg(self):
|
||||||
|
resp = self.create()
|
||||||
|
msg = json.loads(resp.to_msg())
|
||||||
|
self.assertEquals(msg[0], Response.TYPE)
|
||||||
|
self.assertEquals(msg[1], "msgid")
|
||||||
|
self.assertEquals(msg[2], "error")
|
||||||
|
self.assertEquals(msg[3], "result")
|
||||||
|
|
||||||
|
def test_from_msg(self):
|
||||||
|
msg = [Response.TYPE, "msgid", "error", "result"]
|
||||||
|
payload = json.dumps(msg)
|
||||||
|
resp = Response.from_msg(payload)
|
||||||
|
self.assertEquals(msg[1], resp.id)
|
||||||
|
self.assertEquals(msg[2], resp.error)
|
||||||
|
self.assertEquals(msg[3], resp.result)
|
||||||
|
|
||||||
|
|
||||||
|
class TestProto2Command(MessageTestCase):
|
||||||
|
def create(self, name="name", params="params"):
|
||||||
|
return Proto2Command(name, params)
|
||||||
|
|
||||||
|
def test_initialise(self):
|
||||||
|
cmd = self.create()
|
||||||
|
self.assert_attr(cmd, "id")
|
||||||
|
self.assert_attr(cmd, "name")
|
||||||
|
self.assert_attr(cmd, "params")
|
||||||
|
self.assertEqual(None, cmd.id)
|
||||||
|
self.assertEqual("name", cmd.name)
|
||||||
|
self.assertEqual("params", cmd.params)
|
||||||
|
|
||||||
|
def test_from_data_emulator_cmd(self):
|
||||||
|
data = {"emulator_cmd": "emulator_cmd"}
|
||||||
|
cmd = Proto2Command.from_data(data)
|
||||||
|
self.assertEqual("runEmulatorCmd", cmd.name)
|
||||||
|
self.assertEqual(data, cmd.params)
|
||||||
|
|
||||||
|
def test_from_data_emulator_shell(self):
|
||||||
|
data = {"emulator_shell": "emulator_shell"}
|
||||||
|
cmd = Proto2Command.from_data(data)
|
||||||
|
self.assertEqual("runEmulatorShell", cmd.name)
|
||||||
|
self.assertEqual(data, cmd.params)
|
||||||
|
|
||||||
|
def test_from_data_unknown(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
cmd = Proto2Command.from_data({})
|
||||||
|
|
||||||
|
|
||||||
|
class TestProto2Response(MessageTestCase):
|
||||||
|
def create(self, error="error", result="result"):
|
||||||
|
return Proto2Response(error, result)
|
||||||
|
|
||||||
|
def test_initialise(self):
|
||||||
|
resp = self.create()
|
||||||
|
self.assert_attr(resp, "id")
|
||||||
|
self.assert_attr(resp, "error")
|
||||||
|
self.assert_attr(resp, "result")
|
||||||
|
self.assertEqual(None, resp.id)
|
||||||
|
self.assertEqual("error", resp.error)
|
||||||
|
self.assertEqual("result", resp.result)
|
||||||
|
|
||||||
|
def test_from_data_error(self):
|
||||||
|
data = {"error": "error"}
|
||||||
|
resp = Proto2Response.from_data(data)
|
||||||
|
self.assertEqual(data, resp.error)
|
||||||
|
self.assertEqual(None, resp.result)
|
||||||
|
|
||||||
|
def test_from_data_result(self):
|
||||||
|
resp = Proto2Response.from_data("result")
|
||||||
|
self.assertEqual(None, resp.error)
|
||||||
|
self.assertEqual("result", resp.result)
|
|
@ -51,7 +51,6 @@ disabled = "Bug 896046"
|
||||||
|
|
||||||
[test_log.py]
|
[test_log.py]
|
||||||
[test_emulator.py]
|
[test_emulator.py]
|
||||||
browser = false
|
|
||||||
qemu = true
|
qemu = true
|
||||||
|
|
||||||
[test_about_pages.py]
|
[test_about_pages.py]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<html>
|
<html>
|
||||||
<head></head>
|
<head></head>
|
||||||
<frameset cols="*, *">
|
<frameset cols="*, *">
|
||||||
<frame name="child1" src="page/10"/>
|
<frame name="child1" src="test.html"/>
|
||||||
<frame name="child2" src="page/11"/>
|
<frame name="child2" src="test.html"/>
|
||||||
</frameset>
|
</frameset>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1622,6 +1622,13 @@ GeckoDriver.prototype.getActiveFrame = function(cmd, resp) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
GeckoDriver.prototype.switchToParentFrame = function (cmd, resp) {
|
||||||
|
let res = yield this.listener.switchToParentFrame();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch to a given frame within the current window.
|
* Switch to a given frame within the current window.
|
||||||
*
|
*
|
||||||
|
@ -2966,6 +2973,7 @@ GeckoDriver.prototype.commands = {
|
||||||
"setWindowPosition": GeckoDriver.prototype.setWindowPosition,
|
"setWindowPosition": GeckoDriver.prototype.setWindowPosition,
|
||||||
"getActiveFrame": GeckoDriver.prototype.getActiveFrame,
|
"getActiveFrame": GeckoDriver.prototype.getActiveFrame,
|
||||||
"switchToFrame": GeckoDriver.prototype.switchToFrame,
|
"switchToFrame": GeckoDriver.prototype.switchToFrame,
|
||||||
|
"switchToParentFrame": GeckoDriver.prototype.switchToParentFrame,
|
||||||
"switchToWindow": GeckoDriver.prototype.switchToWindow,
|
"switchToWindow": GeckoDriver.prototype.switchToWindow,
|
||||||
"switchToShadowRoot": GeckoDriver.prototype.switchToShadowRoot,
|
"switchToShadowRoot": GeckoDriver.prototype.switchToShadowRoot,
|
||||||
"deleteSession": GeckoDriver.prototype.deleteSession,
|
"deleteSession": GeckoDriver.prototype.deleteSession,
|
||||||
|
|
|
@ -15,7 +15,7 @@ from contextlib import contextmanager
|
||||||
|
|
||||||
from decorators import do_crash_check
|
from decorators import do_crash_check
|
||||||
from keys import Keys
|
from keys import Keys
|
||||||
from marionette_transport import MarionetteTransport
|
import marionette_transport as transport
|
||||||
|
|
||||||
from mozrunner import B2GEmulatorRunner
|
from mozrunner import B2GEmulatorRunner
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import errors
|
||||||
WEBELEMENT_KEY = "ELEMENT"
|
WEBELEMENT_KEY = "ELEMENT"
|
||||||
W3C_WEBELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf"
|
W3C_WEBELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf"
|
||||||
|
|
||||||
|
|
||||||
class HTMLElement(object):
|
class HTMLElement(object):
|
||||||
"""
|
"""
|
||||||
Represents a DOM Element.
|
Represents a DOM Element.
|
||||||
|
@ -623,16 +624,15 @@ class Marionette(object):
|
||||||
self.port = self.emulator.setup_port_forwarding(remote_port=self.port)
|
self.port = self.emulator.setup_port_forwarding(remote_port=self.port)
|
||||||
assert(self.emulator.wait_for_port(self.port)), "Timed out waiting for port!"
|
assert(self.emulator.wait_for_port(self.port)), "Timed out waiting for port!"
|
||||||
|
|
||||||
self.client = MarionetteTransport(
|
|
||||||
self.host,
|
|
||||||
self.port,
|
|
||||||
self.socket_timeout)
|
|
||||||
|
|
||||||
if emulator:
|
if emulator:
|
||||||
if busybox:
|
if busybox:
|
||||||
self.emulator.install_busybox(busybox=busybox)
|
self.emulator.install_busybox(busybox=busybox)
|
||||||
self.emulator.wait_for_system_message(self)
|
self.emulator.wait_for_system_message(self)
|
||||||
|
|
||||||
|
# for callbacks from a protocol level 2 or lower remote,
|
||||||
|
# we store the callback ID so it can be used by _send_emulator_result
|
||||||
|
self.emulator_callback_id = None
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
if self.session:
|
if self.session:
|
||||||
try:
|
try:
|
||||||
|
@ -667,23 +667,24 @@ class Marionette(object):
|
||||||
s.close()
|
s.close()
|
||||||
|
|
||||||
def wait_for_port(self, timeout=60):
|
def wait_for_port(self, timeout=60):
|
||||||
return MarionetteTransport.wait_for_port(self.host,
|
return transport.wait_for_port(self.host, self.port, timeout=timeout)
|
||||||
self.port,
|
|
||||||
timeout=timeout)
|
|
||||||
|
|
||||||
@do_crash_check
|
@do_crash_check
|
||||||
def _send_message(self, command, body=None, key=None):
|
def _send_message(self, name, params=None, key=None):
|
||||||
if not self.session_id and command != "newSession":
|
if not self.session_id and name != "newSession":
|
||||||
raise errors.MarionetteException("Please start a session")
|
raise errors.MarionetteException("Please start a session")
|
||||||
|
|
||||||
message = {"name": command}
|
|
||||||
if body:
|
|
||||||
message["parameters"] = body
|
|
||||||
|
|
||||||
packet = json.dumps(message)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resp = self.client.send(packet)
|
if self.protocol < 3:
|
||||||
|
data = {"name": name}
|
||||||
|
if params:
|
||||||
|
data["parameters"] = params
|
||||||
|
self.client.send(data)
|
||||||
|
msg = self.client.receive()
|
||||||
|
|
||||||
|
else:
|
||||||
|
msg = self.client.request(name, params)
|
||||||
|
|
||||||
except IOError:
|
except IOError:
|
||||||
if self.instance and not hasattr(self.instance, 'detached'):
|
if self.instance and not hasattr(self.instance, 'detached'):
|
||||||
# If we've launched the binary we've connected to, wait
|
# If we've launched the binary we've connected to, wait
|
||||||
|
@ -697,28 +698,24 @@ class Marionette(object):
|
||||||
self.client.close()
|
self.client.close()
|
||||||
raise errors.TimeoutException("Connection timed out")
|
raise errors.TimeoutException("Connection timed out")
|
||||||
|
|
||||||
# Process any emulator commands that are sent from a script
|
if isinstance(msg, transport.Command):
|
||||||
# while it's executing
|
if msg.name == "runEmulatorCmd":
|
||||||
if isinstance(resp, dict) and any (k in resp for k in ("emulator_cmd", "emulator_shell")):
|
self.emulator_callback_id = msg.params.get("id")
|
||||||
while True:
|
msg = self._emulator_cmd(msg.params["emulator_cmd"])
|
||||||
id = resp.get("id")
|
elif msg.name == "runEmulatorShell":
|
||||||
cmd = resp.get("emulator_cmd")
|
self.emulator_callback_id = msg.params.get("id")
|
||||||
shell = resp.get("emulator_shell")
|
msg = self._emulator_shell(msg.params["emulator_shell"])
|
||||||
if cmd:
|
else:
|
||||||
resp = self._emulator_cmd(id, cmd)
|
raise IOError("Unknown command: %s" % msg)
|
||||||
continue
|
|
||||||
if shell:
|
|
||||||
resp = self._emulator_shell(id, shell)
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
|
|
||||||
if "error" in resp:
|
res, err = msg.result, msg.error
|
||||||
self._handle_error(resp)
|
if err:
|
||||||
|
self._handle_error(err)
|
||||||
|
|
||||||
if key is not None:
|
if key is not None:
|
||||||
return self._unwrap_response(resp.get(key))
|
return self._unwrap_response(res.get(key))
|
||||||
else:
|
else:
|
||||||
return self._unwrap_response(resp)
|
return self._unwrap_response(res)
|
||||||
|
|
||||||
def _unwrap_response(self, value):
|
def _unwrap_response(self, value):
|
||||||
if isinstance(value, dict) and \
|
if isinstance(value, dict) and \
|
||||||
|
@ -732,15 +729,15 @@ class Marionette(object):
|
||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def _emulator_cmd(self, id, cmd):
|
def _emulator_cmd(self, cmd):
|
||||||
if not self.emulator:
|
if not self.emulator:
|
||||||
raise errors.MarionetteException(
|
raise errors.MarionetteException(
|
||||||
"No emulator in this test to run command against")
|
"No emulator in this test to run command against")
|
||||||
payload = cmd.encode("ascii")
|
payload = cmd.encode("ascii")
|
||||||
result = self.emulator._run_telnet(payload)
|
result = self.emulator._run_telnet(payload)
|
||||||
return self._send_emulator_result(id, result)
|
return self._send_emulator_result(result)
|
||||||
|
|
||||||
def _emulator_shell(self, id, args):
|
def _emulator_shell(self, args):
|
||||||
if not isinstance(args, list) or not self.emulator:
|
if not isinstance(args, list) or not self.emulator:
|
||||||
raise errors.MarionetteException(
|
raise errors.MarionetteException(
|
||||||
"No emulator in this test to run shell command against")
|
"No emulator in this test to run shell command against")
|
||||||
|
@ -748,25 +745,32 @@ class Marionette(object):
|
||||||
self.emulator.dm.shell(args, buf)
|
self.emulator.dm.shell(args, buf)
|
||||||
result = str(buf.getvalue()[0:-1]).rstrip().splitlines()
|
result = str(buf.getvalue()[0:-1]).rstrip().splitlines()
|
||||||
buf.close()
|
buf.close()
|
||||||
return self._send_emulator_result(id, result)
|
return self._send_emulator_result(result)
|
||||||
|
|
||||||
def _send_emulator_result(self, id, result):
|
def _send_emulator_result(self, result):
|
||||||
return self.client.send(json.dumps({"name": "emulatorCmdResult",
|
if self.protocol < 3:
|
||||||
"id": id,
|
body = {"name": "emulatorCmdResult",
|
||||||
"result": result}))
|
"id": self.emulator_callback_id,
|
||||||
|
"result": result}
|
||||||
def _handle_error(self, resp):
|
self.client.send(body)
|
||||||
if self.protocol == 1:
|
return self.client.receive()
|
||||||
if "error" not in resp or not isinstance(resp["error"], dict):
|
|
||||||
raise errors.MarionetteException(
|
|
||||||
"Malformed packet, expected key 'error' to be a dict: %s" % resp)
|
|
||||||
error = resp["error"].get("status")
|
|
||||||
message = resp["error"].get("message")
|
|
||||||
stacktrace = resp["error"].get("stacktrace")
|
|
||||||
else:
|
else:
|
||||||
error = resp["error"]
|
return self.client.respond(result)
|
||||||
message = resp["message"]
|
|
||||||
stacktrace = resp["stacktrace"]
|
def _handle_error(self, obj):
|
||||||
|
if self.protocol == 1:
|
||||||
|
if "error" not in obj or not isinstance(obj["error"], dict):
|
||||||
|
raise errors.MarionetteException(
|
||||||
|
"Malformed packet, expected key 'error' to be a dict: %s" % obj)
|
||||||
|
error = obj["error"].get("status")
|
||||||
|
message = obj["error"].get("message")
|
||||||
|
stacktrace = obj["error"].get("stacktrace")
|
||||||
|
|
||||||
|
else:
|
||||||
|
error = obj["error"]
|
||||||
|
message = obj["message"]
|
||||||
|
stacktrace = obj["stacktrace"]
|
||||||
|
|
||||||
raise errors.lookup(error)(message, stacktrace=stacktrace)
|
raise errors.lookup(error)(message, stacktrace=stacktrace)
|
||||||
|
|
||||||
def _reset_timeouts(self):
|
def _reset_timeouts(self):
|
||||||
|
@ -1132,6 +1136,10 @@ class Marionette(object):
|
||||||
# We're managing a binary which has terminated, so restart it.
|
# We're managing a binary which has terminated, so restart it.
|
||||||
self.instance.restart()
|
self.instance.restart()
|
||||||
|
|
||||||
|
self.client = transport.TcpTransport(
|
||||||
|
self.host,
|
||||||
|
self.port,
|
||||||
|
self.socket_timeout)
|
||||||
self.protocol, _ = self.client.connect()
|
self.protocol, _ = self.client.connect()
|
||||||
self.wait_for_port(timeout=timeout)
|
self.wait_for_port(timeout=timeout)
|
||||||
|
|
||||||
|
@ -1308,7 +1316,6 @@ class Marionette(object):
|
||||||
|
|
||||||
marionette.set_context(marionette.CONTEXT_CHROME)
|
marionette.set_context(marionette.CONTEXT_CHROME)
|
||||||
"""
|
"""
|
||||||
assert(context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT)
|
|
||||||
if context not in [self.CONTEXT_CHROME, self.CONTEXT_CONTENT]:
|
if context not in [self.CONTEXT_CHROME, self.CONTEXT_CONTENT]:
|
||||||
raise ValueError("Unknown context: %s" % context)
|
raise ValueError("Unknown context: %s" % context)
|
||||||
self._send_message("setContext", {"value": context})
|
self._send_message("setContext", {"value": context})
|
||||||
|
@ -1368,6 +1375,12 @@ class Marionette(object):
|
||||||
"""Switch the current context to page's default content."""
|
"""Switch the current context to page's default content."""
|
||||||
return self.switch_to_frame()
|
return self.switch_to_frame()
|
||||||
|
|
||||||
|
def switch_to_parent_frame(self):
|
||||||
|
"""
|
||||||
|
Switch to the Parent Frame
|
||||||
|
"""
|
||||||
|
self._send_message("switchToParentFrame")
|
||||||
|
|
||||||
def switch_to_frame(self, frame=None, focus=True):
|
def switch_to_frame(self, frame=None, focus=True):
|
||||||
"""Switch the current context to the specified frame. Subsequent
|
"""Switch the current context to the specified frame. Subsequent
|
||||||
commands will operate in the context of the specified frame,
|
commands will operate in the context of the specified frame,
|
||||||
|
|
|
@ -255,6 +255,7 @@ function startListeners() {
|
||||||
addMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
|
addMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
|
||||||
addMessageListenerId("Marionette:clearElement", clearElementFn);
|
addMessageListenerId("Marionette:clearElement", clearElementFn);
|
||||||
addMessageListenerId("Marionette:switchToFrame", switchToFrame);
|
addMessageListenerId("Marionette:switchToFrame", switchToFrame);
|
||||||
|
addMessageListenerId("Marionette:switchToParentFrame", switchToParentFrame);
|
||||||
addMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn);
|
addMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn);
|
||||||
addMessageListenerId("Marionette:deleteSession", deleteSession);
|
addMessageListenerId("Marionette:deleteSession", deleteSession);
|
||||||
addMessageListenerId("Marionette:sleepSession", sleepSession);
|
addMessageListenerId("Marionette:sleepSession", sleepSession);
|
||||||
|
@ -359,6 +360,7 @@ function deleteSession(msg) {
|
||||||
removeMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
|
removeMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
|
||||||
removeMessageListenerId("Marionette:clearElement", clearElementFn);
|
removeMessageListenerId("Marionette:clearElement", clearElementFn);
|
||||||
removeMessageListenerId("Marionette:switchToFrame", switchToFrame);
|
removeMessageListenerId("Marionette:switchToFrame", switchToFrame);
|
||||||
|
removeMessageListenerId("Marionette:switchToParentFrame", switchToParentFrame);
|
||||||
removeMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn);
|
removeMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn);
|
||||||
removeMessageListenerId("Marionette:deleteSession", deleteSession);
|
removeMessageListenerId("Marionette:deleteSession", deleteSession);
|
||||||
removeMessageListenerId("Marionette:sleepSession", sleepSession);
|
removeMessageListenerId("Marionette:sleepSession", sleepSession);
|
||||||
|
@ -1679,6 +1681,20 @@ function switchToShadowRoot(id) {
|
||||||
curContainer.shadowRoot = foundShadowRoot;
|
curContainer.shadowRoot = foundShadowRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch to the parent frame of the current Frame. If the frame is the top most
|
||||||
|
* is the current frame then no action will happen.
|
||||||
|
*/
|
||||||
|
function switchToParentFrame(msg) {
|
||||||
|
let command_id = msg.json.command_id;
|
||||||
|
curContainer.frame = curContainer.frame.parent;
|
||||||
|
let parentElement = elementManager.addToKnownElements(curContainer.frame);
|
||||||
|
|
||||||
|
sendSyncMessage("Marionette:switchedToFrame", { frameValue: parentElement });
|
||||||
|
|
||||||
|
sendOk(msg.json.command_id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch to frame given either the server-assigned element id,
|
* Switch to frame given either the server-assigned element id,
|
||||||
* its index in window.frames, or the iframe's name or id.
|
* its index in window.frames, or the iframe's name or id.
|
||||||
|
|
|
@ -4,5 +4,4 @@
|
||||||
|
|
||||||
__version__ = '0.7.1'
|
__version__ = '0.7.1'
|
||||||
|
|
||||||
|
from transport import *
|
||||||
from transport import MarionetteTransport
|
|
||||||
|
|
|
@ -7,16 +7,111 @@ import errno
|
||||||
import json
|
import json
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
|
import types
|
||||||
|
|
||||||
|
|
||||||
class MarionetteTransport(object):
|
class Message(object):
|
||||||
"""The Marionette socket client. This speaks the same protocol
|
def __init__(self, msgid):
|
||||||
as the remote debugger inside Gecko, in which messages are always
|
self.id = msgid
|
||||||
preceded by the message length and a colon, e.g.:
|
|
||||||
|
|
||||||
20:{"command": "test"}
|
def __eq__(self, other):
|
||||||
|
return self.id == other.id
|
||||||
|
|
||||||
|
|
||||||
|
class Command(Message):
|
||||||
|
TYPE = 0
|
||||||
|
|
||||||
|
def __init__(self, msgid, name, params):
|
||||||
|
Message.__init__(self, msgid)
|
||||||
|
self.name = name
|
||||||
|
self.params = params
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "<Command id=%s, name=%s, params=%s>" % (self.id, self.name, self.params)
|
||||||
|
|
||||||
|
def to_msg(self):
|
||||||
|
msg = [Command.TYPE, self.id, self.name, self.params]
|
||||||
|
return json.dumps(msg)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_msg(payload):
|
||||||
|
data = json.loads(payload)
|
||||||
|
assert data[0] == Command.TYPE
|
||||||
|
cmd = Command(data[1], data[2], data[3])
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
|
||||||
|
class Response(Message):
|
||||||
|
TYPE = 1
|
||||||
|
|
||||||
|
def __init__(self, msgid, error, result):
|
||||||
|
Message.__init__(self, msgid)
|
||||||
|
self.error = error
|
||||||
|
self.result = result
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "<Response id=%s, error=%s, result=%s>" % (self.id, self.error, self.result)
|
||||||
|
|
||||||
|
def to_msg(self):
|
||||||
|
msg = [Response.TYPE, self.id, self.error, self.result]
|
||||||
|
return json.dumps(msg)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_msg(payload):
|
||||||
|
data = json.loads(payload)
|
||||||
|
assert data[0] == Response.TYPE
|
||||||
|
return Response(data[1], data[2], data[3])
|
||||||
|
|
||||||
|
|
||||||
|
class Proto2Command(Command):
|
||||||
|
"""Compatibility shim that marshals messages from a protocol level
|
||||||
|
2 and below remote into ``Command`` objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, params):
|
||||||
|
Command.__init__(self, None, name, params)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_data(data):
|
||||||
|
if "emulator_cmd" in data:
|
||||||
|
name = "runEmulatorCmd"
|
||||||
|
elif "emulator_shell" in data:
|
||||||
|
name = "runEmulatorShell"
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
return Proto2Command(name, data)
|
||||||
|
|
||||||
|
|
||||||
|
class Proto2Response(Response):
|
||||||
|
"""Compatibility shim that marshals messages from a protocol level
|
||||||
|
2 and below remote into ``Response`` objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, error, result):
|
||||||
|
Response.__init__(self, None, error, result)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_data(data):
|
||||||
|
err, res = None, None
|
||||||
|
if "error" in data:
|
||||||
|
err = data
|
||||||
|
else:
|
||||||
|
res = data
|
||||||
|
return Proto2Response(err, res)
|
||||||
|
|
||||||
|
|
||||||
|
class TcpTransport(object):
|
||||||
|
"""Socket client that communciates with Marionette via TCP.
|
||||||
|
|
||||||
|
It speaks the protocol of the remote debugger in Gecko, in which
|
||||||
|
messages are always preceded by the message length and a colon, e.g.:
|
||||||
|
|
||||||
|
7:MESSAGE
|
||||||
|
|
||||||
|
On top of this protocol it uses a Marionette message format, that
|
||||||
|
depending on the protocol level offered by the remote server, varies.
|
||||||
|
Supported protocol levels are 1 and above.
|
||||||
|
"""
|
||||||
max_packet_length = 4096
|
max_packet_length = 4096
|
||||||
connection_lost_msg = "Connection to Marionette server is lost. Check gecko.log (desktop firefox) or logcat (b2g) for errors."
|
connection_lost_msg = "Connection to Marionette server is lost. Check gecko.log (desktop firefox) or logcat (b2g) for errors."
|
||||||
|
|
||||||
|
@ -24,14 +119,16 @@ class MarionetteTransport(object):
|
||||||
self.addr = addr
|
self.addr = addr
|
||||||
self.port = port
|
self.port = port
|
||||||
self.socket_timeout = socket_timeout
|
self.socket_timeout = socket_timeout
|
||||||
self.sock = None
|
|
||||||
self.protocol = 1
|
self.protocol = 1
|
||||||
self.application_type = None
|
self.application_type = None
|
||||||
|
self.last_id = 0
|
||||||
|
self.expected_responses = []
|
||||||
|
|
||||||
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.sock.settimeout(self.socket_timeout)
|
||||||
|
|
||||||
def _recv_n_bytes(self, n):
|
def _recv_n_bytes(self, n):
|
||||||
"""Convenience method for receiving exactly n bytes from self.sock
|
|
||||||
(assuming it's open and connected).
|
|
||||||
"""
|
|
||||||
data = ""
|
data = ""
|
||||||
while len(data) < n:
|
while len(data) < n:
|
||||||
chunk = self.sock.recv(n - len(data))
|
chunk = self.sock.recv(n - len(data))
|
||||||
|
@ -40,41 +137,82 @@ class MarionetteTransport(object):
|
||||||
data += chunk
|
data += chunk
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def receive(self):
|
def _unmarshal(self, packet):
|
||||||
"""Receive the next complete response from the server, and
|
msg = None
|
||||||
return it as a JSON structure. Each response from the server
|
|
||||||
is prepended by len(message) + ":".
|
# protocol 3 and above
|
||||||
|
if self.protocol >= 3:
|
||||||
|
typ = int(packet[1])
|
||||||
|
if typ == Command.TYPE:
|
||||||
|
msg = Command.from_msg(packet)
|
||||||
|
elif typ == Response.TYPE:
|
||||||
|
msg = Response.from_msg(packet)
|
||||||
|
|
||||||
|
# protocol 2 and below
|
||||||
|
else:
|
||||||
|
data = json.loads(packet)
|
||||||
|
|
||||||
|
# emulator callbacks
|
||||||
|
if isinstance(data, dict) and any(k in data for k in ("emulator_cmd", "emulator_shell")):
|
||||||
|
msg = Proto2Command.from_data(data)
|
||||||
|
|
||||||
|
# everything else
|
||||||
|
else:
|
||||||
|
msg = Proto2Response.from_data(data)
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def receive(self, unmarshal=True):
|
||||||
|
"""Wait for the next complete response from the remote.
|
||||||
|
|
||||||
|
:param unmarshal: Default is to deserialise the packet and
|
||||||
|
return a ``Message`` type. Setting this to false will return
|
||||||
|
the raw packet.
|
||||||
"""
|
"""
|
||||||
assert(self.sock)
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
response = ''
|
data = ""
|
||||||
bytes_to_recv = 10
|
bytes_to_recv = 10
|
||||||
|
|
||||||
while time.time() - now < self.socket_timeout:
|
while time.time() - now < self.socket_timeout:
|
||||||
try:
|
try:
|
||||||
data = self.sock.recv(bytes_to_recv)
|
chunk = self.sock.recv(bytes_to_recv)
|
||||||
response += data
|
data += chunk
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if not data:
|
if not chunk:
|
||||||
raise IOError(self.connection_lost_msg)
|
raise IOError(self.connection_lost_msg)
|
||||||
sep = response.find(':')
|
|
||||||
|
sep = data.find(":")
|
||||||
if sep > -1:
|
if sep > -1:
|
||||||
length = response[0:sep]
|
length = data[0:sep]
|
||||||
remaining = response[sep + 1:]
|
remaining = data[sep + 1:]
|
||||||
|
|
||||||
if len(remaining) == int(length):
|
if len(remaining) == int(length):
|
||||||
return json.loads(remaining)
|
if unmarshal:
|
||||||
|
msg = self._unmarshal(remaining)
|
||||||
|
self.last_id = msg.id
|
||||||
|
|
||||||
|
if isinstance(msg, Response) and self.protocol >= 3:
|
||||||
|
if msg not in self.expected_responses:
|
||||||
|
raise Exception("Received unexpected response: %s" % msg)
|
||||||
|
else:
|
||||||
|
self.expected_responses.remove(msg)
|
||||||
|
|
||||||
|
return msg
|
||||||
|
else:
|
||||||
|
return remaining
|
||||||
|
|
||||||
bytes_to_recv = int(length) - len(remaining)
|
bytes_to_recv = int(length) - len(remaining)
|
||||||
raise socket.timeout('connection timed out after %d s' % self.socket_timeout)
|
|
||||||
|
raise socket.timeout("connection timed out after %ds" % self.socket_timeout)
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
"""Connect to the server and process the hello message we expect
|
"""Connect to the server and process the hello message we expect
|
||||||
to receive in response.
|
to receive in response.
|
||||||
|
|
||||||
Return a tuple of the protocol level and the application type.
|
Returns a tuple of the protocol level and the application type.
|
||||||
"""
|
"""
|
||||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
self.sock.settimeout(self.socket_timeout)
|
|
||||||
try:
|
try:
|
||||||
self.sock.connect((self.addr, self.port))
|
self.sock.connect((self.addr, self.port))
|
||||||
except:
|
except:
|
||||||
|
@ -82,22 +220,34 @@ class MarionetteTransport(object):
|
||||||
# another connection attempt.
|
# another connection attempt.
|
||||||
self.sock = None
|
self.sock = None
|
||||||
raise
|
raise
|
||||||
|
|
||||||
self.sock.settimeout(2.0)
|
self.sock.settimeout(2.0)
|
||||||
|
|
||||||
hello = self.receive()
|
# first packet is always a JSON Object
|
||||||
|
# which we can use to tell which protocol level we are at
|
||||||
|
raw = self.receive(unmarshal=False)
|
||||||
|
hello = json.loads(raw)
|
||||||
self.protocol = hello.get("marionetteProtocol", 1)
|
self.protocol = hello.get("marionetteProtocol", 1)
|
||||||
self.application_type = hello.get("applicationType")
|
self.application_type = hello.get("applicationType")
|
||||||
|
|
||||||
return (self.protocol, self.application_type)
|
return (self.protocol, self.application_type)
|
||||||
|
|
||||||
def send(self, data):
|
def send(self, obj):
|
||||||
"""Send a message on the socket, prepending it with len(msg) + ":"."""
|
"""Send message to the remote server. Allowed input is a
|
||||||
|
``Message`` instance or a JSON serialisable object.
|
||||||
|
"""
|
||||||
if not self.sock:
|
if not self.sock:
|
||||||
self.connect()
|
self.connect()
|
||||||
data = "%s:%s" % (len(data), data)
|
|
||||||
|
|
||||||
for packet in [data[i:i + self.max_packet_length] for i in
|
if isinstance(obj, Message):
|
||||||
range(0, len(data), self.max_packet_length)]:
|
data = obj.to_msg()
|
||||||
|
self.expected_responses.append(obj)
|
||||||
|
else:
|
||||||
|
data = json.dumps(obj)
|
||||||
|
payload = "%s:%s" % (len(data), data)
|
||||||
|
|
||||||
|
for packet in [payload[i:i + self.max_packet_length] for i in
|
||||||
|
range(0, len(payload), self.max_packet_length)]:
|
||||||
try:
|
try:
|
||||||
self.sock.send(packet)
|
self.sock.send(packet)
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
|
@ -106,32 +256,55 @@ class MarionetteTransport(object):
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
def respond(self, obj):
|
||||||
|
"""Send a response to a command. This can be an arbitrary JSON
|
||||||
|
serialisable object or an ``Exception``.
|
||||||
|
"""
|
||||||
|
res, err = None, None
|
||||||
|
if isinstance(obj, Exception):
|
||||||
|
err = obj
|
||||||
|
else:
|
||||||
|
res = obj
|
||||||
|
msg = Response(self.last_id, err, res)
|
||||||
|
self.send(msg)
|
||||||
|
return self.receive()
|
||||||
|
|
||||||
|
def request(self, name, params):
|
||||||
|
"""Sends a message to the remote server and waits for a response
|
||||||
|
to come back.
|
||||||
|
"""
|
||||||
|
self.last_id = self.last_id + 1
|
||||||
|
cmd = Command(self.last_id, name, params)
|
||||||
|
self.send(cmd)
|
||||||
return self.receive()
|
return self.receive()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Close the socket."""
|
"""Close the socket."""
|
||||||
if self.sock:
|
if self.sock:
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
self.sock = None
|
self.sock = None
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def wait_for_port(host, port, timeout=60):
|
def wait_for_port(host, port, timeout=60):
|
||||||
""" Wait for the specified Marionette host/port to be available."""
|
""" Wait for the specified host/port to be available."""
|
||||||
starttime = datetime.datetime.now()
|
starttime = datetime.datetime.now()
|
||||||
poll_interval = 0.1
|
poll_interval = 0.1
|
||||||
while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout):
|
while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout):
|
||||||
sock = None
|
sock = None
|
||||||
try:
|
try:
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.connect((host, port))
|
sock.connect((host, port))
|
||||||
data = sock.recv(16)
|
data = sock.recv(16)
|
||||||
|
sock.close()
|
||||||
|
if ':' in data:
|
||||||
|
return True
|
||||||
|
except socket.error:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
if sock:
|
||||||
sock.close()
|
sock.close()
|
||||||
if ':' in data:
|
time.sleep(poll_interval)
|
||||||
return True
|
return False
|
||||||
except socket.error:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
if sock:
|
|
||||||
sock.close()
|
|
||||||
time.sleep(poll_interval)
|
|
||||||
return False
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
[send-content-type-string.htm]
|
|
||||||
type: testharness
|
|
||||||
[XMLHttpRequest: send() - Content-Type 1]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[XMLHttpRequest: send() - Content-Type 2]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
[send-entity-body-document.htm]
|
|
||||||
type: testharness
|
|
||||||
[XMLHttpRequest: send() - Document]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[XMLHttpRequest: send() - Document 1]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[XMLHttpRequest: send() - Document 2]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[XMLHttpRequest: send() - Document 3]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[XMLHttpRequest: send() - Document 4]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -469,12 +469,6 @@
|
||||||
"url": "/_mozilla/service-workers/service-worker/state.https.html"
|
"url": "/_mozilla/service-workers/service-worker/state.https.html"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"service-workers/service-worker/sync-xhr-doesnt-deadlock.https.html": [
|
|
||||||
{
|
|
||||||
"path": "service-workers/service-worker/sync-xhr-doesnt-deadlock.https.html",
|
|
||||||
"url": "/_mozilla/service-workers/service-worker/sync-xhr-doesnt-deadlock.https.html"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"service-workers/service-worker/synced-state.https.html": [
|
"service-workers/service-worker/synced-state.https.html": [
|
||||||
{
|
{
|
||||||
"path": "service-workers/service-worker/synced-state.https.html",
|
"path": "service-workers/service-worker/synced-state.https.html",
|
||||||
|
@ -528,6 +522,12 @@
|
||||||
"path": "service-workers/service-worker/worker-interception.https.html",
|
"path": "service-workers/service-worker/worker-interception.https.html",
|
||||||
"url": "/_mozilla/service-workers/service-worker/worker-interception.https.html"
|
"url": "/_mozilla/service-workers/service-worker/worker-interception.https.html"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"service-workers/service-worker/xhr.https.html": [
|
||||||
|
{
|
||||||
|
"path": "service-workers/service-worker/xhr.https.html",
|
||||||
|
"url": "/_mozilla/service-workers/service-worker/xhr.https.html"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -537,4 +537,4 @@
|
||||||
"rev": null,
|
"rev": null,
|
||||||
"url_base": "/_mozilla/",
|
"url_base": "/_mozilla/",
|
||||||
"version": 2
|
"version": 2
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[sync-xhr-doesnt-deadlock.https.html]
|
|
||||||
type: testharness
|
|
||||||
[Verify SyncXHR does not deadlock]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<title>Service Worker: SyncXHR doesn't deadlock iframe</title>
|
|
||||||
<script>
|
|
||||||
function performSyncXHR() {
|
|
||||||
var url = 'sync-xhr-doesnt-deadlock.data?bustcache=' + Date.now();
|
|
||||||
var syncXhr = new XMLHttpRequest();
|
|
||||||
syncXhr.open("GET", url, false);
|
|
||||||
syncXhr.send();
|
|
||||||
if (syncXhr.responseText != 'hello')
|
|
||||||
throw 'FAIL';
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1 +0,0 @@
|
||||||
hello
|
|
|
@ -1,5 +0,0 @@
|
||||||
self.onfetch = function(event) {
|
|
||||||
if (event.request.url.indexOf('sync-xhr-doesnt-deadlock.data') == -1)
|
|
||||||
return;
|
|
||||||
event.respondWith(fetch('404resource?bustcache=' + Date.now()));
|
|
||||||
};
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
self.addEventListener('activate', function(event) {
|
||||||
|
event.waitUntil(clients.claim());
|
||||||
|
});
|
||||||
|
self.addEventListener('message', function(event) {
|
||||||
|
event.data.port.postMessage({xhr: !!("XMLHttpRequest" in self)});
|
||||||
|
});
|
|
@ -1,23 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<title>Service Worker: SyncXHR doesn't deadlock</title>
|
|
||||||
<script src="/resources/testharness.js"></script>
|
|
||||||
<script src="/resources/testharnessreport.js"></script>
|
|
||||||
<script src="resources/test-helpers.sub.js"></script>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
async_test(function(t) {
|
|
||||||
var url = 'resources/sync-xhr-doesnt-deadlock.js';
|
|
||||||
var scope = 'resources/sync-xhr-doesnt-deadlock-iframe.html';
|
|
||||||
|
|
||||||
service_worker_unregister_and_register(t, url, scope)
|
|
||||||
.then(function(registration) {
|
|
||||||
return wait_for_state(t, registration.installing, 'activated');
|
|
||||||
})
|
|
||||||
.then(function() { return with_iframe(scope); })
|
|
||||||
.then(function(frame) {
|
|
||||||
frame.contentWindow.performSyncXHR();
|
|
||||||
service_worker_unregister_and_done(t, scope);
|
|
||||||
})
|
|
||||||
.catch(unreached_rejection(t));
|
|
||||||
}, 'Verify SyncXHR does not deadlock');
|
|
||||||
</script>
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<title>Service Worker: XHR doesn't exist</title>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="resources/get-host-info.sub.js"></script>
|
||||||
|
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
async_test(function(t) {
|
||||||
|
var path = new URL(".", window.location).pathname
|
||||||
|
var url = 'resources/xhr.js';
|
||||||
|
var scope = 'resources/blank.html?xhr';
|
||||||
|
var host_info = get_host_info();
|
||||||
|
var frameURL = host_info['HTTPS_ORIGIN'] + path + scope;
|
||||||
|
|
||||||
|
service_worker_unregister_and_register(t, url, scope)
|
||||||
|
.then(function(registration) {
|
||||||
|
return wait_for_state(t, registration.installing, 'activated');
|
||||||
|
})
|
||||||
|
.then(function() { return with_iframe(frameURL); })
|
||||||
|
.then(function(frame) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
function onMessage(e) {
|
||||||
|
assert_false(e.data.xhr);
|
||||||
|
service_worker_unregister_and_done(t, scope);
|
||||||
|
}
|
||||||
|
var channel = new MessageChannel();
|
||||||
|
channel.port1.onmessage = t.step_func(onMessage);
|
||||||
|
frame.contentWindow.navigator.serviceWorker.controller.postMessage({port: channel.port2}, [channel.port2]);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(unreached_rejection(t));
|
||||||
|
}, 'Verify XHR does not exist');
|
||||||
|
</script>
|
|
@ -0,0 +1,5 @@
|
||||||
|
def main(request, response):
|
||||||
|
headers = [("Content-type", "text/html;charset=utf-8")]
|
||||||
|
content = "<!DOCTYPE html><div></div>"
|
||||||
|
|
||||||
|
return headers, content
|
|
@ -0,0 +1,5 @@
|
||||||
|
def main(request, response):
|
||||||
|
headers = [("Content-type", "text/html;charset=utf-8")]
|
||||||
|
content = "<img>foo"
|
||||||
|
|
||||||
|
return headers, content
|
|
@ -20,7 +20,7 @@
|
||||||
request("TEST", "text/plain;charset=UTF-8")
|
request("TEST", "text/plain;charset=UTF-8")
|
||||||
function init(fr) { request(fr.contentDocument, fr.getAttribute("data-t")) }
|
function init(fr) { request(fr.contentDocument, fr.getAttribute("data-t")) }
|
||||||
</script>
|
</script>
|
||||||
<iframe src='data:text/xml;charset=windows-1252,<%FF/>' onload="init(this)" data-t="application/xml;charset=windows-1252"></iframe>
|
<iframe src='data:text/xml;charset=windows-1252,<%FF/>' onload="init(this)" data-t="application/xml;charset=UTF-8"></iframe>
|
||||||
<iframe src='data:text/html;charset=windows-1252,%FF' onload="init(this)" data-t="text/html;charset=windows-1252"></iframe>
|
<iframe src='data:text/html;charset=windows-1252,%FF' onload="init(this)" data-t="text/html;charset=UTF-8"></iframe>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
{ contentType: 'text/html;charset=UTF-8', responseText : '<body>\uFFFD<\/body>' }, /*invalid character code in document turns into FFFD*/
|
{ contentType: 'text/html;charset=UTF-8', responseText : '<body>\uFFFD<\/body>' }, /*invalid character code in document turns into FFFD*/
|
||||||
{ contentType: 'text/html;charset=UTF-8', responseText : '<body>\u30C6\u30b9\u30c8<\/body>' } /* correctly serialized Shift-JIS */,
|
{ contentType: 'text/html;charset=UTF-8', responseText : '<body>\u30C6\u30b9\u30c8<\/body>' } /* correctly serialized Shift-JIS */,
|
||||||
{ contentType: 'text/html;charset=UTF-8', responseText: 'top' }, /* There's some markup included, but it's not really relevant for this test suite, so we do an indexOf() test */
|
{ contentType: 'text/html;charset=UTF-8', responseText: 'top' }, /* There's some markup included, but it's not really relevant for this test suite, so we do an indexOf() test */
|
||||||
{ contentType: 'text/html;charset=UTF-8' }
|
{ contentType: 'text/html;charset=UTF-8' },
|
||||||
|
{ contentType: 'text/html;charset=UTF-8', responseText: '<img>foo' },
|
||||||
|
{ contentType: 'text/html;charset=UTF-8', responseText: '<!DOCTYPE html><html><head></head><body><div></div></body></html>' }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
function request(input, isHTML, title) {
|
function request(input, number, title) {
|
||||||
test(function() {
|
test(function() {
|
||||||
var client = new XMLHttpRequest()
|
var client = new XMLHttpRequest()
|
||||||
client.open("POST", "resources/content.py?response_charset_label=UTF-8", false)
|
client.open("POST", "resources/content.py?response_charset_label=UTF-8", false)
|
||||||
|
@ -30,12 +32,14 @@
|
||||||
// The indexOf() assertation will overlook some stuff, i.e. XML prologues that shouldn't be there (looking at you, Presto).
|
// The indexOf() assertation will overlook some stuff, i.e. XML prologues that shouldn't be there (looking at you, Presto).
|
||||||
// However, arguably these things have little to do with the XHR functionality we're testing.
|
// However, arguably these things have little to do with the XHR functionality we're testing.
|
||||||
if(exp.responseText){ // This test does not want to assert anything about what markup a standalone IMG should be wrapped in. Hence the GIF test lacks a responseText expectation.
|
if(exp.responseText){ // This test does not want to assert anything about what markup a standalone IMG should be wrapped in. Hence the GIF test lacks a responseText expectation.
|
||||||
assert_true(client.responseText.indexOf(exp.responseText) != -1);
|
assert_true(client.responseText.indexOf(exp.responseText) != -1,
|
||||||
|
JSON.stringify(exp.responseText) + " not in " +
|
||||||
|
JSON.stringify(client.responseText));
|
||||||
}
|
}
|
||||||
assert_equals(client.responseXML, null)
|
assert_equals(client.responseXML, null)
|
||||||
}, title)
|
}, title)
|
||||||
}
|
}
|
||||||
function init(fr, number, title) { request(fr.contentDocument, number) }
|
function init(fr, number, title) { request(fr.contentDocument, number, title) }
|
||||||
</script>
|
</script>
|
||||||
<!--
|
<!--
|
||||||
This test also tests how documents in various encodings are serialized.
|
This test also tests how documents in various encodings are serialized.
|
||||||
|
@ -50,6 +54,8 @@
|
||||||
<iframe src='resources/shift-jis-html.py' onload="init(this, 2, 'HTML document, shift-jis')"></iframe>
|
<iframe src='resources/shift-jis-html.py' onload="init(this, 2, 'HTML document, shift-jis')"></iframe>
|
||||||
<iframe src='folder.txt' onload="init(this, 3, 'plain text file')"></iframe>
|
<iframe src='folder.txt' onload="init(this, 3, 'plain text file')"></iframe>
|
||||||
<iframe src='resources/image.gif' onload="init(this, 4, 'image file')"></iframe>
|
<iframe src='resources/image.gif' onload="init(this, 4, 'image file')"></iframe>
|
||||||
|
<iframe src='resources/img-utf8-html.py' onload="init(this, 5, 'img tag')"></iframe>
|
||||||
|
<iframe src='resources/empty-div-utf8-html.py' onload="init(this, 6, 'empty div')"></iframe>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -110,6 +110,7 @@ function buildOptionListForChildren(node) {
|
||||||
tagName: tagName,
|
tagName: tagName,
|
||||||
textContent: textContent,
|
textContent: textContent,
|
||||||
disabled: child.disabled,
|
disabled: child.disabled,
|
||||||
|
display: child.style.display,
|
||||||
// We need to do this for every option element as each one can have
|
// We need to do this for every option element as each one can have
|
||||||
// an individual style set for direction
|
// an individual style set for direction
|
||||||
textDirection: getComputedDirection(child),
|
textDirection: getComputedDirection(child),
|
||||||
|
|
|
@ -86,6 +86,7 @@ function populateChildren(menulist, options, selectedIndex, zoom, startIndex = 0
|
||||||
item.setAttribute("label", option.textContent);
|
item.setAttribute("label", option.textContent);
|
||||||
item.style.direction = option.textDirection;
|
item.style.direction = option.textDirection;
|
||||||
item.style.fontSize = adjustedTextSize;
|
item.style.fontSize = adjustedTextSize;
|
||||||
|
item.style.display = option.display;
|
||||||
item.setAttribute("tooltiptext", option.tooltip);
|
item.setAttribute("tooltiptext", option.tooltip);
|
||||||
|
|
||||||
element.appendChild(item);
|
element.appendChild(item);
|
||||||
|
|
|
@ -350,6 +350,8 @@ XRE_InitChildProcess(int aArgc,
|
||||||
// NB: This must be called before profiler_init
|
// NB: This must be called before profiler_init
|
||||||
NS_LogInit();
|
NS_LogInit();
|
||||||
|
|
||||||
|
mozilla::LogModule::Init();
|
||||||
|
|
||||||
char aLocal;
|
char aLocal;
|
||||||
profiler_init(&aLocal);
|
profiler_init(&aLocal);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/* -*- 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/Logging.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "mozilla/ClearOnShutdown.h"
|
||||||
|
#include "mozilla/Mutex.h"
|
||||||
|
#include "mozilla/StaticPtr.h"
|
||||||
|
#include "nsClassHashtable.h"
|
||||||
|
|
||||||
|
// NB: Initial amount determined by auditing the codebase for the total amount
|
||||||
|
// of unique module names and padding up to the next power of 2.
|
||||||
|
const uint32_t kInitialModuleCount = 256;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
/**
|
||||||
|
* Safely converts an integer into a valid LogLevel.
|
||||||
|
*/
|
||||||
|
LogLevel
|
||||||
|
Clamp(int32_t aLevel)
|
||||||
|
{
|
||||||
|
aLevel = std::min(aLevel, static_cast<int32_t>(LogLevel::Verbose));
|
||||||
|
aLevel = std::max(aLevel, static_cast<int32_t>(LogLevel::Disabled));
|
||||||
|
return static_cast<LogLevel>(aLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LogModuleManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LogModuleManager()
|
||||||
|
: mModulesLock("logmodules")
|
||||||
|
, mModules(kInitialModuleCount)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~LogModuleManager()
|
||||||
|
{
|
||||||
|
// NB: mModules owns all of the log modules, they will get destroyed by
|
||||||
|
// its destructor.
|
||||||
|
}
|
||||||
|
|
||||||
|
LogModule* CreateOrGetModule(const char* aName)
|
||||||
|
{
|
||||||
|
OffTheBooksMutexAutoLock guard(mModulesLock);
|
||||||
|
LogModule* module = nullptr;
|
||||||
|
if (!mModules.Get(aName, &module)) {
|
||||||
|
// Create the PRLogModule, this will read any env vars that set the log
|
||||||
|
// level ahead of time. The module is held internally by NSPR, so it's
|
||||||
|
// okay to drop the pointer when leaving this scope.
|
||||||
|
PRLogModuleInfo* prModule = PR_NewLogModule(aName);
|
||||||
|
|
||||||
|
// NSPR does not impose a restriction on the values that log levels can
|
||||||
|
// be. LogModule uses the LogLevel enum class so we must clamp the value
|
||||||
|
// to a max of Verbose.
|
||||||
|
LogLevel logLevel = Clamp(prModule->level);
|
||||||
|
module = new LogModule(logLevel);
|
||||||
|
mModules.Put(aName, module);
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
OffTheBooksMutex mModulesLock;
|
||||||
|
nsClassHashtable<nsCharPtrHashKey, LogModule> mModules;
|
||||||
|
};
|
||||||
|
|
||||||
|
StaticAutoPtr<LogModuleManager> sLogModuleManager;
|
||||||
|
|
||||||
|
LogModule*
|
||||||
|
LogModule::Get(const char* aName)
|
||||||
|
{
|
||||||
|
// This is just a pass through to the LogModuleManager so
|
||||||
|
// that the LogModuleManager implementation can be kept internal.
|
||||||
|
MOZ_ASSERT(sLogModuleManager != nullptr);
|
||||||
|
return sLogModuleManager->CreateOrGetModule(aName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LogModule::Init()
|
||||||
|
{
|
||||||
|
// NB: This method is not threadsafe; it is expected to be called very early
|
||||||
|
// in startup prior to any other threads being run.
|
||||||
|
if (sLogModuleManager) {
|
||||||
|
// Already initialized.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: We intentionally do not register for ClearOnShutdown as that happens
|
||||||
|
// before all logging is complete. And, yes, that means we leak, but
|
||||||
|
// we're doing that intentionally.
|
||||||
|
sLogModuleManager = new LogModuleManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mozilla
|
|
@ -10,6 +10,8 @@
|
||||||
#include "prlog.h"
|
#include "prlog.h"
|
||||||
|
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
|
#include "mozilla/Atomics.h"
|
||||||
|
#include "mozilla/Likely.h"
|
||||||
|
|
||||||
// This file is a placeholder for a replacement to the NSPR logging framework
|
// This file is a placeholder for a replacement to the NSPR logging framework
|
||||||
// that is defined in prlog.h. Currently it is just a pass through, but as
|
// that is defined in prlog.h. Currently it is just a pass through, but as
|
||||||
|
@ -43,6 +45,87 @@ enum class LogLevel {
|
||||||
Verbose,
|
Verbose,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LogModule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Retrieves the module with the given name. If it does not already exist
|
||||||
|
* it will be created.
|
||||||
|
*
|
||||||
|
* @param aName The name of the module.
|
||||||
|
* @return A log module for the given name. This may be shared.
|
||||||
|
*/
|
||||||
|
#if !defined(MOZILLA_XPCOMRT_API)
|
||||||
|
static LogModule* Get(const char* aName);
|
||||||
|
#else
|
||||||
|
// For simplicity, libxpcomrt doesn't supoort logging.
|
||||||
|
static LogModule* Get(const char* aName) { return nullptr; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void Init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether or not the given log level is enabled.
|
||||||
|
*/
|
||||||
|
bool ShouldLog(LogLevel aLevel) const { return mLevel >= aLevel; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the log module's current level.
|
||||||
|
*/
|
||||||
|
LogLevel Level() const { return mLevel; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class LogModuleManager;
|
||||||
|
|
||||||
|
explicit LogModule(LogLevel aLevel) : mLevel(aLevel) {}
|
||||||
|
|
||||||
|
LogModule(LogModule&) = delete;
|
||||||
|
LogModule& operator=(const LogModule&) = delete;
|
||||||
|
|
||||||
|
Atomic<LogLevel, Relaxed> mLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class that lazy loads the given log module. This is safe to use for
|
||||||
|
* declaring static references to log modules and can be used as a replacement
|
||||||
|
* for accessing a LogModule directly.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* static LazyLogModule sLayoutLog("layout");
|
||||||
|
*
|
||||||
|
* void Foo() {
|
||||||
|
* MOZ_LOG(sLayoutLog, LogLevel::Verbose, ("Entering foo"));
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
class LazyLogModule final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MOZ_CONSTEXPR LazyLogModule(const char* aLogName)
|
||||||
|
: mLogName(aLogName)
|
||||||
|
, mLog(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
operator LogModule*()
|
||||||
|
{
|
||||||
|
// NB: The use of an atomic makes the reading and assignment of mLog
|
||||||
|
// thread-safe. There is a small chance that mLog will be set more
|
||||||
|
// than once, but that's okay as it will be set to the same LogModule
|
||||||
|
// instance each time. Also note LogModule::Get is thread-safe.
|
||||||
|
LogModule* tmp = mLog;
|
||||||
|
if (MOZ_UNLIKELY(!tmp)) {
|
||||||
|
tmp = LogModule::Get(mLogName);
|
||||||
|
mLog = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* const mLogName;
|
||||||
|
Atomic<LogModule*, ReleaseAcquire> mLog;
|
||||||
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline bool log_test(const PRLogModuleInfo* module, LogLevel level) {
|
inline bool log_test(const PRLogModuleInfo* module, LogLevel level) {
|
||||||
|
@ -50,6 +133,11 @@ inline bool log_test(const PRLogModuleInfo* module, LogLevel level) {
|
||||||
return module && module->level >= static_cast<int>(level);
|
return module && module->level >= static_cast<int>(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool log_test(const LogModule* module, LogLevel level) {
|
||||||
|
MOZ_ASSERT(level != LogLevel::Disabled);
|
||||||
|
return module && module->ShouldLog(level);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
|
@ -83,6 +83,7 @@ EXPORTS.mozilla += [
|
||||||
'HoldDropJSObjects.h',
|
'HoldDropJSObjects.h',
|
||||||
'JSObjectHolder.h',
|
'JSObjectHolder.h',
|
||||||
'LinuxUtils.h',
|
'LinuxUtils.h',
|
||||||
|
'Logging.h',
|
||||||
'nsMemoryInfoDumper.h',
|
'nsMemoryInfoDumper.h',
|
||||||
'OwningNonNull.h',
|
'OwningNonNull.h',
|
||||||
'StaticMutex.h',
|
'StaticMutex.h',
|
||||||
|
@ -107,6 +108,7 @@ UNIFIED_SOURCES += [
|
||||||
'ErrorNames.cpp',
|
'ErrorNames.cpp',
|
||||||
'HoldDropJSObjects.cpp',
|
'HoldDropJSObjects.cpp',
|
||||||
'JSObjectHolder.cpp',
|
'JSObjectHolder.cpp',
|
||||||
|
'Logging.cpp',
|
||||||
'nsConsoleMessage.cpp',
|
'nsConsoleMessage.cpp',
|
||||||
'nsConsoleService.cpp',
|
'nsConsoleService.cpp',
|
||||||
'nsCycleCollector.cpp',
|
'nsCycleCollector.cpp',
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nsXULAppAPI.h"
|
#include "nsXULAppAPI.h"
|
||||||
#include "prprf.h"
|
#include "prprf.h"
|
||||||
#include "mozilla/Logging.h"
|
|
||||||
#include "nsError.h"
|
#include "nsError.h"
|
||||||
#include "prerror.h"
|
#include "prerror.h"
|
||||||
#include "prerr.h"
|
#include "prerr.h"
|
||||||
|
@ -217,16 +216,6 @@ nsDebugImpl::SetMultiprocessMode(const char* aDesc)
|
||||||
* always compiled in, in case some other module that uses it is
|
* always compiled in, in case some other module that uses it is
|
||||||
* compiled with debugging even if this library is not.
|
* compiled with debugging even if this library is not.
|
||||||
*/
|
*/
|
||||||
static PRLogModuleInfo* gDebugLog;
|
|
||||||
|
|
||||||
static void
|
|
||||||
InitLog()
|
|
||||||
{
|
|
||||||
if (0 == gDebugLog) {
|
|
||||||
gDebugLog = PR_NewLogModule("nsDebug");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum nsAssertBehavior
|
enum nsAssertBehavior
|
||||||
{
|
{
|
||||||
NS_ASSERT_UNINITIALIZED,
|
NS_ASSERT_UNINITIALIZED,
|
||||||
|
@ -317,26 +306,20 @@ EXPORT_XPCOM_API(void)
|
||||||
NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
|
NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
|
||||||
const char* aFile, int32_t aLine)
|
const char* aFile, int32_t aLine)
|
||||||
{
|
{
|
||||||
InitLog();
|
|
||||||
|
|
||||||
FixedBuffer buf;
|
FixedBuffer buf;
|
||||||
mozilla::LogLevel ll = LogLevel::Warning;
|
|
||||||
const char* sevString = "WARNING";
|
const char* sevString = "WARNING";
|
||||||
|
|
||||||
switch (aSeverity) {
|
switch (aSeverity) {
|
||||||
case NS_DEBUG_ASSERTION:
|
case NS_DEBUG_ASSERTION:
|
||||||
sevString = "###!!! ASSERTION";
|
sevString = "###!!! ASSERTION";
|
||||||
ll = LogLevel::Error;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NS_DEBUG_BREAK:
|
case NS_DEBUG_BREAK:
|
||||||
sevString = "###!!! BREAK";
|
sevString = "###!!! BREAK";
|
||||||
ll = LogLevel::Error;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NS_DEBUG_ABORT:
|
case NS_DEBUG_ABORT:
|
||||||
sevString = "###!!! ABORT";
|
sevString = "###!!! ABORT";
|
||||||
ll = LogLevel::Error;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -371,13 +354,9 @@ NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
|
||||||
|
|
||||||
# undef PrintToBuffer
|
# undef PrintToBuffer
|
||||||
|
|
||||||
// Write out the message to the debug log
|
|
||||||
MOZ_LOG(gDebugLog, ll, ("%s", buf.buffer));
|
|
||||||
PR_LogFlush();
|
|
||||||
|
|
||||||
// errors on platforms without a debugdlg ring a bell on stderr
|
// errors on platforms without a debugdlg ring a bell on stderr
|
||||||
#if !defined(XP_WIN)
|
#if !defined(XP_WIN)
|
||||||
if (ll != LogLevel::Warning) {
|
if (aSeverity != NS_DEBUG_WARNING) {
|
||||||
fprintf(stderr, "\07");
|
fprintf(stderr, "\07");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -498,6 +498,8 @@ NS_InitXPCOM2(nsIServiceManager** aResult,
|
||||||
|
|
||||||
NS_LogInit();
|
NS_LogInit();
|
||||||
|
|
||||||
|
mozilla::LogModule::Init();
|
||||||
|
|
||||||
JS_SetCurrentEmbedderTimeFunction(TimeSinceProcessCreation);
|
JS_SetCurrentEmbedderTimeFunction(TimeSinceProcessCreation);
|
||||||
|
|
||||||
char aLocal;
|
char aLocal;
|
||||||
|
|
|
@ -18,16 +18,8 @@ namespace mozilla {
|
||||||
namespace probes {
|
namespace probes {
|
||||||
|
|
||||||
#if defined(MOZ_LOGGING)
|
#if defined(MOZ_LOGGING)
|
||||||
static PRLogModuleInfo*
|
static LazyLogModule sProbeLog("SysProbe");
|
||||||
GetProbeLog()
|
#define LOG(x) MOZ_LOG(sProbeLog, mozilla::LogLevel::Debug, x)
|
||||||
{
|
|
||||||
static PRLogModuleInfo* sLog;
|
|
||||||
if (!sLog) {
|
|
||||||
sLog = PR_NewLogModule("SysProbe");
|
|
||||||
}
|
|
||||||
return sLog;
|
|
||||||
}
|
|
||||||
#define LOG(x) MOZ_LOG(GetProbeLog(), mozilla::LogLevel::Debug, x)
|
|
||||||
#else
|
#else
|
||||||
#define LOG(x)
|
#define LOG(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -81,7 +81,7 @@
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
PRLogModuleInfo* nsComponentManagerLog = nullptr;
|
static LazyLogModule nsComponentManagerLog("nsComponentManager");
|
||||||
|
|
||||||
#if 0 || defined (DEBUG_timeless)
|
#if 0 || defined (DEBUG_timeless)
|
||||||
#define SHOW_DENIED_ON_SHUTDOWN
|
#define SHOW_DENIED_ON_SHUTDOWN
|
||||||
|
@ -369,10 +369,6 @@ nsComponentManagerImpl::Init()
|
||||||
{
|
{
|
||||||
PR_ASSERT(NOT_INITIALIZED == mStatus);
|
PR_ASSERT(NOT_INITIALIZED == mStatus);
|
||||||
|
|
||||||
if (!nsComponentManagerLog) {
|
|
||||||
nsComponentManagerLog = PR_NewLogModule("nsComponentManager");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize our arena
|
// Initialize our arena
|
||||||
PL_INIT_ARENA_POOL(&mArena, "ComponentManagerArena", NS_CM_BLOCK_SIZE);
|
PL_INIT_ARENA_POOL(&mArena, "ComponentManagerArena", NS_CM_BLOCK_SIZE);
|
||||||
|
|
||||||
|
|
|
@ -49,17 +49,8 @@
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
static PRLogModuleInfo*
|
static LazyLogModule sNativeModuleLoaderLog("nsNativeModuleLoader");
|
||||||
GetNativeModuleLoaderLog()
|
#define LOG(level, args) MOZ_LOG(sNativeModuleLoaderLog, level, args)
|
||||||
{
|
|
||||||
static PRLogModuleInfo* sLog;
|
|
||||||
if (!sLog) {
|
|
||||||
sLog = PR_NewLogModule("nsNativeModuleLoader");
|
|
||||||
}
|
|
||||||
return sLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LOG(level, args) MOZ_LOG(GetNativeModuleLoaderLog(), level, args)
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsNativeModuleLoader::Init()
|
nsNativeModuleLoader::Init()
|
||||||
|
@ -202,7 +193,7 @@ nsNativeModuleLoader::UnloadLibraries()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto iter = mLibraries.Iter(); !iter.Done(); iter.Next()) {
|
for (auto iter = mLibraries.Iter(); !iter.Done(); iter.Next()) {
|
||||||
if (MOZ_LOG_TEST(GetNativeModuleLoaderLog(), LogLevel::Debug)) {
|
if (MOZ_LOG_TEST(sNativeModuleLoaderLog, LogLevel::Debug)) {
|
||||||
nsIHashable* hashedFile = iter.Key();
|
nsIHashable* hashedFile = iter.Key();
|
||||||
nsCOMPtr<nsIFile> file(do_QueryInterface(hashedFile));
|
nsCOMPtr<nsIFile> file(do_QueryInterface(hashedFile));
|
||||||
|
|
||||||
|
|
|
@ -30,16 +30,8 @@
|
||||||
//
|
//
|
||||||
// this enables LogLevel::Debug level information and places all output in
|
// this enables LogLevel::Debug level information and places all output in
|
||||||
// the file nspr.log
|
// the file nspr.log
|
||||||
static PRLogModuleInfo*
|
static mozilla::LazyLogModule sObserverServiceLog("ObserverService");
|
||||||
GetObserverServiceLog()
|
#define LOG(x) MOZ_LOG(sObserverServiceLog, mozilla::LogLevel::Debug, x)
|
||||||
{
|
|
||||||
static PRLogModuleInfo* sLog;
|
|
||||||
if (!sLog) {
|
|
||||||
sLog = PR_NewLogModule("ObserverService");
|
|
||||||
}
|
|
||||||
return sLog;
|
|
||||||
}
|
|
||||||
#define LOG(x) MOZ_LOG(GetObserverServiceLog(), mozilla::LogLevel::Debug, x)
|
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,6 @@ EXPORTS.mozilla += [
|
||||||
'FileUtils.h',
|
'FileUtils.h',
|
||||||
'GenericFactory.h',
|
'GenericFactory.h',
|
||||||
'IntentionalCrash.h',
|
'IntentionalCrash.h',
|
||||||
'Logging.h',
|
|
||||||
'Monitor.h',
|
'Monitor.h',
|
||||||
'Mutex.h',
|
'Mutex.h',
|
||||||
'Observer.h',
|
'Observer.h',
|
||||||
|
|
|
@ -22,16 +22,9 @@ using namespace mozilla;
|
||||||
#ifdef LOG
|
#ifdef LOG
|
||||||
#undef LOG
|
#undef LOG
|
||||||
#endif
|
#endif
|
||||||
static PRLogModuleInfo*
|
|
||||||
GetTeeLog()
|
static LazyLogModule sTeeLog("nsInputStreamTee");
|
||||||
{
|
#define LOG(args) MOZ_LOG(sTeeLog, mozilla::LogLevel::Debug, args)
|
||||||
static PRLogModuleInfo* sLog;
|
|
||||||
if (!sLog) {
|
|
||||||
sLog = PR_NewLogModule("nsInputStreamTee");
|
|
||||||
}
|
|
||||||
return sLog;
|
|
||||||
}
|
|
||||||
#define LOG(args) MOZ_LOG(GetTeeLog(), mozilla::LogLevel::Debug, args)
|
|
||||||
|
|
||||||
class nsInputStreamTee final : public nsIInputStreamTee
|
class nsInputStreamTee final : public nsIInputStreamTee
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,16 +31,8 @@ using namespace mozilla;
|
||||||
//
|
//
|
||||||
// set NSPR_LOG_MODULES=nsPipe:5
|
// set NSPR_LOG_MODULES=nsPipe:5
|
||||||
//
|
//
|
||||||
static PRLogModuleInfo*
|
static LazyLogModule sPipeLog("nsPipe");
|
||||||
GetPipeLog()
|
#define LOG(args) MOZ_LOG(sPipeLog, mozilla::LogLevel::Debug, args)
|
||||||
{
|
|
||||||
static PRLogModuleInfo* sLog;
|
|
||||||
if (!sLog) {
|
|
||||||
sLog = PR_NewLogModule("nsPipe");
|
|
||||||
}
|
|
||||||
return sLog;
|
|
||||||
}
|
|
||||||
#define LOG(args) MOZ_LOG(GetPipeLog(), mozilla::LogLevel::Debug, args)
|
|
||||||
|
|
||||||
#define DEFAULT_SEGMENT_SIZE 4096
|
#define DEFAULT_SEGMENT_SIZE 4096
|
||||||
#define DEFAULT_SEGMENT_COUNT 16
|
#define DEFAULT_SEGMENT_COUNT 16
|
||||||
|
|
|
@ -41,19 +41,11 @@ using mozilla::ipc::StringInputStreamParams;
|
||||||
// this enables LogLevel::Debug level information and places all output in
|
// this enables LogLevel::Debug level information and places all output in
|
||||||
// the file nspr.log
|
// the file nspr.log
|
||||||
//
|
//
|
||||||
static PRLogModuleInfo*
|
static LazyLogModule sStorageStreamLog("nsStorageStream");
|
||||||
GetStorageStreamLog()
|
|
||||||
{
|
|
||||||
static PRLogModuleInfo* sLog;
|
|
||||||
if (!sLog) {
|
|
||||||
sLog = PR_NewLogModule("nsStorageStream");
|
|
||||||
}
|
|
||||||
return sLog;
|
|
||||||
}
|
|
||||||
#ifdef LOG
|
#ifdef LOG
|
||||||
#undef LOG
|
#undef LOG
|
||||||
#endif
|
#endif
|
||||||
#define LOG(args) MOZ_LOG(GetStorageStreamLog(), mozilla::LogLevel::Debug, args)
|
#define LOG(args) MOZ_LOG(sStorageStreamLog, mozilla::LogLevel::Debug, args)
|
||||||
|
|
||||||
nsStorageStream::nsStorageStream()
|
nsStorageStream::nsStorageStream()
|
||||||
: mSegmentedBuffer(0), mSegmentSize(0), mWriteInProgress(false),
|
: mSegmentedBuffer(0), mSegmentSize(0), mWriteInProgress(false),
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
LazyLogModule gMozPromiseLog("MozPromise");
|
||||||
|
LazyLogModule gStateWatchingLog("StateWatching");
|
||||||
|
|
||||||
StaticRefPtr<AbstractThread> sMainThread;
|
StaticRefPtr<AbstractThread> sMainThread;
|
||||||
ThreadLocal<AbstractThread*> AbstractThread::sCurrentThreadTLS;
|
ThreadLocal<AbstractThread*> AbstractThread::sCurrentThreadTLS;
|
||||||
|
|
||||||
|
@ -117,9 +120,6 @@ AbstractThread::MainThread()
|
||||||
void
|
void
|
||||||
AbstractThread::InitStatics()
|
AbstractThread::InitStatics()
|
||||||
{
|
{
|
||||||
gMozPromiseLog = PR_NewLogModule("MozPromise");
|
|
||||||
gStateWatchingLog = PR_NewLogModule("StateWatching");
|
|
||||||
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
MOZ_ASSERT(!sMainThread);
|
MOZ_ASSERT(!sMainThread);
|
||||||
nsCOMPtr<nsIThread> mainThread;
|
nsCOMPtr<nsIThread> mainThread;
|
||||||
|
|
|
@ -20,10 +20,9 @@
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
extern PRLogModuleInfo* gMozPromiseLog;
|
extern LazyLogModule gMozPromiseLog;
|
||||||
|
|
||||||
#define PROMISE_LOG(x, ...) \
|
#define PROMISE_LOG(x, ...) \
|
||||||
MOZ_ASSERT(gMozPromiseLog); \
|
|
||||||
MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__))
|
MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__))
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
|
@ -56,10 +56,9 @@
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
extern PRLogModuleInfo* gStateWatchingLog;
|
extern LazyLogModule gStateWatchingLog;
|
||||||
|
|
||||||
#define WATCH_LOG(x, ...) \
|
#define WATCH_LOG(x, ...) \
|
||||||
MOZ_ASSERT(gStateWatchingLog); \
|
|
||||||
MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__))
|
MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -13,19 +13,11 @@
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
static PRLogModuleInfo*
|
static LazyLogModule sEventQueueLog("nsEventQueue");
|
||||||
GetLog()
|
|
||||||
{
|
|
||||||
static PRLogModuleInfo* sLog;
|
|
||||||
if (!sLog) {
|
|
||||||
sLog = PR_NewLogModule("nsEventQueue");
|
|
||||||
}
|
|
||||||
return sLog;
|
|
||||||
}
|
|
||||||
#ifdef LOG
|
#ifdef LOG
|
||||||
#undef LOG
|
#undef LOG
|
||||||
#endif
|
#endif
|
||||||
#define LOG(args) MOZ_LOG(GetLog(), mozilla::LogLevel::Debug, args)
|
#define LOG(args) MOZ_LOG(sEventQueueLog, mozilla::LogLevel::Debug, args)
|
||||||
|
|
||||||
nsEventQueue::nsEventQueue(Mutex& aLock)
|
nsEventQueue::nsEventQueue(Mutex& aLock)
|
||||||
: mHead(nullptr)
|
: mHead(nullptr)
|
||||||
|
|
|
@ -86,19 +86,11 @@ using namespace mozilla::tasktracer;
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
static PRLogModuleInfo*
|
static LazyLogModule sThreadLog("nsThread");
|
||||||
GetThreadLog()
|
|
||||||
{
|
|
||||||
static PRLogModuleInfo* sLog;
|
|
||||||
if (!sLog) {
|
|
||||||
sLog = PR_NewLogModule("nsThread");
|
|
||||||
}
|
|
||||||
return sLog;
|
|
||||||
}
|
|
||||||
#ifdef LOG
|
#ifdef LOG
|
||||||
#undef LOG
|
#undef LOG
|
||||||
#endif
|
#endif
|
||||||
#define LOG(args) MOZ_LOG(GetThreadLog(), mozilla::LogLevel::Debug, args)
|
#define LOG(args) MOZ_LOG(sThreadLog, mozilla::LogLevel::Debug, args)
|
||||||
|
|
||||||
NS_DECL_CI_INTERFACE_GETTER(nsThread)
|
NS_DECL_CI_INTERFACE_GETTER(nsThread)
|
||||||
|
|
||||||
|
|
|
@ -16,19 +16,11 @@
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
static PRLogModuleInfo*
|
static LazyLogModule sThreadPoolLog("nsThreadPool");
|
||||||
GetThreadPoolLog()
|
|
||||||
{
|
|
||||||
static PRLogModuleInfo* sLog;
|
|
||||||
if (!sLog) {
|
|
||||||
sLog = PR_NewLogModule("nsThreadPool");
|
|
||||||
}
|
|
||||||
return sLog;
|
|
||||||
}
|
|
||||||
#ifdef LOG
|
#ifdef LOG
|
||||||
#undef LOG
|
#undef LOG
|
||||||
#endif
|
#endif
|
||||||
#define LOG(args) MOZ_LOG(GetThreadPoolLog(), mozilla::LogLevel::Debug, args)
|
#define LOG(args) MOZ_LOG(sThreadPoolLog, mozilla::LogLevel::Debug, args)
|
||||||
|
|
||||||
// DESIGN:
|
// DESIGN:
|
||||||
// o Allocate anonymous threads.
|
// o Allocate anonymous threads.
|
||||||
|
|
|
@ -40,14 +40,12 @@ static Atomic<int32_t> gGenerator;
|
||||||
static TimerThread* gThread = nullptr;
|
static TimerThread* gThread = nullptr;
|
||||||
|
|
||||||
// This module prints info about the precision of timers.
|
// This module prints info about the precision of timers.
|
||||||
PRLogModuleInfo*
|
static mozilla::LazyLogModule sTimerLog("nsTimerImpl");
|
||||||
|
|
||||||
|
mozilla::LogModule*
|
||||||
GetTimerLog()
|
GetTimerLog()
|
||||||
{
|
{
|
||||||
static PRLogModuleInfo* sLog;
|
return sTimerLog;
|
||||||
if (!sLog) {
|
|
||||||
sLog = PR_NewLogModule("nsTimerImpl");
|
|
||||||
}
|
|
||||||
return sLog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This module prints info about which timers are firing, which is useful for
|
// This module prints info about which timers are firing, which is useful for
|
||||||
|
@ -83,14 +81,12 @@ GetTimerLog()
|
||||||
// More detailed docs are here:
|
// More detailed docs are here:
|
||||||
// https://developer.mozilla.org/en-US/docs/Mozilla/Performance/TimerFirings_logging
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Performance/TimerFirings_logging
|
||||||
//
|
//
|
||||||
PRLogModuleInfo*
|
static mozilla::LazyLogModule sTimerFiringsLog("TimerFirings");
|
||||||
|
|
||||||
|
mozilla::LogModule*
|
||||||
GetTimerFiringsLog()
|
GetTimerFiringsLog()
|
||||||
{
|
{
|
||||||
static PRLogModuleInfo* sLog;
|
return sTimerFiringsLog;
|
||||||
if (!sLog) {
|
|
||||||
sLog = PR_NewLogModule("TimerFirings");
|
|
||||||
}
|
|
||||||
return sLog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
#include "TracedTaskCommon.h"
|
#include "TracedTaskCommon.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern PRLogModuleInfo* GetTimerLog();
|
extern mozilla::LogModule* GetTimerLog();
|
||||||
|
|
||||||
#define NS_TIMER_CID \
|
#define NS_TIMER_CID \
|
||||||
{ /* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */ \
|
{ /* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */ \
|
||||||
|
|
Загрузка…
Ссылка в новой задаче