merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-03-17 11:36:52 +01:00
Родитель 3332e17023 0336d90ab9
Коммит 0503c0d6df
282 изменённых файлов: 4466 добавлений и 2476 удалений

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

@ -136,7 +136,7 @@ MustBeAccessible(nsIContent* aContent, DocAccessible* aDocument)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Accessible constructors // Accessible constructors
Accessible* static Accessible*
New_HTMLLink(nsIContent* aContent, Accessible* aContext) New_HTMLLink(nsIContent* aContent, Accessible* aContext)
{ {
// Only some roles truly enjoy life as HTMLLinkAccessibles, for details // Only some roles truly enjoy life as HTMLLinkAccessibles, for details
@ -150,28 +150,28 @@ New_HTMLLink(nsIContent* aContent, Accessible* aContext)
return new HTMLLinkAccessible(aContent, aContext->Document()); return new HTMLLinkAccessible(aContent, aContext->Document());
} }
Accessible* New_HyperText(nsIContent* aContent, Accessible* aContext) static Accessible* New_HyperText(nsIContent* aContent, Accessible* aContext)
{ return new HyperTextAccessibleWrap(aContent, aContext->Document()); } { return new HyperTextAccessibleWrap(aContent, aContext->Document()); }
Accessible* New_HTMLFigcaption(nsIContent* aContent, Accessible* aContext) static Accessible* New_HTMLFigcaption(nsIContent* aContent, Accessible* aContext)
{ return new HTMLFigcaptionAccessible(aContent, aContext->Document()); } { return new HTMLFigcaptionAccessible(aContent, aContext->Document()); }
Accessible* New_HTMLFigure(nsIContent* aContent, Accessible* aContext) static Accessible* New_HTMLFigure(nsIContent* aContent, Accessible* aContext)
{ return new HTMLFigureAccessible(aContent, aContext->Document()); } { return new HTMLFigureAccessible(aContent, aContext->Document()); }
Accessible* New_HTMLLegend(nsIContent* aContent, Accessible* aContext) static Accessible* New_HTMLLegend(nsIContent* aContent, Accessible* aContext)
{ return new HTMLLegendAccessible(aContent, aContext->Document()); } { return new HTMLLegendAccessible(aContent, aContext->Document()); }
Accessible* New_HTMLOption(nsIContent* aContent, Accessible* aContext) static Accessible* New_HTMLOption(nsIContent* aContent, Accessible* aContext)
{ return new HTMLSelectOptionAccessible(aContent, aContext->Document()); } { return new HTMLSelectOptionAccessible(aContent, aContext->Document()); }
Accessible* New_HTMLOptgroup(nsIContent* aContent, Accessible* aContext) static Accessible* New_HTMLOptgroup(nsIContent* aContent, Accessible* aContext)
{ return new HTMLSelectOptGroupAccessible(aContent, aContext->Document()); } { return new HTMLSelectOptGroupAccessible(aContent, aContext->Document()); }
Accessible* New_HTMLList(nsIContent* aContent, Accessible* aContext) static Accessible* New_HTMLList(nsIContent* aContent, Accessible* aContext)
{ return new HTMLListAccessible(aContent, aContext->Document()); } { return new HTMLListAccessible(aContent, aContext->Document()); }
Accessible* static Accessible*
New_HTMLListitem(nsIContent* aContent, Accessible* aContext) New_HTMLListitem(nsIContent* aContent, Accessible* aContext)
{ {
// If list item is a child of accessible list then create an accessible for // If list item is a child of accessible list then create an accessible for
@ -183,7 +183,7 @@ New_HTMLListitem(nsIContent* aContent, Accessible* aContext)
return nullptr; return nullptr;
} }
Accessible* static Accessible*
New_HTMLDefinition(nsIContent* aContent, Accessible* aContext) New_HTMLDefinition(nsIContent* aContent, Accessible* aContext)
{ {
if (aContext->IsList()) if (aContext->IsList())
@ -191,16 +191,16 @@ New_HTMLDefinition(nsIContent* aContent, Accessible* aContext)
return nullptr; return nullptr;
} }
Accessible* New_HTMLLabel(nsIContent* aContent, Accessible* aContext) static Accessible* New_HTMLLabel(nsIContent* aContent, Accessible* aContext)
{ return new HTMLLabelAccessible(aContent, aContext->Document()); } { return new HTMLLabelAccessible(aContent, aContext->Document()); }
Accessible* New_HTMLOutput(nsIContent* aContent, Accessible* aContext) static Accessible* New_HTMLOutput(nsIContent* aContent, Accessible* aContext)
{ return new HTMLOutputAccessible(aContent, aContext->Document()); } { return new HTMLOutputAccessible(aContent, aContext->Document()); }
Accessible* New_HTMLProgress(nsIContent* aContent, Accessible* aContext) static Accessible* New_HTMLProgress(nsIContent* aContent, Accessible* aContext)
{ return new HTMLProgressMeterAccessible(aContent, aContext->Document()); } { return new HTMLProgressMeterAccessible(aContent, aContext->Document()); }
Accessible* static Accessible*
New_HTMLTableHeaderCell(nsIContent* aContent, Accessible* aContext) New_HTMLTableHeaderCell(nsIContent* aContent, Accessible* aContext)
{ {
if (aContext->IsTableRow() && aContext->GetContent() == aContent->GetParent()) if (aContext->IsTableRow() && aContext->GetContent() == aContent->GetParent())
@ -208,7 +208,7 @@ New_HTMLTableHeaderCell(nsIContent* aContent, Accessible* aContext)
return nullptr; return nullptr;
} }
Accessible* static Accessible*
New_HTMLTableHeaderCellIfScope(nsIContent* aContent, Accessible* aContext) New_HTMLTableHeaderCellIfScope(nsIContent* aContent, Accessible* aContext)
{ {
if (aContext->IsTableRow() && aContext->GetContent() == aContent->GetParent() && if (aContext->IsTableRow() && aContext->GetContent() == aContent->GetParent() &&

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

@ -42,6 +42,9 @@ public:
uint32_t ChildrenCount() const { return mChildren.Length(); } uint32_t ChildrenCount() const { return mChildren.Length(); }
ProxyAccessible* ChildAt(uint32_t aIdx) const { return mChildren[aIdx]; } ProxyAccessible* ChildAt(uint32_t aIdx) const { return mChildren[aIdx]; }
// XXX evaluate if this is fast enough.
size_t IndexInParent() const { return mParent->mChildren.IndexOf(this); }
bool MustPruneChildren() const; bool MustPruneChildren() const;
void Shutdown(); void Shutdown();

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

@ -301,22 +301,22 @@ this.Utils = { // jshint ignore:line
}, },
getContentResolution: function _getContentResolution(aAccessible) { getContentResolution: function _getContentResolution(aAccessible) {
let resX = { value: 1 }, resY = { value: 1 }; let res = { value: 1 };
aAccessible.document.window.QueryInterface( aAccessible.document.window.QueryInterface(
Ci.nsIInterfaceRequestor).getInterface( Ci.nsIInterfaceRequestor).getInterface(
Ci.nsIDOMWindowUtils).getResolution(resX, resY); Ci.nsIDOMWindowUtils).getResolution(res);
return [resX.value, resY.value]; return res.value;
}, },
getBounds: function getBounds(aAccessible, aPreserveContentScale) { getBounds: function getBounds(aAccessible, aPreserveContentScale) {
let objX = {}, objY = {}, objW = {}, objH = {}; let objX = {}, objY = {}, objW = {}, objH = {};
aAccessible.getBounds(objX, objY, objW, objH); aAccessible.getBounds(objX, objY, objW, objH);
let [scaleX, scaleY] = aPreserveContentScale ? [1, 1] : let scale = aPreserveContentScale ? 1 :
this.getContentResolution(aAccessible); this.getContentResolution(aAccessible);
return new Rect(objX.value, objY.value, objW.value, objH.value).scale( return new Rect(objX.value, objY.value, objW.value, objH.value).scale(
scaleX, scaleY); scale, scale);
}, },
getTextBounds: function getTextBounds(aAccessible, aStart, aEnd, getTextBounds: function getTextBounds(aAccessible, aStart, aEnd,
@ -326,11 +326,11 @@ this.Utils = { // jshint ignore:line
accText.getRangeExtents(aStart, aEnd, objX, objY, objW, objH, accText.getRangeExtents(aStart, aEnd, objX, objY, objW, objH,
Ci.nsIAccessibleCoordinateType.COORDTYPE_SCREEN_RELATIVE); Ci.nsIAccessibleCoordinateType.COORDTYPE_SCREEN_RELATIVE);
let [scaleX, scaleY] = aPreserveContentScale ? [1, 1] : let scale = aPreserveContentScale ? 1 :
this.getContentResolution(aAccessible); this.getContentResolution(aAccessible);
return new Rect(objX.value, objY.value, objW.value, objH.value).scale( return new Rect(objX.value, objY.value, objW.value, objH.value).scale(
scaleX, scaleY); scale, scale);
}, },
/** /**

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

@ -370,7 +370,8 @@ function testAccessibleTree(aAccOrElmOrID, aAccTree, aFlags)
// Test accessible properties. // Test accessible properties.
for (var prop in accTree) { for (var prop in accTree) {
var msg = "Wrong value of property '" + prop + "' for " + prettyName(acc) + "."; var msg = "Wrong value of property '" + prop + "' for " +
prettyName(acc) + ".";
switch (prop) { switch (prop) {
case "actions": { case "actions": {
@ -451,10 +452,29 @@ function testAccessibleTree(aAccOrElmOrID, aAccTree, aFlags)
var children = acc.children; var children = acc.children;
var childCount = children.length; var childCount = children.length;
is(childCount, accTree.children.length,
"Different amount of expected children of " + prettyName(acc) + ".");
if (accTree.children.length == childCount) { if (accTree.children.length != childCount) {
for (var i = 0; i < Math.max(accTree.children.length, childCount); i++) {
var accChild;
try {
accChild = children.queryElementAt(i, nsIAccessible);
if (!accTree.children[i]) {
ok(false, prettyName(acc) + " has an extra child at index " + i +
" : " + prettyName(accChild));
}
if (accChild.role !== accTree.children[i].role) {
ok(false, prettyName(accTree) + " and " + prettyName(acc) +
" have different children at index " + i + " : " +
prettyName(accTree.children[i]) + ", " + prettyName(accChild));
}
info("Matching " + prettyName(accTree) + " and " + prettyName(acc) +
" child at index " + i + " : " + prettyName(accChild));
} catch (e) {
ok(false, prettyName(accTree) + " has an extra child at index " + i +
" : " + prettyName(accTree.children[i]));
}
}
} else {
if (aFlags & kSkipTreeFullCheck) { if (aFlags & kSkipTreeFullCheck) {
for (var i = 0; i < childCount; i++) { for (var i = 0; i < childCount; i++) {
var child = children.queryElementAt(i, nsIAccessible); var child = children.queryElementAt(i, nsIAccessible);
@ -537,7 +557,8 @@ function testDefunctAccessible(aAcc, aNodeOrId)
ok(!isAccessible(aNodeOrId), ok(!isAccessible(aNodeOrId),
"Accessible for " + aNodeOrId + " wasn't properly shut down!"); "Accessible for " + aNodeOrId + " wasn't properly shut down!");
var msg = " doesn't fail for shut down accessible " + prettyName(aNodeOrId) + "!"; var msg = " doesn't fail for shut down accessible " +
prettyName(aNodeOrId) + "!";
// firstChild // firstChild
var success = false; var success = false;
@ -720,6 +741,10 @@ function prettyName(aIdentifier)
if (aIdentifier instanceof nsIDOMNode) if (aIdentifier instanceof nsIDOMNode)
return "[ " + getNodePrettyName(aIdentifier) + " ]"; return "[ " + getNodePrettyName(aIdentifier) + " ]";
if (aIdentifier && typeof aIdentifier === "object" ) {
return JSON.stringify(aIdentifier);
}
return " '" + aIdentifier + "' "; return " '" + aIdentifier + "' ";
} }

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

@ -16,6 +16,7 @@
#include "IUnknownImpl.h" #include "IUnknownImpl.h"
#include "nsCoreUtils.h" #include "nsCoreUtils.h"
#include "nsIAccessibleTypes.h" #include "nsIAccessibleTypes.h"
#include "mozilla/a11y/PDocAccessible.h"
#include "Relation.h" #include "Relation.h"
#include "nsIPersistentProperties2.h" #include "nsIPersistentProperties2.h"
@ -24,6 +25,8 @@
using namespace mozilla; using namespace mozilla;
using namespace mozilla::a11y; using namespace mozilla::a11y;
template<typename String> static void EscapeAttributeChars(String& aStr);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// ia2Accessible // ia2Accessible
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -65,6 +68,15 @@ ia2Accessible::get_nRelations(long* aNRelations)
if (acc->IsDefunct()) if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED; return CO_E_OBJNOTCONNECTED;
if (acc->IsProxy()) {
// XXX evaluate performance of collecting all relation targets.
nsTArray<RelationType> types;
nsTArray<nsTArray<ProxyAccessible*>> targetSets;
acc->Proxy()->Relations(&types, &targetSets);
*aNRelations = types.Length();
return S_OK;
}
for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
continue; continue;
@ -84,7 +96,7 @@ ia2Accessible::get_relation(long aRelationIndex,
{ {
A11Y_TRYBLOCK_BEGIN A11Y_TRYBLOCK_BEGIN
if (!aRelation) if (!aRelation || aRelationIndex < 0)
return E_INVALIDARG; return E_INVALIDARG;
*aRelation = nullptr; *aRelation = nullptr;
@ -92,6 +104,34 @@ ia2Accessible::get_relation(long aRelationIndex,
if (acc->IsDefunct()) if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED; return CO_E_OBJNOTCONNECTED;
if (acc->IsProxy()) {
nsTArray<RelationType> types;
nsTArray<nsTArray<ProxyAccessible*>> targetSets;
acc->Proxy()->Relations(&types, &targetSets);
size_t targetSetCount = targetSets.Length();
for (size_t i = 0; i < targetSetCount; i++) {
uint32_t relTypeIdx = static_cast<uint32_t>(types[i]);
MOZ_ASSERT(sRelationTypePairs[relTypeIdx].first == types[i]);
if (sRelationTypePairs[relTypeIdx].second == IA2_RELATION_NULL)
continue;
if (static_cast<size_t>(aRelationIndex) == i) {
nsTArray<nsRefPtr<Accessible>> targets;
size_t targetCount = targetSets[i].Length();
for (size_t j = 0; j < targetCount; j++)
targets.AppendElement(WrapperFor(targetSets[i][j]));
nsRefPtr<ia2AccessibleRelation> rel =
new ia2AccessibleRelation(types[i], Move(targets));
rel.forget(aRelation);
return S_OK;
}
}
return E_INVALIDARG;
}
long relIdx = 0; long relIdx = 0;
for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
@ -123,7 +163,7 @@ ia2Accessible::get_relations(long aMaxRelations,
{ {
A11Y_TRYBLOCK_BEGIN A11Y_TRYBLOCK_BEGIN
if (!aRelation || !aNRelations) if (!aRelation || !aNRelations || aMaxRelations <= 0)
return E_INVALIDARG; return E_INVALIDARG;
*aNRelations = 0; *aNRelations = 0;
@ -131,6 +171,34 @@ ia2Accessible::get_relations(long aMaxRelations,
if (acc->IsDefunct()) if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED; return CO_E_OBJNOTCONNECTED;
if (acc->IsProxy()) {
nsTArray<RelationType> types;
nsTArray<nsTArray<ProxyAccessible*>> targetSets;
acc->Proxy()->Relations(&types, &targetSets);
size_t count = std::min(targetSets.Length(),
static_cast<size_t>(aMaxRelations));
size_t i = 0;
while (i < count) {
uint32_t relTypeIdx = static_cast<uint32_t>(types[i]);
if (sRelationTypePairs[relTypeIdx].second == IA2_RELATION_NULL)
continue;
size_t targetCount = targetSets[i].Length();
nsTArray<nsRefPtr<Accessible>> targets(targetCount);
for (size_t j = 0; j < targetCount; j++)
targets.AppendElement(WrapperFor(targetSets[i][j]));
nsRefPtr<ia2AccessibleRelation> rel =
new ia2AccessibleRelation(types[i], Move(targets));
rel.forget(aRelation + i);
i++;
}
*aNRelations = i;
return S_OK;
}
for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs) && for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs) &&
*aNRelations < aMaxRelations; idx++) { *aNRelations < aMaxRelations; idx++) {
if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
@ -169,7 +237,11 @@ ia2Accessible::role(long* aRole)
*aRole = ia2Role; \ *aRole = ia2Role; \
break; break;
a11y::role geckoRole = acc->Role(); a11y::role geckoRole;
if (acc->IsProxy())
geckoRole = acc->Proxy()->Role();
else
geckoRole = acc->Role();
switch (geckoRole) { switch (geckoRole) {
#include "RoleMap.h" #include "RoleMap.h"
default: default:
@ -180,10 +252,16 @@ ia2Accessible::role(long* aRole)
// Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call
// the IA2 role a ROLE_OUTLINEITEM. // the IA2 role a ROLE_OUTLINEITEM.
if (geckoRole == roles::ROW) { if (acc->IsProxy()) {
Accessible* xpParent = acc->Parent(); if (geckoRole == roles::ROW && acc->Proxy()->Parent() &&
if (xpParent && xpParent->Role() == roles::TREE_TABLE) acc->Proxy()->Parent()->Role() == roles::TREE_TABLE)
*aRole = ROLE_SYSTEM_OUTLINEITEM; *aRole = ROLE_SYSTEM_OUTLINEITEM;
} else {
if (geckoRole == roles::ROW) {
Accessible* xpParent = acc->Parent();
if (xpParent && xpParent->Role() == roles::TREE_TABLE)
*aRole = ROLE_SYSTEM_OUTLINEITEM;
}
} }
return S_OK; return S_OK;
@ -274,7 +352,16 @@ ia2Accessible::get_states(AccessibleStates* aStates)
// XXX: bug 344674 should come with better approach that we have here. // XXX: bug 344674 should come with better approach that we have here.
AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
uint64_t state = acc->State(); if (acc->IsDefunct()) {
*aStates = IA2_STATE_DEFUNCT;
return CO_E_OBJNOTCONNECTED;
}
uint64_t state;
if (acc->IsProxy())
state = acc->Proxy()->State();
else
state = acc->State();
if (state & states::INVALID) if (state & states::INVALID)
*aStates |= IA2_STATE_INVALID_ENTRY; *aStates |= IA2_STATE_INVALID_ENTRY;
@ -446,7 +533,11 @@ ia2Accessible::get_indexInParent(long* aIndexInParent)
if (acc->IsDefunct()) if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED; return CO_E_OBJNOTCONNECTED;
*aIndexInParent = acc->IndexInParent(); if (acc->IsProxy())
*aIndexInParent = acc->Proxy()->IndexInParent();
else
*aIndexInParent = acc->IndexInParent();
if (*aIndexInParent == -1) if (*aIndexInParent == -1)
return S_FALSE; return S_FALSE;
@ -521,8 +612,29 @@ ia2Accessible::get_attributes(BSTR* aAttributes)
// The format is name:value;name:value; with \ for escaping these // The format is name:value;name:value; with \ for escaping these
// characters ":;=,\". // characters ":;=,\".
nsCOMPtr<nsIPersistentProperties> attributes = acc->Attributes(); if (!acc->IsProxy()) {
return ConvertToIA2Attributes(attributes, aAttributes); nsCOMPtr<nsIPersistentProperties> attributes = acc->Attributes();
return ConvertToIA2Attributes(attributes, aAttributes);
}
nsTArray<Attribute> attrs;
acc->Proxy()->Attributes(&attrs);
nsString attrStr;
size_t attrCount = attrs.Length();
for (size_t i = 0; i < attrCount; i++) {
EscapeAttributeChars(attrs[i].Name());
EscapeAttributeChars(attrs[i].Value());
AppendUTF8toUTF16(attrs[i].Name(), attrStr);
attrStr.Append(':');
attrStr.Append(attrs[i].Value());
attrStr.Append(';');
}
if (attrStr.IsEmpty())
return S_FALSE;
*aAttributes = ::SysAllocStringLen(attrStr.get(), attrStr.Length());
return *aAttributes ? S_OK : E_OUTOFMEMORY;
A11Y_TRYBLOCK_END A11Y_TRYBLOCK_END
} }
@ -567,7 +679,7 @@ ia2Accessible::get_relationTargetsOfType(BSTR aType,
{ {
A11Y_TRYBLOCK_BEGIN A11Y_TRYBLOCK_BEGIN
if (!aTargets || !aNTargets) if (!aTargets || !aNTargets || aMaxTargets < 0)
return E_INVALIDARG; return E_INVALIDARG;
*aNTargets = 0; *aNTargets = 0;
@ -585,13 +697,23 @@ ia2Accessible::get_relationTargetsOfType(BSTR aType,
if (acc->IsDefunct()) if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED; return CO_E_OBJNOTCONNECTED;
Relation rel = acc->RelationByType(*relationType);
nsTArray<Accessible*> targets; nsTArray<Accessible*> targets;
Accessible* target = nullptr; if (acc->IsProxy()) {
while ((target = rel.Next()) && nsTArray<ProxyAccessible*> targetProxies =
static_cast<long>(targets.Length()) <= aMaxTargets) acc->Proxy()->RelationByType(*relationType);
targets.AppendElement(target);
size_t targetCount = aMaxTargets;
if (targetProxies.Length() < targetCount)
targetCount = targetProxies.Length();
for (size_t i = 0; i < targetCount; i++)
targets.AppendElement(WrapperFor(targetProxies[i]));
} else {
Relation rel = acc->RelationByType(*relationType);
Accessible* target = nullptr;
while ((target = rel.Next()) &&
static_cast<long>(targets.Length()) <= aMaxTargets)
targets.AppendElement(target);
}
*aNTargets = targets.Length(); *aNTargets = targets.Length();
*aTargets = static_cast<IUnknown**>( *aTargets = static_cast<IUnknown**>(
@ -613,6 +735,18 @@ ia2Accessible::get_relationTargetsOfType(BSTR aType,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Helpers // Helpers
template<typename String>
static inline void
EscapeAttributeChars(String& aStr)
{
int32_t offset = 0;
static const char kCharsToEscape[] = ":;=,\\";
while ((offset = aStr.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
aStr.Insert('\\', offset);
offset += 2;
}
}
HRESULT HRESULT
ia2Accessible::ConvertToIA2Attributes(nsIPersistentProperties* aAttributes, ia2Accessible::ConvertToIA2Attributes(nsIPersistentProperties* aAttributes,
BSTR* aIA2Attributes) BSTR* aIA2Attributes)
@ -632,8 +766,6 @@ ia2Accessible::ConvertToIA2Attributes(nsIPersistentProperties* aAttributes,
nsAutoString strAttrs; nsAutoString strAttrs;
const char kCharsToEscape[] = ":;=,\\";
bool hasMore = false; bool hasMore = false;
while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) { while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> propSupports; nsCOMPtr<nsISupports> propSupports;
@ -647,21 +779,13 @@ ia2Accessible::ConvertToIA2Attributes(nsIPersistentProperties* aAttributes,
if (NS_FAILED(propElem->GetKey(name))) if (NS_FAILED(propElem->GetKey(name)))
return E_FAIL; return E_FAIL;
int32_t offset = 0; EscapeAttributeChars(name);
while ((offset = name.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
name.Insert('\\', offset);
offset += 2;
}
nsAutoString value; nsAutoString value;
if (NS_FAILED(propElem->GetValue(value))) if (NS_FAILED(propElem->GetValue(value)))
return E_FAIL; return E_FAIL;
offset = 0; EscapeAttributeChars(value);
while ((offset = value.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
value.Insert('\\', offset);
offset += 2;
}
AppendUTF8toUTF16(name, strAttrs); AppendUTF8toUTF16(name, strAttrs);
strAttrs.Append(':'); strAttrs.Append(':');

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

@ -24,6 +24,10 @@ class ia2AccessibleRelation MOZ_FINAL : public IAccessibleRelation
public: public:
ia2AccessibleRelation(RelationType aType, Relation* aRel); ia2AccessibleRelation(RelationType aType, Relation* aRel);
ia2AccessibleRelation(RelationType aType,
nsTArray<nsRefPtr<Accessible>>&& aTargets) :
mType(aType), mTargets(Move(aTargets)) {}
// IUnknown // IUnknown
DECL_IUNKNOWN DECL_IUNKNOWN

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

@ -52,3 +52,5 @@ if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['NOMINMAX'] = True DEFINES['NOMINMAX'] = True
FAIL_ON_WARNINGS = True FAIL_ON_WARNINGS = True
include('/ipc/chromium/chromium-config.mozbuild')

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

@ -694,6 +694,9 @@ pref("ui.scrollbarFadeDuration", 200);
// Scrollbar position follows the document `dir` attribute // Scrollbar position follows the document `dir` attribute
pref("layout.scrollbar.side", 1); pref("layout.scrollbar.side", 1);
// CSS Scroll Snapping
pref("layout.css.scroll-snap.enabled", true);
// Enable the ProcessPriorityManager, and give processes with no visible // Enable the ProcessPriorityManager, and give processes with no visible
// documents a 1s grace period before they're eligible to be marked as // documents a 1s grace period before they're eligible to be marked as
// background. Background processes that are perceivable due to playing // background. Background processes that are perceivable due to playing

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

@ -1231,7 +1231,7 @@ pref("security.sandbox.windows.log.stackTraceDepth", 0);
// This setting is read when the content process is started. On Mac the content // This setting is read when the content process is started. On Mac the content
// process is killed when all windows are closed, so a change will take effect // process is killed when all windows are closed, so a change will take effect
// when the 1st window is opened. // when the 1st window is opened.
pref("security.sandbox.content.level", 0); pref("security.sandbox.content.level", 1);
#endif #endif
// This pref governs whether we attempt to work around problems caused by // This pref governs whether we attempt to work around problems caused by

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

@ -182,7 +182,6 @@ skip-if = e10s # Bug 866413 - PageInfo doesn't work in e10s
[browser_bug462289.js] [browser_bug462289.js]
skip-if = toolkit == "cocoa" || e10s # Bug 1102017 - middle-button mousedown on selected tab2 does not activate tab - Didn't expect [object XULElement], but got it skip-if = toolkit == "cocoa" || e10s # Bug 1102017 - middle-button mousedown on selected tab2 does not activate tab - Didn't expect [object XULElement], but got it
[browser_bug462673.js] [browser_bug462673.js]
skip-if = e10s # Bug 1093404 - test expects sync window opening from content and is disappointed in that expectation
[browser_bug477014.js] [browser_bug477014.js]
[browser_bug479408.js] [browser_bug479408.js]
skip-if = buildapp == 'mulet' skip-if = buildapp == 'mulet'

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

@ -1,61 +1,36 @@
var runs = [ add_task(function* () {
function (win, tabbrowser, tab) {
is(tabbrowser.browsers.length, 2, "test_bug462673.html has opened a second tab");
is(tabbrowser.selectedTab, tab.nextSibling, "dependent tab is selected");
tabbrowser.removeTab(tab);
// Closing a tab will also close its parent chrome window, but async
executeSoon(function() {
ok(win.closed, "Window is closed");
testComplete(win);
});
},
function (win, tabbrowser, tab) {
var newTab = tabbrowser.addTab();
var newBrowser = newTab.linkedBrowser;
tabbrowser.removeTab(tab);
ok(!win.closed, "Window stays open");
if (!win.closed) {
is(tabbrowser.tabContainer.childElementCount, 1, "Window has one tab");
is(tabbrowser.browsers.length, 1, "Window has one browser");
is(tabbrowser.selectedTab, newTab, "Remaining tab is selected");
is(tabbrowser.selectedBrowser, newBrowser, "Browser for remaining tab is selected");
is(tabbrowser.mTabBox.selectedPanel, newBrowser.parentNode.parentNode.parentNode.parentNode, "Panel for remaining tab is selected");
}
testComplete(win);
}
];
function test() {
waitForExplicitFinish();
runOneTest();
}
function testComplete(win) {
win.close();
if (runs.length)
runOneTest();
else
finish();
}
function runOneTest() {
var win = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no"); var win = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no");
yield SimpleTest.promiseFocus(win);
win.addEventListener("load", function () { let tab = win.gBrowser.tabContainer.firstChild;
win.removeEventListener("load", arguments.callee, false); yield promiseTabLoadEvent(tab, getRootDirectory(gTestPath) + "test_bug462673.html");
var tab = win.gBrowser.tabContainer.firstChild; is(win.gBrowser.browsers.length, 2, "test_bug462673.html has opened a second tab");
var browser = tab.linkedBrowser; is(win.gBrowser.selectedTab, tab.nextSibling, "dependent tab is selected");
win.gBrowser.removeTab(tab);
browser.addEventListener("load", function () { // Closing a tab will also close its parent chrome window, but async
browser.removeEventListener("load", arguments.callee, true); yield promiseWindowWillBeClosed(win);
});
executeSoon(function () { add_task(function* () {
runs.shift()(win, win.gBrowser, tab); var win = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no");
}); yield SimpleTest.promiseFocus(win);
}, true);
var rootDir = getRootDirectory(gTestPath); let tab = win.gBrowser.tabContainer.firstChild;
browser.contentWindow.location = rootDir + "test_bug462673.html" yield promiseTabLoadEvent(tab, getRootDirectory(gTestPath) + "test_bug462673.html");
}, false);
} var newTab = win.gBrowser.addTab();
var newBrowser = newTab.linkedBrowser;
win.gBrowser.removeTab(tab);
ok(!win.closed, "Window stays open");
if (!win.closed) {
is(win.gBrowser.tabContainer.childElementCount, 1, "Window has one tab");
is(win.gBrowser.browsers.length, 1, "Window has one browser");
is(win.gBrowser.selectedTab, newTab, "Remaining tab is selected");
is(win.gBrowser.selectedBrowser, newBrowser, "Browser for remaining tab is selected");
is(win.gBrowser.mTabBox.selectedPanel, newBrowser.parentNode.parentNode.parentNode.parentNode, "Panel for remaining tab is selected");
}
yield promiseWindowClosed(win);
});

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

@ -34,7 +34,7 @@ function test() {
chain: null, chain: null,
loc: [[1, 4], [1, 7]] loc: [[1, 4], [1, 7]]
}); });
verify("\nvar\nfoo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression", { verify("\nvar\nfoo\n=\n(\n)=>\n{\n}\n", e => e.type == "ArrowFunctionExpression", {
name: "foo", name: "foo",
chain: null, chain: null,
loc: [[3, 0], [3, 3]] loc: [[3, 0], [3, 3]]
@ -45,31 +45,31 @@ function test() {
verify("foo=()=>{}", e => e.type == "ArrowFunctionExpression", verify("foo=()=>{}", e => e.type == "ArrowFunctionExpression",
{ name: "foo", chain: [], loc: [[1, 0], [1, 3]] }); { name: "foo", chain: [], loc: [[1, 0], [1, 3]] });
verify("\nfoo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nfoo\n=\n(\n)=>\n{\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "foo", chain: [], loc: [[2, 0], [2, 3]] }); { name: "foo", chain: [], loc: [[2, 0], [2, 3]] });
verify("foo.bar=()=>{}", e => e.type == "ArrowFunctionExpression", verify("foo.bar=()=>{}", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["foo"], loc: [[1, 0], [1, 7]] }); { name: "bar", chain: ["foo"], loc: [[1, 0], [1, 7]] });
verify("\nfoo.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nfoo.bar\n=\n(\n)=>\n{\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["foo"], loc: [[2, 0], [2, 7]] }); { name: "bar", chain: ["foo"], loc: [[2, 0], [2, 7]] });
verify("this.foo=()=>{}", e => e.type == "ArrowFunctionExpression", verify("this.foo=()=>{}", e => e.type == "ArrowFunctionExpression",
{ name: "foo", chain: ["this"], loc: [[1, 0], [1, 8]] }); { name: "foo", chain: ["this"], loc: [[1, 0], [1, 8]] });
verify("\nthis.foo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nthis.foo\n=\n(\n)=>\n{\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "foo", chain: ["this"], loc: [[2, 0], [2, 8]] }); { name: "foo", chain: ["this"], loc: [[2, 0], [2, 8]] });
verify("this.foo.bar=()=>{}", e => e.type == "ArrowFunctionExpression", verify("this.foo.bar=()=>{}", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["this", "foo"], loc: [[1, 0], [1, 12]] }); { name: "bar", chain: ["this", "foo"], loc: [[1, 0], [1, 12]] });
verify("\nthis.foo.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nthis.foo.bar\n=\n(\n)=>\n{\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["this", "foo"], loc: [[2, 0], [2, 12]] }); { name: "bar", chain: ["this", "foo"], loc: [[2, 0], [2, 12]] });
verify("foo.this.bar=()=>{}", e => e.type == "ArrowFunctionExpression", verify("foo.this.bar=()=>{}", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["foo", "this"], loc: [[1, 0], [1, 12]] }); { name: "bar", chain: ["foo", "this"], loc: [[1, 0], [1, 12]] });
verify("\nfoo.this.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nfoo.this.bar\n=\n(\n)=>\n{\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["foo", "this"], loc: [[2, 0], [2, 12]] }); { name: "bar", chain: ["foo", "this"], loc: [[2, 0], [2, 12]] });
// ObjectExpression // ObjectExpression
@ -77,13 +77,13 @@ function test() {
verify("({foo:()=>{}})", e => e.type == "ArrowFunctionExpression", verify("({foo:()=>{}})", e => e.type == "ArrowFunctionExpression",
{ name: "foo", chain: [], loc: [[1, 2], [1, 5]] }); { name: "foo", chain: [], loc: [[1, 2], [1, 5]] });
verify("(\n{\nfoo\n:\n(\n)\n=>\n{\n}\n}\n)", e => e.type == "ArrowFunctionExpression", verify("(\n{\nfoo\n:\n(\n)=>\n{\n}\n}\n)", e => e.type == "ArrowFunctionExpression",
{ name: "foo", chain: [], loc: [[3, 0], [3, 3]] }); { name: "foo", chain: [], loc: [[3, 0], [3, 3]] });
verify("({foo:{bar:()=>{}}})", e => e.type == "ArrowFunctionExpression", verify("({foo:{bar:()=>{}}})", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["foo"], loc: [[1, 7], [1, 10]] }); { name: "bar", chain: ["foo"], loc: [[1, 7], [1, 10]] });
verify("(\n{\nfoo\n:\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n}\n)", e => e.type == "ArrowFunctionExpression", verify("(\n{\nfoo\n:\n{\nbar\n:\n(\n)=>\n{\n}\n}\n}\n)", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["foo"], loc: [[6, 0], [6, 3]] }); { name: "bar", chain: ["foo"], loc: [[6, 0], [6, 3]] });
// AssignmentExpression + ObjectExpression // AssignmentExpression + ObjectExpression
@ -91,61 +91,61 @@ function test() {
verify("foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression", verify("foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["foo"], loc: [[1, 5], [1, 8]] }); { name: "bar", chain: ["foo"], loc: [[1, 5], [1, 8]] });
verify("\nfoo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nfoo\n=\n{\nbar\n:\n(\n)=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["foo"], loc: [[5, 0], [5, 3]] }); { name: "bar", chain: ["foo"], loc: [[5, 0], [5, 3]] });
verify("foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression", verify("foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["foo", "bar"], loc: [[1, 10], [1, 13]] }); { name: "baz", chain: ["foo", "bar"], loc: [[1, 10], [1, 13]] });
verify("\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["foo", "bar"], loc: [[8, 0], [8, 3]] }); { name: "baz", chain: ["foo", "bar"], loc: [[8, 0], [8, 3]] });
verify("nested.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression", verify("nested.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["nested", "foo"], loc: [[1, 12], [1, 15]] }); { name: "bar", chain: ["nested", "foo"], loc: [[1, 12], [1, 15]] });
verify("\nnested.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nnested.foo\n=\n{\nbar\n:\n(\n)=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["nested", "foo"], loc: [[5, 0], [5, 3]] }); { name: "bar", chain: ["nested", "foo"], loc: [[5, 0], [5, 3]] });
verify("nested.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression", verify("nested.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["nested", "foo", "bar"], loc: [[1, 17], [1, 20]] }); { name: "baz", chain: ["nested", "foo", "bar"], loc: [[1, 17], [1, 20]] });
verify("\nnested.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nnested.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["nested", "foo", "bar"], loc: [[8, 0], [8, 3]] }); { name: "baz", chain: ["nested", "foo", "bar"], loc: [[8, 0], [8, 3]] });
verify("this.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression", verify("this.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["this", "foo"], loc: [[1, 10], [1, 13]] }); { name: "bar", chain: ["this", "foo"], loc: [[1, 10], [1, 13]] });
verify("\nthis.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nthis.foo\n=\n{\nbar\n:\n(\n)=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["this", "foo"], loc: [[5, 0], [5, 3]] }); { name: "bar", chain: ["this", "foo"], loc: [[5, 0], [5, 3]] });
verify("this.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression", verify("this.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["this", "foo", "bar"], loc: [[1, 15], [1, 18]] }); { name: "baz", chain: ["this", "foo", "bar"], loc: [[1, 15], [1, 18]] });
verify("\nthis.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nthis.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["this", "foo", "bar"], loc: [[8, 0], [8, 3]] }); { name: "baz", chain: ["this", "foo", "bar"], loc: [[8, 0], [8, 3]] });
verify("this.nested.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression", verify("this.nested.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["this", "nested", "foo"], loc: [[1, 17], [1, 20]] }); { name: "bar", chain: ["this", "nested", "foo"], loc: [[1, 17], [1, 20]] });
verify("\nthis.nested.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nthis.nested.foo\n=\n{\nbar\n:\n(\n)=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["this", "nested", "foo"], loc: [[5, 0], [5, 3]] }); { name: "bar", chain: ["this", "nested", "foo"], loc: [[5, 0], [5, 3]] });
verify("this.nested.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression", verify("this.nested.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["this", "nested", "foo", "bar"], loc: [[1, 22], [1, 25]] }); { name: "baz", chain: ["this", "nested", "foo", "bar"], loc: [[1, 22], [1, 25]] });
verify("\nthis.nested.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nthis.nested.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["this", "nested", "foo", "bar"], loc: [[8, 0], [8, 3]] }); { name: "baz", chain: ["this", "nested", "foo", "bar"], loc: [[8, 0], [8, 3]] });
verify("nested.this.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression", verify("nested.this.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["nested", "this", "foo"], loc: [[1, 17], [1, 20]] }); { name: "bar", chain: ["nested", "this", "foo"], loc: [[1, 17], [1, 20]] });
verify("\nnested.this.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nnested.this.foo\n=\n{\nbar\n:\n(\n)=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["nested", "this", "foo"], loc: [[5, 0], [5, 3]] }); { name: "bar", chain: ["nested", "this", "foo"], loc: [[5, 0], [5, 3]] });
verify("nested.this.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression", verify("nested.this.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["nested", "this", "foo", "bar"], loc: [[1, 22], [1, 25]] }); { name: "baz", chain: ["nested", "this", "foo", "bar"], loc: [[1, 22], [1, 25]] });
verify("\nnested.this.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nnested.this.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["nested", "this", "foo", "bar"], loc: [[8, 0], [8, 3]] }); { name: "baz", chain: ["nested", "this", "foo", "bar"], loc: [[8, 0], [8, 3]] });
// VariableDeclarator + AssignmentExpression + ObjectExpression // VariableDeclarator + AssignmentExpression + ObjectExpression
@ -153,13 +153,13 @@ function test() {
verify("let foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression", verify("let foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["foo"], loc: [[1, 9], [1, 12]] }); { name: "bar", chain: ["foo"], loc: [[1, 9], [1, 12]] });
verify("\nlet\nfoo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\nfoo\n=\n{\nbar\n:\n(\n)=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["foo"], loc: [[6, 0], [6, 3]] }); { name: "bar", chain: ["foo"], loc: [[6, 0], [6, 3]] });
verify("let foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression", verify("let foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["foo", "bar"], loc: [[1, 14], [1, 17]] }); { name: "baz", chain: ["foo", "bar"], loc: [[1, 14], [1, 17]] });
verify("\nlet\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["foo", "bar"], loc: [[9, 0], [9, 3]] }); { name: "baz", chain: ["foo", "bar"], loc: [[9, 0], [9, 3]] });
// New/CallExpression + AssignmentExpression + ObjectExpression // New/CallExpression + AssignmentExpression + ObjectExpression
@ -167,61 +167,61 @@ function test() {
verify("foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression", verify("foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: [], loc: [[1, 5], [1, 8]] }); { name: "bar", chain: [], loc: [[1, 5], [1, 8]] });
verify("\nfoo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nfoo\n(\n{\nbar\n:\n(\n)=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] }); { name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
verify("foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression", verify("foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["bar"], loc: [[1, 10], [1, 13]] }); { name: "baz", chain: ["bar"], loc: [[1, 10], [1, 13]] });
verify("\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] }); { name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
verify("nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression", verify("nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: [], loc: [[1, 12], [1, 15]] }); { name: "bar", chain: [], loc: [[1, 12], [1, 15]] });
verify("\nnested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nnested.foo\n(\n{\nbar\n:\n(\n)=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] }); { name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
verify("nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression", verify("nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["bar"], loc: [[1, 17], [1, 20]] }); { name: "baz", chain: ["bar"], loc: [[1, 17], [1, 20]] });
verify("\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] }); { name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
verify("this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression", verify("this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: [], loc: [[1, 10], [1, 13]] }); { name: "bar", chain: [], loc: [[1, 10], [1, 13]] });
verify("\nthis.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nthis.foo\n(\n{\nbar\n:\n(\n)=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] }); { name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
verify("this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression", verify("this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["bar"], loc: [[1, 15], [1, 18]] }); { name: "baz", chain: ["bar"], loc: [[1, 15], [1, 18]] });
verify("\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] }); { name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
verify("this.nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression", verify("this.nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: [], loc: [[1, 17], [1, 20]] }); { name: "bar", chain: [], loc: [[1, 17], [1, 20]] });
verify("\nthis.nested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nthis.nested.foo\n(\n{\nbar\n:\n(\n)=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] }); { name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
verify("this.nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression", verify("this.nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["bar"], loc: [[1, 22], [1, 25]] }); { name: "baz", chain: ["bar"], loc: [[1, 22], [1, 25]] });
verify("\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] }); { name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
verify("nested.this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression", verify("nested.this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: [], loc: [[1, 17], [1, 20]] }); { name: "bar", chain: [], loc: [[1, 17], [1, 20]] });
verify("\nnested.this.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nnested.this.foo\n(\n{\nbar\n:\n(\n)=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] }); { name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
verify("nested.this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression", verify("nested.this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["bar"], loc: [[1, 22], [1, 25]] }); { name: "baz", chain: ["bar"], loc: [[1, 22], [1, 25]] });
verify("\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] }); { name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
// New/CallExpression + VariableDeclarator + AssignmentExpression + ObjectExpression // New/CallExpression + VariableDeclarator + AssignmentExpression + ObjectExpression
@ -229,61 +229,61 @@ function test() {
verify("let target=foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression", verify("let target=foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["target"], loc: [[1, 16], [1, 19]] }); { name: "bar", chain: ["target"], loc: [[1, 16], [1, 19]] });
verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\n(\n)=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] }); { name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
verify("let target=foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression", verify("let target=foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["target", "bar"], loc: [[1, 21], [1, 24]] }); { name: "baz", chain: ["target", "bar"], loc: [[1, 21], [1, 24]] });
verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] }); { name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
verify("let target=nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression", verify("let target=nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["target"], loc: [[1, 23], [1, 26]] }); { name: "bar", chain: ["target"], loc: [[1, 23], [1, 26]] });
verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\n(\n)=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] }); { name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
verify("let target=nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression", verify("let target=nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["target", "bar"], loc: [[1, 28], [1, 31]] }); { name: "baz", chain: ["target", "bar"], loc: [[1, 28], [1, 31]] });
verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] }); { name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
verify("let target=this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression", verify("let target=this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["target"], loc: [[1, 21], [1, 24]] }); { name: "bar", chain: ["target"], loc: [[1, 21], [1, 24]] });
verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\n(\n)=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] }); { name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
verify("let target=this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression", verify("let target=this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["target", "bar"], loc: [[1, 26], [1, 29]] }); { name: "baz", chain: ["target", "bar"], loc: [[1, 26], [1, 29]] });
verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] }); { name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
verify("let target=this.nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression", verify("let target=this.nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["target"], loc: [[1, 28], [1, 31]] }); { name: "bar", chain: ["target"], loc: [[1, 28], [1, 31]] });
verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\n(\n)=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] }); { name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
verify("let target=this.nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression", verify("let target=this.nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["target", "bar"], loc: [[1, 33], [1, 36]] }); { name: "baz", chain: ["target", "bar"], loc: [[1, 33], [1, 36]] });
verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] }); { name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
verify("let target=nested.this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression", verify("let target=nested.this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["target"], loc: [[1, 28], [1, 31]] }); { name: "bar", chain: ["target"], loc: [[1, 28], [1, 31]] });
verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\n(\n)=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] }); { name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
verify("let target=nested.this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression", verify("let target=nested.this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["target", "bar"], loc: [[1, 33], [1, 36]] }); { name: "baz", chain: ["target", "bar"], loc: [[1, 33], [1, 36]] });
verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression", verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] }); { name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
finish(); finish();

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

@ -737,6 +737,18 @@
@RESPATH@/res/table-remove-row-active.gif @RESPATH@/res/table-remove-row-active.gif
@RESPATH@/res/table-remove-row-hover.gif @RESPATH@/res/table-remove-row-hover.gif
@RESPATH@/res/table-remove-row.gif @RESPATH@/res/table-remove-row.gif
@RESPATH@/res/text_caret.png
@RESPATH@/res/text_caret@1.5x.png
@RESPATH@/res/text_caret@2.25x.png
@RESPATH@/res/text_caret@2x.png
@RESPATH@/res/text_caret_tilt_left.png
@RESPATH@/res/text_caret_tilt_left@1.5x.png
@RESPATH@/res/text_caret_tilt_left@2.25x.png
@RESPATH@/res/text_caret_tilt_left@2x.png
@RESPATH@/res/text_caret_tilt_right.png
@RESPATH@/res/text_caret_tilt_right@1.5x.png
@RESPATH@/res/text_caret_tilt_right@2.25x.png
@RESPATH@/res/text_caret_tilt_right@2x.png
@RESPATH@/res/grabber.gif @RESPATH@/res/grabber.gif
#ifdef XP_MACOSX #ifdef XP_MACOSX
@RESPATH@/res/cursors/* @RESPATH@/res/cursors/*

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

@ -339,10 +339,9 @@
.defaultView .defaultView
.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils); .getInterface(Components.interfaces.nsIDOMWindowUtils);
let resx = {}, resy = {}; let res = {};
cwu.getResolution(resx, resy); cwu.getResolution(res);
// Resolution set by the apzc and is symmetric. return res.value;
return resx.value;
]]></getter> ]]></getter>
</property> </property>

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

@ -852,9 +852,15 @@ nsContentSink::PrefetchDNS(const nsAString &aHref)
if (!uri) { if (!uri) {
return; return;
} }
nsAutoCString host; nsresult rv;
uri->GetHost(host); bool isLocalResource = false;
CopyUTF8toUTF16(host, hostname); rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
&isLocalResource);
if (NS_SUCCEEDED(rv) && !isLocalResource) {
nsAutoCString host;
uri->GetHost(host);
CopyUTF8toUTF16(host, hostname);
}
} }
if (!hostname.IsEmpty() && nsHTMLDNSPrefetch::IsAllowed(mDocument)) { if (!hostname.IsEmpty() && nsHTMLDNSPrefetch::IsAllowed(mDocument)) {

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

@ -6491,6 +6491,11 @@ nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
AutoJSAPI jsapi; AutoJSAPI jsapi;
jsapi.Init(); jsapi.Init();
JSContext* cx = jsapi.cx(); JSContext* cx = jsapi.cx();
// Failure to create or run the regexp results in the invalid pattern
// matching, but we can still report the error to the console.
jsapi.TakeOwnershipOfErrorReporting();
// We can use the junk scope here, because we're just using it for // We can use the junk scope here, because we're just using it for
// regexp evaluation, not actual script execution. // regexp evaluation, not actual script execution.
JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope()); JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope());
@ -6504,7 +6509,6 @@ nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
static_cast<char16_t*>(aPattern.BeginWriting()), static_cast<char16_t*>(aPattern.BeginWriting()),
aPattern.Length(), 0)); aPattern.Length(), 0));
if (!re) { if (!re) {
JS_ClearPendingException(cx);
return true; return true;
} }
@ -6513,7 +6517,6 @@ nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
if (!JS_ExecuteRegExpNoStatics(cx, re, if (!JS_ExecuteRegExpNoStatics(cx, re,
static_cast<char16_t*>(aValue.BeginWriting()), static_cast<char16_t*>(aValue.BeginWriting()),
aValue.Length(), &idx, true, &rval)) { aValue.Length(), &idx, true, &rval)) {
JS_ClearPendingException(cx);
return true; return true;
} }

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

@ -490,7 +490,7 @@ nsDOMWindowUtils::SetDisplayPortBaseForElement(int32_t aX,
} }
NS_IMETHODIMP NS_IMETHODIMP
nsDOMWindowUtils::SetResolution(float aXResolution, float aYResolution) nsDOMWindowUtils::SetResolution(float aResolution)
{ {
if (!nsContentUtils::IsCallerChrome()) { if (!nsContentUtils::IsCallerChrome()) {
return NS_ERROR_DOM_SECURITY_ERR; return NS_ERROR_DOM_SECURITY_ERR;
@ -503,15 +503,15 @@ nsDOMWindowUtils::SetResolution(float aXResolution, float aYResolution)
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable(); nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
if (sf) { if (sf) {
sf->SetResolution(gfxSize(aXResolution, aYResolution)); sf->SetResolution(aResolution);
presShell->SetResolution(aXResolution, aYResolution); presShell->SetResolution(aResolution);
} }
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
nsDOMWindowUtils::SetResolutionAndScaleTo(float aXResolution, float aYResolution) nsDOMWindowUtils::SetResolutionAndScaleTo(float aResolution)
{ {
if (!nsContentUtils::IsCallerChrome()) { if (!nsContentUtils::IsCallerChrome()) {
return NS_ERROR_DOM_SECURITY_ERR; return NS_ERROR_DOM_SECURITY_ERR;
@ -524,15 +524,15 @@ nsDOMWindowUtils::SetResolutionAndScaleTo(float aXResolution, float aYResolution
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable(); nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
if (sf) { if (sf) {
sf->SetResolutionAndScaleTo(gfxSize(aXResolution, aYResolution)); sf->SetResolutionAndScaleTo(aResolution);
presShell->SetResolutionAndScaleTo(aXResolution, aYResolution); presShell->SetResolutionAndScaleTo(aResolution);
} }
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
nsDOMWindowUtils::GetResolution(float* aXResolution, float* aYResolution) nsDOMWindowUtils::GetResolution(float* aResolution)
{ {
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
@ -543,12 +543,9 @@ nsDOMWindowUtils::GetResolution(float* aXResolution, float* aYResolution)
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable(); nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
if (sf) { if (sf) {
const gfxSize& res = sf->GetResolution(); *aResolution = sf->GetResolution();
*aXResolution = res.width;
*aYResolution = res.height;
} else { } else {
*aXResolution = presShell->GetXResolution(); *aResolution = presShell->GetResolution();
*aYResolution = presShell->GetYResolution();
} }
return NS_OK; return NS_OK;

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

@ -12844,6 +12844,35 @@ nsIDocument::CreateHTMLElement(nsIAtom* aTag)
return element.forget(); return element.forget();
} }
nsresult
nsIDocument::GetId(nsAString& aId)
{
if (mId.IsEmpty()) {
nsresult rv;
nsCOMPtr<nsIUUIDGenerator> uuidgen = do_GetService("@mozilla.org/uuid-generator;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsID id;
rv = uuidgen->GenerateUUIDInPlace(&id);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Build a string in {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} format
char buffer[NSID_LENGTH];
id.ToProvidedString(buffer);
NS_ConvertASCIItoUTF16 uuid(buffer);
// Remove {} and the null terminator
mId.Assign(Substring(uuid, 1, NSID_LENGTH - 3));
}
aId = mId;
return NS_OK;
}
bool bool
MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData) MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
{ {

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

@ -16,6 +16,8 @@
#include "nsILoadGroup.h" // for member (in nsCOMPtr) #include "nsILoadGroup.h" // for member (in nsCOMPtr)
#include "nsINode.h" // for base class #include "nsINode.h" // for base class
#include "nsIScriptGlobalObject.h" // for member (in nsCOMPtr) #include "nsIScriptGlobalObject.h" // for member (in nsCOMPtr)
#include "nsIServiceManager.h"
#include "nsIUUIDGenerator.h"
#include "nsPIDOMWindow.h" // for use in inline functions #include "nsPIDOMWindow.h" // for use in inline functions
#include "nsPropertyTable.h" // for member #include "nsPropertyTable.h" // for member
#include "nsTHashtable.h" // for member #include "nsTHashtable.h" // for member
@ -747,6 +749,8 @@ public:
return mAnonymousContents; return mAnonymousContents;
} }
nsresult GetId(nsAString& aId);
protected: protected:
virtual Element *GetRootElementInternal() const = 0; virtual Element *GetRootElementInternal() const = 0;
@ -2798,6 +2802,7 @@ protected:
nsCOMPtr<nsIChannel> mChannel; nsCOMPtr<nsIChannel> mChannel;
private: private:
nsCString mContentType; nsCString mContentType;
nsString mId;
protected: protected:
// The document's security info // The document's security info

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

@ -1117,9 +1117,9 @@ PackPDU(const BluetoothAvrcpElementAttribute& aIn, BluetoothDaemonPDU& aPDU)
return NS_ERROR_ILLEGAL_VALUE; /* integer overflow detected */ return NS_ERROR_ILLEGAL_VALUE; /* integer overflow detected */
} }
PRUint32 clen = cstr.Length() + 1; /* include \0 character */ uint32_t clen = cstr.Length() + 1; /* include \0 character */
rv = PackPDU(PackConversion<PRUint32, uint8_t>(clen), aPDU); rv = PackPDU(PackConversion<uint32_t, uint8_t>(clen), aPDU);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }

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

@ -1070,9 +1070,9 @@ PackPDU(const BluetoothAvrcpElementAttribute& aIn, BluetoothDaemonPDU& aPDU)
return NS_ERROR_ILLEGAL_VALUE; /* integer overflow detected */ return NS_ERROR_ILLEGAL_VALUE; /* integer overflow detected */
} }
PRUint32 clen = cstr.Length() + 1; /* include \0 character */ uint32_t clen = cstr.Length() + 1; /* include \0 character */
rv = PackPDU(PackConversion<PRUint32, uint8_t>(clen), aPDU); rv = PackPDU(PackConversion<uint32_t, uint8_t>(clen), aPDU);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }

