merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-06-26 13:19:12 +02:00
Родитель 81ae202190 1a7bf3eeb5
Коммит 66f9f77e3d
259 изменённых файлов: 5230 добавлений и 2830 удалений

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

@ -98,6 +98,16 @@ static nsRoleMapEntry sWAIRoleMaps[] =
kNoReqStates
// eARIAPressed is auto applied on any button
},
{ // cell
&nsGkAtoms::cell,
roles::CELL,
kUseMapRole,
eNoValue,
eNoAction,
eNoLiveAttr,
eTableCell,
kNoReqStates
},
{ // checkbox
&nsGkAtoms::checkbox,
roles::CHECKBUTTON,
@ -631,6 +641,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
kNoReqStates,
eARIASelectable
},
{ // table
&nsGkAtoms::table,
roles::TABLE,
kUseMapRole,
eNoValue,
eNoAction,
eNoLiveAttr,
eTable,
kNoReqStates,
eARIASelectable
},
{ // tablist
&nsGkAtoms::tablist,
roles::PAGETABLIST,

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

@ -48,8 +48,9 @@ uint32_t
filters::GetCell(Accessible* aAccessible)
{
a11y::role role = aAccessible->Role();
return role == roles::GRID_CELL || role == roles::ROWHEADER ||
role == roles::COLUMNHEADER ? eMatch : eSkipSubtree;
return role == roles::CELL || role == roles::GRID_CELL ||
role == roles::ROWHEADER || role == roles::COLUMNHEADER ?
eMatch : eSkipSubtree;
}
uint32_t

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

@ -68,7 +68,7 @@ ARIAGridAccessible::RowCount()
Accessible*
ARIAGridAccessible::CellAt(uint32_t aRowIndex, uint32_t aColumnIndex)
{
{
Accessible* row = GetRowAt(aRowIndex);
if (!row)
return nullptr;
@ -79,6 +79,9 @@ ARIAGridAccessible::CellAt(uint32_t aRowIndex, uint32_t aColumnIndex)
bool
ARIAGridAccessible::IsColSelected(uint32_t aColIdx)
{
if (IsARIARole(nsGkAtoms::table))
return false;
AccIterator rowIter(this, filters::GetRow);
Accessible* row = rowIter.Next();
if (!row)
@ -98,6 +101,9 @@ ARIAGridAccessible::IsColSelected(uint32_t aColIdx)
bool
ARIAGridAccessible::IsRowSelected(uint32_t aRowIdx)
{
if (IsARIARole(nsGkAtoms::table))
return false;
Accessible* row = GetRowAt(aRowIdx);
if(!row)
return false;
@ -117,6 +123,9 @@ ARIAGridAccessible::IsRowSelected(uint32_t aRowIdx)
bool
ARIAGridAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx)
{
if (IsARIARole(nsGkAtoms::table))
return false;
Accessible* row = GetRowAt(aRowIdx);
if(!row)
return false;
@ -133,6 +142,9 @@ ARIAGridAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx)
uint32_t
ARIAGridAccessible::SelectedCellCount()
{
if (IsARIARole(nsGkAtoms::table))
return 0;
uint32_t count = 0, colCount = ColCount();
AccIterator rowIter(this, filters::GetRow);
@ -159,6 +171,9 @@ ARIAGridAccessible::SelectedCellCount()
uint32_t
ARIAGridAccessible::SelectedColCount()
{
if (IsARIARole(nsGkAtoms::table))
return 0;
uint32_t colCount = ColCount();
if (!colCount)
return 0;
@ -193,6 +208,9 @@ ARIAGridAccessible::SelectedColCount()
uint32_t
ARIAGridAccessible::SelectedRowCount()
{
if (IsARIARole(nsGkAtoms::table))
return 0;
uint32_t count = 0;
AccIterator rowIter(this, filters::GetRow);
@ -227,6 +245,9 @@ ARIAGridAccessible::SelectedRowCount()
void
ARIAGridAccessible::SelectedCells(nsTArray<Accessible*>* aCells)
{
if (IsARIARole(nsGkAtoms::table))
return;
AccIterator rowIter(this, filters::GetRow);
Accessible* row = nullptr;
@ -251,6 +272,9 @@ ARIAGridAccessible::SelectedCells(nsTArray<Accessible*>* aCells)
void
ARIAGridAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells)
{
if (IsARIARole(nsGkAtoms::table))
return;
uint32_t colCount = ColCount();
AccIterator rowIter(this, filters::GetRow);
@ -275,6 +299,9 @@ ARIAGridAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells)
void
ARIAGridAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols)
{
if (IsARIARole(nsGkAtoms::table))
return;
uint32_t colCount = ColCount();
if (!colCount)
return;
@ -309,6 +336,9 @@ ARIAGridAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols)
void
ARIAGridAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows)
{
if (IsARIARole(nsGkAtoms::table))
return;
AccIterator rowIter(this, filters::GetRow);
Accessible* row = nullptr;
for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
@ -338,6 +368,9 @@ ARIAGridAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows)
void
ARIAGridAccessible::SelectRow(uint32_t aRowIdx)
{
if (IsARIARole(nsGkAtoms::table))
return;
AccIterator rowIter(this, filters::GetRow);
Accessible* row = nullptr;
@ -350,6 +383,9 @@ ARIAGridAccessible::SelectRow(uint32_t aRowIdx)
void
ARIAGridAccessible::SelectCol(uint32_t aColIdx)
{
if (IsARIARole(nsGkAtoms::table))
return;
AccIterator rowIter(this, filters::GetRow);
Accessible* row = nullptr;
@ -368,8 +404,10 @@ ARIAGridAccessible::SelectCol(uint32_t aColIdx)
void
ARIAGridAccessible::UnselectRow(uint32_t aRowIdx)
{
Accessible* row = GetRowAt(aRowIdx);
if (IsARIARole(nsGkAtoms::table))
return;
Accessible* row = GetRowAt(aRowIdx);
if (row)
SetARIASelected(row, false);
}
@ -377,6 +415,9 @@ ARIAGridAccessible::UnselectRow(uint32_t aRowIdx)
void
ARIAGridAccessible::UnselectCol(uint32_t aColIdx)
{
if (IsARIARole(nsGkAtoms::table))
return;
AccIterator rowIter(this, filters::GetRow);
Accessible* row = nullptr;
@ -421,6 +462,9 @@ nsresult
ARIAGridAccessible::SetARIASelected(Accessible* aAccessible,
bool aIsSelected, bool aNotify)
{
if (IsARIARole(nsGkAtoms::table))
return NS_OK;
nsIContent *content = aAccessible->GetContent();
NS_ENSURE_STATE(content);
@ -524,8 +568,8 @@ ARIAGridCellAccessible::ColIdx() const
for (int32_t idx = 0; idx < indexInRow; idx++) {
Accessible* cell = row->GetChildAt(idx);
roles::Role role = cell->Role();
if (role == roles::GRID_CELL || role == roles::ROWHEADER ||
role == roles::COLUMNHEADER)
if (role == roles::CELL || role == roles::GRID_CELL ||
role == roles::ROWHEADER || role == roles::COLUMNHEADER)
colIdx++;
}
@ -593,8 +637,8 @@ ARIAGridCellAccessible::NativeAttributes()
colIdx = colCount;
roles::Role role = child->Role();
if (role == roles::GRID_CELL || role == roles::ROWHEADER ||
role == roles::COLUMNHEADER)
if (role == roles::CELL || role == roles::GRID_CELL ||
role == roles::ROWHEADER || role == roles::COLUMNHEADER)
colCount++;
}

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

@ -1755,6 +1755,42 @@ HyperTextAccessible::RemoveChild(Accessible* aAccessible)
return Accessible::RemoveChild(aAccessible);
}
Relation
HyperTextAccessible::RelationByType(RelationType aType)
{
Relation rel = Accessible::RelationByType(aType);
switch (aType) {
case RelationType::NODE_CHILD_OF:
if (mContent->IsMathMLElement()) {
Accessible* parent = Parent();
if (parent) {
nsIContent* parentContent = parent->GetContent();
if (parentContent->IsMathMLElement(nsGkAtoms::mroot_)) {
// Add a relation pointing to the parent <mroot>.
rel.AppendTarget(parent);
}
}
}
break;
case RelationType::NODE_PARENT_OF:
if (mContent->IsMathMLElement(nsGkAtoms::mroot_)) {
Accessible* base = GetChildAt(0);
Accessible* index = GetChildAt(1);
if (base && index) {
// Append the <mroot> children in the order index, base.
rel.AppendTarget(index);
rel.AppendTarget(base);
}
}
break;
default:
break;
}
return rel;
}
void
HyperTextAccessible::CacheChildren()
{

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

@ -62,6 +62,7 @@ public:
virtual void InvalidateChildren() override;
virtual bool RemoveChild(Accessible* aAccessible) override;
virtual Relation RelationByType(RelationType aType) override;
// HyperTextAccessible (static helper method)

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

@ -124,6 +124,9 @@ static const uintptr_t IS_PROXY = 1;
- (void)valueDidChange;
- (void)selectedTextDidChange;
// internal method to retrieve a child at a given index.
- (id)childAt:(uint32_t)i;
#pragma mark -
// invalidates and removes all our children from our cached array.

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

@ -27,6 +27,23 @@
using namespace mozilla;
using namespace mozilla::a11y;
#define NSAccessibilityMathRootRadicandAttribute @"AXMathRootRadicand"
#define NSAccessibilityMathRootIndexAttribute @"AXMathRootIndex"
#define NSAccessibilityMathFractionNumeratorAttribute @"AXMathFractionNumerator"
#define NSAccessibilityMathFractionDenominatorAttribute @"AXMathFractionDenominator"
#define NSAccessibilityMathBaseAttribute @"AXMathBase"
#define NSAccessibilityMathSubscriptAttribute @"AXMathSubscript"
#define NSAccessibilityMathSuperscriptAttribute @"AXMathSuperscript"
#define NSAccessibilityMathUnderAttribute @"AXMathUnder"
#define NSAccessibilityMathOverAttribute @"AXMathOver"
// XXX WebKit also defines the following attributes.
// See bugs 1176970, 1176973 and 1176983.
// - NSAccessibilityMathFencedOpenAttribute @"AXMathFencedOpen"
// - NSAccessibilityMathFencedCloseAttribute @"AXMathFencedClose"
// - NSAccessibilityMathLineThicknessAttribute @"AXMathLineThickness"
// - NSAccessibilityMathPrescriptsAttribute @"AXMathPrescripts"
// - NSAccessibilityMathPostscriptsAttribute @"AXMathPostscripts"
// returns the passed in object if it is not ignored. if it's ignored, will return
// the first unignored ancestor.
static inline id
@ -121,6 +138,52 @@ GetClosestInterestingAccessible(id anObject)
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
}
- (NSArray*)additionalAccessibilityAttributeNames
{
NSMutableArray* additional = [NSMutableArray array];
switch (mRole) {
case roles::MATHML_ROOT:
[additional addObject:NSAccessibilityMathRootIndexAttribute];
[additional addObject:NSAccessibilityMathRootRadicandAttribute];
break;
case roles::MATHML_SQUARE_ROOT:
[additional addObject:NSAccessibilityMathRootRadicandAttribute];
break;
case roles::MATHML_FRACTION:
[additional addObject:NSAccessibilityMathFractionNumeratorAttribute];
[additional addObject:NSAccessibilityMathFractionDenominatorAttribute];
// XXX bug 1176973
// WebKit also defines NSAccessibilityMathLineThicknessAttribute
break;
case roles::MATHML_SUB:
case roles::MATHML_SUP:
case roles::MATHML_SUB_SUP:
[additional addObject:NSAccessibilityMathBaseAttribute];
[additional addObject:NSAccessibilityMathSubscriptAttribute];
[additional addObject:NSAccessibilityMathSuperscriptAttribute];
break;
case roles::MATHML_UNDER:
case roles::MATHML_OVER:
case roles::MATHML_UNDER_OVER:
[additional addObject:NSAccessibilityMathBaseAttribute];
[additional addObject:NSAccessibilityMathUnderAttribute];
[additional addObject:NSAccessibilityMathOverAttribute];
break;
// XXX bug 1176983
// roles::MATHML_MULTISCRIPTS should also have the following attributes:
// - NSAccessibilityMathPrescriptsAttribute
// - NSAccessibilityMathPostscriptsAttribute
// XXX bug 1176970
// roles::MATHML_FENCED should also have the following attributes:
// - NSAccessibilityMathFencedOpenAttribute
// - NSAccessibilityMathFencedCloseAttribute
default:
break;
}
return additional;
}
- (NSArray*)accessibilityAttributeNames
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
@ -155,7 +218,27 @@ GetClosestInterestingAccessible(id anObject)
nil];
}
return generalAttributes;
NSArray* objectAttributes = generalAttributes;
NSArray* additionalAttributes = [self additionalAccessibilityAttributeNames];
if ([additionalAttributes count])
objectAttributes = [objectAttributes arrayByAddingObjectsFromArray:additionalAttributes];
return objectAttributes;
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
}
- (id)childAt:(uint32_t)i
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
AccessibleWrap* accWrap = [self getGeckoAccessible];
if (accWrap) {
Accessible* acc = accWrap->GetChildAt(i);
return acc ? GetNativeFromGeckoAccessible(acc) : nil;
}
return nil;
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
}
@ -214,6 +297,93 @@ GetClosestInterestingAccessible(id anObject)
if ([attribute isEqualToString:NSAccessibilityHelpAttribute])
return [self help];
switch (mRole) {
case roles::MATHML_ROOT:
if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute])
return [self childAt:0];
if ([attribute isEqualToString:NSAccessibilityMathRootIndexAttribute])
return [self childAt:1];
break;
case roles::MATHML_SQUARE_ROOT:
if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute])
return [self childAt:0];
break;
case roles::MATHML_FRACTION:
if ([attribute isEqualToString:NSAccessibilityMathFractionNumeratorAttribute])
return [self childAt:0];
if ([attribute isEqualToString:NSAccessibilityMathFractionDenominatorAttribute])
return [self childAt:1];
// XXX bug 1176973
// WebKit also defines NSAccessibilityMathLineThicknessAttribute
break;
case roles::MATHML_SUB:
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
return [self childAt:0];
if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
return [self childAt:1];
#ifdef DEBUG
if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
return nil;
#endif
break;
case roles::MATHML_SUP:
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
return [self childAt:0];
#ifdef DEBUG
if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
return nil;
#endif
if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
return [self childAt:1];
break;
case roles::MATHML_SUB_SUP:
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
return [self childAt:0];
if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
return [self childAt:1];
if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
return [self childAt:2];
break;
case roles::MATHML_UNDER:
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
return [self childAt:0];
if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
return [self childAt:1];
#ifdef DEBUG
if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
return nil;
#endif
break;
case roles::MATHML_OVER:
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
return [self childAt:0];
#ifdef DEBUG
if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
return nil;
#endif
if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
return [self childAt:1];
break;
case roles::MATHML_UNDER_OVER:
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
return [self childAt:0];
if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
return [self childAt:1];
if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
return [self childAt:2];
break;
// XXX bug 1176983
// roles::MATHML_MULTISCRIPTS should also have the following attributes:
// - NSAccessibilityMathPrescriptsAttribute
// - NSAccessibilityMathPostscriptsAttribute
// XXX bug 1176970
// roles::MATHML_FENCED should also have the following attributes:
// - NSAccessibilityMathFencedOpenAttribute
// - NSAccessibilityMathFencedCloseAttribute
default:
break;
}
#ifdef DEBUG
NSLog (@"!!! %@ can't respond to attribute %@", self, attribute);
#endif
@ -510,7 +680,8 @@ GetClosestInterestingAccessible(id anObject)
return @"AXMathFraction";
case roles::MATHML_FENCED:
// XXX This should be AXMathFence, but doing so without implementing the
// XXX bug 1176970
// This should be AXMathFence, but doing so without implementing the
// whole fence interface seems to make VoiceOver crash, so we present it
// as a row for now.
return @"AXMathRow";
@ -557,6 +728,8 @@ GetClosestInterestingAccessible(id anObject)
// NS_MATHML_OPERATOR_SEPARATOR bits of nsOperatorFlags, but currently they
// are only available from the MathML layout code. Hence we just fallback
// to subrole AXMathOperator for now.
// XXX bug 1175747 WebKit also creates anonymous operators for <mfenced>
// which have subroles AXMathSeparatorOperator and AXMathFenceOperator.
case roles::MATHML_OPERATOR:
return @"AXMathOperator";
@ -607,10 +780,12 @@ struct RoleDescrComparator
NSString* subrole = [self subrole];
size_t idx = 0;
if (BinarySearchIf(sRoleDescrMap, 0, ArrayLength(sRoleDescrMap),
RoleDescrComparator(subrole), &idx)) {
return utils::LocalizedString(sRoleDescrMap[idx].description);
if (subrole) {
size_t idx = 0;
if (BinarySearchIf(sRoleDescrMap, 0, ArrayLength(sRoleDescrMap),
RoleDescrComparator(subrole), &idx)) {
return utils::LocalizedString(sRoleDescrMap[idx].description);
}
}
return NSAccessibilityRoleDescription([self role], subrole);

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

@ -125,6 +125,19 @@
obj = {
role: ROLE_MATHML_ROOT,
relations: {
RELATION_NODE_PARENT_OF: ["mroot_index", "mroot_base"]
},
children: [
{
role: ROLE_MATHML_IDENTIFIER,
relations: { RELATION_NODE_CHILD_OF: "mroot" }
},
{
role: ROLE_MATHML_NUMBER,
relations: { RELATION_NODE_CHILD_OF: "mroot" }
}
]
};
testElm("mroot", obj);
@ -386,8 +399,8 @@
<mn>2</mn>
</mfrac>
<mroot id="mroot">
<mi>x</mi>
<mn>5</mn>
<mi id="mroot_base">x</mi>
<mn id="mroot_index">5</mn>
</mroot>
<mspace width="1em"/>
<mfenced id="mfenced" close="[" open="]" separators=".">

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

@ -1,6 +1,7 @@
[DEFAULT]
[test_headers_ariagrid.html]
[test_headers_ariatable.html]
[test_headers_listbox.xul]
[test_headers_table.html]
[test_headers_tree.xul]

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

@ -0,0 +1,96 @@
<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<title>Table header information cells for ARIA table</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../table.js"></script>
<script type="application/javascript">
function doTest()
{
//////////////////////////////////////////////////////////////////////////
// column and row headers from markup
headerInfoMap = [
{
cell: "table_dc_1",
rowHeaderCells: [ "table_rh_1" ],
columnHeaderCells: [ "table_ch_2" ]
},
{
cell: "table_dc_2",
rowHeaderCells: [ "table_rh_1" ],
columnHeaderCells: [ "table_ch_3" ]
},
{
cell: "table_dc_3",
rowHeaderCells: [ "table_rh_2" ],
columnHeaderCells: [ "table_ch_2" ]
},
{
cell: "table_dc_4",
rowHeaderCells: [ "table_rh_2" ],
columnHeaderCells: [ "table_ch_3" ]
},
{
cell: "table_rh_1",
rowHeaderCells: [],
columnHeaderCells: [ "table_ch_1" ]
},
{
cell: "table_rh_2",
rowHeaderCells: [],
columnHeaderCells: [ "table_ch_1" ]
}
];
testHeaderCells(headerInfoMap);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
title="support ARIA table and cell roles"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173364">Bug 1173364</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<div role="table">
<div role="row">
<span id="table_ch_1" role="columnheader">col_1</span>
<span id="table_ch_2" role="columnheader">col_2</span>
<span id="table_ch_3" role="columnheader">col_3</span>
</div>
<div role="row">
<span id="table_rh_1" role="rowheader">row_1</span>
<span id="table_dc_1" role="cell">cell1</span>
<span id="table_dc_2" role="cell">cell2</span>
</div>
<div role="row">
<span id="table_rh_2" role="rowheader">row_2</span>
<span id="table_dc_3" role="cell">cell3</span>
<span id="table_dc_4" role="cell">cell4</span>
</div>
</div>
</body>
</html>

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

@ -11,6 +11,7 @@ skip-if = true # Bug 561508
[test_aria_list.html]
[test_aria_menu.html]
[test_aria_presentation.html]
[test_aria_table.html]
[test_brokencontext.html]
[test_button.xul]
[test_canvas.html]

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

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<title>ARIA table tests</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript">
function doTest()
{
//////////////////////////////////////////////////////////////////////////
// table having rowgroups
var accTree =
{ TABLE: [
{ GROUPING: [
{ ROW: [
{ CELL: [
{ TEXT_LEAF: [ ] }
] }
] }
] },
] };
testAccessibleTree("table", accTree);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
title="support ARIA table and cell roles"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173364">
Bug 1173364
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<div id="table" role="table">
<div role="rowgroup">
<div role="row">
<div role="cell">cell</div>
</div>
</div>
</div>
</body>
</html>

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

@ -3132,9 +3132,12 @@ var PrintPreviewListener = {
getPrintPreviewBrowser: function () {
if (!this._printPreviewTab) {
let browser = gBrowser.selectedTab.linkedBrowser;
let forceNotRemote = gMultiProcessBrowser && !browser.isRemoteBrowser;
this._tabBeforePrintPreview = gBrowser.selectedTab;
this._printPreviewTab = gBrowser.loadOneTab("about:blank",
{ inBackground: false });
{ inBackground: false,
forceNotRemote });
gBrowser.selectedTab = this._printPreviewTab;
}
return gBrowser.getBrowserForTab(this._printPreviewTab);

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

@ -21,28 +21,28 @@
namespace mozilla {
using dom::URLSearchParams;
using dom::URLParams;
void
OriginAttributes::CreateSuffix(nsACString& aStr) const
{
MOZ_RELEASE_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
nsRefPtr<URLSearchParams> usp = new URLSearchParams(nullptr);
UniquePtr<URLParams> params(new URLParams());
nsAutoString value;
if (mAppId != nsIScriptSecurityManager::NO_APP_ID) {
value.AppendInt(mAppId);
usp->Set(NS_LITERAL_STRING("appId"), value);
params->Set(NS_LITERAL_STRING("appId"), value);
}
if (mInBrowser) {
usp->Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
params->Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
}
aStr.Truncate();
usp->Serialize(value);
params->Serialize(value);
if (!value.IsEmpty()) {
aStr.AppendLiteral("!");
aStr.Append(NS_ConvertUTF16toUTF8(value));
@ -52,7 +52,7 @@ OriginAttributes::CreateSuffix(nsACString& aStr) const
namespace {
class MOZ_STACK_CLASS PopulateFromSuffixIterator final
: public URLSearchParams::ForEachIterator
: public URLParams::ForEachIterator
{
public:
explicit PopulateFromSuffixIterator(OriginAttributes* aOriginAttributes)
@ -61,8 +61,8 @@ public:
MOZ_ASSERT(aOriginAttributes);
}
bool URLSearchParamsIterator(const nsString& aName,
const nsString& aValue) override
bool URLParamsIterator(const nsString& aName,
const nsString& aValue) override
{
if (aName.EqualsLiteral("appId")) {
nsresult rv;
@ -108,11 +108,28 @@ OriginAttributes::PopulateFromSuffix(const nsACString& aStr)
return false;
}
nsRefPtr<URLSearchParams> usp = new URLSearchParams(nullptr);
usp->ParseInput(Substring(aStr, 1, aStr.Length() - 1));
UniquePtr<URLParams> params(new URLParams());
params->ParseInput(Substring(aStr, 1, aStr.Length() - 1));
PopulateFromSuffixIterator iterator(this);
return usp->ForEach(iterator);
return params->ForEach(iterator);
}
bool
OriginAttributes::PopulateFromOrigin(const nsACString& aOrigin,
nsACString& aOriginNoSuffix)
{
// RFindChar is only available on nsCString.
nsCString origin(aOrigin);
int32_t pos = origin.RFindChar('!');
if (pos == kNotFound) {
aOriginNoSuffix = origin;
return true;
}
aOriginNoSuffix = Substring(origin, 0, pos);
return PopulateFromSuffix(Substring(origin, pos));
}
void

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

@ -48,6 +48,11 @@ public:
bool PopulateFromSuffix(const nsACString& aStr);
void CookieJar(nsACString& aStr);
// Populates the attributes from a string like
// |uri!key1=value1&key2=value2| and returns the uri without the suffix.
bool PopulateFromOrigin(const nsACString& aOrigin,
nsACString& aOriginNoSuffix);
};
/*

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

@ -12,106 +12,134 @@
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams, mObserver)
NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
URLSearchParams::URLSearchParams(URLSearchParamsObserver* aObserver)
: mObserver(aObserver)
bool
URLParams::Has(const nsAString& aName)
{
}
for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
if (mParams[i].mKey.Equals(aName)) {
return true;
}
}
URLSearchParams::~URLSearchParams()
{
DeleteAll();
}
JSObject*
URLSearchParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return URLSearchParamsBinding::Wrap(aCx, this, aGivenProto);
}
/* static */ already_AddRefed<URLSearchParams>
URLSearchParams::Constructor(const GlobalObject& aGlobal,
const nsAString& aInit,
ErrorResult& aRv)
{
nsRefPtr<URLSearchParams> sp = new URLSearchParams(nullptr);
sp->ParseInput(NS_ConvertUTF16toUTF8(aInit));
return sp.forget();
}
/* static */ already_AddRefed<URLSearchParams>
URLSearchParams::Constructor(const GlobalObject& aGlobal,
URLSearchParams& aInit,
ErrorResult& aRv)
{
nsRefPtr<URLSearchParams> sp = new URLSearchParams(nullptr);
sp->mSearchParams = aInit.mSearchParams;
return sp.forget();
return false;
}
void
URLSearchParams::ParseInput(const nsACString& aInput)
URLParams::Get(const nsAString& aName, nsString& aRetval)
{
// Remove all the existing data before parsing a new input.
DeleteAll();
SetDOMStringToNull(aRetval);
nsACString::const_iterator start, end;
aInput.BeginReading(start);
aInput.EndReading(end);
nsACString::const_iterator iter(start);
while (start != end) {
nsAutoCString string;
if (FindCharInReadable('&', iter, end)) {
string.Assign(Substring(start, iter));
start = ++iter;
} else {
string.Assign(Substring(start, end));
start = end;
for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
if (mParams[i].mKey.Equals(aName)) {
aRetval.Assign(mParams[i].mValue);
break;
}
if (string.IsEmpty()) {
continue;
}
nsACString::const_iterator eqStart, eqEnd;
string.BeginReading(eqStart);
string.EndReading(eqEnd);
nsACString::const_iterator eqIter(eqStart);
nsAutoCString name;
nsAutoCString value;
if (FindCharInReadable('=', eqIter, eqEnd)) {
name.Assign(Substring(eqStart, eqIter));
++eqIter;
value.Assign(Substring(eqIter, eqEnd));
} else {
name.Assign(string);
}
nsAutoString decodedName;
DecodeString(name, decodedName);
nsAutoString decodedValue;
DecodeString(value, decodedValue);
AppendInternal(decodedName, decodedValue);
}
}
void
URLSearchParams::DecodeString(const nsACString& aInput, nsAString& aOutput)
URLParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
{
aRetval.Clear();
for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
if (mParams[i].mKey.Equals(aName)) {
aRetval.AppendElement(mParams[i].mValue);
}
}
}
void
URLParams::Append(const nsAString& aName, const nsAString& aValue)
{
Param* param = mParams.AppendElement();
param->mKey = aName;
param->mValue = aValue;
}
void
URLParams::Set(const nsAString& aName, const nsAString& aValue)
{
Param* param = nullptr;
for (uint32_t i = 0, len = mParams.Length(); i < len;) {
if (!mParams[i].mKey.Equals(aName)) {
++i;
continue;
}
if (!param) {
param = &mParams[i];
++i;
continue;
}
// Remove duplicates.
mParams.RemoveElementAt(i);
--len;
}
if (!param) {
param = mParams.AppendElement();
param->mKey = aName;
}
param->mValue = aValue;
}
bool
URLParams::Delete(const nsAString& aName)
{
bool found = false;
for (uint32_t i = 0; i < mParams.Length();) {
if (mParams[i].mKey.Equals(aName)) {
mParams.RemoveElementAt(i);
found = true;
} else {
++i;
}
}
return found;
}
void
URLParams::ConvertString(const nsACString& aInput, nsAString& aOutput)
{
aOutput.Truncate();
if (!mDecoder) {
mDecoder = EncodingUtils::DecoderForEncoding("UTF-8");
if (!mDecoder) {
MOZ_ASSERT(mDecoder, "Failed to create a decoder.");
return;
}
}
int32_t inputLength = aInput.Length();
int32_t outputLength = 0;
nsresult rv = mDecoder->GetMaxLength(aInput.BeginReading(), inputLength,
&outputLength);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (!aOutput.SetLength(outputLength, fallible)) {
return;
}
int32_t newOutputLength = outputLength;
rv = mDecoder->Convert(aInput.BeginReading(), &inputLength,
aOutput.BeginWriting(), &newOutputLength);
if (NS_FAILED(rv)) {
aOutput.Truncate();
return;
}
if (newOutputLength < outputLength) {
aOutput.Truncate(newOutputLength);
}
}
void
URLParams::DecodeString(const nsACString& aInput, nsAString& aOutput)
{
nsACString::const_iterator start, end;
aInput.BeginReading(start);
@ -168,147 +196,56 @@ URLSearchParams::DecodeString(const nsACString& aInput, nsAString& aOutput)
}
void
URLSearchParams::ConvertString(const nsACString& aInput, nsAString& aOutput)
URLParams::ParseInput(const nsACString& aInput)
{
aOutput.Truncate();
// Remove all the existing data before parsing a new input.
DeleteAll();
if (!mDecoder) {
mDecoder = EncodingUtils::DecoderForEncoding("UTF-8");
if (!mDecoder) {
MOZ_ASSERT(mDecoder, "Failed to create a decoder.");
return;
}
}
nsACString::const_iterator start, end;
aInput.BeginReading(start);
aInput.EndReading(end);
nsACString::const_iterator iter(start);
int32_t inputLength = aInput.Length();
int32_t outputLength = 0;
while (start != end) {
nsAutoCString string;
nsresult rv = mDecoder->GetMaxLength(aInput.BeginReading(), inputLength,
&outputLength);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (!aOutput.SetLength(outputLength, fallible)) {
return;
}
int32_t newOutputLength = outputLength;
rv = mDecoder->Convert(aInput.BeginReading(), &inputLength,
aOutput.BeginWriting(), &newOutputLength);
if (NS_FAILED(rv)) {
aOutput.Truncate();
return;
}
if (newOutputLength < outputLength) {
aOutput.Truncate(newOutputLength);
}
}
void
URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
{
SetDOMStringToNull(aRetval);
for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
if (mSearchParams[i].mKey.Equals(aName)) {
aRetval.Assign(mSearchParams[i].mValue);
break;
}
}
}
void
URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
{
aRetval.Clear();
for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
if (mSearchParams[i].mKey.Equals(aName)) {
aRetval.AppendElement(mSearchParams[i].mValue);
}
}
}
void
URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
{
Param* param = nullptr;
for (uint32_t i = 0, len = mSearchParams.Length(); i < len;) {
if (!mSearchParams[i].mKey.Equals(aName)) {
++i;
continue;
}
if (!param) {
param = &mSearchParams[i];
++i;
continue;
}
// Remove duplicates.
mSearchParams.RemoveElementAt(i);
--len;
}
if (!param) {
param = mSearchParams.AppendElement();
param->mKey = aName;
}
param->mValue = aValue;
NotifyObserver();
}
void
URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
{
AppendInternal(aName, aValue);
NotifyObserver();
}
void
URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
{
Param* param = mSearchParams.AppendElement();
param->mKey = aName;
param->mValue = aValue;
}
bool
URLSearchParams::Has(const nsAString& aName)
{
for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
if (mSearchParams[i].mKey.Equals(aName)) {
return true;
}
}
return false;
}
void
URLSearchParams::Delete(const nsAString& aName)
{
bool found = false;
for (uint32_t i = 0; i < mSearchParams.Length();) {
if (mSearchParams[i].mKey.Equals(aName)) {
mSearchParams.RemoveElementAt(i);
found = true;
if (FindCharInReadable('&', iter, end)) {
string.Assign(Substring(start, iter));
start = ++iter;
} else {
++i;
string.Assign(Substring(start, end));
start = end;
}
}
if (found) {
NotifyObserver();
}
}
if (string.IsEmpty()) {
continue;
}
void
URLSearchParams::DeleteAll()
{
mSearchParams.Clear();
nsACString::const_iterator eqStart, eqEnd;
string.BeginReading(eqStart);
string.EndReading(eqEnd);
nsACString::const_iterator eqIter(eqStart);
nsAutoCString name;
nsAutoCString value;
if (FindCharInReadable('=', eqIter, eqEnd)) {
name.Assign(Substring(eqStart, eqIter));
++eqIter;
value.Assign(Substring(eqIter, eqEnd));
} else {
name.Assign(string);
}
nsAutoString decodedName;
DecodeString(name, decodedName);
nsAutoString decodedValue;
DecodeString(value, decodedValue);
Append(decodedName, decodedValue);
}
}
namespace {
@ -338,24 +275,131 @@ void SerializeString(const nsCString& aInput, nsAString& aValue)
} // anonymous namespace
void
URLSearchParams::Serialize(nsAString& aValue) const
URLParams::Serialize(nsAString& aValue) const
{
aValue.Truncate();
bool first = true;
for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
if (first) {
first = false;
} else {
aValue.Append('&');
}
SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mKey), aValue);
SerializeString(NS_ConvertUTF16toUTF8(mParams[i].mKey), aValue);
aValue.Append('=');
SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mValue), aValue);
SerializeString(NS_ConvertUTF16toUTF8(mParams[i].mValue), aValue);
}
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams, mObserver)
NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
URLSearchParams::URLSearchParams(URLSearchParamsObserver* aObserver)
: mParams(new URLParams()), mObserver(aObserver)
{
}
URLSearchParams::URLSearchParams(const URLSearchParams& aOther)
: mParams(new URLParams(*aOther.mParams.get())), mObserver(aOther.mObserver)
{
}
URLSearchParams::~URLSearchParams()
{
DeleteAll();
}
JSObject*
URLSearchParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return URLSearchParamsBinding::Wrap(aCx, this, aGivenProto);
}
/* static */ already_AddRefed<URLSearchParams>
URLSearchParams::Constructor(const GlobalObject& aGlobal,
const nsAString& aInit,
ErrorResult& aRv)
{
nsRefPtr<URLSearchParams> sp = new URLSearchParams(nullptr);
sp->ParseInput(NS_ConvertUTF16toUTF8(aInit));
return sp.forget();
}
/* static */ already_AddRefed<URLSearchParams>
URLSearchParams::Constructor(const GlobalObject& aGlobal,
URLSearchParams& aInit,
ErrorResult& aRv)
{
nsRefPtr<URLSearchParams> sp = new URLSearchParams(aInit);
return sp.forget();
}
void
URLSearchParams::ParseInput(const nsACString& aInput)
{
mParams->ParseInput(aInput);
}
void
URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
{
return mParams->Get(aName, aRetval);
}
void
URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
{
return mParams->GetAll(aName, aRetval);
}
void
URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
{
mParams->Set(aName, aValue);
NotifyObserver();
}
void
URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
{
mParams->Append(aName, aValue);
NotifyObserver();
}
bool
URLSearchParams::Has(const nsAString& aName)
{
return mParams->Has(aName);
}
void
URLSearchParams::Delete(const nsAString& aName)
{
if (mParams->Delete(aName)) {
NotifyObserver();
}
}
void
URLSearchParams::DeleteAll()
{
mParams->DeleteAll();
}
void
URLSearchParams::Serialize(nsAString& aValue) const
{
mParams->Serialize(aValue);
}
void
URLSearchParams::NotifyObserver()
{

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

@ -31,6 +31,80 @@ public:
// attributes are kept in the correct order. If this changes, please, update
// BasePrincipal code.
class URLParams final
{
public:
URLParams() {}
~URLParams()
{
DeleteAll();
}
explicit URLParams(const URLParams& aOther)
: mParams(aOther.mParams)
{}
explicit URLParams(const URLParams&& aOther)
: mParams(Move(aOther.mParams))
{}
class ForEachIterator
{
public:
virtual bool
URLParamsIterator(const nsString& aName, const nsString& aValue) = 0;
};
void
ParseInput(const nsACString& aInput);
bool
ForEach(ForEachIterator& aIterator) const
{
for (uint32_t i = 0; i < mParams.Length(); ++i) {
if (!aIterator.URLParamsIterator(mParams[i].mKey, mParams[i].mValue)) {
return false;
}
}
return true;
}
void Serialize(nsAString& aValue) const;
void Get(const nsAString& aName, nsString& aRetval);
void GetAll(const nsAString& aName, nsTArray<nsString >& aRetval);
void Set(const nsAString& aName, const nsAString& aValue);
void Append(const nsAString& aName, const nsAString& aValue);
bool Has(const nsAString& aName);
// Returns true if aName was found and deleted, false otherwise.
bool Delete(const nsAString& aName);
void DeleteAll()
{
mParams.Clear();
}
private:
void DecodeString(const nsACString& aInput, nsAString& aOutput);
void ConvertString(const nsACString& aInput, nsAString& aOutput);
struct Param
{
nsString mKey;
nsString mValue;
};
nsTArray<Param> mParams;
nsCOMPtr<nsIUnicodeDecoder> mDecoder;
};
class URLSearchParams final : public nsISupports,
public nsWrapperCache
{
@ -42,6 +116,8 @@ public:
explicit URLSearchParams(URLSearchParamsObserver* aObserver);
explicit URLSearchParams(const URLSearchParams& aOther);
// WebIDL methods
nsISupports* GetParentObject() const
{
@ -65,7 +141,7 @@ public:
void Get(const nsAString& aName, nsString& aRetval);
void GetAll(const nsAString& aName, nsTArray<nsString >& aRetval);
void GetAll(const nsAString& aName, nsTArray<nsString>& aRetval);
void Set(const nsAString& aName, const nsAString& aValue);
@ -80,22 +156,12 @@ public:
Serialize(aRetval);
}
class ForEachIterator
{
public:
virtual bool
URLSearchParamsIterator(const nsString& aName, const nsString& aValue) = 0;
};
typedef URLParams::ForEachIterator ForEachIterator;
bool
ForEach(ForEachIterator& aIterator)
ForEach(ForEachIterator& aIterator) const
{
for (uint32_t i = 0; i < mSearchParams.Length(); ++i) {
if (!aIterator.URLSearchParamsIterator(mSearchParams[i].mKey,
mSearchParams[i].mValue)) {
return false;
}
}
return mParams->ForEach(aIterator);
return true;
}
@ -105,21 +171,10 @@ private:
void DeleteAll();
void DecodeString(const nsACString& aInput, nsAString& aOutput);
void ConvertString(const nsACString& aInput, nsAString& aOutput);
void NotifyObserver();
struct Param
{
nsString mKey;
nsString mValue;
};
nsTArray<Param> mSearchParams;
UniquePtr<URLParams> mParams;
nsRefPtr<URLSearchParamsObserver> mObserver;
nsCOMPtr<nsIUnicodeDecoder> mDecoder;
};
} // namespace dom

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

