merge mozilla-inbound to mozilla-central on a CLOSED TREE

This commit is contained in:
Carsten "Tomcat" Book 2015-10-21 15:32:33 +02:00
Родитель fddab43f09 e1cd84d88d
Коммит 894a28d90b
98 изменённых файлов: 2200 добавлений и 1374 удалений

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

@ -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("&quot;");
break;
case '&':
aOut.AppendLiteral("&amp;");
break;
case 0x00A0:
aOut.AppendLiteral("&nbsp;");
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("&lt;");
break;
case '>':
aOut.AppendLiteral("&gt;");
break;
case '&':
aOut.AppendLiteral("&amp;");
break;
case 0x00A0:
aOut.AppendLiteral("&nbsp;");
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("&lt;");
break;
case '>':
aOut.AppendLiteral("&gt;");
break;
case '&':
aOut.AppendLiteral("&amp;");
break;
case 0x00A0:
aOut.AppendLiteral("&nbsp;");
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("&lt;") - 2;
break;
case '>':
extraSpaceNeeded += ArrayLength("&gt;") - 2;
break;
case '&':
extraSpaceNeeded += ArrayLength("&amp;") - 2;
break;
case 0x00A0:
extraSpaceNeeded += ArrayLength("&nbsp;") - 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("&lt;") - 2;
break;
case '>':
extraSpaceNeeded += ArrayLength("&gt;") - 2;
break;
case '&':
extraSpaceNeeded += ArrayLength("&amp;") - 2;
break;
case 0x00A0:
extraSpaceNeeded += ArrayLength("&nbsp;") - 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("&quot;") - 2;
break;
case '&':
extraSpaceNeeded += ArrayLength("&amp;") - 2;
break;
case 0x00A0:
extraSpaceNeeded += ArrayLength("&nbsp;") - 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("&quot;");
break;
case '&':
aOut.AppendLiteral("&amp;");
break;
case 0x00A0:
aOut.AppendLiteral("&nbsp;");
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("&lt;");
break;
case '>':
aOut.AppendLiteral("&gt;");
break;
case '&':
aOut.AppendLiteral("&amp;");
break;
case 0x00A0:
aOut.AppendLiteral("&nbsp;");
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("&lt;");
break;
case '>':
aOut.AppendLiteral("&gt;");
break;
case '&':
aOut.AppendLiteral("&amp;");
break;
case 0x00A0:
aOut.AppendLiteral("&nbsp;");
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("&lt;") - 2;
break;
case '>':
extraSpaceNeeded += ArrayLength("&gt;") - 2;
break;
case '&':
extraSpaceNeeded += ArrayLength("&amp;") - 2;
break;
case 0x00A0:
extraSpaceNeeded += ArrayLength("&nbsp;") - 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("&lt;") - 2;
break;
case '>':
extraSpaceNeeded += ArrayLength("&gt;") - 2;
break;
case '&':
extraSpaceNeeded += ArrayLength("&amp;") - 2;
break;
case 0x00A0:
extraSpaceNeeded += ArrayLength("&nbsp;") - 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("&quot;") - 2;
break;
case '&':
extraSpaceNeeded += ArrayLength("&amp;") - 2;
break;
case 0x00A0:
extraSpaceNeeded += ArrayLength("&nbsp;") - 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"

30
netwerk/base/ARefBase.h Normal file
Просмотреть файл

@ -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,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);

100
xpcom/base/Logging.cpp Normal file
Просмотреть файл

@ -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 */ \