4
dom/cache/ActorChild.cpp поставляемый
Просмотреть файл

@ -50,6 +50,10 @@ ActorChild::FeatureNotified() const
return mFeature && mFeature->Notified(); return mFeature && mFeature->Notified();
} }
ActorChild::ActorChild()
{
}
ActorChild::~ActorChild() ActorChild::~ActorChild()
{ {
MOZ_ASSERT(!mFeature); MOZ_ASSERT(!mFeature);

1
dom/cache/ActorChild.h поставляемый
Просмотреть файл

@ -34,6 +34,7 @@ public:
FeatureNotified() const; FeatureNotified() const;
protected: protected:
ActorChild();
~ActorChild(); ~ActorChild();
private: private:

3
dom/cache/test/mochitest/driver.js поставляемый
Просмотреть файл

@ -81,7 +81,8 @@ function runTests(testFile, order) {
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
if (typeof order == "undefined") { if (typeof order == "undefined") {
order = "both"; // both by default order = "sequential"; // sequential by default, see bug 1143222.
// TODO: Make this "both".
} }
ok(order == "parallel" || order == "sequential" || order == "both", ok(order == "parallel" || order == "sequential" || order == "both",

2
dom/cache/test/mochitest/mochitest.ini поставляемый
Просмотреть файл

@ -14,6 +14,4 @@ support-files =
[test_cache.html] [test_cache.html]
[test_cache_add.html] [test_cache_add.html]
[test_cache_match_request.html] [test_cache_match_request.html]
skip-if = true # bug 1143222
[test_cache_matchAll_request.html] [test_cache_matchAll_request.html]
skip-if = true # bug 1143222

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

@ -1,6 +1,8 @@
var request1 = new Request("//mochi.test:8888/?1&" + context); var request1 = new Request("//mochi.test:8888/?1&" + context + "#fragment");
var request2 = new Request("//mochi.test:8888/?2&" + context); var request2 = new Request("//mochi.test:8888/?2&" + context);
var request3 = new Request("//mochi.test:8888/?3&" + context); var request3 = new Request("//mochi.test:8888/?3&" + context);
var requestWithAltQS = new Request("//mochi.test:8888/?queryString");
var unknownRequest = new Request("//mochi.test:8888/non/existing/path?" + context);
var response1, response3; var response1, response3;
var c; var c;
var response1Text, response3Text; var response1Text, response3Text;
@ -8,7 +10,8 @@ var name = "matchAll-request" + context;
function checkResponse(r, response, responseText) { function checkResponse(r, response, responseText) {
ok(r !== response, "The objects should not be the same"); ok(r !== response, "The objects should not be the same");
is(r.url, response.url, "The URLs should be the same"); is(r.url, response.url.replace("#fragment", ""),
"The URLs should be the same");
is(r.status, response.status, "The status codes should be the same"); is(r.status, response.status, "The status codes should be the same");
is(r.type, response.type, "The response types should be the same"); is(r.type, response.type, "The response types should be the same");
is(r.ok, response.ok, "Both responses should have succeeded"); is(r.ok, response.ok, "Both responses should have succeeded");
@ -30,64 +33,110 @@ fetch(new Request(request1)).then(function(r) {
return response3.text(); return response3.text();
}).then(function(text) { }).then(function(text) {
response3Text = text; response3Text = text;
return caches.open(name); return testRequest(request1, request2, request3, unknownRequest,
}).then(function(cache) { requestWithAltQS,
c = cache; request1.url.replace("#fragment", "#other"));
return c.add(request1);
}).then(function() { }).then(function() {
return c.add(request3); return testRequest(request1.url, request2.url, request3.url,
}).then(function() { unknownRequest.url, requestWithAltQS.url,
return c.matchAll(request1); request1.url.replace("#fragment", "#other"));
}).then(function(r) {
is(r.length, 1, "Should only find 1 item");
return checkResponse(r[0], response1, response1Text);
}).then(function() {
return c.matchAll(request3);
}).then(function(r) {
is(r.length, 1, "Should only find 1 item");
return checkResponse(r[0], response3, response3Text);
}).then(function() {
return c.matchAll();
}).then(function(r) {
is(r.length, 2, "Should find 2 items");
return Promise.all([
checkResponse(r[0], response1, response1Text),
checkResponse(r[1], response3, response3Text)
]);
}).then(function() {
return c.matchAll({cacheName: name + "mambojambo"});
}).catch(function(err) {
is(err.name, "NotFoundError", "Searching in the wrong cache should not succeed");
}).then(function() {
return caches.delete(name);
}).then(function(success) {
ok(success, "We should be able to delete the cache successfully");
// Make sure that the cache is still usable after deletion.
return c.matchAll(request1);
}).then(function(r) {
is(r.length, 1, "Should only find 1 item");
return checkResponse(r[0], response1, response1Text);
}).then(function() {
return c.matchAll(request3);
}).then(function(r) {
is(r.length, 1, "Should only find 1 item");
return checkResponse(r[0], response3, response3Text);
}).then(function() {
return c.matchAll();
}).then(function(r) {
is(r.length, 2, "Should find 2 items");
return Promise.all([
checkResponse(r[0], response1, response1Text),
checkResponse(r[1], response3, response3Text)
]);
}).then(function() {
// Now, drop the cache, reopen and verify that we can't find the request any more.
c = null;
return caches.open(name);
}).then(function(cache) {
return cache.matchAll();
}).catch(function(err) {
is(err.name, "NotFoundError", "Searching in the cache after deletion should not succeed");
}).then(function() { }).then(function() {
testDone(); testDone();
}); });
// The request arguments can either be a URL string, or a Request object.
function testRequest(request1, request2, request3, unknownRequest,
requestWithAlternateQueryString,
requestWithDifferentFragment) {
return caches.open(name).then(function(cache) {
c = cache;
return c.add(request1);
}).then(function() {
return c.add(request3);
}).then(function() {
return Promise.all(
["HEAD", "POST", "PUT", "DELETE", "OPTIONS"]
.map(function(method) {
var r = new Request(request1, {method: method});
return c.add(r)
.then(function() {
ok(false, "Promise should be rejected");
}, function(err) {
is(err.name, "TypeError", "Adding a request with type '" + method + "' should fail");
});
})
);
}).then(function() {
return c.matchAll(request1);
}).then(function(r) {
is(r.length, 1, "Should only find 1 item");
return checkResponse(r[0], response1, response1Text);
}).then(function() {
return c.matchAll(requestWithDifferentFragment);
}).then(function(r) {
is(r.length, 1, "Should only find 1 item");
return checkResponse(r[0], response1, response1Text);
}).then(function() {
return c.matchAll(requestWithAlternateQueryString,
{ignoreSearch: true, cacheName: name});
}).then(function(r) {
is(r.length, 2, "Should find 2 items");
return Promise.all([
checkResponse(r[0], response1, response1Text),
checkResponse(r[1], response3, response3Text)
]);
}).then(function() {
return c.matchAll(request3);
}).then(function(r) {
is(r.length, 1, "Should only find 1 item");
return checkResponse(r[0], response3, response3Text);
}).then(function() {
return c.matchAll();
}).then(function(r) {
is(r.length, 2, "Should find 2 items");
return Promise.all([
checkResponse(r[0], response1, response1Text),
checkResponse(r[1], response3, response3Text)
]);
}).then(function() {
return c.matchAll({cacheName: name + "mambojambo"});
}).then(function(r) {
is(r.length, 0, "Searching in the wrong cache should not succeed");
}).then(function() {
return c.matchAll(unknownRequest);
}).then(function(r) {
is(r.length, 0, "Searching for an unknown request should not succeed");
return c.matchAll(unknownRequest, {cacheName: name});
}).then(function(r) {
is(r.length, 0, "Searching for an unknown request should not succeed");
return caches.delete(name);
}).then(function(success) {
ok(success, "We should be able to delete the cache successfully");
// Make sure that the cache is still usable after deletion.
return c.matchAll(request1);
}).then(function(r) {
is(r.length, 1, "Should only find 1 item");
return checkResponse(r[0], response1, response1Text);
}).then(function() {
return c.matchAll(request3);
}).then(function(r) {
is(r.length, 1, "Should only find 1 item");
return checkResponse(r[0], response3, response3Text);
}).then(function() {
return c.matchAll();
}).then(function(r) {
is(r.length, 2, "Should find 2 items");
return Promise.all([
checkResponse(r[0], response1, response1Text),
checkResponse(r[1], response3, response3Text)
]);
}).then(function() {
// Now, drop the cache, reopen and verify that we can't find the request any more.
c = null;
return caches.open(name);
}).then(function(cache) {
return cache.matchAll();
}).then(function(r) {
is(r.length, 0, "Searching in the cache after deletion should not succeed");
});
}

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

@ -1,4 +1,6 @@
var request = new Request("//mochi.test:8888/?" + context); var request = new Request("//mochi.test:8888/?" + context + "#fragment");
var requestWithAltQS = new Request("//mochi.test:8888/?queryString");
var unknownRequest = new Request("//mochi.test:8888/non/existing/path?" + context);
var response; var response;
var c; var c;
var responseText; var responseText;
@ -6,7 +8,8 @@ var name = "match-request" + context;
function checkResponse(r) { function checkResponse(r) {
ok(r !== response, "The objects should not be the same"); ok(r !== response, "The objects should not be the same");
is(r.url, response.url, "The URLs should be the same"); is(r.url, response.url.replace("#fragment", ""),
"The URLs should be the same");
is(r.status, response.status, "The status codes should be the same"); is(r.status, response.status, "The status codes should be the same");
is(r.type, response.type, "The response types should be the same"); is(r.type, response.type, "The response types should be the same");
is(r.ok, response.ok, "Both responses should have succeeded"); is(r.ok, response.ok, "Both responses should have succeeded");
@ -22,42 +25,86 @@ fetch(new Request(request)).then(function(r) {
return response.text(); return response.text();
}).then(function(text) { }).then(function(text) {
responseText = text; responseText = text;
return caches.open(name); return testRequest(request, unknownRequest, requestWithAltQS,
}).then(function(cache) { request.url.replace("#fragment", "#other"));
c = cache;
return c.add(request);
}).then(function() { }).then(function() {
return c.match(request); return testRequest(request.url, unknownRequest.url, requestWithAltQS.url,
}).then(function(r) { request.url.replace("#fragment", "#other"));
return checkResponse(r);
}).then(function() {
return caches.match(request);
}).then(function(r) {
return checkResponse(r);
}).then(function() {
return caches.match(request, {cacheName: name});
}).then(function(r) {
return checkResponse(r);
}).then(function() {
return caches.match(request, {cacheName: name + "mambojambo"});
}).catch(function(err) {
is(err.name, "NotFoundError", "Searching in the wrong cache should not succeed");
}).then(function() {
return caches.delete(name);
}).then(function(success) {
ok(success, "We should be able to delete the cache successfully");
// Make sure that the cache is still usable after deletion.
return c.match(request);
}).then(function(r) {
return checkResponse(r);
}).then(function() {
// Now, drop the cache, reopen and verify that we can't find the request any more.
c = null;
return caches.open(name);
}).then(function(cache) {
return cache.match(request);
}).catch(function(err) {
is(err.name, "NotFoundError", "Searching in the cache after deletion should not succeed");
}).then(function() { }).then(function() {
testDone(); testDone();
}); });
// The request argument can either be a URL string, or a Request object.
function testRequest(request, unknownRequest, requestWithAlternateQueryString,
requestWithDifferentFragment) {
return caches.open(name).then(function(cache) {
c = cache;
return c.add(request);
}).then(function() {
return Promise.all(
["HEAD", "POST", "PUT", "DELETE", "OPTIONS"]
.map(function(method) {
var r = new Request(request, {method: method});
return c.add(r)
.then(function() {
ok(false, "Promise should be rejected");
}, function(err) {
is(err.name, "TypeError", "Adding a request with type '" + method + "' should fail");
});
})
);
}).then(function() {
return c.match(request);
}).then(function(r) {
return checkResponse(r);
}).then(function() {
return caches.match(request);
}).then(function(r) {
return checkResponse(r);
}).then(function() {
return caches.match(requestWithDifferentFragment);
}).then(function(r) {
return checkResponse(r);
}).then(function() {
return caches.match(requestWithAlternateQueryString,
{ignoreSearch: true, cacheName: name});
}).then(function(r) {
return checkResponse(r);
}).then(function() {
return caches.match(request, {cacheName: name});
}).then(function(r) {
return checkResponse(r);
}).then(function() {
return caches.match(request, {cacheName: name + "mambojambo"})
.then(function() {
ok(false, "Promise should be rejected");
}, function(err) {
is(err.name, "NotFoundError", "Searching in the wrong cache should not succeed");
});
}).then(function() {
return c.match(unknownRequest);
}).then(function(r) {
is(typeof r, "undefined", "Searching for an unknown request should not succeed");
return caches.match(unknownRequest);
}).then(function(r) {
is(typeof r, "undefined", "Searching for an unknown request should not succeed");
return caches.match(unknownRequest, {cacheName: name});
}).then(function(r) {
is(typeof r, "undefined", "Searching for an unknown request should not succeed");
return caches.delete(name);
}).then(function(success) {
ok(success, "We should be able to delete the cache successfully");
// Make sure that the cache is still usable after deletion.
return c.match(request);
}).then(function(r) {
return checkResponse(r);
}).then(function() {
// Now, drop the cache, reopen and verify that we can't find the request any more.
c = null;
return caches.open(name);
}).then(function(cache) {
return cache.match(request);
}).then(function(r) {
is(typeof r, "undefined", "Searching in the cache after deletion should not succeed");
});
}

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

@ -81,7 +81,6 @@ static const gl::GLFeature kRequiredFeatures[] = {
gl::GLFeature::gpu_shader4, gl::GLFeature::gpu_shader4,
gl::GLFeature::instanced_arrays, gl::GLFeature::instanced_arrays,
gl::GLFeature::instanced_non_arrays, gl::GLFeature::instanced_non_arrays,
gl::GLFeature::invalidate_framebuffer,
gl::GLFeature::map_buffer_range, gl::GLFeature::map_buffer_range,
gl::GLFeature::occlusion_query2, gl::GLFeature::occlusion_query2,
gl::GLFeature::packed_depth_stencil, gl::GLFeature::packed_depth_stencil,

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

@ -398,6 +398,13 @@ WebGL2Context::InvalidateFramebuffer(GLenum target, const dom::Sequence<GLenum>&
} }
} }
// InvalidateFramebuffer is a hint to the driver. Should be OK to
// skip calls if not supported, for example by OSX 10.9 GL
// drivers.
static bool invalidateFBSupported = gl->IsSupported(gl::GLFeature::invalidate_framebuffer);
if (!invalidateFBSupported)
return;
if (!fb && !isDefaultFB) { if (!fb && !isDefaultFB) {
dom::Sequence<GLenum> tmpAttachments; dom::Sequence<GLenum> tmpAttachments;
TranslateDefaultAttachments(attachments, &tmpAttachments); TranslateDefaultAttachments(attachments, &tmpAttachments);
@ -445,6 +452,13 @@ WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenu
} }
} }
// InvalidateFramebuffer is a hint to the driver. Should be OK to
// skip calls if not supported, for example by OSX 10.9 GL
// drivers.
static bool invalidateFBSupported = gl->IsSupported(gl::GLFeature::invalidate_framebuffer);
if (!invalidateFBSupported)
return;
if (!fb && !isDefaultFB) { if (!fb && !isDefaultFB) {
dom::Sequence<GLenum> tmpAttachments; dom::Sequence<GLenum> tmpAttachments;
TranslateDefaultAttachments(attachments, &tmpAttachments); TranslateDefaultAttachments(attachments, &tmpAttachments);

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

@ -921,6 +921,8 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
mViewportWidth = mWidth; mViewportWidth = mWidth;
mViewportHeight = mHeight; mViewportHeight = mHeight;
gl->fScissor(0, 0, mWidth, mHeight);
// Make sure that we clear this out, otherwise // Make sure that we clear this out, otherwise
// we'll end up displaying random memory // we'll end up displaying random memory
gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0); gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);

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

@ -264,9 +264,6 @@ bool
WebGLContext::IsFormatValidForFB(GLenum sizedFormat) const WebGLContext::IsFormatValidForFB(GLenum sizedFormat) const
{ {
switch (sizedFormat) { switch (sizedFormat) {
case LOCAL_GL_ALPHA8:
case LOCAL_GL_LUMINANCE8:
case LOCAL_GL_LUMINANCE8_ALPHA8:
case LOCAL_GL_RGB8: case LOCAL_GL_RGB8:
case LOCAL_GL_RGBA8: case LOCAL_GL_RGBA8:
case LOCAL_GL_RGB565: case LOCAL_GL_RGB565:

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

@ -4,7 +4,7 @@
# Mark failing tests in mochi-single.html. # Mark failing tests in mochi-single.html.
[DEFAULT] [DEFAULT]
skip-if = e10s || os == 'b2g' || ((os == 'linux') && (buildapp == 'b2g')) || ((os == 'linux') && (buildapp == 'mulet')) # Bug 1136181 disabled on B2G Desktop and Mulet for intermittent failures skip-if = e10s || os == 'b2g' || ((os == 'linux') && (buildapp == 'b2g')) || ((os == 'linux') && (buildapp == 'mulet'))
support-files = webgl-conformance/../webgl-mochitest/driver-info.js support-files = webgl-conformance/../webgl-mochitest/driver-info.js
webgl-conformance/always-fail.html webgl-conformance/always-fail.html
@ -526,7 +526,6 @@ fail-if = (os == 'linux')
[webgl-conformance/_wrappers/test_conformance__extensions__webgl-debug-shaders.html] [webgl-conformance/_wrappers/test_conformance__extensions__webgl-debug-shaders.html]
[webgl-conformance/_wrappers/test_conformance__extensions__webgl-compressed-texture-etc1.html] [webgl-conformance/_wrappers/test_conformance__extensions__webgl-compressed-texture-etc1.html]
[webgl-conformance/_wrappers/test_conformance__extensions__webgl-compressed-texture-s3tc.html] [webgl-conformance/_wrappers/test_conformance__extensions__webgl-compressed-texture-s3tc.html]
fail-if = (os == 'mac' && os_version == '10.10')
[webgl-conformance/_wrappers/test_conformance__extensions__ext-sRGB.html] [webgl-conformance/_wrappers/test_conformance__extensions__ext-sRGB.html]
[webgl-conformance/_wrappers/test_conformance__extensions__ext-shader-texture-lod.html] [webgl-conformance/_wrappers/test_conformance__extensions__ext-shader-texture-lod.html]
[webgl-conformance/_wrappers/test_conformance__glsl__functions__glsl-function.html] [webgl-conformance/_wrappers/test_conformance__glsl__functions__glsl-function.html]

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

@ -59,11 +59,11 @@ fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersG
== webgl-color-test.html?frame=6&__&________&_______&_____ wrapper.html?colors-no-alpha.png == webgl-color-test.html?frame=6&__&________&_______&_____ wrapper.html?colors-no-alpha.png
== webgl-color-test.html?frame=6&aa&________&_______&_____ wrapper.html?colors-no-alpha.png == webgl-color-test.html?frame=6&aa&________&_______&_____ wrapper.html?colors-no-alpha.png
== webgl-color-test.html?frame=6&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png == webgl-color-test.html?frame=6&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png
fails-if(winWidget&&layersGPUAccelerated&&d2d) == webgl-color-test.html?frame=6&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png == webgl-color-test.html?frame=6&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png
== webgl-color-test.html?frame=6&__&________&premult&_____ wrapper.html?colors-no-alpha.png == webgl-color-test.html?frame=6&__&________&premult&_____ wrapper.html?colors-no-alpha.png
== webgl-color-test.html?frame=6&aa&________&premult&_____ wrapper.html?colors-no-alpha.png == webgl-color-test.html?frame=6&aa&________&premult&_____ wrapper.html?colors-no-alpha.png
== webgl-color-test.html?frame=6&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png == webgl-color-test.html?frame=6&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png
fails-if(winWidget&&layersGPUAccelerated&&d2d) == webgl-color-test.html?frame=6&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png == webgl-color-test.html?frame=6&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png
fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) == webgl-color-test.html?frame=6&__&________&_______&alpha wrapper.html?colors-non-premult.png fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) == webgl-color-test.html?frame=6&__&________&_______&alpha wrapper.html?colors-non-premult.png
fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) == webgl-color-test.html?frame=6&aa&________&_______&alpha wrapper.html?colors-non-premult.png fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) == webgl-color-test.html?frame=6&aa&________&_______&alpha wrapper.html?colors-non-premult.png
fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) == webgl-color-test.html?frame=6&__&preserve&_______&alpha wrapper.html?colors-non-premult.png fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) == webgl-color-test.html?frame=6&__&preserve&_______&alpha wrapper.html?colors-non-premult.png
@ -93,11 +93,11 @@ fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersG
pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&_______&_____ wrapper.html?colors-no-alpha.png pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&_______&_____ wrapper.html?colors-no-alpha.png
pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&_______&_____ wrapper.html?colors-no-alpha.png pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&_______&_____ wrapper.html?colors-no-alpha.png
pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png
fails-if(winWidget&&layersGPUAccelerated&&d2d) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png
pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&premult&_____ wrapper.html?colors-no-alpha.png pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&premult&_____ wrapper.html?colors-no-alpha.png
pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&premult&_____ wrapper.html?colors-no-alpha.png pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&premult&_____ wrapper.html?colors-no-alpha.png
pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png
random-if(winWidget&&layersGPUAccelerated&&d2d) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png
fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&_______&alpha wrapper.html?colors-non-premult.png fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&_______&alpha wrapper.html?colors-non-premult.png
fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&_______&alpha wrapper.html?colors-non-premult.png fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&_______&alpha wrapper.html?colors-non-premult.png
fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&_______&alpha wrapper.html?colors-non-premult.png fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&_______&alpha wrapper.html?colors-non-premult.png

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