@ -446,6 +446,9 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
nsAutoString type;
nsAutoString media;
nsAutoString anchor;
nsAutoString crossOrigin;
crossOrigin.SetIsVoid(true);
// copy to work buffer
nsAutoString stringList(aLinkData);
@ -620,6 +623,12 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
anchor = value;
anchor.StripWhitespace();
}
} else if (attr.LowerCaseEqualsLiteral("crossorigin")) {
if (crossOrigin.IsVoid()) {
crossOrigin.SetIsVoid(false);
crossOrigin = value;
crossOrigin.StripWhitespace();
}
}
}
}
@ -633,7 +642,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
rv = ProcessLink(anchor, href, rel,
// prefer RFC 5987 variant over non-I18zed version
titleStar.IsEmpty() ? title : titleStar,
type, media);
type, media, crossOrigin);
}
href.Truncate();
@ -642,6 +651,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
type.Truncate();
media.Truncate();
anchor.Truncate();
crossOrigin.SetIsVoid(true);
seenParameters = false;
}
@ -654,7 +664,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
rv = ProcessLink(anchor, href, rel,
// prefer RFC 5987 variant over non-I18zed version
titleStar.IsEmpty() ? title : titleStar,
type, media);
type, media, crossOrigin);
}
return rv;
@ -664,7 +674,8 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
nsresult
nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref,
const nsSubstring& aRel, const nsSubstring& aTitle,
const nsSubstring& aType, const nsSubstring& aMedia)
const nsSubstring& aType, const nsSubstring& aMedia,
const nsSubstring& aCrossOrigin)
{
uint32_t linkTypes =
nsStyleLinkElement::ParseLinkTypes(aRel, mDocument->NodePrincipal());
@ -688,7 +699,7 @@ nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref,
}
if (!aHref.IsEmpty() && (linkTypes & nsStyleLinkElement::ePRECONNECT)) {
Preconnect(aHref);
Preconnect(aHref, aCrossOrigin);
}
// is it a stylesheet link?
@ -875,7 +886,7 @@ nsContentSink::PrefetchDNS(const nsAString &aHref)
}
void
nsContentSink::Preconnect(const nsAString &aHref)
nsContentSink::Preconnect(const nsAString& aHref, const nsAString& aCrossOrigin)
{
// construct URI using document charset
const nsACString& charset = mDocument->GetDocumentCharacterSet();
@ -885,7 +896,7 @@ nsContentSink::Preconnect(const nsAString &aHref)
mDocument->GetDocBaseURI());
if (uri && mDocument) {
mDocument->MaybePreconnect(uri);
mDocument->MaybePreconnect(uri, dom::Element::StringToCORSMode(aCrossOrigin));
}
}

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

@ -151,7 +151,7 @@ protected:
nsresult ProcessLink(const nsSubstring& aAnchor,
const nsSubstring& aHref, const nsSubstring& aRel,
const nsSubstring& aTitle, const nsSubstring& aType,
const nsSubstring& aMedia);
const nsSubstring& aMedia, const nsSubstring& aCrossOrigin);
virtual nsresult ProcessStyleLink(nsIContent* aElement,
const nsSubstring& aHref,
@ -225,7 +225,7 @@ public:
// For Preconnect() aHref can either be the usual
// URI format or of the form "//www.hostname.com" without a scheme.
void Preconnect(const nsAString &aHref);
void Preconnect(const nsAString& aHref, const nsAString& aCrossOrigin);
protected:
// Tries to scroll to the URI's named anchor. Once we've successfully

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

@ -7862,3 +7862,54 @@ nsContentUtils::InternalContentPolicyTypeToExternal(nsContentPolicyType aType)
return aType;
}
}
nsresult
nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
nsIDocument* aDoc,
nsIHttpChannel* aChannel)
{
NS_ENSURE_ARG_POINTER(aPrincipal);
NS_ENSURE_ARG_POINTER(aChannel);
nsCOMPtr<nsIURI> principalURI;
if (IsSystemPrincipal(aPrincipal)) {
return NS_OK;
}
aPrincipal->GetURI(getter_AddRefs(principalURI));
if (!aDoc) {
return aChannel->SetReferrerWithPolicy(principalURI, net::RP_Default);
}
// If it weren't for history.push/replaceState, we could just use the
// principal's URI here. But since we want changes to the URI effected
// by push/replaceState to be reflected in the XHR referrer, we have to
// be more clever.
//
// If the document's original URI (before any push/replaceStates) matches
// our principal, then we use the document's current URI (after
// push/replaceStates). Otherwise (if the document is, say, a data:
// URI), we just use the principal's URI.
nsCOMPtr<nsIURI> docCurURI = aDoc->GetDocumentURI();
nsCOMPtr<nsIURI> docOrigURI = aDoc->GetOriginalURI();
nsCOMPtr<nsIURI> referrerURI;
if (principalURI && docCurURI && docOrigURI) {
bool equal = false;
principalURI->Equals(docOrigURI, &equal);
if (equal) {
referrerURI = docCurURI;
}
}
if (!referrerURI) {
referrerURI = principalURI;
}
net::ReferrerPolicy referrerPolicy = aDoc->GetReferrerPolicy();
return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
}

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

@ -2406,6 +2406,25 @@ public:
static already_AddRefed<nsPIWindowRoot> GetWindowRoot(nsIDocument* aDoc);
/*
* Implements step 3.1 and 3.3 of the Determine request's Referrer algorithm
* from the Referrer Policy specification.
*
* The referrer policy of the document is applied by Necko when using
* channels.
*
* For documents representing an iframe srcdoc attribute, the document sets
* its own URI correctly, so this method simply uses the document's original
* or current URI as appropriate.
*
* aDoc may be null.
*
* https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer
*/
static nsresult SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
nsIDocument* aDoc,
nsIHttpChannel* aChannel);
private:
static bool InitializeEventTable();

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

@ -9763,8 +9763,26 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
}
void
nsDocument::MaybePreconnect(nsIURI* uri)
nsDocument::MaybePreconnect(nsIURI* aOrigURI, mozilla::CORSMode aCORSMode)
{
nsCOMPtr<nsIURI> uri;
if (NS_FAILED(aOrigURI->Clone(getter_AddRefs(uri)))) {
return;
}
// The URI created here is used in 2 contexts. One is nsISpeculativeConnect
// which ignores the path and uses only the origin. The other is for the
// document mPreloadedPreconnects de-duplication hash. Anonymous vs
// non-Anonymous preconnects create different connections on the wire and
// therefore should not be considred duplicates of each other and we
// normalize the path before putting it in the hash to accomplish that.
if (aCORSMode == CORS_ANONYMOUS) {
uri->SetPath(NS_LITERAL_CSTRING("/anonymous"));
} else {
uri->SetPath(NS_LITERAL_CSTRING("/"));
}
if (mPreloadedPreconnects.Contains(uri)) {
return;
}
@ -9776,7 +9794,11 @@ nsDocument::MaybePreconnect(nsIURI* uri)
return;
}
speculator->SpeculativeConnect(uri, nullptr);
if (aCORSMode == CORS_ANONYMOUS) {
speculator->SpeculativeAnonymousConnect(uri, nullptr);
} else {
speculator->SpeculativeConnect(uri, nullptr);
}
}
void

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