@ -1,3 +1,7 @@
# *** WARNING! ***
# Modification to this file only take effect after running
# generate-wrappers-and-manifest.py
# See python/mozbuild/mozbuild/mozinfo.py for incoming data. # See python/mozbuild/mozbuild/mozinfo.py for incoming data.
[DEFAULT] [DEFAULT]
@ -96,9 +100,6 @@ fail-if = (os == 'linux')
[_wrappers/test_conformance__canvas__drawingbuffer-static-canvas-test.html] [_wrappers/test_conformance__canvas__drawingbuffer-static-canvas-test.html]
# Intermittent crash on OSX. # Intermittent crash on OSX.
skip-if = os == 'mac' skip-if = os == 'mac'
[_wrappers/test_conformance__extensions__webgl-compressed-texture-s3tc.html]
# Fails on OS X 10.10
fail-if = (os == 'mac' && os_version == '10.10')
[_wrappers/test_conformance__misc__object-deletion-behaviour.html] [_wrappers/test_conformance__misc__object-deletion-behaviour.html]
# Fails on OS X 10.10 # Fails on OS X 10.10
fail-if = (os == 'mac' && os_version == '10.10') fail-if = (os == 'mac' && os_version == '10.10')

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

@ -13,6 +13,9 @@
#include "jsapi.h" #include "jsapi.h"
#include "nsGlobalWindow.h" // So we can assign an nsGlobalWindow* to mWindowSource #include "nsGlobalWindow.h" // So we can assign an nsGlobalWindow* to mWindowSource
#include "ServiceWorker.h"
#include "ServiceWorkerClient.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
@ -103,12 +106,14 @@ MessageEvent::GetSource(nsIDOMWindow** aSource)
} }
void void
MessageEvent::GetSource(Nullable<OwningWindowProxyOrMessagePort>& aValue) const MessageEvent::GetSource(Nullable<OwningWindowProxyOrMessagePortOrClient>& aValue) const
{ {
if (mWindowSource) { if (mWindowSource) {
aValue.SetValue().SetAsWindowProxy() = mWindowSource; aValue.SetValue().SetAsWindowProxy() = mWindowSource;
} else if (mPortSource) { } else if (mPortSource) {
aValue.SetValue().SetAsMessagePort() = mPortSource; aValue.SetValue().SetAsMessagePort() = mPortSource;
} else if (mClientSource) {
aValue.SetValue().SetAsClient() = mClientSource;
} }
} }
@ -206,6 +211,12 @@ MessageEvent::SetSource(mozilla::dom::MessagePort* aPort)
mPortSource = aPort; mPortSource = aPort;
} }
void
MessageEvent::SetSource(mozilla::dom::workers::ServiceWorkerClient* aClient)
{
mClientSource = aClient;
}
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla

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

@ -18,7 +18,13 @@ struct MessageEventInit;
class MessagePort; class MessagePort;
class MessagePortBase; class MessagePortBase;
class MessagePortList; class MessagePortList;
class OwningWindowProxyOrMessagePort; class OwningWindowProxyOrMessagePortOrClient;
namespace workers {
class ServiceWorkerClient;
}
/** /**
* Implements the MessageEvent event, used for cross-document messaging and * Implements the MessageEvent event, used for cross-document messaging and
@ -48,7 +54,7 @@ public:
void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aData, void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aData,
ErrorResult& aRv); ErrorResult& aRv);
void GetSource(Nullable<OwningWindowProxyOrMessagePort>& aValue) const; void GetSource(Nullable<OwningWindowProxyOrMessagePortOrClient>& aValue) const;
MessagePortList* GetPorts() MessagePortList* GetPorts()
{ {
@ -60,6 +66,8 @@ public:
// Non WebIDL methods // Non WebIDL methods
void SetSource(mozilla::dom::MessagePort* aPort); void SetSource(mozilla::dom::MessagePort* aPort);
void SetSource(workers::ServiceWorkerClient* aClient);
void SetSource(nsPIDOMWindow* aWindow) void SetSource(nsPIDOMWindow* aWindow)
{ {
mWindowSource = aWindow; mWindowSource = aWindow;
@ -86,6 +94,7 @@ private:
nsString mLastEventId; nsString mLastEventId;
nsCOMPtr<nsIDOMWindow> mWindowSource; nsCOMPtr<nsIDOMWindow> mWindowSource;
nsRefPtr<MessagePortBase> mPortSource; nsRefPtr<MessagePortBase> mPortSource;
nsRefPtr<workers::ServiceWorkerClient> mClientSource;
nsRefPtr<MessagePortList> mPorts; nsRefPtr<MessagePortList> mPorts;
}; };

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

@ -50,7 +50,7 @@ interface nsITranslationNodeList;
interface nsIJSRAIIHelper; interface nsIJSRAIIHelper;
interface nsIContentPermissionRequest; interface nsIContentPermissionRequest;
[scriptable, uuid(b39cb73f-ff99-4744-9780-2c26f830c6f7)] [scriptable, uuid(dde97573-f4cf-45ce-bbb0-5af4e5f77440)]
interface nsIDOMWindowUtils : nsISupports { interface nsIDOMWindowUtils : nsISupports {
/** /**
@ -205,15 +205,14 @@ interface nsIDOMWindowUtils : nsISupports {
* *
* The effect of this API is for gfx code to allocate more or fewer * The effect of this API is for gfx code to allocate more or fewer
* pixels for rescalable content by a factor of |resolution| in * pixels for rescalable content by a factor of |resolution| in
* either or both dimensions. The scale at which the content is * both dimensions. The scale at which the content is displayed does
* displayed does not change; if that is desired, use * not change; if that is desired, use setResolutionAndScaleTo() instead.
* setResolutionAndScaleTo() instead.
* *
* The caller of this method must have chrome privileges. * The caller of this method must have chrome privileges.
*/ */
void setResolution(in float aXResolution, in float aYResolution); void setResolution(in float aResolution);
void getResolution(out float aXResolution, out float aYResolution); void getResolution(out float aResolution);
/** /**
* Similar to setResolution(), but also scales the content by the * Similar to setResolution(), but also scales the content by the
@ -226,7 +225,7 @@ interface nsIDOMWindowUtils : nsISupports {
* *
* The caller of this method must have chrome privileges. * The caller of this method must have chrome privileges.
*/ */
void setResolutionAndScaleTo(in float aXResolution, in float aYResolution); void setResolutionAndScaleTo(in float aResolution);
/** /**
* Whether the resolution has been set by the user. * Whether the resolution has been set by the user.

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

@ -19,7 +19,7 @@ interface nsIServiceWorkerUnregisterCallback : nsISupports
[noscript] void UnregisterFailed(); [noscript] void UnregisterFailed();
}; };
[builtinclass, uuid(464882c8-81c0-4620-b9c4-44c12085b65b)] [builtinclass, uuid(706c3e6b-c9d2-4857-893d-4b4845fec48f)]
interface nsIServiceWorkerManager : nsISupports interface nsIServiceWorkerManager : nsISupports
{ {
/** /**
@ -30,7 +30,7 @@ interface nsIServiceWorkerManager : nsISupports
* *
* Returns a Promise. * Returns a Promise.
*/ */
nsISupports register(in nsIDOMWindow aWindow, in DOMString aScope, in DOMString aScriptURI); nsISupports register(in nsIDOMWindow aWindow, in nsIURI aScope, in nsIURI aScriptURI);
/** /**
* Unregister an existing ServiceWorker registration for `aScope`. * Unregister an existing ServiceWorker registration for `aScope`.

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

@ -390,7 +390,7 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
// This is the root layer, so the cumulative resolution is the same // This is the root layer, so the cumulative resolution is the same
// as the resolution. // as the resolution.
metrics.SetPresShellResolution(metrics.GetCumulativeResolution().ToScaleFactor().scale); metrics.SetPresShellResolution(metrics.GetCumulativeResolution().ToScaleFactor().scale);
utils->SetResolutionAndScaleTo(metrics.GetPresShellResolution(), metrics.GetPresShellResolution()); utils->SetResolutionAndScaleTo(metrics.GetPresShellResolution());
CSSSize scrollPort = metrics.CalculateCompositedSizeInCssPixels(); CSSSize scrollPort = metrics.CalculateCompositedSizeInCssPixels();
utils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height); utils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height);
@ -917,8 +917,7 @@ TabChild::Observe(nsISupports *aSubject,
// until we we get an inner size. // until we we get an inner size.
if (HasValidInnerSize()) { if (HasValidInnerSize()) {
InitializeRootMetrics(); InitializeRootMetrics();
utils->SetResolutionAndScaleTo(mLastRootMetrics.GetPresShellResolution(), utils->SetResolutionAndScaleTo(mLastRootMetrics.GetPresShellResolution());
mLastRootMetrics.GetPresShellResolution());
HandlePossibleViewportChange(mInnerSize); HandlePossibleViewportChange(mInnerSize);
} }
} }
@ -2344,7 +2343,7 @@ TabChild::GetPresShellResolution() const
if (!shell) { if (!shell) {
return 1.0f; return 1.0f;
} }
return shell->GetXResolution(); return shell->GetResolution();
} }
bool bool

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

@ -236,8 +236,6 @@ private:
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
TabParent* sEventCapturer;
TabParent *TabParent::mIMETabParent = nullptr; TabParent *TabParent::mIMETabParent = nullptr;
TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr; TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr;
@ -262,7 +260,6 @@ TabParent::TabParent(nsIContentParent* aManager,
, mIMECompositionStart(0) , mIMECompositionStart(0)
, mIMESeqno(0) , mIMESeqno(0)
, mIMECompositionRectOffset(0) , mIMECompositionRectOffset(0)
, mEventCaptureDepth(0)
, mRect(0, 0, 0, 0) , mRect(0, 0, 0, 0)
, mDimensions(0, 0) , mDimensions(0, 0)
, mOrientation(0) , mOrientation(0)
@ -401,9 +398,6 @@ TabParent::Recv__delete__()
void void
TabParent::ActorDestroy(ActorDestroyReason why) TabParent::ActorDestroy(ActorDestroyReason why)
{ {
if (sEventCapturer == this) {
sEventCapturer = nullptr;
}
if (mIMETabParent == this) { if (mIMETabParent == this) {
mIMETabParent = nullptr; mIMETabParent = nullptr;
} }
@ -1325,22 +1319,7 @@ bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event)
return false; return false;
} }
if (event.message == NS_TOUCH_START) { if (event.message == NS_TOUCH_START) {
// Adjust the widget coordinates to be relative to our frame.
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
if (!frameLoader) {
// No frame anymore?
sEventCapturer = nullptr;
return false;
}
mChildProcessOffsetAtTouchStart = GetChildProcessOffset(); mChildProcessOffsetAtTouchStart = GetChildProcessOffset();
MOZ_ASSERT((!sEventCapturer && mEventCaptureDepth == 0) ||
(sEventCapturer == this && mEventCaptureDepth > 0));
// We want to capture all remaining touch events in this series
// for fast-path dispatch.
sEventCapturer = this;
++mEventCaptureDepth;
} }
// PresShell::HandleEventInternal adds touches on touch end/cancel. This // PresShell::HandleEventInternal adds touches on touch end/cancel. This
@ -1373,41 +1352,6 @@ bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event)
PBrowserParent::SendRealTouchEvent(event, guid, blockId); PBrowserParent::SendRealTouchEvent(event, guid, blockId);
} }
/*static*/ TabParent*
TabParent::GetEventCapturer()
{
return sEventCapturer;
}
bool
TabParent::TryCapture(const WidgetGUIEvent& aEvent)
{
MOZ_ASSERT(sEventCapturer == this && mEventCaptureDepth > 0);
if (aEvent.mClass != eTouchEventClass) {
// Only capture of touch events is implemented, for now.
return false;
}
WidgetTouchEvent event(*aEvent.AsTouchEvent());
bool isTouchPointUp = (event.message == NS_TOUCH_END ||
event.message == NS_TOUCH_CANCEL);
if (event.message == NS_TOUCH_START || isTouchPointUp) {
// Let the DOM see touch start/end events so that its touch-point
// state stays consistent.
if (isTouchPointUp && 0 == --mEventCaptureDepth) {
// All event series are un-captured, don't try to catch any
// more.
sEventCapturer = nullptr;
}
return false;
}
SendRealTouchEvent(event);
return true;
}
bool bool
TabParent::RecvSyncMessage(const nsString& aMessage, TabParent::RecvSyncMessage(const nsString& aMessage,
const ClonedMessageData& aData, const ClonedMessageData& aData,
@ -2550,11 +2494,6 @@ TabParent::GetLoadContext()
return loadContext.forget(); return loadContext.forget();
} }
/* Be careful if you call this method while proceding a real touch event. For
* example sending a touchstart during a real touchend may results into
* a busted mEventCaptureDepth and following touch events may not do what you
* expect.
*/
NS_IMETHODIMP NS_IMETHODIMP
TabParent::InjectTouchEvent(const nsAString& aType, TabParent::InjectTouchEvent(const nsAString& aType,
uint32_t* aIdentifiers, uint32_t* aIdentifiers,
@ -2614,11 +2553,6 @@ TabParent::InjectTouchEvent(const nsAString& aType,
event.touches.AppendElement(t); event.touches.AppendElement(t);
} }
if ((msg == NS_TOUCH_END || msg == NS_TOUCH_CANCEL) && sEventCapturer) {
WidgetGUIEvent* guiEvent = event.AsGUIEvent();
TryCapture(*guiEvent);
}
SendRealTouchEvent(event); SendRealTouchEvent(event);
return NS_OK; return NS_OK;
} }

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

@ -102,30 +102,6 @@ public:
nsIXULBrowserWindow* GetXULBrowserWindow(); nsIXULBrowserWindow* GetXULBrowserWindow();
/**
* Return the TabParent that has decided it wants to capture an
* event series for fast-path dispatch to its subprocess, if one
* has.
*
* DOM event dispatch and widget are free to ignore capture
* requests from TabParents; the end result wrt remote content is
* (must be) always the same, albeit usually slower without
* subprocess capturing. This allows frontends/widget backends to
* "opt in" to faster cross-process dispatch.
*/
static TabParent* GetEventCapturer();
/**
* If this is the current event capturer, give this a chance to
* capture the event. If it was captured, return true, false
* otherwise. Un-captured events should follow normal DOM
* dispatch; captured events should result in no further
* processing from the caller of TryCapture().
*
* It's an error to call TryCapture() if this isn't the event
* capturer.
*/
bool TryCapture(const WidgetGUIEvent& aEvent);
void Destroy(); void Destroy();
virtual bool RecvMoveFocus(const bool& aForward) MOZ_OVERRIDE; virtual bool RecvMoveFocus(const bool& aForward) MOZ_OVERRIDE;
@ -440,9 +416,6 @@ protected:
LayoutDeviceIntRect mIMECaretRect; LayoutDeviceIntRect mIMECaretRect;
LayoutDeviceIntRect mIMEEditorRect; LayoutDeviceIntRect mIMEEditorRect;
// The number of event series we're currently capturing.
int32_t mEventCaptureDepth;
nsIntRect mRect; nsIntRect mRect;
ScreenIntSize mDimensions; ScreenIntSize mDimensions;
ScreenOrientation mOrientation; ScreenOrientation mOrientation;

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

@ -191,12 +191,12 @@ void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
if (mIsDormant) { if (mIsDormant) {
DECODER_LOG("UpdateDormantState() entering DORMANT state"); DECODER_LOG("UpdateDormantState() entering DORMANT state");
// enter dormant state // enter dormant state
nsCOMPtr<nsIRunnable> event = RefPtr<nsRunnable> event =
NS_NewRunnableMethodWithArg<bool>( NS_NewRunnableMethodWithArg<bool>(
mDecoderStateMachine, mDecoderStateMachine,
&MediaDecoderStateMachine::SetDormant, &MediaDecoderStateMachine::SetDormant,
true); true);
mDecoderStateMachine->GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL); mDecoderStateMachine->TaskQueue()->Dispatch(event);
if (IsEnded()) { if (IsEnded()) {
mWasEndedWhenEnteredDormant = true; mWasEndedWhenEnteredDormant = true;
@ -207,12 +207,12 @@ void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
DECODER_LOG("UpdateDormantState() leaving DORMANT state"); DECODER_LOG("UpdateDormantState() leaving DORMANT state");
// exit dormant state // exit dormant state
// trigger to state machine. // trigger to state machine.
nsCOMPtr<nsIRunnable> event = RefPtr<nsRunnable> event =
NS_NewRunnableMethodWithArg<bool>( NS_NewRunnableMethodWithArg<bool>(
mDecoderStateMachine, mDecoderStateMachine,
&MediaDecoderStateMachine::SetDormant, &MediaDecoderStateMachine::SetDormant,
false); false);
mDecoderStateMachine->GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL); mDecoderStateMachine->TaskQueue()->Dispatch(event);
} }
} }
@ -750,7 +750,8 @@ nsresult MediaDecoder::ScheduleStateMachineThread()
if (mShuttingDown) if (mShuttingDown)
return NS_OK; return NS_OK;
ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
return mDecoderStateMachine->ScheduleStateMachine(); mDecoderStateMachine->ScheduleStateMachine();
return NS_OK;
} }
nsresult MediaDecoder::Play() nsresult MediaDecoder::Play()
@ -763,8 +764,7 @@ nsresult MediaDecoder::Play()
if (mPausedForPlaybackRateNull) { if (mPausedForPlaybackRateNull) {
return NS_OK; return NS_OK;
} }
nsresult res = ScheduleStateMachineThread(); ScheduleStateMachineThread();
NS_ENSURE_SUCCESS(res,res);
if (IsEnded()) { if (IsEnded()) {
return Seek(0, SeekTarget::PrevSyncPoint); return Seek(0, SeekTarget::PrevSyncPoint);
} else if (mPlayState == PLAY_STATE_LOADING || mPlayState == PLAY_STATE_SEEKING) { } else if (mPlayState == PLAY_STATE_LOADING || mPlayState == PLAY_STATE_SEEKING) {
@ -1317,7 +1317,7 @@ void MediaDecoder::ApplyStateToStateMachine(PlayState aState)
mDecoderStateMachine->Play(); mDecoderStateMachine->Play();
break; break;
case PLAY_STATE_SEEKING: case PLAY_STATE_SEEKING:
mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->GetStateMachineThread(), mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->TaskQueue(),
mDecoderStateMachine.get(), __func__, mDecoderStateMachine.get(), __func__,
&MediaDecoderStateMachine::Seek, mRequestedSeekTarget) &MediaDecoderStateMachine::Seek, mRequestedSeekTarget)
->RefableThen(NS_GetCurrentThread(), __func__, this, ->RefableThen(NS_GetCurrentThread(), __func__, this,

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

@ -13,7 +13,7 @@
#include <stdint.h> #include <stdint.h>
#include "MediaDecoderStateMachine.h" #include "MediaDecoderStateMachine.h"
#include "MediaDecoderStateMachineScheduler.h" #include "MediaTimer.h"
#include "AudioSink.h" #include "AudioSink.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "MediaDecoder.h" #include "MediaDecoder.h"
@ -201,9 +201,9 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
MediaDecoderReader* aReader, MediaDecoderReader* aReader,
bool aRealTime) : bool aRealTime) :
mDecoder(aDecoder), mDecoder(aDecoder),
mScheduler(new MediaDecoderStateMachineScheduler( mRealTime(aRealTime),
aDecoder->GetReentrantMonitor(), mDispatchedStateMachine(false),
&MediaDecoderStateMachine::TimeoutExpired, this, aRealTime)), mDelayedScheduler(this),
mState(DECODER_STATE_DECODING_NONE), mState(DECODER_STATE_DECODING_NONE),
mPlayDuration(0), mPlayDuration(0),
mStartTime(-1), mStartTime(-1),
@ -248,6 +248,11 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
MOZ_COUNT_CTOR(MediaDecoderStateMachine); MOZ_COUNT_CTOR(MediaDecoderStateMachine);
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
// Set up our task queue.
RefPtr<SharedThreadPool> threadPool(
SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1));
mTaskQueue = new MediaTaskQueue(threadPool.forget());
static bool sPrefCacheInit = false; static bool sPrefCacheInit = false;
if (!sPrefCacheInit) { if (!sPrefCacheInit) {
sPrefCacheInit = true; sPrefCacheInit = true;
@ -387,7 +392,7 @@ void MediaDecoderStateMachine::SendStreamData()
{ {
MOZ_ASSERT(OnStateMachineThread(), "Should be on state machine thread"); MOZ_ASSERT(OnStateMachineThread(), "Should be on state machine thread");
AssertCurrentThreadInMonitor(); AssertCurrentThreadInMonitor();
MOZ_ASSERT(!mAudioSink, "Should've been stopped in CallRunStateMachine()"); MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
DecodedStreamData* stream = mDecoder->GetDecodedStream(); DecodedStreamData* stream = mDecoder->GetDecodedStream();
@ -405,7 +410,7 @@ void MediaDecoderStateMachine::SendStreamData()
mediaStream->AddAudioTrack(audioTrackId, mInfo.mAudio.mRate, 0, audio, mediaStream->AddAudioTrack(audioTrackId, mInfo.mAudio.mRate, 0, audio,
SourceMediaStream::ADDTRACK_QUEUED); SourceMediaStream::ADDTRACK_QUEUED);
stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId, stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId,
GetStateMachineThread(), GetWakeDecoderRunnable()); TaskQueue(), GetWakeDecoderRunnable());
stream->mNextAudioTime = mStartTime + stream->mInitialTime; stream->mNextAudioTime = mStartTime + stream->mInitialTime;
} }
if (mInfo.HasVideo()) { if (mInfo.HasVideo()) {
@ -414,7 +419,7 @@ void MediaDecoderStateMachine::SendStreamData()
mediaStream->AddTrack(videoTrackId, 0, video, mediaStream->AddTrack(videoTrackId, 0, video,
SourceMediaStream::ADDTRACK_QUEUED); SourceMediaStream::ADDTRACK_QUEUED);
stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId, stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId,
GetStateMachineThread(), GetWakeDecoderRunnable()); TaskQueue(), GetWakeDecoderRunnable());
// TODO: We can't initialize |mNextVideoTime| until |mStartTime| // TODO: We can't initialize |mNextVideoTime| until |mStartTime|
// is set. This is a good indication that DecodedStreamData is in // is set. This is a good indication that DecodedStreamData is in
@ -570,7 +575,7 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs)
return false; return false;
} }
stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId, stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId,
GetStateMachineThread(), GetWakeDecoderRunnable()); TaskQueue(), GetWakeDecoderRunnable());
} }
return true; return true;
@ -593,7 +598,7 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
return false; return false;
} }
stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId, stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId,
GetStateMachineThread(), GetWakeDecoderRunnable()); TaskQueue(), GetWakeDecoderRunnable());
} }
return true; return true;
@ -855,7 +860,7 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
"Readers that send WAITING_FOR_DATA need to implement WaitForData"); "Readers that send WAITING_FOR_DATA need to implement WaitForData");
WaitRequestRef(aType).Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, WaitRequestRef(aType).Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
&MediaDecoderReader::WaitForData, aType) &MediaDecoderReader::WaitForData, aType)
->RefableThen(mScheduler.get(), __func__, this, ->RefableThen(TaskQueue(), __func__, this,
&MediaDecoderStateMachine::OnWaitForDataResolved, &MediaDecoderStateMachine::OnWaitForDataResolved,
&MediaDecoderStateMachine::OnWaitForDataRejected)); &MediaDecoderStateMachine::OnWaitForDataRejected));
return; return;
@ -1084,7 +1089,7 @@ MediaDecoderStateMachine::CheckIfSeekComplete()
mDecodeToSeekTarget = false; mDecodeToSeekTarget = false;
RefPtr<nsIRunnable> task( RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SeekCompleted)); NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SeekCompleted));
nsresult rv = GetStateMachineThread()->Dispatch(task, NS_DISPATCH_NORMAL); nsresult rv = TaskQueue()->Dispatch(task);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
DecodeError(); DecodeError();
} }
@ -1146,10 +1151,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
cloneReader = aCloneDonor->mReader; cloneReader = aCloneDonor->mReader;
} }
nsresult rv = mScheduler->Init(); nsresult rv = mReader->Init(cloneReader);
NS_ENSURE_SUCCESS(rv, rv);
rv = mReader->Init(cloneReader);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
return NS_OK; return NS_OK;
@ -1343,7 +1345,7 @@ int64_t MediaDecoderStateMachine::GetCurrentTimeUs() const
} }
bool MediaDecoderStateMachine::IsRealTime() const { bool MediaDecoderStateMachine::IsRealTime() const {
return mScheduler->IsRealTime(); return mRealTime;
} }
int64_t MediaDecoderStateMachine::GetDuration() int64_t MediaDecoderStateMachine::GetDuration()
@ -1492,8 +1494,18 @@ void MediaDecoderStateMachine::SetDormant(bool aDormant)
} }
mPendingSeek.RejectIfExists(__func__); mPendingSeek.RejectIfExists(__func__);
mCurrentSeek.RejectIfExists(__func__); mCurrentSeek.RejectIfExists(__func__);
ScheduleStateMachine();
SetState(DECODER_STATE_DORMANT); SetState(DECODER_STATE_DORMANT);
if (IsPlaying()) {
StopPlayback();
}
StopAudioThread();
FlushDecoding();
// Now that those threads are stopped, there's no possibility of
// mPendingWakeDecoder being needed again. Revoke it.
mPendingWakeDecoder = nullptr;
DebugOnly<nsresult> rv = DecodeTaskQueue()->Dispatch(
NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources));
MOZ_ASSERT(NS_SUCCEEDED(rv));
mDecoder->GetReentrantMonitor().NotifyAll(); mDecoder->GetReentrantMonitor().NotifyAll();
} else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) { } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) {
mDecodingFrozenAtStateDecoding = true; mDecodingFrozenAtStateDecoding = true;
@ -1513,9 +1525,8 @@ void MediaDecoderStateMachine::Shutdown()
// Change state before issuing shutdown request to threads so those // Change state before issuing shutdown request to threads so those
// threads can start exiting cleanly during the Shutdown call. // threads can start exiting cleanly during the Shutdown call.
DECODER_LOG("Changed state to SHUTDOWN"); ScheduleStateMachine();
SetState(DECODER_STATE_SHUTDOWN); SetState(DECODER_STATE_SHUTDOWN);
mScheduler->ScheduleAndShutdown();
if (mAudioSink) { if (mAudioSink) {
mAudioSink->PrepareToShutdown(); mAudioSink->PrepareToShutdown();
} }
@ -1774,7 +1785,7 @@ MediaDecoderStateMachine::EnqueueDecodeFirstFrameTask()
RefPtr<nsIRunnable> task( RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeFirstFrame)); NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeFirstFrame));
nsresult rv = GetStateMachineThread()->Dispatch(task, NS_DISPATCH_NORMAL); nsresult rv = TaskQueue()->Dispatch(task);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
return NS_OK; return NS_OK;
} }
@ -1921,7 +1932,7 @@ MediaDecoderStateMachine::InitiateSeek()
mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
&MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime, &MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
GetEndTime()) GetEndTime())
->RefableThen(mScheduler.get(), __func__, this, ->RefableThen(TaskQueue(), __func__, this,
&MediaDecoderStateMachine::OnSeekCompleted, &MediaDecoderStateMachine::OnSeekCompleted,
&MediaDecoderStateMachine::OnSeekFailed)); &MediaDecoderStateMachine::OnSeekFailed));
} }
@ -1969,7 +1980,7 @@ MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
__func__, &MediaDecoderReader::RequestAudioData) __func__, &MediaDecoderReader::RequestAudioData)
->RefableThen(mScheduler.get(), __func__, this, ->RefableThen(TaskQueue(), __func__, this,
&MediaDecoderStateMachine::OnAudioDecoded, &MediaDecoderStateMachine::OnAudioDecoded,
&MediaDecoderStateMachine::OnAudioNotDecoded)); &MediaDecoderStateMachine::OnAudioNotDecoded));
@ -2029,7 +2040,7 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
&MediaDecoderReader::RequestVideoData, &MediaDecoderReader::RequestVideoData,
skipToNextKeyFrame, currentTime) skipToNextKeyFrame, currentTime)
->RefableThen(mScheduler.get(), __func__, this, ->RefableThen(TaskQueue(), __func__, this,
&MediaDecoderStateMachine::OnVideoDecoded, &MediaDecoderStateMachine::OnVideoDecoded,
&MediaDecoderStateMachine::OnVideoNotDecoded)); &MediaDecoderStateMachine::OnVideoNotDecoded));
return NS_OK; return NS_OK;
@ -2167,9 +2178,9 @@ MediaDecoderStateMachine::DecodeError()
// Change state to shutdown before sending error report to MediaDecoder // Change state to shutdown before sending error report to MediaDecoder
// and the HTMLMediaElement, so that our pipeline can start exiting // and the HTMLMediaElement, so that our pipeline can start exiting
// cleanly during the sync dispatch below. // cleanly during the sync dispatch below.
DECODER_WARN("Decode error, changed state to SHUTDOWN due to error"); ScheduleStateMachine();
SetState(DECODER_STATE_SHUTDOWN); SetState(DECODER_STATE_SHUTDOWN);
mScheduler->ScheduleAndShutdown(); DECODER_WARN("Decode error, changed state to SHUTDOWN due to error");
mDecoder->GetReentrantMonitor().NotifyAll(); mDecoder->GetReentrantMonitor().NotifyAll();
// Dispatch the event to call DecodeError synchronously. This ensures // Dispatch the event to call DecodeError synchronously. This ensures
@ -2322,12 +2333,12 @@ MediaDecoderStateMachine::DecodeFirstFrame()
if (HasAudio()) { if (HasAudio()) {
RefPtr<nsIRunnable> decodeTask( RefPtr<nsIRunnable> decodeTask(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded)); NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded));
AudioQueue().AddPopListener(decodeTask, GetStateMachineThread()); AudioQueue().AddPopListener(decodeTask, TaskQueue());
} }
if (HasVideo()) { if (HasVideo()) {
RefPtr<nsIRunnable> decodeTask( RefPtr<nsIRunnable> decodeTask(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded)); NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded));
VideoQueue().AddPopListener(decodeTask, GetStateMachineThread()); VideoQueue().AddPopListener(decodeTask, TaskQueue());
} }
if (IsRealTime()) { if (IsRealTime()) {
@ -2345,7 +2356,7 @@ MediaDecoderStateMachine::DecodeFirstFrame()
if (HasAudio()) { if (HasAudio()) {
mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
__func__, &MediaDecoderReader::RequestAudioData) __func__, &MediaDecoderReader::RequestAudioData)
->RefableThen(mScheduler.get(), __func__, this, ->RefableThen(TaskQueue(), __func__, this,
&MediaDecoderStateMachine::OnAudioDecoded, &MediaDecoderStateMachine::OnAudioDecoded,
&MediaDecoderStateMachine::OnAudioNotDecoded)); &MediaDecoderStateMachine::OnAudioNotDecoded));
} }
@ -2353,7 +2364,7 @@ MediaDecoderStateMachine::DecodeFirstFrame()
mVideoDecodeStartTime = TimeStamp::Now(); mVideoDecodeStartTime = TimeStamp::Now();
mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
__func__, &MediaDecoderReader::RequestVideoData, false, int64_t(0)) __func__, &MediaDecoderReader::RequestVideoData, false, int64_t(0))
->RefableThen(mScheduler.get(), __func__, this, ->RefableThen(TaskQueue(), __func__, this,
&MediaDecoderStateMachine::OnVideoDecoded, &MediaDecoderStateMachine::OnVideoDecoded,
&MediaDecoderStateMachine::OnVideoNotDecoded)); &MediaDecoderStateMachine::OnVideoNotDecoded));
} }
@ -2550,42 +2561,26 @@ MediaDecoderStateMachine::SeekCompleted()
} }
} }
// Runnable to dispose of the decoder and state machine on the main thread. class DecoderDisposer
class nsDecoderDisposeEvent : public nsRunnable { {
public: public:
nsDecoderDisposeEvent(already_AddRefed<MediaDecoder> aDecoder, NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecoderDisposer)
already_AddRefed<MediaDecoderStateMachine> aStateMachine) DecoderDisposer(MediaDecoder* aDecoder, MediaDecoderStateMachine* aStateMachine)
: mDecoder(aDecoder), mStateMachine(aStateMachine) {} : mDecoder(aDecoder), mStateMachine(aStateMachine) {}
NS_IMETHOD Run() {
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread."); void OnTaskQueueShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mStateMachine); MOZ_ASSERT(mStateMachine);
MOZ_ASSERT(mDecoder); MOZ_ASSERT(mDecoder);
mStateMachine->BreakCycles(); mStateMachine->BreakCycles();
mDecoder->BreakCycles(); mDecoder->BreakCycles();
mStateMachine = nullptr; mStateMachine = nullptr;
mDecoder = nullptr; mDecoder = nullptr;
return NS_OK;
} }
private:
nsRefPtr<MediaDecoder> mDecoder;
nsRefPtr<MediaDecoderStateMachine> mStateMachine;
};
// Runnable which dispatches an event to the main thread to dispose of the
// decoder and state machine. This runs on the state machine thread after
// the state machine has shutdown, and all events for that state machine have
// finished running.
class nsDispatchDisposeEvent : public nsRunnable {
public:
nsDispatchDisposeEvent(MediaDecoder* aDecoder,
MediaDecoderStateMachine* aStateMachine)
: mDecoder(aDecoder), mStateMachine(aStateMachine) {}
NS_IMETHOD Run() {
NS_DispatchToMainThread(new nsDecoderDisposeEvent(mDecoder.forget(),
mStateMachine.forget()));
return NS_OK;
}
private: private:
virtual ~DecoderDisposer() {}
nsRefPtr<MediaDecoder> mDecoder; nsRefPtr<MediaDecoder> mDecoder;
nsRefPtr<MediaDecoderStateMachine> mStateMachine; nsRefPtr<MediaDecoderStateMachine> mStateMachine;
}; };
@ -2594,7 +2589,7 @@ void
MediaDecoderStateMachine::ShutdownReader() MediaDecoderStateMachine::ShutdownReader()
{ {
MOZ_ASSERT(OnDecodeThread()); MOZ_ASSERT(OnDecodeThread());
mReader->Shutdown()->Then(mScheduler.get(), __func__, this, mReader->Shutdown()->Then(TaskQueue(), __func__, this,
&MediaDecoderStateMachine::FinishShutdown, &MediaDecoderStateMachine::FinishShutdown,
&MediaDecoderStateMachine::FinishShutdown); &MediaDecoderStateMachine::FinishShutdown);
} }
@ -2630,15 +2625,27 @@ MediaDecoderStateMachine::FinishShutdown()
// finished and released its monitor/references. That event then will // finished and released its monitor/references. That event then will
// dispatch an event to the main thread to release the decoder and // dispatch an event to the main thread to release the decoder and
// state machine. // state machine.
GetStateMachineThread()->Dispatch( DECODER_LOG("Shutting down state machine task queue");
new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL); nsCOMPtr<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
DECODER_LOG("Dispose Event Dispatched"); RefPtr<DecoderDisposer> disposer = new DecoderDisposer(mDecoder, this);
TaskQueue()->BeginShutdown()->Then(mainThread.get(), __func__, disposer.get(),
&DecoderDisposer::OnTaskQueueShutdown,
&DecoderDisposer::OnTaskQueueShutdown);
} }
nsresult MediaDecoderStateMachine::RunStateMachine() nsresult MediaDecoderStateMachine::RunStateMachine()
{ {
AssertCurrentThreadInMonitor(); MOZ_ASSERT(OnStateMachineThread());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDelayedScheduler.Reset(); // Must happen on state machine thread.
mDispatchedStateMachine = false;
// If audio is being captured, stop the audio sink if it's running
if (mAudioCaptured) {
StopAudioThread();
}
MediaResource* resource = mDecoder->GetResource(); MediaResource* resource = mDecoder->GetResource();
NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER); NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
@ -2668,17 +2675,6 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
} }
case DECODER_STATE_DORMANT: { case DECODER_STATE_DORMANT: {
if (IsPlaying()) {
StopPlayback();
}
StopAudioThread();
FlushDecoding();
// Now that those threads are stopped, there's no possibility of
// mPendingWakeDecoder being needed again. Revoke it.
mPendingWakeDecoder = nullptr;
DebugOnly<nsresult> rv = DecodeTaskQueue()->Dispatch(
NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources));
MOZ_ASSERT(NS_SUCCEEDED(rv));
return NS_OK; return NS_OK;
} }
@ -2739,7 +2735,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
DECODER_LOG("Buffering: wait %ds, timeout in %.3lfs %s", DECODER_LOG("Buffering: wait %ds, timeout in %.3lfs %s",
mBufferingWait, mBufferingWait - elapsed.ToSeconds(), mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
(mQuickBuffering ? "(quick exit)" : "")); (mQuickBuffering ? "(quick exit)" : ""));
ScheduleStateMachine(USECS_PER_S); ScheduleStateMachineIn(USECS_PER_S);
return NS_OK; return NS_OK;
} }
} else if (OutOfDecodedAudio() || OutOfDecodedVideo()) { } else if (OutOfDecodedAudio() || OutOfDecodedVideo()) {
@ -3063,7 +3059,7 @@ void MediaDecoderStateMachine::AdvanceFrame()
// Don't go straight back to the state machine loop since that might // Don't go straight back to the state machine loop since that might
// cause us to start decoding again and we could flip-flop between // cause us to start decoding again and we could flip-flop between
// decoding and quick-buffering. // decoding and quick-buffering.
ScheduleStateMachine(USECS_PER_S); ScheduleStateMachineIn(USECS_PER_S);
return; return;
} }
} }
@ -3125,7 +3121,12 @@ void MediaDecoderStateMachine::AdvanceFrame()
// ready state. Post an update to do so. // ready state. Post an update to do so.
UpdateReadyState(); UpdateReadyState();
ScheduleStateMachine(remainingTime / mPlaybackRate); int64_t delay = remainingTime / mPlaybackRate;
if (delay > 0) {
ScheduleStateMachineIn(delay);
} else {
ScheduleStateMachine();
}
} }
nsresult nsresult
@ -3371,33 +3372,59 @@ void MediaDecoderStateMachine::SetPlayStartTime(const TimeStamp& aTimeStamp)
} }
} }
nsresult MediaDecoderStateMachine::CallRunStateMachine()
{
AssertCurrentThreadInMonitor();
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
// If audio is being captured, stop the audio sink if it's running
if (mAudioCaptured) {
StopAudioThread();
}
return RunStateMachine();
}
nsresult MediaDecoderStateMachine::TimeoutExpired(void* aClosure)
{
MediaDecoderStateMachine* p = static_cast<MediaDecoderStateMachine*>(aClosure);
return p->CallRunStateMachine();
}
void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() { void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
DispatchAudioDecodeTaskIfNeeded(); DispatchAudioDecodeTaskIfNeeded();
DispatchVideoDecodeTaskIfNeeded(); DispatchVideoDecodeTaskIfNeeded();
} }
nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) { void
return mScheduler->Schedule(aUsecs); MediaDecoderStateMachine::ScheduleStateMachine() {
AssertCurrentThreadInMonitor();
if (mState == DECODER_STATE_SHUTDOWN) {
NS_WARNING("Refusing to schedule shutdown state machine");
return;
}
if (mDispatchedStateMachine) {
return;
}
mDispatchedStateMachine = true;
RefPtr<nsIRunnable> task =
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::RunStateMachine);
nsresult rv = TaskQueue()->Dispatch(task);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
(void) rv;
}
void
MediaDecoderStateMachine::ScheduleStateMachineIn(int64_t aMicroseconds)
{
AssertCurrentThreadInMonitor();
MOZ_ASSERT(OnStateMachineThread()); // mDelayedScheduler.Ensure() may Disconnect()
// the promise, which must happen on the state
// machine thread.
MOZ_ASSERT(aMicroseconds > 0);
if (mState == DECODER_STATE_SHUTDOWN) {
NS_WARNING("Refusing to schedule shutdown state machine");
return;
}
if (mDispatchedStateMachine) {
return;
}
// Real-time weirdness.
if (IsRealTime()) {
aMicroseconds = std::min(aMicroseconds, int64_t(40000));
}
TimeStamp now = TimeStamp::Now();
TimeStamp target = now + TimeDuration::FromMicroseconds(aMicroseconds);
SAMPLE_LOG("Scheduling state machine for %lf ms from now", (target - now).ToMilliseconds());
mDelayedScheduler.Ensure(target);
} }
bool MediaDecoderStateMachine::OnDecodeThread() const bool MediaDecoderStateMachine::OnDecodeThread() const
@ -3407,17 +3434,12 @@ bool MediaDecoderStateMachine::OnDecodeThread() const
bool MediaDecoderStateMachine::OnStateMachineThread() const bool MediaDecoderStateMachine::OnStateMachineThread() const
{ {
return mScheduler->OnStateMachineThread(); return TaskQueue()->IsCurrentThreadIn();
}
nsIEventTarget* MediaDecoderStateMachine::GetStateMachineThread() const
{
return mScheduler->GetStateMachineThread();
} }
bool MediaDecoderStateMachine::IsStateMachineScheduled() const bool MediaDecoderStateMachine::IsStateMachineScheduled() const
{ {
return mScheduler->IsScheduled(); return mDispatchedStateMachine || mDelayedScheduler.IsScheduled();
} }
void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate) void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate)

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