@ -1141,7 +1141,8 @@ public:
ReferrerPolicy aReferrerPolicy) override;
virtual void ForgetImagePreload(nsIURI* aURI) override;
virtual void MaybePreconnect(nsIURI* uri) override;
virtual void MaybePreconnect(nsIURI* uri,
mozilla::CORSMode aCORSMode) override;
virtual void PreloadStyle(nsIURI* uri, const nsAString& charset,
const nsAString& aCrossOriginAttr,

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

@ -31,6 +31,7 @@
#include "nsClassHashtable.h"
#include "prclist.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/CORSMode.h"
#include <bitset> // for member
class imgIRequest;
@ -2037,7 +2038,8 @@ public:
/**
* Called by Parser for link rel=preconnect
*/
virtual void MaybePreconnect(nsIURI* uri) = 0;
virtual void MaybePreconnect(nsIURI* uri,
mozilla::CORSMode aCORSMode) = 0;
enum DocumentTheme {
Doc_Theme_Uninitialized, // not determined yet

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

@ -2682,50 +2682,10 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
if (!IsSystemXHR()) {
// Get the referrer for the request.
//
// If it weren't for history.push/replaceState, we could just use the
// principal's URI here. But since we want changes to the URI effected
// by push/replaceState to be reflected in the XHR referrer, we have to
// be more clever.
//
// If the document's original URI (before any push/replaceStates) matches
// our principal, then we use the document's current URI (after
// push/replaceStates). Otherwise (if the document is, say, a data:
// URI), we just use the principal's URI.
nsCOMPtr<nsIURI> principalURI;
mPrincipal->GetURI(getter_AddRefs(principalURI));
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocument> doc =
nsContentUtils::GetDocumentFromScriptContext(sc);
nsCOMPtr<nsIURI> docCurURI;
nsCOMPtr<nsIURI> docOrigURI;
net::ReferrerPolicy referrerPolicy = net::RP_Default;
if (doc) {
docCurURI = doc->GetDocumentURI();
docOrigURI = doc->GetOriginalURI();
referrerPolicy = doc->GetReferrerPolicy();
}
nsCOMPtr<nsIURI> referrerURI;
if (principalURI && docCurURI && docOrigURI) {
bool equal = false;
principalURI->Equals(docOrigURI, &equal);
if (equal) {
referrerURI = docCurURI;
}
}
if (!referrerURI)
referrerURI = principalURI;
httpChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
httpChannel);
}
// Some extensions override the http protocol handler and provide their own

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

@ -13404,7 +13404,7 @@ class CGExampleClass(CGBindingImplClass):
ccImpl = dedent("""
// Only needed for refcounted objects.
NS_IMPL_CYCLE_COLLECTION_INHERITED_0(${nativeType}, ${parentType})
#error "If you don't have members that need cycle collection, then remove all the cycle collection bits from this implementation and the corresponding header. If you do, you want NS_IMPL_CYCLE_COLLECTION_INHERITED(${nativeType}, ${parentType}, your, members, here)"
NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})

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

@ -7,6 +7,7 @@ include protocol PCachePushStream;
include protocol PCacheStreamControl;
include InputStreamParams;
include ChannelInfo;
include PBackgroundSharedTypes;
using HeadersGuardEnum from "mozilla/dom/cache/IPCUtils.h";
using RequestCredentials from "mozilla/dom/cache/IPCUtils.h";
@ -81,6 +82,7 @@ struct CacheResponse
HeadersGuardEnum headersGuard;
CacheReadStreamOrVoid body;
IPCChannelInfo channelInfo;
OptionalPrincipalInfo principalInfo;
};
union CacheResponseOrVoid

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

@ -9,6 +9,7 @@
#include "mozilla/AutoRestore.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/cache/Action.h"
#include "mozilla/dom/cache/FileUtils.h"
#include "mozilla/dom/cache/Manager.h"
#include "mozilla/dom/cache/ManagerId.h"
#include "mozilla/dom/cache/OfflineStorage.h"
@ -155,6 +156,7 @@ public:
MOZ_ASSERT(mData);
MOZ_ASSERT(mTarget);
MOZ_ASSERT(mInitiatingThread);
MOZ_ASSERT(mInitAction);
}
nsresult Dispatch()
@ -402,11 +404,6 @@ Context::QuotaInitRunnable::Run()
break;
}
if (!mInitAction) {
resolver->Resolve(NS_OK);
break;
}
mState = STATE_RUN_ON_TARGET;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
@ -427,15 +424,20 @@ Context::QuotaInitRunnable::Run()
mData = nullptr;
// If the database was opened, then we should always succeed when creating
// the marker file. If it wasn't opened successfully, then no need to
// create a marker file anyway.
if (NS_SUCCEEDED(resolver->Result())) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(CreateMarkerFile(mQuotaInfo)));
}
break;
}
// -------------------
case STATE_COMPLETING:
{
NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
if (mInitAction) {
mInitAction->CompleteOnInitiatingThread(mResult);
}
mInitAction->CompleteOnInitiatingThread(mResult);
mContext->OnQuotaInit(mResult, mQuotaInfo, mOfflineStorage);
mState = STATE_COMPLETE;
@ -819,6 +821,7 @@ Context::Context(Manager* aManager, nsIThread* aTarget)
, mTarget(aTarget)
, mData(new Data(aTarget))
, mState(STATE_CONTEXT_PREINIT)
, mOrphanedData(false)
{
MOZ_ASSERT(mManager);
}
@ -927,8 +930,13 @@ Context::~Context()
mThreadsafeHandle->ContextDestroyed(this);
}
// Note, this may set the mOrphanedData flag.
mManager->RemoveContext(this);
if (mQuotaInfo.mDir && !mOrphanedData) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(DeleteMarkerFile(mQuotaInfo)));
}
if (mNextContext) {
mNextContext->Start();
}
@ -1050,6 +1058,14 @@ Context::RemoveActivity(Activity* aActivity)
MOZ_ASSERT(!mActivityList.Contains(aActivity));
}
void
Context::NoteOrphanedData()
{
NS_ASSERT_OWNINGTHREAD(Context);
// This may be called more than once
mOrphanedData = true;
}
already_AddRefed<Context::ThreadsafeHandle>
Context::CreateThreadsafeHandle()
{

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

@ -152,6 +152,11 @@ public:
return mQuotaInfo;
}
// Tell the Context that some state information has been orphaned in the
// data store and won't be cleaned up. The Context will leave the marker
// in place to trigger cleanup the next times its opened.
void NoteOrphanedData();
private:
class Data;
class QuotaInitRunnable;
@ -192,6 +197,7 @@ private:
nsCOMPtr<nsIThread> mTarget;
nsRefPtr<Data> mData;
State mState;
bool mOrphanedData;
QuotaInfo mQuotaInfo;
nsRefPtr<QuotaInitRunnable> mInitRunnable;
nsTArray<PendingAction> mPendingActions;

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

@ -19,6 +19,7 @@
#include "nsCRT.h"
#include "nsHttp.h"
#include "nsICryptoHash.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/dom/HeadersBinding.h"
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/ResponseBinding.h"
@ -282,6 +283,7 @@ CreateSchema(mozIStorageConnection* aConn)
"response_headers_guard INTEGER NOT NULL, "
"response_body_id TEXT NULL, "
"response_security_info_id INTEGER NULL REFERENCES security_info(id), "
"response_principal_info TEXT NOT NULL, "
"response_redirected INTEGER NOT NULL, "
// Note that response_redirected_url is either going to be empty, or
// it's going to be a URL different than response_url.
@ -535,6 +537,62 @@ IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId,
return rv;
}
nsresult
FindOrphanedCacheIds(mozIStorageConnection* aConn,
nsTArray<CacheId>& aOrphanedListOut)
{
nsCOMPtr<mozIStorageStatement> state;
nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT id FROM caches "
"WHERE id NOT IN (SELECT cache_id from storage);"
), getter_AddRefs(state));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
bool hasMoreData = false;
while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
CacheId cacheId = INVALID_CACHE_ID;
rv = state->GetInt64(0, &cacheId);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
aOrphanedListOut.AppendElement(cacheId);
}
return rv;
}
nsresult
GetKnownBodyIds(mozIStorageConnection* aConn, nsTArray<nsID>& aBodyIdListOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
nsCOMPtr<mozIStorageStatement> state;
nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT request_body_id, response_body_id FROM entries;"
), getter_AddRefs(state));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
bool hasMoreData = false;
while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
// extract 0 to 2 nsID structs per row
for (uint32_t i = 0; i < 2; ++i) {
bool isNull = false;
rv = state->GetIsNull(i, &isNull);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (!isNull) {
nsID id;
rv = ExtractId(state, i, &id);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
aBodyIdListOut.AppendElement(id);
}
}
}
return rv;
}
nsresult
CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequest& aRequest,
@ -1477,6 +1535,7 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
"response_headers_guard, "
"response_body_id, "
"response_security_info_id, "
"response_principal_info, "
"response_redirected, "
"response_redirected_url, "
"cache_id "
@ -1500,6 +1559,7 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
":response_headers_guard, "
":response_body_id, "
":response_security_info_id, "
":response_principal_info, "
":response_redirected, "
":response_redirected_url, "
":cache_id "
@ -1593,6 +1653,28 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
}
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsAutoCString serializedInfo;
// We only allow content serviceworkers right now.
if (aResponse.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
const mozilla::ipc::PrincipalInfo& principalInfo =
aResponse.principalInfo().get_PrincipalInfo();
MOZ_ASSERT(principalInfo.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
const mozilla::ipc::ContentPrincipalInfo& cInfo =
principalInfo.get_ContentPrincipalInfo();
serializedInfo.Append(cInfo.spec());
MOZ_ASSERT(cInfo.appId() != nsIScriptSecurityManager::UNKNOWN_APP_ID);
OriginAttributes attrs(cInfo.appId(), cInfo.isInBrowserElement());
nsAutoCString suffix;
attrs.CreateSuffix(suffix);
serializedInfo.Append(suffix);
}
rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_principal_info"),
serializedInfo);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_redirected"),
aResponse.channelInfo().redirected() ? 1 : 0);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@ -1692,6 +1774,7 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
"entries.response_status_text, "
"entries.response_headers_guard, "
"entries.response_body_id, "
"entries.response_principal_info, "
"entries.response_redirected, "
"entries.response_redirected_url, "
"security_info.data "
@ -1741,15 +1824,32 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
}
int32_t redirected;
rv = state->GetInt32(6, &redirected);
nsAutoCString serializedInfo;
rv = state->GetUTF8String(6, serializedInfo);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
aSavedResponseOut->mValue.principalInfo() = void_t();
if (!serializedInfo.IsEmpty()) {
nsAutoCString originNoSuffix;
OriginAttributes attrs;
fprintf(stderr, "\n%s\n", serializedInfo.get());
if (!attrs.PopulateFromOrigin(serializedInfo, originNoSuffix)) {
NS_WARNING("Something went wrong parsing a serialized principal!");
return NS_ERROR_FAILURE;
}
aSavedResponseOut->mValue.principalInfo() =
mozilla::ipc::ContentPrincipalInfo(attrs.mAppId, attrs.mInBrowser, originNoSuffix);
}
int32_t redirected;
rv = state->GetInt32(7, &redirected);
aSavedResponseOut->mValue.channelInfo().redirected() = !!redirected;
rv = state->GetUTF8String(7, aSavedResponseOut->mValue.channelInfo().redirectedURI());
rv = state->GetUTF8String(8, aSavedResponseOut->mValue.channelInfo().redirectedURI());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->GetBlobAsUTF8String(8, aSavedResponseOut->mValue.channelInfo().securityInfo());
rv = state->GetBlobAsUTF8String(9, aSavedResponseOut->mValue.channelInfo().securityInfo());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = aConn->CreateStatement(NS_LITERAL_CSTRING(

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

@ -48,6 +48,13 @@ nsresult
IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId,
bool* aOrphanedOut);
nsresult
FindOrphanedCacheIds(mozIStorageConnection* aConn,
nsTArray<CacheId>& aOrphanedListOut);
nsresult
GetKnownBodyIds(mozIStorageConnection* aConn, nsTArray<nsID>& aBodyIdListOut);
nsresult
CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequest& aRequest, const CacheQueryParams& aParams,

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

@ -319,6 +319,182 @@ BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
} // anonymous namespace
nsresult
BodyDeleteOrphanedFiles(nsIFile* aBaseDir, nsTArray<nsID>& aKnownBodyIdList)
{
MOZ_ASSERT(aBaseDir);
// body files are stored in a directory structure like:
//
// /morgue/01/{01fdddb2-884d-4c3d-95ba-0c8062f6c325}.final
// /morgue/02/{02fdddb2-884d-4c3d-95ba-0c8062f6c325}.tmp
nsCOMPtr<nsIFile> dir;
nsresult rv = aBaseDir->Clone(getter_AddRefs(dir));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// Add the root morgue directory
rv = dir->Append(NS_LITERAL_STRING("morgue"));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsISimpleEnumerator> entries;
rv = dir->GetDirectoryEntries(getter_AddRefs(entries));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// Iterate over all the intermediate morgue subdirs
bool hasMore = false;
while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> entry;
rv = entries->GetNext(getter_AddRefs(entry));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsIFile> subdir = do_QueryInterface(entry);
bool isDir = false;
rv = subdir->IsDirectory(&isDir);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// If a file got in here somehow, try to remove it and move on
if (NS_WARN_IF(!isDir)) {
rv = subdir->Remove(false /* recursive */);
MOZ_ASSERT(NS_SUCCEEDED(rv));
continue;
}
nsCOMPtr<nsISimpleEnumerator> subEntries;
rv = subdir->GetDirectoryEntries(getter_AddRefs(subEntries));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// Now iterate over all the files in the subdir
bool subHasMore = false;
while(NS_SUCCEEDED(rv = subEntries->HasMoreElements(&subHasMore)) &&
subHasMore) {
nsCOMPtr<nsISupports> subEntry;
rv = subEntries->GetNext(getter_AddRefs(subEntry));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsIFile> file = do_QueryInterface(subEntry);
nsAutoCString leafName;
rv = file->GetNativeLeafName(leafName);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// Delete all tmp files regardless of known bodies. These are
// all considered orphans.
if (StringEndsWith(leafName, NS_LITERAL_CSTRING(".tmp"))) {
// remove recursively in case its somehow a directory
rv = file->Remove(true /* recursive */);
MOZ_ASSERT(NS_SUCCEEDED(rv));
continue;
}
nsCString suffix(NS_LITERAL_CSTRING(".final"));
// Otherwise, it must be a .final file. If its not, then just
// skip it.
if (NS_WARN_IF(!StringEndsWith(leafName, suffix) ||
leafName.Length() != NSID_LENGTH - 1 + suffix.Length())) {
continue;
}
// Finally, parse the uuid out of the name. If its fails to parse,
// the ignore the file.
nsID id;
if (NS_WARN_IF(!id.Parse(leafName.BeginReading()))) {
continue;
}
if (!aKnownBodyIdList.Contains(id)) {
// remove recursively in case its somehow a directory
rv = file->Remove(true /* recursive */);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
}
return rv;
}
namespace {
nsresult
GetMarkerFileHandle(const QuotaInfo& aQuotaInfo, nsIFile** aFileOut)
{
MOZ_ASSERT(aFileOut);
nsCOMPtr<nsIFile> marker;
nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(marker));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = marker->Append(NS_LITERAL_STRING("cache"));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = marker->Append(NS_LITERAL_STRING("context_open.marker"));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
marker.forget(aFileOut);
return rv;
}
} // anonymous namespace
nsresult
CreateMarkerFile(const QuotaInfo& aQuotaInfo)
{
nsCOMPtr<nsIFile> marker;
nsresult rv = GetMarkerFileHandle(aQuotaInfo, getter_AddRefs(marker));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = marker->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
rv = NS_OK;
}
// Note, we don't need to fsync here. We only care about actually
// writing the marker if later modifications to the Cache are
// actually flushed to the disk. If the OS crashes before the marker
// is written then we are ensured no other changes to the Cache were
// flushed either.
return rv;
}
nsresult
DeleteMarkerFile(const QuotaInfo& aQuotaInfo)
{
nsCOMPtr<nsIFile> marker;
nsresult rv = GetMarkerFileHandle(aQuotaInfo, getter_AddRefs(marker));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = marker->Remove(/* recursive = */ false);
if (rv == NS_ERROR_FILE_NOT_FOUND ||
rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
rv = NS_OK;
}
// Again, no fsync is necessary. If the OS crashes before the file
// removal is flushed, then the Cache will search for stale data on
// startup. This will cause the next Cache access to be a bit slow, but
// it seems appropriate after an OS crash.
return NS_OK;
}
bool
MarkerFileExists(const QuotaInfo& aQuotaInfo)
{
nsCOMPtr<nsIFile> marker;
nsresult rv = GetMarkerFileHandle(aQuotaInfo, getter_AddRefs(marker));
if (NS_WARN_IF(NS_FAILED(rv))) { return false; }
bool exists = false;
rv = marker->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) { return false; }
return exists;
}
} // namespace cache
} // namespace dom
} // namespace mozilla

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

@ -50,6 +50,18 @@ BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId,
nsresult
BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList);
nsresult
BodyDeleteOrphanedFiles(nsIFile* aBaseDir, nsTArray<nsID>& aKnownBodyIdList);
nsresult
CreateMarkerFile(const QuotaInfo& aQuotaInfo);
nsresult
DeleteMarkerFile(const QuotaInfo& aQuotaInfo);
bool
MarkerFileExists(const QuotaInfo& aQuotaInfo);
} // namespace cache
} // namespace dom
} // namespace mozilla

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

@ -30,15 +30,12 @@
#include "nsThreadUtils.h"
#include "nsTObserverArray.h"
namespace {
using mozilla::unused;
using mozilla::dom::cache::Action;
using mozilla::dom::cache::BodyCreateDir;
using mozilla::dom::cache::BodyDeleteFiles;
using mozilla::dom::cache::QuotaInfo;
using mozilla::dom::cache::SyncDBAction;
using mozilla::dom::cache::db::CreateSchema;
namespace mozilla {
namespace dom {
namespace cache {
namespace {
// An Action that is executed when a Context is first created. It ensures that
// the directory and database are setup properly. This lets other actions
@ -54,23 +51,56 @@ public:
RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
mozIStorageConnection* aConn) override
{
// TODO: init maintainance marker (bug 1110446)
// TODO: perform maintainance if necessary (bug 1110446)
// TODO: find orphaned caches in database (bug 1110446)
// TODO: have Context create/delete marker files in constructor/destructor
// and only do expensive maintenance if that marker is present (bug 1110446)
nsresult rv = BodyCreateDir(aDBDir);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
mozStorageTransaction trans(aConn, false,
mozIStorageConnection::TRANSACTION_IMMEDIATE);
{
mozStorageTransaction trans(aConn, false,
mozIStorageConnection::TRANSACTION_IMMEDIATE);
rv = CreateSchema(aConn);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = db::CreateSchema(aConn);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = trans.Commit();
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = trans.Commit();
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
}
// If the Context marker file exists, then the last session was
// not cleanly shutdown. In these cases sqlite will ensure that
// the database is valid, but we might still orphan data. Both
// Cache objects and body files can be referenced by DOM objects
// after they are "removed" from their parent. So we need to
// look and see if any of these late access objects have been
// orphaned.
//
// Note, this must be done after any schema version updates to
// ensure our DBSchema methods work correctly.
if (MarkerFileExists(aQuotaInfo)) {
NS_WARNING("Cache not shutdown cleanly! Cleaning up stale data...");
mozStorageTransaction trans(aConn, false,
mozIStorageConnection::TRANSACTION_IMMEDIATE);
// Clean up orphaned Cache objects
nsAutoTArray<CacheId, 8> orphanedCacheIdList;
nsresult rv = db::FindOrphanedCacheIds(aConn, orphanedCacheIdList);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
for (uint32_t i = 0; i < orphanedCacheIdList.Length(); ++i) {
nsAutoTArray<nsID, 16> deletedBodyIdList;
rv = db::DeleteCacheId(aConn, orphanedCacheIdList[i], deletedBodyIdList);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = BodyDeleteFiles(aDBDir, deletedBodyIdList);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
}
// Clean up orphaned body objects
nsAutoTArray<nsID, 64> knownBodyIdList;
rv = db::GetKnownBodyIds(aConn, knownBodyIdList);
rv = BodyDeleteOrphanedFiles(aDBDir, knownBodyIdList);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
}
return rv;
}
@ -124,14 +154,6 @@ private:
nsTArray<nsID> mDeletedBodyIdList;
};
} // anonymous namespace
namespace mozilla {
namespace dom {
namespace cache {
namespace {
bool IsHeadRequest(CacheRequest aRequest, CacheQueryParams aParams)
{
return !aParams.ignoreMethod() && aRequest.method().LowerCaseEqualsLiteral("head");
@ -1326,8 +1348,9 @@ public:
// no outstanding references, delete immediately
nsRefPtr<Context> context = mManager->mContext;
// TODO: note that we need to check this cache for staleness on startup (bug 1110446)
if (!context->IsCanceled()) {
if (context->IsCanceled()) {
context->NoteOrphanedData();
} else {
context->CancelForCacheId(mCacheId);
nsRefPtr<Action> action =
new DeleteOrphanedCacheAction(mManager, mCacheId);
@ -1480,9 +1503,26 @@ Manager::RemoveContext(Context* aContext)
// Whether the Context destruction was triggered from the Manager going
// idle or the underlying storage being invalidated, we should know we
// are closing before the Conext is destroyed.
// are closing before the Context is destroyed.
MOZ_ASSERT(mState == Closing);
// Before forgetting the Context, check to see if we have any outstanding
// cache or body objects waiting for deletion. If so, note that we've
// orphaned data so it will be cleaned up on the next open.
for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
if (mCacheIdRefs[i].mOrphaned) {
aContext->NoteOrphanedData();
break;
}
}
for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
if (mBodyIdRefs[i].mOrphaned) {
aContext->NoteOrphanedData();
break;
}
}
mContext = nullptr;
// Once the context is gone, we can immediately remove ourself from the
@ -1534,13 +1574,18 @@ Manager::ReleaseCacheId(CacheId aCacheId)
if (mCacheIdRefs[i].mCount == 0) {
bool orphaned = mCacheIdRefs[i].mOrphaned;
mCacheIdRefs.RemoveElementAt(i);
// TODO: note that we need to check this cache for staleness on startup (bug 1110446)
nsRefPtr<Context> context = mContext;
if (orphaned && context && !context->IsCanceled()) {
context->CancelForCacheId(aCacheId);
nsRefPtr<Action> action = new DeleteOrphanedCacheAction(this,
aCacheId);
context->Dispatch(action);
// If the context is already gone, then orphan flag should have been
// set in RemoveContext().
if (orphaned && context) {
if (context->IsCanceled()) {
context->NoteOrphanedData();
} else {
context->CancelForCacheId(aCacheId);
nsRefPtr<Action> action = new DeleteOrphanedCacheAction(this,
aCacheId);
context->Dispatch(action);
}
}
}
MaybeAllowContextToClose();
@ -1578,11 +1623,16 @@ Manager::ReleaseBodyId(const nsID& aBodyId)
if (mBodyIdRefs[i].mCount < 1) {
bool orphaned = mBodyIdRefs[i].mOrphaned;
mBodyIdRefs.RemoveElementAt(i);
// TODO: note that we need to check this body for staleness on startup (bug 1110446)
nsRefPtr<Context> context = mContext;
if (orphaned && context && !context->IsCanceled()) {
nsRefPtr<Action> action = new DeleteOrphanedBodyAction(aBodyId);
context->Dispatch(action);
// If the context is already gone, then orphan flag should have been
// set in RemoveContext().
if (orphaned && context) {
if (context->IsCanceled()) {
context->NoteOrphanedData();
} else {
nsRefPtr<Action> action = new DeleteOrphanedBodyAction(aBodyId);
context->Dispatch(action);
}
}
}
MaybeAllowContextToClose();

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

@ -169,10 +169,11 @@ public:
continue;
}
// Ignore transient sqlite files
// Ignore transient sqlite files and marker files
if (leafName.EqualsLiteral("caches.sqlite-journal") ||
leafName.EqualsLiteral("caches.sqlite-shm") ||
leafName.Find(NS_LITERAL_CSTRING("caches.sqlite-mj"), false, 0, 0) == 0) {
leafName.Find(NS_LITERAL_CSTRING("caches.sqlite-mj"), false, 0, 0) == 0 ||
leafName.EqualsLiteral("context_open.marker")) {
continue;
}

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

@ -223,6 +223,11 @@ TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut,
ToHeadersEntryList(aOut.headers(), headers);
aOut.headersGuard() = headers->Guard();
aOut.channelInfo() = aIn.GetChannelInfo().AsIPCChannelInfo();
if (aIn.GetPrincipalInfo()) {
aOut.principalInfo() = *aIn.GetPrincipalInfo();
} else {
aOut.principalInfo() = void_t();
}
}
void
@ -289,6 +294,10 @@ TypeUtils::ToResponse(const CacheResponse& aIn)
MOZ_ASSERT(!result.Failed());
ir->InitChannelInfo(aIn.channelInfo());
if (aIn.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
UniquePtr<mozilla::ipc::PrincipalInfo> info(new mozilla::ipc::PrincipalInfo(aIn.principalInfo().get_PrincipalInfo()));
ir->SetPrincipalInfo(Move(info));
}
nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
ir->SetBody(stream);

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

@ -40,3 +40,5 @@ support-files =
skip-if = buildapp == 'b2g' # bug 1162353
[test_cache_restart.html]
[test_cache_shrink.html]
[test_cache_orphaned_cache.html]
[test_cache_orphaned_body.html]

178
dom/cache/test/mochitest/test_cache_orphaned_body.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,178 @@
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE HTML>
<html>
<head>
<title>Test Cache with QuotaManager Restart</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="large_url_list.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script class="testbody" type="text/javascript">
function setupTestIframe() {
return new Promise(function(resolve) {
var iframe = document.createElement("iframe");
iframe.src = "empty.html";
iframe.onload = function() {
window.caches = iframe.contentWindow.caches;
resolve();
};
document.body.appendChild(iframe);
});
}
function clearStorage() {
return new Promise(function(resolve, reject) {
var principal = SpecialPowers.wrap(document).nodePrincipal;
var appId, inBrowser;
var nsIPrincipal = SpecialPowers.Components.interfaces.nsIPrincipal;
if (principal.appId != nsIPrincipal.UNKNOWN_APP_ID &&
principal.appId != nsIPrincipal.NO_APP_ID) {
appId = principal.appId;
inBrowser = principal.isInBrowserElement;
}
SpecialPowers.clearStorageForURI(document.documentURI, resolve, appId,
inBrowser);
});
}
function storageUsage() {
return new Promise(function(resolve, reject) {
var principal = SpecialPowers.wrap(document).nodePrincipal;
var appId, inBrowser;
var nsIPrincipal = SpecialPowers.Components.interfaces.nsIPrincipal;
if (principal.appId != nsIPrincipal.UNKNOWN_APP_ID &&
principal.appId != nsIPrincipal.NO_APP_ID) {
appId = principal.appId;
inBrowser = principal.isInBrowserElement;
}
SpecialPowers.getStorageUsageForURI(document.documentURI, resolve, appId,
inBrowser);
});
}
function resetStorage() {
return new Promise(function(resolve, reject) {
var principal = SpecialPowers.wrap(document).nodePrincipal;
var appId, inBrowser;
var nsIPrincipal = SpecialPowers.Components.interfaces.nsIPrincipal;
if (principal.appId != nsIPrincipal.UNKNOWN_APP_ID &&
principal.appId != nsIPrincipal.NO_APP_ID) {
appId = principal.appId;
inBrowser = principal.isInBrowserElement;
}
SpecialPowers.resetStorageForURI(document.documentURI, resolve, appId,
inBrowser);
});
}
function gc() {
return new Promise(function(resolve, reject) {
SpecialPowers.exactGC(window, resolve);
});
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({
"set": [["dom.caches.enabled", true],
["dom.quotaManager.testing", true]],
}, function() {
var name = 'orphanedBodyOwner';
var cache = null;
var response = null;
var initialUsage = 0;
var fullUsage = 0;
var resetUsage = 0;
var endUsage = 0;
var url = 'cache_add.js';
// start from a fresh origin directory so other tests do not influence our
// results
setupTestIframe().then(function() {
return clearStorage();
}).then(function() {
return storageUsage();
}).then(function(usage) {
is(0, usage, 'disk usage should be zero to start');
})
// Initialize and populate an initial cache to get the base sqlite pages
// and directory structure allocated.
.then(function() {
return caches.open(name);
}).then(function(c) {
return c.add(url);
}).then(function() {
return gc();
}).then(function() {
return caches.delete(name);
}).then(function(deleted) {
ok(deleted, 'cache should be deleted');
})
// Now measure initial disk usage
.then(function() {
return resetStorage();
}).then(function() {
return storageUsage();
}).then(function(usage) {
initialUsage = usage;
})
// Now re-populate the Cache object
.then(function() {
return caches.open(name);
}).then(function(c) {
cache = c;
return cache.add(url);
})
// Get a reference to the body we've stored in the Cache.
.then(function() {
return cache.match(url);
}).then(function(r) {
response = r;
return cache.delete(url);
}).then(function(result) {
ok(result, "Cache entry should be deleted");
})
// Reset the quota dir while the cache entry is deleted, but still referenced
// from the DOM. This forces the body to be orphaned.
.then(function() {
return resetStorage();
}).then(function() {
return storageUsage();
}).then(function(usage) {
fullUsage = usage;
ok(fullUsage > initialUsage, 'disk usage should have grown');
})
// Now perform a new Cache operation that will reopen the origin. This
// should clean up the orphaned body.
.then(function() {
return caches.match(url);
}).then(function(r) {
ok(!r, 'response should not exist in storage');
})
// Finally, verify orphaned data was cleaned up by re-checking the disk
// usage. Reset the storage first to ensure any WAL transaction files
// are flushed before measuring the usage.
.then(function() {
return resetStorage();
}).then(function() {
return storageUsage();
}).then(function(usage) {
endUsage = usage;
dump("### ### initial:" + initialUsage + ", full:" + fullUsage +
", end:" + endUsage + "\n");
ok(endUsage < fullUsage, 'disk usage should have shrank');
is(endUsage, initialUsage, 'disk usage should return to original');
SimpleTest.finish();
});
});
</script>
</body>
</html>

171
dom/cache/test/mochitest/test_cache_orphaned_cache.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,171 @@
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE HTML>
<html>
<head>
<title>Test Cache with QuotaManager Restart</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="large_url_list.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script class="testbody" type="text/javascript">
function setupTestIframe() {
return new Promise(function(resolve) {
var iframe = document.createElement("iframe");
iframe.src = "empty.html";
iframe.onload = function() {
window.caches = iframe.contentWindow.caches;
resolve();
};
document.body.appendChild(iframe);
});
}
function clearStorage() {
return new Promise(function(resolve, reject) {
var principal = SpecialPowers.wrap(document).nodePrincipal;
var appId, inBrowser;
var nsIPrincipal = SpecialPowers.Components.interfaces.nsIPrincipal;
if (principal.appId != nsIPrincipal.UNKNOWN_APP_ID &&
principal.appId != nsIPrincipal.NO_APP_ID) {
appId = principal.appId;
inBrowser = principal.isInBrowserElement;
}
SpecialPowers.clearStorageForURI(document.documentURI, resolve, appId,
inBrowser);
});
}
function storageUsage() {
return new Promise(function(resolve, reject) {
var principal = SpecialPowers.wrap(document).nodePrincipal;
var appId, inBrowser;
var nsIPrincipal = SpecialPowers.Components.interfaces.nsIPrincipal;
if (principal.appId != nsIPrincipal.UNKNOWN_APP_ID &&
principal.appId != nsIPrincipal.NO_APP_ID) {
appId = principal.appId;
inBrowser = principal.isInBrowserElement;
}
SpecialPowers.getStorageUsageForURI(document.documentURI, resolve, appId,
inBrowser);
});
}
function resetStorage() {
return new Promise(function(resolve, reject) {
var principal = SpecialPowers.wrap(document).nodePrincipal;
var appId, inBrowser;
var nsIPrincipal = SpecialPowers.Components.interfaces.nsIPrincipal;
if (principal.appId != nsIPrincipal.UNKNOWN_APP_ID &&
principal.appId != nsIPrincipal.NO_APP_ID) {
appId = principal.appId;
inBrowser = principal.isInBrowserElement;
}
SpecialPowers.resetStorageForURI(document.documentURI, resolve, appId,
inBrowser);
});
}
function gc() {
return new Promise(function(resolve, reject) {
SpecialPowers.exactGC(window, resolve);
});
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({
"set": [["dom.caches.enabled", true],
["dom.quotaManager.testing", true]],
}, function() {
var name = 'toBeOrphaned';
var cache = null;
var initialUsage = 0;
var fullUsage = 0;
var resetUsage = 0;
var endUsage = 0;
var url = 'cache_add.js';
// start from a fresh origin directory so other tests do not influence our
// results
setupTestIframe().then(function() {
return clearStorage();
}).then(function() {
return storageUsage();
}).then(function(usage) {
is(0, usage, 'disk usage should be zero to start');
})
// Initialize and populate an initial cache to get the base sqlite pages
// and directory structure allocated.
.then(function() {
return caches.open(name);
}).then(function(c) {
return c.add(url);
}).then(function() {
return gc();
}).then(function() {
return caches.delete(name);
}).then(function(deleted) {
ok(deleted, 'cache should be deleted');
})
// Now measure initial disk usage
.then(function() {
return resetStorage();
}).then(function() {
return storageUsage();
}).then(function(usage) {
initialUsage = usage;
})
// Now re-populate the Cache object
.then(function() {
return caches.open(name);
}).then(function(c) {
cache = c;
return cache.add(url);
}).then(function() {
return caches.delete(name);
}).then(function(deleted) {
ok(deleted, 'cache should be deleted');
})
// Reset the quota dir while the cache is deleted, but still referenced
// from the DOM. This forces it to be orphaned.
.then(function() {
return resetStorage();
}).then(function() {
return storageUsage();
}).then(function(usage) {
fullUsage = usage;
ok(fullUsage > initialUsage, 'disk usage should have grown');
})
// Now perform a new Cache operation that will reopen the origin. This
// should clean up the orphaned Cache data.
.then(function() {
return caches.has(name);
}).then(function(result) {
ok(!result, 'cache should not exist in storage');
})
// Finally, verify orphaned data was cleaned up by re-checking the disk
// usage. Reset the storage first to ensure any WAL transaction files
// are flushed before measuring the usage.
.then(function() {
return resetStorage();
}).then(function() {
return storageUsage();
}).then(function(usage) {
endUsage = usage;
dump("### ### initial:" + initialUsage + ", full:" + fullUsage +
", end:" + endUsage + "\n");
ok(endUsage < fullUsage, 'disk usage should have shrank');
is(endUsage, initialUsage, 'disk usage should return to original');
SimpleTest.finish();
});
});
</script>
</body>
</html>

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

@ -64,7 +64,7 @@ Event::ConstructorInit(EventTarget* aOwner,
WidgetEvent* aEvent)
{
SetOwner(aOwner);
mIsMainThreadEvent = mOwner || NS_IsMainThread();
mIsMainThreadEvent = NS_IsMainThread();
if (mIsMainThreadEvent && !sReturnHighResTimeStampIsSet) {
Preferences::AddBoolVarCache(&sReturnHighResTimeStamp,

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

@ -217,11 +217,6 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
nsRefPtr<InternalRequest> r = request->GetInternalRequest();
aRv = UpdateRequestReferrer(aGlobal, r);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (NS_IsMainThread()) {
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
nsCOMPtr<nsIDocument> doc;
@ -398,58 +393,6 @@ WorkerFetchResolver::OnResponseEnd()
}
}
// This method sets the request's referrerURL, as specified by the "determine
// request's referrer" steps from Referrer Policy [1].
// The actual referrer policy and stripping is dealt with by HttpBaseChannel,
// this always sets the full API referrer URL of the relevant global if it is
// not already a url or no-referrer.
// [1]: https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer
nsresult
UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest)
{
nsAutoString originalReferrer;
aRequest->GetReferrer(originalReferrer);
// If it is no-referrer ("") or a URL, don't modify.
if (!originalReferrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
return NS_OK;
}
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
if (window) {
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
if (doc) {
nsAutoString referrer;
doc->GetReferrer(referrer);
aRequest->SetReferrer(referrer);
}
} else if (NS_IsMainThread()) {
// Pull the principal from the global for non-worker scripts.
nsIPrincipal *principal = aGlobal->PrincipalOrNull();
bool isNull;
// Only set the referrer if the principal is present,
// and the principal is not null or the system principal.
if (principal &&
NS_SUCCEEDED(principal->GetIsNullPrincipal(&isNull)) && !isNull &&
!nsContentUtils::IsSystemPrincipal(principal)) {
nsCOMPtr<nsIURI> uri;
if (NS_SUCCEEDED(principal->GetURI(getter_AddRefs(uri))) && uri) {
nsAutoCString referrer;
if (NS_SUCCEEDED(uri->GetSpec(referrer))) {
aRequest->SetReferrer(NS_ConvertUTF8toUTF16(referrer));
}
}
}
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
worker->AssertIsOnWorkerThread();
WorkerPrivate::LocationInfo& info = worker->GetLocationInfo();
aRequest->SetReferrer(NS_ConvertUTF8toUTF16(info.mHref));
}
return NS_OK;
}
namespace {
nsresult
ExtractFromArrayBuffer(const ArrayBuffer& aBuffer,
@ -558,8 +501,8 @@ public:
MOZ_ASSERT(aFormData);
}
bool URLSearchParamsIterator(const nsString& aName,
const nsString& aValue) override
bool URLParamsIterator(const nsString& aName,
const nsString& aValue) override
{
mFormData->Append(aName, aValue);
return true;

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

@ -416,21 +416,37 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
// Step 2. Set the referrer.
nsAutoString referrer;
mRequest->GetReferrer(referrer);
// The referrer should have already been resolved to a URL by the caller.
MOZ_ASSERT(!referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR));
if (!referrer.IsEmpty()) {
nsCOMPtr<nsIURI> refURI;
rv = NS_NewURI(getter_AddRefs(refURI), referrer, nullptr, nullptr);
if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
mDocument,
httpChan);
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}
} else if (referrer.IsEmpty()) {
rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}
} else {
// From "Determine request's Referrer" step 3
// "If request's referrer is a URL, let referrerSource be request's
// referrer."
//
// XXXnsm - We never actually hit this from a fetch() call since both
// fetch and Request() create a new internal request whose referrer is
// always set to about:client. Should we just crash here instead until
// someone tries to use FetchDriver for non-fetch() APIs?
nsCOMPtr<nsIURI> referrerURI;
rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}
net::ReferrerPolicy referrerPolicy = net::RP_Default;
if (mDocument) {
referrerPolicy = mDocument->GetReferrerPolicy();
}
rv = httpChan->SetReferrerWithPolicy(refURI, referrerPolicy);
rv =
httpChan->SetReferrerWithPolicy(referrerURI,
mDocument ? mDocument->GetReferrerPolicy() :
net::RP_Default);
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}

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

@ -7,6 +7,8 @@
#include "InternalResponse.h"
#include "mozilla/dom/InternalHeaders.h"
#include "mozilla/dom/cache/CacheTypes.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsStreamUtils.h"
namespace mozilla {
@ -20,6 +22,10 @@ InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusTe
{
}
InternalResponse::~InternalResponse()
{
}
already_AddRefed<InternalResponse>
InternalResponse::Clone()
{
@ -73,5 +79,41 @@ InternalResponse::CORSResponse()
return cors.forget();
}
void
InternalResponse::SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo)
{
mPrincipalInfo = Move(aPrincipalInfo);
}
already_AddRefed<InternalResponse>
InternalResponse::OpaqueResponse()
{
MOZ_ASSERT(!mWrappedResponse, "Can't OpaqueResponse a already wrapped response");
nsRefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
response->mType = ResponseType::Opaque;
response->mTerminationReason = mTerminationReason;
response->mURL = mURL;
response->mChannelInfo = mChannelInfo;
if (mPrincipalInfo) {
response->mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
}
response->mWrappedResponse = this;
return response.forget();
}
already_AddRefed<InternalResponse>
InternalResponse::CreateIncompleteCopy()
{
nsRefPtr<InternalResponse> copy = new InternalResponse(mStatus, mStatusText);
copy->mType = mType;
copy->mTerminationReason = mTerminationReason;
copy->mURL = mURL;
copy->mChannelInfo = mChannelInfo;
if (mPrincipalInfo) {
copy->mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
}
return copy.forget();
}
} // namespace dom
} // namespace mozilla

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

@ -12,8 +12,13 @@
#include "mozilla/dom/ResponseBinding.h"
#include "mozilla/dom/ChannelInfo.h"
#include "mozilla/UniquePtr.h"
namespace mozilla {
namespace ipc {
class PrincipalInfo;
}
namespace dom {
class InternalHeaders;
@ -41,17 +46,7 @@ public:
}
already_AddRefed<InternalResponse>
OpaqueResponse()
{
MOZ_ASSERT(!mWrappedResponse, "Can't OpaqueResponse a already wrapped response");
nsRefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
response->mType = ResponseType::Opaque;
response->mTerminationReason = mTerminationReason;
response->mURL = mURL;
response->mChannelInfo = mChannelInfo;
response->mWrappedResponse = this;
return response.forget();
}
OpaqueResponse();
already_AddRefed<InternalResponse>
BasicResponse();
@ -174,9 +169,18 @@ public:
return mChannelInfo;
}
const UniquePtr<mozilla::ipc::PrincipalInfo>&
GetPrincipalInfo() const
{
return mPrincipalInfo;
}
// Takes ownership of the principal info.
void
SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo);
private:
~InternalResponse()
{ }
~InternalResponse();
explicit InternalResponse(const InternalResponse& aOther) = delete;
InternalResponse& operator=(const InternalResponse&) = delete;
@ -184,15 +188,7 @@ private:
// Returns an instance of InternalResponse which is a copy of this
// InternalResponse, except headers, body and wrapped response (if any) which
// are left uninitialized. Used for cloning and filtering.
already_AddRefed<InternalResponse> CreateIncompleteCopy()
{
nsRefPtr<InternalResponse> copy = new InternalResponse(mStatus, mStatusText);
copy->mType = mType;
copy->mTerminationReason = mTerminationReason;
copy->mURL = mURL;
copy->mChannelInfo = mChannelInfo;
return copy.forget();
}
already_AddRefed<InternalResponse> CreateIncompleteCopy();
ResponseType mType;
nsCString mTerminationReason;
@ -202,6 +198,7 @@ private:
nsRefPtr<InternalHeaders> mHeaders;
nsCOMPtr<nsIInputStream> mBody;
ChannelInfo mChannelInfo;
UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
// For filtered responses.
// Cache, and SW interception should always serialize/access the underlying

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

@ -17,6 +17,10 @@
#include "InternalResponse.h"
namespace mozilla {
namespace ipc {
class PrincipalInfo;
}
namespace dom {
class Headers;
@ -90,6 +94,12 @@ public:
return mInternalResponse->GetChannelInfo();
}
const UniquePtr<mozilla::ipc::PrincipalInfo>&
GetPrincipalInfo() const
{
return mInternalResponse->GetPrincipalInfo();
}
Headers* Headers_();
void

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

@ -316,7 +316,7 @@ HTMLLinkElement::UpdatePreconnect()
if (owner) {
nsCOMPtr<nsIURI> uri = GetHrefURI();
if (uri) {
owner->MaybePreconnect(uri);
owner->MaybePreconnect(uri, GetCORSMode());
}
}
}

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

@ -2201,7 +2201,11 @@ HTMLMediaElement::Play(ErrorResult& aRv)
{
// Prevent media element from being auto-started by a script when
// media.autoplay.enabled=false
if (!IsAutoplayEnabled() && !EventStateManager::IsHandlingUserInput() && !nsContentUtils::IsCallerChrome()) {
nsRefPtr<TimeRanges> played(Played());
if (played->Length() == 0
&& !IsAutoplayEnabled()
&& !EventStateManager::IsHandlingUserInput()
&& !nsContentUtils::IsCallerChrome()) {
LOG(LogLevel::Debug, ("%p Blocked attempt to autoplay media.", this));
return;
}

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

@ -896,70 +896,104 @@ IDBDatabase::AbortTransactions(bool aShouldWarn)
class MOZ_STACK_CLASS Helper final
{
typedef nsAutoTArray<nsRefPtr<IDBTransaction>, 20> StrongTransactionArray;
typedef nsAutoTArray<IDBTransaction*, 20> WeakTransactionArray;
public:
static void
AbortTransactions(nsTHashtable<nsPtrHashKey<IDBTransaction>>& aTable,
nsTArray<nsRefPtr<IDBTransaction>>& aAbortedTransactions)
AbortTransactions(IDBDatabase* aDatabase, const bool aShouldWarn)
{
const uint32_t count = aTable.Count();
if (!count) {
MOZ_ASSERT(aDatabase);
aDatabase->AssertIsOnOwningThread();
nsTHashtable<nsPtrHashKey<IDBTransaction>>& transactionTable =
aDatabase->mTransactions;
if (!transactionTable.Count()) {
return;
}
nsAutoTArray<nsRefPtr<IDBTransaction>, 20> transactions;
transactions.SetCapacity(count);
StrongTransactionArray transactionsToAbort;
transactionsToAbort.SetCapacity(transactionTable.Count());
aTable.EnumerateEntries(Collect, &transactions);
transactionTable.EnumerateEntries(Collect, &transactionsToAbort);
MOZ_ASSERT(transactionsToAbort.Length() <= transactionTable.Count());
MOZ_ASSERT(transactions.Length() == count);
if (transactionsToAbort.IsEmpty()) {
return;
}
for (uint32_t index = 0; index < count; index++) {
nsRefPtr<IDBTransaction> transaction = Move(transactions[index]);
// We want to abort transactions as soon as possible so we iterate the
// transactions once and abort them all first, collecting the transactions
// that need to have a warning issued along the way. Those that need a
// warning will be a subset of those that are aborted, so we don't need
// additional strong references here.
WeakTransactionArray transactionsThatNeedWarning;
for (nsRefPtr<IDBTransaction>& transaction : transactionsToAbort) {
MOZ_ASSERT(transaction);
MOZ_ASSERT(!transaction->IsDone());
if (aShouldWarn) {
switch (transaction->GetMode()) {
// We ignore transactions that could not have written any data.
case IDBTransaction::READ_ONLY:
break;
// We warn for any transactions that could have written data.
case IDBTransaction::READ_WRITE:
case IDBTransaction::READ_WRITE_FLUSH:
case IDBTransaction::VERSION_CHANGE:
transactionsThatNeedWarning.AppendElement(transaction);
break;
default:
MOZ_CRASH("Unknown mode!");
}
}
transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
// We only care about warning for write transactions.
if (transaction->GetMode() != IDBTransaction::READ_ONLY) {
aAbortedTransactions.AppendElement(Move(transaction));
}
static const char kWarningMessage[] =
"IndexedDBTransactionAbortNavigation";
for (IDBTransaction* transaction : transactionsThatNeedWarning) {
MOZ_ASSERT(transaction);
nsString filename;
uint32_t lineNo;
transaction->GetCallerLocation(filename, &lineNo);
aDatabase->LogWarning(kWarningMessage, filename, lineNo);
}
}
private:
static PLDHashOperator
Collect(nsPtrHashKey<IDBTransaction>* aTransaction, void* aClosure)
Collect(nsPtrHashKey<IDBTransaction>* aTransactionKey, void* aClosure)
{
MOZ_ASSERT(aTransaction);
aTransaction->GetKey()->AssertIsOnOwningThread();
MOZ_ASSERT(aTransactionKey);
MOZ_ASSERT(aClosure);
auto* array = static_cast<nsTArray<nsRefPtr<IDBTransaction>>*>(aClosure);
array->AppendElement(aTransaction->GetKey());
IDBTransaction* transaction = aTransactionKey->GetKey();
MOZ_ASSERT(transaction);
transaction->AssertIsOnOwningThread();
// Transactions that are already done can simply be ignored. Otherwise
// there is a race here and it's possible that the transaction has not
// been successfully committed yet so we will warn the user.
if (!transaction->IsDone()) {
auto* array = static_cast<StrongTransactionArray*>(aClosure);
array->AppendElement(transaction);
}
return PL_DHASH_NEXT;
}
};
nsAutoTArray<nsRefPtr<IDBTransaction>, 5> abortedTransactions;
Helper::AbortTransactions(mTransactions, abortedTransactions);
if (aShouldWarn && !abortedTransactions.IsEmpty()) {
static const char kWarningMessage[] = "IndexedDBTransactionAbortNavigation";
for (uint32_t count = abortedTransactions.Length(), index = 0;
index < count;
index++) {
nsRefPtr<IDBTransaction>& transaction = abortedTransactions[index];
MOZ_ASSERT(transaction);
nsString filename;
uint32_t lineNo;
transaction->GetCallerLocation(filename, &lineNo);
LogWarning(kWarningMessage, filename, lineNo);
}
}
Helper::AbortTransactions(this, aShouldWarn);
}
PBackgroundIDBDatabaseFileChild*

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

@ -1437,7 +1437,7 @@ IDBObjectStore::Index(const nsAString& aName, ErrorResult &aRv)
{
AssertIsOnOwningThread();
if (mTransaction->IsFinished()) {
if (mTransaction->IsCommittingOrDone() || mDeletedSpec) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}

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

@ -448,7 +448,7 @@ IDBTransaction::SendCommit()
{
AssertIsOnOwningThread();
MOZ_ASSERT(NS_SUCCEEDED(mAbortCode));
MOZ_ASSERT(IsFinished());
MOZ_ASSERT(IsCommittingOrDone());
MOZ_ASSERT(!mSentCommitOrAbort);
MOZ_ASSERT(!mPendingRequestCount);
@ -481,7 +481,7 @@ IDBTransaction::SendAbort(nsresult aResultCode)
{
AssertIsOnOwningThread();
MOZ_ASSERT(NS_FAILED(aResultCode));
MOZ_ASSERT(IsFinished());
MOZ_ASSERT(IsCommittingOrDone());
MOZ_ASSERT(!mSentCommitOrAbort);
// Don't do this in the macro because we always need to increment the serial
@ -640,14 +640,10 @@ IDBTransaction::AbortInternal(nsresult aAbortCode,
{
AssertIsOnOwningThread();
MOZ_ASSERT(NS_FAILED(aAbortCode));
MOZ_ASSERT(!IsCommittingOrDone());
nsRefPtr<DOMError> error = aError;
if (IsFinished()) {
// Already finished, nothing to do here.
return;
}
const bool isVersionChange = mMode == VERSION_CHANGE;
const bool isInvalidated = mDatabase->IsInvalidated();
bool needToSendAbort = mReadyState == INITIAL && !isInvalidated;
@ -740,6 +736,12 @@ IDBTransaction::Abort(IDBRequest* aRequest)
AssertIsOnOwningThread();
MOZ_ASSERT(aRequest);
if (IsCommittingOrDone()) {
// Already started (and maybe finished) the commit or abort so there is
// nothing to do here.
return;
}
ErrorResult rv;
nsRefPtr<DOMError> error = aRequest->GetError(rv);
@ -751,6 +753,12 @@ IDBTransaction::Abort(nsresult aErrorCode)
{
AssertIsOnOwningThread();
if (IsCommittingOrDone()) {
// Already started (and maybe finished) the commit or abort so there is
// nothing to do here.
return;
}
nsRefPtr<DOMError> error = new DOMError(GetOwner(), aErrorCode);
AbortInternal(aErrorCode, error.forget());
}
@ -760,7 +768,7 @@ IDBTransaction::Abort(ErrorResult& aRv)
{
AssertIsOnOwningThread();
if (IsFinished()) {
if (IsCommittingOrDone()) {
aRv = NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
return;
}
@ -905,7 +913,7 @@ IDBTransaction::ObjectStore(const nsAString& aName, ErrorResult& aRv)
{
AssertIsOnOwningThread();
if (IsFinished()) {
if (IsCommittingOrDone()) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
@ -1029,11 +1037,12 @@ WorkerFeature::Notify(JSContext* aCx, Status aStatus)
if (mTransaction && aStatus > Terminating) {
mTransaction->AssertIsOnOwningThread();
nsRefPtr<IDBTransaction> transaction = mTransaction;
mTransaction = nullptr;
nsRefPtr<IDBTransaction> transaction = Move(mTransaction);
IDB_REPORT_INTERNAL_ERR();
transaction->AbortInternal(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, nullptr);
if (!transaction->IsCommittingOrDone()) {
IDB_REPORT_INTERNAL_ERR();
transaction->AbortInternal(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, nullptr);
}
}
return true;

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

@ -166,10 +166,19 @@ public:
IsOpen() const;
bool
IsFinished() const
IsCommittingOrDone() const
{
AssertIsOnOwningThread();
return mReadyState > LOADING;
return mReadyState == COMMITTING || mReadyState == DONE;
}
bool
IsDone() const
{
AssertIsOnOwningThread();
return mReadyState == DONE;
}
bool

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

@ -469,7 +469,7 @@ protected:
// IME
static TabParent *mIMETabParent;
ContentCache mContentCache;
ContentCacheInParent mContentCache;
nsIntRect mRect;
ScreenIntSize mDimensions;

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

@ -323,25 +323,30 @@ RTCPeerConnection.prototype = {
__init: function(rtcConfig) {
this._winID = this._win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
if (!rtcConfig.iceServers ||
!Services.prefs.getBoolPref("media.peerconnection.use_document_iceservers")) {
rtcConfig.iceServers =
JSON.parse(Services.prefs.getCharPref("media.peerconnection.default_iceservers"));
}
// Normalize iceServers input
rtcConfig.iceServers.forEach(server => {
if (typeof server.urls === "string") {
server.urls = [server.urls];
} else if (!server.urls && server.url) {
// TODO: Remove support for legacy iceServer.url eventually (Bug 1116766)
server.urls = [server.url];
this.logWarning("RTCIceServer.url is deprecated! Use urls instead.", null, 0);
try {
rtcConfig.iceServers =
JSON.parse(Services.prefs.getCharPref("media.peerconnection.default_iceservers") || "[]");
} catch (e) {
this.logWarning(
"Ignoring invalid media.peerconnection.default_iceservers in about:config",
null, 0);
rtcConfig.iceServers = [];
}
});
this._mustValidateRTCConfiguration(rtcConfig,
try {
this._mustValidateRTCConfiguration(rtcConfig,
"Ignoring invalid media.peerconnection.default_iceservers in about:config");
} catch (e) {
this.logWarning(e.message, null, 0);
rtcConfig.iceServers = [];
}
} else {
// This gets executed in the typical case when iceServers
// are passed in through the web page.
this._mustValidateRTCConfiguration(rtcConfig,
"RTCPeerConnection constructor passed invalid RTCConfiguration");
}
// Save the appId
this._appId = Cu.getWebIDLCallerPrincipal().appId;
@ -463,12 +468,23 @@ RTCPeerConnection.prototype = {
* { urls: ["turn:turn1.x.org", "turn:turn2.x.org"],
* username:"jib", credential:"mypass"} ] }
*
* WebIDL normalizes structure for us, so we test well-formed stun/turn urls,
* but not validity of servers themselves, before passing along to C++.
*
* This function normalizes the structure of the input for rtcConfig.iceServers for us,
* so we test well-formed stun/turn urls before passing along to C++.
* msg - Error message to detail which array-entry failed, if any.
*/
_mustValidateRTCConfiguration: function(rtcConfig, msg) {
// Normalize iceServers input
rtcConfig.iceServers.forEach(server => {
if (typeof server.urls === "string") {
server.urls = [server.urls];
} else if (!server.urls && server.url) {
// TODO: Remove support for legacy iceServer.url eventually (Bug 1116766)
server.urls = [server.url];
this.logWarning("RTCIceServer.url is deprecated! Use urls instead.", null, 0);
}
});
let ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
let nicerNewURI = uriStr => {

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

@ -72,7 +72,21 @@ runNetworkTest(() => {
"mozRTCPeerConnection() constructor has readable exceptions");
}
networkTestFinished();
// Below tests are setting the about:config User preferences for default
// ice servers and checking the outputs when mozRTCPeerConnection() is
// invoked. See Bug 1167922 for more information.
// Note - We use promises here since the SpecialPowers API will be
// performed asynchronously.
var push = prefs => new Promise(resolve =>
SpecialPowers.pushPrefEnv(prefs, resolve));
push({ set: [['media.peerconnection.default_iceservers', ""]] })
.then(() => makePC())
.then(() => push({ set: [['media.peerconnection.default_iceservers', "k"]] }))
.then(() => makePC())
.then(() => push({ set: [['media.peerconnection.default_iceservers', "[{\"urls\": [\"stun:stun.services.mozilla.com\"]}]"]] }))
.then(() => makePC())
.then(networkTestFinished);
});
</script>
</pre>

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

@ -35,16 +35,13 @@ var testsRemaining = tests.length;
tests.forEach(function(e) {
e.ac = new AudioContext();
var a = new Audio();
a.loop = true;
if (e.cors) {
a.crossOrigin = e.cors;
}
a.src = e.url;
a.controls = true;
var measn = e.ac.createMediaElementSource(a);
var sp = e.ac.createScriptProcessor(2048, 1);
// Set a couple expandos to track the status of the test
sp.iterationsLeft = 300;
sp.seenSound = false;
measn.connect(sp);
@ -52,9 +49,8 @@ tests.forEach(function(e) {
document.body.appendChild(a);
function checkFinished(sp) {
if (--sp.iterationsLeft == 0) {
if (a.ended) {
sp.onaudioprocess = null;
a.pause();
var not = e.expectSilence ? "" : "not";
is(e.expectSilence, !sp.seenSound,
"Buffer is " + not + " silent as expected, for " +
@ -82,9 +78,7 @@ tests.forEach(function(e) {
return silent;
}
a.onplaying = function () {
sp.onaudioprocess = checkBufferSilent;
}
sp.onaudioprocess = checkBufferSilent;
});
</script>
</pre>

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

@ -0,0 +1,472 @@
/* -*- 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 "WebMReader.h"
#ifdef MOZ_TREMOR
#include "tremor/ivorbiscodec.h"
#else
#include "vorbis/codec.h"
#endif
#include "OpusParser.h"
#include "VorbisUtils.h"
#include "OggReader.h"
#undef LOG
#ifdef PR_LOGGING
#include "prprf.h"
#define LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
#else
#define LOG(type, msg)
#endif
namespace mozilla {
extern PRLogModuleInfo* gMediaDecoderLog;
ogg_packet InitOggPacket(const unsigned char* aData, size_t aLength,
bool aBOS, bool aEOS,
int64_t aGranulepos, int64_t aPacketNo)
{
ogg_packet packet;
packet.packet = const_cast<unsigned char*>(aData);
packet.bytes = aLength;
packet.b_o_s = aBOS;
packet.e_o_s = aEOS;
packet.granulepos = aGranulepos;
packet.packetno = aPacketNo;
return packet;
}
class VorbisDecoder : public WebMAudioDecoder
{
public:
nsresult Init();
void Shutdown();
nsresult ResetDecode();
nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
nsresult FinishInit(AudioInfo& aInfo);
bool Decode(const unsigned char* aData, size_t aLength,
int64_t aOffset, uint64_t aTstampUsecs,
int64_t aDiscardPadding, int32_t* aTotalFrames);
explicit VorbisDecoder(WebMReader* aReader);
~VorbisDecoder();
private:
nsRefPtr<WebMReader> mReader;
// Vorbis decoder state
vorbis_info mVorbisInfo;
vorbis_comment mVorbisComment;
vorbis_dsp_state mVorbisDsp;
vorbis_block mVorbisBlock;
int64_t mPacketCount;
};
VorbisDecoder::VorbisDecoder(WebMReader* aReader)
: mReader(aReader)
, mPacketCount(0)
{
// Zero these member vars to avoid crashes in Vorbis clear functions when
// destructor is called before |Init|.
PodZero(&mVorbisBlock);
PodZero(&mVorbisDsp);
PodZero(&mVorbisInfo);
PodZero(&mVorbisComment);
}
VorbisDecoder::~VorbisDecoder()
{
vorbis_block_clear(&mVorbisBlock);
vorbis_dsp_clear(&mVorbisDsp);
vorbis_info_clear(&mVorbisInfo);
vorbis_comment_clear(&mVorbisComment);
}
void
VorbisDecoder::Shutdown()
{
mReader = nullptr;
}
nsresult
VorbisDecoder::Init()
{
vorbis_info_init(&mVorbisInfo);
vorbis_comment_init(&mVorbisComment);
PodZero(&mVorbisDsp);
PodZero(&mVorbisBlock);
return NS_OK;
}
nsresult
VorbisDecoder::ResetDecode()
{
// Ignore failed results from vorbis_synthesis_restart. They
// aren't fatal and it fails when ResetDecode is called at a
// time when no vorbis data has been read.
vorbis_synthesis_restart(&mVorbisDsp);
return NS_OK;
}
nsresult
VorbisDecoder::DecodeHeader(const unsigned char* aData, size_t aLength)
{
bool bos = mPacketCount == 0;
ogg_packet pkt = InitOggPacket(aData, aLength, bos, false, 0, mPacketCount++);
MOZ_ASSERT(mPacketCount <= 3);
int r = vorbis_synthesis_headerin(&mVorbisInfo,
&mVorbisComment,
&pkt);
return r == 0 ? NS_OK : NS_ERROR_FAILURE;
}
nsresult
VorbisDecoder::FinishInit(AudioInfo& aInfo)
{
MOZ_ASSERT(mPacketCount == 3);
int r = vorbis_synthesis_init(&mVorbisDsp, &mVorbisInfo);
if (r) {
return NS_ERROR_FAILURE;
}
r = vorbis_block_init(&mVorbisDsp, &mVorbisBlock);
if (r) {
return NS_ERROR_FAILURE;
}
aInfo.mRate = mVorbisDsp.vi->rate;
aInfo.mChannels = mVorbisDsp.vi->channels;
return NS_OK;
}
bool
VorbisDecoder::Decode(const unsigned char* aData, size_t aLength,
int64_t aOffset, uint64_t aTstampUsecs,
int64_t aDiscardPadding, int32_t* aTotalFrames)
{
MOZ_ASSERT(mPacketCount >= 3);
ogg_packet pkt = InitOggPacket(aData, aLength, false, false, -1, mPacketCount++);
bool first_packet = mPacketCount == 4;
if (vorbis_synthesis(&mVorbisBlock, &pkt)) {
return false;
}
if (vorbis_synthesis_blockin(&mVorbisDsp,
&mVorbisBlock)) {
return false;
}
VorbisPCMValue** pcm = 0;
int32_t frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
// If the first packet of audio in the media produces no data, we
// still need to produce an AudioData for it so that the correct media
// start time is calculated. Otherwise we'd end up with a media start
// time derived from the timecode of the first packet that produced
// data.
if (frames == 0 && first_packet) {
mReader->AudioQueue().Push(new AudioData(aOffset, aTstampUsecs, 0, 0, nullptr,
mVorbisDsp.vi->channels,
mVorbisDsp.vi->rate));
}
while (frames > 0) {
uint32_t channels = mVorbisDsp.vi->channels;
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames*channels]);
for (uint32_t j = 0; j < channels; ++j) {
VorbisPCMValue* channel = pcm[j];
for (uint32_t i = 0; i < uint32_t(frames); ++i) {
buffer[i*channels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]);
}
}
CheckedInt64 duration = FramesToUsecs(frames, mVorbisDsp.vi->rate);
if (!duration.isValid()) {
NS_WARNING("Int overflow converting WebM audio duration");
return false;
}
CheckedInt64 total_duration = FramesToUsecs(*aTotalFrames,
mVorbisDsp.vi->rate);
if (!total_duration.isValid()) {
NS_WARNING("Int overflow converting WebM audio total_duration");
return false;
}
CheckedInt64 time = total_duration + aTstampUsecs;
if (!time.isValid()) {
NS_WARNING("Int overflow adding total_duration and aTstampUsecs");
return false;
};
*aTotalFrames += frames;
mReader->AudioQueue().Push(new AudioData(aOffset,
time.value(),
duration.value(),
frames,
buffer.forget(),
mVorbisDsp.vi->channels,
mVorbisDsp.vi->rate));
if (vorbis_synthesis_read(&mVorbisDsp, frames)) {
return false;
}
frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
}
return true;
}
// ------------------------------------------------------------------------
class OpusDecoder : public WebMAudioDecoder
{
public:
nsresult Init();
void Shutdown();
nsresult ResetDecode();
nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
nsresult FinishInit(AudioInfo& aInfo);
bool Decode(const unsigned char* aData, size_t aLength,
int64_t aOffset, uint64_t aTstampUsecs,
int64_t aDiscardPadding, int32_t* aTotalFrames);
explicit OpusDecoder(WebMReader* aReader);
~OpusDecoder();
private:
nsRefPtr<WebMReader> mReader;
// Opus decoder state
nsAutoPtr<OpusParser> mOpusParser;
OpusMSDecoder* mOpusDecoder;
uint16_t mSkip; // Samples left to trim before playback.
bool mDecodedHeader;
// Opus padding should only be discarded on the final packet. Once this
// is set to true, if the reader attempts to decode any further packets it
// will raise an error so we can indicate that the file is invalid.
bool mPaddingDiscarded;
};
OpusDecoder::OpusDecoder(WebMReader* aReader)
: mReader(aReader)
, mOpusDecoder(nullptr)
, mSkip(0)
, mDecodedHeader(false)
, mPaddingDiscarded(false)
{
}
OpusDecoder::~OpusDecoder()
{
if (mOpusDecoder) {
opus_multistream_decoder_destroy(mOpusDecoder);
mOpusDecoder = nullptr;
}
}
void
OpusDecoder::Shutdown()
{
mReader = nullptr;
}
nsresult
OpusDecoder::Init()
{
return NS_OK;
}
nsresult
OpusDecoder::ResetDecode()
{
if (mOpusDecoder) {
// Reset the decoder.
opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE);
mSkip = mOpusParser->mPreSkip;
mPaddingDiscarded = false;
}
return NS_OK;
}
nsresult
OpusDecoder::DecodeHeader(const unsigned char* aData, size_t aLength)
{
MOZ_ASSERT(!mOpusParser);
MOZ_ASSERT(!mOpusDecoder);
MOZ_ASSERT(!mDecodedHeader);
mDecodedHeader = true;
mOpusParser = new OpusParser;
if (!mOpusParser->DecodeHeader(const_cast<unsigned char*>(aData), aLength)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
OpusDecoder::FinishInit(AudioInfo& aInfo)
{
MOZ_ASSERT(mDecodedHeader);
int r;
mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate,
mOpusParser->mChannels,
mOpusParser->mStreams,
mOpusParser->mCoupledStreams,
mOpusParser->mMappingTable,
&r);
mSkip = mOpusParser->mPreSkip;
mPaddingDiscarded = false;
if (int64_t(mReader->GetCodecDelay()) != FramesToUsecs(mOpusParser->mPreSkip,
mOpusParser->mRate).value()) {
LOG(LogLevel::Warning,
("Invalid Opus header: CodecDelay and pre-skip do not match!"));
return NS_ERROR_FAILURE;
}
aInfo.mRate = mOpusParser->mRate;
aInfo.mChannels = mOpusParser->mChannels;
return r == OPUS_OK ? NS_OK : NS_ERROR_FAILURE;
}
bool
OpusDecoder::Decode(const unsigned char* aData, size_t aLength,
int64_t aOffset, uint64_t aTstampUsecs,
int64_t aDiscardPadding, int32_t* aTotalFrames)
{
uint32_t channels = mOpusParser->mChannels;
// No channel mapping for more than 8 channels.
if (channels > 8) {
return false;
}
if (mPaddingDiscarded) {
// Discard padding should be used only on the final packet, so
// decoding after a padding discard is invalid.
LOG(LogLevel::Debug, ("Opus error, discard padding on interstitial packet"));
return false;
}
// Maximum value is 63*2880, so there's no chance of overflow.
int32_t frames_number = opus_packet_get_nb_frames(aData, aLength);
if (frames_number <= 0) {
return false; // Invalid packet header.
}
int32_t samples =
opus_packet_get_samples_per_frame(aData, opus_int32(mOpusParser->mRate));
// A valid Opus packet must be between 2.5 and 120 ms long (48kHz).
int32_t frames = frames_number*samples;
if (frames < 120 || frames > 5760)
return false;
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
// Decode to the appropriate sample type.
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
int ret = opus_multistream_decode_float(mOpusDecoder,
aData, aLength,
buffer, frames, false);
#else
int ret = opus_multistream_decode(mOpusDecoder,
aData, aLength,
buffer, frames, false);
#endif
if (ret < 0)
return false;
NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
CheckedInt64 startTime = aTstampUsecs;
// Trim the initial frames while the decoder is settling.
if (mSkip > 0) {
int32_t skipFrames = std::min<int32_t>(mSkip, frames);
int32_t keepFrames = frames - skipFrames;
LOG(LogLevel::Debug, ("Opus decoder skipping %d of %d frames",
skipFrames, frames));
PodMove(buffer.get(),
buffer.get() + skipFrames * channels,
keepFrames * channels);
startTime = startTime + FramesToUsecs(skipFrames, mOpusParser->mRate);
frames = keepFrames;
mSkip -= skipFrames;
}
if (aDiscardPadding < 0) {
// Negative discard padding is invalid.
LOG(LogLevel::Debug, ("Opus error, negative discard padding"));
return false;
}
if (aDiscardPadding > 0) {
CheckedInt64 discardFrames = UsecsToFrames(aDiscardPadding / NS_PER_USEC,
mOpusParser->mRate);
if (!discardFrames.isValid()) {
NS_WARNING("Int overflow in DiscardPadding");
return false;
}
if (discardFrames.value() > frames) {
// Discarding more than the entire packet is invalid.
LOG(LogLevel::Debug, ("Opus error, discard padding larger than packet"));
return false;
}
LOG(LogLevel::Debug, ("Opus decoder discarding %d of %d frames",
int32_t(discardFrames.value()), frames));
// Padding discard is only supposed to happen on the final packet.
// Record the discard so we can return an error if another packet is
// decoded.
mPaddingDiscarded = true;
int32_t keepFrames = frames - discardFrames.value();
frames = keepFrames;
}
// Apply the header gain if one was specified.
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
if (mOpusParser->mGain != 1.0f) {
float gain = mOpusParser->mGain;
int samples = frames * channels;
for (int i = 0; i < samples; i++) {
buffer[i] *= gain;
}
}
#else
if (mOpusParser->mGain_Q16 != 65536) {
int64_t gain_Q16 = mOpusParser->mGain_Q16;
int samples = frames * channels;
for (int i = 0; i < samples; i++) {
int32_t val = static_cast<int32_t>((gain_Q16*buffer[i] + 32768)>>16);
buffer[i] = static_cast<AudioDataValue>(MOZ_CLIP_TO_15(val));
}
}
#endif
CheckedInt64 duration = FramesToUsecs(frames, mOpusParser->mRate);
if (!duration.isValid()) {
NS_WARNING("Int overflow converting WebM audio duration");
return false;
}
CheckedInt64 time = startTime - mReader->GetCodecDelay();
if (!time.isValid()) {
NS_WARNING("Int overflow shifting tstamp by codec delay");
return false;
};
mReader->AudioQueue().Push(new AudioData(aOffset,
time.value(),
duration.value(),
frames,
buffer.forget(),
mOpusParser->mChannels,
mOpusParser->mRate));
return true;
}
} // namespace mozilla

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

@ -200,7 +200,7 @@ IntelWebMVideoDecoder::Demux(nsRefPtr<VP8Sample>& aSample, bool* aEOS)
}
vpx_codec_stream_info_t si;
memset(&si, 0, sizeof(si));
PodZero(&si);
si.sz = sizeof(si);
if (mReader->GetVideoCodec() == NESTEGG_CODEC_VP8) {
vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), data, length, &si);

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

@ -30,7 +30,7 @@ SoftwareWebMVideoDecoder::SoftwareWebMVideoDecoder(WebMReader* aReader)
mReader(aReader)
{
MOZ_COUNT_CTOR(SoftwareWebMVideoDecoder);
memset(&mVPX, 0, sizeof(vpx_codec_ctx_t));
PodZero(&mVPX);
}
SoftwareWebMVideoDecoder::~SoftwareWebMVideoDecoder()
@ -128,7 +128,7 @@ SoftwareWebMVideoDecoder::DecodeVideoFrame(bool &aKeyframeSkip,
}
vpx_codec_stream_info_t si;
memset(&si, 0, sizeof(si));
PodZero(&si);
si.sz = sizeof(si);
if (mReader->GetVideoCodec() == NESTEGG_CODEC_VP8) {
vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), data, length, &si);

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

@ -10,7 +10,6 @@
#include "SoftwareWebMVideoDecoder.h"
#include "WebMReader.h"
#include "WebMBufferedParser.h"
#include "VorbisUtils.h"
#include "gfx2DGlue.h"
#include "Layers.h"
#include "mozilla/Preferences.h"
@ -22,8 +21,6 @@
#include "vpx/vp8dx.h"
#include "vpx/vpx_decoder.h"
#include "OggReader.h"
// IntelWebMVideoDecoder uses the WMF backend, which is Windows Vista+ only.
#if defined(MOZ_PDM_VPX)
#include "IntelWebMVideoDecoder.h"
@ -143,20 +140,6 @@ static void webm_log(nestegg * context,
va_end(args);
}
ogg_packet InitOggPacket(const unsigned char* aData, size_t aLength,
bool aBOS, bool aEOS,
int64_t aGranulepos, int64_t aPacketNo)
{
ogg_packet packet;
packet.packet = const_cast<unsigned char*>(aData);
packet.bytes = aLength;
packet.b_o_s = aBOS;
packet.e_o_s = aEOS;
packet.granulepos = aGranulepos;
packet.packetno = aPacketNo;
return packet;
}
#if defined(MOZ_PDM_VPX)
static bool sIsIntelDecoderEnabled = false;
#endif
@ -164,32 +147,22 @@ static bool sIsIntelDecoderEnabled = false;
WebMReader::WebMReader(AbstractMediaDecoder* aDecoder, MediaTaskQueue* aBorrowedTaskQueue)
: MediaDecoderReader(aDecoder, aBorrowedTaskQueue)
, mContext(nullptr)
, mPacketCount(0)
, mOpusDecoder(nullptr)
, mSkip(0)
, mSeekPreroll(0)
, mVideoTrack(0)
, mAudioTrack(0)
, mAudioStartUsec(-1)
, mAudioFrames(0)
, mSeekPreroll(0)
, mLastVideoFrameTime(0)
, mAudioCodec(-1)
, mVideoCodec(-1)
, mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
, mHasVideo(false)
, mHasAudio(false)
, mPaddingDiscarded(false)
{
MOZ_COUNT_CTOR(WebMReader);
if (!gNesteggLog) {
gNesteggLog = PR_NewLogModule("Nestegg");
}
// Zero these member vars to avoid crashes in VP8 destroy and Vorbis clear
// functions when destructor is called before |Init|.
memset(&mVorbisBlock, 0, sizeof(vorbis_block));
memset(&mVorbisDsp, 0, sizeof(vorbis_dsp_state));
memset(&mVorbisInfo, 0, sizeof(vorbis_info));
memset(&mVorbisComment, 0, sizeof(vorbis_comment));
#if defined(MOZ_PDM_VPX)
sIsIntelDecoderEnabled = Preferences::GetBool("media.webm.intel_decoder.enabled", false);
@ -201,14 +174,7 @@ WebMReader::~WebMReader()
Cleanup();
mVideoPackets.Reset();
mAudioPackets.Reset();
vorbis_block_clear(&mVorbisBlock);
vorbis_dsp_clear(&mVorbisDsp);
vorbis_info_clear(&mVorbisInfo);
vorbis_comment_clear(&mVorbisComment);
if (mOpusDecoder) {
opus_multistream_decoder_destroy(mOpusDecoder);
mOpusDecoder = nullptr;
}
MOZ_ASSERT(!mAudioDecoder);
MOZ_ASSERT(!mVideoDecoder);
MOZ_COUNT_DTOR(WebMReader);
}
@ -222,6 +188,10 @@ WebMReader::Shutdown()
mVideoTaskQueue->AwaitShutdownAndIdle();
}
#endif
if (mAudioDecoder) {
mAudioDecoder->Shutdown();
mAudioDecoder = nullptr;
}
if (mVideoDecoder) {
mVideoDecoder->Shutdown();
@ -233,11 +203,6 @@ WebMReader::Shutdown()
nsresult WebMReader::Init(MediaDecoderReader* aCloneDonor)
{
vorbis_info_init(&mVorbisInfo);
vorbis_comment_init(&mVorbisComment);
memset(&mVorbisDsp, 0, sizeof(vorbis_dsp_state));
memset(&mVorbisBlock, 0, sizeof(vorbis_block));
#if defined(MOZ_PDM_VPX)
if (sIsIntelDecoderEnabled) {
PlatformDecoderModule::Init();
@ -293,18 +258,8 @@ nsresult WebMReader::ResetDecode()
res = NS_ERROR_FAILURE;
}
if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
// Ignore failed results from vorbis_synthesis_restart. They
// aren't fatal and it fails when ResetDecode is called at a
// time when no vorbis data has been read.
vorbis_synthesis_restart(&mVorbisDsp);
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
if (mOpusDecoder) {
// Reset the decoder.
opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE);
mSkip = mOpusParser->mPreSkip;
mPaddingDiscarded = false;
}
if (mAudioDecoder) {
mAudioDecoder->ResetDecode();
}
mVideoPackets.Reset();
@ -461,84 +416,42 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
mHasAudio = true;
mAudioCodec = nestegg_track_codec_id(mContext, track);
mCodecDelay = params.codec_delay / NS_PER_USEC;
mSeekPreroll = params.seek_preroll;
if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
// Get the Vorbis header data
unsigned int nheaders = 0;
r = nestegg_track_codec_data_count(mContext, track, &nheaders);
if (r == -1 || nheaders != 3) {
Cleanup();
return NS_ERROR_FAILURE;
}
for (uint32_t header = 0; header < nheaders; ++header) {
unsigned char* data = 0;
size_t length = 0;
r = nestegg_track_codec_data(mContext, track, header, &data, &length);
if (r == -1) {
Cleanup();
return NS_ERROR_FAILURE;
}
ogg_packet opacket = InitOggPacket(data, length, header == 0, false,
0, mPacketCount++);
r = vorbis_synthesis_headerin(&mVorbisInfo,
&mVorbisComment,
&opacket);
if (r != 0) {
Cleanup();
return NS_ERROR_FAILURE;
}
}
r = vorbis_synthesis_init(&mVorbisDsp, &mVorbisInfo);
if (r != 0) {
Cleanup();
return NS_ERROR_FAILURE;
}
r = vorbis_block_init(&mVorbisDsp, &mVorbisBlock);
if (r != 0) {
Cleanup();
return NS_ERROR_FAILURE;
}
mInfo.mAudio.mRate = mVorbisDsp.vi->rate;
mInfo.mAudio.mChannels = mVorbisDsp.vi->channels;
mAudioDecoder = new VorbisDecoder(this);
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
mAudioDecoder = new OpusDecoder(this);
} else {
Cleanup();
return NS_ERROR_FAILURE;
}
if (NS_FAILED(mAudioDecoder->Init())) {
Cleanup();
return NS_ERROR_FAILURE;
}
unsigned int nheaders = 0;
r = nestegg_track_codec_data_count(mContext, track, &nheaders);
if (r == -1) {
Cleanup();
return NS_ERROR_FAILURE;
}
for (uint32_t header = 0; header < nheaders; ++header) {
unsigned char* data = 0;
size_t length = 0;
r = nestegg_track_codec_data(mContext, track, 0, &data, &length);
r = nestegg_track_codec_data(mContext, track, header, &data, &length);
if (r == -1) {
Cleanup();
return NS_ERROR_FAILURE;
}
mOpusParser = new OpusParser;
if (!mOpusParser->DecodeHeader(data, length)) {
if (NS_FAILED(mAudioDecoder->DecodeHeader(data, length))) {
Cleanup();
return NS_ERROR_FAILURE;
}
if (!InitOpusDecoder()) {
Cleanup();
return NS_ERROR_FAILURE;
}
if (int64_t(mCodecDelay) != FramesToUsecs(mOpusParser->mPreSkip,
mOpusParser->mRate).value()) {
LOG(LogLevel::Warning,
("Invalid Opus header: CodecDelay and pre-skip do not match!"));
Cleanup();
return NS_ERROR_FAILURE;
}
mInfo.mAudio.mRate = mOpusParser->mRate;
mInfo.mAudio.mChannels = mOpusParser->mChannels;
mSeekPreroll = params.seek_preroll;
} else {
}
if (NS_FAILED(mAudioDecoder->FinishInit(mInfo.mAudio))) {
Cleanup();
return NS_ERROR_FAILURE;
}
@ -558,24 +471,6 @@ WebMReader::IsMediaSeekable()
return mContext && nestegg_has_cues(mContext);
}
bool WebMReader::InitOpusDecoder()
{
int r;
NS_ASSERTION(mOpusDecoder == nullptr, "leaking OpusDecoder");
mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate,
mOpusParser->mChannels,
mOpusParser->mStreams,
mOpusParser->mCoupledStreams,
mOpusParser->mMappingTable,
&r);
mSkip = mOpusParser->mPreSkip;
mPaddingDiscarded = false;
return r == OPUS_OK;
}
bool WebMReader::DecodeAudioPacket(NesteggPacketHolder* aHolder)
{
MOZ_ASSERT(OnTaskQueue());
@ -617,7 +512,6 @@ bool WebMReader::DecodeAudioPacket(NesteggPacketHolder* aHolder)
usecs.isValid() ? usecs.value() : -1,
gap_frames));
#endif
mPacketCount++;
mAudioStartUsec = tstamp;
mAudioFrames = 0;
}
@ -630,227 +524,16 @@ bool WebMReader::DecodeAudioPacket(NesteggPacketHolder* aHolder)
if (r == -1) {
return false;
}
if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
if (!DecodeVorbis(data, length, aHolder->Offset(), tstamp, &total_frames)) {
return false;
}
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
if (!DecodeOpus(data, length, aHolder->Offset(), tstamp, aHolder->Packet())) {
return false;
}
}
}
int64_t discardPadding = 0;
(void) nestegg_packet_discard_padding(aHolder->Packet(), &discardPadding);
return true;
}
bool WebMReader::DecodeVorbis(const unsigned char* aData, size_t aLength,
int64_t aOffset, uint64_t aTstampUsecs,
int32_t* aTotalFrames)
{
ogg_packet opacket = InitOggPacket(aData, aLength, false, false, -1,
mPacketCount++);
if (vorbis_synthesis(&mVorbisBlock, &opacket) != 0) {
return false;
}
if (vorbis_synthesis_blockin(&mVorbisDsp,
&mVorbisBlock) != 0) {
return false;
}
VorbisPCMValue** pcm = 0;
int32_t frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
// If the first packet of audio in the media produces no data, we
// still need to produce an AudioData for it so that the correct media
// start time is calculated. Otherwise we'd end up with a media start
// time derived from the timecode of the first packet that produced
// data.
if (frames == 0 && mAudioFrames == 0) {
AudioQueue().Push(new AudioData(aOffset, aTstampUsecs, 0, 0, nullptr,
mInfo.mAudio.mChannels,
mInfo.mAudio.mRate));
}
while (frames > 0) {
uint32_t channels = mInfo.mAudio.mChannels;
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames*channels]);
for (uint32_t j = 0; j < channels; ++j) {
VorbisPCMValue* channel = pcm[j];
for (uint32_t i = 0; i < uint32_t(frames); ++i) {
buffer[i*channels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]);
}
}
CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
if (!duration.isValid()) {
NS_WARNING("Int overflow converting WebM audio duration");
return false;
}
CheckedInt64 total_duration = FramesToUsecs(*aTotalFrames,
mInfo.mAudio.mRate);
if (!total_duration.isValid()) {
NS_WARNING("Int overflow converting WebM audio total_duration");
return false;
}
CheckedInt64 time = total_duration + aTstampUsecs;
if (!time.isValid()) {
NS_WARNING("Int overflow adding total_duration and aTstampUsecs");
return false;
};
*aTotalFrames += frames;
AudioQueue().Push(new AudioData(aOffset,
time.value(),
duration.value(),
frames,
buffer.forget(),
mInfo.mAudio.mChannels,
mInfo.mAudio.mRate));
mAudioFrames += frames;
if (vorbis_synthesis_read(&mVorbisDsp, frames) != 0) {
return false;
}
frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
}
return true;
}
bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength,
int64_t aOffset, uint64_t aTstampUsecs,
nestegg_packet* aPacket)
{
uint32_t channels = mOpusParser->mChannels;
// No channel mapping for more than 8 channels.
if (channels > 8) {
return false;
}
if (mPaddingDiscarded) {
// Discard padding should be used only on the final packet, so
// decoding after a padding discard is invalid.
LOG(LogLevel::Debug, ("Opus error, discard padding on interstitial packet"));
mHitAudioDecodeError = true;
return false;
}
// Maximum value is 63*2880, so there's no chance of overflow.
int32_t frames_number = opus_packet_get_nb_frames(aData, aLength);
if (frames_number <= 0) {
return false; // Invalid packet header.
}
int32_t samples =
opus_packet_get_samples_per_frame(aData, opus_int32(mInfo.mAudio.mRate));
// A valid Opus packet must be between 2.5 and 120 ms long (48kHz).
int32_t frames = frames_number*samples;
if (frames < 120 || frames > 5760)
return false;
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
// Decode to the appropriate sample type.
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
int ret = opus_multistream_decode_float(mOpusDecoder,
aData, aLength,
buffer, frames, false);
#else
int ret = opus_multistream_decode(mOpusDecoder,
aData, aLength,
buffer, frames, false);
#endif
if (ret < 0)
return false;
NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
CheckedInt64 startTime = aTstampUsecs;
// Trim the initial frames while the decoder is settling.
if (mSkip > 0) {
int32_t skipFrames = std::min<int32_t>(mSkip, frames);
int32_t keepFrames = frames - skipFrames;
LOG(LogLevel::Debug, ("Opus decoder skipping %d of %d frames",
skipFrames, frames));
PodMove(buffer.get(),
buffer.get() + skipFrames * channels,
keepFrames * channels);
startTime = startTime + FramesToUsecs(skipFrames, mInfo.mAudio.mRate);
frames = keepFrames;
mSkip -= skipFrames;
}
int64_t discardPadding = 0;
(void) nestegg_packet_discard_padding(aPacket, &discardPadding);
if (discardPadding < 0) {
// Negative discard padding is invalid.
LOG(LogLevel::Debug, ("Opus error, negative discard padding"));
mHitAudioDecodeError = true;
}
if (discardPadding > 0) {
CheckedInt64 discardFrames = UsecsToFrames(discardPadding / NS_PER_USEC,
mInfo.mAudio.mRate);
if (!discardFrames.isValid()) {
NS_WARNING("Int overflow in DiscardPadding");
return false;
}
if (discardFrames.value() > frames) {
// Discarding more than the entire packet is invalid.
LOG(LogLevel::Debug, ("Opus error, discard padding larger than packet"));
if (!mAudioDecoder->Decode(data, length, aHolder->Offset(), tstamp, discardPadding, &total_frames)) {
mHitAudioDecodeError = true;
return false;
}
LOG(LogLevel::Debug, ("Opus decoder discarding %d of %d frames",
int32_t(discardFrames.value()), frames));
// Padding discard is only supposed to happen on the final packet.
// Record the discard so we can return an error if another packet is
// decoded.
mPaddingDiscarded = true;
int32_t keepFrames = frames - discardFrames.value();
frames = keepFrames;
}
// Apply the header gain if one was specified.
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
if (mOpusParser->mGain != 1.0f) {
float gain = mOpusParser->mGain;
int samples = frames * channels;
for (int i = 0; i < samples; i++) {
buffer[i] *= gain;
}
}
#else
if (mOpusParser->mGain_Q16 != 65536) {
int64_t gain_Q16 = mOpusParser->mGain_Q16;
int samples = frames * channels;
for (int i = 0; i < samples; i++) {
int32_t val = static_cast<int32_t>((gain_Q16*buffer[i] + 32768)>>16);
buffer[i] = static_cast<AudioDataValue>(MOZ_CLIP_TO_15(val));
}
}
#endif
CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
if (!duration.isValid()) {
NS_WARNING("Int overflow converting WebM audio duration");
return false;
}
CheckedInt64 time = startTime - mCodecDelay;
if (!time.isValid()) {
NS_WARNING("Int overflow shifting tstamp by codec delay");
return false;
};
AudioQueue().Push(new AudioData(aOffset,
time.value(),
duration.value(),
frames,
buffer.forget(),
mInfo.mAudio.mChannels,
mInfo.mAudio.mRate));
mAudioFrames += frames;
mAudioFrames += total_frames;
return true;
}

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

@ -17,14 +17,6 @@
#include "mozilla/layers/LayersTypes.h"
#ifdef MOZ_TREMOR
#include "tremor/ivorbiscodec.h"
#else
#include "vorbis/codec.h"
#endif
#include "OpusParser.h"
namespace mozilla {
static const unsigned NS_PER_USEC = 1000;
static const double NS_PER_S = 1e9;
@ -138,6 +130,21 @@ public:
virtual ~WebMVideoDecoder() {}
};
// Class to handle various audio decode paths
class WebMAudioDecoder
{
public:
virtual nsresult Init() = 0;
virtual void Shutdown() = 0;
virtual nsresult ResetDecode() = 0;
virtual nsresult DecodeHeader(const unsigned char* aData, size_t aLength) = 0;
virtual nsresult FinishInit(AudioInfo& aInfo) = 0;
virtual bool Decode(const unsigned char* aData, size_t aLength,
int64_t aOffset, uint64_t aTstampUsecs,
int64_t aDiscardPadding, int32_t* aTotalFrames) = 0;
virtual ~WebMAudioDecoder() {}
};
class WebMReader : public MediaDecoderReader
{
public:
@ -201,10 +208,9 @@ public:
void SetLastVideoFrameTime(int64_t aFrameTime);
layers::LayersBackend GetLayersBackendType() { return mLayersBackendType; }
FlushableMediaTaskQueue* GetVideoTaskQueue() { return mVideoTaskQueue; }
uint64_t GetCodecDelay() { return mCodecDelay; }
protected:
// Setup opus decoder
bool InitOpusDecoder();
// Decode a nestegg packet of audio data. Push the audio data on the
// audio queue. Returns true when there's more audio to decode,
@ -213,12 +219,6 @@ protected:
// must be held during this call. The caller is responsible for freeing
// aPacket.
bool DecodeAudioPacket(NesteggPacketHolder* aHolder);
bool DecodeVorbis(const unsigned char* aData, size_t aLength,
int64_t aOffset, uint64_t aTstampUsecs,
int32_t* aTotalFrames);
bool DecodeOpus(const unsigned char* aData, size_t aLength,
int64_t aOffset, uint64_t aTstampUsecs,
nestegg_packet* aPacket);
// Release context and set to null. Called when an error occurs during
// reading metadata or destruction of the reader itself.
@ -246,22 +246,9 @@ private:
// or decoder thread only.
nestegg* mContext;
// The video decoder
nsAutoPtr<WebMAudioDecoder> mAudioDecoder;
nsAutoPtr<WebMVideoDecoder> mVideoDecoder;
// Vorbis decoder state
vorbis_info mVorbisInfo;
vorbis_comment mVorbisComment;
vorbis_dsp_state mVorbisDsp;
vorbis_block mVorbisBlock;
int64_t mPacketCount;
// Opus decoder state
nsAutoPtr<OpusParser> mOpusParser;
OpusMSDecoder *mOpusDecoder;
uint16_t mSkip; // Samples left to trim before playback.
uint64_t mSeekPreroll; // Nanoseconds to discard after seeking.
// Queue of video and audio packets that have been read but not decoded. These
// must only be accessed from the decode thread.
WebMPacketQueue mVideoPackets;
@ -280,6 +267,9 @@ private:
// Number of microseconds that must be discarded from the start of the Stream.
uint64_t mCodecDelay;
// Nanoseconds to discard after seeking.
uint64_t mSeekPreroll;
// Calculate the frame duration from the last decodeable frame using the
// previous frame's timestamp. In NS.
int64_t mLastVideoFrameTime;
@ -309,10 +299,6 @@ private:
bool mHasVideo;
bool mHasAudio;
// Opus padding should only be discarded on the final packet. Once this
// is set to true, if the reader attempts to decode any further packets it
// will raise an error so we can indicate that the file is invalid.
bool mPaddingDiscarded;
};
} // namespace mozilla

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

@ -13,6 +13,7 @@ EXPORTS += [
]
UNIFIED_SOURCES += [
'AudioDecoder.cpp',
'SoftwareWebMVideoDecoder.cpp',
'WebMBufferedParser.cpp',
'WebMDecoder.cpp',

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

@ -26,7 +26,7 @@ enum nsPluginTagType {
// Do not make this interface scriptable, because the virtual functions in C++
// blocks will make script call the wrong functions.
[uuid(c4e26e5d-7a9b-4900-b567-e128c4be6e31)]
[uuid(8ff5f46e-96fa-4905-a75c-35aac30bdcee)]
interface nsIPluginInstanceOwner : nsISupports
{
/**
@ -69,15 +69,6 @@ interface nsIPluginInstanceOwner : nsISupports
void *aHeadersData, uint32_t aHeadersDataLen) = 0;
%}
/**
* Show a status message in the host environment.
*/
void showStatus(in string aStatusMsg);
%{C++
NS_IMETHOD ShowStatus(const char16_t *aStatusMsg) = 0;
%}
/**
* Get the associated document.
*/
@ -104,10 +95,9 @@ interface nsIPluginInstanceOwner : nsISupports
void getNetscapeWindow(in voidPtr aValue);
/**
* Show native context menu
* Convert between plugin, window, and screen coordinate spaces.
*/
%{C++
virtual NPError ShowNativeContextMenu(NPMenu* menu, void* event) = 0;
virtual NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
double *destX, double *destY, NPCoordinateSpace destSpace) = 0;
%}

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

@ -360,10 +360,11 @@ UnregisterGCCallbacks()
JS_RemoveExtraGCRootsTracer(jsRuntime, TraceJSObjWrappers, nullptr);
// Remove delayed destruction callback.
sCallbackRuntime->UnregisterGCCallback(DelayedReleaseGCCallback);
// Unset runtime pointer to indicate callbacks are no longer registered.
sCallbackRuntime = nullptr;
if (sCallbackRuntime) {
sCallbackRuntime->UnregisterGCCallback(DelayedReleaseGCCallback);
// Unset runtime pointer to indicate callbacks are no longer registered.
sCallbackRuntime = nullptr;
}
}
static bool

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

@ -1066,7 +1066,7 @@ _destroystream(NPP npp, NPStream *pstream, NPError reason)
// the reference until it is to be deleted here. Deleting the wrapper will
// release the wrapped nsIOutputStream.
//
// The NPStream the plugin references should always be a sub-object of it's own
// The NPStream the plugin references should always be a sub-object of its own
// 'ndata', which is our nsNPAPIStramWrapper. See bug 548441.
NS_ASSERTION((char*)streamWrapper <= (char*)pstream &&
((char*)pstream) + sizeof(*pstream)
@ -1084,23 +1084,7 @@ _destroystream(NPP npp, NPStream *pstream, NPError reason)
void
_status(NPP npp, const char *message)
{
if (!NS_IsMainThread()) {
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_status called from the wrong thread\n"));
return;
}
NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_Status: npp=%p, message=%s\n",
(void*)npp, message));
if (!npp || !npp->ndata) {
NS_WARNING("_status: npp or npp->ndata == 0");
return;
}
nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance*)npp->ndata;
PluginDestructionGuard guard(inst);
inst->ShowStatus(message);
// NPN_Status is no longer supported.
}
void

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

@ -1605,15 +1605,6 @@ nsNPAPIPluginInstance::SetOwner(nsPluginInstanceOwner *aOwner)
mOwner = aOwner;
}
nsresult
nsNPAPIPluginInstance::ShowStatus(const char* message)
{
if (mOwner)
return mOwner->ShowStatus(message);
return NS_ERROR_FAILURE;
}
nsresult
nsNPAPIPluginInstance::AsyncSetWindow(NPWindow& window)
{

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

@ -116,7 +116,6 @@ public:
nsresult GetJSContext(JSContext* *outContext);
nsPluginInstanceOwner* GetOwner();
void SetOwner(nsPluginInstanceOwner *aOwner);
nsresult ShowStatus(const char* message);
nsNPAPIPlugin* GetPlugin();

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

@ -547,43 +547,6 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL,
return rv;
}
NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const char *aStatusMsg)
{
nsresult rv = NS_ERROR_FAILURE;
rv = this->ShowStatus(NS_ConvertUTF8toUTF16(aStatusMsg).get());
return rv;
}
NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const char16_t *aStatusMsg)
{
nsresult rv = NS_ERROR_FAILURE;
if (!mPluginFrame) {
return rv;
}
nsCOMPtr<nsIDocShellTreeItem> docShellItem = mPluginFrame->PresContext()->GetDocShell();
if (NS_FAILED(rv) || !docShellItem) {
return rv;
}
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
rv = docShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
if (NS_FAILED(rv) || !treeOwner) {
return rv;
}
nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(treeOwner, &rv));
if (NS_FAILED(rv) || !browserChrome) {
return rv;
}
rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT,
aStatusMsg);
return rv;
}
NS_IMETHODIMP nsPluginInstanceOwner::GetDocument(nsIDocument* *aDocument)
{
nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
@ -747,13 +710,6 @@ NS_IMETHODIMP nsPluginInstanceOwner::SetEventModel(int32_t eventModel)
#endif
}
// This is no longer used, just leaving it here so we don't have to change
// the nsIPluginInstanceOwner interface.
NPError nsPluginInstanceOwner::ShowNativeContextMenu(NPMenu* menu, void* event)
{
return NPERR_GENERIC_ERROR;
}
#ifdef XP_MACOSX
NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget *widget,
nsPluginFrame* pluginFrame,

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

@ -63,11 +63,6 @@ public:
nsIInputStream *aPostStream,
void *aHeadersData, uint32_t aHeadersDataLen) override;
NS_IMETHOD ShowStatus(const char16_t *aStatusMsg) override;
// This can go away, just leaving it here to avoid changing the interface.
NPError ShowNativeContextMenu(NPMenu* menu, void* event) override;
NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
double *destX, double *destY, NPCoordinateSpace destSpace) override;

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

@ -1326,9 +1326,7 @@ void
_status(NPP aNPP,
const char* aMessage)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
NS_WARNING("Not yet implemented!");
// NPN_Status is no longer supported.
}
void

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

@ -1,3 +1,19 @@
onfetch = function(e) {
if (e.request.url.indexOf("Referer") >= 0) {
// Silently rewrite the referrer so the referrer test passes since the
// document/worker isn't aware of this service worker.
var url = e.request.url.substring(0, e.request.url.indexOf('?'));
url += '?headers=' + ({ 'Referer': self.location.href }).toSource();
e.respondWith(fetch(url, {
method: e.request.method,
headers: e.request.headers,
body: e.request.body,
mode: e.request.mode,
credentials: e.request.credentials,
cache: e.request.cache,
}));
return;
}
e.respondWith(fetch(e.request));
};

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

@ -1250,6 +1250,25 @@ function testRedirects() {
return Promise.all(fetches);
}
function testReferrer() {
var referrer;
if (self && self.location) {
referrer = self.location.href;
} else {
referrer = document.documentURI;
}
var dict = {
'Referer': referrer
};
return fetch(corsServerPath + "headers=" + dict.toSource()).then(function(res) {
is(res.status, 200, "expected correct referrer header to be sent");
dump(res.statusText);
}, function(e) {
ok(false, "expected correct referrer header to be sent");
});
}
function runTest() {
testNoCorsCtor();
@ -1260,5 +1279,6 @@ function runTest() {
.then(testSameOriginCredentials)
.then(testCrossOriginCredentials)
.then(testRedirects)
.then(testReferrer)
// Put more promise based tests here.
}

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

@ -38,6 +38,7 @@
#include "mozilla/LoadContext.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/dom/CacheBinding.h"
#include "mozilla/dom/cache/CacheTypes.h"
#include "mozilla/dom/cache/Cache.h"
#include "mozilla/dom/cache/CacheStorage.h"
#include "mozilla/dom/Exceptions.h"
@ -190,6 +191,14 @@ ChannelFromScriptURL(nsIPrincipal* principal,
NS_ENSURE_SUCCESS(rv, rv);
if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
rv = nsContentUtils::SetFetchReferrerURIWithPolicy(principal, parentDoc,
httpChannel);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
channel.forget(aChannel);
return rv;
}
@ -422,6 +431,7 @@ private:
nsCOMPtr<nsIInputStreamPump> mPump;
nsCOMPtr<nsIURI> mBaseURI;
ChannelInfo mChannelInfo;
UniquePtr<PrincipalInfo> mPrincipalInfo;
};
NS_IMPL_ISUPPORTS(CacheScriptLoader, nsIStreamLoaderObserver)
@ -592,6 +602,27 @@ private:
// saved in the cache.
ir->InitChannelInfo(channel);
// Save the principal of the channel since its URI encodes the script URI
// rather than the ServiceWorkerRegistrationInfo URI.
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
NS_ASSERTION(ssm, "Should never be null!");
nsCOMPtr<nsIPrincipal> channelPrincipal;
nsresult rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
if (NS_WARN_IF(NS_FAILED(rv))) {
channel->Cancel(rv);
return rv;
}
UniquePtr<PrincipalInfo> principalInfo(new PrincipalInfo());
rv = PrincipalToPrincipalInfo(channelPrincipal, principalInfo.get());
if (NS_WARN_IF(NS_FAILED(rv))) {
channel->Cancel(rv);
return rv;
}
ir->SetPrincipalInfo(Move(principalInfo));
nsRefPtr<Response> response = new Response(mCacheCreator->Global(), ir);
RequestOrUSVString request;
@ -1030,7 +1061,8 @@ private:
void
DataReceivedFromCache(uint32_t aIndex, const uint8_t* aString,
uint32_t aStringLen,
const ChannelInfo& aChannelInfo)
const ChannelInfo& aChannelInfo,
UniquePtr<PrincipalInfo> aPrincipalInfo)
{
AssertIsOnMainThread();
MOZ_ASSERT(aIndex < mLoadInfos.Length());
@ -1054,14 +1086,19 @@ private:
mWorkerPrivate->SetBaseURI(finalURI);
}
nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
DebugOnly<nsIPrincipal*> principal = mWorkerPrivate->GetPrincipal();
MOZ_ASSERT(principal);
nsILoadGroup* loadGroup = mWorkerPrivate->GetLoadGroup();
MOZ_ASSERT(loadGroup);
nsCOMPtr<nsIPrincipal> responsePrincipal =
PrincipalInfoToPrincipal(*aPrincipalInfo);
DebugOnly<bool> equal = false;
MOZ_ASSERT(responsePrincipal && NS_SUCCEEDED(responsePrincipal->Equals(principal, &equal)));
MOZ_ASSERT(equal);
mWorkerPrivate->InitChannelInfo(aChannelInfo);
// Needed to initialize the principal info. This is fine because
// the cache principal cannot change, unlike the channel principal.
mWorkerPrivate->SetPrincipal(principal, loadGroup);
mWorkerPrivate->SetPrincipal(responsePrincipal, loadGroup);
}
if (NS_SUCCEEDED(rv)) {
@ -1413,10 +1450,16 @@ CacheScriptLoader::ResolvedCallback(JSContext* aCx,
nsCOMPtr<nsIInputStream> inputStream;
response->GetBody(getter_AddRefs(inputStream));
mChannelInfo = response->GetChannelInfo();
const UniquePtr<mozilla::ipc::PrincipalInfo>& pInfo =
response->GetPrincipalInfo();
if (pInfo) {
mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(*pInfo);
}
if (!inputStream) {
mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0, mChannelInfo);
mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0, mChannelInfo,
Move(mPrincipalInfo));
return;
}
@ -1472,7 +1515,9 @@ CacheScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aCont
mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen, mChannelInfo);
MOZ_ASSERT(mPrincipalInfo);
mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen, mChannelInfo,
Move(mPrincipalInfo));
return NS_OK;
}

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

@ -3303,6 +3303,7 @@ class FetchEventRunnable : public WorkerRunnable
RequestCredentials mRequestCredentials;
nsContentPolicyType mContentPolicyType;
nsCOMPtr<nsIInputStream> mUploadStream;
nsCString mReferrer;
public:
FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
@ -3319,6 +3320,7 @@ public:
// send credentials to same-origin websites unless explicitly forbidden.
, mRequestCredentials(RequestCredentials::Same_origin)
, mContentPolicyType(nsIContentPolicy::TYPE_INVALID)
, mReferrer(kFETCH_CLIENT_REFERRER_STR)
{
MOZ_ASSERT(aWorkerPrivate);
}
@ -3358,6 +3360,15 @@ public:
mContentPolicyType = loadInfo->InternalContentPolicyType();
nsCOMPtr<nsIURI> referrerURI;
rv = NS_GetReferrerFromChannel(channel, getter_AddRefs(referrerURI));
// We can't bail on failure since certain non-http channels like JAR
// channels are intercepted but don't have referrers.
if (NS_SUCCEEDED(rv) && referrerURI) {
rv = referrerURI->GetSpec(mReferrer);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
if (httpChannel) {
rv = httpChannel->GetRequestMethod(mMethod);
@ -3487,6 +3498,7 @@ private:
internalReq->SetCreatedByFetchEvent();
internalReq->SetBody(mUploadStream);
internalReq->SetReferrer(NS_ConvertUTF8toUTF16(mReferrer));
request->SetContentPolicyType(mContentPolicyType);

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

@ -9,6 +9,8 @@
#include "mozilla/dom/CacheBinding.h"
#include "mozilla/dom/cache/CacheStorage.h"
#include "mozilla/dom/cache/Cache.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsIThreadRetargetableRequest.h"
#include "nsIPrincipal.h"
@ -88,11 +90,17 @@ public:
return rv;
}
nsCOMPtr<nsILoadGroup> loadGroup;
rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), aPrincipal);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = NS_NewChannel(getter_AddRefs(mChannel),
uri, aPrincipal,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_SCRIPT, // FIXME(nsm): TYPE_SERVICEWORKER
aLoadGroup);
loadGroup);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -451,6 +459,28 @@ public:
mChannelInfo.InitFromChannel(aChannel);
}
nsresult
SetPrincipalInfo(nsIChannel* aChannel)
{
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
NS_ASSERTION(ssm, "Should never be null!");
nsCOMPtr<nsIPrincipal> channelPrincipal;
nsresult rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(channelPrincipal));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo(new mozilla::ipc::PrincipalInfo());
rv = PrincipalToPrincipalInfo(channelPrincipal, principalInfo.get());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mPrincipalInfo = Move(principalInfo);
return NS_OK;
}
private:
~CompareManager()
{
@ -546,6 +576,9 @@ private:
ir->SetBody(body);
ir->InitChannelInfo(mChannelInfo);
if (mPrincipalInfo) {
ir->SetPrincipalInfo(Move(mPrincipalInfo));
}
nsRefPtr<Response> response = new Response(aCache->GetGlobalObject(), ir);
@ -579,6 +612,8 @@ private:
ChannelInfo mChannelInfo;
UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
nsCString mMaxScope;
enum {
@ -607,6 +642,10 @@ CompareNetwork::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
#endif
mManager->InitChannelInfo(mChannel);
nsresult rv = mManager->SetPrincipalInfo(mChannel);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}

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

@ -103,6 +103,7 @@ support-files =
empty.html
worker_performance_user_timing.js
sharedworker_performance_user_timing.js
referrer.sjs
[test_404.html]
[test_atob.html]
@ -209,3 +210,4 @@ skip-if = buildapp == 'b2g' || e10s
[test_xhr_timeout.html]
skip-if = (os == "win") || (os == "mac") || toolkit == 'android' || e10s #bug 798220
[test_xhrAbort.html]
[test_referrer.html]

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

@ -0,0 +1,11 @@
function handleRequest(request, response)
{
if (request.queryString == "result") {
response.write(getState("referer"));
} else {
response.setHeader("Content-Type", "text/javascript", false);
response.write("onmessage = function() { postMessage(42); }");
setState("referer", request.getHeader("referer"));
}
}

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

@ -0,0 +1,35 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test the referrer of workers</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">
var worker = new Worker("referrer.sjs");
worker.onmessage = function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'referrer.sjs?result', true);
xhr.onload = function() {
is(xhr.responseText, location.href, "The referrer has been sent.");
SimpleTest.finish();
}
xhr.send();
}
worker.postMessage(42);
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

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

@ -1069,6 +1069,11 @@ nsEditorEventListener::Focus(nsIDOMEvent* aEvent)
// Spell check a textarea the first time that it is focused.
SpellCheckIfNeeded();
if (!mEditor) {
// In e10s, this can cause us to flush notifications, which can destroy
// the node we're about to focus.
return NS_OK;
}
nsCOMPtr<nsIDOMEventTarget> target;
aEvent->GetTarget(getter_AddRefs(target));

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

@ -192,6 +192,13 @@ struct RectCornerRadii {
return radii[aCorner];
}
bool operator==(const RectCornerRadii& aOther) const {
for (size_t i = 0; i < RectCorner::Count; i++) {
if (radii[i] != aOther.radii[i]) return false;
}
return true;
}
void Scale(Float aXScale, Float aYScale) {
for (int i = 0; i < RectCorner::Count; i++) {
radii[i].Scale(aXScale, aYScale);

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

@ -31,6 +31,19 @@ function nativeHorizontalWheelEventMsg() {
throw "Native wheel events not supported on platform " + getPlatform();
}
// Given a pixel scrolling delta, converts it to the platform's native units.
function nativeScrollUnits(aElement, aDimen) {
switch (getPlatform()) {
case "linux": {
// GTK deltas are treated as line height divided by 3 by gecko.
var targetWindow = aElement.ownerDocument.defaultView;
var lineHeight = targetWindow.getComputedStyle(aElement)["font-size"];
return aDimen / (parseInt(lineHeight) * 3);
}
}
return aDimen;
}
function nativeMouseMoveEventMsg() {
switch (getPlatform()) {
case "windows": return 1; // MOUSEEVENTF_MOVE
@ -40,6 +53,18 @@ function nativeMouseMoveEventMsg() {
throw "Native wheel events not supported on platform " + getPlatform();
}
// Convert (aX, aY), in CSS pixels relative to aElement's bounding rect,
// to device pixels relative to aElement's containing window.
function coordinatesRelativeToWindow(aX, aY, aElement) {
var targetWindow = aElement.ownerDocument.defaultView;
var scale = targetWindow.devicePixelRatio;
var rect = aElement.getBoundingClientRect();
return {
x: targetWindow.mozInnerScreenX + ((rect.left + aX) * scale),
y: targetWindow.mozInnerScreenY + ((rect.top + aY) * scale)
};
}
// Synthesizes a native mousewheel event and returns immediately. This does not
// guarantee anything; you probably want to use one of the other functions below
// which actually wait for results.
@ -47,14 +72,15 @@ function nativeMouseMoveEventMsg() {
// aDeltaX and aDeltaY are pixel deltas, and aObserver can be left undefined
// if not needed.
function synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, aObserver) {
var targetWindow = aElement.ownerDocument.defaultView;
aX += targetWindow.mozInnerScreenX;
aY += targetWindow.mozInnerScreenY;
var pt = coordinatesRelativeToWindow(aX, aY, aElement);
if (aDeltaX && aDeltaY) {
throw "Simultaneous wheeling of horizontal and vertical is not supported on all platforms.";
}
aDeltaX = nativeScrollUnits(aElement, aDeltaX);
aDeltaY = nativeScrollUnits(aElement, aDeltaY);
var msg = aDeltaX ? nativeHorizontalWheelEventMsg() : nativeVerticalWheelEventMsg();
_getDOMWindowUtils().sendNativeMouseScrollEvent(aX, aY, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeMouseScrollEvent(pt.x, pt.y, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver);
return true;
}
@ -104,10 +130,9 @@ function synthesizeNativeWheelAndWaitForScrollEvent(aElement, aX, aY, aDeltaX, a
// Synthesizes a native mouse move event and returns immediately.
// aX and aY are relative to the top-left of |aElement|'s containing window.
function synthesizeNativeMouseMove(aElement, aX, aY) {
var targetWindow = aElement.ownerDocument.defaultView;
aX += targetWindow.mozInnerScreenX;
aY += targetWindow.mozInnerScreenY;
_getDOMWindowUtils().sendNativeMouseEvent(aX, aY, nativeMouseMoveEventMsg(), 0, aElement);
var pt = coordinatesRelativeToWindow(aX, aY, aElement);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseMoveEventMsg(), 0, aElement);
return true;
}
@ -128,15 +153,9 @@ function synthesizeNativeMouseMoveAndWaitForMoveEvent(aElement, aX, aY, aCallbac
// Synthesizes a native touch event and dispatches it. aX and aY in CSS pixels
// relative to the top-left of |aElement|'s bounding rect.
function synthesizeNativeTouch(aElement, aX, aY, aType, aObserver = null, aTouchId = 0) {
var targetWindow = aElement.ownerDocument.defaultView;
var scale = targetWindow.devicePixelRatio;
var rect = aElement.getBoundingClientRect();
var x = targetWindow.mozInnerScreenX + ((rect.left + aX) * scale);
var y = targetWindow.mozInnerScreenY + ((rect.top + aY) * scale);
var utils = SpecialPowers.getDOMWindowUtils(targetWindow);
utils.sendNativeTouchPoint(aTouchId, aType, x, y, 1, 90, aObserver);
var pt = coordinatesRelativeToWindow(aX, aY, aElement);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeTouchPoint(aTouchId, aType, pt.x, pt.y, 1, 90, aObserver);
return true;
}

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

@ -44,11 +44,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1151667
function startTest() {
var subframe = document.getElementById('subframe');
var scale = window.devicePixelRatio;
var rect = subframe.getBoundingClientRect();
var x = (rect.left + 100) * scale;
var y = (rect.top + 150) * scale;
synthesizeNativeWheelAndWaitForScrollEvent(subframe, x, y, 0, -10, continueTest);
synthesizeNativeWheelAndWaitForScrollEvent(subframe, 100, 150, 0, -10, continueTest);
}
function continueTest() {

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

@ -52,10 +52,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1173580
// Scroll the mouse wheel over |element|.
function scrollWheelOver(element) {
var scale = window.devicePixelRatio;
var rect = element.getBoundingClientRect();
var x = (rect.left + 10) * scale;
var y = (rect.top + 10) * scale;
var x = 10;
var y = 10;
// Move the mouse to the desired wheel location.
// Not doing so can result in the wheel events from two consecutive
// scrollWheelOver() calls on different elements being incorrectly considered

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

@ -32,7 +32,7 @@
// Scroll diff
var dx = 0;
var dy = -10; // Negative to scroll down
synthesizeNativeWheelAndWaitForEvent(scrollDiv, x, y, dx, dy);
synthesizeNativeWheelAndWaitForWheelEvent(scrollDiv, x, y, dx, dy);
window.requestAnimationFrame(sendScrollEvent);
} else {
// Locally, with silk and apz + e10s, retina 15" mbp usually get ~1.0 - 1.5

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

@ -447,9 +447,12 @@ TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlag
return false;
}
ID3D11Device* d3d11device = gfxWindowsPlatform::GetPlatform()->GetD3D11ContentDevice();
gfxWindowsPlatform* windowsPlatform = gfxWindowsPlatform::GetPlatform();
ID3D11Device* d3d11device = windowsPlatform->GetD3D11ContentDevice();
bool haveD3d11Backend = windowsPlatform->GetContentBackend() == BackendType::DIRECT2D1_1;
if (gfxPrefs::Direct2DUse1_1() && d3d11device) {
if (haveD3d11Backend) {
MOZ_ASSERT(d3d11device != nullptr);
CD3D11_TEXTURE2D_DESC newDesc(mFormat == SurfaceFormat::A8 ? DXGI_FORMAT_R8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM,
aSize.width, aSize.height, 1, 1,

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

@ -14,6 +14,7 @@
#include "mozilla/UniquePtr.h"
#include "nsExpirationTracker.h"
#include "nsClassHashtable.h"
#include "gfxUtils.h"
using namespace mozilla;
using namespace mozilla::gfx;
@ -164,44 +165,61 @@ struct BlurCacheKey : public PLDHashEntryHdr {
typedef const BlurCacheKey* KeyTypePointer;
enum { ALLOW_MEMMOVE = true };
gfxRect mRect;
IntSize mMinSize;
IntSize mBlurRadius;
gfxRect mSkipRect;
gfxRGBA mShadowColor;
BackendType mBackend;
RectCornerRadii mCornerRadii;
BlurCacheKey(const gfxRect& aRect, const IntSize &aBlurRadius, const gfxRect& aSkipRect, BackendType aBackend)
: mRect(aRect)
BlurCacheKey(IntSize aMinimumSize, gfxIntSize aBlurRadius,
RectCornerRadii* aCornerRadii, gfxRGBA aShadowColor,
BackendType aBackend)
: mMinSize(aMinimumSize)
, mBlurRadius(aBlurRadius)
, mSkipRect(aSkipRect)
, mShadowColor(aShadowColor)
, mBackend(aBackend)
, mCornerRadii(aCornerRadii ? *aCornerRadii : RectCornerRadii())
{ }
explicit BlurCacheKey(const BlurCacheKey* aOther)
: mRect(aOther->mRect)
: mMinSize(aOther->mMinSize)
, mBlurRadius(aOther->mBlurRadius)
, mSkipRect(aOther->mSkipRect)
, mShadowColor(aOther->mShadowColor)
, mBackend(aOther->mBackend)
, mCornerRadii(aOther->mCornerRadii)
{ }
static PLDHashNumber
HashKey(const KeyTypePointer aKey)
{
PLDHashNumber hash = HashBytes(&aKey->mRect.x, 4 * sizeof(gfxFloat));
PLDHashNumber hash = 0;
hash = AddToHash(hash, aKey->mMinSize.width, aKey->mMinSize.height);
hash = AddToHash(hash, aKey->mBlurRadius.width, aKey->mBlurRadius.height);
hash = AddToHash(hash, HashBytes(&aKey->mSkipRect.x, 4 * sizeof(gfxFloat)));
hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.r, sizeof(gfxFloat)));
hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.g, sizeof(gfxFloat)));
hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.b, sizeof(gfxFloat)));
hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.a, sizeof(gfxFloat)));
for (int i = 0; i < 4; i++) {
hash = AddToHash(hash, aKey->mCornerRadii[i].width, aKey->mCornerRadii[i].height);
}
hash = AddToHash(hash, (uint32_t)aKey->mBackend);
return hash;
}
bool KeyEquals(KeyTypePointer aKey) const
{
if (aKey->mRect.IsEqualInterior(mRect) &&
if (aKey->mMinSize == mMinSize &&
aKey->mBlurRadius == mBlurRadius &&
aKey->mSkipRect.IsEqualInterior(mSkipRect) &&
aKey->mCornerRadii == mCornerRadii &&
aKey->mShadowColor == mShadowColor &&
aKey->mBackend == mBackend) {
return true;
}
return false;
}
return false;
}
static KeyTypePointer KeyToPointer(KeyType aKey)
{
@ -214,17 +232,15 @@ struct BlurCacheKey : public PLDHashEntryHdr {
* to the cache entry to be able to be tracked by the nsExpirationTracker.
* */
struct BlurCacheData {
BlurCacheData(SourceSurface* aBlur, const IntPoint& aTopLeft, const gfxRect& aDirtyRect, const BlurCacheKey& aKey)
BlurCacheData(SourceSurface* aBlur, IntMargin aExtendDestBy, const BlurCacheKey& aKey)
: mBlur(aBlur)
, mTopLeft(aTopLeft)
, mDirtyRect(aDirtyRect)
, mExtendDest(aExtendDestBy)
, mKey(aKey)
{}
BlurCacheData(const BlurCacheData& aOther)
: mBlur(aOther.mBlur)
, mTopLeft(aOther.mTopLeft)
, mDirtyRect(aOther.mDirtyRect)
, mExtendDest(aOther.mExtendDest)
, mKey(aOther.mKey)
{ }
@ -234,8 +250,7 @@ struct BlurCacheData {
nsExpirationState mExpirationState;
RefPtr<SourceSurface> mBlur;
IntPoint mTopLeft;
gfxRect mDirtyRect;
IntMargin mExtendDest;
BlurCacheKey mKey;
};
@ -259,19 +274,17 @@ class BlurCache final : public nsExpirationTracker<BlurCacheData,4>
mHashEntries.Remove(aObject->mKey);
}
BlurCacheData* Lookup(const gfxRect& aRect,
const IntSize& aBlurRadius,
const gfxRect& aSkipRect,
BackendType aBackendType,
const gfxRect* aDirtyRect)
BlurCacheData* Lookup(const IntSize aMinSize,
const gfxIntSize& aBlurRadius,
RectCornerRadii* aCornerRadii,
const gfxRGBA& aShadowColor,
BackendType aBackendType)
{
BlurCacheData* blur =
mHashEntries.Get(BlurCacheKey(aRect, aBlurRadius, aSkipRect, aBackendType));
mHashEntries.Get(BlurCacheKey(aMinSize, aBlurRadius,
aCornerRadii, aShadowColor,
aBackendType));
if (blur) {
if (aDirtyRect && !blur->mDirtyRect.Contains(*aDirtyRect)) {
return nullptr;
}
MarkUsed(blur);
}
@ -306,52 +319,164 @@ class BlurCache final : public nsExpirationTracker<BlurCacheData,4>
static BlurCache* gBlurCache = nullptr;
static IntSize
ComputeMinSizeForShadowShape(RectCornerRadii* aCornerRadii,
gfxIntSize aBlurRadius,
IntMargin& aSlice,
const IntSize& aRectSize)
{
float cornerWidth = 0;
float cornerHeight = 0;
if (aCornerRadii) {
RectCornerRadii corners = *aCornerRadii;
for (size_t i = 0; i < 4; i++) {
cornerWidth = std::max(cornerWidth, corners[i].width);
cornerHeight = std::max(cornerHeight, corners[i].height);
}
}
aSlice = IntMargin(ceil(cornerHeight) + aBlurRadius.height,
ceil(cornerWidth) + aBlurRadius.width,
ceil(cornerHeight) + aBlurRadius.height,
ceil(cornerWidth) + aBlurRadius.width);
IntSize minSize(aSlice.LeftRight() + 1,
aSlice.TopBottom() + 1);
// If aRectSize is smaller than minSize, the border-image approach won't
// work; there's no way to squeeze parts of the min box-shadow source
// image such that the result looks correct. So we need to adjust minSize
// in such a way that we can later draw it without stretching in the affected
// dimension. We also need to adjust "slice" to ensure that we're not trying
// to slice away more than we have.
if (aRectSize.width < minSize.width) {
minSize.width = aRectSize.width;
aSlice.left = 0;
aSlice.right = 0;
}
if (aRectSize.height < minSize.height) {
minSize.height = aRectSize.height;
aSlice.top = 0;
aSlice.bottom = 0;
}
MOZ_ASSERT(aSlice.LeftRight() <= minSize.width);
MOZ_ASSERT(aSlice.TopBottom() <= minSize.height);
return minSize;
}
void
CacheBlur(DrawTarget& aDT,
const IntSize& aMinSize,
const gfxIntSize& aBlurRadius,
RectCornerRadii* aCornerRadii,
const gfxRGBA& aShadowColor,
IntMargin aExtendDest,
SourceSurface* aBoxShadow)
{
BlurCacheKey key(aMinSize, aBlurRadius, aCornerRadii, aShadowColor, aDT.GetBackendType());
BlurCacheData* data = new BlurCacheData(aBoxShadow, aExtendDest, key);
if (!gBlurCache->RegisterEntry(data)) {
delete data;
}
}
// Blurs a small surface and creates the mask.
static TemporaryRef<SourceSurface>
CreateBlurMask(const IntSize& aRectSize,
RectCornerRadii* aCornerRadii,
gfxIntSize aBlurRadius,
IntMargin& aExtendDestBy,
IntMargin& aSliceBorder,
DrawTarget& aDestDrawTarget)
{
IntMargin slice;
gfxAlphaBoxBlur blur;
IntSize minSize =
ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, slice, aRectSize);
IntRect minRect(IntPoint(), minSize);
gfxContext* blurCtx = blur.Init(ThebesRect(Rect(minRect)), gfxIntSize(),
aBlurRadius, nullptr, nullptr);
if (!blurCtx) {
return nullptr;
}
DrawTarget* blurDT = blurCtx->GetDrawTarget();
ColorPattern black(Color(0.f, 0.f, 0.f, 1.f));
if (aCornerRadii) {
RefPtr<Path> roundedRect =
MakePathForRoundedRect(*blurDT, Rect(minRect), *aCornerRadii);
blurDT->Fill(roundedRect, black);
} else {
blurDT->FillRect(Rect(minRect), black);
}
IntPoint topLeft;
RefPtr<SourceSurface> result = blur.DoBlur(&aDestDrawTarget, &topLeft);
IntRect expandedMinRect(topLeft, result->GetSize());
aExtendDestBy = expandedMinRect - minRect;
aSliceBorder = slice + aExtendDestBy;
MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinRect.width);
MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinRect.height);
return result.forget();
}
static TemporaryRef<SourceSurface>
CreateBoxShadow(DrawTarget& aDT, SourceSurface* aBlurMask, const gfxRGBA& aShadowColor)
{
IntSize blurredSize = aBlurMask->GetSize();
gfxPlatform* platform = gfxPlatform::GetPlatform();
RefPtr<DrawTarget> boxShadowDT =
platform->CreateOffscreenContentDrawTarget(blurredSize, SurfaceFormat::B8G8R8A8);
MOZ_ASSERT(boxShadowDT->GetType() == aDT.GetType());
ColorPattern shadowColor(ToDeviceColor(aShadowColor));
boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0));
return boxShadowDT->Snapshot();
}
SourceSurface*
GetCachedBlur(DrawTarget *aDT,
const gfxRect& aRect,
const IntSize& aBlurRadius,
const gfxRect& aSkipRect,
const gfxRect& aDirtyRect,
IntPoint* aTopLeft)
GetBlur(DrawTarget& aDT,
const IntSize& aRectSize,
const gfxIntSize& aBlurRadius,
RectCornerRadii* aCornerRadii,
const gfxRGBA& aShadowColor,
IntMargin& aExtendDestBy,
IntMargin& aSlice)
{
if (!gBlurCache) {
gBlurCache = new BlurCache();
}
BlurCacheData* cached = gBlurCache->Lookup(aRect, aBlurRadius, aSkipRect,
aDT->GetBackendType(),
&aDirtyRect);
IntSize minSize =
ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aSlice, aRectSize);
BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius,
aCornerRadii, aShadowColor,
aDT.GetBackendType());
if (cached) {
*aTopLeft = cached->mTopLeft;
// See CreateBlurMask() for these values
aExtendDestBy = cached->mExtendDest;
aSlice = aSlice + aExtendDestBy;
return cached->mBlur;
}
return nullptr;
}
void
CacheBlur(DrawTarget *aDT,
const gfxRect& aRect,
const IntSize& aBlurRadius,
const gfxRect& aSkipRect,
SourceSurface* aBlur,
const IntPoint& aTopLeft,
const gfxRect& aDirtyRect)
{
// If we already had a cached value with this key, but an incorrect dirty region then just update
// the existing entry
if (BlurCacheData* cached = gBlurCache->Lookup(aRect, aBlurRadius, aSkipRect,
aDT->GetBackendType(),
nullptr)) {
cached->mBlur = aBlur;
cached->mTopLeft = aTopLeft;
cached->mDirtyRect = aDirtyRect;
return;
RefPtr<SourceSurface> blurMask =
CreateBlurMask(aRectSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice, aDT);
if (!blurMask) {
return nullptr;
}
BlurCacheKey key(aRect, aBlurRadius, aSkipRect, aDT->GetBackendType());
BlurCacheData* data = new BlurCacheData(aBlur, aTopLeft, aDirtyRect, key);
if (!gBlurCache->RegisterEntry(data)) {
delete data;
}
RefPtr<SourceSurface> boxShadow = CreateBoxShadow(aDT, blurMask, aShadowColor);
CacheBlur(aDT, minSize, aBlurRadius, aCornerRadii, aShadowColor, aExtendDestBy, boxShadow);
return boxShadow;
}
void
@ -361,8 +486,65 @@ gfxAlphaBoxBlur::ShutdownBlurCache()
gBlurCache = nullptr;
}
static Rect
RectWithEdgesTRBL(Float aTop, Float aRight, Float aBottom, Float aLeft)
{
return Rect(aLeft, aTop, aRight - aLeft, aBottom - aTop);
}
static void
RepeatOrStretchSurface(DrawTarget& aDT, SourceSurface* aSurface,
const Rect& aDest, const Rect& aSrc, Rect& aSkipRect)
{
if (aSkipRect.Contains(aDest)) {
return;
}
if ((!aDT.GetTransform().IsRectilinear() &&
aDT.GetBackendType() != BackendType::CAIRO) ||
(aDT.GetBackendType() == BackendType::DIRECT2D)) {
// Use stretching if possible, since it leads to less seams when the
// destination is transformed. However, don't do this if we're using cairo,
// because if cairo is using pixman it won't render anything for large
// stretch factors because pixman's internal fixed point precision is not
// high enough to handle those scale factors.
// Calling FillRect on a D2D backend with a repeating pattern is much slower
// than DrawSurface, so special case the D2D backend here.
aDT.DrawSurface(aSurface, aDest, aSrc);
return;
}
SurfacePattern pattern(aSurface, ExtendMode::REPEAT,
Matrix::Translation(aDest.TopLeft() - aSrc.TopLeft()),
Filter::GOOD, RoundedToInt(aSrc));
aDT.FillRect(aDest, pattern);
}
static void
DrawCorner(DrawTarget& aDT, SourceSurface* aSurface,
const Rect& aDest, const Rect& aSrc, Rect& aSkipRect)
{
if (aSkipRect.Contains(aDest)) {
return;
}
aDT.DrawSurface(aSurface, aDest, aSrc);
}
/***
* We draw a blurred a rectangle by only blurring a smaller rectangle and
* splitting the rectangle into 9 parts.
* First, a small minimum source rect is calculated and used to create a blur
* mask since the actual blurring itself is expensive. Next, we use the mask
* with the given shadow color to create a minimally-sized box shadow of the
* right color. Finally, we cut out the 9 parts from the box-shadow source and
* paint each part in the right place, stretching the non-corner parts to fill
* the space between the corners.
*/
/* static */ void
gfxAlphaBoxBlur::BlurRectangle(gfxContext *aDestinationCtx,
gfxAlphaBoxBlur::BlurRectangle(gfxContext* aDestinationCtx,
const gfxRect& aRect,
RectCornerRadii* aCornerRadii,
const gfxPoint& aBlurStdDev,
@ -370,43 +552,123 @@ gfxAlphaBoxBlur::BlurRectangle(gfxContext *aDestinationCtx,
const gfxRect& aDirtyRect,
const gfxRect& aSkipRect)
{
DrawTarget& aDrawTarget = *aDestinationCtx->GetDrawTarget();
DrawTarget& destDrawTarget = *aDestinationCtx->GetDrawTarget();
IntSize blurRadius = CalculateBlurRadius(aBlurStdDev);
IntPoint topLeft;
RefPtr<SourceSurface> surface = GetCachedBlur(&aDrawTarget, aRect, blurRadius, aSkipRect, aDirtyRect, &topLeft);
if (!surface) {
// Create the temporary surface for blurring
gfxAlphaBoxBlur blur;
gfxContext* blurCtx = blur.Init(aRect, IntSize(), blurRadius, &aDirtyRect, &aSkipRect);
if (!blurCtx) {
return;
}
DrawTarget* blurDT = blurCtx->GetDrawTarget();
IntRect rect = RoundedToInt(ToRect(aRect));
IntMargin extendDestBy;
IntMargin slice;
Rect shadowGfxRect = ToRect(aRect);
shadowGfxRect.Round();
ColorPattern black(Color(0.f, 0.f, 0.f, 1.f)); // For masking, so no ToDeviceColor!
if (aCornerRadii) {
RefPtr<Path> roundedRect = MakePathForRoundedRect(*blurDT,
shadowGfxRect,
*aCornerRadii);
blurDT->Fill(roundedRect, black);
} else {
blurDT->FillRect(shadowGfxRect, black);
}
surface = blur.DoBlur(&aDrawTarget, &topLeft);
if (!surface) {
return;
}
CacheBlur(&aDrawTarget, aRect, blurRadius, aSkipRect, surface, topLeft, aDirtyRect);
RefPtr<SourceSurface> boxShadow = GetBlur(destDrawTarget,
rect.Size(), blurRadius,
aCornerRadii, aShadowColor,
extendDestBy, slice);
if (!boxShadow) {
return;
}
aDestinationCtx->SetColor(aShadowColor);
Rect dirtyRect(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
DrawBlur(aDestinationCtx, surface, topLeft, &dirtyRect);
destDrawTarget.PushClipRect(ToRect(aDirtyRect));
// Copy the right parts from boxShadow into destDrawTarget. The middle parts
// will be stretched, border-image style.
Rect srcOuter(Point(), Size(boxShadow->GetSize()));
Rect srcInner = srcOuter;
srcInner.Deflate(Margin(slice));
rect.Inflate(extendDestBy);
Rect dstOuter(rect);
Rect dstInner(rect);
dstInner.Deflate(Margin(slice));
Rect skipRect = ToRect(aSkipRect);
if (srcInner.IsEqualInterior(srcOuter)) {
MOZ_ASSERT(dstInner.IsEqualInterior(dstOuter));
// The target rect is smaller than the minimal size so just draw the surface
destDrawTarget.DrawSurface(boxShadow, dstInner, srcInner);
} else {
// Corners: top left, top right, bottom left, bottom right
DrawCorner(destDrawTarget, boxShadow,
RectWithEdgesTRBL(dstOuter.Y(), dstInner.X(),
dstInner.Y(), dstOuter.X()),
RectWithEdgesTRBL(srcOuter.Y(), srcInner.X(),
srcInner.Y(), srcOuter.X()),
skipRect);
DrawCorner(destDrawTarget, boxShadow,
RectWithEdgesTRBL(dstOuter.Y(), dstOuter.XMost(),
dstInner.Y(), dstInner.XMost()),
RectWithEdgesTRBL(srcOuter.Y(), srcOuter.XMost(),
srcInner.Y(), srcInner.XMost()),
skipRect);
DrawCorner(destDrawTarget, boxShadow,
RectWithEdgesTRBL(dstInner.YMost(), dstInner.X(),
dstOuter.YMost(), dstOuter.X()),
RectWithEdgesTRBL(srcInner.YMost(), srcInner.X(),
srcOuter.YMost(), srcOuter.X()),
skipRect);
DrawCorner(destDrawTarget, boxShadow,
RectWithEdgesTRBL(dstInner.YMost(), dstOuter.XMost(),
dstOuter.YMost(), dstInner.XMost()),
RectWithEdgesTRBL(srcInner.YMost(), srcOuter.XMost(),
srcOuter.YMost(), srcInner.XMost()),
skipRect);
// Edges: top, left, right, bottom
RepeatOrStretchSurface(destDrawTarget, boxShadow,
RectWithEdgesTRBL(dstOuter.Y(), dstInner.XMost(),
dstInner.Y(), dstInner.X()),
RectWithEdgesTRBL(srcOuter.Y(), srcInner.XMost(),
srcInner.Y(), srcInner.X()),
skipRect);
RepeatOrStretchSurface(destDrawTarget, boxShadow,
RectWithEdgesTRBL(dstInner.Y(), dstInner.X(),
dstInner.YMost(), dstOuter.X()),
RectWithEdgesTRBL(srcInner.Y(), srcInner.X(),
srcInner.YMost(), srcOuter.X()),
skipRect);
RepeatOrStretchSurface(destDrawTarget, boxShadow,
RectWithEdgesTRBL(dstInner.Y(), dstOuter.XMost(),
dstInner.YMost(), dstInner.XMost()),
RectWithEdgesTRBL(srcInner.Y(), srcOuter.XMost(),
srcInner.YMost(), srcInner.XMost()),
skipRect);
RepeatOrStretchSurface(destDrawTarget, boxShadow,
RectWithEdgesTRBL(dstInner.YMost(), dstInner.XMost(),
dstOuter.YMost(), dstInner.X()),
RectWithEdgesTRBL(srcInner.YMost(), srcInner.XMost(),
srcOuter.YMost(), srcInner.X()),
skipRect);
// Middle part
RepeatOrStretchSurface(destDrawTarget, boxShadow,
RectWithEdgesTRBL(dstInner.Y(), dstInner.XMost(),
dstInner.YMost(), dstInner.X()),
RectWithEdgesTRBL(srcInner.Y(), srcInner.XMost(),
srcInner.YMost(), srcInner.X()),
skipRect);
}
// A note about anti-aliasing and seems between adjacent parts:
// We don't explicitly disable anti-aliasing in the DrawSurface calls above,
// so if there's a transform on destDrawTarget that is not pixel-aligned,
// there will be seams between adjacent parts of the box-shadow. It's hard to
// avoid those without the use of an intermediate surface.
// You might think that we could avoid those by just turning of AA, but there
// is a problem with that: Box-shadow rendering needs to clip out the
// element's border box, and we'd like that clip to have anti-aliasing -
// especially if the element has rounded corners! So we can't do that unless
// we have a way to say "Please anti-alias the clip, but don't antialias the
// destination rect of the DrawSurface call".
// On OS X there is an additional problem with turning off AA: CoreGraphics
// will not just fill the pixels that have their pixel center inside the
// filled shape. Instead, it will fill all the pixels which are partially
// covered by the shape. So for pixels on the edge between two adjacent parts,
// all those pixels will be painted to by both parts, which looks very bad.
destDrawTarget.PopClip();
}

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

@ -5629,6 +5629,41 @@ class CheckSimdVectorScalarArgs
}
};
class CheckSimdReplaceLaneArgs
{
AsmJSSimdType formalSimdType_;
public:
explicit CheckSimdReplaceLaneArgs(AsmJSSimdType t) : formalSimdType_(t) {}
bool operator()(FunctionCompiler& f, ParseNode* arg, unsigned argIndex, Type actualType,
MDefinition** def) const
{
MOZ_ASSERT(argIndex < 3);
uint32_t u32;
switch (argIndex) {
case 0:
// First argument is the vector
if (!(actualType <= Type(formalSimdType_))) {
return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
Type(formalSimdType_).toChars());
}
return true;
case 1:
// Second argument is the lane < 4
if (!IsLiteralOrConstInt(f, arg, &u32))
return f.failf(arg, "lane selector should be a constant integer literal");
if (u32 >= SimdTypeToLength(formalSimdType_))
return f.failf(arg, "lane selector should be in bounds");
return true;
case 2:
// Third argument is the scalar
return CheckSimdScalarArgs(formalSimdType_)(f, arg, argIndex, actualType, def);
}
return false;
}
};
} // anonymous namespace
static inline bool
@ -5684,14 +5719,17 @@ CheckSimdBinary<MSimdShift::Operation>(FunctionCompiler& f, ParseNode* call, Asm
}
static bool
CheckSimdWith(FunctionCompiler& f, ParseNode* call, AsmJSSimdType opType, SimdLane lane,
MDefinition** def, Type* type)
CheckSimdReplaceLane(FunctionCompiler& f, ParseNode* call, AsmJSSimdType opType,
MDefinition** def, Type* type)
{
DefinitionVector defs;
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(opType), &defs))
if (!CheckSimdCallArgs(f, call, 3, CheckSimdReplaceLaneArgs(opType), &defs))
return false;
ParseNode* laneArg = NextNode(CallArgList(call));
uint32_t lane;
JS_ALWAYS_TRUE(IsLiteralInt(f.m(), laneArg, &lane));
*type = opType;
*def = f.insertElementSimd(defs[0], defs[1], lane, type->toMIRType());
*def = f.insertElementSimd(defs[0], defs[2], SimdLane(lane), type->toMIRType());
return true;
}
@ -5950,14 +5988,8 @@ CheckSimdOperationCall(FunctionCompiler& f, ParseNode* call, const ModuleCompile
case AsmJSSimdOperation_xor:
return CheckSimdBinary(f, call, opType, MSimdBinaryBitwise::xor_, def, type);
case AsmJSSimdOperation_withX:
return CheckSimdWith(f, call, opType, SimdLane::LaneX, def, type);
case AsmJSSimdOperation_withY:
return CheckSimdWith(f, call, opType, SimdLane::LaneY, def, type);
case AsmJSSimdOperation_withZ:
return CheckSimdWith(f, call, opType, SimdLane::LaneZ, def, type);
case AsmJSSimdOperation_withW:
return CheckSimdWith(f, call, opType, SimdLane::LaneW, def, type);
case AsmJSSimdOperation_replaceLane:
return CheckSimdReplaceLane(f, call, opType, def, type);
case AsmJSSimdOperation_fromInt32x4:
return CheckSimdCast<MSimdConvert>(f, call, AsmJSSimdType_int32x4, opType, def, type);

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

@ -614,22 +614,6 @@ template<typename T>
struct Or {
static T apply(T l, T r) { return l | r; }
};
template<typename T>
struct WithX {
static T apply(int32_t lane, T scalar, T x) { return lane == 0 ? scalar : x; }
};
template<typename T>
struct WithY {
static T apply(int32_t lane, T scalar, T x) { return lane == 1 ? scalar : x; }
};
template<typename T>
struct WithZ {
static T apply(int32_t lane, T scalar, T x) { return lane == 2 ? scalar : x; }
};
template<typename T>
struct WithW {
static T apply(int32_t lane, T scalar, T x) { return lane == 3 ? scalar : x; }
};
// For the following three operators, if the value v we're trying to shift is
// such that v << bits can't fit in the int32 range, then we have undefined
// behavior, according to C++11 [expr.shift]p2.
@ -717,26 +701,33 @@ BinaryFunc(JSContext* cx, unsigned argc, Value* vp)
return CoercedBinaryFunc<In, Out, Op, Out>(cx, argc, vp);
}
template<typename V, template<typename T> class OpWith>
template<typename V>
static bool
FuncWith(JSContext* cx, unsigned argc, Value* vp)
ReplaceLane(JSContext* cx, unsigned argc, Value* vp)
{
typedef typename V::Elem Elem;
CallArgs args = CallArgsFromVp(argc, vp);
// Only the first argument is mandatory
if (args.length() < 1 || !IsVectorObject<V>(args[0]))
// Only the first and second arguments are mandatory
if (args.length() < 2 || !IsVectorObject<V>(args[0]))
return ErrorBadArgs(cx);
Elem* vec = TypedObjectMemory<Elem*>(args[0]);
Elem result[V::lanes];
if (!args[1].isInt32())
return ErrorBadArgs(cx);
int32_t lanearg = args[1].toInt32();
if (lanearg < 0 || uint32_t(lanearg) >= V::lanes)
return ErrorBadArgs(cx);
uint32_t lane = uint32_t(lanearg);
Elem value;
if (!V::toType(cx, args.get(1), &value))
if (!V::toType(cx, args.get(2), &value))
return false;
for (unsigned i = 0; i < V::lanes; i++)
result[i] = OpWith<Elem>::apply(i, value, vec[i]);
result[i] = i == lane ? value : vec[i];
return StoreResult<V>(cx, args, result);
}
@ -1170,4 +1161,3 @@ js::simd_int32x4_##Name(JSContext* cx, unsigned argc, Value* vp) \
}
INT32X4_FUNCTION_LIST(DEFINE_SIMD_INT32X4_FUNCTION)
#undef DEFINE_SIMD_INT32X4_FUNCTION

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

@ -59,15 +59,12 @@
V(store2, (Store<Float32x4, 2>), 3) \
V(store1, (Store<Float32x4, 1>), 3) \
V(sub, (BinaryFunc<Float32x4, Sub, Float32x4>), 2) \
V(withX, (FuncWith<Float32x4, WithX>), 2) \
V(withY, (FuncWith<Float32x4, WithY>), 2) \
V(withZ, (FuncWith<Float32x4, WithZ>), 2) \
V(withW, (FuncWith<Float32x4, WithW>), 2) \
V(xor, (CoercedBinaryFunc<Float32x4, Int32x4, Xor, Float32x4>), 2)
#define FLOAT32X4_TERNARY_FUNCTION_LIST(V) \
V(bitselect, BitSelect<Float32x4>, 3) \
V(clamp, Clamp<Float32x4>, 3) \
V(replaceLane, (ReplaceLane<Float32x4>), 3) \
V(select, Select<Float32x4>, 3)
#define FLOAT32X4_SHUFFLE_FUNCTION_LIST(V) \
@ -111,13 +108,12 @@
V(notEqual, (CompareFunc<Float64x2, NotEqual>), 2) \
V(store, (Store<Float64x2, 2>), 3) \
V(store1, (Store<Float64x2, 1>), 3) \
V(sub, (BinaryFunc<Float64x2, Sub, Float64x2>), 2) \
V(withX, (FuncWith<Float64x2, WithX>), 2) \
V(withY, (FuncWith<Float64x2, WithY>), 2)
V(sub, (BinaryFunc<Float64x2, Sub, Float64x2>), 2)
#define FLOAT64X2_TERNARY_FUNCTION_LIST(V) \
V(bitselect, BitSelect<Float64x2>, 3) \
V(clamp, Clamp<Float64x2>, 3) \
V(replaceLane, (ReplaceLane<Float64x2>), 3) \
V(select, Select<Float64x2>, 3)
#define FLOAT64X2_SHUFFLE_FUNCTION_LIST(V) \
@ -163,14 +159,11 @@
V(store3, (Store<Int32x4, 3>), 3) \
V(store2, (Store<Int32x4, 2>), 3) \
V(store1, (Store<Int32x4, 1>), 3) \
V(withX, (FuncWith<Int32x4, WithX>), 2) \
V(withY, (FuncWith<Int32x4, WithY>), 2) \
V(withZ, (FuncWith<Int32x4, WithZ>), 2) \
V(withW, (FuncWith<Int32x4, WithW>), 2) \
V(xor, (BinaryFunc<Int32x4, Xor, Int32x4>), 2)
#define INT32X4_TERNARY_FUNCTION_LIST(V) \
V(bitselect, BitSelect<Int32x4>, 3) \
V(replaceLane, (ReplaceLane<Int32x4>), 3) \
V(select, Select<Int32x4>, 3)
#define INT32X4_QUARTERNARY_FUNCTION_LIST(V) \
@ -226,16 +219,11 @@
_(notEqual) \
_(greaterThan) \
_(greaterThanOrEqual)
#define WITH_COMMONX4_SIMD_OP(_) \
_(withX) \
_(withY) \
_(withZ) \
_(withW)
// TODO: remove when all SIMD calls are inlined (bug 1112155)
#define ION_COMMONX4_SIMD_OP(_) \
ARITH_COMMONX4_SIMD_OP(_) \
BITWISE_COMMONX4_SIMD_OP(_) \
WITH_COMMONX4_SIMD_OP(_) \
_(replaceLane) \
_(bitselect) \
_(select) \
_(splat) \

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

@ -1973,14 +1973,18 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind
}
}
if (src->is<InlineTypedObject>()) {
InlineTypedObject::objectMovedDuringMinorGC(this, dst, src);
} else if (src->is<UnboxedArrayObject>()) {
tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
} else {
// Objects with JSCLASS_SKIP_NURSERY_FINALIZE need to be handled above
// to ensure any additional nursery buffers they hold are moved.
MOZ_ASSERT(!(src->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE));
if (src->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE) {
if (src->is<InlineTypedObject>()) {
InlineTypedObject::objectMovedDuringMinorGC(this, dst, src);
} else if (src->is<UnboxedArrayObject>()) {
tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
} else if (src->is<ArgumentsObject>()) {
tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
} else {
// Objects with JSCLASS_SKIP_NURSERY_FINALIZE need to be handled above
// to ensure any additional nursery buffers they hold are moved.
MOZ_CRASH("Unhandled JSCLASS_SKIP_NURSERY_FINALIZE Class");
}
}
return tenuredSize;

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

@ -0,0 +1,102 @@
load(libdir + 'simd.js');
setJitCompilerOption("ion.warmup.trigger", 50);
function f() {
var f4 = SIMD.float32x4(1, 2, 3, 4);
var i4 = SIMD.int32x4(1, 2, 3, 4);
for (var i = 0; i < 150; i++) {
assertEqX4(SIMD.int32x4.replaceLane(i4, 0, 42), [42, 2, 3, 4]);
assertEqX4(SIMD.int32x4.replaceLane(i4, 1, 42), [1, 42, 3, 4]);
assertEqX4(SIMD.int32x4.replaceLane(i4, 2, 42), [1, 2, 42, 4]);
assertEqX4(SIMD.int32x4.replaceLane(i4, 3, 42), [1, 2, 3, 42]);
assertEqX4(SIMD.float32x4.replaceLane(f4, 0, 42), [42, 2, 3, 4]);
assertEqX4(SIMD.float32x4.replaceLane(f4, 1, 42), [1, 42, 3, 4]);
assertEqX4(SIMD.float32x4.replaceLane(f4, 2, 42), [1, 2, 42, 4]);
assertEqX4(SIMD.float32x4.replaceLane(f4, 3, 42), [1, 2, 3, 42]);
}
}
f();
function e() {
var f4 = SIMD.float32x4(1, 2, 3, 4);
var i4 = SIMD.int32x4(1, 2, 3, 4);
for (let i = 0; i < 150; i++) {
let caught = false;
try {
let x = SIMD.int32x4.replaceLane(i < 149 ? i4 : f4, 0, 42);
} catch(e) {
assertEq(e instanceof TypeError, true);
assertEq(i, 149);
caught = true;
}
assertEq(i < 149 || caught, true);
}
for (let i = 0; i < 150; i++) {
let caught = false;
try {
let x = SIMD.int32x4.replaceLane(i4, i < 149 ? 0 : 4, 42);
} catch(e) {
assertEq(e instanceof TypeError, true);
assertEq(i, 149);
caught = true;
}
assertEq(i < 149 || caught, true);
}
for (let i = 0; i < 150; i++) {
let caught = false;
try {
let x = SIMD.int32x4.replaceLane(i4, i < 149 ? 0 : 1.1, 42);
} catch(e) {
assertEq(e instanceof TypeError, true);
assertEq(i, 149);
caught = true;
}
assertEq(i < 149 || caught, true);
}
for (let i = 0; i < 150; i++) {
let caught = false;
try {
let x = SIMD.float32x4.replaceLane(i < 149 ? f4 : i4, 0, 42);
} catch(e) {
assertEq(e instanceof TypeError, true);
assertEq(i, 149);
caught = true;
}
assertEq(i < 149 || caught, true);
}
for (let i = 0; i < 150; i++) {
let caught = false;
try {
let x = SIMD.float32x4.replaceLane(f4, i < 149 ? 0 : 4, 42);
} catch(e) {
assertEq(e instanceof TypeError, true);
assertEq(i, 149);
caught = true;
}
assertEq(i < 149 || caught, true);
}
for (let i = 0; i < 150; i++) {
let caught = false;
try {
let x = SIMD.float32x4.replaceLane(f4, i < 149 ? 0 : 1.1, 42);
} catch(e) {
assertEq(e instanceof TypeError, true);
assertEq(i, 149);
caught = true;
}
assertEq(i < 149 || caught, true);
}
}
e();

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

@ -1,23 +0,0 @@
load(libdir + 'simd.js');
setJitCompilerOption("ion.warmup.trigger", 50);
function f() {
var f4 = SIMD.float32x4(1, 2, 3, 4);
var i4 = SIMD.int32x4(1, 2, 3, 4);
for (var i = 0; i < 150; i++) {
assertEqX4(SIMD.int32x4.withX(i4, 42), [42, 2, 3, 4]);
assertEqX4(SIMD.int32x4.withY(i4, 42), [1, 42, 3, 4]);
assertEqX4(SIMD.int32x4.withZ(i4, 42), [1, 2, 42, 4]);
assertEqX4(SIMD.int32x4.withW(i4, 42), [1, 2, 3, 42]);
assertEqX4(SIMD.float32x4.withX(f4, 42), [42, 2, 3, 4]);
assertEqX4(SIMD.float32x4.withY(f4, 42), [1, 42, 3, 4]);
assertEqX4(SIMD.float32x4.withZ(f4, 42), [1, 2, 42, 4]);
assertEqX4(SIMD.float32x4.withW(f4, 42), [1, 2, 3, 42]);
}
}
f();

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

@ -609,37 +609,34 @@ CheckF4(F32MAXNUM, 'var x=f4(0,0,-0,-0); var y=f4(0,-0,0,-0); x=max(x,y)', [0,0,
CheckF4(F32MAXNUM + FROUND + 'var NaN = glob.NaN;', 'var x=f4(0,0,0,0); var y=f4(0,0,0,0); var n=f32(0); n=f32(NaN); x=f4(n,0.,n,0.); y=f4(n,n,0.,0.); x=max(x,y)', [NaN, 0, 0, 0]);
// With
const WXF = 'var w = f4.withX;';
const WYF = 'var w = f4.withY;';
const WZF = 'var w = f4.withZ;';
const WWF = 'var w = f4.withW;';
const RLF = 'var r = f4.replaceLane;';
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, 1);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, x);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1, f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1., f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(f32(1), f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); var y = i4(1,2,3,4); x = w(y, f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + RLF + "function f() {var x = f4(1,2,3,4); x = r(x, 0, 1);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + RLF + "function f() {var x = f4(1,2,3,4); x = r(x, 0, x);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + RLF + FROUND + "function f() {var x = f4(1,2,3,4); x = r(x, 4, f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + RLF + FROUND + "function f() {var x = f4(1,2,3,4); x = r(x, f32(0), f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + RLF + FROUND + "function f() {var x = f4(1,2,3,4); x = r(1, 0, f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + RLF + FROUND + "function f() {var x = f4(1,2,3,4); x = r(1, 0., f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + RLF + FROUND + "function f() {var x = f4(1,2,3,4); x = r(f32(1), 0, f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + RLF + FROUND + "function f() {var x = f4(1,2,3,4); var l = 0; x = r(x, l, f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + RLF + FROUND + "function f() {var x = f4(1,2,3,4); var y = i4(1,2,3,4); x = r(y, 0, f32(1));} return f");
CheckF4(WXF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [Math.fround(13.37), 2, 3, 4]);
CheckF4(WYF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, Math.fround(13.37), 3, 4]);
CheckF4(WZF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, 2, Math.fround(13.37), 4]);
CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, 2, 3, Math.fround(13.37)]);
CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37) + f32(6.63));', [1, 2, 3, Math.fround(Math.fround(13.37) + Math.fround(6.63))]);
CheckF4(RLF + FROUND, 'var x = f4(1,2,3,4); x = r(x, 0, f32(13.37));', [Math.fround(13.37), 2, 3, 4]);
CheckF4(RLF + FROUND, 'var x = f4(1,2,3,4); x = r(x, 1, f32(13.37));', [1, Math.fround(13.37), 3, 4]);
CheckF4(RLF + FROUND, 'var x = f4(1,2,3,4); x = r(x, 2, f32(13.37));', [1, 2, Math.fround(13.37), 4]);
CheckF4(RLF + FROUND, 'var x = f4(1,2,3,4); x = r(x, 3, f32(13.37));', [1, 2, 3, Math.fround(13.37)]);
CheckF4(RLF + FROUND, 'var x = f4(1,2,3,4); x = r(x, 3, f32(13.37) + f32(6.63));', [1, 2, 3, Math.fround(Math.fround(13.37) + Math.fround(6.63))]);
CheckF4(WXF + FROUND, 'var x = f4(1,2,3,4); x = w(x, 13.37);', [Math.fround(13.37), 2, 3, 4]);
CheckF4(WYF + FROUND, 'var x = f4(1,2,3,4); x = w(x, 13.37);', [1, Math.fround(13.37), 3, 4]);
CheckF4(WZF + FROUND, 'var x = f4(1,2,3,4); x = w(x, 13.37);', [1, 2, Math.fround(13.37), 4]);
CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, 13.37);', [1, 2, 3, Math.fround(13.37)]);
CheckF4(RLF + FROUND, 'var x = f4(1,2,3,4); x = r(x, 0, 13.37);', [Math.fround(13.37), 2, 3, 4]);
CheckF4(RLF + FROUND, 'var x = f4(1,2,3,4); x = r(x, 1, 13.37);', [1, Math.fround(13.37), 3, 4]);
CheckF4(RLF + FROUND, 'var x = f4(1,2,3,4); x = r(x, 2, 13.37);', [1, 2, Math.fround(13.37), 4]);
CheckF4(RLF + FROUND, 'var x = f4(1,2,3,4); x = r(x, 3, 13.37);', [1, 2, 3, Math.fround(13.37)]);
const WXI = 'var w = i4.withX;';
const WYI = 'var w = i4.withY;';
const WZI = 'var w = i4.withZ;';
const WWI = 'var w = i4.withW;';
CheckI4(WXI, 'var x = i4(1,2,3,4); x = w(x, 42);', [42, 2, 3, 4]);
CheckI4(WYI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 42, 3, 4]);
CheckI4(WZI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 2, 42, 4]);
CheckI4(WWI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 2, 3, 42]);
const RLI = 'var r = i4.replaceLane;';
CheckI4(RLI, 'var x = i4(1,2,3,4); x = r(x, 0, 42);', [42, 2, 3, 4]);
CheckI4(RLI, 'var x = i4(1,2,3,4); x = r(x, 1, 42);', [1, 42, 3, 4]);
CheckI4(RLI, 'var x = i4(1,2,3,4); x = r(x, 2, 42);', [1, 2, 42, 4]);
CheckI4(RLI, 'var x = i4(1,2,3,4); x = r(x, 3, 42);', [1, 2, 3, 42]);
// Comparisons
// True yields all bits set to 1 (i.e as an int32, 0xFFFFFFFF === -1), false
@ -1319,4 +1316,3 @@ asmLink(asmCompile('glob', 'ffi', code), this, assertEqFFI)();
print('Error:', e)
throw e;
}

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

@ -1,43 +0,0 @@
// |jit-test| test-join=--no-unboxed-objects
//
// Unboxed object optimization might not trigger in all cases, thus we ensure
// that Scalar Replacement optimization is working well independently of the
// object representation.
// Ion eager fails the test below because we have not yet created any
// template object in baseline before running the content of the top-level
// function.
if (getJitCompilerOptions()["ion.warmup.trigger"] <= 90)
setJitCompilerOption("ion.warmup.trigger", 90);
// This test checks that we are able to remove the getprop & setprop with scalar
// replacement, so we should not force inline caches, as this would skip the
// generation of getprop & setprop instructions.
if (getJitCompilerOptions()["ion.forceinlineCaches"])
setJitCompilerOption("ion.forceinlineCaches", 0);
// Frequent GCs can interfere with the tests being performed here.
if (typeof gczeal != "undefined")
gczeal(0);
var arr = new Array();
var max = 2000;
for (var i=0; i < max; i++)
arr[i] = i;
function f() {
var res = 0;
var nextObj;
var itr = arr[Symbol.iterator]();
do {
nextObj = itr.next();
if (nextObj.done)
break;
res += nextObj.value;
assertRecoveredOnBailout(nextObj, true);
} while (true);
return res;
}
for (var j = 0; j < 10; j++)
assertEq(f(), max * (max - 1) / 2);

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

@ -28,8 +28,8 @@ function f(j) {
i: i,
v: i + i
};
assertRecoveredOnBailout(obj, true);
assertRecoveredOnBailout(obj.v, true);
assertRecoveredOnBailout(obj, false); // :TODO: Fixed by Bug 1165348
assertRecoveredOnBailout(obj.v, false); // :TODO: Fixed by Bug 1165348
if (uceFault(j) || uceFault(j)) {
// MObjectState::recover should neither fail,
// nor coerce its result to an int32.

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

@ -50,9 +50,9 @@ function notSoEmpty1() {
assertRecoveredOnBailout(c, true);
assertRecoveredOnBailout(d, true);
assertRecoveredOnBailout(unused, true);
// The ucefault branch is not taken yet, and GVN removes it. Scalar
// Replacement thus removes the creation of the object.
assertRecoveredOnBailout(res, true);
// Scalar Replacement is coming after the branch removal made by GVN, and
// the ucefault branch is not taken yet.
assertRecoveredOnBailout(res, false);
}
// Check that we can recover objects with their content.
@ -75,9 +75,9 @@ function notSoEmpty2(i) {
assertRecoveredOnBailout(c, true);
assertRecoveredOnBailout(d, true);
assertRecoveredOnBailout(unused, true);
// The ucefault branch is not taken yet, and GVN removes it. Scalar
// Replacement thus removes the creation of the object.
assertRecoveredOnBailout(res, true);
// Scalar Replacement is coming after the branch removal made by GVN, and
// the ucefault branch is not taken yet.
assertRecoveredOnBailout(res, false);
}
// Check that we can recover objects with their content.

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

@ -1352,14 +1352,16 @@ OptimizeMIR(MIRGenerator* mir)
return false;
}
ValueNumberer gvn(mir, graph);
if (!gvn.init())
return false;
if (mir->optimizationInfo().scalarReplacementEnabled()) {
AutoTraceLog log(logger, TraceLogger_ScalarReplacement);
if (!ScalarReplacement(mir, graph))
return false;
gs.spewPass("Scalar Replacement");
AssertGraphCoherency(graph);
size_t doRepeatOptimizations = 0;
repeatOptimizations:
doRepeatOptimizations++;
MOZ_ASSERT(doRepeatOptimizations <= 2);
if (mir->shouldCancel("Scalar Replacement"))
return false;
}
if (!mir->compilingAsmJS()) {
AutoTraceLog log(logger, TraceLogger_ApplyTypes);
@ -1395,6 +1397,10 @@ OptimizeMIR(MIRGenerator* mir)
return false;
}
ValueNumberer gvn(mir, graph);
if (!gvn.init())
return false;
// Alias analysis is required for LICM and GVN so that we don't move
// loads across stores.
if (mir->optimizationInfo().licmEnabled() ||
@ -1410,50 +1416,7 @@ OptimizeMIR(MIRGenerator* mir)
if (mir->shouldCancel("Alias analysis"))
return false;
// We only eliminate dead resume point operands in the first pass
// because it is currently unsound to do so after GVN.
//
// Consider the following example, where def1 dominates, and is
// congruent with def4, and use3 dominates, and is congruent with,
// use6.
//
// def1
// nop2
// resumepoint def1
// use3 def1
// def4
// nop5
// resumepoint def4
// use6 def4
// use7 use3 use6
//
// Assume that use3, use6, and use7 can cause OSI and are
// non-effectful. That is, use3 will resume at nop2, and use6 and use7
// will resume at nop5.
//
// After GVN, since def1 =~ def4, we have:
//
// def4 - replaced with def1 and pruned
// use6 - replaced with use3 and pruned
// use7 - renumbered to use5
//
// def1
// nop2
// resumepoint def1
// use3 def1
// nop4
// resumepoint def1
// use5 use3 use3
//
// nop4's resumepoint's operand of def1 is considered dead, because it
// is dominated by the last use of def1, use3.
//
// However, if use5 causes OSI, we will resume at nop4's resume
// point. The baseline frame at that point expects the now-pruned def4
// to exist. However, since it was replaced with def1 by GVN, and def1
// is dead at the point of nop4, the baseline frame incorrectly gets
// an optimized out value.
if (!mir->compilingAsmJS() && doRepeatOptimizations == 1) {
if (!mir->compilingAsmJS()) {
// Eliminating dead resume point operands requires basic block
// instructions to be numbered. Reuse the numbering computed during
// alias analysis.
@ -1493,26 +1456,6 @@ OptimizeMIR(MIRGenerator* mir)
}
}
if (mir->optimizationInfo().scalarReplacementEnabled() && doRepeatOptimizations <= 1) {
AutoTraceLog log(logger, TraceLogger_ScalarReplacement);
bool success = false;
if (!ScalarReplacement(mir, graph, &success))
return false;
gs.spewPass("Scalar Replacement");
AssertGraphCoherency(graph);
if (mir->shouldCancel("Scalar Replacement"))
return false;
// We got some success at removing objects allocation and removing the
// loads and stores, unfortunately, this phase is terrible at keeping
// the type consistency, so we re-run the Apply Type phase. As this
// optimization folds loads and stores, it might also introduce new
// opportunities for GVN and LICM, so re-run them as well.
if (success)
goto repeatOptimizations;
}
if (mir->optimizationInfo().rangeAnalysisEnabled()) {
AutoTraceLog log(logger, TraceLogger_RangeAnalysis);
RangeAnalysis r(mir, graph);

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

@ -817,8 +817,8 @@ class IonBuilder
MSimdBinaryComp::Operation op, SimdTypeDescr::Type compType);
InliningStatus inlineUnarySimd(CallInfo& callInfo, JSNative native,
MSimdUnaryArith::Operation op, SimdTypeDescr::Type type);
InliningStatus inlineSimdWith(CallInfo& callInfo, JSNative native, SimdLane lane,
SimdTypeDescr::Type type);
InliningStatus inlineSimdReplaceLane(CallInfo& callInfo, JSNative native,
SimdTypeDescr::Type type);
InliningStatus inlineSimdSplat(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type);
InliningStatus inlineSimdShuffle(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type,
unsigned numVectors, unsigned numLanes);

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

@ -313,17 +313,10 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
COMP_COMMONX4_TO_INT32X4_SIMD_OP(INLINE_SIMD_COMPARISON_)
#undef INLINE_SIMD_COMPARISON_
#define INLINE_SIMD_SETTER_(LANE) \
if (native == js::simd_int32x4_with##LANE) \
return inlineSimdWith(callInfo, native, SimdLane::Lane##LANE, SimdTypeDescr::Int32x4); \
if (native == js::simd_float32x4_with##LANE) \
return inlineSimdWith(callInfo, native, SimdLane::Lane##LANE, SimdTypeDescr::Float32x4);
INLINE_SIMD_SETTER_(X)
INLINE_SIMD_SETTER_(Y)
INLINE_SIMD_SETTER_(Z)
INLINE_SIMD_SETTER_(W)
#undef INLINE_SIMD_SETTER_
if (native == js::simd_int32x4_replaceLane)
return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Int32x4);
if (native == js::simd_float32x4_replaceLane)
return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Float32x4);
if (native == js::simd_int32x4_not)
return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Int32x4);
@ -3216,17 +3209,22 @@ IonBuilder::inlineSimdSplat(CallInfo& callInfo, JSNative native, SimdTypeDescr::
}
IonBuilder::InliningStatus
IonBuilder::inlineSimdWith(CallInfo& callInfo, JSNative native, SimdLane lane,
SimdTypeDescr::Type type)
IonBuilder::inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type)
{
InlineTypedObject* templateObj = nullptr;
if (!checkInlineSimd(callInfo, native, type, 2, &templateObj))
if (!checkInlineSimd(callInfo, native, type, 3, &templateObj))
return InliningStatus_NotInlined;
MDefinition* arg = callInfo.getArg(1);
if (!arg->isConstantValue() || arg->type() != MIRType_Int32)
return InliningStatus_NotInlined;
int32_t lane = arg->constantValue().toInt32();
if (lane < 0 || lane >= 4)
return InliningStatus_NotInlined;
// See comment in inlineBinarySimd
MIRType mirType = SimdTypeDescrToMIRType(type);
MSimdInsertElement* ins = MSimdInsertElement::New(alloc(), callInfo.getArg(0),
callInfo.getArg(1), mirType, lane);
callInfo.getArg(2), mirType, SimdLane(lane));
return boxSimd(callInfo, ins, templateObj);
}

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

@ -1389,14 +1389,6 @@ RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const
if (val.isUndefined())
continue;
// In order to simplify the code, we do not have a
// MStoreUnboxedBoolean, but we reuse the MStoreUnboxedScalar code.
// This has a nasty side-effect of add a MTruncate which coerce the
// boolean into an Int32. The following code check that if the
// property was expected to be a boolean, then we coerce it here.
if (properties[i].type == JSVAL_TYPE_BOOLEAN)
val.setBoolean(val.toInt32() != 0);
id = NameToId(properties[i].name);
ObjectOpResult result;

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

@ -1240,12 +1240,11 @@ ArrayMemoryView::visitArrayLength(MArrayLength* ins)
}
bool
ScalarReplacement(MIRGenerator* mir, MIRGraph& graph, bool* success)
ScalarReplacement(MIRGenerator* mir, MIRGraph& graph)
{
EmulateStateOf<ObjectMemoryView> replaceObject(mir, graph);
EmulateStateOf<ArrayMemoryView> replaceArray(mir, graph);
bool addedPhi = false;
*success = false;
for (ReversePostorderIterator block = graph.rpoBegin(); block != graph.rpoEnd(); block++) {
if (mir->shouldCancel("Scalar Replacement (main loop)"))
@ -1259,7 +1258,6 @@ ScalarReplacement(MIRGenerator* mir, MIRGraph& graph, bool* success)
if (!replaceObject.run(view))
return false;
view.assertSuccess();
*success = true;
addedPhi = true;
continue;
}
@ -1269,7 +1267,6 @@ ScalarReplacement(MIRGenerator* mir, MIRGraph& graph, bool* success)
if (!replaceArray.run(view))
return false;
view.assertSuccess();
*success = true;
addedPhi = true;
continue;
}

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