@ -89,8 +89,8 @@ hardware (via AudioStream).
#include "MediaDecoderReader.h" #include "MediaDecoderReader.h"
#include "MediaDecoderOwner.h" #include "MediaDecoderOwner.h"
#include "MediaMetadataManager.h" #include "MediaMetadataManager.h"
#include "MediaDecoderStateMachineScheduler.h"
#include "mozilla/RollingMean.h" #include "mozilla/RollingMean.h"
#include "MediaTimer.h"
class nsITimer; class nsITimer;
@ -211,8 +211,8 @@ public:
void Play() void Play()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<nsRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::PlayInternal); RefPtr<nsRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::PlayInternal);
GetStateMachineThread()->Dispatch(r, NS_DISPATCH_NORMAL); TaskQueue()->Dispatch(r);
} }
private: private:
@ -311,21 +311,30 @@ public:
void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset); void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
// Returns the shared state machine thread. // Returns the state machine task queue.
nsIEventTarget* GetStateMachineThread() const; MediaTaskQueue* TaskQueue() const { return mTaskQueue; }
// Calls ScheduleStateMachine() after taking the decoder lock. Also // Calls ScheduleStateMachine() after taking the decoder lock. Also
// notifies the decoder thread in case it's waiting on the decoder lock. // notifies the decoder thread in case it's waiting on the decoder lock.
void ScheduleStateMachineWithLockAndWakeDecoder(); void ScheduleStateMachineWithLockAndWakeDecoder();
// Schedules the shared state machine thread to run the state machine // Schedules the shared state machine thread to run the state machine.
// in aUsecs microseconds from now, if it's not already scheduled to run void ScheduleStateMachine();
// earlier, in which case the request is discarded.
nsresult ScheduleStateMachine(int64_t aUsecs = 0);
// Callback function registered with MediaDecoderStateMachineScheduler // Invokes ScheduleStateMachine to run in |aMicroseconds| microseconds,
// to run state machine cycles. // unless it's already scheduled to run earlier, in which case the
static nsresult TimeoutExpired(void* aClosure); // request is discarded.
void ScheduleStateMachineIn(int64_t aMicroseconds);
void OnDelayedSchedule()
{
MOZ_ASSERT(OnStateMachineThread());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDelayedScheduler.CompleteRequest();
ScheduleStateMachine();
}
void NotReached() { MOZ_DIAGNOSTIC_ASSERT(false); }
// Set the media fragment end time. aEndTime is in microseconds. // Set the media fragment end time. aEndTime is in microseconds.
void SetFragmentEndTime(int64_t aEndTime); void SetFragmentEndTime(int64_t aEndTime);
@ -689,9 +698,6 @@ protected:
void SendStreamAudio(AudioData* aAudio, DecodedStreamData* aStream, void SendStreamAudio(AudioData* aAudio, DecodedStreamData* aStream,
AudioSegment* aOutput); AudioSegment* aOutput);
// State machine thread run function. Defers to RunStateMachine().
nsresult CallRunStateMachine();
// Performs one "cycle" of the state machine. Polls the state, and may send // Performs one "cycle" of the state machine. Polls the state, and may send
// a video frame to be displayed, and generally manages the decode. Called // a video frame to be displayed, and generally manages the decode. Called
// periodically via timer to ensure the video stays in sync. // periodically via timer to ensure the video stays in sync.
@ -745,9 +751,60 @@ protected:
// state machine, audio and main threads. // state machine, audio and main threads.
nsRefPtr<MediaDecoder> mDecoder; nsRefPtr<MediaDecoder> mDecoder;
// Used to schedule state machine cycles. This should never outlive // Task queue for running the state machine.
// the life cycle of the state machine. nsRefPtr<MediaTaskQueue> mTaskQueue;
const nsRefPtr<MediaDecoderStateMachineScheduler> mScheduler;
// True is we are decoding a realtime stream, like a camera stream.
bool mRealTime;
// True if we've dispatched a task to run the state machine but the task has
// yet to run.
bool mDispatchedStateMachine;
// Class for managing delayed dispatches of the state machine.
class DelayedScheduler {
public:
explicit DelayedScheduler(MediaDecoderStateMachine* aSelf)
: mSelf(aSelf), mMediaTimer(new MediaTimer()) {}
bool IsScheduled() const { return !mTarget.IsNull(); }
void Reset()
{
MOZ_ASSERT(mSelf->OnStateMachineThread(),
"Must be on state machine queue to disconnect");
if (IsScheduled()) {
mRequest.Disconnect();
mTarget = TimeStamp();
}
}
void Ensure(mozilla::TimeStamp& aTarget)
{
if (IsScheduled() && mTarget <= aTarget) {
return;
}
Reset();
mTarget = aTarget;
mRequest.Begin(mMediaTimer->WaitUntil(mTarget, __func__)->RefableThen(
mSelf->TaskQueue(), __func__, mSelf,
&MediaDecoderStateMachine::OnDelayedSchedule,
&MediaDecoderStateMachine::NotReached));
}
void CompleteRequest()
{
mRequest.Complete();
mTarget = TimeStamp();
}
private:
MediaDecoderStateMachine* mSelf;
nsRefPtr<MediaTimer> mMediaTimer;
MediaPromiseConsumerHolder<mozilla::MediaTimerPromise> mRequest;
TimeStamp mTarget;
} mDelayedScheduler;
// Time at which the last video sample was requested. If it takes too long // Time at which the last video sample was requested. If it takes too long
// before the sample arrives, we will increase the amount of audio we buffer. // before the sample arrives, we will increase the amount of audio we buffer.
@ -955,7 +1012,7 @@ protected:
// samples we must consume before are considered to be finished prerolling. // samples we must consume before are considered to be finished prerolling.
uint32_t AudioPrerollUsecs() const uint32_t AudioPrerollUsecs() const
{ {
if (mScheduler->IsRealTime()) { if (IsRealTime()) {
return 0; return 0;
} }
@ -966,7 +1023,7 @@ protected:
uint32_t VideoPrerollFrames() const uint32_t VideoPrerollFrames() const
{ {
return mScheduler->IsRealTime() ? 0 : GetAmpleVideoFrames() / 2; return IsRealTime() ? 0 : GetAmpleVideoFrames() / 2;
} }
bool DonePrerollingAudio() bool DonePrerollingAudio()

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

@ -1,194 +0,0 @@
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MediaDecoderStateMachineScheduler.h"
#include "SharedThreadPool.h"
#include "mozilla/Preferences.h"
#include "mozilla/ReentrantMonitor.h"
#include "nsITimer.h"
#include "nsComponentManagerUtils.h"
#include "VideoUtils.h"
namespace {
class TimerEvent : public nsITimerCallback, public nsRunnable {
typedef mozilla::MediaDecoderStateMachineScheduler Scheduler;
NS_DECL_ISUPPORTS_INHERITED
public:
TimerEvent(Scheduler* aScheduler, int aTimerId)
: mScheduler(aScheduler), mTimerId(aTimerId) {}
NS_IMETHOD Run() MOZ_OVERRIDE {
return mScheduler->TimeoutExpired(mTimerId);
}
NS_IMETHOD Notify(nsITimer* aTimer) MOZ_OVERRIDE {
return mScheduler->TimeoutExpired(mTimerId);
}
private:
~TimerEvent() {}
Scheduler* const mScheduler;
const int mTimerId;
};
NS_IMPL_ISUPPORTS_INHERITED(TimerEvent, nsRunnable, nsITimerCallback);
} // anonymous namespace
static already_AddRefed<nsIEventTarget>
CreateStateMachineThread()
{
using mozilla::SharedThreadPool;
using mozilla::RefPtr;
RefPtr<SharedThreadPool> threadPool(
SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1));
nsCOMPtr<nsIEventTarget> rv = threadPool.get();
return rv.forget();
}
namespace mozilla {
MediaDecoderStateMachineScheduler::MediaDecoderStateMachineScheduler(
ReentrantMonitor& aMonitor,
nsresult (*aTimeoutCallback)(void*),
void* aClosure, bool aRealTime)
: mTimeoutCallback(aTimeoutCallback)
, mClosure(aClosure)
// Only enable realtime mode when "media.realtime_decoder.enabled" is true.
, mRealTime(aRealTime &&
Preferences::GetBool("media.realtime_decoder.enabled", false))
, mMonitor(aMonitor)
, mEventTarget(CreateStateMachineThread())
, mTimer(do_CreateInstance("@mozilla.org/timer;1"))
, mTimerId(0)
, mState(SCHEDULER_STATE_NONE)
, mInRunningStateMachine(false)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_CTOR(MediaDecoderStateMachineScheduler);
}
MediaDecoderStateMachineScheduler::~MediaDecoderStateMachineScheduler()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_DTOR(MediaDecoderStateMachineScheduler);
}
nsresult
MediaDecoderStateMachineScheduler::Init()
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(mEventTarget, NS_ERROR_FAILURE);
nsresult rv = mTimer->SetTarget(mEventTarget);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
MediaDecoderStateMachineScheduler::Schedule(int64_t aUsecs)
{
mMonitor.AssertCurrentThreadIn();
if (NS_WARN_IF(mState == SCHEDULER_STATE_SHUTDOWN)) {
return NS_ERROR_FAILURE;
}
aUsecs = std::max<int64_t>(aUsecs, 0);
TimeStamp timeout = TimeStamp::Now() +
TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
if (!mTimeout.IsNull() && timeout >= mTimeout) {
// We've already scheduled a timer set to expire at or before this time,
// or have an event dispatched to run the state machine.
return NS_OK;
}
uint32_t ms = static_cast<uint32_t>((aUsecs / USECS_PER_MS) & 0xFFFFFFFF);
if (IsRealTime() && ms > 40) {
ms = 40;
}
// Don't cancel the timer here for this function will be called from
// different threads.
nsresult rv = NS_ERROR_FAILURE;
nsRefPtr<TimerEvent> event = new TimerEvent(this, mTimerId+1);
if (ms == 0) {
// Dispatch a runnable to the state machine thread when delay is 0.
// It will has less latency than dispatching a runnable to the state
// machine thread which will then schedule a zero-delay timer.
rv = mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
} else if (OnStateMachineThread()) {
rv = mTimer->InitWithCallback(event, ms, nsITimer::TYPE_ONE_SHOT);
} else {
MOZ_ASSERT(false, "non-zero delay timer should be only "
"scheduled in state machine thread");
}
if (NS_SUCCEEDED(rv)) {
mTimeout = timeout;
++mTimerId;
} else {
NS_WARNING("Failed to schedule state machine");
}
return rv;
}
nsresult
MediaDecoderStateMachineScheduler::TimeoutExpired(int aTimerId)
{
ReentrantMonitorAutoEnter mon(mMonitor);
MOZ_ASSERT(OnStateMachineThread());
MOZ_ASSERT(!mInRunningStateMachine,
"State machine cycles must run in sequence!");
mInRunningStateMachine = true;
// Only run state machine cycles when id matches.
nsresult rv = NS_OK;
if (mTimerId == aTimerId) {
ResetTimer();
rv = mTimeoutCallback(mClosure);
}
mInRunningStateMachine = false;
return rv;
}
void
MediaDecoderStateMachineScheduler::ScheduleAndShutdown()
{
mMonitor.AssertCurrentThreadIn();
// Schedule next cycle to handle SHUTDOWN in state machine thread.
Schedule();
// This must be set after calling Schedule()
// which does nothing in shutdown state.
mState = SCHEDULER_STATE_SHUTDOWN;
}
bool
MediaDecoderStateMachineScheduler::OnStateMachineThread() const
{
bool rv = false;
mEventTarget->IsOnCurrentThread(&rv);
return rv;
}
bool
MediaDecoderStateMachineScheduler::IsScheduled() const
{
mMonitor.AssertCurrentThreadIn();
return !mTimeout.IsNull();
}
void
MediaDecoderStateMachineScheduler::ResetTimer()
{
mMonitor.AssertCurrentThreadIn();
mTimer->Cancel();
mTimeout = TimeStamp();
}
} // namespace mozilla

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

@ -1,78 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MediaDecoderStateMachineScheduler_h__
#define MediaDecoderStateMachineScheduler_h__
#include "nsCOMPtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/DebugOnly.h"
class nsITimer;
class nsIEventTarget;
namespace mozilla {
class ReentrantMonitor;
class MediaDecoderStateMachineScheduler {
enum State {
SCHEDULER_STATE_NONE,
SCHEDULER_STATE_SHUTDOWN
};
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderStateMachineScheduler)
MediaDecoderStateMachineScheduler(ReentrantMonitor& aMonitor,
nsresult (*aTimeoutCallback)(void*),
void* aClosure, bool aRealTime);
nsresult Init();
nsresult Schedule(int64_t aUsecs = 0);
void ScheduleAndShutdown();
nsresult TimeoutExpired(int aTimerId);
bool OnStateMachineThread() const;
bool IsScheduled() const;
bool IsRealTime() const {
return mRealTime;
}
nsIEventTarget* GetStateMachineThread() const {
return mEventTarget;
}
private:
~MediaDecoderStateMachineScheduler();
void ResetTimer();
// Callback function provided by MediaDecoderStateMachine to run
// state machine cycles.
nsresult (*const mTimeoutCallback)(void*);
// Since StateMachineScheduler will never outlive the state machine,
// it is safe to keep a raw pointer only to avoid reference cycles.
void* const mClosure;
// True is we are decoding a realtime stream, like a camera stream
const bool mRealTime;
// Monitor of the decoder
ReentrantMonitor& mMonitor;
// State machine thread
const nsCOMPtr<nsIEventTarget> mEventTarget;
// Timer to schedule callbacks to run the state machine cycles.
nsCOMPtr<nsITimer> mTimer;
// Timestamp at which the next state machine cycle will run.
TimeStamp mTimeout;
// The id of timer tasks, timer callback will only run if id matches.
int mTimerId;
// No more state machine cycles in shutdown state.
State mState;
// Used to check if state machine cycles are running in sequence.
DebugOnly<bool> mInRunningStateMachine;
};
} // namespace mozilla
#endif // MediaDecoderStateMachineScheduler_h__

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

@ -112,8 +112,8 @@ using dom::SupportedVideoConstraints;
static bool static bool
HostInDomain(const nsCString &aHost, const nsCString &aPattern) HostInDomain(const nsCString &aHost, const nsCString &aPattern)
{ {
PRInt32 patternOffset = 0; int32_t patternOffset = 0;
PRInt32 hostOffset = 0; int32_t hostOffset = 0;
// Act on '*.' wildcard in the left-most position in a domain pattern. // Act on '*.' wildcard in the left-most position in a domain pattern.
if (aPattern.Length() > 2 && aPattern[0] == '*' && aPattern[1] == '.') { if (aPattern.Length() > 2 && aPattern[0] == '*' && aPattern[1] == '.') {

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

@ -6,7 +6,6 @@
#include "MediaPromise.h" #include "MediaPromise.h"
#include "MediaDecoderStateMachineScheduler.h"
#include "MediaTaskQueue.h" #include "MediaTaskQueue.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
@ -25,12 +24,6 @@ DispatchMediaPromiseRunnable(nsIEventTarget* aEventTarget, nsIRunnable* aRunnabl
return aEventTarget->Dispatch(aRunnable, NS_DISPATCH_NORMAL); return aEventTarget->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
} }
nsresult
DispatchMediaPromiseRunnable(MediaDecoderStateMachineScheduler* aScheduler, nsIRunnable* aRunnable)
{
return aScheduler->GetStateMachineThread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
}
void void
AssertOnThread(MediaTaskQueue* aQueue) AssertOnThread(MediaTaskQueue* aQueue)
{ {
@ -44,11 +37,5 @@ void AssertOnThread(nsIEventTarget* aTarget)
MOZ_ASSERT(NS_GetCurrentThread() == targetThread); MOZ_ASSERT(NS_GetCurrentThread() == targetThread);
} }
void
AssertOnThread(MediaDecoderStateMachineScheduler* aScheduler)
{
MOZ_ASSERT(aScheduler->OnStateMachineThread());
}
} }
} // namespace mozilla } // namespace mozilla

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

@ -16,6 +16,7 @@
#include "mozilla/Maybe.h" #include "mozilla/Maybe.h"
#include "mozilla/Mutex.h" #include "mozilla/Mutex.h"
#include "mozilla/Monitor.h" #include "mozilla/Monitor.h"
#include "mozilla/unused.h"
/* Polyfill __func__ on MSVC for consumers to pass to the MediaPromise API. */ /* Polyfill __func__ on MSVC for consumers to pass to the MediaPromise API. */
#ifdef _MSC_VER #ifdef _MSC_VER
@ -32,17 +33,14 @@ extern PRLogModuleInfo* gMediaPromiseLog;
PR_LOG(gMediaPromiseLog, PR_LOG_DEBUG, (x, ##__VA_ARGS__)) PR_LOG(gMediaPromiseLog, PR_LOG_DEBUG, (x, ##__VA_ARGS__))
class MediaTaskQueue; class MediaTaskQueue;
class MediaDecoderStateMachineScheduler;
namespace detail { namespace detail {
nsresult DispatchMediaPromiseRunnable(MediaTaskQueue* aQueue, nsIRunnable* aRunnable); nsresult DispatchMediaPromiseRunnable(MediaTaskQueue* aQueue, nsIRunnable* aRunnable);
nsresult DispatchMediaPromiseRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable); nsresult DispatchMediaPromiseRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable);
nsresult DispatchMediaPromiseRunnable(MediaDecoderStateMachineScheduler* aScheduler, nsIRunnable* aRunnable);
#ifdef DEBUG #ifdef DEBUG
void AssertOnThread(MediaTaskQueue* aQueue); void AssertOnThread(MediaTaskQueue* aQueue);
void AssertOnThread(nsIEventTarget* aTarget); void AssertOnThread(nsIEventTarget* aTarget);
void AssertOnThread(MediaDecoderStateMachineScheduler* aScheduler);
#endif #endif
} // namespace detail } // namespace detail
@ -108,18 +106,11 @@ public:
public: public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Consumer) NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Consumer)
void Disconnect() virtual void Disconnect() = 0;
{
AssertOnDispatchThread();
MOZ_DIAGNOSTIC_ASSERT(!mComplete);
mDisconnected = true;
}
#ifdef DEBUG // MSVC complains when an inner class (ThenValueBase::{Resolve,Reject}Runnable)
virtual void AssertOnDispatchThread() = 0; // tries to access an inherited protected member.
#else bool IsDisconnected() const { return mDisconnected; }
void AssertOnDispatchThread() {}
#endif
protected: protected:
Consumer() : mComplete(false), mDisconnected(false) {} Consumer() : mComplete(false), mDisconnected(false) {}
@ -149,7 +140,7 @@ protected:
~ResolveRunnable() ~ResolveRunnable()
{ {
MOZ_ASSERT(!mThenValue); MOZ_DIAGNOSTIC_ASSERT(!mThenValue || mThenValue->IsDisconnected());
} }
NS_IMETHODIMP Run() NS_IMETHODIMP Run()
@ -174,7 +165,7 @@ protected:
~RejectRunnable() ~RejectRunnable()
{ {
MOZ_ASSERT(!mThenValue); MOZ_DIAGNOSTIC_ASSERT(!mThenValue || mThenValue->IsDisconnected());
} }
NS_IMETHODIMP Run() NS_IMETHODIMP Run()
@ -252,35 +243,54 @@ protected:
PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]", PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]",
resolved ? "Resolving" : "Rejecting", ThenValueBase::mCallSite, resolved ? "Resolving" : "Rejecting", ThenValueBase::mCallSite,
runnable.get(), aPromise, this); runnable.get(), aPromise, this);
DebugOnly<nsresult> rv = detail::DispatchMediaPromiseRunnable(mResponseTarget, runnable); nsresult rv = detail::DispatchMediaPromiseRunnable(mResponseTarget, runnable);
MOZ_ASSERT(NS_SUCCEEDED(rv)); unused << rv;
// NB: mDisconnected is only supposed to be accessed on the dispatch
// thread. However, we require the consumer to have disconnected any
// oustanding promise requests _before_ initiating shutdown on the
// thread or task queue. So the only non-buggy scenario for dispatch
// failing involves the target thread being unable to manipulate the
// ThenValue (since it's been disconnected), so it's safe to read here.
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv) || Consumer::mDisconnected);
} }
#ifdef DEBUG #ifdef DEBUG
virtual void AssertOnDispatchThread() MOZ_OVERRIDE void AssertOnDispatchThread()
{ {
detail::AssertOnThread(mResponseTarget); detail::AssertOnThread(mResponseTarget);
} }
#else
void AssertOnDispatchThread() {}
#endif #endif
virtual void Disconnect() MOZ_OVERRIDE
{
AssertOnDispatchThread();
MOZ_DIAGNOSTIC_ASSERT(!Consumer::mComplete);
Consumer::mDisconnected = true;
// If a Consumer has been disconnected, we don't guarantee that the
// resolve/reject runnable will be dispatched. Null out our refcounted
// this-value now so that it's released predictably on the dispatch thread.
mThisVal = nullptr;
}
protected: protected:
virtual void DoResolve(ResolveValueType aResolveValue) MOZ_OVERRIDE virtual void DoResolve(ResolveValueType aResolveValue) MOZ_OVERRIDE
{ {
Consumer::mComplete = true; Consumer::mComplete = true;
if (Consumer::mDisconnected) { if (Consumer::mDisconnected) {
MOZ_ASSERT(!mThisVal);
PROMISE_LOG("ThenValue::DoResolve disconnected - bailing out [this=%p]", this); PROMISE_LOG("ThenValue::DoResolve disconnected - bailing out [this=%p]", this);
// Null these out for the same reasons described below.
mResponseTarget = nullptr;
mThisVal = nullptr;
return; return;
} }
InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aResolveValue); InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aResolveValue);
// Null these out after invoking the callback so that any references are // Null out mThisVal after invoking the callback so that any references are
// released predictably on the target thread. Otherwise, they would be // released predictably on the dispatch thread. Otherwise, it would be
// released on whatever thread last drops its reference to the ThenValue, // released on whatever thread last drops its reference to the ThenValue,
// which may or may not be ok. // which may or may not be ok.
mResponseTarget = nullptr;
mThisVal = nullptr; mThisVal = nullptr;
} }
@ -288,25 +298,22 @@ protected:
{ {
Consumer::mComplete = true; Consumer::mComplete = true;
if (Consumer::mDisconnected) { if (Consumer::mDisconnected) {
MOZ_ASSERT(!mThisVal);
PROMISE_LOG("ThenValue::DoReject disconnected - bailing out [this=%p]", this); PROMISE_LOG("ThenValue::DoReject disconnected - bailing out [this=%p]", this);
// Null these out for the same reasons described below.
mResponseTarget = nullptr;
mThisVal = nullptr;
return; return;
} }
InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aRejectValue); InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aRejectValue);
// Null these out after invoking the callback so that any references are // Null out mThisVal after invoking the callback so that any references are
// released predictably on the target thread. Otherwise, they would be // released predictably on the dispatch thread. Otherwise, it would be
// released on whatever thread last drops its reference to the ThenValue, // released on whatever thread last drops its reference to the ThenValue,
// which may or may not be ok. // which may or may not be ok.
mResponseTarget = nullptr;
mThisVal = nullptr; mThisVal = nullptr;
} }
private: private:
nsRefPtr<TargetType> mResponseTarget; nsRefPtr<TargetType> mResponseTarget; // May be released on any thread.
nsRefPtr<ThisType> mThisVal; nsRefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
ResolveMethodType mResolveMethod; ResolveMethodType mResolveMethod;
RejectMethodType mRejectMethod; RejectMethodType mRejectMethod;
}; };
@ -661,7 +668,7 @@ ProxyInternal(TargetType* aTarget, MethodCallBase<PromiseType>* aMethodCall, con
nsRefPtr<ProxyRunnable<PromiseType>> r = new ProxyRunnable<PromiseType>(p, aMethodCall); nsRefPtr<ProxyRunnable<PromiseType>> r = new ProxyRunnable<PromiseType>(p, aMethodCall);
nsresult rv = detail::DispatchMediaPromiseRunnable(aTarget, r); nsresult rv = detail::DispatchMediaPromiseRunnable(aTarget, r);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
(void) rv; // Avoid compilation failures in builds with MOZ_DIAGNOSTIC_ASSERT disabled. unused << rv;
return Move(p); return Move(p);
} }

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

@ -157,7 +157,7 @@ template <class T> class MediaQueue : private nsDeque {
mPopListeners.Clear(); mPopListeners.Clear();
} }
void AddPopListener(nsIRunnable* aRunnable, nsIEventTarget* aTarget) { void AddPopListener(nsIRunnable* aRunnable, MediaTaskQueue* aTarget) {
ReentrantMonitorAutoEnter mon(mReentrantMonitor); ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mPopListeners.AppendElement(Listener(aRunnable, aTarget)); mPopListeners.AppendElement(Listener(aRunnable, aTarget));
} }
@ -166,7 +166,7 @@ private:
mutable ReentrantMonitor mReentrantMonitor; mutable ReentrantMonitor mReentrantMonitor;
struct Listener { struct Listener {
Listener(nsIRunnable* aRunnable, nsIEventTarget* aTarget) Listener(nsIRunnable* aRunnable, MediaTaskQueue* aTarget)
: mRunnable(aRunnable) : mRunnable(aRunnable)
, mTarget(aTarget) , mTarget(aTarget)
{ {
@ -177,7 +177,7 @@ private:
{ {
} }
RefPtr<nsIRunnable> mRunnable; RefPtr<nsIRunnable> mRunnable;
RefPtr<nsIEventTarget> mTarget; RefPtr<MediaTaskQueue> mTarget;
}; };
nsTArray<Listener> mPopListeners; nsTArray<Listener> mPopListeners;
@ -185,7 +185,7 @@ private:
void NotifyPopListeners() { void NotifyPopListeners() {
for (uint32_t i = 0; i < mPopListeners.Length(); i++) { for (uint32_t i = 0; i < mPopListeners.Length(); i++) {
Listener& l = mPopListeners[i]; Listener& l = mPopListeners[i];
l.mTarget->Dispatch(l.mRunnable, NS_DISPATCH_NORMAL); l.mTarget->Dispatch(l.mRunnable);
} }
} }

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

@ -278,7 +278,7 @@ MediaStreamGraphImpl::UpdateBufferSufficiencyState(SourceMediaStream* aStream)
} }
for (uint32_t i = 0; i < runnables.Length(); ++i) { for (uint32_t i = 0; i < runnables.Length(); ++i) {
runnables[i].mTarget->Dispatch(runnables[i].mRunnable, 0); runnables[i].mTarget->Dispatch(runnables[i].mRunnable);
} }
} }
@ -1084,6 +1084,23 @@ SetImageToBlackPixel(PlanarYCbCrImage* aImage)
aImage->SetData(data); aImage->SetData(data);
} }
class VideoFrameContainerInvalidateRunnable : public nsRunnable {
public:
explicit VideoFrameContainerInvalidateRunnable(VideoFrameContainer* aVideoFrameContainer)
: mVideoFrameContainer(aVideoFrameContainer)
{}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
mVideoFrameContainer->Invalidate();
return NS_OK;
}
private:
nsRefPtr<VideoFrameContainer> mVideoFrameContainer;
};
void void
MediaStreamGraphImpl::PlayVideo(MediaStream* aStream) MediaStreamGraphImpl::PlayVideo(MediaStream* aStream)
{ {
@ -1147,7 +1164,7 @@ MediaStreamGraphImpl::PlayVideo(MediaStream* aStream)
} }
nsCOMPtr<nsIRunnable> event = nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(output, &VideoFrameContainer::Invalidate); new VideoFrameContainerInvalidateRunnable(output);
DispatchToMainThreadAfterStreamStateUpdate(event.forget()); DispatchToMainThreadAfterStreamStateUpdate(event.forget());
} }
if (!aStream->mNotifiedFinished) { if (!aStream->mNotifiedFinished) {
@ -2476,21 +2493,21 @@ SourceMediaStream::GetEndOfAppendedData(TrackID aID)
void void
SourceMediaStream::DispatchWhenNotEnoughBuffered(TrackID aID, SourceMediaStream::DispatchWhenNotEnoughBuffered(TrackID aID,
nsIEventTarget* aSignalThread, nsIRunnable* aSignalRunnable) MediaTaskQueue* aSignalQueue, nsIRunnable* aSignalRunnable)
{ {
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
TrackData* data = FindDataForTrack(aID); TrackData* data = FindDataForTrack(aID);
if (!data) { if (!data) {
aSignalThread->Dispatch(aSignalRunnable, 0); aSignalQueue->Dispatch(aSignalRunnable);
return; return;
} }
if (data->mHaveEnough) { if (data->mHaveEnough) {
if (data->mDispatchWhenNotEnough.IsEmpty()) { if (data->mDispatchWhenNotEnough.IsEmpty()) {
data->mDispatchWhenNotEnough.AppendElement()->Init(aSignalThread, aSignalRunnable); data->mDispatchWhenNotEnough.AppendElement()->Init(aSignalQueue, aSignalRunnable);
} }
} else { } else {
aSignalThread->Dispatch(aSignalRunnable, 0); aSignalQueue->Dispatch(aSignalRunnable);
} }
} }

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

@ -16,6 +16,7 @@
#include "VideoFrameContainer.h" #include "VideoFrameContainer.h"
#include "VideoSegment.h" #include "VideoSegment.h"
#include "MainThreadUtils.h" #include "MainThreadUtils.h"
#include "MediaTaskQueue.h"
#include "nsAutoRef.h" #include "nsAutoRef.h"
#include "GraphDriver.h" #include "GraphDriver.h"
#include <speex/speex_resampler.h> #include <speex/speex_resampler.h>
@ -783,7 +784,7 @@ public:
* does not exist. No op if a runnable is already present for this track. * does not exist. No op if a runnable is already present for this track.
*/ */
void DispatchWhenNotEnoughBuffered(TrackID aID, void DispatchWhenNotEnoughBuffered(TrackID aID,
nsIEventTarget* aSignalThread, nsIRunnable* aSignalRunnable); MediaTaskQueue* aSignalQueue, nsIRunnable* aSignalRunnable);
/** /**
* Indicate that a track has ended. Do not do any more API calls * Indicate that a track has ended. Do not do any more API calls
* affecting this track. * affecting this track.
@ -847,14 +848,14 @@ public:
protected: protected:
struct ThreadAndRunnable { struct ThreadAndRunnable {
void Init(nsIEventTarget* aTarget, nsIRunnable* aRunnable) void Init(MediaTaskQueue* aTarget, nsIRunnable* aRunnable)
{ {
mTarget = aTarget; mTarget = aTarget;
mRunnable = aRunnable; mRunnable = aRunnable;
} }
nsCOMPtr<nsIEventTarget> mTarget; nsRefPtr<MediaTaskQueue> mTarget;
nsCOMPtr<nsIRunnable> mRunnable; RefPtr<nsIRunnable> mRunnable;
}; };
enum TrackCommands { enum TrackCommands {
TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED, TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED,

165
dom/media/MediaTimer.cpp Normal file
Просмотреть файл

@ -0,0 +1,165 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MediaTimer.h"
#include <math.h>
#include "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
namespace mozilla {
NS_IMPL_ADDREF(MediaTimer)
NS_IMPL_RELEASE_WITH_DESTROY(MediaTimer, DispatchDestroy())
MediaTimer::MediaTimer()
: mMonitor("MediaTimer Monitor")
, mTimer(do_CreateInstance("@mozilla.org/timer;1"))
, mUpdateScheduled(false)
{
// Use the SharedThreadPool to create an nsIThreadPool with a maximum of one
// thread, which is equivalent to an nsIThread for our purposes.
RefPtr<SharedThreadPool> threadPool(
SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaTimer"), 1));
mThread = threadPool.get();
mTimer->SetTarget(mThread);
}
void
MediaTimer::DispatchDestroy()
{
nsCOMPtr<nsIRunnable> task = NS_NewNonOwningRunnableMethod(this, &MediaTimer::Destroy);
nsresult rv = mThread->Dispatch(task, NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
(void) rv;
}
void
MediaTimer::Destroy()
{
MOZ_ASSERT(OnMediaTimerThread());
// Reject any outstanding entries. There's no need to acquire the monitor
// here, because we're on the timer thread and all other references to us
// must be gone.
while (!mEntries.empty()) {
mEntries.top().mPromise->Reject(false, __func__);
mEntries.pop();
}
// Cancel the timer if necessary.
CancelTimerIfArmed();
delete this;
}
bool
MediaTimer::OnMediaTimerThread()
{
bool rv = false;
mThread->IsOnCurrentThread(&rv);
return rv;
}
nsRefPtr<MediaTimerPromise>
MediaTimer::WaitUntil(const TimeStamp& aTimeStamp, const char* aCallSite)
{
MonitorAutoLock mon(mMonitor);
Entry e(aTimeStamp, aCallSite);
nsRefPtr<MediaTimerPromise> p = e.mPromise.get();
mEntries.push(e);
ScheduleUpdate();
return p;
}
void
MediaTimer::ScheduleUpdate()
{
mMonitor.AssertCurrentThreadOwns();
if (mUpdateScheduled) {
return;
}
mUpdateScheduled = true;
nsCOMPtr<nsIRunnable> task = NS_NewRunnableMethod(this, &MediaTimer::Update);
nsresult rv = mThread->Dispatch(task, NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
(void) rv;
}
void
MediaTimer::Update()
{
MonitorAutoLock mon(mMonitor);
UpdateLocked();
}
void
MediaTimer::UpdateLocked()
{
MOZ_ASSERT(OnMediaTimerThread());
mMonitor.AssertCurrentThreadOwns();
mUpdateScheduled = false;
// Resolve all the promises whose time is up.
TimeStamp now = TimeStamp::Now();
while (!mEntries.empty() && mEntries.top().mTimeStamp <= now) {
mEntries.top().mPromise->Resolve(true, __func__);
mEntries.pop();
}
// If we've got no more entries, cancel any pending timer and bail out.
if (mEntries.empty()) {
CancelTimerIfArmed();
return;
}
// We've got more entries - (re)arm the timer for the soonest one.
if (!TimerIsArmed() || mEntries.top().mTimeStamp < mCurrentTimerTarget) {
CancelTimerIfArmed();
ArmTimer(mEntries.top().mTimeStamp, now);
}
}
/*
* We use a callback function, rather than a callback method, to ensure that
* the nsITimer does not artifically keep the refcount of the MediaTimer above
* zero. When the MediaTimer is destroyed, it safely cancels the nsITimer so that
* we never fire against a dangling closure.
*/
/* static */ void
MediaTimer::TimerCallback(nsITimer* aTimer, void* aClosure)
{
static_cast<MediaTimer*>(aClosure)->TimerFired();
}
void
MediaTimer::TimerFired()
{
MonitorAutoLock mon(mMonitor);
MOZ_ASSERT(OnMediaTimerThread());
mCurrentTimerTarget = TimeStamp();
UpdateLocked();
}
void
MediaTimer::ArmTimer(const TimeStamp& aTarget, const TimeStamp& aNow)
{
MOZ_DIAGNOSTIC_ASSERT(!TimerIsArmed());
MOZ_DIAGNOSTIC_ASSERT(aTarget > aNow);
// XPCOM timer resolution is in milliseconds. It's important to never resolve
// a timer when mTarget might compare < now (even if very close), so round up.
unsigned long delay = std::ceil((aTarget - aNow).ToMilliseconds());
mCurrentTimerTarget = aTarget;
nsresult rv = mTimer->InitWithFuncCallback(&TimerCallback, this, delay, nsITimer::TYPE_ONE_SHOT);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
(void) rv;
}
} // namespace mozilla

99
dom/media/MediaTimer.h Normal file
Просмотреть файл

@ -0,0 +1,99 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if !defined(MediaTimer_h_)
#define MediaTimer_h_
#include "MediaPromise.h"
#include <queue>
#include "nsITimer.h"
#include "nsRefPtr.h"
#include "mozilla/Monitor.h"
#include "mozilla/TimeStamp.h"
namespace mozilla {
// This promise type is only exclusive because so far there isn't a reason for
// it not to be. Feel free to change that.
typedef MediaPromise<bool, bool, /* IsExclusive = */ true> MediaTimerPromise;
// Timers only know how to fire at a given thread, which creates an impedence
// mismatch with code that operates with MediaTaskQueues. This class solves
// that mismatch with a dedicated (but shared) thread and a nice MediaPromise-y
// interface.
class MediaTimer
{
public:
MediaTimer();
// We use a release with a custom Destroy().
NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
NS_IMETHOD_(MozExternalRefCountType) Release(void);
nsRefPtr<MediaTimerPromise> WaitUntil(const TimeStamp& aTimeStamp, const char* aCallSite);
private:
virtual ~MediaTimer() { MOZ_ASSERT(OnMediaTimerThread()); }
void DispatchDestroy(); // Invoked by Release on an arbitrary thread.
void Destroy(); // Runs on the timer thread.
bool OnMediaTimerThread();
void ScheduleUpdate();
void Update();
void UpdateLocked();
static void TimerCallback(nsITimer* aTimer, void* aClosure);
void TimerFired();
void ArmTimer(const TimeStamp& aTarget, const TimeStamp& aNow);
bool TimerIsArmed()
{
return !mCurrentTimerTarget.IsNull();
}
void CancelTimerIfArmed()
{
MOZ_ASSERT(OnMediaTimerThread());
if (TimerIsArmed()) {
mTimer->Cancel();
mCurrentTimerTarget = TimeStamp();
}
}
struct Entry
{
TimeStamp mTimeStamp;
nsRefPtr<MediaTimerPromise::Private> mPromise;
explicit Entry(const TimeStamp& aTimeStamp, const char* aCallSite)
: mTimeStamp(aTimeStamp)
, mPromise(new MediaTimerPromise::Private(aCallSite))
{}
bool operator<(const Entry& aOther) const
{
return mTimeStamp < aOther.mTimeStamp;
}
};
ThreadSafeAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
nsCOMPtr<nsIEventTarget> mThread;
std::priority_queue<Entry> mEntries;
Monitor mMonitor;
nsCOMPtr<nsITimer> mTimer;
TimeStamp mCurrentTimerTarget;
bool mUpdateScheduled;
};
} // namespace mozilla
#endif

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

@ -136,7 +136,9 @@ nsresult
AVCCMediaDataDecoder::Shutdown() AVCCMediaDataDecoder::Shutdown()
{ {
if (mDecoder) { if (mDecoder) {
return mDecoder->Shutdown(); nsresult rv = mDecoder->Shutdown();
mDecoder = nullptr;
return rv;
} }
return NS_OK; return NS_OK;
} }
@ -165,10 +167,7 @@ AVCCMediaDataDecoder::AllocateMediaResources()
void void
AVCCMediaDataDecoder::ReleaseMediaResources() AVCCMediaDataDecoder::ReleaseMediaResources()
{ {
if (mDecoder) { Shutdown();
mDecoder->Shutdown();
mDecoder = nullptr;
}
} }
nsresult nsresult
@ -280,7 +279,8 @@ AVCCDecoderModule::CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aCo
{ {
nsRefPtr<MediaDataDecoder> decoder; nsRefPtr<MediaDataDecoder> decoder;
if (strcmp(aConfig.mime_type, "video/avc") || if ((strcmp(aConfig.mime_type, "video/avc") &&
strcmp(aConfig.mime_type, "video/mp4")) ||
!mPDM->DecoderNeedsAVCC(aConfig)) { !mPDM->DecoderNeedsAVCC(aConfig)) {
// There is no need for an AVCC wrapper for non-AVC content. // There is no need for an AVCC wrapper for non-AVC content.
decoder = mPDM->CreateVideoDecoder(aConfig, decoder = mPDM->CreateVideoDecoder(aConfig,

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

@ -39,8 +39,8 @@ PRLogModuleInfo* GetDemuxerLog() {
} }
return log; return log;
} }
#define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__)) #define LOG(arg, ...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, ("MP4Reader(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
#define VLOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG+1, (__VA_ARGS__)) #define VLOG(arg, ...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, ("MP4Reader(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
#else #else
#define LOG(...) #define LOG(...)
#define VLOG(...) #define VLOG(...)
@ -322,7 +322,7 @@ bool MP4Reader::IsWaitingOnCDMResource() {
// We'll keep waiting if the CDM hasn't informed Gecko of its capabilities. // We'll keep waiting if the CDM hasn't informed Gecko of its capabilities.
{ {
CDMCaps::AutoLock caps(proxy->Capabilites()); CDMCaps::AutoLock caps(proxy->Capabilites());
LOG("MP4Reader::IsWaitingMediaResources() capsKnown=%d", caps.AreCapsKnown()); LOG("capsKnown=%d", caps.AreCapsKnown());
return !caps.AreCapsKnown(); return !caps.AreCapsKnown();
} }
#else #else
@ -618,7 +618,7 @@ MP4Reader::RequestVideoData(bool aSkipToNextKeyframe,
int64_t aTimeThreshold) int64_t aTimeThreshold)
{ {
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
VLOG("RequestVideoData skip=%d time=%lld", aSkipToNextKeyframe, aTimeThreshold); VLOG("skip=%d time=%lld", aSkipToNextKeyframe, aTimeThreshold);
if (mShutdown) { if (mShutdown) {
NS_WARNING("RequestVideoData on shutdown MP4Reader!"); NS_WARNING("RequestVideoData on shutdown MP4Reader!");
@ -654,7 +654,7 @@ nsRefPtr<MediaDecoderReader::AudioDataPromise>
MP4Reader::RequestAudioData() MP4Reader::RequestAudioData()
{ {
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
VLOG("RequestAudioData"); VLOG("");
if (mShutdown) { if (mShutdown) {
NS_WARNING("RequestAudioData on shutdown MP4Reader!"); NS_WARNING("RequestAudioData on shutdown MP4Reader!");
return AudioDataPromise::CreateAndReject(CANCELED, __func__); return AudioDataPromise::CreateAndReject(CANCELED, __func__);
@ -789,7 +789,7 @@ MP4Reader::ReturnOutput(MediaData* aData, TrackType aTrack)
if (audioData->mChannels != mInfo.mAudio.mChannels || if (audioData->mChannels != mInfo.mAudio.mChannels ||
audioData->mRate != mInfo.mAudio.mRate) { audioData->mRate != mInfo.mAudio.mRate) {
LOG("MP4Reader::ReturnOutput change of sampling rate:%d->%d", LOG("change of sampling rate:%d->%d",
mInfo.mAudio.mRate, audioData->mRate); mInfo.mAudio.mRate, audioData->mRate);
mInfo.mAudio.mRate = audioData->mRate; mInfo.mAudio.mRate = audioData->mRate;
mInfo.mAudio.mChannels = audioData->mChannels; mInfo.mAudio.mChannels = audioData->mChannels;
@ -999,7 +999,7 @@ MP4Reader::SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed
nsRefPtr<MediaDecoderReader::SeekPromise> nsRefPtr<MediaDecoderReader::SeekPromise>
MP4Reader::Seek(int64_t aTime, int64_t aEndTime) MP4Reader::Seek(int64_t aTime, int64_t aEndTime)
{ {
LOG("MP4Reader::Seek(%lld)", aTime); LOG("aTime=(%lld)", aTime);
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
MonitorAutoLock mon(mDemuxerMonitor); MonitorAutoLock mon(mDemuxerMonitor);
if (!mDecoder->GetResource()->IsTransportSeekable() || !mDemuxer->CanSeek()) { if (!mDecoder->GetResource()->IsTransportSeekable() || !mDemuxer->CanSeek()) {
@ -1019,7 +1019,7 @@ MP4Reader::Seek(int64_t aTime, int64_t aEndTime)
if (mDemuxer->HasValidAudio()) { if (mDemuxer->HasValidAudio()) {
mAudio.mTrackDemuxer->Seek(seekTime); mAudio.mTrackDemuxer->Seek(seekTime);
} }
LOG("MP4Reader::Seek(%lld) exit", aTime); LOG("aTime=%lld exit", aTime);
return SeekPromise::CreateAndResolve(seekTime, __func__); return SeekPromise::CreateAndResolve(seekTime, __func__);
} }
@ -1083,8 +1083,9 @@ bool MP4Reader::IsDormantNeeded()
#endif #endif
mVideo.mDecoder && mVideo.mDecoder &&
mVideo.mDecoder->IsDormantNeeded(); mVideo.mDecoder->IsDormantNeeded();
#endif #else
return false; return false;
#endif
} }
void MP4Reader::ReleaseMediaResources() void MP4Reader::ReleaseMediaResources()

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

@ -289,7 +289,7 @@ FFmpegH264Decoder<LIBAV_VER>::~FFmpegH264Decoder()
AVCodecID AVCodecID
FFmpegH264Decoder<LIBAV_VER>::GetCodecId(const char* aMimeType) FFmpegH264Decoder<LIBAV_VER>::GetCodecId(const char* aMimeType)
{ {
if (!strcmp(aMimeType, "video/avc")) { if (!strcmp(aMimeType, "video/avc") || !strcmp(aMimeType, "video/mp4")) {
return AV_CODEC_ID_H264; return AV_CODEC_ID_H264;
} }

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

@ -43,6 +43,19 @@ static const int MAX_VOUCHER_LENGTH = 500000;
#endif #endif
namespace mozilla { namespace mozilla {
#undef LOG
#undef LOGD
#ifdef PR_LOGGING
extern PRLogModuleInfo* GetGMPLog();
#define LOG(level, x, ...) PR_LOG(GetGMPLog(), (level), (x, ##__VA_ARGS__))
#define LOGD(x, ...) LOG(PR_LOG_DEBUG, "GMPChild[pid=%d] " x, (int)base::GetCurrentProcId(), ##__VA_ARGS__)
#else
#define LOG(level, x, ...)
#define LOGD(x, ...)
#endif
namespace gmp { namespace gmp {
GMPChild::GMPChild() GMPChild::GMPChild()
@ -50,11 +63,13 @@ GMPChild::GMPChild()
, mGMPMessageLoop(MessageLoop::current()) , mGMPMessageLoop(MessageLoop::current())
, mGMPLoader(nullptr) , mGMPLoader(nullptr)
{ {
LOGD("GMPChild ctor");
nsDebugImpl::SetMultiprocessMode("GMP"); nsDebugImpl::SetMultiprocessMode("GMP");
} }
GMPChild::~GMPChild() GMPChild::~GMPChild()
{ {
LOGD("GMPChild dtor");
} }
static bool static bool
@ -257,7 +272,9 @@ GMPChild::Init(const std::string& aPluginPath,
MessageLoop* aIOLoop, MessageLoop* aIOLoop,
IPC::Channel* aChannel) IPC::Channel* aChannel)
{ {
if (!Open(aChannel, aParentProcessHandle, aIOLoop)) { LOGD("%s pluginPath=%s", __FUNCTION__, aPluginPath.c_str());
if (NS_WARN_IF(!Open(aChannel, aParentProcessHandle, aIOLoop))) {
return false; return false;
} }
@ -273,6 +290,8 @@ GMPChild::Init(const std::string& aPluginPath,
bool bool
GMPChild::RecvSetNodeId(const nsCString& aNodeId) GMPChild::RecvSetNodeId(const nsCString& aNodeId)
{ {
LOGD("%s nodeId=%s", __FUNCTION__, aNodeId.Data());
// Store the per origin salt for the node id. Note: we do this in a // Store the per origin salt for the node id. Note: we do this in a
// separate message than RecvStartPlugin() so that the string is not // separate message than RecvStartPlugin() so that the string is not
// sitting in a string on the IPC code's call stack. // sitting in a string on the IPC code's call stack.
@ -393,6 +412,8 @@ GMPChild::GetLibPath(nsACString& aOutLibPath)
bool bool
GMPChild::RecvStartPlugin() GMPChild::RecvStartPlugin()
{ {
LOGD("%s", __FUNCTION__);
#if defined(XP_WIN) #if defined(XP_WIN)
PreLoadLibraries(mPluginPath); PreLoadLibraries(mPluginPath);
#endif #endif
@ -447,6 +468,8 @@ GMPChild::GMPMessageLoop()
void void
GMPChild::ActorDestroy(ActorDestroyReason aWhy) GMPChild::ActorDestroy(ActorDestroyReason aWhy)
{ {
LOGD("%s reason=%d", __FUNCTION__, aWhy);
if (mGMPLoader) { if (mGMPLoader) {
mGMPLoader->Shutdown(); mGMPLoader->Shutdown();
} }
@ -678,6 +701,8 @@ GMPChild::RecvCrashPluginNow()
bool bool
GMPChild::RecvBeginAsyncShutdown() GMPChild::RecvBeginAsyncShutdown()
{ {
LOGD("%s AsyncShutdown=%d", __FUNCTION__, mAsyncShutdown!=nullptr);
MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current()); MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
if (mAsyncShutdown) { if (mAsyncShutdown) {
mAsyncShutdown->BeginShutdown(); mAsyncShutdown->BeginShutdown();
@ -690,6 +715,7 @@ GMPChild::RecvBeginAsyncShutdown()
void void
GMPChild::ShutdownComplete() GMPChild::ShutdownComplete()
{ {
LOGD("%s", __FUNCTION__);
MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current()); MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
SendAsyncShutdownComplete(); SendAsyncShutdownComplete();
} }
@ -775,3 +801,6 @@ GMPChild::PreLoadSandboxVoucher()
} // namespace gmp } // namespace gmp
} // namespace mozilla } // namespace mozilla
#undef LOG
#undef LOGD

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

@ -293,7 +293,7 @@ public:
// consumers of ParseStartAndEndTimestamps to add their timestamp offset // consumers of ParseStartAndEndTimestamps to add their timestamp offset
// manually. This allows the ContainerParser to be shared across different // manually. This allows the ContainerParser to be shared across different
// timestampOffsets. // timestampOffsets.
mParser = new mp4_demuxer::MoofParser(mStream, 0, &mMonitor); mParser = new mp4_demuxer::MoofParser(mStream, 0, /* aIsAudio = */ false, &mMonitor);
mInitData = new LargeDataBuffer(); mInitData = new LargeDataBuffer();
} else if (!mStream || !mParser) { } else if (!mStream || !mParser) {
return false; return false;

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

@ -102,7 +102,6 @@ EXPORTS += [
'MediaDecoderOwner.h', 'MediaDecoderOwner.h',
'MediaDecoderReader.h', 'MediaDecoderReader.h',
'MediaDecoderStateMachine.h', 'MediaDecoderStateMachine.h',
'MediaDecoderStateMachineScheduler.h',
'MediaInfo.h', 'MediaInfo.h',
'MediaMetadataManager.h', 'MediaMetadataManager.h',
'MediaPromise.h', 'MediaPromise.h',
@ -112,6 +111,7 @@ EXPORTS += [
'MediaSegment.h', 'MediaSegment.h',
'MediaStreamGraph.h', 'MediaStreamGraph.h',
'MediaTaskQueue.h', 'MediaTaskQueue.h',
'MediaTimer.h',
'MediaTrack.h', 'MediaTrack.h',
'MediaTrackList.h', 'MediaTrackList.h',
'MP3FrameParser.h', 'MP3FrameParser.h',
@ -181,7 +181,6 @@ UNIFIED_SOURCES += [
'MediaDecoder.cpp', 'MediaDecoder.cpp',
'MediaDecoderReader.cpp', 'MediaDecoderReader.cpp',
'MediaDecoderStateMachine.cpp', 'MediaDecoderStateMachine.cpp',
'MediaDecoderStateMachineScheduler.cpp',
'MediaDevices.cpp', 'MediaDevices.cpp',
'MediaManager.cpp', 'MediaManager.cpp',
'MediaPromise.cpp', 'MediaPromise.cpp',
@ -192,6 +191,7 @@ UNIFIED_SOURCES += [
'MediaStreamGraph.cpp', 'MediaStreamGraph.cpp',
'MediaStreamTrack.cpp', 'MediaStreamTrack.cpp',
'MediaTaskQueue.cpp', 'MediaTaskQueue.cpp',
'MediaTimer.cpp',
'MediaTrack.cpp', 'MediaTrack.cpp',
'MediaTrackList.cpp', 'MediaTrackList.cpp',
'MP3FrameParser.cpp', 'MP3FrameParser.cpp',

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

@ -17,12 +17,24 @@ createHTML({
// output side. We then sanity check the audio by comparing the frequency domain // output side. We then sanity check the audio by comparing the frequency domain
// data from both analysers. // data from both analysers.
/*
* Use as callback to Array.reduce to get an object { value, index }
* that contains the largest element in the array.
*/
var maxWithIndex = function(a, b, i) {
if (b >= a.value) {
return { value: b, index: i };
} else {
return a;
}
};
runNetworkTest(function() { runNetworkTest(function() {
var test = new PeerConnectionTest(); var test = new PeerConnectionTest();
var audioContext = new AudioContext(); var audioContext = new AudioContext();
var inputAnalyser; var inputAnalyser, outputAnalyser;
var outputAnalyser; var inputData, outputData;
test.setMediaConstraints([{audio: true}], []); test.setMediaConstraints([{audio: true}], []);
test.chain.replace("PC_LOCAL_GUM", [ test.chain.replace("PC_LOCAL_GUM", [
@ -56,45 +68,38 @@ runNetworkTest(function() {
return Promise.resolve(); return Promise.resolve();
}]); }]);
test.chain.append([ test.chain.append([
function WAIT_FOR_CLEAN_AUDIO(test) { function GET_INPUT_DATA(test) {
inputData = new Uint8Array(inputAnalyser.frequencyBinCount);
inputAnalyser.getByteFrequencyData(inputData);
return Promise.resolve();
},
function GET_OUTPUT_DATA(test) {
// We've seen completely silent output with e10s, suggesting that the // We've seen completely silent output with e10s, suggesting that the
// machine is overloaded. Here we wait for the media element on the // machine is overloaded. Here we wait for actual audio data on the
// output side to progress a bit after all previous steps finish to // output side before we proceed.
// ensure we have healthy data to check. return new Promise(resolve => {
var wait = function(elem, startTime, resolve) { is(test.pcRemote.mediaCheckers.length, 1, "One media element on remote side");
elem.ontimeupdate = function(ev) { var elem = test.pcRemote.mediaCheckers[0].element;
info("Waiting... current: " + elem.currentTime + ", start: " + startTime); var data = new Uint8Array(outputAnalyser.frequencyBinCount);
if (elem.currentTime - startTime < 0.5) { elem.ontimeupdate = ev => {
outputAnalyser.getByteFrequencyData(data);
if (data.reduce(maxWithIndex, { value: -1, index: -1 }).value === 0) {
info("Waiting for output data... time: " + elem.currentTime);
return; return;
} }
elem.ontimeupdate = null; elem.ontimeupdate = null;
outputData = data;
resolve(); resolve();
} };
}; });
return Promise.all(test.pcRemote.mediaCheckers.map(function(checker) {
var elem = checker.element;
var startTime = elem.currentTime;
return new Promise((y, n) => wait(elem, startTime, y));
}));
}, },
function CHECK_AUDIO_FLOW(test) { function CHECK_AUDIO_FLOW(test) {
// This is for sanity check only. We'll deem that the streams are working // This is for sanity check only. We'll deem that the streams are working
// if the global maxima in the frequency domain for both the input and // if the global maxima in the frequency domain for both the input and
// the output are within 10 (out of 1024) steps of each other. // the output are within 10 (out of 1024) steps of each other.
var inputData = new Uint8Array(inputAnalyser.frequencyBinCount);
inputAnalyser.getByteFrequencyData(inputData);
var outputData = new Uint8Array(outputAnalyser.frequencyBinCount);
outputAnalyser.getByteFrequencyData(outputData);
is(inputData.length, outputData.length, "Equally sized datasets"); is(inputData.length, outputData.length, "Equally sized datasets");
var maxWithIndex = function(a, b, i) {
if (b >= a.value) {
return { value: b, index: i };
} else {
return a;
}
};
var initialValue = { value: -1, index: -1 }; var initialValue = { value: -1, index: -1 };
var inputMax = inputData.reduce(maxWithIndex, initialValue); var inputMax = inputData.reduce(maxWithIndex, initialValue);
var outputMax = outputData.reduce(maxWithIndex, initialValue); var outputMax = outputData.reduce(maxWithIndex, initialValue);

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

@ -20,6 +20,10 @@
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
NS_IMPL_ISUPPORTS(UDPSocket::ListenerProxy,
nsIUDPSocketListener,
nsIUDPSocketInternal)
NS_IMPL_CYCLE_COLLECTION_CLASS(UDPSocket) NS_IMPL_CYCLE_COLLECTION_CLASS(UDPSocket)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(UDPSocket, DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(UDPSocket, DOMEventTargetHelper)
@ -175,6 +179,11 @@ UDPSocket::CloseWithReason(nsresult aReason)
mReadyState = SocketReadyState::Closed; mReadyState = SocketReadyState::Closed;
if (mListenerProxy) {
mListenerProxy->Disconnect();
mListenerProxy = nullptr;
}
if (mSocket) { if (mSocket) {
mSocket->Close(); mSocket->Close();
mSocket = nullptr; mSocket = nullptr;
@ -430,7 +439,9 @@ UDPSocket::InitLocal(const nsAString& aLocalAddress,
} }
mLocalPort.SetValue(localPort); mLocalPort.SetValue(localPort);
rv = mSocket->AsyncListen(this); mListenerProxy = new ListenerProxy(this);
rv = mSocket->AsyncListen(mListenerProxy);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -458,7 +469,14 @@ UDPSocket::InitRemote(const nsAString& aLocalAddress,
return rv; return rv;
} }
rv = sock->Bind(this, NS_ConvertUTF16toUTF8(aLocalAddress), aLocalPort, mAddressReuse, mLoopback); mListenerProxy = new ListenerProxy(this);
rv = sock->Bind(mListenerProxy,
NS_ConvertUTF16toUTF8(aLocalAddress),
aLocalPort,
mAddressReuse,
mLoopback);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }

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

@ -128,6 +128,30 @@ public:
ErrorResult& aRv); ErrorResult& aRv);
private: private:
class ListenerProxy : public nsIUDPSocketListener
, public nsIUDPSocketInternal
{
public:
NS_DECL_ISUPPORTS
NS_FORWARD_SAFE_NSIUDPSOCKETLISTENER(mSocket)
NS_FORWARD_SAFE_NSIUDPSOCKETINTERNAL(mSocket)
explicit ListenerProxy(UDPSocket* aSocket)
: mSocket(aSocket)
{
}
void Disconnect()
{
mSocket = nullptr;
}
private:
virtual ~ListenerProxy() {}
UDPSocket* mSocket;
};
UDPSocket(nsPIDOMWindow* aOwner, UDPSocket(nsPIDOMWindow* aOwner,
const nsCString& aRemoteAddress, const nsCString& aRemoteAddress,
const Nullable<uint16_t>& aRemotePort); const Nullable<uint16_t>& aRemotePort);
@ -176,6 +200,7 @@ private:
nsCOMPtr<nsIUDPSocket> mSocket; nsCOMPtr<nsIUDPSocket> mSocket;
nsCOMPtr<nsIUDPSocketChild> mSocketChild; nsCOMPtr<nsIUDPSocketChild> mSocketChild;
nsRefPtr<ListenerProxy> mListenerProxy;
struct MulticastCommand { struct MulticastCommand {
enum CommandType { Join, Leave }; enum CommandType { Join, Leave };

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

@ -24,4 +24,3 @@ skip-if = true # Bug 958689
[test_networkstats_enabled_perm.html] [test_networkstats_enabled_perm.html]
skip-if = toolkit != "gonk" skip-if = toolkit != "gonk"
[test_udpsocket.html] [test_udpsocket.html]
skip-if = toolkit != "gonk" || (toolkit == 'gonk' && debug) # Bug 1061174 for B2G debug

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

@ -146,15 +146,9 @@ function testSendBigArray(socket) {
ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']'); ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']');
if (byteReceived >= BIG_TYPED_ARRAY.length) { if (byteReceived >= BIG_TYPED_ARRAY.length) {
socket.removeEventListener('message', recv_callback); socket.removeEventListener('message', recv_callback);
clearTimeout(timeout);
resolve(socket); resolve(socket);
} }
}); });
let timeout = setTimeout(function() {
ok(false, 'timeout for sending big array');
resolve(socket);
}, 5000);
}); });
} }
@ -173,15 +167,9 @@ function testSendBigBlob(socket) {
ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']'); ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']');
if (byteReceived >= BIG_TYPED_ARRAY.length) { if (byteReceived >= BIG_TYPED_ARRAY.length) {
socket.removeEventListener('message', recv_callback); socket.removeEventListener('message', recv_callback);
clearTimeout(timeout);
resolve(socket); resolve(socket);
} }
}); });
let timeout = setTimeout(function() {
ok(false, 'timeout for sending big blob');
resolve(socket);
}, 5000);
}); });
} }

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

@ -122,21 +122,26 @@ NPObjectIsOutOfProcessProxy(NPObject *obj)
// Helper class that reports any JS exceptions that were thrown while // Helper class that reports any JS exceptions that were thrown while
// the plugin executed JS. // the plugin executed JS.
class AutoJSExceptionReporter class MOZ_STACK_CLASS AutoJSExceptionReporter
{ {
public: public:
explicit AutoJSExceptionReporter(JSContext* aCx) AutoJSExceptionReporter(dom::AutoJSAPI& jsapi, nsJSObjWrapper* aWrapper)
: mCx(aCx) : mJsapi(jsapi)
, mIsDestroyPending(aWrapper->mDestroyPending)
{ {
jsapi.TakeOwnershipOfErrorReporting();
} }
~AutoJSExceptionReporter() ~AutoJSExceptionReporter()
{ {
JS_ReportPendingException(mCx); if (mIsDestroyPending) {
mJsapi.ClearException();
}
} }
protected: protected:
JSContext *mCx; dom::AutoJSAPI& mJsapi;
bool mIsDestroyPending;
}; };
@ -746,7 +751,7 @@ nsJSObjWrapper::NP_HasMethod(NPObject *npobj, NPIdentifier id)
JSAutoCompartment ac(cx, npjsobj->mJSObj); JSAutoCompartment ac(cx, npjsobj->mJSObj);
AutoJSExceptionReporter reporter(cx); AutoJSExceptionReporter reporter(jsapi, npjsobj);
JS::Rooted<JS::Value> v(cx); JS::Rooted<JS::Value> v(cx);
bool ok = GetProperty(cx, npjsobj->mJSObj, id, &v); bool ok = GetProperty(cx, npjsobj->mJSObj, id, &v);
@ -786,7 +791,7 @@ doInvoke(NPObject *npobj, NPIdentifier method, const NPVariant *args,
JSAutoCompartment ac(cx, jsobj); JSAutoCompartment ac(cx, jsobj);
JS::Rooted<JS::Value> fv(cx); JS::Rooted<JS::Value> fv(cx);
AutoJSExceptionReporter reporter(cx); AutoJSExceptionReporter reporter(aes, npjsobj);
if (method != NPIdentifier_VOID) { if (method != NPIdentifier_VOID) {
if (!GetProperty(cx, jsobj, method, &fv) || if (!GetProperty(cx, jsobj, method, &fv) ||
@ -871,7 +876,7 @@ nsJSObjWrapper::NP_HasProperty(NPObject *npobj, NPIdentifier npid)
nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
bool found, ok = false; bool found, ok = false;
AutoJSExceptionReporter reporter(cx); AutoJSExceptionReporter reporter(jsapi, npjsobj);
JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj); JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
JSAutoCompartment ac(cx, jsobj); JSAutoCompartment ac(cx, jsobj);
@ -908,7 +913,7 @@ nsJSObjWrapper::NP_GetProperty(NPObject *npobj, NPIdentifier id,
nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
AutoJSExceptionReporter reporter(cx); AutoJSExceptionReporter reporter(aes, npjsobj);
JSAutoCompartment ac(cx, npjsobj->mJSObj); JSAutoCompartment ac(cx, npjsobj->mJSObj);
JS::Rooted<JS::Value> v(cx); JS::Rooted<JS::Value> v(cx);
@ -943,7 +948,7 @@ nsJSObjWrapper::NP_SetProperty(NPObject *npobj, NPIdentifier npid,
nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
bool ok = false; bool ok = false;
AutoJSExceptionReporter reporter(cx); AutoJSExceptionReporter reporter(aes, npjsobj);
JS::Rooted<JSObject*> jsObj(cx, npjsobj->mJSObj); JS::Rooted<JSObject*> jsObj(cx, npjsobj->mJSObj);
JSAutoCompartment ac(cx, jsObj); JSAutoCompartment ac(cx, jsObj);
@ -977,7 +982,7 @@ nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier npid)
nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
AutoJSExceptionReporter reporter(cx); AutoJSExceptionReporter reporter(jsapi, npjsobj);
JS::ObjectOpResult result; JS::ObjectOpResult result;
JS::Rooted<JSObject*> obj(cx, npjsobj->mJSObj); JS::Rooted<JSObject*> obj(cx, npjsobj->mJSObj);
JSAutoCompartment ac(cx, obj); JSAutoCompartment ac(cx, obj);
@ -1029,7 +1034,7 @@ nsJSObjWrapper::NP_Enumerate(NPObject *npobj, NPIdentifier **idarray,
nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
AutoJSExceptionReporter reporter(cx); AutoJSExceptionReporter reporter(jsapi, npjsobj);
JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj); JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
JSAutoCompartment ac(cx, jsobj); JSAutoCompartment ac(cx, jsobj);
@ -1120,6 +1125,17 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, JS::Handle<JSObject*> obj)
return nullptr; return nullptr;
} }
// If we're running out-of-process and initializing asynchronously, and if
// the plugin has been asked to destroy itself during initialization,
// don't return any new NPObjects.
nsNPAPIPluginInstance* inst = static_cast<nsNPAPIPluginInstance*>(npp->ndata);
if (inst->GetPlugin()->GetLibrary()->IsOOP()) {
PluginAsyncSurrogate* surrogate = PluginAsyncSurrogate::Cast(npp);
if (surrogate && surrogate->IsDestroyPending()) {
return nullptr;
}
}
if (!cx) { if (!cx) {
cx = GetJSContext(npp); cx = GetJSContext(npp);
@ -2029,6 +2045,23 @@ nsJSNPRuntime::OnPluginDestroy(NPP npp)
} }
} }
// static
void
nsJSNPRuntime::OnPluginDestroyPending(NPP npp)
{
if (sJSObjWrappersAccessible) {
// Prevent modification of sJSObjWrappers table if we go reentrant.
sJSObjWrappersAccessible = false;
for (JSObjWrapperTable::Enum e(sJSObjWrappers); !e.empty(); e.popFront()) {
nsJSObjWrapper *npobj = e.front().value();
MOZ_ASSERT(npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass);
if (npobj->mNpp == npp) {
npobj->mDestroyPending = true;
}
}
sJSObjWrappersAccessible = true;
}
}
// Find the NPP for a NPObject. // Find the NPP for a NPObject.
static NPP static NPP
@ -2291,7 +2324,7 @@ nsJSObjWrapper::HasOwnProperty(NPObject *npobj, NPIdentifier npid)
nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
bool found, ok = false; bool found, ok = false;
AutoJSExceptionReporter reporter(cx); AutoJSExceptionReporter reporter(jsapi, npjsobj);
JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj); JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
JSAutoCompartment ac(cx, jsobj); JSAutoCompartment ac(cx, jsobj);

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

@ -15,6 +15,7 @@ class nsJSNPRuntime
{ {
public: public:
static void OnPluginDestroy(NPP npp); static void OnPluginDestroy(NPP npp);
static void OnPluginDestroyPending(NPP npp);
}; };
class nsJSObjWrapperKey class nsJSObjWrapperKey
@ -41,6 +42,7 @@ class nsJSObjWrapper : public NPObject
public: public:
JS::Heap<JSObject *> mJSObj; JS::Heap<JSObject *> mJSObj;
const NPP mNpp; const NPP mNpp;
bool mDestroyPending;
static NPObject *GetNewOrUsed(NPP npp, JSContext *cx, static NPObject *GetNewOrUsed(NPP npp, JSContext *cx,
JS::Handle<JSObject*> obj); JS::Handle<JSObject*> obj);

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

@ -230,8 +230,8 @@ nsPluginInstanceOwner::GetImageContainer()
// NotifySize() causes Flash to do a bunch of stuff like ask for surfaces to render // NotifySize() causes Flash to do a bunch of stuff like ask for surfaces to render
// into, set y-flip flags, etc, so we do this at the beginning. // into, set y-flip flags, etc, so we do this at the beginning.
gfxSize resolution = mPluginFrame->PresContext()->PresShell()->GetCumulativeResolution(); float resolution = mPluginFrame->PresContext()->PresShell()->GetCumulativeResolution();
ScreenSize screenSize = (r * LayoutDeviceToScreenScale2D(resolution.width, resolution.height)).Size(); ScreenSize screenSize = (r * LayoutDeviceToScreenScale(resolution)).Size();
mInstance->NotifySize(nsIntSize(screenSize.width, screenSize.height)); mInstance->NotifySize(nsIntSize(screenSize.width, screenSize.height));
container = LayerManager::CreateImageContainer(); container = LayerManager::CreateImageContainer();
@ -1468,6 +1468,23 @@ nsPluginInstanceOwner::NotifyHostCreateWidget()
#endif #endif
} }
void
nsPluginInstanceOwner::NotifyDestroyPending()
{
if (!mInstance) {
return;
}
bool isOOP = false;
if (NS_FAILED(mInstance->GetIsOOP(&isOOP)) || !isOOP) {
return;
}
NPP npp = nullptr;
if (NS_FAILED(mInstance->GetNPP(&npp)) || !npp) {
return;
}
PluginAsyncSurrogate::NotifyDestroyPending(npp);
}
nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent) nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent)
{ {
#ifdef MOZ_WIDGET_ANDROID #ifdef MOZ_WIDGET_ANDROID

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

@ -258,6 +258,7 @@ public:
void NotifyHostAsyncInitFailed(); void NotifyHostAsyncInitFailed();
void NotifyHostCreateWidget(); void NotifyHostCreateWidget();
void NotifyDestroyPending();
private: private:
virtual ~nsPluginInstanceOwner(); virtual ~nsPluginInstanceOwner();

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

@ -99,6 +99,7 @@ PluginAsyncSurrogate::PluginAsyncSurrogate(PluginModuleParent* aParent)
, mInstantiated(false) , mInstantiated(false)
, mAsyncSetWindow(false) , mAsyncSetWindow(false)
, mInitCancelled(false) , mInitCancelled(false)
, mDestroyPending(false)
, mAsyncCallsInFlight(0) , mAsyncCallsInFlight(0)
{ {
MOZ_ASSERT(aParent); MOZ_ASSERT(aParent);
@ -179,9 +180,27 @@ PluginAsyncSurrogate::NP_GetEntryPoints(NPPluginFuncs* aFuncs)
aFuncs->asfile = &PluginModuleParent::NPP_StreamAsFile; aFuncs->asfile = &PluginModuleParent::NPP_StreamAsFile;
} }
/* static */ void
PluginAsyncSurrogate::NotifyDestroyPending(NPP aInstance)
{
PluginAsyncSurrogate* surrogate = Cast(aInstance);
if (!surrogate) {
return;
}
surrogate->NotifyDestroyPending();
}
void
PluginAsyncSurrogate::NotifyDestroyPending()
{
mDestroyPending = true;
nsJSNPRuntime::OnPluginDestroyPending(mInstance);
}
NPError NPError
PluginAsyncSurrogate::NPP_Destroy(NPSavedData** aSave) PluginAsyncSurrogate::NPP_Destroy(NPSavedData** aSave)
{ {
NotifyDestroyPending();
if (!WaitForInit()) { if (!WaitForInit()) {
return NPERR_GENERIC_ERROR; return NPERR_GENERIC_ERROR;
} }
@ -417,7 +436,7 @@ PluginAsyncSurrogate::SetStreamType(NPStream* aStream, uint16_t aStreamType)
void void
PluginAsyncSurrogate::OnInstanceCreated(PluginInstanceParent* aInstance) PluginAsyncSurrogate::OnInstanceCreated(PluginInstanceParent* aInstance)
{ {
for (PRUint32 i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) { for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) {
PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i]; PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i];
uint16_t streamType = NP_NORMAL; uint16_t streamType = NP_NORMAL;
NPError curError = aInstance->NPP_NewStream( NPError curError = aInstance->NPP_NewStream(

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

@ -50,6 +50,8 @@ public:
static const NPClass* GetClass() { return &sNPClass; } static const NPClass* GetClass() { return &sNPClass; }
static void NP_GetEntryPoints(NPPluginFuncs* aFuncs); static void NP_GetEntryPoints(NPPluginFuncs* aFuncs);
static PluginAsyncSurrogate* Cast(NPP aInstance); static PluginAsyncSurrogate* Cast(NPP aInstance);
static void NotifyDestroyPending(NPP aInstance);
void NotifyDestroyPending();
virtual PluginAsyncSurrogate* virtual PluginAsyncSurrogate*
GetAsyncSurrogate() { return this; } GetAsyncSurrogate() { return this; }
@ -63,8 +65,9 @@ public:
bool* aHasProperty, bool* aHasMethod, bool* aHasProperty, bool* aHasMethod,
NPVariant* aResult); NPVariant* aResult);
PluginModuleParent* PluginModuleParent* GetParent() { return mParent; }
GetParent() { return mParent; }
bool IsDestroyPending() const { return mDestroyPending; }
bool SetAcceptingCalls(bool aAccept) bool SetAcceptingCalls(bool aAccept)
{ {
@ -151,6 +154,7 @@ private:
bool mInstantiated; bool mInstantiated;
bool mAsyncSetWindow; bool mAsyncSetWindow;
bool mInitCancelled; bool mInitCancelled;
bool mDestroyPending;
int32_t mAsyncCallsInFlight; int32_t mAsyncCallsInFlight;
static const NPClass sNPClass; static const NPClass sNPClass;

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

@ -8,6 +8,7 @@
#define mozilla_dom_PromiseNativeHandler_h #define mozilla_dom_PromiseNativeHandler_h
#include "nsISupports.h" #include "nsISupports.h"
#include "js/TypeDecls.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {

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

@ -166,3 +166,4 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1
[test_window_bar.html] [test_window_bar.html]
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s
[test_bug1022869.html] [test_bug1022869.html]
[test_bug1112040.html]

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

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1112040
-->
<head>
<title>Test for Bug 1112040</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<base href="http://mochi.test:8888/tests/dom/tests/mochitest/">
<meta charset="UTF-8">
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1112040">Mozilla Bug 1112040</a>
<p id="display">
</p>
<div id="content">
<input id="i" type="text" pattern="^.**$">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 1112040 **/
SimpleTest.runTestExpectingConsoleMessages(
function() { $('i').value = "42"; },
[{ errorMessage: "SyntaxError: nothing to repeat" }]
);
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,86 @@
function testScript(script) {
function workerTest() {
return new Promise(function(resolve, reject) {
var worker = new Worker("worker_wrapper.js");
worker.onmessage = function(event) {
if (event.data.type == 'finish') {
resolve();
} else if (event.data.type == 'status') {
ok(event.data.status, "Worker fetch test: " + event.data.msg);
}
}
worker.onerror = function(event) {
reject("Worker error: " + event.message);
};
worker.postMessage({ "script": script });
});
}
function windowTest() {
return new Promise(function(resolve, reject) {
var scriptEl = document.createElement("script");
scriptEl.setAttribute("src", script);
scriptEl.onload = function() {
runTest().then(resolve, reject);
};
document.body.appendChild(scriptEl);
});
}
SimpleTest.waitForExplicitFinish();
// We have to run the window and worker tests sequentially since some tests
// set and compare cookies and running in parallel can lead to conflicting
// values.
windowTest()
.then(function() {
return workerTest();
})
.catch(function(e) {
ok(false, "Some test failed in " + script);
info(e);
info(e.message);
return Promise.resolve();
})
.then(function() {
SimpleTest.finish();
});
}
// Utilities
// =========
// Helper that uses FileReader or FileReaderSync based on context and returns
// a Promise that resolves with the text or rejects with error.
function readAsText(blob) {
if (typeof FileReader !== "undefined") {
return new Promise(function(resolve, reject) {
var fs = new FileReader();
fs.onload = function() {
resolve(fs.result);
}
fs.onerror = reject;
fs.readAsText(blob);
});
} else {
var fs = new FileReaderSync();
return Promise.resolve(fs.readAsText(blob));
}
}
function readAsArrayBuffer(blob) {
if (typeof FileReader !== "undefined") {
return new Promise(function(resolve, reject) {
var fs = new FileReader();
fs.onload = function() {
resolve(fs.result);
}
fs.onerror = reject;
fs.readAsArrayBuffer(blob);
});
} else {
var fs = new FileReaderSync();
return Promise.resolve(fs.readAsArrayBuffer(blob));
}
}

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

@ -1,13 +1,18 @@
[DEFAULT] [DEFAULT]
support-files = support-files =
fetch_test_framework.js
test_fetch_basic.js
test_fetch_basic_http.js
test_fetch_cors.js
test_headers_common.js test_headers_common.js
test_headers_mainthread.js test_request.js
worker_test_fetch_basic.js test_response.js
worker_test_fetch_basic_http.js
worker_test_fetch_cors.js
worker_wrapper.js worker_wrapper.js
[test_headers.html] [test_headers.html]
[test_headers_mainthread.html]
[test_fetch_basic.html] [test_fetch_basic.html]
[test_fetch_basic_http.html] [test_fetch_basic_http.html]
[test_fetch_cors.html] [test_fetch_cors.html]
[test_request.html]
[test_response.html]

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

@ -13,41 +13,10 @@
<p id="display"></p> <p id="display"></p>
<div id="content" style="display: none"></div> <div id="content" style="display: none"></div>
<pre id="test"></pre> <pre id="test"></pre>
<script type="text/javascript" src="worker_test_fetch_basic.js"> </script> <script type="text/javascript" src="fetch_test_framework.js"> </script>
<script class="testbody" type="text/javascript"> <script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish(); testScript("test_fetch_basic.js");
function testOnWorker(done) {
ok(true, "=== Start Worker Tests ===");
var worker = new Worker("worker_test_fetch_basic.js");
worker.onmessage = function(event) {
if (event.data.type == "finish") {
ok(true, "=== Finish Worker Tests ===");
done();
} else if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
}
}
worker.onerror = function(event) {
ok(false, "Worker had an error: " + event.data);
ok(true, "=== Finish Worker Tests ===");
done();
};
worker.postMessage("start");
}
//
// Driver
//
testOnWorker(function() {
SimpleTest.finish();
});
</script> </script>
</script>
</pre>
</body> </body>
</html> </html>

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

@ -1,17 +1,3 @@
if (typeof ok !== "function") {
function ok(a, msg) {
dump("OK: " + !!a + " => " + a + " " + msg + "\n");
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
}
}
if (typeof is !== "function") {
function is(a, b, msg) {
dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
}
}
function testAboutURL() { function testAboutURL() {
var p1 = fetch('about:blank').then(function(res) { var p1 = fetch('about:blank').then(function(res) {
is(res.status, 200, "about:blank should load a valid Response"); is(res.status, 200, "about:blank should load a valid Response");
@ -72,24 +58,9 @@ function testSameOriginBlobURL() {
} }
function runTest() { function runTest() {
var done = function() { return Promise.resolve()
if (typeof SimpleTest === "object") {
SimpleTest.finish();
} else {
postMessage({ type: 'finish' });
}
}
Promise.resolve()
.then(testAboutURL) .then(testAboutURL)
.then(testDataURL) .then(testDataURL)
.then(testSameOriginBlobURL) .then(testSameOriginBlobURL)
// Put more promise based tests here. // Put more promise based tests here.
.then(done)
.catch(function(e) {
ok(false, "Some Response tests failed " + e);
done();
})
} }
onmessage = runTest;

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

@ -13,41 +13,10 @@
<p id="display"></p> <p id="display"></p>
<div id="content" style="display: none"></div> <div id="content" style="display: none"></div>
<pre id="test"></pre> <pre id="test"></pre>
<script type="text/javascript" src="worker_test_fetch_basic.js"> </script> <script type="text/javascript" src="fetch_test_framework.js"> </script>
<script class="testbody" type="text/javascript"> <script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish(); testScript("test_fetch_basic_http.js");
function testOnWorker(done) {
ok(true, "=== Start Worker Tests ===");
var worker = new Worker("worker_test_fetch_basic_http.js");
worker.onmessage = function(event) {
if (event.data.type == "finish") {
ok(true, "=== Finish Worker Tests ===");
done();
} else if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
}
}
worker.onerror = function(event) {
ok(false, "Worker had an error: " + event.message);
ok(true, "=== Finish Worker Tests ===");
done();
};
worker.postMessage("start");
}
//
// Driver
//
testOnWorker(function() {
SimpleTest.finish();
});
</script> </script>
</script>
</pre>
</body> </body>
</html> </html>

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

@ -1,15 +1,3 @@
if (typeof ok !== "function") {
function ok(a, msg) {
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
}
}
if (typeof is !== "function") {
function is(a, b, msg) {
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
}
}
var path = "/tests/dom/base/test/"; var path = "/tests/dom/base/test/";
var passFiles = [['file_XHR_pass1.xml', 'GET', 200, 'OK', 'text/xml'], var passFiles = [['file_XHR_pass1.xml', 'GET', 200, 'OK', 'text/xml'],
@ -133,40 +121,25 @@ function testBlob() {
ok(r.status, 200, "status should match"); ok(r.status, 200, "status should match");
return r.blob().then((b) => { return r.blob().then((b) => {
ok(b.size, 65536, "blob should have size 65536"); ok(b.size, 65536, "blob should have size 65536");
var frs = new FileReaderSync(); return readAsArrayBuffer(b).then(function(ab) {
var buf = frs.readAsArrayBuffer(b); var u8 = new Uint8Array(ab);
var u8 = new Uint8Array(buf); for (var i = 0; i < 65536; i++) {
for (var i = 0; i < 65536; i++) { if (u8[i] !== (i & 255)) {
if (u8[i] !== (i & 255)) { break;
break; }
} }
} is(i, 65536, "wrong value at offset " + i);
is(i, 65536, "wrong value at offset " + i); });
}); });
}); });
} }
function runTest() { function runTest() {
var done = function() { return Promise.resolve()
if (typeof SimpleTest === "object") {
SimpleTest.finish();
} else {
postMessage({ type: 'finish' });
}
}
Promise.resolve()
.then(testURL) .then(testURL)
.then(testURLFail) .then(testURLFail)
.then(testRequestGET) .then(testRequestGET)
.then(testResponses) .then(testResponses)
.then(testBlob) .then(testBlob)
// Put more promise based tests here. // Put more promise based tests here.
.then(done)
.catch(function(e) {
ok(false, "Some test failed " + e);
done();
});
} }
onmessage = runTest;

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

@ -1,44 +0,0 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1039846 - Test fetch() function in worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
function runTest() {
var worker = new Worker("worker_test_fetch_basic.js");
worker.onmessage = function(event) {
if (event.data.type == 'finish') {
SimpleTest.finish();
} else if (event.data.type == 'status') {
ok(event.data.status, event.data.msg);
}
}
worker.onerror = function(event) {
ok(false, "Worker had an error: " + event.message + " at " + event.lineno);
SimpleTest.finish();
};
worker.postMessage(true);
}
SimpleTest.waitForExplicitFinish();
runTest();
</script>
</pre>
</body>
</html>

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

@ -13,41 +13,10 @@
<p id="display"></p> <p id="display"></p>
<div id="content" style="display: none"></div> <div id="content" style="display: none"></div>
<pre id="test"></pre> <pre id="test"></pre>
<script type="text/javascript" src="fetch_test_framework.js"> </script>
<script class="testbody" type="text/javascript"> <script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish(); testScript("test_fetch_cors.js");
var worker;
function testOnWorker(done) {
ok(true, "=== Start Worker Tests ===");
worker = new Worker("worker_test_fetch_cors.js");
worker.onmessage = function(event) {
if (event.data.type == "finish") {
ok(true, "=== Finish Worker Tests ===");
done();
} else if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
}
}
worker.onerror = function(event) {
ok(false, "Worker had an error: " + event.message);
ok(true, "=== Finish Worker Tests ===");
done();
};
worker.postMessage("start");
}
//
// Driver
//
testOnWorker(function() {
SimpleTest.finish();
});
</script> </script>
</script>
</pre>
</body> </body>
</html> </html>

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

@ -1,15 +1,3 @@
if (typeof ok !== "function") {
function ok(a, msg) {
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
}
}
if (typeof is !== "function") {
function is(a, b, msg) {
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
}
}
var path = "/tests/dom/base/test/"; var path = "/tests/dom/base/test/";
function isOpaqueResponse(response) { function isOpaqueResponse(response) {
@ -1261,17 +1249,9 @@ function testRedirects() {
} }
function runTest() { function runTest() {
var done = function() {
if (typeof SimpleTest === "object") {
SimpleTest.finish();
} else {
postMessage({ type: 'finish' });
}
}
testNoCorsCtor(); testNoCorsCtor();
Promise.resolve() return Promise.resolve()
.then(testModeSameOrigin) .then(testModeSameOrigin)
.then(testModeNoCors) .then(testModeNoCors)
.then(testModeCors) .then(testModeCors)
@ -1279,11 +1259,4 @@ function runTest() {
.then(testCrossOriginCredentials) .then(testCrossOriginCredentials)
.then(testRedirects) .then(testRedirects)
// Put more promise based tests here. // Put more promise based tests here.
.then(done)
.catch(function(e) {
ok(false, "Some test failed " + e);
done();
});
} }
onmessage = runTest;

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

@ -8,56 +8,9 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head> </head>
<body> <body>
<script type="text/javascript" src="fetch_test_framework.js"> </script>
<script class="testbody" type="text/javascript"> <script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish(); testScript("test_headers_common.js");
function testOnWorker(done) {
ok(true, "=== Start Worker Headers Tests ===");
var worker = new Worker("worker_wrapper.js");
worker.onmessage = function(event) {
if (event.data.type == "finish") {
ok(true, "=== Finish Worker Headers Tests ===");
done();
} else if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
}
}
worker.onerror = function(event) {
ok(false, "Worker had an error: " + event.data);
ok(true, "=== Finish Worker Headers Tests ===");
done();
};
worker.postMessage({ script: "test_headers_common.js" });
}
function testOnMainThread(done) {
ok(true, "=== Start Main Thread Headers Tests ===");
var commonScript = document.createElement("script");
commonScript.setAttribute("src", "test_headers_common.js");
commonScript.onload = function() {
var mainThreadScript = document.createElement("script");
mainThreadScript.setAttribute("src", "test_headers_mainthread.js");
mainThreadScript.onload = function() {
ok(true, "=== Finish Main Thread Headers Tests ===");
done();
}
document.head.appendChild(mainThreadScript);
};
document.head.appendChild(commonScript);
}
//
// Driver
//
testOnMainThread(function() {
testOnWorker(function() {
SimpleTest.finish();
});
});
</script> </script>
</body> </body>
</html> </html>

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

@ -169,5 +169,8 @@ function TestFilledHeaders() {
}, TypeError, "Fill with non-tuple sequence should throw TypeError."); }, TypeError, "Fill with non-tuple sequence should throw TypeError.");
} }
TestEmptyHeaders(); function runTest() {
TestFilledHeaders(); TestEmptyHeaders();
TestFilledHeaders();
return Promise.resolve();
}

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

@ -1,3 +1,15 @@
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE HTML>
<html>
<head>
<title>Test Fetch Headers - Basic</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script type="text/javascript" src="test_headers_common.js"> </script>
<script type="text/javascript">
// Main thread specific tests because they need SpecialPowers. Expects // Main thread specific tests because they need SpecialPowers. Expects
// test_headers_common.js to already be loaded. // test_headers_common.js to already be loaded.
@ -139,3 +151,7 @@ TestRequestHeaders();
TestRequestNoCorsHeaders(); TestRequestNoCorsHeaders();
TestResponseHeaders(); TestResponseHeaders();
TestImmutableHeaders(); TestImmutableHeaders();
</script>
</body>
</html>

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

@ -0,0 +1,22 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug XXXXXX - Test Request object in worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script type="text/javascript" src="fetch_test_framework.js"> </script>
<script class="testbody" type="text/javascript">
testScript("test_request.js");
</script>
</body>
</html>

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

@ -1,11 +1,3 @@
function ok(a, msg) {
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
}
function is(a, b, msg) {
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
}
function testDefaultCtor() { function testDefaultCtor() {
var req = new Request(""); var req = new Request("");
is(req.method, "GET", "Default Request method is GET"); is(req.method, "GET", "Default Request method is GET");
@ -271,8 +263,9 @@ function testBodyExtraction() {
}).then(function() { }).then(function() {
return newReq().blob().then(function(v) { return newReq().blob().then(function(v) {
ok(v instanceof Blob, "Should resolve to Blob"); ok(v instanceof Blob, "Should resolve to Blob");
var fs = new FileReaderSync(); return readAsText(v).then(function(result) {
is(fs.readAsText(v), text, "Decoded Blob should match original"); is(result, text, "Decoded Blob should match original");
});
}); });
}).then(function() { }).then(function() {
return newReq().json().then(function(v) { return newReq().json().then(function(v) {
@ -311,9 +304,7 @@ function testModeCorsPreflightEnumValue() {
} }
} }
onmessage = function() { function runTest() {
var done = function() { postMessage({ type: 'finish' }) }
testDefaultCtor(); testDefaultCtor();
testSimpleUrlParse(); testSimpleUrlParse();
testUrlFragment(); testUrlFragment();
@ -321,16 +312,11 @@ onmessage = function() {
testBug1109574(); testBug1109574();
testModeCorsPreflightEnumValue(); testModeCorsPreflightEnumValue();
Promise.resolve() return Promise.resolve()
.then(testBodyCreation) .then(testBodyCreation)
.then(testBodyUsed) .then(testBodyUsed)
.then(testBodyExtraction) .then(testBodyExtraction)
.then(testUsedRequest) .then(testUsedRequest)
.then(testClone()) .then(testClone())
// Put more promise based tests here. // Put more promise based tests here.
.then(done)
.catch(function(e) {
ok(false, "Some Request tests failed " + e);
done();
})
} }

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

@ -0,0 +1,22 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1039846 - Test Response object in worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script type="text/javascript" src="fetch_test_framework.js"> </script>
<script class="testbody" type="text/javascript">
testScript("test_response.js");
</script>
</body>
</html>

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

@ -1,13 +1,3 @@
function ok(a, msg) {
dump("OK: " + !!a + " => " + a + " " + msg + "\n");
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
}
function is(a, b, msg) {
dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
}
function testDefaultCtor() { function testDefaultCtor() {
var res = new Response(); var res = new Response();
is(res.type, "default", "Default Response type is default"); is(res.type, "default", "Default Response type is default");
@ -212,8 +202,9 @@ function testBodyExtraction() {
}).then(function() { }).then(function() {
return newRes().blob().then(function(v) { return newRes().blob().then(function(v) {
ok(v instanceof Blob, "Should resolve to Blob"); ok(v instanceof Blob, "Should resolve to Blob");
var fs = new FileReaderSync(); return readAsText(v).then(function(result) {
is(fs.readAsText(v), text, "Decoded Blob should match original"); is(result, text, "Decoded Blob should match original");
});
}); });
}).then(function() { }).then(function() {
return newRes().json().then(function(v) { return newRes().json().then(function(v) {
@ -230,24 +221,17 @@ function testBodyExtraction() {
}) })
} }
onmessage = function() { function runTest() {
var done = function() { postMessage({ type: 'finish' }) }
testDefaultCtor(); testDefaultCtor();
testError(); testError();
testRedirect(); testRedirect();
testOk(); testOk();
testFinalURL(); testFinalURL();
Promise.resolve() return Promise.resolve()
.then(testBodyCreation) .then(testBodyCreation)
.then(testBodyUsed) .then(testBodyUsed)
.then(testBodyExtraction) .then(testBodyExtraction)
.then(testClone) .then(testClone)
// Put more promise based tests here. // Put more promise based tests here.
.then(done)
.catch(function(e) {
ok(false, "Some Response tests failed " + e);
done();
})
} }

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

@ -1,24 +1,29 @@
function ok(a, msg) { function ok(a, msg) {
dump("OK: " + !!a + " => " + a + " " + msg + "\n");
postMessage({type: 'status', status: !!a, msg: a + ": " + msg }); postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
} }
function is(a, b, msg) { function is(a, b, msg) {
dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg }); postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
} }
addEventListener('message', function workerWrapperOnMessage(e) { addEventListener('message', function workerWrapperOnMessage(e) {
removeEventListener('message', workerWrapperOnMessage); removeEventListener('message', workerWrapperOnMessage);
var data = e.data; var data = e.data;
var done = function() {
postMessage({ type: 'finish' });
}
try { try {
importScripts(data.script); importScripts(data.script);
// runTest() is provided by the test.
runTest().then(done, done);
} catch(e) { } catch(e) {
postMessage({ postMessage({
type: 'status', type: 'status',
status: false, status: false,
msg: 'worker failed to import ' + data.script + "; error: " + e.message msg: 'worker failed to import ' + data.script + "; error: " + e.message
}); });
done();
} }
postMessage({ type: 'finish' });
}); });

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

@ -10,6 +10,7 @@
[Exposed=ServiceWorker] [Exposed=ServiceWorker]
interface Client { interface Client {
readonly attribute DOMString id;
readonly attribute USVString url; readonly attribute USVString url;
[Throws] [Throws]

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

@ -31,9 +31,12 @@ interface MessageEvent : Event {
readonly attribute DOMString lastEventId; readonly attribute DOMString lastEventId;
/** /**
* The window or the port which originated this event. * The window, port or client which originated this event.
* FIXME(catalinb): Update this when the spec changes are implemented.
* https://www.w3.org/Bugs/Public/show_bug.cgi?id=28199
* https://bugzilla.mozilla.org/show_bug.cgi?id=1143717
*/ */
readonly attribute (WindowProxy or MessagePort)? source; readonly attribute (WindowProxy or MessagePort or Client)? source;
/** /**
* Initializes this event with the given data, in a manner analogous to * Initializes this event with the given data, in a manner analogous to

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

@ -21,7 +21,7 @@ interface ServiceWorkerContainer : EventTarget {
[Throws] [Throws]
Promise<ServiceWorkerRegistration> register(USVString scriptURL, Promise<ServiceWorkerRegistration> register(USVString scriptURL,
optional RegistrationOptionList options); optional RegistrationOptions options);
[Throws] [Throws]
Promise<ServiceWorkerRegistration> getRegistration(optional USVString documentURL = ""); Promise<ServiceWorkerRegistration> getRegistration(optional USVString documentURL = "");
@ -41,6 +41,6 @@ partial interface ServiceWorkerContainer {
DOMString getScopeForUrl(DOMString url); DOMString getScopeForUrl(DOMString url);
}; };
dictionary RegistrationOptionList { dictionary RegistrationOptions {
USVString scope = "/"; USVString scope;
}; };

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

@ -6,6 +6,7 @@
#include "ServiceWorker.h" #include "ServiceWorker.h"
#include "nsPIDOMWindow.h" #include "nsPIDOMWindow.h"
#include "ServiceWorkerClient.h"
#include "ServiceWorkerManager.h" #include "ServiceWorkerManager.h"
#include "SharedWorker.h" #include "SharedWorker.h"
#include "WorkerPrivate.h" #include "WorkerPrivate.h"
@ -95,7 +96,12 @@ ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
return; return;
} }
workerPrivate->PostMessage(aCx, aMessage, aTransferable, aRv); nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetParentObject());
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
nsAutoPtr<ServiceWorkerClientInfo> clientInfo(new ServiceWorkerClientInfo(doc));
workerPrivate->PostMessageToServiceWorker(aCx, aMessage, aTransferable,
clientInfo, aRv);
} }
WorkerPrivate* WorkerPrivate*

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

@ -31,8 +31,13 @@ ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc)
MOZ_ASSERT(aDoc); MOZ_ASSERT(aDoc);
MOZ_ASSERT(aDoc->GetWindow()); MOZ_ASSERT(aDoc->GetWindow());
nsresult rv = aDoc->GetId(mClientId);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get the UUID of the document.");
}
nsRefPtr<nsGlobalWindow> outerWindow = static_cast<nsGlobalWindow*>(aDoc->GetWindow()); nsRefPtr<nsGlobalWindow> outerWindow = static_cast<nsGlobalWindow*>(aDoc->GetWindow());
mClientId = outerWindow->WindowID(); mWindowId = outerWindow->WindowID();
aDoc->GetURL(mUrl); aDoc->GetURL(mUrl);
mVisibilityState = aDoc->VisibilityState(); mVisibilityState = aDoc->VisibilityState();
@ -61,15 +66,15 @@ namespace {
class ServiceWorkerClientPostMessageRunnable MOZ_FINAL : public nsRunnable class ServiceWorkerClientPostMessageRunnable MOZ_FINAL : public nsRunnable
{ {
uint64_t mId; uint64_t mWindowId;
JSAutoStructuredCloneBuffer mBuffer; JSAutoStructuredCloneBuffer mBuffer;
nsTArray<nsCOMPtr<nsISupports>> mClonedObjects; nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
public: public:
ServiceWorkerClientPostMessageRunnable(uint64_t aId, ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId,
JSAutoStructuredCloneBuffer&& aData, JSAutoStructuredCloneBuffer&& aData,
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects) nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
: mId(aId), : mWindowId(aWindowId),
mBuffer(Move(aData)) mBuffer(Move(aData))
{ {
mClonedObjects.SwapElements(aClonedObjects); mClonedObjects.SwapElements(aClonedObjects);
@ -79,7 +84,7 @@ public:
Run() Run()
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
nsGlobalWindow* window = nsGlobalWindow::GetOuterWindowWithId(mId); nsGlobalWindow* window = nsGlobalWindow::GetOuterWindowWithId(mWindowId);
if (!window) { if (!window) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -182,7 +187,7 @@ ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
} }
nsRefPtr<ServiceWorkerClientPostMessageRunnable> runnable = nsRefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
new ServiceWorkerClientPostMessageRunnable(mId, Move(buffer), clonedObjects); new ServiceWorkerClientPostMessageRunnable(mWindowId, Move(buffer), clonedObjects);
nsresult rv = NS_DispatchToMainThread(runnable); nsresult rv = NS_DispatchToMainThread(runnable);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
aRv.Throw(NS_ERROR_FAILURE); aRv.Throw(NS_ERROR_FAILURE);

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

@ -31,7 +31,8 @@ public:
explicit ServiceWorkerClientInfo(nsIDocument* aDoc); explicit ServiceWorkerClientInfo(nsIDocument* aDoc);
private: private:
uint64_t mClientId; nsString mClientId;
uint64_t mWindowId;
nsString mUrl; nsString mUrl;
// Window Clients // Window Clients
@ -51,6 +52,7 @@ public:
const ServiceWorkerClientInfo& aClientInfo) const ServiceWorkerClientInfo& aClientInfo)
: mOwner(aOwner), : mOwner(aOwner),
mId(aClientInfo.mClientId), mId(aClientInfo.mClientId),
mWindowId(aClientInfo.mWindowId),
mUrl(aClientInfo.mUrl) mUrl(aClientInfo.mUrl)
{ {
MOZ_ASSERT(aOwner); MOZ_ASSERT(aOwner);
@ -62,6 +64,11 @@ public:
return mOwner; return mOwner;
} }
void GetId(nsString& aRetval) const
{
aRetval = mId;
}
void void
GetUrl(nsAString& aUrl) const GetUrl(nsAString& aUrl) const
{ {
@ -81,7 +88,8 @@ protected:
private: private:
nsCOMPtr<nsISupports> mOwner; nsCOMPtr<nsISupports> mOwner;
uint64_t mId; nsString mId;
uint64_t mWindowId;
nsString mUrl; nsString mUrl;
}; };

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

@ -73,7 +73,7 @@ ServiceWorkerContainer::WrapObject(JSContext* aCx)
already_AddRefed<Promise> already_AddRefed<Promise>
ServiceWorkerContainer::Register(const nsAString& aScriptURL, ServiceWorkerContainer::Register(const nsAString& aScriptURL,
const RegistrationOptionList& aOptions, const RegistrationOptions& aOptions,
ErrorResult& aRv) ErrorResult& aRv)
{ {
nsCOMPtr<nsISupports> promise; nsCOMPtr<nsISupports> promise;
@ -84,7 +84,38 @@ ServiceWorkerContainer::Register(const nsAString& aScriptURL,
return nullptr; return nullptr;
} }
aRv = swm->Register(GetOwner(), aOptions.mScope, aScriptURL, getter_AddRefs(promise)); nsCOMPtr<nsPIDOMWindow> window = GetOwner();
MOZ_ASSERT(window);
nsresult rv;
nsCOMPtr<nsIURI> scriptURI;
rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr,
window->GetDocBaseURI());
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.ThrowTypeError(MSG_INVALID_URL, &aScriptURL);
return nullptr;
}
// In ServiceWorkerContainer.register() the scope argument is parsed against
// different base URLs depending on whether it was passed or not.
nsCOMPtr<nsIURI> scopeURI;
// Step 4. If none passed, parse against script's URL
if (!aOptions.mScope.WasPassed()) {
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), NS_LITERAL_CSTRING("./"),
nullptr, scriptURI);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
} else {
// Step 5. Parse against entry settings object's base URL.
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aOptions.mScope.Value(),
nullptr, window->GetDocBaseURI());
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.ThrowTypeError(MSG_INVALID_URL, &aOptions.mScope.Value());
return nullptr;
}
}
aRv = swm->Register(window, scopeURI, scriptURI, getter_AddRefs(promise));
if (aRv.Failed()) { if (aRv.Failed()) {
return nullptr; return nullptr;
} }

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

@ -15,7 +15,7 @@ namespace mozilla {
namespace dom { namespace dom {
class Promise; class Promise;
struct RegistrationOptionList; struct RegistrationOptions;
namespace workers { namespace workers {
class ServiceWorker; class ServiceWorker;
@ -40,7 +40,7 @@ public:
already_AddRefed<Promise> already_AddRefed<Promise>
Register(const nsAString& aScriptURL, Register(const nsAString& aScriptURL,
const RegistrationOptionList& aOptions, const RegistrationOptions& aOptions,
ErrorResult& aRv); ErrorResult& aRv);
already_AddRefed<workers::ServiceWorker> already_AddRefed<workers::ServiceWorker>

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

@ -442,6 +442,38 @@ public:
} }
}; };
namespace {
nsresult
GetRequiredScopeStringPrefix(const nsACString& aScriptSpec, nsACString& aPrefix)
{
nsCOMPtr<nsIURI> scriptURI;
nsresult rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptSpec,
nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = scriptURI->GetPrePath(aPrefix);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIURL> scriptURL(do_QueryInterface(scriptURI));
if (NS_WARN_IF(!scriptURL)) {
return rv;
}
nsAutoCString dir;
rv = scriptURL->GetDirectory(dir);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
aPrefix.Append(dir);
return NS_OK;
}
} // anonymous namespace
class ServiceWorkerRegisterJob MOZ_FINAL : public ServiceWorkerJob, class ServiceWorkerRegisterJob MOZ_FINAL : public ServiceWorkerJob,
public nsIStreamLoaderObserver public nsIStreamLoaderObserver
{ {
@ -563,6 +595,20 @@ public:
NS_WARNING("Byte wise check is disabled, just using new one"); NS_WARNING("Byte wise check is disabled, just using new one");
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
// FIXME: Bug 1130101 - Read max scope from Service-Worker-Allowed header.
nsAutoCString allowedPrefix;
rv = GetRequiredScopeStringPrefix(mRegistration->mScriptSpec, allowedPrefix);
if (NS_WARN_IF(NS_FAILED(rv))) {
Fail(NS_ERROR_DOM_SECURITY_ERR);
return rv;
}
if (!StringBeginsWith(mRegistration->mScope, allowedPrefix)) {
NS_WARNING("By default a service worker's scope is restricted to at or below it's script's location.");
Fail(NS_ERROR_DOM_SECURITY_ERR);
return NS_ERROR_DOM_SECURITY_ERR;
}
// We have to create a ServiceWorker here simply to ensure there are no // We have to create a ServiceWorker here simply to ensure there are no
// errors. Ideally we should just pass this worker on to ContinueInstall. // errors. Ideally we should just pass this worker on to ContinueInstall.
MOZ_ASSERT(!swm->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope)); MOZ_ASSERT(!swm->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
@ -592,7 +638,7 @@ public:
if (NS_WARN_IF(!ok)) { if (NS_WARN_IF(!ok)) {
swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope); swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
Fail(NS_ERROR_DOM_ABORT_ERR); Fail(NS_ERROR_DOM_ABORT_ERR);
return rv; return NS_ERROR_FAILURE;
} }
return NS_OK; return NS_OK;
@ -813,8 +859,8 @@ ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmed
// automatically reject the Promise. // automatically reject the Promise.
NS_IMETHODIMP NS_IMETHODIMP
ServiceWorkerManager::Register(nsIDOMWindow* aWindow, ServiceWorkerManager::Register(nsIDOMWindow* aWindow,
const nsAString& aScope, nsIURI* aScopeURI,
const nsAString& aScriptURL, nsIURI* aScriptURI,
nsISupports** aPromise) nsISupports** aPromise)
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
@ -884,41 +930,29 @@ ServiceWorkerManager::Register(nsIDOMWindow* aWindow,
} }
} }
nsCOMPtr<nsIURI> scriptURI;
rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, documentURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Data URLs are not allowed. // Data URLs are not allowed.
nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal(); nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal();
rv = documentPrincipal->CheckMayLoad(scriptURI, true /* report */, rv = documentPrincipal->CheckMayLoad(aScriptURI, true /* report */,
false /* allowIfInheritsPrincipal */); false /* allowIfInheritsPrincipal */);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_DOM_SECURITY_ERR; return NS_ERROR_DOM_SECURITY_ERR;
} }
nsCOMPtr<nsIURI> scopeURI; rv = documentPrincipal->CheckMayLoad(aScopeURI, true /* report */,
rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, documentURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_DOM_SECURITY_ERR;
}
rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */,
false /* allowIfInheritsPrinciple */); false /* allowIfInheritsPrinciple */);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_DOM_SECURITY_ERR; return NS_ERROR_DOM_SECURITY_ERR;
} }
nsCString cleanedScope; nsCString cleanedScope;
rv = scopeURI->GetSpecIgnoringRef(cleanedScope); rv = aScopeURI->GetSpecIgnoringRef(cleanedScope);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
nsAutoCString spec; nsAutoCString spec;
rv = scriptURI->GetSpec(spec); rv = aScriptURI->GetSpec(spec);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше