зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
5c359d92c8
|
@ -2072,7 +2072,10 @@ DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
|||
int32_t aIdxInParent)
|
||||
{
|
||||
Accessible* oldParent = aChild->Parent();
|
||||
NS_PRECONDITION(oldParent, "No parent?");
|
||||
if (!oldParent) {
|
||||
NS_ERROR("No parent? The tree is broken!");
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t oldIdxInParent = aChild->IndexInParent();
|
||||
|
||||
|
@ -2160,6 +2163,10 @@ DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
|
|||
// If the child is in the tree then remove it from the owner.
|
||||
if (child->IsInDocument()) {
|
||||
Accessible* owner = child->Parent();
|
||||
if (!owner) {
|
||||
NS_ERROR("Cannot put the child back. No parent, a broken tree.");
|
||||
continue;
|
||||
}
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(child, child->GetContent(), false);
|
||||
|
|
|
@ -1028,6 +1028,10 @@ pref("apz.fling_friction", "0.0019");
|
|||
pref("apz.max_velocity_inches_per_ms", "0.07");
|
||||
pref("apz.touch_start_tolerance", "0.1");
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
pref("apz.touch_move_tolerance", "0.03");
|
||||
#endif
|
||||
|
||||
// Tweak default displayport values to reduce the risk of running out of
|
||||
// memory when zooming in
|
||||
pref("apz.x_skate_size_multiplier", "1.25");
|
||||
|
|
|
@ -30,6 +30,7 @@ NodeToParentOffset(nsINode* aNode, int32_t* aOffset)
|
|||
|
||||
if (parent) {
|
||||
*aOffset = parent->IndexOf(aNode);
|
||||
NS_WARN_IF(*aOffset < 0);
|
||||
}
|
||||
|
||||
return parent;
|
||||
|
@ -44,7 +45,7 @@ NodeIsInTraversalRange(nsINode* aNode, bool aIsPreMode,
|
|||
nsINode* aStartNode, int32_t aStartOffset,
|
||||
nsINode* aEndNode, int32_t aEndOffset)
|
||||
{
|
||||
if (!aStartNode || !aEndNode || !aNode) {
|
||||
if (NS_WARN_IF(!aStartNode) || NS_WARN_IF(!aEndNode) || NS_WARN_IF(!aNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -71,6 +72,7 @@ NodeIsInTraversalRange(nsINode* aNode, bool aIsPreMode,
|
|||
}
|
||||
|
||||
int32_t indx = parent->IndexOf(aNode);
|
||||
NS_WARN_IF(indx == -1);
|
||||
|
||||
if (!aIsPreMode) {
|
||||
++indx;
|
||||
|
@ -262,7 +264,7 @@ nsContentIterator::~nsContentIterator()
|
|||
nsresult
|
||||
nsContentIterator::Init(nsINode* aRoot)
|
||||
{
|
||||
if (!aRoot) {
|
||||
if (NS_WARN_IF(!aRoot)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
|
@ -272,8 +274,10 @@ nsContentIterator::Init(nsINode* aRoot)
|
|||
if (mPre) {
|
||||
mFirst = aRoot;
|
||||
mLast = GetDeepLastChild(aRoot);
|
||||
NS_WARN_IF(!mLast);
|
||||
} else {
|
||||
mFirst = GetDeepFirstChild(aRoot);
|
||||
NS_WARN_IF(!mFirst);
|
||||
mLast = aRoot;
|
||||
}
|
||||
|
||||
|
@ -286,24 +290,34 @@ nsContentIterator::Init(nsINode* aRoot)
|
|||
nsresult
|
||||
nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aDOMRange);
|
||||
if (NS_WARN_IF(!aDOMRange)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
nsRange* range = static_cast<nsRange*>(aDOMRange);
|
||||
|
||||
mIsDone = false;
|
||||
|
||||
// get common content parent
|
||||
mCommonParent = range->GetCommonAncestor();
|
||||
NS_ENSURE_TRUE(mCommonParent, NS_ERROR_FAILURE);
|
||||
if (NS_WARN_IF(!mCommonParent)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// get the start node and offset
|
||||
int32_t startIndx = range->StartOffset();
|
||||
NS_WARN_IF(startIndx < 0);
|
||||
nsINode* startNode = range->GetStartParent();
|
||||
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
|
||||
if (NS_WARN_IF(!startNode)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// get the end node and offset
|
||||
int32_t endIndx = range->EndOffset();
|
||||
NS_WARN_IF(endIndx < 0);
|
||||
nsINode* endNode = range->GetEndParent();
|
||||
NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
|
||||
if (NS_WARN_IF(!endNode)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool startIsData = startNode->IsNodeOfType(nsINode::eDATA_NODE);
|
||||
|
||||
|
@ -327,7 +341,8 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
|||
mLast = mFirst;
|
||||
mCurNode = mFirst;
|
||||
|
||||
RebuildIndexStack();
|
||||
nsresult rv = RebuildIndexStack();
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
@ -338,6 +353,7 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
|||
|
||||
if (!startIsData && startNode->HasChildren()) {
|
||||
cChild = startNode->GetChildAt(startIndx);
|
||||
NS_WARN_IF(!cChild);
|
||||
}
|
||||
|
||||
if (!cChild) {
|
||||
|
@ -356,11 +372,13 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
|||
// In other words, if the offset is 1, the node should be ignored.
|
||||
if (!startIsData && startIndx) {
|
||||
mFirst = GetNextSibling(startNode);
|
||||
NS_WARN_IF(!mFirst);
|
||||
|
||||
// Does mFirst node really intersect the range? The range could be
|
||||
// 'degenerate', i.e., not collapsed but still contain no content.
|
||||
if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, startNode,
|
||||
startIndx, endNode, endIndx)) {
|
||||
if (mFirst &&
|
||||
NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre, startNode,
|
||||
startIndx, endNode, endIndx))) {
|
||||
mFirst = nullptr;
|
||||
}
|
||||
} else {
|
||||
|
@ -368,11 +386,11 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
|||
}
|
||||
} else {
|
||||
// post-order
|
||||
if (startNode->IsContent()) {
|
||||
mFirst = startNode->AsContent();
|
||||
} else {
|
||||
if (NS_WARN_IF(!startNode->IsContent())) {
|
||||
// What else can we do?
|
||||
mFirst = nullptr;
|
||||
} else {
|
||||
mFirst = startNode->AsContent();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -381,12 +399,14 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
|||
} else {
|
||||
// post-order
|
||||
mFirst = GetDeepFirstChild(cChild);
|
||||
NS_WARN_IF(!mFirst);
|
||||
|
||||
// Does mFirst node really intersect the range? The range could be
|
||||
// 'degenerate', i.e., not collapsed but still contain no content.
|
||||
|
||||
if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, startNode, startIndx,
|
||||
endNode, endIndx)) {
|
||||
if (mFirst &&
|
||||
NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre, startNode, startIndx,
|
||||
endNode, endIndx))) {
|
||||
mFirst = nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -399,22 +419,24 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
|||
|
||||
if (endIsData || !endNode->HasChildren() || endIndx == 0) {
|
||||
if (mPre) {
|
||||
if (endNode->IsContent()) {
|
||||
if (NS_WARN_IF(!endNode->IsContent())) {
|
||||
// Not much else to do here...
|
||||
mLast = nullptr;
|
||||
} else {
|
||||
// If the end node is an empty element and the end offset is 0,
|
||||
// the last element should be the previous node (i.e., shouldn't
|
||||
// include the end node in the range).
|
||||
if (!endIsData && !endNode->HasChildren() && !endIndx) {
|
||||
mLast = GetPrevSibling(endNode);
|
||||
if (!NodeIsInTraversalRange(mLast, mPre, startNode, startIndx,
|
||||
endNode, endIndx)) {
|
||||
NS_WARN_IF(!mLast);
|
||||
if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
|
||||
startNode, startIndx,
|
||||
endNode, endIndx))) {
|
||||
mLast = nullptr;
|
||||
}
|
||||
} else {
|
||||
mLast = endNode->AsContent();
|
||||
}
|
||||
} else {
|
||||
// Not much else to do here...
|
||||
mLast = nullptr;
|
||||
}
|
||||
} else {
|
||||
// post-order
|
||||
|
@ -424,9 +446,11 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
|||
|
||||
if (!endIsData) {
|
||||
mLast = GetPrevSibling(endNode);
|
||||
NS_WARN_IF(!mLast);
|
||||
|
||||
if (!NodeIsInTraversalRange(mLast, mPre, startNode, startIndx,
|
||||
endNode, endIndx)) {
|
||||
if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
|
||||
startNode, startIndx,
|
||||
endNode, endIndx))) {
|
||||
mLast = nullptr;
|
||||
}
|
||||
} else {
|
||||
|
@ -438,7 +462,7 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
|||
|
||||
cChild = endNode->GetChildAt(--indx);
|
||||
|
||||
if (!cChild) {
|
||||
if (NS_WARN_IF(!cChild)) {
|
||||
// No child at offset!
|
||||
NS_NOTREACHED("nsContentIterator::nsContentIterator");
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -446,9 +470,11 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
|||
|
||||
if (mPre) {
|
||||
mLast = GetDeepLastChild(cChild);
|
||||
NS_WARN_IF(!mLast);
|
||||
|
||||
if (!NodeIsInTraversalRange(mLast, mPre, startNode, startIndx,
|
||||
endNode, endIndx)) {
|
||||
if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
|
||||
startNode, startIndx,
|
||||
endNode, endIndx))) {
|
||||
mLast = nullptr;
|
||||
}
|
||||
} else {
|
||||
|
@ -459,7 +485,7 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
|||
|
||||
// If either first or last is null, they both have to be null!
|
||||
|
||||
if (!mFirst || !mLast) {
|
||||
if (NS_WARN_IF(!mFirst) || NS_WARN_IF(!mLast)) {
|
||||
mFirst = nullptr;
|
||||
mLast = nullptr;
|
||||
}
|
||||
|
@ -470,7 +496,8 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
|
|||
if (!mCurNode) {
|
||||
mIndexes.Clear();
|
||||
} else {
|
||||
RebuildIndexStack();
|
||||
nsresult rv = RebuildIndexStack();
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -499,7 +526,7 @@ nsContentIterator::RebuildIndexStack()
|
|||
while (current != mCommonParent) {
|
||||
parent = current->GetParentNode();
|
||||
|
||||
if (!parent) {
|
||||
if (NS_WARN_IF(!parent)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -526,7 +553,7 @@ nsINode*
|
|||
nsContentIterator::GetDeepFirstChild(nsINode* aRoot,
|
||||
nsTArray<int32_t>* aIndexes)
|
||||
{
|
||||
if (!aRoot || !aRoot->HasChildren()) {
|
||||
if (NS_WARN_IF(!aRoot) || !aRoot->HasChildren()) {
|
||||
return aRoot;
|
||||
}
|
||||
// We can't pass aRoot itself to the full GetDeepFirstChild, because that
|
||||
|
@ -542,7 +569,7 @@ nsIContent*
|
|||
nsContentIterator::GetDeepFirstChild(nsIContent* aRoot,
|
||||
nsTArray<int32_t>* aIndexes)
|
||||
{
|
||||
if (!aRoot) {
|
||||
if (NS_WARN_IF(!aRoot)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -565,7 +592,7 @@ nsINode*
|
|||
nsContentIterator::GetDeepLastChild(nsINode* aRoot,
|
||||
nsTArray<int32_t>* aIndexes)
|
||||
{
|
||||
if (!aRoot || !aRoot->HasChildren()) {
|
||||
if (NS_WARN_IF(!aRoot) || !aRoot->HasChildren()) {
|
||||
return aRoot;
|
||||
}
|
||||
// We can't pass aRoot itself to the full GetDeepLastChild, because that will
|
||||
|
@ -581,7 +608,7 @@ nsIContent*
|
|||
nsContentIterator::GetDeepLastChild(nsIContent* aRoot,
|
||||
nsTArray<int32_t>* aIndexes)
|
||||
{
|
||||
if (!aRoot) {
|
||||
if (NS_WARN_IF(!aRoot)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -607,12 +634,12 @@ nsIContent*
|
|||
nsContentIterator::GetNextSibling(nsINode* aNode,
|
||||
nsTArray<int32_t>* aIndexes)
|
||||
{
|
||||
if (!aNode) {
|
||||
if (NS_WARN_IF(!aNode)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsINode* parent = aNode->GetParentNode();
|
||||
if (!parent) {
|
||||
if (NS_WARN_IF(!parent)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -626,6 +653,7 @@ nsContentIterator::GetNextSibling(nsINode* aNode,
|
|||
} else {
|
||||
indx = mCachedIndex;
|
||||
}
|
||||
NS_WARN_IF(indx < 0);
|
||||
|
||||
// reverify that the index of the current node hasn't changed.
|
||||
// not super cheap, but a lot cheaper than IndexOf(), and still O(1).
|
||||
|
@ -634,6 +662,7 @@ nsContentIterator::GetNextSibling(nsINode* aNode,
|
|||
if (sib != aNode) {
|
||||
// someone changed our index - find the new index the painful way
|
||||
indx = parent->IndexOf(aNode);
|
||||
NS_WARN_IF(indx < 0);
|
||||
}
|
||||
|
||||
// indx is now canonically correct
|
||||
|
@ -668,12 +697,12 @@ nsIContent*
|
|||
nsContentIterator::GetPrevSibling(nsINode* aNode,
|
||||
nsTArray<int32_t>* aIndexes)
|
||||
{
|
||||
if (!aNode) {
|
||||
if (NS_WARN_IF(!aNode)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsINode* parent = aNode->GetParentNode();
|
||||
if (!parent) {
|
||||
if (NS_WARN_IF(!parent)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -694,6 +723,7 @@ nsContentIterator::GetPrevSibling(nsINode* aNode,
|
|||
if (sib != aNode) {
|
||||
// someone changed our index - find the new index the painful way
|
||||
indx = parent->IndexOf(aNode);
|
||||
NS_WARN_IF(indx < 0);
|
||||
}
|
||||
|
||||
// indx is now canonically correct
|
||||
|
@ -725,6 +755,7 @@ nsContentIterator::NextNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
|
|||
// if it has children then next node is first child
|
||||
if (node->HasChildren()) {
|
||||
nsIContent* firstChild = node->GetFirstChild();
|
||||
MOZ_ASSERT(firstChild);
|
||||
|
||||
// update cache
|
||||
if (aIndexes) {
|
||||
|
@ -743,6 +774,7 @@ nsContentIterator::NextNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
|
|||
|
||||
// post-order
|
||||
nsINode* parent = node->GetParentNode();
|
||||
NS_WARN_IF(!parent);
|
||||
nsIContent* sibling = nullptr;
|
||||
int32_t indx = 0;
|
||||
|
||||
|
@ -765,6 +797,7 @@ nsContentIterator::NextNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
|
|||
if (sibling != node) {
|
||||
// someone changed our index - find the new index the painful way
|
||||
indx = parent->IndexOf(node);
|
||||
NS_WARN_IF(indx < 0);
|
||||
}
|
||||
|
||||
// indx is now canonically correct
|
||||
|
@ -806,6 +839,7 @@ nsContentIterator::PrevNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
|
|||
// if we are a Pre-order iterator, use pre-order
|
||||
if (mPre) {
|
||||
nsINode* parent = node->GetParentNode();
|
||||
NS_WARN_IF(!parent);
|
||||
nsIContent* sibling = nullptr;
|
||||
int32_t indx = 0;
|
||||
|
||||
|
@ -824,11 +858,13 @@ nsContentIterator::PrevNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
|
|||
// this time - the index may now be out of range.
|
||||
if (indx >= 0) {
|
||||
sibling = parent->GetChildAt(indx);
|
||||
NS_WARN_IF(!sibling);
|
||||
}
|
||||
|
||||
if (sibling != node) {
|
||||
// someone changed our index - find the new index the painful way
|
||||
indx = parent->IndexOf(node);
|
||||
NS_WARN_IF(indx < 0);
|
||||
}
|
||||
|
||||
// indx is now canonically correct
|
||||
|
@ -858,10 +894,12 @@ nsContentIterator::PrevNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
|
|||
|
||||
// post-order
|
||||
int32_t numChildren = node->GetChildCount();
|
||||
NS_WARN_IF(numChildren < 0);
|
||||
|
||||
// if it has children then prev node is last child
|
||||
if (numChildren) {
|
||||
nsIContent* lastChild = node->GetLastChild();
|
||||
NS_WARN_IF(!lastChild);
|
||||
numChildren--;
|
||||
|
||||
// update cache
|
||||
|
@ -887,11 +925,7 @@ void
|
|||
nsContentIterator::First()
|
||||
{
|
||||
if (mFirst) {
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
#endif
|
||||
PositionAt(mFirst);
|
||||
|
||||
DebugOnly<nsresult> rv = PositionAt(mFirst);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to position iterator!");
|
||||
}
|
||||
|
||||
|
@ -905,11 +939,7 @@ nsContentIterator::Last()
|
|||
NS_ASSERTION(mLast, "No last node!");
|
||||
|
||||
if (mLast) {
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
#endif
|
||||
PositionAt(mLast);
|
||||
|
||||
DebugOnly<nsresult> rv = PositionAt(mLast);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to position iterator!");
|
||||
}
|
||||
|
||||
|
@ -920,7 +950,7 @@ nsContentIterator::Last()
|
|||
void
|
||||
nsContentIterator::Next()
|
||||
{
|
||||
if (mIsDone || !mCurNode) {
|
||||
if (mIsDone || NS_WARN_IF(!mCurNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -936,7 +966,7 @@ nsContentIterator::Next()
|
|||
void
|
||||
nsContentIterator::Prev()
|
||||
{
|
||||
if (mIsDone || !mCurNode) {
|
||||
if (NS_WARN_IF(mIsDone) || NS_WARN_IF(!mCurNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -961,7 +991,7 @@ nsContentIterator::IsDone()
|
|||
nsresult
|
||||
nsContentIterator::PositionAt(nsINode* aCurNode)
|
||||
{
|
||||
if (!aCurNode) {
|
||||
if (NS_WARN_IF(!aCurNode)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
|
@ -984,11 +1014,15 @@ nsContentIterator::PositionAt(nsINode* aCurNode)
|
|||
if (firstNode && lastNode) {
|
||||
if (mPre) {
|
||||
firstNode = NodeToParentOffset(mFirst, &firstOffset);
|
||||
NS_WARN_IF(!firstNode);
|
||||
NS_WARN_IF(firstOffset < 0);
|
||||
|
||||
if (lastNode->GetChildCount()) {
|
||||
lastOffset = 0;
|
||||
} else {
|
||||
lastNode = NodeToParentOffset(mLast, &lastOffset);
|
||||
NS_WARN_IF(!lastNode);
|
||||
NS_WARN_IF(lastOffset < 0);
|
||||
++lastOffset;
|
||||
}
|
||||
} else {
|
||||
|
@ -996,11 +1030,16 @@ nsContentIterator::PositionAt(nsINode* aCurNode)
|
|||
|
||||
if (numChildren) {
|
||||
firstOffset = numChildren;
|
||||
NS_WARN_IF(firstOffset < 0);
|
||||
} else {
|
||||
firstNode = NodeToParentOffset(mFirst, &firstOffset);
|
||||
NS_WARN_IF(!firstNode);
|
||||
NS_WARN_IF(firstOffset < 0);
|
||||
}
|
||||
|
||||
lastNode = NodeToParentOffset(mLast, &lastOffset);
|
||||
NS_WARN_IF(!lastNode);
|
||||
NS_WARN_IF(lastOffset < 0);
|
||||
++lastOffset;
|
||||
}
|
||||
}
|
||||
|
@ -1009,9 +1048,10 @@ nsContentIterator::PositionAt(nsINode* aCurNode)
|
|||
// need to allow that or 'iter->Init(root)' would assert in Last() or First()
|
||||
// for example, bug 327694.
|
||||
if (mFirst != mCurNode && mLast != mCurNode &&
|
||||
(!firstNode || !lastNode ||
|
||||
!NodeIsInTraversalRange(mCurNode, mPre, firstNode, firstOffset,
|
||||
lastNode, lastOffset))) {
|
||||
(NS_WARN_IF(!firstNode) || NS_WARN_IF(!lastNode) ||
|
||||
NS_WARN_IF(!NodeIsInTraversalRange(mCurNode, mPre,
|
||||
firstNode, firstOffset,
|
||||
lastNode, lastOffset)))) {
|
||||
mIsDone = true;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -1040,7 +1080,7 @@ nsContentIterator::PositionAt(nsINode* aCurNode)
|
|||
|
||||
nsINode* parent = tempNode->GetParentNode();
|
||||
|
||||
if (!parent) {
|
||||
if (NS_WARN_IF(!parent)) {
|
||||
// this node has no parent, and thus no index
|
||||
break;
|
||||
}
|
||||
|
@ -1060,12 +1100,13 @@ nsContentIterator::PositionAt(nsINode* aCurNode)
|
|||
while (newCurNode) {
|
||||
nsINode* parent = newCurNode->GetParentNode();
|
||||
|
||||
if (!parent) {
|
||||
if (NS_WARN_IF(!parent)) {
|
||||
// this node has no parent, and thus no index
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t indx = parent->IndexOf(newCurNode);
|
||||
NS_WARN_IF(indx < 0);
|
||||
|
||||
// insert at the head!
|
||||
newIndexes.InsertElementAt(0, indx);
|
||||
|
|
|
@ -1498,13 +1498,19 @@ nsDocument::nsDocument(const char* aContentType)
|
|||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
ClearAllBoxObjects(nsIContent* aKey, nsPIBoxObject* aBoxObject, void* aUserArg)
|
||||
void
|
||||
nsDocument::ClearAllBoxObjects()
|
||||
{
|
||||
if (aBoxObject) {
|
||||
aBoxObject->Clear();
|
||||
if (mBoxObjectTable) {
|
||||
for (auto iter = mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) {
|
||||
nsPIBoxObject* boxObject = iter.UserData();
|
||||
if (boxObject) {
|
||||
boxObject->Clear();
|
||||
}
|
||||
}
|
||||
delete mBoxObjectTable;
|
||||
mBoxObjectTable = nullptr;
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsIDocument::~nsIDocument()
|
||||
|
@ -1653,10 +1659,7 @@ nsDocument::~nsDocument()
|
|||
|
||||
delete mHeaderData;
|
||||
|
||||
if (mBoxObjectTable) {
|
||||
mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
|
||||
delete mBoxObjectTable;
|
||||
}
|
||||
ClearAllBoxObjects();
|
||||
|
||||
mPendingTitleChangeEvent.Revoke();
|
||||
|
||||
|
@ -1746,39 +1749,6 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
|
|||
return Element::CanSkipThis(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
|
||||
|
||||
static PLDHashOperator
|
||||
RadioGroupsTraverser(const nsAString& aKey, nsRadioGroupStruct* aData,
|
||||
void* aClosure)
|
||||
{
|
||||
nsCycleCollectionTraversalCallback *cb =
|
||||
static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
|
||||
"mRadioGroups entry->mSelectedRadioButton");
|
||||
cb->NoteXPCOMChild(ToSupports(aData->mSelectedRadioButton));
|
||||
|
||||
uint32_t i, count = aData->mRadioButtons.Count();
|
||||
for (i = 0; i < count; ++i) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
|
||||
"mRadioGroups entry->mRadioButtons[i]");
|
||||
cb->NoteXPCOMChild(aData->mRadioButtons[i]);
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
BoxObjectTraverser(nsIContent* key, nsPIBoxObject* boxObject, void* userArg)
|
||||
{
|
||||
nsCycleCollectionTraversalCallback *cb =
|
||||
static_cast<nsCycleCollectionTraversalCallback*>(userArg);
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mBoxObjectTable entry");
|
||||
cb->NoteXPCOMChild(boxObject);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static const char* kNSURIs[] = {
|
||||
"([none])",
|
||||
"(xmlns)",
|
||||
|
@ -1855,12 +1825,27 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImportManager)
|
||||
|
||||
tmp->mRadioGroups.EnumerateRead(RadioGroupsTraverser, &cb);
|
||||
for (auto iter = tmp->mRadioGroups.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsRadioGroupStruct* radioGroup = iter.UserData();
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
|
||||
cb, "mRadioGroups entry->mSelectedRadioButton");
|
||||
cb.NoteXPCOMChild(ToSupports(radioGroup->mSelectedRadioButton));
|
||||
|
||||
uint32_t i, count = radioGroup->mRadioButtons.Count();
|
||||
for (i = 0; i < count; ++i) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
|
||||
cb, "mRadioGroups entry->mRadioButtons[i]");
|
||||
cb.NoteXPCOMChild(radioGroup->mRadioButtons[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// The boxobject for an element will only exist as long as it's in the
|
||||
// document, so we'll traverse the table here instead of from the element.
|
||||
if (tmp->mBoxObjectTable) {
|
||||
tmp->mBoxObjectTable->EnumerateRead(BoxObjectTraverser, &cb);
|
||||
for (auto iter = tmp->mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mBoxObjectTable entry");
|
||||
cb.NoteXPCOMChild(iter.UserData());
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
|
||||
|
@ -1981,12 +1966,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
|
||||
|
||||
|
||||
if (tmp->mBoxObjectTable) {
|
||||
tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
|
||||
delete tmp->mBoxObjectTable;
|
||||
tmp->mBoxObjectTable = nullptr;
|
||||
}
|
||||
tmp->ClearAllBoxObjects();
|
||||
|
||||
if (tmp->mListenerManager) {
|
||||
tmp->mListenerManager->Disconnect();
|
||||
|
@ -3787,14 +3767,6 @@ nsIDocument::ShouldThrottleFrameRequests()
|
|||
return false;
|
||||
}
|
||||
|
||||
PLDHashOperator RequestDiscardEnumerator(imgIRequest* aKey,
|
||||
uint32_t aData,
|
||||
void* userArg)
|
||||
{
|
||||
aKey->RequestDiscard();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::DeleteShell()
|
||||
{
|
||||
|
@ -3809,7 +3781,9 @@ nsDocument::DeleteShell()
|
|||
// When our shell goes away, request that all our images be immediately
|
||||
// discarded, so we don't carry around decoded image data for a document we
|
||||
// no longer intend to paint.
|
||||
mImageTracker.EnumerateRead(RequestDiscardEnumerator, nullptr);
|
||||
for (auto iter = mImageTracker.Iter(); !iter.Done(); iter.Next()) {
|
||||
iter.Key()->RequestDiscard();
|
||||
}
|
||||
|
||||
// Now that we no longer have a shell, we need to forget about any FontFace
|
||||
// objects for @font-face rules that came from the style set.
|
||||
|
@ -10567,23 +10541,6 @@ nsDocument::NotifyMediaFeatureValuesChanged()
|
|||
}
|
||||
}
|
||||
|
||||
PLDHashOperator LockEnumerator(imgIRequest* aKey,
|
||||
uint32_t aData,
|
||||
void* userArg)
|
||||
{
|
||||
aKey->LockImage();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator UnlockEnumerator(imgIRequest* aKey,
|
||||
uint32_t aData,
|
||||
void* userArg)
|
||||
{
|
||||
aKey->UnlockImage();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDocument::SetImageLockingState(bool aLocked)
|
||||
{
|
||||
|
@ -10597,9 +10554,14 @@ nsDocument::SetImageLockingState(bool aLocked)
|
|||
return NS_OK;
|
||||
|
||||
// Otherwise, iterate over our images and perform the appropriate action.
|
||||
mImageTracker.EnumerateRead(aLocked ? LockEnumerator
|
||||
: UnlockEnumerator,
|
||||
nullptr);
|
||||
for (auto iter = mImageTracker.Iter(); !iter.Done(); iter.Next()) {
|
||||
imgIRequest* image = iter.Key();
|
||||
if (aLocked) {
|
||||
image->LockImage();
|
||||
} else {
|
||||
image->UnlockImage();
|
||||
}
|
||||
}
|
||||
|
||||
// Update state.
|
||||
mLockingImages = aLocked;
|
||||
|
@ -10607,22 +10569,6 @@ nsDocument::SetImageLockingState(bool aLocked)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
PLDHashOperator IncrementAnimationEnumerator(imgIRequest* aKey,
|
||||
uint32_t aData,
|
||||
void* userArg)
|
||||
{
|
||||
aKey->IncrementAnimationConsumers();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator DecrementAnimationEnumerator(imgIRequest* aKey,
|
||||
uint32_t aData,
|
||||
void* userArg)
|
||||
{
|
||||
aKey->DecrementAnimationConsumers();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::SetImagesNeedAnimating(bool aAnimating)
|
||||
{
|
||||
|
@ -10631,9 +10577,14 @@ nsDocument::SetImagesNeedAnimating(bool aAnimating)
|
|||
return;
|
||||
|
||||
// Otherwise, iterate over our images and perform the appropriate action.
|
||||
mImageTracker.EnumerateRead(aAnimating ? IncrementAnimationEnumerator
|
||||
: DecrementAnimationEnumerator,
|
||||
nullptr);
|
||||
for (auto iter = mImageTracker.Iter(); !iter.Done(); iter.Next()) {
|
||||
imgIRequest* image = iter.Key();
|
||||
if (aAnimating) {
|
||||
image->IncrementAnimationConsumers();
|
||||
} else {
|
||||
image->DecrementAnimationConsumers();
|
||||
}
|
||||
}
|
||||
|
||||
// Update state.
|
||||
mAnimatingImages = aAnimating;
|
||||
|
|
|
@ -1767,6 +1767,8 @@ private:
|
|||
// requestAnimationFrame, if it's OK to do so.
|
||||
void MaybeRescheduleAnimationFrameNotifications();
|
||||
|
||||
void ClearAllBoxObjects();
|
||||
|
||||
// Returns true if the scheme for the url for this document is "about"
|
||||
bool IsAboutPage();
|
||||
|
||||
|
|
|
@ -294,6 +294,7 @@ GK_ATOM(destructor, "destructor")
|
|||
GK_ATOM(details, "details")
|
||||
GK_ATOM(deviceAspectRatio, "device-aspect-ratio")
|
||||
GK_ATOM(deviceHeight, "device-height")
|
||||
GK_ATOM(devicePixelRatio, "device-pixel-ratio")
|
||||
GK_ATOM(deviceWidth, "device-width")
|
||||
GK_ATOM(dfn, "dfn")
|
||||
GK_ATOM(dialog, "dialog")
|
||||
|
|
|
@ -26,6 +26,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=976673
|
|||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// In e10s mode, ContentCacheInChild tries to retrieve selected text and
|
||||
// caret position when IMEContentObserver notifies IME of focus. At this time,
|
||||
// we hit assertion in nsContentIterator.
|
||||
SimpleTest.expectAssertions(0, 6);
|
||||
|
||||
window.addEventListener("mousedown", function (aEvent) { aEvent.preventDefault(); }, false);
|
||||
|
||||
function testSetFocus(aEventType, aCallback)
|
||||
|
|
|
@ -135,6 +135,12 @@ ThrowMethodFailed(JSContext* cx, ErrorResult& rv)
|
|||
// uncatchable exception.
|
||||
return false;
|
||||
}
|
||||
if (rv.IsJSContextException()) {
|
||||
// Whatever we need to throw is on the JSContext already. We
|
||||
// can't assert that there is a pending exception on it, though,
|
||||
// because in the uncatchable exception case there won't be one.
|
||||
return false;
|
||||
}
|
||||
if (rv.IsErrorWithMessage()) {
|
||||
rv.ReportErrorWithMessage(cx);
|
||||
return false;
|
||||
|
|
|
@ -167,6 +167,17 @@ public:
|
|||
void ReportDOMException(JSContext* cx);
|
||||
bool IsDOMException() const { return ErrorCode() == NS_ERROR_DOM_DOMEXCEPTION; }
|
||||
|
||||
// Flag on the ErrorResult that whatever needs throwing has been
|
||||
// thrown on the JSContext already and we should not mess with it.
|
||||
void NoteJSContextException() {
|
||||
mResult = NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT;
|
||||
}
|
||||
// Check whether the ErrorResult says to just throw whatever is on
|
||||
// the JSContext already.
|
||||
bool IsJSContextException() {
|
||||
return ErrorCode() == NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT;
|
||||
}
|
||||
|
||||
// Report a generic error. This should only be used if we're not
|
||||
// some more specific exception type.
|
||||
void ReportGenericError(JSContext* cx);
|
||||
|
@ -274,6 +285,8 @@ private:
|
|||
MOZ_ASSERT(aRv != NS_ERROR_DOM_DOMEXCEPTION, "Use ThrowDOMException()");
|
||||
MOZ_ASSERT(!IsDOMException(), "Don't overwrite DOM exceptions");
|
||||
MOZ_ASSERT(aRv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "May need to bring back ThrowNotEnoughArgsError");
|
||||
MOZ_ASSERT(aRv != NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT,
|
||||
"Use NoteJSContextException");
|
||||
mResult = aRv;
|
||||
}
|
||||
|
||||
|
|
|
@ -473,6 +473,14 @@ EventStateManager::OnStopObservingContent(
|
|||
mIMEContentObserver = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
EventStateManager::TryToFlushPendingNotificationsToIME()
|
||||
{
|
||||
if (mIMEContentObserver) {
|
||||
mIMEContentObserver->TryToFlushPendingNotifications();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
||||
WidgetEvent* aEvent,
|
||||
|
|
|
@ -146,6 +146,12 @@ public:
|
|||
void OnStartToObserveContent(IMEContentObserver* aIMEContentObserver);
|
||||
void OnStopObservingContent(IMEContentObserver* aIMEContentObserver);
|
||||
|
||||
/**
|
||||
* TryToFlushPendingNotificationsToIME() suggests flushing pending
|
||||
* notifications to IME to IMEContentObserver.
|
||||
*/
|
||||
void TryToFlushPendingNotificationsToIME();
|
||||
|
||||
/**
|
||||
* Register accesskey on the given element. When accesskey is activated then
|
||||
* the element will be notified via nsIContent::PerformAccesskey() method.
|
||||
|
|
|
@ -202,7 +202,6 @@ IMEContentObserver::IMEContentObserver()
|
|||
, mNeedsToNotifyIMEOfTextChange(false)
|
||||
, mNeedsToNotifyIMEOfSelectionChange(false)
|
||||
, mNeedsToNotifyIMEOfPositionChange(false)
|
||||
, mIsFlushingPendingNotifications(false)
|
||||
, mIsHandlingQueryContentEvent(false)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
|
@ -251,26 +250,56 @@ IMEContentObserver::Init(nsIWidget* aWidget,
|
|||
}
|
||||
|
||||
if (firstInitialization) {
|
||||
// Now, try to send NOTIFY_IME_OF_FOCUS to IME via the widget.
|
||||
MaybeNotifyIMEOfFocusSet();
|
||||
|
||||
// While Init() notifies IME of focus, pending layout may be flushed
|
||||
// because the notification may cause querying content. Then, recursive
|
||||
// call of Init() with the latest content may be occur. In such case, we
|
||||
// shouldn't keep first initialization.
|
||||
if (GetState() != eState_Initializing) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
|
||||
// instance via IMEStateManager::UpdateIMEState(). So, this
|
||||
// instance might already have been destroyed, check it.
|
||||
if (!mRootContent) {
|
||||
return;
|
||||
}
|
||||
// When this is called first time, IME has not received NOTIFY_IME_OF_FOCUS
|
||||
// yet since NOTIFY_IME_OF_FOCUS will be sent to widget asynchronously.
|
||||
// So, we need to do nothing here. After NOTIFY_IME_OF_FOCUS has been
|
||||
// sent, OnIMEReceivedFocus() will be called and content, selection and/or
|
||||
// position changes will be observed
|
||||
return;
|
||||
}
|
||||
|
||||
// When this is called after editor reframing (i.e., the root editable node
|
||||
// is also recreated), IME has usually received NOTIFY_IME_OF_FOCUS. In this
|
||||
// case, we need to restart to observe content, selection and/or position
|
||||
// changes in new root editable node.
|
||||
ObserveEditableNode();
|
||||
|
||||
if (!NeedsToNotifyIMEOfSomething()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Some change events may wait to notify IME because this was being
|
||||
// initialized. It is the time to flush them.
|
||||
FlushMergeableNotifications();
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::OnIMEReceivedFocus()
|
||||
{
|
||||
// While Init() notifies IME of focus, pending layout may be flushed
|
||||
// because the notification may cause querying content. Then, recursive
|
||||
// call of Init() with the latest content may occur. In such case, we
|
||||
// shouldn't keep first initialization which notified IME of focus.
|
||||
if (GetState() != eState_Initializing) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
|
||||
// instance via IMEStateManager::UpdateIMEState(). So, this
|
||||
// instance might already have been destroyed, check it.
|
||||
if (!mRootContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start to observe which is needed by IME when IME actually has focus.
|
||||
ObserveEditableNode();
|
||||
|
||||
if (!NeedsToNotifyIMEOfSomething()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Some change events may wait to notify IME because this was being
|
||||
// initialized. It is the time to flush them.
|
||||
FlushMergeableNotifications();
|
||||
|
@ -404,6 +433,18 @@ IMEContentObserver::ObserveEditableNode()
|
|||
MOZ_RELEASE_ASSERT(mRootContent);
|
||||
MOZ_RELEASE_ASSERT(GetState() != eState_Observing);
|
||||
|
||||
// If this is called before sending NOTIFY_IME_OF_FOCUS (it's possible when
|
||||
// the editor is reframed before sending NOTIFY_IME_OF_FOCUS asynchronously),
|
||||
// the update preference of mWidget may be different from after the widget
|
||||
// receives NOTIFY_IME_OF_FOCUS. So, this should be called again by
|
||||
// OnIMEReceivedFocus() which is called after sending NOTIFY_IME_OF_FOCUS.
|
||||
if (!mIMEHasFocus) {
|
||||
MOZ_ASSERT(!mWidget || mNeedsToNotifyIMEOfFocusSet ||
|
||||
mSendingNotification == NOTIFY_IME_OF_FOCUS,
|
||||
"Wow, OnIMEReceivedFocus() won't be called?");
|
||||
return;
|
||||
}
|
||||
|
||||
mIsObserving = true;
|
||||
if (mEditor) {
|
||||
mEditor->AddEditorObserver(this);
|
||||
|
@ -668,9 +709,14 @@ nsresult
|
|||
IMEContentObserver::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
|
||||
{
|
||||
// If the instance has cache, it should use the cached selection which was
|
||||
// sent to the widget.
|
||||
// sent to the widget. However, if this instance has already received new
|
||||
// selection change notification but hasn't updated the cache yet (i.e.,
|
||||
// not sending selection change notification to IME, don't use the cached
|
||||
// value. Note that don't update selection cache here since if you update
|
||||
// selection cache here, IMENotificationSender won't notify IME of selection
|
||||
// change because it looks like that the selection isn't actually changed.
|
||||
if (aEvent->mMessage == eQuerySelectedText && aEvent->mUseNativeLineBreak &&
|
||||
mSelectionData.IsValid()) {
|
||||
mSelectionData.IsValid() && !mNeedsToNotifyIMEOfSelectionChange) {
|
||||
aEvent->mReply.mContentsRoot = mRootContent;
|
||||
aEvent->mReply.mHasSelection = !mSelectionData.IsCollapsed();
|
||||
aEvent->mReply.mOffset = mSelectionData.mOffset;
|
||||
|
@ -1314,7 +1360,7 @@ IMEContentObserver::FlushMergeableNotifications()
|
|||
// event. Then, it causes flushing layout which may cause another layout
|
||||
// change notification.
|
||||
|
||||
if (mIsFlushingPendingNotifications) {
|
||||
if (mQueuedSender) {
|
||||
// So, if this is already called, this should do nothing.
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), "
|
||||
|
@ -1336,14 +1382,32 @@ IMEContentObserver::FlushMergeableNotifications()
|
|||
("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), "
|
||||
"creating IMENotificationSender...", this));
|
||||
|
||||
mIsFlushingPendingNotifications = true;
|
||||
nsContentUtils::AddScriptRunner(new IMENotificationSender(this));
|
||||
// If contents in selection range is modified, the selection range still
|
||||
// has removed node from the tree. In such case, nsContentIterator won't
|
||||
// work well. Therefore, we shouldn't use AddScriptRunnder() here since
|
||||
// it may kick runnable event immediately after DOM tree is changed but
|
||||
// the selection range isn't modified yet.
|
||||
mQueuedSender = new IMENotificationSender(this);
|
||||
NS_DispatchToCurrentThread(mQueuedSender);
|
||||
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), "
|
||||
"finished", this));
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::TryToFlushPendingNotifications()
|
||||
{
|
||||
if (!mQueuedSender || mSendingNotification != NOTIFY_IME_OF_NOTHING) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::TryToFlushPendingNotifications(), "
|
||||
"performing queued IMENotificationSender forcibly", this));
|
||||
mQueuedSender->Run();
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* mozilla::IMEContentObserver::AChangeEvent
|
||||
******************************************************************************/
|
||||
|
@ -1410,7 +1474,20 @@ IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(
|
|||
NS_IMETHODIMP
|
||||
IMEContentObserver::IMENotificationSender::Run()
|
||||
{
|
||||
MOZ_ASSERT(mIMEContentObserver->mIsFlushingPendingNotifications);
|
||||
if (NS_WARN_IF(mIsRunning)) {
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Error,
|
||||
("IMECO: 0x%p IMEContentObserver::IMENotificationSender::Run(), FAILED, "
|
||||
"called recursively", this));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AutoRestore<bool> running(mIsRunning);
|
||||
mIsRunning = true;
|
||||
|
||||
// This instance was already performed forcibly.
|
||||
if (mIMEContentObserver->mQueuedSender != this) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// NOTE: Reset each pending flag because sending notification may cause
|
||||
// another change.
|
||||
|
@ -1418,10 +1495,25 @@ IMEContentObserver::IMENotificationSender::Run()
|
|||
if (mIMEContentObserver->mNeedsToNotifyIMEOfFocusSet) {
|
||||
mIMEContentObserver->mNeedsToNotifyIMEOfFocusSet = false;
|
||||
SendFocusSet();
|
||||
mIMEContentObserver->mQueuedSender = nullptr;
|
||||
// If it's not safe to notify IME of focus, SendFocusSet() sets
|
||||
// mNeedsToNotifyIMEOfFocusSet true again. For guaranteeing to send the
|
||||
// focus notification later, we should put a new sender into the queue but
|
||||
// this case must be rare. Note that if mIMEContentObserver is already
|
||||
// destroyed, mNeedsToNotifyIMEOfFocusSet is never set true again.
|
||||
if (mIMEContentObserver->mNeedsToNotifyIMEOfFocusSet) {
|
||||
MOZ_ASSERT(!mIMEContentObserver->mIMEHasFocus);
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::IMENotificationSender::Run(), "
|
||||
"posting IMENotificationSender to current thread", this));
|
||||
mIMEContentObserver->mQueuedSender =
|
||||
new IMENotificationSender(mIMEContentObserver);
|
||||
NS_DispatchToCurrentThread(mIMEContentObserver->mQueuedSender);
|
||||
return NS_OK;
|
||||
}
|
||||
// This is the first notification to IME. So, we don't need to notify
|
||||
// anymore since IME starts to query content after it gets focus.
|
||||
mIMEContentObserver->ClearPendingNotifications();
|
||||
mIMEContentObserver->mIsFlushingPendingNotifications = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1454,16 +1546,16 @@ IMEContentObserver::IMENotificationSender::Run()
|
|||
}
|
||||
}
|
||||
|
||||
mIMEContentObserver->mQueuedSender = nullptr;
|
||||
|
||||
// If notifications caused some new change, we should notify them now.
|
||||
mIMEContentObserver->mIsFlushingPendingNotifications =
|
||||
mIMEContentObserver->NeedsToNotifyIMEOfSomething();
|
||||
if (mIMEContentObserver->mIsFlushingPendingNotifications) {
|
||||
if (mIMEContentObserver->NeedsToNotifyIMEOfSomething()) {
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::IMENotificationSender::Run(), "
|
||||
"posting AsyncMergeableNotificationsFlusher to current thread", this));
|
||||
RefPtr<AsyncMergeableNotificationsFlusher> asyncFlusher =
|
||||
new AsyncMergeableNotificationsFlusher(mIMEContentObserver);
|
||||
NS_DispatchToCurrentThread(asyncFlusher);
|
||||
"posting IMENotificationSender to current thread", this));
|
||||
mIMEContentObserver->mQueuedSender =
|
||||
new IMENotificationSender(mIMEContentObserver);
|
||||
NS_DispatchToCurrentThread(mIMEContentObserver->mQueuedSender);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1505,6 +1597,12 @@ IMEContentObserver::IMENotificationSender::SendFocusSet()
|
|||
mIMEContentObserver->mWidget);
|
||||
mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_NOTHING;
|
||||
|
||||
// nsIMEUpdatePreference referred by ObserveEditableNode() may be different
|
||||
// before or after widget receives NOTIFY_IME_OF_FOCUS. Therefore, we need
|
||||
// to guarantee to call ObserveEditableNode() after sending
|
||||
// NOTIFY_IME_OF_FOCUS.
|
||||
mIMEContentObserver->OnIMEReceivedFocus();
|
||||
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::IMENotificationSender::"
|
||||
"SendFocusSet(), sent NOTIFY_IME_OF_FOCUS", this));
|
||||
|
@ -1668,31 +1766,4 @@ IMEContentObserver::IMENotificationSender::SendPositionChange()
|
|||
"SendPositionChange(), sent NOTIFY_IME_OF_POSITION_CHANGE", this));
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* mozilla::IMEContentObserver::AsyncMergeableNotificationsFlusher
|
||||
******************************************************************************/
|
||||
|
||||
NS_IMETHODIMP
|
||||
IMEContentObserver::AsyncMergeableNotificationsFlusher::Run()
|
||||
{
|
||||
if (!CanNotifyIME(eChangeEventType_FlushPendingEvents)) {
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::AsyncMergeableNotificationsFlusher::"
|
||||
"Run(), FAILED, due to impossible to flush pending notifications",
|
||||
this));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Info,
|
||||
("IMECO: 0x%p IMEContentObserver::AsyncMergeableNotificationsFlusher::"
|
||||
"Run(), calling FlushMergeableNotifications()...", this));
|
||||
|
||||
mIMEContentObserver->FlushMergeableNotifications();
|
||||
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::AsyncMergeableNotificationsFlusher::"
|
||||
"Run(), called FlushMergeableNotifications()", this));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -103,6 +103,12 @@ public:
|
|||
nsresult GetSelectionAndRoot(nsISelection** aSelection,
|
||||
nsIContent** aRoot) const;
|
||||
|
||||
/**
|
||||
* TryToFlushPendingNotifications() should be called when pending events
|
||||
* should be flushed. This tries to run the queued IMENotificationSender.
|
||||
*/
|
||||
void TryToFlushPendingNotifications();
|
||||
|
||||
private:
|
||||
~IMEContentObserver() {}
|
||||
|
||||
|
@ -117,6 +123,7 @@ private:
|
|||
nsIEditor* aEditor);
|
||||
bool InitWithPlugin(nsPresContext* aPresContext, nsIContent* aContent);
|
||||
bool IsInitializedWithPlugin() const { return !mEditor; }
|
||||
void OnIMEReceivedFocus();
|
||||
void Clear();
|
||||
bool IsObservingContent(nsPresContext* aPresContext,
|
||||
nsIContent* aContent) const;
|
||||
|
@ -182,6 +189,63 @@ private:
|
|||
nsCOMPtr<nsIDocShell> mDocShell;
|
||||
nsCOMPtr<nsIEditor> mEditor;
|
||||
|
||||
/**
|
||||
* Helper classes to notify IME.
|
||||
*/
|
||||
|
||||
class AChangeEvent: public nsRunnable
|
||||
{
|
||||
protected:
|
||||
enum ChangeEventType
|
||||
{
|
||||
eChangeEventType_Focus,
|
||||
eChangeEventType_Selection,
|
||||
eChangeEventType_Text,
|
||||
eChangeEventType_Position,
|
||||
eChangeEventType_FlushPendingEvents
|
||||
};
|
||||
|
||||
explicit AChangeEvent(IMEContentObserver* aIMEContentObserver)
|
||||
: mIMEContentObserver(aIMEContentObserver)
|
||||
{
|
||||
MOZ_ASSERT(mIMEContentObserver);
|
||||
}
|
||||
|
||||
RefPtr<IMEContentObserver> mIMEContentObserver;
|
||||
|
||||
/**
|
||||
* CanNotifyIME() checks if mIMEContentObserver can and should notify IME.
|
||||
*/
|
||||
bool CanNotifyIME(ChangeEventType aChangeEventType) const;
|
||||
|
||||
/**
|
||||
* IsSafeToNotifyIME() checks if it's safe to noitify IME.
|
||||
*/
|
||||
bool IsSafeToNotifyIME(ChangeEventType aChangeEventType) const;
|
||||
};
|
||||
|
||||
class IMENotificationSender: public AChangeEvent
|
||||
{
|
||||
public:
|
||||
explicit IMENotificationSender(IMEContentObserver* aIMEContentObserver)
|
||||
: AChangeEvent(aIMEContentObserver)
|
||||
, mIsRunning(false)
|
||||
{
|
||||
}
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
private:
|
||||
void SendFocusSet();
|
||||
void SendSelectionChange();
|
||||
void SendTextChange();
|
||||
void SendPositionChange();
|
||||
|
||||
bool mIsRunning;
|
||||
};
|
||||
|
||||
// mQueuedSender is, it was put into the event queue but not run yet.
|
||||
RefPtr<IMENotificationSender> mQueuedSender;
|
||||
|
||||
/**
|
||||
* FlatTextCache stores flat text length from start of the content to
|
||||
* mNodeOffset of mContainerNode.
|
||||
|
@ -261,76 +325,9 @@ private:
|
|||
bool mNeedsToNotifyIMEOfTextChange;
|
||||
bool mNeedsToNotifyIMEOfSelectionChange;
|
||||
bool mNeedsToNotifyIMEOfPositionChange;
|
||||
// mIsFlushingPendingNotifications is true between
|
||||
// FlushMergeableNotifications() creates IMENotificationSender and
|
||||
// IMENotificationSender sent all pending notifications.
|
||||
bool mIsFlushingPendingNotifications;
|
||||
// mIsHandlingQueryContentEvent is true when IMEContentObserver is handling
|
||||
// WidgetQueryContentEvent with ContentEventHandler.
|
||||
bool mIsHandlingQueryContentEvent;
|
||||
|
||||
|
||||
/**
|
||||
* Helper classes to notify IME.
|
||||
*/
|
||||
|
||||
class AChangeEvent: public nsRunnable
|
||||
{
|
||||
protected:
|
||||
enum ChangeEventType
|
||||
{
|
||||
eChangeEventType_Focus,
|
||||
eChangeEventType_Selection,
|
||||
eChangeEventType_Text,
|
||||
eChangeEventType_Position,
|
||||
eChangeEventType_FlushPendingEvents
|
||||
};
|
||||
|
||||
explicit AChangeEvent(IMEContentObserver* aIMEContentObserver)
|
||||
: mIMEContentObserver(aIMEContentObserver)
|
||||
{
|
||||
MOZ_ASSERT(mIMEContentObserver);
|
||||
}
|
||||
|
||||
RefPtr<IMEContentObserver> mIMEContentObserver;
|
||||
|
||||
/**
|
||||
* CanNotifyIME() checks if mIMEContentObserver can and should notify IME.
|
||||
*/
|
||||
bool CanNotifyIME(ChangeEventType aChangeEventType) const;
|
||||
|
||||
/**
|
||||
* IsSafeToNotifyIME() checks if it's safe to noitify IME.
|
||||
*/
|
||||
bool IsSafeToNotifyIME(ChangeEventType aChangeEventType) const;
|
||||
};
|
||||
|
||||
class IMENotificationSender: public AChangeEvent
|
||||
{
|
||||
public:
|
||||
explicit IMENotificationSender(IMEContentObserver* aIMEContentObserver)
|
||||
: AChangeEvent(aIMEContentObserver)
|
||||
{
|
||||
}
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
private:
|
||||
void SendFocusSet();
|
||||
void SendSelectionChange();
|
||||
void SendTextChange();
|
||||
void SendPositionChange();
|
||||
};
|
||||
|
||||
class AsyncMergeableNotificationsFlusher : public AChangeEvent
|
||||
{
|
||||
public:
|
||||
explicit AsyncMergeableNotificationsFlusher(
|
||||
IMEContentObserver* aIMEContentObserver)
|
||||
: AChangeEvent(aIMEContentObserver)
|
||||
{
|
||||
}
|
||||
NS_IMETHOD Run() override;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -515,6 +515,13 @@ IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
|
|||
// editor.
|
||||
if (newState.mEnabled == IMEState::PLUGIN) {
|
||||
CreateIMEContentObserver(nullptr);
|
||||
if (sActiveIMEContentObserver) {
|
||||
MOZ_LOG(sISMLog, LogLevel::Debug,
|
||||
("ISM: IMEStateManager::OnChangeFocusInternal(), an "
|
||||
"IMEContentObserver instance is created for plugin and trying to "
|
||||
"flush its pending notifications..."));
|
||||
sActiveIMEContentObserver->TryToFlushPendingNotifications();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -684,6 +691,14 @@ IMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
|
|||
}
|
||||
|
||||
CreateIMEContentObserver(aEditor);
|
||||
|
||||
// Let's flush the focus notification now.
|
||||
if (sActiveIMEContentObserver) {
|
||||
MOZ_LOG(sISMLog, LogLevel::Debug,
|
||||
("ISM: IMEStateManager::OnFocusInEditor(), new IMEContentObserver is "
|
||||
"created, trying to flush pending notifications..."));
|
||||
sActiveIMEContentObserver->TryToFlushPendingNotifications();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -797,6 +812,9 @@ IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
|
|||
}
|
||||
|
||||
if (createTextStateManager) {
|
||||
// XXX In this case, it might not be enough safe to notify IME of anything.
|
||||
// So, don't try to flush pending notifications of IMEContentObserver
|
||||
// here.
|
||||
CreateIMEContentObserver(aEditor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,9 +154,9 @@ nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs);
|
|||
// The maximum height and width of the video. Used for
|
||||
// sanitizing the memory allocation of the RGB buffer.
|
||||
// The maximum resolution we anticipate encountering in the
|
||||
// wild is 2160p - 3840x2160 pixels.
|
||||
static const int32_t MAX_VIDEO_WIDTH = 4000;
|
||||
static const int32_t MAX_VIDEO_HEIGHT = 3000;
|
||||
// wild is 2160p (UHD "4K") or 4320p - 7680x4320 pixels for VR.
|
||||
static const int32_t MAX_VIDEO_WIDTH = 8192;
|
||||
static const int32_t MAX_VIDEO_HEIGHT = 4608;
|
||||
|
||||
// Scales the display rect aDisplay by aspect ratio aAspectRatio.
|
||||
// Note that aDisplay must be validated by IsValidVideoRegion()
|
||||
|
|
|
@ -570,7 +570,10 @@ CDMProxy::OnExpirationChange(const nsAString& aSessionId,
|
|||
GMPTimestamp aExpiryTime)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_WARNING("CDMProxy::OnExpirationChange() not implemented");
|
||||
RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
if (session) {
|
||||
session->SetExpiration(static_cast<double>(aExpiryTime));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -55,6 +55,7 @@ MediaKeySession::MediaKeySession(JSContext* aCx,
|
|||
, mIsClosed(false)
|
||||
, mUninitialized(true)
|
||||
, mKeyStatusMap(new MediaKeyStatusMap(aCx, aParent, aRv))
|
||||
, mExpiration(JS::GenericNaN())
|
||||
{
|
||||
EME_LOG("MediaKeySession[%p,''] session Id set", this);
|
||||
|
||||
|
@ -114,7 +115,7 @@ MediaKeySession::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|||
double
|
||||
MediaKeySession::Expiration() const
|
||||
{
|
||||
return JS::GenericNaN();
|
||||
return mExpiration;
|
||||
}
|
||||
|
||||
Promise*
|
||||
|
@ -263,6 +264,16 @@ MediaKeySession::Update(const ArrayBufferViewOrArrayBuffer& aResponse, ErrorResu
|
|||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!IsCallable()) {
|
||||
// If this object's callable value is false, return a promise rejected
|
||||
// with a new DOMException whose name is InvalidStateError.
|
||||
EME_LOG("MediaKeySession[%p,''] Update() called before sessionId set by CDM", this);
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("MediaKeySession.Update() called before sessionId set by CDM"));
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> data;
|
||||
if (IsClosed() || !mKeys->GetCDMProxy()) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
|
@ -308,6 +319,14 @@ MediaKeySession::Close(ErrorResult& aRv)
|
|||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!IsCallable()) {
|
||||
// If this object's callable value is false, return a promise rejected
|
||||
// with a new DOMException whose name is InvalidStateError.
|
||||
EME_LOG("MediaKeySession[%p,''] Close() called before sessionId set by CDM", this);
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("MediaKeySession.Close() called before sessionId set by CDM"));
|
||||
return promise.forget();
|
||||
}
|
||||
if (IsClosed() || !mKeys->GetCDMProxy()) {
|
||||
EME_LOG("MediaKeySession[%p,'%s'] Close() already closed",
|
||||
this, NS_ConvertUTF16toUTF8(mSessionId).get());
|
||||
|
@ -351,6 +370,14 @@ MediaKeySession::Remove(ErrorResult& aRv)
|
|||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!IsCallable()) {
|
||||
// If this object's callable value is false, return a promise rejected
|
||||
// with a new DOMException whose name is InvalidStateError.
|
||||
EME_LOG("MediaKeySession[%p,''] Remove() called before sessionId set by CDM", this);
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("MediaKeySession.Remove() called before sessionId set by CDM"));
|
||||
return promise.forget();
|
||||
}
|
||||
if (mSessionType != SessionType::Persistent) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
|
||||
NS_LITERAL_CSTRING("Calling MediaKeySession.remove() on non-persistent session"));
|
||||
|
@ -437,5 +464,15 @@ MediaKeySession::MakePromise(ErrorResult& aRv, const nsACString& aName)
|
|||
return DetailedPromise::Create(global, aRv, aName);
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeySession::SetExpiration(double aExpiration)
|
||||
{
|
||||
EME_LOG("MediaKeySession[%p,'%s'] SetExpiry(%lf)",
|
||||
this,
|
||||
NS_ConvertUTF16toUTF8(mSessionId).get(),
|
||||
aExpiration);
|
||||
mExpiration = aExpiration;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -92,6 +92,8 @@ public:
|
|||
|
||||
bool IsClosed() const;
|
||||
|
||||
void SetExpiration(double aExpiry);
|
||||
|
||||
// Process-unique identifier.
|
||||
uint32_t Token() const;
|
||||
|
||||
|
@ -99,6 +101,14 @@ private:
|
|||
~MediaKeySession();
|
||||
|
||||
void UpdateKeyStatusMap();
|
||||
|
||||
bool IsCallable() const {
|
||||
// The EME spec sets the "callable value" to true whenever the CDM sets
|
||||
// the sessionId. When the session is initialized, sessionId is empty and
|
||||
// callable is thus false.
|
||||
return !mSessionId.IsEmpty();
|
||||
}
|
||||
|
||||
already_AddRefed<DetailedPromise> MakePromise(ErrorResult& aRv,
|
||||
const nsACString& aName);
|
||||
|
||||
|
@ -114,6 +124,7 @@ private:
|
|||
bool mIsClosed;
|
||||
bool mUninitialized;
|
||||
RefPtr<MediaKeyStatusMap> mKeyStatusMap;
|
||||
double mExpiration;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -616,6 +616,7 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
|||
skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
|
||||
[test_dormant_playback.html]
|
||||
skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'gonk')
|
||||
[test_eme_session_callable_value.html]
|
||||
[test_eme_canvas_blocked.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
[test_eme_non_mse_fails.html]
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Encrypted Media Extensions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function Test() {
|
||||
navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{ initDataTypes: ['cenc'] }])
|
||||
.then(access => access.createMediaKeys())
|
||||
.then(mediaKeys => {
|
||||
var initData = (new TextEncoder()).encode( 'this is an invalid license, and that is ok');
|
||||
var s = mediaKeys.createSession("temporary");
|
||||
s.generateRequest("cenc", initData); // ignore result.
|
||||
// "update()" call should fail, because MediaKeySession is "not callable"
|
||||
// yet, since CDM won't have had a chance to set the sessionId on MediaKeySession.
|
||||
return s.update(initData);
|
||||
})
|
||||
.then(()=>{ok(false, "An exception should be thrown; MediaKeySession should be not callable."); SimpleTest.finish();},
|
||||
()=>{ok(true, "We expect this to fail; MediaKeySession should be not callable."); SimpleTest.finish();});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SetupEMEPref(Test);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -9,6 +9,11 @@ var gift = require('gift'),
|
|||
alias: 'dir',
|
||||
describe: 'Path to WebVTT directory.'
|
||||
})
|
||||
.options('r', {
|
||||
alias: 'rev',
|
||||
describe: 'Revision to update to.',
|
||||
default: 'master'
|
||||
})
|
||||
.options('w', {
|
||||
alias: 'write',
|
||||
describe: 'Path to file to write to.',
|
||||
|
@ -22,8 +27,8 @@ repo.status(function(err, status) {
|
|||
console.log("The repository's working directory is not clean. Aborting.");
|
||||
process.exit(1);
|
||||
}
|
||||
repo.checkout("master", function() {
|
||||
repo.commits("master", 1, function(err, commits) {
|
||||
repo.checkout(argv.r, function() {
|
||||
repo.commits(argv.r, 1, function(err, commits) {
|
||||
var vttjs = fs.readFileSync(argv.d + "/lib/vtt.js", 'utf8');
|
||||
|
||||
// Remove settings for VIM and Emacs.
|
||||
|
|
|
@ -8,7 +8,7 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
|
|||
* Code below is vtt.js the JS WebVTT implementation.
|
||||
* Current source code can be found at http://github.com/mozilla/vtt.js
|
||||
*
|
||||
* Code taken from commit f5a1a60775a615cd9670d6cdaedddf2c6f25fae3
|
||||
* Code taken from commit 364c6b951a07306848a706d1d03c2a6ae942517d
|
||||
*/
|
||||
/**
|
||||
* Copyright 2013 vtt.js Contributors
|
||||
|
@ -726,38 +726,60 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
|
|||
// Constructs the computed display state of the cue (a div). Places the div
|
||||
// into the overlay which should be a block level element (usually a div).
|
||||
function CueStyleBox(window, cue, styleOptions) {
|
||||
var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
|
||||
var color = "rgba(255, 255, 255, 1)";
|
||||
var backgroundColor = "rgba(0, 0, 0, 0.8)";
|
||||
|
||||
if (isIE8) {
|
||||
color = "rgb(255, 255, 255)";
|
||||
backgroundColor = "rgb(0, 0, 0)";
|
||||
}
|
||||
|
||||
StyleBox.call(this);
|
||||
this.cue = cue;
|
||||
|
||||
// Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
|
||||
// have inline positioning and will function as the cue background box.
|
||||
this.cueDiv = parseContent(window, cue.text);
|
||||
this.applyStyles({
|
||||
color: "rgba(255, 255, 255, 1)",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.8)",
|
||||
var styles = {
|
||||
color: color,
|
||||
backgroundColor: backgroundColor,
|
||||
position: "relative",
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
display: "inline"
|
||||
}, this.cueDiv);
|
||||
};
|
||||
|
||||
if (!isIE8) {
|
||||
styles.writingMode = cue.vertical === "" ? "horizontal-tb"
|
||||
: cue.vertical === "lr" ? "vertical-lr"
|
||||
: "vertical-rl";
|
||||
styles.unicodeBidi = "plaintext";
|
||||
}
|
||||
this.applyStyles(styles, this.cueDiv);
|
||||
|
||||
// Create an absolutely positioned div that will be used to position the cue
|
||||
// div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
|
||||
// mirrors of them except "middle" which is "center" in CSS.
|
||||
this.div = window.document.createElement("div");
|
||||
this.applyStyles({
|
||||
styles = {
|
||||
textAlign: cue.align === "middle" ? "center" : cue.align,
|
||||
direction: determineBidi(this.cueDiv),
|
||||
writingMode: cue.vertical === "" ? "horizontal-tb"
|
||||
: cue.vertical === "lr" ? "vertical-lr"
|
||||
: "vertical-rl",
|
||||
unicodeBidi: "plaintext",
|
||||
font: styleOptions.font,
|
||||
whiteSpace: "pre-line",
|
||||
position: "absolute"
|
||||
});
|
||||
};
|
||||
|
||||
if (!isIE8) {
|
||||
styles.direction = determineBidi(this.cueDiv);
|
||||
styles.writingMode = cue.vertical === "" ? "horizontal-tb"
|
||||
: cue.vertical === "lr" ? "vertical-lr"
|
||||
: "vertical-rl".
|
||||
stylesunicodeBidi = "plaintext";
|
||||
}
|
||||
|
||||
this.applyStyles(styles);
|
||||
|
||||
this.div.appendChild(this.cueDiv);
|
||||
|
||||
|
@ -783,7 +805,7 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
|
|||
if (cue.vertical === "") {
|
||||
this.applyStyles({
|
||||
left: this.formatStyle(textPos, "%"),
|
||||
width: this.formatStyle(cue.size, "%"),
|
||||
width: this.formatStyle(cue.size, "%")
|
||||
});
|
||||
// Vertical box orientation; textPos is the distance from the top edge of the
|
||||
// area to the top edge of the box and cue.size is the height extending
|
||||
|
@ -802,7 +824,7 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
|
|||
left: this.formatStyle(box.left, "px"),
|
||||
right: this.formatStyle(box.right, "px"),
|
||||
height: this.formatStyle(box.height, "px"),
|
||||
width: this.formatStyle(box.width, "px"),
|
||||
width: this.formatStyle(box.width, "px")
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -813,12 +835,18 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
|
|||
// compute things with such as if it overlaps or intersects with another Element.
|
||||
// Can initialize it with either a StyleBox or another BoxPosition.
|
||||
function BoxPosition(obj) {
|
||||
var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
|
||||
|
||||
// Either a BoxPosition was passed in and we need to copy it, or a StyleBox
|
||||
// was passed in and we need to copy the results of 'getBoundingClientRect'
|
||||
// as the object returned is readonly. All co-ordinate values are in reference
|
||||
// to the viewport origin (top left).
|
||||
var lh;
|
||||
var lh, height, width, top;
|
||||
if (obj.div) {
|
||||
height = obj.div.offsetHeight;
|
||||
width = obj.div.offsetWidth;
|
||||
top = obj.div.offsetTop;
|
||||
|
||||
var rects = (rects = obj.div.childNodes) && (rects = rects[0]) &&
|
||||
rects.getClientRects && rects.getClientRects();
|
||||
obj = obj.div.getBoundingClientRect();
|
||||
|
@ -828,14 +856,19 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
|
|||
// result in the desired behaviour.
|
||||
lh = rects ? Math.max((rects[0] && rects[0].height) || 0, obj.height / rects.length)
|
||||
: 0;
|
||||
|
||||
}
|
||||
this.left = obj.left;
|
||||
this.right = obj.right;
|
||||
this.top = obj.top;
|
||||
this.height = obj.height;
|
||||
this.bottom = obj.bottom;
|
||||
this.width = obj.width;
|
||||
this.top = obj.top || top;
|
||||
this.height = obj.height || height;
|
||||
this.bottom = obj.bottom || (top + (obj.height || height));
|
||||
this.width = obj.width || width;
|
||||
this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
|
||||
|
||||
if (isIE8 && !this.lineHeight) {
|
||||
this.lineHeight = 13;
|
||||
}
|
||||
}
|
||||
|
||||
// Move the box along a particular axis. Optionally pass in an amount to move
|
||||
|
@ -933,16 +966,21 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
|
|||
// Get an object that represents the box's position without anything extra.
|
||||
// Can pass a StyleBox, HTMLElement, or another BoxPositon.
|
||||
BoxPosition.getSimpleBoxPosition = function(obj) {
|
||||
var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
|
||||
var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
|
||||
var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;
|
||||
|
||||
obj = obj.div ? obj.div.getBoundingClientRect() :
|
||||
obj.tagName ? obj.getBoundingClientRect() : obj;
|
||||
return {
|
||||
var ret = {
|
||||
left: obj.left,
|
||||
right: obj.right,
|
||||
top: obj.top,
|
||||
height: obj.height,
|
||||
bottom: obj.bottom,
|
||||
width: obj.width
|
||||
top: obj.top || top,
|
||||
height: obj.height || height,
|
||||
bottom: obj.bottom || (top + (obj.height || height)),
|
||||
width: obj.width || width
|
||||
};
|
||||
return ret;
|
||||
};
|
||||
|
||||
// Move a StyleBox to its specified, or next best, position. The containerBox
|
||||
|
@ -1141,9 +1179,9 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
|
|||
|
||||
// We don't need to recompute the cues' display states. Just reuse them.
|
||||
if (!shouldCompute(cues)) {
|
||||
cues.forEach(function(cue) {
|
||||
paddedOverlay.appendChild(cue.displayState);
|
||||
});
|
||||
for (var i = 0; i < cues.length; i++) {
|
||||
paddedOverlay.appendChild(cues[i].displayState);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1154,20 +1192,26 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
|
|||
font: fontSize + "px " + FONT_STYLE
|
||||
};
|
||||
|
||||
cues.forEach(function(cue) {
|
||||
// Compute the intial position and styles of the cue div.
|
||||
var styleBox = new CueStyleBox(window, cue, styleOptions);
|
||||
paddedOverlay.appendChild(styleBox.div);
|
||||
(function() {
|
||||
var styleBox, cue;
|
||||
|
||||
// Move the cue div to it's correct line position.
|
||||
moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);
|
||||
for (var i = 0; i < cues.length; i++) {
|
||||
cue = cues[i];
|
||||
|
||||
// Remember the computed div so that we don't have to recompute it later
|
||||
// if we don't have too.
|
||||
cue.displayState = styleBox.div;
|
||||
// Compute the intial position and styles of the cue div.
|
||||
styleBox = new CueStyleBox(window, cue, styleOptions);
|
||||
paddedOverlay.appendChild(styleBox.div);
|
||||
|
||||
boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
|
||||
});
|
||||
// Move the cue div to it's correct line position.
|
||||
moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);
|
||||
|
||||
// Remember the computed div so that we don't have to recompute it later
|
||||
// if we don't have too.
|
||||
cue.displayState = styleBox.div;
|
||||
|
||||
boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
WebVTT.Parser = function(window, decoder) {
|
||||
|
|
|
@ -4,14 +4,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
// Don't modify this, instead set dom.push.debug.
|
||||
var gDebuggingEnabled = false;
|
||||
|
||||
function debug(s) {
|
||||
if (gDebuggingEnabled)
|
||||
dump("-*- Push.js: " + s + "\n");
|
||||
}
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
@ -21,6 +13,14 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "dom.push.loglevel",
|
||||
prefix: "Push",
|
||||
});
|
||||
});
|
||||
|
||||
const PUSH_CID = Components.ID("{cde1d019-fad8-4044-b141-65fb4fb7a245}");
|
||||
|
||||
/**
|
||||
|
@ -29,7 +29,7 @@ const PUSH_CID = Components.ID("{cde1d019-fad8-4044-b141-65fb4fb7a245}");
|
|||
* one actually performing all operations.
|
||||
*/
|
||||
function Push() {
|
||||
debug("Push Constructor");
|
||||
console.debug("Push()");
|
||||
}
|
||||
|
||||
Push.prototype = {
|
||||
|
@ -44,11 +44,7 @@ Push.prototype = {
|
|||
Ci.nsIObserver]),
|
||||
|
||||
init: function(aWindow) {
|
||||
// Set debug first so that all debugging actually works.
|
||||
// NOTE: We don't add an observer here like in PushService. Flipping the
|
||||
// pref will require a reload of the app/page, which seems acceptable.
|
||||
gDebuggingEnabled = Services.prefs.getBoolPref("dom.push.debug");
|
||||
debug("init()");
|
||||
console.debug("init()");
|
||||
|
||||
this._window = aWindow;
|
||||
|
||||
|
@ -60,12 +56,12 @@ Push.prototype = {
|
|||
},
|
||||
|
||||
setScope: function(scope){
|
||||
debug('setScope ' + scope);
|
||||
console.debug("setScope()", scope);
|
||||
this._scope = scope;
|
||||
},
|
||||
|
||||
askPermission: function (aAllowCallback, aCancelCallback) {
|
||||
debug("askPermission");
|
||||
console.debug("askPermission()");
|
||||
|
||||
return this.createPromise((resolve, reject) => {
|
||||
let permissionDenied = () => {
|
||||
|
@ -94,7 +90,7 @@ Push.prototype = {
|
|||
},
|
||||
|
||||
subscribe: function() {
|
||||
debug("subscribe()");
|
||||
console.debug("subscribe()", this._scope);
|
||||
|
||||
let histogram = Services.telemetry.getHistogramById("PUSH_API_USED");
|
||||
histogram.add(true);
|
||||
|
@ -107,7 +103,7 @@ Push.prototype = {
|
|||
},
|
||||
|
||||
getSubscription: function() {
|
||||
debug("getSubscription()" + this._scope);
|
||||
console.debug("getSubscription()", this._scope);
|
||||
|
||||
return this.createPromise((resolve, reject) => {
|
||||
let callback = new PushEndpointCallback(this, resolve, reject);
|
||||
|
@ -116,7 +112,7 @@ Push.prototype = {
|
|||
},
|
||||
|
||||
permissionState: function() {
|
||||
debug("permissionState()" + this._scope);
|
||||
console.debug("permissionState()", this._scope);
|
||||
|
||||
return this.createPromise((resolve, reject) => {
|
||||
let permission = Ci.nsIPermissionManager.UNKNOWN_ACTION;
|
||||
|
|
|
@ -4,14 +4,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
// Don't modify this, instead set dom.push.debug.
|
||||
var gDebuggingEnabled = false;
|
||||
|
||||
function debug(s) {
|
||||
if (gDebuggingEnabled)
|
||||
dump("-*- PushClient.js: " + s + "\n");
|
||||
}
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
@ -20,7 +12,13 @@ const Cr = Components.results;
|
|||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
gDebuggingEnabled = Services.prefs.getBoolPref("dom.push.debug");
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "dom.push.loglevel",
|
||||
prefix: "PushClient",
|
||||
});
|
||||
});
|
||||
|
||||
const kMessages = [
|
||||
"PushService:Register:OK",
|
||||
|
@ -32,7 +30,7 @@ const kMessages = [
|
|||
];
|
||||
|
||||
this.PushClient = function PushClient() {
|
||||
debug("PushClient created!");
|
||||
console.debug("PushClient()");
|
||||
this._cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(Ci.nsISyncMessageSender);
|
||||
this._requests = {};
|
||||
|
@ -73,7 +71,7 @@ PushClient.prototype = {
|
|||
},
|
||||
|
||||
subscribe: function(scope, principal, callback) {
|
||||
debug("subscribe() " + scope);
|
||||
console.debug("subscribe()", scope);
|
||||
let requestId = this.addRequest(callback);
|
||||
this._cpmm.sendAsyncMessage("Push:Register", {
|
||||
scope: scope,
|
||||
|
@ -82,7 +80,7 @@ PushClient.prototype = {
|
|||
},
|
||||
|
||||
unsubscribe: function(scope, principal, callback) {
|
||||
debug("unsubscribe() " + scope);
|
||||
console.debug("unsubscribe()", scope);
|
||||
let requestId = this.addRequest(callback);
|
||||
this._cpmm.sendAsyncMessage("Push:Unregister", {
|
||||
scope: scope,
|
||||
|
@ -91,9 +89,10 @@ PushClient.prototype = {
|
|||
},
|
||||
|
||||
getSubscription: function(scope, principal, callback) {
|
||||
debug("getSubscription()" + scope);
|
||||
console.debug("getSubscription()", scope);
|
||||
let requestId = this.addRequest(callback);
|
||||
debug("Going to send " + scope + " " + principal + " " + requestId);
|
||||
console.debug("getSubscription: Going to send", scope, principal,
|
||||
requestId);
|
||||
this._cpmm.sendAsyncMessage("Push:Registration", {
|
||||
scope: scope,
|
||||
requestID: requestId,
|
||||
|
@ -101,6 +100,10 @@ PushClient.prototype = {
|
|||
},
|
||||
|
||||
_deliverPushEndpoint: function(request, registration) {
|
||||
if (!registration) {
|
||||
request.onPushEndpoint(Cr.NS_OK, "", 0, null);
|
||||
return;
|
||||
}
|
||||
if (registration.p256dhKey) {
|
||||
let key = new Uint8Array(registration.p256dhKey);
|
||||
request.onPushEndpoint(Cr.NS_OK,
|
||||
|
@ -114,38 +117,31 @@ PushClient.prototype = {
|
|||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
console.debug("receiveMessage()", aMessage);
|
||||
|
||||
let json = aMessage.data;
|
||||
let request = this.takeRequest(json.requestID);
|
||||
|
||||
if (!request) {
|
||||
console.error("receiveMessage: Unknown request ID", json.requestID);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("receiveMessage(): " + JSON.stringify(aMessage))
|
||||
switch (aMessage.name) {
|
||||
case "PushService:Register:OK":
|
||||
this._deliverPushEndpoint(request, json);
|
||||
break;
|
||||
case "PushService:Register:KO":
|
||||
request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null);
|
||||
break;
|
||||
case "PushService:Registration:OK":
|
||||
{
|
||||
let endpoint = "";
|
||||
if (!json.registration) {
|
||||
request.onPushEndpoint(Cr.NS_OK, "", 0, null);
|
||||
} else {
|
||||
this._deliverPushEndpoint(request, json.registration);
|
||||
}
|
||||
this._deliverPushEndpoint(request, json.result);
|
||||
break;
|
||||
}
|
||||
|
||||
case "PushService:Register:KO":
|
||||
case "PushService:Registration:KO":
|
||||
request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null);
|
||||
break;
|
||||
|
||||
case "PushService:Unregister:OK":
|
||||
if (typeof json.result !== "boolean") {
|
||||
debug("Expected boolean result from PushService!");
|
||||
console.error("receiveMessage: Expected boolean for unregister response",
|
||||
json.result);
|
||||
request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false);
|
||||
return;
|
||||
}
|
||||
|
@ -156,7 +152,7 @@ PushClient.prototype = {
|
|||
request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false);
|
||||
break;
|
||||
default:
|
||||
debug("NOT IMPLEMENTED! receiveMessage for " + aMessage.name);
|
||||
console.error("receiveMessage: NOT IMPLEMENTED!", aMessage.name);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -5,26 +5,24 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
// Don't modify this, instead set dom.push.debug.
|
||||
var gDebuggingEnabled = false;
|
||||
|
||||
function debug(s) {
|
||||
if (gDebuggingEnabled) {
|
||||
dump("-*- PushDB.jsm: " + s + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.importGlobalProperties(["indexedDB"]);
|
||||
|
||||
const prefs = new Preferences("dom.push.");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PushDB"];
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "dom.push.loglevel",
|
||||
prefix: "PushDB",
|
||||
});
|
||||
});
|
||||
|
||||
this.PushDB = function PushDB(dbName, dbVersion, dbStoreName, keyPath, model) {
|
||||
debug("PushDB()");
|
||||
console.debug("PushDB()");
|
||||
this._dbStoreName = dbStoreName;
|
||||
this._keyPath = keyPath;
|
||||
this._model = model;
|
||||
|
@ -32,8 +30,6 @@ this.PushDB = function PushDB(dbName, dbVersion, dbStoreName, keyPath, model) {
|
|||
// set the indexeddb database
|
||||
this.initDBHelper(dbName, dbVersion,
|
||||
[dbStoreName]);
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
prefs.observe("debug", this);
|
||||
};
|
||||
|
||||
this.PushDB.prototype = {
|
||||
|
@ -90,7 +86,7 @@ this.PushDB.prototype = {
|
|||
*/
|
||||
|
||||
put: function(aRecord) {
|
||||
debug("put()" + JSON.stringify(aRecord));
|
||||
console.debug("put()", aRecord);
|
||||
if (!this.isValidRecord(aRecord)) {
|
||||
return Promise.reject(new TypeError(
|
||||
"Scope, originAttributes, and quota are required! " +
|
||||
|
@ -107,8 +103,8 @@ this.PushDB.prototype = {
|
|||
aTxn.result = undefined;
|
||||
|
||||
aStore.put(aRecord).onsuccess = aEvent => {
|
||||
debug("Request successful. Updated record ID: " +
|
||||
aEvent.target.result);
|
||||
console.debug("put: Request successful. Updated record",
|
||||
aEvent.target.result);
|
||||
aTxn.result = this.toPushRecord(aRecord);
|
||||
};
|
||||
},
|
||||
|
@ -123,14 +119,14 @@ this.PushDB.prototype = {
|
|||
* The ID of record to be deleted.
|
||||
*/
|
||||
delete: function(aKeyID) {
|
||||
debug("delete()");
|
||||
console.debug("delete()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
"readwrite",
|
||||
this._dbStoreName,
|
||||
function txnCb(aTxn, aStore) {
|
||||
debug("Going to delete " + aKeyID);
|
||||
console.debug("delete: Removing record", aKeyID);
|
||||
aStore.delete(aKeyID);
|
||||
},
|
||||
resolve,
|
||||
|
@ -139,25 +135,10 @@ this.PushDB.prototype = {
|
|||
);
|
||||
},
|
||||
|
||||
clearAll: function clear() {
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
"readwrite",
|
||||
this._dbStoreName,
|
||||
function (aTxn, aStore) {
|
||||
debug("Going to clear all!");
|
||||
aStore.clear();
|
||||
},
|
||||
resolve,
|
||||
reject
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
// testFn(record) is called with a database record and should return true if
|
||||
// that record should be deleted.
|
||||
clearIf: function(testFn) {
|
||||
debug("clearIf()");
|
||||
console.debug("clearIf()");
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
"readwrite",
|
||||
|
@ -168,10 +149,12 @@ this.PushDB.prototype = {
|
|||
aStore.openCursor().onsuccess = event => {
|
||||
let cursor = event.target.result;
|
||||
if (cursor) {
|
||||
if (testFn(this.toPushRecord(cursor.value))) {
|
||||
let record = this.toPushRecord(cursor.value);
|
||||
if (testFn(record)) {
|
||||
let deleteRequest = cursor.delete();
|
||||
deleteRequest.onerror = e => {
|
||||
debug("Failed to delete entry even when test succeeded!");
|
||||
console.error("clearIf: Error removing record",
|
||||
record.keyID, e);
|
||||
}
|
||||
}
|
||||
cursor.continue();
|
||||
|
@ -185,7 +168,7 @@ this.PushDB.prototype = {
|
|||
},
|
||||
|
||||
getByPushEndpoint: function(aPushEndpoint) {
|
||||
debug("getByPushEndpoint()");
|
||||
console.debug("getByPushEndpoint()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
|
@ -196,8 +179,9 @@ this.PushDB.prototype = {
|
|||
|
||||
let index = aStore.index("pushEndpoint");
|
||||
index.get(aPushEndpoint).onsuccess = aEvent => {
|
||||
aTxn.result = this.toPushRecord(aEvent.target.result);
|
||||
debug("Fetch successful " + aEvent.target.result);
|
||||
let record = this.toPushRecord(aEvent.target.result);
|
||||
console.debug("getByPushEndpoint: Got record", record);
|
||||
aTxn.result = record;
|
||||
};
|
||||
},
|
||||
resolve,
|
||||
|
@ -207,7 +191,7 @@ this.PushDB.prototype = {
|
|||
},
|
||||
|
||||
getByKeyID: function(aKeyID) {
|
||||
debug("getByKeyID()");
|
||||
console.debug("getByKeyID()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
|
@ -217,8 +201,9 @@ this.PushDB.prototype = {
|
|||
aTxn.result = undefined;
|
||||
|
||||
aStore.get(aKeyID).onsuccess = aEvent => {
|
||||
aTxn.result = this.toPushRecord(aEvent.target.result);
|
||||
debug("Fetch successful " + aEvent.target.result);
|
||||
let record = this.toPushRecord(aEvent.target.result);
|
||||
console.debug("getByKeyID: Got record", record);
|
||||
aTxn.result = record;
|
||||
};
|
||||
},
|
||||
resolve,
|
||||
|
@ -242,7 +227,7 @@ this.PushDB.prototype = {
|
|||
* updated.
|
||||
*/
|
||||
updateByOrigin: function(origin, originAttributes, updateFunc) {
|
||||
debug("updateByOrigin()");
|
||||
console.debug("updateByOrigin()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
|
@ -264,16 +249,16 @@ this.PushDB.prototype = {
|
|||
let record = this.toPushRecord(cursor.value);
|
||||
let newRecord = updateFunc(record);
|
||||
if (newRecord === false) {
|
||||
debug("updateByOrigin: Removing record for key ID " +
|
||||
console.debug("updateByOrigin: Removing record for key ID",
|
||||
record.keyID);
|
||||
cursor.delete();
|
||||
} else if (this.isValidRecord(newRecord)) {
|
||||
debug("updateByOrigin: Updating record for key ID " +
|
||||
record.keyID);
|
||||
console.debug("updateByOrigin: Updating record for key ID",
|
||||
record.keyID, newRecord);
|
||||
cursor.update(newRecord);
|
||||
} else {
|
||||
debug("updateByOrigin: Ignoring invalid update for key ID " +
|
||||
record.keyID + ": " + JSON.stringify(newRecord));
|
||||
console.error("updateByOrigin: Ignoring invalid update for record",
|
||||
record.keyID, newRecord);
|
||||
}
|
||||
cursor.continue();
|
||||
};
|
||||
|
@ -286,12 +271,11 @@ this.PushDB.prototype = {
|
|||
|
||||
// Perform a unique match against { scope, originAttributes }
|
||||
getByIdentifiers: function(aPageRecord) {
|
||||
debug("getByIdentifiers() { " + aPageRecord.scope + ", " +
|
||||
JSON.stringify(aPageRecord.originAttributes) + " }");
|
||||
console.debug("getByIdentifiers()", aPageRecord);
|
||||
if (!aPageRecord.scope || aPageRecord.originAttributes == undefined) {
|
||||
return Promise.reject(
|
||||
new TypeError("Scope and originAttributes are required! " +
|
||||
JSON.stringify(aPageRecord)));
|
||||
console.error("getByIdentifiers: Scope and originAttributes are required",
|
||||
aPageRecord);
|
||||
return Promise.reject(new TypeError("Invalid page record"));
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
|
@ -346,7 +330,7 @@ this.PushDB.prototype = {
|
|||
},
|
||||
|
||||
getAllKeyIDs: function() {
|
||||
debug("getAllKeyIDs()");
|
||||
console.debug("getAllKeyIDs()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
|
@ -366,7 +350,7 @@ this.PushDB.prototype = {
|
|||
},
|
||||
|
||||
_getAllByPushQuota: function(range) {
|
||||
debug("getAllByPushQuota()");
|
||||
console.debug("getAllByPushQuota()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
|
@ -391,12 +375,12 @@ this.PushDB.prototype = {
|
|||
},
|
||||
|
||||
getAllUnexpired: function() {
|
||||
debug("getAllUnexpired()");
|
||||
console.debug("getAllUnexpired()");
|
||||
return this._getAllByPushQuota(IDBKeyRange.lowerBound(1));
|
||||
},
|
||||
|
||||
getAllExpired: function() {
|
||||
debug("getAllExpired()");
|
||||
console.debug("getAllExpired()");
|
||||
return this._getAllByPushQuota(IDBKeyRange.only(0));
|
||||
},
|
||||
|
||||
|
@ -422,17 +406,17 @@ this.PushDB.prototype = {
|
|||
|
||||
let record = aEvent.target.result;
|
||||
if (!record) {
|
||||
debug("update: Key ID " + aKeyID + " does not exist");
|
||||
console.error("update: Record does not exist", aKeyID);
|
||||
return;
|
||||
}
|
||||
let newRecord = aUpdateFunc(this.toPushRecord(record));
|
||||
if (!this.isValidRecord(newRecord)) {
|
||||
debug("update: Ignoring invalid update for key ID " + aKeyID +
|
||||
": " + JSON.stringify(newRecord));
|
||||
console.error("update: Ignoring invalid update",
|
||||
aKeyID, newRecord);
|
||||
return;
|
||||
}
|
||||
aStore.put(newRecord).onsuccess = aEvent => {
|
||||
debug("update: Update successful for key ID " + aKeyID);
|
||||
console.debug("update: Update successful", aKeyID, newRecord);
|
||||
aTxn.result = newRecord;
|
||||
};
|
||||
};
|
||||
|
@ -444,7 +428,7 @@ this.PushDB.prototype = {
|
|||
},
|
||||
|
||||
drop: function() {
|
||||
debug("drop()");
|
||||
console.debug("drop()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
|
@ -458,9 +442,4 @@ this.PushDB.prototype = {
|
|||
)
|
||||
);
|
||||
},
|
||||
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
if ((aTopic == "nsPref:changed") && (aData == "dom.push.debug"))
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -39,7 +39,7 @@ PushNotificationService.prototype = {
|
|||
Ci.nsIPushNotificationService]),
|
||||
|
||||
register: function register(scope, originAttributes) {
|
||||
return PushService._register({
|
||||
return PushService.register({
|
||||
scope: scope,
|
||||
originAttributes: originAttributes,
|
||||
maxQuota: Infinity,
|
||||
|
@ -47,11 +47,11 @@ PushNotificationService.prototype = {
|
|||
},
|
||||
|
||||
unregister: function unregister(scope, originAttributes) {
|
||||
return PushService._unregister({scope, originAttributes});
|
||||
return PushService.unregister({scope, originAttributes});
|
||||
},
|
||||
|
||||
registration: function registration(scope, originAttributes) {
|
||||
return PushService._registration({scope, originAttributes});
|
||||
return PushService.registration({scope, originAttributes});
|
||||
},
|
||||
|
||||
clearAll: function clearAll() {
|
||||
|
|
|
@ -5,15 +5,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
// Don't modify this, instead set dom.push.debug.
|
||||
var gDebuggingEnabled = false;
|
||||
|
||||
function debug(s) {
|
||||
if (gDebuggingEnabled) {
|
||||
dump("-*- PushService.jsm: " + s + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
@ -38,9 +29,15 @@ XPCOMUtils.defineLazyModuleGetter(this, "AlarmService",
|
|||
|
||||
this.EXPORTED_SYMBOLS = ["PushService"];
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "dom.push.loglevel",
|
||||
prefix: "PushService",
|
||||
});
|
||||
});
|
||||
|
||||
const prefs = new Preferences("dom.push.");
|
||||
// Set debug first so that all debugging actually works.
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
|
||||
const kCHILD_PROCESS_MESSAGES = ["Push:Register", "Push:Unregister",
|
||||
"Push:Registration", "Push:RegisterEventNotificationListener",
|
||||
|
@ -120,7 +117,7 @@ this.PushService = {
|
|||
_activated: null,
|
||||
_checkActivated: function() {
|
||||
if (this._state < PUSH_SERVICE_ACTIVATING) {
|
||||
return Promise.reject({state: 0, error: "Service not active"});
|
||||
return Promise.reject(new Error("Push service not active"));
|
||||
} else if (this._state > PUSH_SERVICE_ACTIVATING) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
|
@ -152,7 +149,7 @@ this.PushService = {
|
|||
},
|
||||
|
||||
_setState: function(aNewState) {
|
||||
debug("new state: " + aNewState + " old state: " + this._state);
|
||||
console.debug("setState()", "new state", aNewState, "old state", this._state);
|
||||
|
||||
if (this._state == aNewState) {
|
||||
return;
|
||||
|
@ -164,7 +161,7 @@ this.PushService = {
|
|||
this._state = aNewState;
|
||||
if (this._notifyActivated) {
|
||||
if (aNewState < PUSH_SERVICE_ACTIVATING) {
|
||||
this._notifyActivated.reject({state: 0, error: "Service not active"});
|
||||
this._notifyActivated.reject(new Error("Push service not active"));
|
||||
} else {
|
||||
this._notifyActivated.resolve();
|
||||
}
|
||||
|
@ -176,7 +173,7 @@ this.PushService = {
|
|||
},
|
||||
|
||||
_changeStateOfflineEvent: function(offline, calledFromConnEnabledEvent) {
|
||||
debug("changeStateOfflineEvent: " + offline);
|
||||
console.debug("changeStateOfflineEvent()", offline);
|
||||
|
||||
if (this._state < PUSH_SERVICE_ACTIVE_OFFLINE &&
|
||||
this._state != PUSH_SERVICE_ACTIVATING &&
|
||||
|
@ -208,7 +205,7 @@ this.PushService = {
|
|||
},
|
||||
|
||||
_changeStateConnectionEnabledEvent: function(enabled) {
|
||||
debug("changeStateConnectionEnabledEvent: " + enabled);
|
||||
console.debug("changeStateConnectionEnabledEvent()", enabled);
|
||||
|
||||
if (this._state < PUSH_SERVICE_CONNECTION_DISABLE &&
|
||||
this._state != PUSH_SERVICE_ACTIVATING) {
|
||||
|
@ -244,7 +241,7 @@ this.PushService = {
|
|||
|
||||
case "nsPref:changed":
|
||||
if (aData == "dom.push.serverURL") {
|
||||
debug("dom.push.serverURL changed! websocket. new value " +
|
||||
console.debug("observe: dom.push.serverURL changed for websocket",
|
||||
prefs.get("serverURL"));
|
||||
this._stateChangeProcessEnqueue(_ =>
|
||||
this._changeServerURL(prefs.get("serverURL"),
|
||||
|
@ -255,9 +252,6 @@ this.PushService = {
|
|||
this._stateChangeProcessEnqueue(_ =>
|
||||
this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled"))
|
||||
);
|
||||
|
||||
} else if (aData == "dom.push.debug") {
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -267,19 +261,19 @@ this.PushService = {
|
|||
|
||||
case "perm-changed":
|
||||
this._onPermissionChange(aSubject, aData).catch(error => {
|
||||
debug("onPermissionChange: Error updating registrations: " +
|
||||
console.error("onPermissionChange: Error updating registrations:",
|
||||
error);
|
||||
})
|
||||
break;
|
||||
|
||||
case "webapps-clear-data":
|
||||
debug("webapps-clear-data");
|
||||
console.debug("webapps-clear-data");
|
||||
|
||||
let data = aSubject
|
||||
.QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
|
||||
if (!data) {
|
||||
debug("webapps-clear-data: Failed to get information about " +
|
||||
"application");
|
||||
console.error("webapps-clear-data: Failed to get information " +
|
||||
"about application");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -289,12 +283,12 @@ this.PushService = {
|
|||
this._db.getAllByOriginAttributes(originAttributes)
|
||||
.then(records => Promise.all(records.map(record =>
|
||||
this._db.delete(record.keyID).then(
|
||||
_ => this._unregisterIfConnected(record),
|
||||
_ => this._backgroundUnregister(record),
|
||||
err => {
|
||||
debug("webapps-clear-data: " + record.scope +
|
||||
" Could not delete entry " + record.channelID);
|
||||
console.error("webapps-clear-data: Error removing record",
|
||||
record, err);
|
||||
|
||||
return this._unregisterIfConnected(record);
|
||||
this._backgroundUnregister(record);
|
||||
})
|
||||
)
|
||||
));
|
||||
|
@ -303,14 +297,14 @@ this.PushService = {
|
|||
}
|
||||
},
|
||||
|
||||
_unregisterIfConnected: function(record) {
|
||||
_backgroundUnregister: function(record) {
|
||||
if (this._service.isConnected()) {
|
||||
// courtesy, but don't establish a connection
|
||||
// just for it
|
||||
debug("Had a connection, so telling the server");
|
||||
return this._sendUnregister({channelID: record.channelID})
|
||||
console.debug("backgroundUnregister: Notifying server", record);
|
||||
return this._sendUnregister(record)
|
||||
.catch(function(e) {
|
||||
debug("Unregister errored " + e);
|
||||
console.error("backgroundUnregister: Error notifying server", e);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -342,7 +336,7 @@ this.PushService = {
|
|||
},
|
||||
|
||||
_changeServerURL: function(serverURI, event) {
|
||||
debug("changeServerURL");
|
||||
console.debug("changeServerURL()");
|
||||
|
||||
switch(event) {
|
||||
case UNINIT_EVENT:
|
||||
|
@ -414,7 +408,7 @@ this.PushService = {
|
|||
* PUSH_SERVICE_CONNECTION_DISABLE.
|
||||
*/
|
||||
init: function(options = {}) {
|
||||
debug("init()");
|
||||
console.debug("init()");
|
||||
|
||||
if (this._state > PUSH_SERVICE_UNINIT) {
|
||||
return;
|
||||
|
@ -422,9 +416,6 @@ this.PushService = {
|
|||
|
||||
this._setState(PUSH_SERVICE_ACTIVATING);
|
||||
|
||||
// Debugging
|
||||
prefs.observe("debug", this);
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
|
||||
if (options.serverURI) {
|
||||
|
@ -469,7 +460,7 @@ this.PushService = {
|
|||
},
|
||||
|
||||
_startObservers: function() {
|
||||
debug("startObservers");
|
||||
console.debug("startObservers()");
|
||||
|
||||
if (this._state != PUSH_SERVICE_ACTIVATING) {
|
||||
return;
|
||||
|
@ -510,7 +501,7 @@ this.PushService = {
|
|||
},
|
||||
|
||||
_startService: function(service, serverURI, event, options = {}) {
|
||||
debug("startService");
|
||||
console.debug("startService()");
|
||||
|
||||
if (this._state != PUSH_SERVICE_ACTIVATING) {
|
||||
return;
|
||||
|
@ -549,7 +540,7 @@ this.PushService = {
|
|||
* state is change to PUSH_SERVICE_UNINIT
|
||||
*/
|
||||
_stopService: function(event) {
|
||||
debug("stopService");
|
||||
console.debug("stopService()");
|
||||
|
||||
if (this._state < PUSH_SERVICE_ACTIVATING) {
|
||||
return;
|
||||
|
@ -593,13 +584,12 @@ this.PushService = {
|
|||
},
|
||||
|
||||
_stopObservers: function() {
|
||||
debug("stopObservers()");
|
||||
console.debug("stopObservers()");
|
||||
|
||||
if (this._state < PUSH_SERVICE_ACTIVATING) {
|
||||
return;
|
||||
}
|
||||
|
||||
prefs.ignore("debug", this);
|
||||
prefs.ignore("connection.enabled", this);
|
||||
|
||||
Services.obs.removeObserver(this, this._networkStateChangeEventName);
|
||||
|
@ -609,7 +599,7 @@ this.PushService = {
|
|||
},
|
||||
|
||||
uninit: function() {
|
||||
debug("uninit()");
|
||||
console.debug("uninit()");
|
||||
|
||||
this._childListeners = [];
|
||||
|
||||
|
@ -624,7 +614,7 @@ this.PushService = {
|
|||
|
||||
this._stateChangeProcessEnqueue(_ =>
|
||||
this._changeServerURL("", UNINIT_EVENT));
|
||||
debug("shutdown complete!");
|
||||
console.debug("uninit: shutdown complete!");
|
||||
},
|
||||
|
||||
/** |delay| should be in milliseconds. */
|
||||
|
@ -654,7 +644,8 @@ this.PushService = {
|
|||
}
|
||||
}, (alarmID) => {
|
||||
this._alarmID = alarmID;
|
||||
debug("Set alarm " + delay + " in the future " + this._alarmID);
|
||||
console.debug("setAlarm: Set alarm", delay, "in the future",
|
||||
this._alarmID);
|
||||
this._settingAlarm = false;
|
||||
|
||||
if (this._waitingForAlarmSet) {
|
||||
|
@ -667,7 +658,7 @@ this.PushService = {
|
|||
|
||||
stopAlarm: function() {
|
||||
if (this._alarmID !== null) {
|
||||
debug("Stopped existing alarm " + this._alarmID);
|
||||
console.debug("stopAlarm: Stopped existing alarm", this._alarmID);
|
||||
AlarmService.remove(this._alarmID);
|
||||
this._alarmID = null;
|
||||
}
|
||||
|
@ -716,7 +707,7 @@ this.PushService = {
|
|||
// Fires a push-register system message to all applications that have
|
||||
// registration.
|
||||
_notifyAllAppsRegister: function() {
|
||||
debug("notifyAllAppsRegister()");
|
||||
console.debug("notifyAllAppsRegister()");
|
||||
// records are objects describing the registration as stored in IndexedDB.
|
||||
return this._db.getAllUnexpired().then(records => {
|
||||
records.forEach(record => {
|
||||
|
@ -792,7 +783,7 @@ this.PushService = {
|
|||
* versions.
|
||||
*/
|
||||
receivedPushMessage: function(keyID, message, cryptoParams, updateFunc) {
|
||||
debug("receivedPushMessage()");
|
||||
console.debug("receivedPushMessage()");
|
||||
Services.telemetry.getHistogramById("PUSH_API_NOTIFICATION_RECEIVED").add();
|
||||
|
||||
let shouldNotify = false;
|
||||
|
@ -820,7 +811,8 @@ this.PushService = {
|
|||
// this, we check if the record has expired before *and* after updating
|
||||
// the quota.
|
||||
if (newRecord.isExpired()) {
|
||||
debug("receivedPushMessage: Ignoring update for expired key ID " + keyID);
|
||||
console.error("receivedPushMessage: Ignoring update for expired key ID",
|
||||
keyID);
|
||||
return null;
|
||||
}
|
||||
newRecord.receivedPush(lastVisit);
|
||||
|
@ -852,26 +844,23 @@ this.PushService = {
|
|||
// Drop the registration in the background. If the user returns to the
|
||||
// site, the service worker will be notified on the next `idle-daily`
|
||||
// event.
|
||||
this._sendUnregister(record).catch(error => {
|
||||
debug("receivedPushMessage: Unregister error: " + error);
|
||||
});
|
||||
this._backgroundUnregister(record);
|
||||
}
|
||||
return notified;
|
||||
});
|
||||
}).catch(error => {
|
||||
debug("receivedPushMessage: Error notifying app: " + error);
|
||||
console.error("receivedPushMessage: Error notifying app", error);
|
||||
});
|
||||
},
|
||||
|
||||
_notifyApp: function(aPushRecord, message) {
|
||||
if (!aPushRecord || !aPushRecord.scope ||
|
||||
aPushRecord.originAttributes === undefined) {
|
||||
debug("notifyApp() something is undefined. Dropping notification: " +
|
||||
JSON.stringify(aPushRecord) );
|
||||
console.error("notifyApp: Invalid record", aPushRecord);
|
||||
return false;
|
||||
}
|
||||
|
||||
debug("notifyApp() " + aPushRecord.scope);
|
||||
console.debug("notifyApp()", aPushRecord.scope);
|
||||
// Notify XPCOM observers.
|
||||
let notification = Cc["@mozilla.org/push/ObserverNotification;1"]
|
||||
.createInstance(Ci.nsIPushObserverNotification);
|
||||
|
@ -898,7 +887,7 @@ this.PushService = {
|
|||
|
||||
// If permission has been revoked, trash the message.
|
||||
if (!aPushRecord.hasPermission()) {
|
||||
debug("Does not have permission for push.");
|
||||
console.warn("notifyApp: Missing push permission", aPushRecord);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -923,12 +912,12 @@ this.PushService = {
|
|||
|
||||
_sendRequest: function(action, aRecord) {
|
||||
if (this._state == PUSH_SERVICE_CONNECTION_DISABLE) {
|
||||
return Promise.reject({state: 0, error: "Service not active"});
|
||||
return Promise.reject(new Error("Push service disabled"));
|
||||
} else if (this._state == PUSH_SERVICE_ACTIVE_OFFLINE) {
|
||||
if (this._service.serviceType() == "WebSocket" && action == "unregister") {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject({state: 0, error: "NetworkError"});
|
||||
return Promise.reject(new Error("Push service offline"));
|
||||
}
|
||||
return this._service.request(action, aRecord);
|
||||
},
|
||||
|
@ -938,7 +927,7 @@ this.PushService = {
|
|||
* navigator.push, identifying the sending page and other fields.
|
||||
*/
|
||||
_registerWithServer: function(aPageRecord) {
|
||||
debug("registerWithServer()" + JSON.stringify(aPageRecord));
|
||||
console.debug("registerWithServer()", aPageRecord);
|
||||
|
||||
Services.telemetry.getHistogramById("PUSH_API_SUBSCRIBE_ATTEMPT").add();
|
||||
return this._sendRequest("register", aPageRecord)
|
||||
|
@ -946,42 +935,13 @@ this.PushService = {
|
|||
err => this._onRegisterError(err))
|
||||
.then(record => {
|
||||
this._deletePendingRequest(aPageRecord);
|
||||
return record;
|
||||
return record.toRegister();
|
||||
}, err => {
|
||||
this._deletePendingRequest(aPageRecord);
|
||||
throw err;
|
||||
});
|
||||
},
|
||||
|
||||
_register: function(aPageRecord) {
|
||||
debug("_register()");
|
||||
if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
|
||||
return Promise.reject({state: 0, error: "NotFoundError"});
|
||||
}
|
||||
|
||||
return this._checkActivated()
|
||||
.then(_ => this._db.getByIdentifiers(aPageRecord))
|
||||
.then(record => {
|
||||
if (!record) {
|
||||
return this._lookupOrPutPendingRequest(aPageRecord);
|
||||
}
|
||||
if (record.isExpired()) {
|
||||
return record.quotaChanged().then(isChanged => {
|
||||
if (isChanged) {
|
||||
// If the user revisited the site, drop the expired push
|
||||
// registration and re-register.
|
||||
return this._db.delete(record.keyID);
|
||||
}
|
||||
throw {state: 0, error: "NotFoundError"};
|
||||
}).then(_ => this._lookupOrPutPendingRequest(aPageRecord));
|
||||
}
|
||||
return record;
|
||||
}, error => {
|
||||
debug("getByIdentifiers failed");
|
||||
throw error;
|
||||
});
|
||||
},
|
||||
|
||||
_sendUnregister: function(aRecord) {
|
||||
Services.telemetry.getHistogramById("PUSH_API_UNSUBSCRIBE_ATTEMPT").add();
|
||||
return this._sendRequest("unregister", aRecord).then(function(v) {
|
||||
|
@ -998,7 +958,7 @@ this.PushService = {
|
|||
* from _service.request, causing the promise to be rejected instead.
|
||||
*/
|
||||
_onRegisterSuccess: function(aRecord) {
|
||||
debug("_onRegisterSuccess()");
|
||||
console.debug("_onRegisterSuccess()");
|
||||
|
||||
return this._db.put(aRecord)
|
||||
.then(record => {
|
||||
|
@ -1008,10 +968,7 @@ this.PushService = {
|
|||
.catch(error => {
|
||||
Services.telemetry.getHistogramById("PUSH_API_SUBSCRIBE_FAILED").add()
|
||||
// Unable to save. Destroy the subscription in the background.
|
||||
this._sendUnregister(aRecord).catch(err => {
|
||||
debug("_onRegisterSuccess: Error unregistering stale subscription" +
|
||||
err);
|
||||
});
|
||||
this._backgroundUnregister(aRecord);
|
||||
throw error;
|
||||
});
|
||||
},
|
||||
|
@ -1021,34 +978,36 @@ this.PushService = {
|
|||
* from _service.request, causing the promise to be rejected instead.
|
||||
*/
|
||||
_onRegisterError: function(reply) {
|
||||
debug("_onRegisterError()");
|
||||
console.debug("_onRegisterError()");
|
||||
Services.telemetry.getHistogramById("PUSH_API_SUBSCRIBE_FAILED").add()
|
||||
if (!reply.error) {
|
||||
debug("Called without valid error message!");
|
||||
throw "Registration error";
|
||||
console.warn("onRegisterError: Called without valid error message!",
|
||||
reply.error);
|
||||
throw new Error("Registration error");
|
||||
}
|
||||
throw reply.error;
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
debug("receiveMessage(): " + aMessage.name);
|
||||
console.debug("receiveMessage()", aMessage.name);
|
||||
|
||||
if (kCHILD_PROCESS_MESSAGES.indexOf(aMessage.name) == -1) {
|
||||
debug("Invalid message from child " + aMessage.name);
|
||||
console.debug("receiveMessage: Invalid message from child",
|
||||
aMessage.name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aMessage.name === "Push:RegisterEventNotificationListener") {
|
||||
debug("Adding child listener");
|
||||
console.debug("receiveMessage: Adding child listener");
|
||||
this._childListeners.push(aMessage.target);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aMessage.name === "child-process-shutdown") {
|
||||
debug("Possibly removing child listener");
|
||||
console.debug("receiveMessage: Possibly removing child listener");
|
||||
for (var i = this._childListeners.length - 1; i >= 0; --i) {
|
||||
if (this._childListeners[i] == aMessage.target) {
|
||||
debug("Removed child listener");
|
||||
console.debug("receiveMessage: Removed child listener");
|
||||
this._childListeners.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
@ -1056,54 +1015,73 @@ this.PushService = {
|
|||
}
|
||||
|
||||
if (!aMessage.target.assertPermission("push")) {
|
||||
debug("Got message from a child process that does not have 'push' permission.");
|
||||
console.debug("receiveMessage: Got message from a child process that",
|
||||
"does not have 'push' permission");
|
||||
return null;
|
||||
}
|
||||
|
||||
let mm = aMessage.target.QueryInterface(Ci.nsIMessageSender);
|
||||
let pageRecord = aMessage.data;
|
||||
|
||||
let name = aMessage.name.slice("Push:".length);
|
||||
Promise.resolve().then(_ => {
|
||||
let pageRecord = this._validatePageRecord(aMessage);
|
||||
return this[name.toLowerCase()](pageRecord);
|
||||
}).then(result => {
|
||||
mm.sendAsyncMessage("PushService:" + name + ":OK", {
|
||||
requestID: aMessage.data.requestID,
|
||||
result: result,
|
||||
});
|
||||
}, error => {
|
||||
console.error("receiveMessage: Error handling message", aMessage, error);
|
||||
mm.sendAsyncMessage("PushService:" + name + ":KO", {
|
||||
requestID: aMessage.data.requestID,
|
||||
});
|
||||
}).catch(error => {
|
||||
console.error("receiveMessage: Error sending reply", error);
|
||||
});
|
||||
},
|
||||
|
||||
_validatePageRecord: function(aMessage) {
|
||||
let principal = aMessage.principal;
|
||||
if (!principal) {
|
||||
debug("No principal passed!");
|
||||
let message = {
|
||||
requestID: pageRecord.requestID,
|
||||
error: "SecurityError"
|
||||
};
|
||||
mm.sendAsyncMessage("PushService:Register:KO", message);
|
||||
return;
|
||||
throw new Error("Missing message principal");
|
||||
}
|
||||
|
||||
let pageRecord = aMessage.data;
|
||||
if (!pageRecord.scope) {
|
||||
throw new Error("Missing page record scope");
|
||||
}
|
||||
|
||||
pageRecord.originAttributes =
|
||||
ChromeUtils.originAttributesToSuffix(principal.originAttributes);
|
||||
|
||||
if (!pageRecord.scope || pageRecord.originAttributes === undefined) {
|
||||
debug("Incorrect identifier values set! " + JSON.stringify(pageRecord));
|
||||
let message = {
|
||||
requestID: pageRecord.requestID,
|
||||
error: "SecurityError"
|
||||
};
|
||||
mm.sendAsyncMessage("PushService:Register:KO", message);
|
||||
return;
|
||||
}
|
||||
|
||||
this[aMessage.name.slice("Push:".length).toLowerCase()](pageRecord, mm);
|
||||
return pageRecord;
|
||||
},
|
||||
|
||||
register: function(aPageRecord, aMessageManager) {
|
||||
debug("register(): " + JSON.stringify(aPageRecord));
|
||||
register: function(aPageRecord) {
|
||||
console.debug("register()", aPageRecord);
|
||||
|
||||
this._register(aPageRecord)
|
||||
if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
|
||||
return Promise.reject(new Error("Invalid page record"));
|
||||
}
|
||||
|
||||
return this._checkActivated()
|
||||
.then(_ => this._db.getByIdentifiers(aPageRecord))
|
||||
.then(record => {
|
||||
let message = record.toRegister();
|
||||
message.requestID = aPageRecord.requestID;
|
||||
aMessageManager.sendAsyncMessage("PushService:Register:OK", message);
|
||||
}, error => {
|
||||
let message = {
|
||||
requestID: aPageRecord.requestID,
|
||||
error
|
||||
};
|
||||
aMessageManager.sendAsyncMessage("PushService:Register:KO", message);
|
||||
if (!record) {
|
||||
return this._lookupOrPutPendingRequest(aPageRecord);
|
||||
}
|
||||
if (record.isExpired()) {
|
||||
return record.quotaChanged().then(isChanged => {
|
||||
if (isChanged) {
|
||||
// If the user revisited the site, drop the expired push
|
||||
// registration and re-register.
|
||||
return this._db.delete(record.keyID);
|
||||
}
|
||||
throw new Error("Push subscription expired");
|
||||
}).then(_ => this._lookupOrPutPendingRequest(aPageRecord));
|
||||
}
|
||||
return record.toRegister();
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -1132,10 +1110,11 @@ this.PushService = {
|
|||
* client acknowledge. On a server, data is cheap, reliable notification is
|
||||
* not.
|
||||
*/
|
||||
_unregister: function(aPageRecord) {
|
||||
debug("_unregister()");
|
||||
unregister: function(aPageRecord) {
|
||||
console.debug("unregister()", aPageRecord);
|
||||
|
||||
if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
|
||||
return Promise.reject({state: 0, error: "NotFoundError"});
|
||||
return Promise.reject(new Error("Invalid page record"));
|
||||
}
|
||||
|
||||
return this._checkActivated()
|
||||
|
@ -1152,27 +1131,9 @@ this.PushService = {
|
|||
});
|
||||
},
|
||||
|
||||
unregister: function(aPageRecord, aMessageManager) {
|
||||
debug("unregister() " + JSON.stringify(aPageRecord));
|
||||
|
||||
this._unregister(aPageRecord)
|
||||
.then(result => {
|
||||
aMessageManager.sendAsyncMessage("PushService:Unregister:OK", {
|
||||
requestID: aPageRecord.requestID,
|
||||
result: result,
|
||||
})
|
||||
}, error => {
|
||||
debug("unregister(): Actual error " + error);
|
||||
aMessageManager.sendAsyncMessage("PushService:Unregister:KO", {
|
||||
requestID: aPageRecord.requestID,
|
||||
})
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
_clearAll: function _clearAll() {
|
||||
return this._checkActivated()
|
||||
.then(_ => this._db.clearAll())
|
||||
.then(_ => this._db.drop())
|
||||
.catch(_ => Promise.resolve());
|
||||
},
|
||||
|
||||
|
@ -1213,18 +1174,15 @@ this.PushService = {
|
|||
return this._checkActivated()
|
||||
.then(_ => clear(this._db, domain))
|
||||
.catch(e => {
|
||||
debug("Error forgetting about domain! " + e);
|
||||
console.warn("clearForDomain: Error forgetting about domain", e);
|
||||
return Promise.resolve();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Called on message from the child process
|
||||
*/
|
||||
_registration: function(aPageRecord) {
|
||||
debug("_registration()");
|
||||
registration: function(aPageRecord) {
|
||||
console.debug("registration()");
|
||||
if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
|
||||
return Promise.reject({state: 0, error: "NotFoundError"});
|
||||
return Promise.reject(new Error("Invalid page record"));
|
||||
}
|
||||
|
||||
return this._checkActivated()
|
||||
|
@ -1245,24 +1203,8 @@ this.PushService = {
|
|||
});
|
||||
},
|
||||
|
||||
registration: function(aPageRecord, aMessageManager) {
|
||||
debug("registration()");
|
||||
|
||||
return this._registration(aPageRecord)
|
||||
.then(registration =>
|
||||
aMessageManager.sendAsyncMessage("PushService:Registration:OK", {
|
||||
requestID: aPageRecord.requestID,
|
||||
registration
|
||||
}), error =>
|
||||
aMessageManager.sendAsyncMessage("PushService:Registration:KO", {
|
||||
requestID: aPageRecord.requestID,
|
||||
error
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
_dropExpiredRegistrations: function() {
|
||||
debug("dropExpiredRegistrations()");
|
||||
console.debug("dropExpiredRegistrations()");
|
||||
|
||||
return this._db.getAllExpired().then(records => {
|
||||
return Promise.all(records.map(record =>
|
||||
|
@ -1273,15 +1215,15 @@ this.PushService = {
|
|||
return this.dropRecordAndNotifyApp(record);
|
||||
}
|
||||
}).catch(error => {
|
||||
debug("dropExpiredRegistrations: Error dropping registration " +
|
||||
record.keyID + ": " + error);
|
||||
console.error("dropExpiredRegistrations: Error dropping registration",
|
||||
record.keyID, error);
|
||||
})
|
||||
));
|
||||
});
|
||||
},
|
||||
|
||||
_onPermissionChange: function(subject, data) {
|
||||
debug("onPermissionChange()");
|
||||
console.debug("onPermissionChange()");
|
||||
|
||||
if (data == "cleared") {
|
||||
// If the permission list was cleared, drop all registrations
|
||||
|
@ -1290,7 +1232,7 @@ this.PushService = {
|
|||
if (record.quotaApplies()) {
|
||||
if (!record.isExpired()) {
|
||||
// Drop the registration in the background.
|
||||
this._unregisterIfConnected(record);
|
||||
this._backgroundUnregister(record);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1307,7 +1249,7 @@ this.PushService = {
|
|||
},
|
||||
|
||||
_updatePermission: function(permission, type) {
|
||||
debug("updatePermission()");
|
||||
console.debug("updatePermission()");
|
||||
|
||||
let isAllow = permission.capability ==
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION;
|
||||
|
@ -1356,7 +1298,7 @@ this.PushService = {
|
|||
return null;
|
||||
}
|
||||
// Drop the registration in the background.
|
||||
this._unregisterIfConnected(record);
|
||||
this._backgroundUnregister(record);
|
||||
record.setQuota(0);
|
||||
return record;
|
||||
},
|
||||
|
|
|
@ -28,18 +28,16 @@ const {
|
|||
|
||||
this.EXPORTED_SYMBOLS = ["PushServiceHttp2"];
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "dom.push.loglevel",
|
||||
prefix: "PushServiceHttp2",
|
||||
});
|
||||
});
|
||||
|
||||
const prefs = new Preferences("dom.push.");
|
||||
|
||||
// Don't modify this, instead set dom.push.debug.
|
||||
// Set debug first so that all debugging actually works.
|
||||
var gDebuggingEnabled = prefs.get("debug");
|
||||
|
||||
function debug(s) {
|
||||
if (gDebuggingEnabled) {
|
||||
dump("-*- PushServiceHttp2.jsm: " + s + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
const kPUSHHTTP2DB_DB_NAME = "pushHttp2";
|
||||
const kPUSHHTTP2DB_DB_VERSION = 5; // Change this if the IndexedDB format changes
|
||||
const kPUSHHTTP2DB_STORE_NAME = "pushHttp2";
|
||||
|
@ -53,7 +51,7 @@ const kPUSHHTTP2DB_STORE_NAME = "pushHttp2";
|
|||
* It's easier to stop listening than to have checks at specific points.
|
||||
*/
|
||||
var PushSubscriptionListener = function(pushService, uri) {
|
||||
debug("Creating a new pushSubscription listener.");
|
||||
console.debug("PushSubscriptionListener()");
|
||||
this._pushService = pushService;
|
||||
this.uri = uri;
|
||||
};
|
||||
|
@ -73,12 +71,12 @@ PushSubscriptionListener.prototype = {
|
|||
},
|
||||
|
||||
onStartRequest: function(aRequest, aContext) {
|
||||
debug("PushSubscriptionListener onStartRequest()");
|
||||
console.debug("PushSubscriptionListener: onStartRequest()");
|
||||
// We do not do anything here.
|
||||
},
|
||||
|
||||
onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
|
||||
debug("PushSubscriptionListener onDataAvailable()");
|
||||
console.debug("PushSubscriptionListener: onDataAvailable()");
|
||||
// Nobody should send data, but just to be sure, otherwise necko will
|
||||
// complain.
|
||||
if (aCount === 0) {
|
||||
|
@ -93,7 +91,7 @@ PushSubscriptionListener.prototype = {
|
|||
},
|
||||
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
debug("PushSubscriptionListener onStopRequest()");
|
||||
console.debug("PushSubscriptionListener: onStopRequest()");
|
||||
if (!this._pushService) {
|
||||
return;
|
||||
}
|
||||
|
@ -104,7 +102,7 @@ PushSubscriptionListener.prototype = {
|
|||
},
|
||||
|
||||
onPush: function(associatedChannel, pushChannel) {
|
||||
debug("PushSubscriptionListener onPush()");
|
||||
console.debug("PushSubscriptionListener: onPush()");
|
||||
var pushChannelListener = new PushChannelListener(this);
|
||||
pushChannel.asyncOpen(pushChannelListener, pushChannel);
|
||||
},
|
||||
|
@ -119,7 +117,7 @@ PushSubscriptionListener.prototype = {
|
|||
* OnDataAvailable and send to the app in OnStopRequest.
|
||||
*/
|
||||
var PushChannelListener = function(pushSubscriptionListener) {
|
||||
debug("Creating a new push channel listener.");
|
||||
console.debug("PushChannelListener()");
|
||||
this._mainListener = pushSubscriptionListener;
|
||||
this._message = [];
|
||||
this._ackUri = null;
|
||||
|
@ -132,7 +130,7 @@ PushChannelListener.prototype = {
|
|||
},
|
||||
|
||||
onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
|
||||
debug("push channel listener onDataAvailable()");
|
||||
console.debug("PushChannelListener: onDataAvailable()");
|
||||
|
||||
if (aCount === 0) {
|
||||
return;
|
||||
|
@ -148,7 +146,8 @@ PushChannelListener.prototype = {
|
|||
},
|
||||
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
debug("push channel listener onStopRequest() status code:" + aStatusCode);
|
||||
console.debug("PushChannelListener: onStopRequest()", "status code",
|
||||
aStatusCode);
|
||||
if (Components.isSuccessCode(aStatusCode) &&
|
||||
this._mainListener &&
|
||||
this._mainListener._pushService) {
|
||||
|
@ -228,14 +227,14 @@ PushServiceDelete.prototype = {
|
|||
if (Components.isSuccessCode(aStatusCode)) {
|
||||
this._resolve();
|
||||
} else {
|
||||
this._reject({status: 0, error: "NetworkError"});
|
||||
this._reject(new Error("Error removing subscription: " + aStatusCode));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var SubscriptionListener = function(aSubInfo, aResolve, aReject,
|
||||
aServerURI, aPushServiceHttp2) {
|
||||
debug("Creating a new subscription listener.");
|
||||
console.debug("SubscriptionListener()");
|
||||
this._subInfo = aSubInfo;
|
||||
this._resolve = aResolve;
|
||||
this._reject = aReject;
|
||||
|
@ -250,7 +249,7 @@ SubscriptionListener.prototype = {
|
|||
onStartRequest: function(aRequest, aContext) {},
|
||||
|
||||
onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
|
||||
debug("subscription listener onDataAvailable()");
|
||||
console.debug("SubscriptionListener: onDataAvailable()");
|
||||
|
||||
// We do not expect any data, but necko will complain if we do not consume
|
||||
// it.
|
||||
|
@ -266,16 +265,16 @@ SubscriptionListener.prototype = {
|
|||
},
|
||||
|
||||
onStopRequest: function(aRequest, aContext, aStatus) {
|
||||
debug("subscription listener onStopRequest()");
|
||||
console.debug("SubscriptionListener: onStopRequest()");
|
||||
|
||||
// Check if pushService is still active.
|
||||
if (!this._service.hasmainPushService()) {
|
||||
this._reject({error: "Service deactivated"});
|
||||
this._reject(new Error("Push service unavailable"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Components.isSuccessCode(aStatus)) {
|
||||
this._reject({error: "Error status" + aStatus});
|
||||
this._reject(new Error("Error listening for messages: " + aStatus));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -292,11 +291,11 @@ SubscriptionListener.prototype = {
|
|||
}),
|
||||
retryAfter);
|
||||
} else {
|
||||
this._reject({error: "Error response code: " + statusCode });
|
||||
this._reject(new Error("Unexpected server response: " + statusCode));
|
||||
}
|
||||
return;
|
||||
} else if (statusCode != 201) {
|
||||
this._reject({error: "Error response code: " + statusCode });
|
||||
this._reject(new Error("Unexpected server response: " + statusCode));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -304,37 +303,39 @@ SubscriptionListener.prototype = {
|
|||
try {
|
||||
subscriptionUri = aRequest.getResponseHeader("location");
|
||||
} catch (err) {
|
||||
this._reject({error: "Return code 201, but the answer is bogus"});
|
||||
this._reject(new Error("Missing Location header"));
|
||||
return;
|
||||
}
|
||||
|
||||
debug("subscriptionUri: " + subscriptionUri);
|
||||
console.debug("onStopRequest: subscriptionUri", subscriptionUri);
|
||||
|
||||
var linkList;
|
||||
try {
|
||||
linkList = aRequest.getResponseHeader("link");
|
||||
} catch (err) {
|
||||
this._reject({error: "Return code 201, but the answer is bogus"});
|
||||
this._reject(new Error("Missing Link header"));
|
||||
return;
|
||||
}
|
||||
|
||||
var linkParserResult = linkParser(linkList, this._serverURI);
|
||||
if (linkParserResult.error) {
|
||||
this._reject(linkParserResult);
|
||||
var linkParserResult;
|
||||
try {
|
||||
linkParserResult = linkParser(linkList, this._serverURI);
|
||||
} catch (e) {
|
||||
this._reject(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!subscriptionUri) {
|
||||
this._reject({error: "Return code 201, but the answer is bogus," +
|
||||
" missing subscriptionUri"});
|
||||
this._reject(new Error("Invalid Location header"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let uriTry = Services.io.newURI(subscriptionUri, null, null);
|
||||
} catch (e) {
|
||||
debug("Invalid URI " + subscriptionUri);
|
||||
this._reject({error: "Return code 201, but URI is bogus. " +
|
||||
subscriptionUri});
|
||||
console.error("onStopRequest: Invalid subscription URI",
|
||||
subscriptionUri);
|
||||
this._reject(new Error("Invalid subscription endpoint: " +
|
||||
subscriptionUri));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -372,7 +373,7 @@ function linkParser(linkHeader, serverURI) {
|
|||
|
||||
var linkList = linkHeader.split(',');
|
||||
if ((linkList.length < 1)) {
|
||||
return {error: "Return code 201, but the answer is bogus"};
|
||||
throw new Error("Invalid Link header");
|
||||
}
|
||||
|
||||
var pushEndpoint;
|
||||
|
@ -393,32 +394,23 @@ function linkParser(linkHeader, serverURI) {
|
|||
}
|
||||
});
|
||||
|
||||
debug("pushEndpoint: " + pushEndpoint);
|
||||
debug("pushReceiptEndpoint: " + pushReceiptEndpoint);
|
||||
console.debug("linkParser: pushEndpoint", pushEndpoint);
|
||||
console.debug("linkParser: pushReceiptEndpoint", pushReceiptEndpoint);
|
||||
// Missing pushReceiptEndpoint is allowed.
|
||||
if (!pushEndpoint) {
|
||||
return {error: "Return code 201, but the answer is bogus, missing" +
|
||||
" pushEndpoint"};
|
||||
throw new Error("Missing push endpoint");
|
||||
}
|
||||
|
||||
var uri;
|
||||
var resUri = [];
|
||||
try {
|
||||
[pushEndpoint, pushReceiptEndpoint].forEach(u => {
|
||||
if (u) {
|
||||
uri = u;
|
||||
resUri[u] = Services.io.newURI(uri, null, serverURI);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
debug("Invalid URI " + uri);
|
||||
return {error: "Return code 201, but URI is bogus. " + uri};
|
||||
var pushURI = Services.io.newURI(pushEndpoint, null, serverURI);
|
||||
var pushReceiptURI;
|
||||
if (pushReceiptEndpoint) {
|
||||
pushReceiptURI = Services.io.newURI(pushReceiptEndpoint, null,
|
||||
serverURI);
|
||||
}
|
||||
|
||||
return {
|
||||
pushEndpoint: resUri[pushEndpoint].spec,
|
||||
pushReceiptEndpoint: (pushReceiptEndpoint) ? resUri[pushReceiptEndpoint].spec
|
||||
: ""
|
||||
pushEndpoint: pushURI.spec,
|
||||
pushReceiptEndpoint: (pushReceiptURI) ? pushReceiptURI.spec : "",
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -451,7 +443,7 @@ this.PushServiceHttp2 = {
|
|||
|
||||
checkServerURI: function(serverURL) {
|
||||
if (!serverURL) {
|
||||
debug("No dom.push.serverURL found!");
|
||||
console.warn("checkServerURI: No dom.push.serverURL found");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -459,26 +451,18 @@ this.PushServiceHttp2 = {
|
|||
try {
|
||||
uri = Services.io.newURI(serverURL, null, null);
|
||||
} catch(e) {
|
||||
debug("Error creating valid URI from dom.push.serverURL (" +
|
||||
serverURL + ")");
|
||||
console.warn("checkServerURI: Error creating valid URI from",
|
||||
"dom.push.serverURL", serverURL);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (uri.scheme !== "https") {
|
||||
debug("Unsupported websocket scheme " + uri.scheme);
|
||||
console.warn("checkServerURI: Unsupported scheme", uri.scheme);
|
||||
return null;
|
||||
}
|
||||
return uri;
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "nsPref:changed") {
|
||||
if (aData == "dom.push.debug") {
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
connect: function(subscriptions) {
|
||||
this.startConnections(subscriptions);
|
||||
},
|
||||
|
@ -516,7 +500,7 @@ this.PushServiceHttp2 = {
|
|||
* Subscribe new resource.
|
||||
*/
|
||||
_subscribeResource: function(aRecord) {
|
||||
debug("subscribeResource()");
|
||||
console.debug("subscribeResource()");
|
||||
|
||||
return this._subscribeResourceInternal({
|
||||
record: aRecord,
|
||||
|
@ -541,7 +525,7 @@ this.PushServiceHttp2 = {
|
|||
},
|
||||
|
||||
_subscribeResourceInternal: function(aSubInfo) {
|
||||
debug("subscribeResourceInternal()");
|
||||
console.debug("subscribeResourceInternal()");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
var listener = new SubscriptionListener(aSubInfo,
|
||||
|
@ -552,11 +536,7 @@ this.PushServiceHttp2 = {
|
|||
|
||||
var chan = this._makeChannel(this._serverURI.spec);
|
||||
chan.requestMethod = "POST";
|
||||
try {
|
||||
chan.asyncOpen(listener, null);
|
||||
} catch(e) {
|
||||
reject({status: 0, error: "NetworkError"});
|
||||
}
|
||||
chan.asyncOpen(listener, null);
|
||||
})
|
||||
.catch(err => {
|
||||
if ("retry" in err) {
|
||||
|
@ -572,11 +552,7 @@ this.PushServiceHttp2 = {
|
|||
return new Promise((resolve,reject) => {
|
||||
var chan = this._makeChannel(aUri);
|
||||
chan.requestMethod = "DELETE";
|
||||
try {
|
||||
chan.asyncOpen(new PushServiceDelete(resolve, reject), null);
|
||||
} catch(err) {
|
||||
reject({status: 0, error: "NetworkError"});
|
||||
}
|
||||
chan.asyncOpen(new PushServiceDelete(resolve, reject), null);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -585,7 +561,7 @@ this.PushServiceHttp2 = {
|
|||
* We can't do anything about it if it fails, so we don't listen for response.
|
||||
*/
|
||||
_unsubscribeResource: function(aSubscriptionUri) {
|
||||
debug("unsubscribeResource()");
|
||||
console.debug("unsubscribeResource()");
|
||||
|
||||
return this._deleteResource(aSubscriptionUri);
|
||||
},
|
||||
|
@ -594,9 +570,10 @@ this.PushServiceHttp2 = {
|
|||
* Start listening for messages.
|
||||
*/
|
||||
_listenForMsgs: function(aSubscriptionUri) {
|
||||
debug("listenForMsgs() " + aSubscriptionUri);
|
||||
console.debug("listenForMsgs()", aSubscriptionUri);
|
||||
if (!this._conns[aSubscriptionUri]) {
|
||||
debug("We do not have this subscription " + aSubscriptionUri);
|
||||
console.warn("listenForMsgs: We do not have this subscription",
|
||||
aSubscriptionUri);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -611,7 +588,8 @@ this.PushServiceHttp2 = {
|
|||
try {
|
||||
chan.asyncOpen(listener, chan);
|
||||
} catch (e) {
|
||||
debug("Error connecting to push server. asyncOpen failed!");
|
||||
console.error("listenForMsgs: Error connecting to push server.",
|
||||
"asyncOpen failed", e);
|
||||
conn.listener.disconnect();
|
||||
chan.cancel(Cr.NS_ERROR_ABORT);
|
||||
this._retryAfterBackoff(aSubscriptionUri, -1);
|
||||
|
@ -625,22 +603,20 @@ this.PushServiceHttp2 = {
|
|||
},
|
||||
|
||||
_ackMsgRecv: function(aAckUri) {
|
||||
debug("ackMsgRecv() " + aAckUri);
|
||||
console.debug("ackMsgRecv()", aAckUri);
|
||||
// We can't do anything about it if it fails,
|
||||
// so we don't listen for response.
|
||||
this._deleteResource(aAckUri);
|
||||
},
|
||||
|
||||
init: function(aOptions, aMainPushService, aServerURL) {
|
||||
debug("init()");
|
||||
console.debug("init()");
|
||||
this._mainPushService = aMainPushService;
|
||||
this._serverURI = aServerURL;
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
prefs.observe("debug", this);
|
||||
},
|
||||
|
||||
_retryAfterBackoff: function(aSubscriptionUri, retryAfter) {
|
||||
debug("retryAfterBackoff()");
|
||||
console.debug("retryAfterBackoff()");
|
||||
|
||||
var resetRetryCount = prefs.get("http2.reset_retry_count_after_ms");
|
||||
// If it was running for some time, reset retry counter.
|
||||
|
@ -680,12 +656,13 @@ this.PushServiceHttp2 = {
|
|||
this._conns[aSubscriptionUri].waitingForAlarm = true;
|
||||
this._mainPushService.setAlarm(retryAfter);
|
||||
}
|
||||
debug("Retry in " + retryAfter);
|
||||
|
||||
console.debug("retryAfterBackoff: Retry in", retryAfter);
|
||||
},
|
||||
|
||||
// Close connections.
|
||||
_shutdownConnections: function(deleteInfo) {
|
||||
debug("shutdownConnections()");
|
||||
console.debug("shutdownConnections()");
|
||||
|
||||
for (let subscriptionUri in this._conns) {
|
||||
if (this._conns[subscriptionUri]) {
|
||||
|
@ -710,20 +687,21 @@ this.PushServiceHttp2 = {
|
|||
|
||||
// Start listening if subscriptions present.
|
||||
startConnections: function(aSubscriptions) {
|
||||
debug("startConnections() " + aSubscriptions.length);
|
||||
console.debug("startConnections()", aSubscriptions.length);
|
||||
|
||||
for (let i = 0; i < aSubscriptions.length; i++) {
|
||||
let record = aSubscriptions[i];
|
||||
this._mainPushService.ensureP256dhKey(record).then(record => {
|
||||
this._startSingleConnection(record);
|
||||
}, error => {
|
||||
debug("startConnections: Error updating record " + record.keyID);
|
||||
console.error("startConnections: Error updating record",
|
||||
record.keyID, error);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_startSingleConnection: function(record) {
|
||||
debug("_startSingleConnection()");
|
||||
console.debug("_startSingleConnection()");
|
||||
if (typeof this._conns[record.subscriptionUri] != "object") {
|
||||
this._conns[record.subscriptionUri] = {channel: null,
|
||||
listener: null,
|
||||
|
@ -738,7 +716,7 @@ this.PushServiceHttp2 = {
|
|||
|
||||
// Start listening if subscriptions present.
|
||||
_startConnectionsWaitingForAlarm: function() {
|
||||
debug("startConnectionsWaitingForAlarm()");
|
||||
console.debug("startConnectionsWaitingForAlarm()");
|
||||
for (let subscriptionUri in this._conns) {
|
||||
if ((this._conns[subscriptionUri]) &&
|
||||
!this._conns[subscriptionUri].conn &&
|
||||
|
@ -751,7 +729,7 @@ this.PushServiceHttp2 = {
|
|||
|
||||
// Close connection and notify apps that subscription are gone.
|
||||
_shutdownSubscription: function(aSubscriptionUri) {
|
||||
debug("shutdownSubscriptions()");
|
||||
console.debug("shutdownSubscriptions()");
|
||||
|
||||
if (typeof this._conns[aSubscriptionUri] == "object") {
|
||||
if (this._conns[aSubscriptionUri].listener) {
|
||||
|
@ -768,7 +746,7 @@ this.PushServiceHttp2 = {
|
|||
},
|
||||
|
||||
uninit: function() {
|
||||
debug("uninit()");
|
||||
console.debug("uninit()");
|
||||
this._shutdownConnections(true);
|
||||
this._mainPushService = null;
|
||||
},
|
||||
|
@ -777,11 +755,12 @@ this.PushServiceHttp2 = {
|
|||
request: function(action, aRecord) {
|
||||
switch (action) {
|
||||
case "register":
|
||||
debug("register");
|
||||
return this._subscribeResource(aRecord);
|
||||
case "unregister":
|
||||
this._shutdownSubscription(aRecord.subscriptionUri);
|
||||
return this._unsubscribeResource(aRecord.subscriptionUri);
|
||||
default:
|
||||
return Promise.reject(new Error("Unknown request type: " + action));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -809,7 +788,7 @@ this.PushServiceHttp2 = {
|
|||
|
||||
connOnStop: function(aRequest, aSuccess,
|
||||
aSubscriptionUri) {
|
||||
debug("connOnStop() succeeded: " + aSuccess);
|
||||
console.debug("connOnStop() succeeded", aSuccess);
|
||||
|
||||
var conn = this._conns[aSubscriptionUri];
|
||||
if (!conn) {
|
||||
|
@ -840,7 +819,7 @@ this.PushServiceHttp2 = {
|
|||
},
|
||||
|
||||
_pushChannelOnStop: function(aUri, aAckUri, aMessage, dh, salt, rs) {
|
||||
debug("pushChannelOnStop() ");
|
||||
console.debug("pushChannelOnStop()");
|
||||
|
||||
let cryptoParams = {
|
||||
dh: dh,
|
||||
|
@ -855,7 +834,8 @@ this.PushServiceHttp2 = {
|
|||
)
|
||||
.then(_ => this._ackMsgRecv(aAckUri))
|
||||
.catch(err => {
|
||||
debug("Error receiving message: " + err);
|
||||
console.error("pushChannelOnStop: Error receiving message",
|
||||
err);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -51,8 +51,13 @@ const prefs = new Preferences("dom.push.");
|
|||
|
||||
this.EXPORTED_SYMBOLS = ["PushServiceWebSocket"];
|
||||
|
||||
// Don't modify this, instead set dom.push.debug.
|
||||
var gDebuggingEnabled = true;
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "dom.push.loglevel",
|
||||
prefix: "PushServiceWebSocket",
|
||||
});
|
||||
});
|
||||
|
||||
function getCryptoParams(headers) {
|
||||
if (!headers) {
|
||||
|
@ -75,15 +80,6 @@ function getCryptoParams(headers) {
|
|||
return {dh, salt, rs};
|
||||
}
|
||||
|
||||
function debug(s) {
|
||||
if (gDebuggingEnabled) {
|
||||
dump("-*- PushServiceWebSocket.jsm: " + s + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Set debug first so that all debugging actually works.
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
|
||||
/**
|
||||
* A proxy between the PushService and the WebSocket. The listener is used so
|
||||
* that the PushService can silence messages from the WebSocket by setting
|
||||
|
@ -169,9 +165,7 @@ this.PushServiceWebSocket = {
|
|||
|
||||
switch (aTopic) {
|
||||
case "nsPref:changed":
|
||||
if (aData == "dom.push.debug") {
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
} else if (aData == "dom.push.userAgentID") {
|
||||
if (aData == "dom.push.userAgentID") {
|
||||
this._shutdownWS();
|
||||
this._reconnectAfterBackoff();
|
||||
}
|
||||
|
@ -190,10 +184,10 @@ this.PushServiceWebSocket = {
|
|||
// also made to fail, since we are going to be disconnecting the
|
||||
// socket.
|
||||
if (requestTimedOut || duration > this._requestTimeout) {
|
||||
debug("Request timeout: Removing " + channelID);
|
||||
requestTimedOut = true;
|
||||
this._registerRequests[channelID]
|
||||
.reject({status: 0, error: "TimeoutError"});
|
||||
.reject(new Error("Register request timed out for channel ID " +
|
||||
channelID));
|
||||
|
||||
delete this._registerRequests[channelID];
|
||||
}
|
||||
|
@ -211,7 +205,7 @@ this.PushServiceWebSocket = {
|
|||
|
||||
checkServerURI: function(serverURL) {
|
||||
if (!serverURL) {
|
||||
debug("No dom.push.serverURL found!");
|
||||
console.warn("checkServerURI: No dom.push.serverURL found");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -219,13 +213,13 @@ this.PushServiceWebSocket = {
|
|||
try {
|
||||
uri = Services.io.newURI(serverURL, null, null);
|
||||
} catch(e) {
|
||||
debug("Error creating valid URI from dom.push.serverURL (" +
|
||||
serverURL + ")");
|
||||
console.warn("checkServerURI: Error creating valid URI from",
|
||||
"dom.push.serverURL", serverURL);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (uri.scheme !== "wss") {
|
||||
debug("Unsupported websocket scheme " + uri.scheme);
|
||||
console.warn("checkServerURI: Unsupported websocket scheme", uri.scheme);
|
||||
return null;
|
||||
}
|
||||
return uri;
|
||||
|
@ -237,11 +231,11 @@ this.PushServiceWebSocket = {
|
|||
|
||||
set _UAID(newID) {
|
||||
if (typeof(newID) !== "string") {
|
||||
debug("Got invalid, non-string UAID " + newID +
|
||||
". Not updating userAgentID");
|
||||
console.warn("Got invalid, non-string UAID", newID,
|
||||
"Not updating userAgentID");
|
||||
return;
|
||||
}
|
||||
debug("New _UAID: " + newID);
|
||||
console.debug("New _UAID", newID);
|
||||
prefs.set("userAgentID", newID);
|
||||
},
|
||||
|
||||
|
@ -308,16 +302,17 @@ this.PushServiceWebSocket = {
|
|||
*/
|
||||
_wsSendMessage: function(msg) {
|
||||
if (!this._ws) {
|
||||
debug("No WebSocket initialized. Cannot send a message.");
|
||||
console.warn("wsSendMessage: No WebSocket initialized.",
|
||||
"Cannot send a message");
|
||||
return;
|
||||
}
|
||||
msg = JSON.stringify(msg);
|
||||
debug("Sending message: " + msg);
|
||||
console.debug("wsSendMessage: Sending message", msg);
|
||||
this._ws.sendMsg(msg);
|
||||
},
|
||||
|
||||
init: function(options, mainPushService, serverURI) {
|
||||
debug("init()");
|
||||
console.debug("init()");
|
||||
|
||||
this._mainPushService = mainPushService;
|
||||
this._serverURI = serverURI;
|
||||
|
@ -344,18 +339,16 @@ this.PushServiceWebSocket = {
|
|||
this._requestTimeout = prefs.get("requestTimeout");
|
||||
this._adaptiveEnabled = prefs.get('adaptive.enabled');
|
||||
this._upperLimit = prefs.get('adaptive.upperLimit');
|
||||
gDebuggingEnabled = prefs.get("debug");
|
||||
prefs.observe("debug", this);
|
||||
},
|
||||
|
||||
_reconnect: function () {
|
||||
debug("reconnect()");
|
||||
console.debug("reconnect()");
|
||||
this._shutdownWS(false);
|
||||
this._reconnectAfterBackoff();
|
||||
},
|
||||
|
||||
_shutdownWS: function(shouldCancelPending = true) {
|
||||
debug("shutdownWS()");
|
||||
console.debug("shutdownWS()");
|
||||
this._currentState = STATE_SHUT_DOWN;
|
||||
this._willBeWokenUpByUDP = false;
|
||||
|
||||
|
@ -373,7 +366,7 @@ this.PushServiceWebSocket = {
|
|||
if (this._mainPushService) {
|
||||
this._mainPushService.stopAlarm();
|
||||
} else {
|
||||
dump("This should not happend");
|
||||
console.error("shutdownWS: Uninitialized push service");
|
||||
}
|
||||
|
||||
if (shouldCancelPending) {
|
||||
|
@ -387,8 +380,6 @@ this.PushServiceWebSocket = {
|
|||
},
|
||||
|
||||
uninit: function() {
|
||||
prefs.ignore("debug", this);
|
||||
|
||||
if (this._udpServer) {
|
||||
this._udpServer.close();
|
||||
this._udpServer = null;
|
||||
|
@ -426,7 +417,7 @@ this.PushServiceWebSocket = {
|
|||
* cancelled), so the connection won't be reset.
|
||||
*/
|
||||
_reconnectAfterBackoff: function() {
|
||||
debug("reconnectAfterBackoff()");
|
||||
console.debug("reconnectAfterBackoff()");
|
||||
//Calculate new ping interval
|
||||
this._calculateAdaptivePing(true /* wsWentDown */);
|
||||
|
||||
|
@ -437,11 +428,12 @@ this.PushServiceWebSocket = {
|
|||
|
||||
this._retryFailCount++;
|
||||
|
||||
debug("Retry in " + retryTimeout + " Try number " + this._retryFailCount);
|
||||
console.debug("reconnectAfterBackoff: Retry in", retryTimeout,
|
||||
"Try number", this._retryFailCount);
|
||||
if (this._mainPushService) {
|
||||
this._mainPushService.setAlarm(retryTimeout);
|
||||
} else {
|
||||
dump("This should not happend");
|
||||
console.error("reconnectAfterBackoff: Uninitialized push service");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -471,22 +463,22 @@ this.PushServiceWebSocket = {
|
|||
*
|
||||
*/
|
||||
_calculateAdaptivePing: function(wsWentDown) {
|
||||
debug('_calculateAdaptivePing()');
|
||||
console.debug("_calculateAdaptivePing()");
|
||||
if (!this._adaptiveEnabled) {
|
||||
debug('Adaptive ping is disabled');
|
||||
console.debug("calculateAdaptivePing: Adaptive ping is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._retryFailCount > 0) {
|
||||
debug('Push has failed to connect to the Push Server ' +
|
||||
this._retryFailCount + ' times. ' +
|
||||
'Do not calculate a new pingInterval now');
|
||||
console.warn("calculateAdaptivePing: Push has failed to connect to the",
|
||||
"Push Server", this._retryFailCount, "times. Do not calculate a new",
|
||||
"pingInterval now");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._recalculatePing && !wsWentDown) {
|
||||
debug('We do not need to recalculate the ping now, based on previous ' +
|
||||
'data');
|
||||
console.debug("calculateAdaptivePing: We do not need to recalculate the",
|
||||
"ping now, based on previous data");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -495,15 +487,15 @@ this.PushServiceWebSocket = {
|
|||
|
||||
if (ns.ip) {
|
||||
// mobile
|
||||
debug('mobile');
|
||||
console.debug("calculateAdaptivePing: mobile");
|
||||
let oldNetwork = prefs.get('adaptive.mobile');
|
||||
let newNetwork = 'mobile-' + ns.mcc + '-' + ns.mnc;
|
||||
|
||||
// Mobile networks differ, reset all intervals and pings
|
||||
if (oldNetwork !== newNetwork) {
|
||||
// Network differ, reset all values
|
||||
debug('Mobile networks differ. Old network is ' + oldNetwork +
|
||||
' and new is ' + newNetwork);
|
||||
console.debug("calculateAdaptivePing: Mobile networks differ. Old",
|
||||
"network is", oldNetwork, "and new is", newNetwork);
|
||||
prefs.set('adaptive.mobile', newNetwork);
|
||||
//We reset the upper bound member
|
||||
this._recalculatePing = true;
|
||||
|
@ -522,7 +514,7 @@ this.PushServiceWebSocket = {
|
|||
|
||||
} else {
|
||||
// wifi
|
||||
debug('wifi');
|
||||
console.debug("calculateAdaptivePing: wifi");
|
||||
prefs.set('pingInterval', prefs.get('pingInterval.wifi'));
|
||||
this._lastGoodPingInterval = prefs.get('adaptive.lastGoodPingInterval.wifi');
|
||||
}
|
||||
|
@ -531,7 +523,8 @@ this.PushServiceWebSocket = {
|
|||
let lastTriedPingInterval = prefs.get('pingInterval');
|
||||
|
||||
if (wsWentDown) {
|
||||
debug('The WebSocket was disconnected, calculating next ping');
|
||||
console.debug("calculateAdaptivePing: The WebSocket was disconnected.",
|
||||
"Calculating next ping");
|
||||
|
||||
// If we have not tried this pingInterval yet, initialize
|
||||
this._pingIntervalRetryTimes[lastTriedPingInterval] =
|
||||
|
@ -540,8 +533,9 @@ this.PushServiceWebSocket = {
|
|||
// Try the pingInterval at least 3 times, just to be sure that the
|
||||
// calculated interval is not valid.
|
||||
if (this._pingIntervalRetryTimes[lastTriedPingInterval] < 2) {
|
||||
debug('pingInterval= ' + lastTriedPingInterval + ' tried only ' +
|
||||
this._pingIntervalRetryTimes[lastTriedPingInterval] + ' times');
|
||||
console.debug("calculateAdaptivePing: pingInterval=",
|
||||
lastTriedPingInterval, "tried only",
|
||||
this._pingIntervalRetryTimes[lastTriedPingInterval], "times");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -552,32 +546,33 @@ this.PushServiceWebSocket = {
|
|||
// optimum, so stop calculating.
|
||||
if (nextPingInterval - this._lastGoodPingInterval <
|
||||
prefs.get('adaptive.gap')) {
|
||||
debug('We have reached the gap, we have finished the calculation');
|
||||
debug('nextPingInterval=' + nextPingInterval);
|
||||
debug('lastGoodPing=' + this._lastGoodPingInterval);
|
||||
console.debug("calculateAdaptivePing: We have reached the gap, we",
|
||||
"have finished the calculation. nextPingInterval=", nextPingInterval,
|
||||
"lastGoodPing=", this._lastGoodPingInterval);
|
||||
nextPingInterval = this._lastGoodPingInterval;
|
||||
this._recalculatePing = false;
|
||||
} else {
|
||||
debug('We need to calculate next time');
|
||||
console.debug("calculateAdaptivePing: We need to calculate next time");
|
||||
this._recalculatePing = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
debug('The WebSocket is still up');
|
||||
console.debug("calculateAdaptivePing: The WebSocket is still up");
|
||||
this._lastGoodPingInterval = lastTriedPingInterval;
|
||||
nextPingInterval = Math.floor(lastTriedPingInterval * 1.5);
|
||||
}
|
||||
|
||||
// Check if we have reached the upper limit
|
||||
if (this._upperLimit < nextPingInterval) {
|
||||
debug('Next ping will be bigger than the configured upper limit, ' +
|
||||
'capping interval');
|
||||
console.debug("calculateAdaptivePing: Next ping will be bigger than the",
|
||||
"configured upper limit, capping interval");
|
||||
this._recalculatePing = false;
|
||||
this._lastGoodPingInterval = lastTriedPingInterval;
|
||||
nextPingInterval = lastTriedPingInterval;
|
||||
}
|
||||
|
||||
debug('Setting the pingInterval to ' + nextPingInterval);
|
||||
console.debug("calculateAdaptivePing: Setting the pingInterval to",
|
||||
nextPingInterval);
|
||||
prefs.set('pingInterval', nextPingInterval);
|
||||
|
||||
//Save values for our current network
|
||||
|
@ -594,11 +589,12 @@ this.PushServiceWebSocket = {
|
|||
|
||||
_makeWebSocket: function(uri) {
|
||||
if (!prefs.get("connection.enabled")) {
|
||||
debug("_makeWebSocket: connection.enabled is not set to true. Aborting.");
|
||||
console.warn("makeWebSocket: connection.enabled is not set to true.",
|
||||
"Aborting.");
|
||||
return null;
|
||||
}
|
||||
if (Services.io.offline) {
|
||||
debug("Network is offline.");
|
||||
console.warn("makeWebSocket: Network is offline.");
|
||||
return null;
|
||||
}
|
||||
let socket = Cc["@mozilla.org/network/protocol;1?name=wss"]
|
||||
|
@ -614,10 +610,10 @@ this.PushServiceWebSocket = {
|
|||
},
|
||||
|
||||
_beginWSSetup: function() {
|
||||
debug("beginWSSetup()");
|
||||
console.debug("beginWSSetup()");
|
||||
if (this._currentState != STATE_SHUT_DOWN) {
|
||||
debug("_beginWSSetup: Not in shutdown state! Current state " +
|
||||
this._currentState);
|
||||
console.error("_beginWSSetup: Not in shutdown state! Current state",
|
||||
this._currentState);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -636,7 +632,7 @@ this.PushServiceWebSocket = {
|
|||
}
|
||||
this._ws = socket.QueryInterface(Ci.nsIWebSocketChannel);
|
||||
|
||||
debug("serverURL: " + uri.spec);
|
||||
console.debug("beginWSSetup: Connecting to", uri.spec);
|
||||
this._wsListener = new PushWebSocketListener(this);
|
||||
this._ws.protocol = "push-notification";
|
||||
|
||||
|
@ -647,13 +643,14 @@ this.PushServiceWebSocket = {
|
|||
this._acquireWakeLock();
|
||||
this._currentState = STATE_WAITING_FOR_WS_START;
|
||||
} catch(e) {
|
||||
debug("Error opening websocket. asyncOpen failed!");
|
||||
console.error("beginWSSetup: Error opening websocket.",
|
||||
"asyncOpen failed", e);
|
||||
this._reconnect();
|
||||
}
|
||||
},
|
||||
|
||||
connect: function(records) {
|
||||
debug("connect");
|
||||
console.debug("connect()");
|
||||
// Check to see if we need to do anything.
|
||||
if (records.length > 0) {
|
||||
this._beginWSSetup();
|
||||
|
@ -694,7 +691,8 @@ this.PushServiceWebSocket = {
|
|||
// Conditions are arranged in decreasing specificity.
|
||||
// i.e. when _waitingForPong is true, other conditions are also true.
|
||||
if (this._waitingForPong) {
|
||||
debug("Did not receive pong in time. Reconnecting WebSocket.");
|
||||
console.debug("onAlarmFired: Did not receive pong in time.",
|
||||
"Reconnecting WebSocket");
|
||||
this._reconnect();
|
||||
}
|
||||
else if (this._currentState == STATE_READY) {
|
||||
|
@ -713,7 +711,7 @@ this.PushServiceWebSocket = {
|
|||
this._mainPushService.setAlarm(prefs.get("requestTimeout"));
|
||||
}
|
||||
else if (this._mainPushService && this._mainPushService._alarmID !== null) {
|
||||
debug("reconnect alarm fired.");
|
||||
console.debug("onAlarmFired: reconnect alarm fired");
|
||||
// Reconnect after back-off.
|
||||
// The check for a non-null _alarmID prevents a situation where the alarm
|
||||
// fires, but _shutdownWS() is called from another code-path (e.g.
|
||||
|
@ -738,16 +736,16 @@ this.PushServiceWebSocket = {
|
|||
|
||||
// Disable the wake lock on non-B2G platforms to work around bug 1154492.
|
||||
if (!this._socketWakeLock) {
|
||||
debug("Acquiring Socket Wakelock");
|
||||
console.debug("acquireWakeLock: Acquiring Socket Wakelock");
|
||||
this._socketWakeLock = gPowerManagerService.newWakeLock("cpu");
|
||||
}
|
||||
if (!this._socketWakeLockTimer) {
|
||||
debug("Creating Socket WakeLock Timer");
|
||||
console.debug("acquireWakeLock: Creating Socket WakeLock Timer");
|
||||
this._socketWakeLockTimer = Cc["@mozilla.org/timer;1"]
|
||||
.createInstance(Ci.nsITimer);
|
||||
}
|
||||
|
||||
debug("Setting Socket WakeLock Timer");
|
||||
console.debug("acquireWakeLock: Setting Socket WakeLock Timer");
|
||||
this._socketWakeLockTimer
|
||||
.initWithCallback(this._releaseWakeLock.bind(this),
|
||||
// Allow the same time for socket setup as we do for
|
||||
|
@ -763,7 +761,7 @@ this.PushServiceWebSocket = {
|
|||
return;
|
||||
}
|
||||
|
||||
debug("Releasing Socket WakeLock");
|
||||
console.debug("releaseWakeLock: Releasing Socket WakeLock");
|
||||
if (this._socketWakeLockTimer) {
|
||||
this._socketWakeLockTimer.cancel();
|
||||
}
|
||||
|
@ -777,30 +775,30 @@ this.PushServiceWebSocket = {
|
|||
* Protocol handler invoked by server message.
|
||||
*/
|
||||
_handleHelloReply: function(reply) {
|
||||
debug("handleHelloReply()");
|
||||
console.debug("handleHelloReply()");
|
||||
if (this._currentState != STATE_WAITING_FOR_HELLO) {
|
||||
debug("Unexpected state " + this._currentState +
|
||||
"(expected STATE_WAITING_FOR_HELLO)");
|
||||
console.error("handleHelloReply: Unexpected state", this._currentState,
|
||||
"(expected STATE_WAITING_FOR_HELLO)");
|
||||
this._shutdownWS();
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof reply.uaid !== "string") {
|
||||
debug("No UAID received or non string UAID received");
|
||||
console.error("handleHelloReply: Received invalid UAID", reply.uaid);
|
||||
this._shutdownWS();
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.uaid === "") {
|
||||
debug("Empty UAID received!");
|
||||
console.error("handleHelloReply: Received empty UAID");
|
||||
this._shutdownWS();
|
||||
return;
|
||||
}
|
||||
|
||||
// To avoid sticking extra large values sent by an evil server into prefs.
|
||||
if (reply.uaid.length > 128) {
|
||||
debug("UAID received from server was too long: " +
|
||||
reply.uaid);
|
||||
console.error("handleHelloReply: UAID received from server was too long",
|
||||
reply.uaid);
|
||||
this._shutdownWS();
|
||||
return;
|
||||
}
|
||||
|
@ -823,7 +821,8 @@ this.PushServiceWebSocket = {
|
|||
this._mainPushService.getAllUnexpired().then(records =>
|
||||
Promise.all(records.map(record =>
|
||||
this._mainPushService.ensureP256dhKey(record).catch(error => {
|
||||
debug("finishHandshake: Error updating record " + record.keyID);
|
||||
console.error("finishHandshake: Error updating record",
|
||||
record.keyID, error);
|
||||
})
|
||||
))
|
||||
).then(sendRequests);
|
||||
|
@ -839,7 +838,7 @@ this.PushServiceWebSocket = {
|
|||
// workers if we receive a new UAID. This ensures we expunge all stale
|
||||
// registrations if the `userAgentID` pref is reset.
|
||||
if (this._UAID != reply.uaid) {
|
||||
debug("got new UAID: all re-register");
|
||||
console.debug("handleHelloReply: Received new UAID");
|
||||
|
||||
this._mainPushService.dropRegistrations()
|
||||
.then(finishHandshake.bind(this));
|
||||
|
@ -855,7 +854,7 @@ this.PushServiceWebSocket = {
|
|||
* Protocol handler invoked by server message.
|
||||
*/
|
||||
_handleRegisterReply: function(reply) {
|
||||
debug("handleRegisterReply()");
|
||||
console.debug("handleRegisterReply()");
|
||||
if (typeof reply.channelID !== "string" ||
|
||||
typeof this._registerRequests[reply.channelID] !== "object") {
|
||||
return;
|
||||
|
@ -873,9 +872,7 @@ this.PushServiceWebSocket = {
|
|||
Services.io.newURI(reply.pushEndpoint, null, null);
|
||||
}
|
||||
catch (e) {
|
||||
debug("Invalid pushEndpoint " + reply.pushEndpoint);
|
||||
tmp.reject({state: 0, error: "Invalid pushEndpoint " +
|
||||
reply.pushEndpoint});
|
||||
tmp.reject(new Error("Invalid push endpoint: " + reply.pushEndpoint));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -888,18 +885,20 @@ this.PushServiceWebSocket = {
|
|||
quota: tmp.record.maxQuota,
|
||||
ctime: Date.now(),
|
||||
});
|
||||
dump("PushWebSocket " + JSON.stringify(record));
|
||||
Services.telemetry.getHistogramById("PUSH_API_SUBSCRIBE_WS_TIME").add(Date.now() - tmp.ctime);
|
||||
tmp.resolve(record);
|
||||
} else {
|
||||
tmp.reject(reply);
|
||||
console.error("handleRegisterReply: Unexpected server response", reply);
|
||||
tmp.reject(new Error("Wrong status code for register reply: " +
|
||||
reply.status));
|
||||
}
|
||||
},
|
||||
|
||||
_handleDataUpdate: function(update) {
|
||||
let promise;
|
||||
if (typeof update.channelID != "string") {
|
||||
debug("handleDataUpdate: Discarding message without channel ID");
|
||||
console.warn("handleDataUpdate: Discarding update without channel ID",
|
||||
update);
|
||||
return;
|
||||
}
|
||||
// Unconditionally ack the update. This is important because the Push
|
||||
|
@ -921,7 +920,8 @@ this.PushServiceWebSocket = {
|
|||
} else {
|
||||
let params = getCryptoParams(update.headers);
|
||||
if (!params) {
|
||||
debug("handleDataUpdate: Discarding invalid encrypted message");
|
||||
console.warn("handleDataUpdate: Discarding invalid encrypted message",
|
||||
update);
|
||||
return;
|
||||
}
|
||||
let message = base64UrlDecode(update.data);
|
||||
|
@ -933,7 +933,7 @@ this.PushServiceWebSocket = {
|
|||
);
|
||||
}
|
||||
promise.catch(err => {
|
||||
debug("handleDataUpdate: Error delivering message: " + err);
|
||||
console.error("handleDataUpdate: Error delivering message", err);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -941,28 +941,29 @@ this.PushServiceWebSocket = {
|
|||
* Protocol handler invoked by server message.
|
||||
*/
|
||||
_handleNotificationReply: function(reply) {
|
||||
debug("handleNotificationReply()");
|
||||
console.debug("handleNotificationReply()");
|
||||
if (this._dataEnabled) {
|
||||
this._handleDataUpdate(reply);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof reply.updates !== 'object') {
|
||||
debug("No 'updates' field in response. Type = " + typeof reply.updates);
|
||||
console.warn("handleNotificationReply: Missing updates", reply.updates);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("Reply updates: " + reply.updates.length);
|
||||
console.debug("handleNotificationReply: Got updates", reply.updates);
|
||||
for (let i = 0; i < reply.updates.length; i++) {
|
||||
let update = reply.updates[i];
|
||||
debug("Update: " + update.channelID + ": " + update.version);
|
||||
console.debug("handleNotificationReply: Handling update", update);
|
||||
if (typeof update.channelID !== "string") {
|
||||
debug("Invalid update literal at index " + i);
|
||||
console.debug("handleNotificationReply: Invalid update at index",
|
||||
i, update);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (update.version === undefined) {
|
||||
debug("update.version does not exist");
|
||||
console.debug("handleNotificationReply: Missing version", update);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -983,7 +984,7 @@ this.PushServiceWebSocket = {
|
|||
|
||||
// FIXME(nsm): batch acks for efficiency reasons.
|
||||
_sendAck: function(channelID, version) {
|
||||
debug("sendAck()");
|
||||
console.debug("sendAck()");
|
||||
var data = {messageType: 'ack',
|
||||
updates: [{channelID: channelID,
|
||||
version: version}]
|
||||
|
@ -999,7 +1000,7 @@ this.PushServiceWebSocket = {
|
|||
},
|
||||
|
||||
request: function(action, record) {
|
||||
debug("request() " + action);
|
||||
console.debug("request() ", action);
|
||||
|
||||
if (Object.keys(this._registerRequests).length === 0) {
|
||||
// start the timer since we now have at least one request
|
||||
|
@ -1045,7 +1046,7 @@ this.PushServiceWebSocket = {
|
|||
_notifyRequestQueue: null,
|
||||
_queue: null,
|
||||
_enqueue: function(op) {
|
||||
debug("enqueue");
|
||||
console.debug("enqueue()");
|
||||
if (!this._queue) {
|
||||
this._queue = this._queueStart;
|
||||
}
|
||||
|
@ -1101,29 +1102,30 @@ this.PushServiceWebSocket = {
|
|||
},
|
||||
|
||||
_receivedUpdate: function(aChannelID, aLatestVersion) {
|
||||
debug("Updating: " + aChannelID + " -> " + aLatestVersion);
|
||||
console.debug("receivedUpdate: Updating", aChannelID, "->", aLatestVersion);
|
||||
|
||||
this._mainPushService.receivedPushMessage(aChannelID, null, null, record => {
|
||||
if (record.version === null ||
|
||||
record.version < aLatestVersion) {
|
||||
debug("Version changed for " + aChannelID + ": " + aLatestVersion);
|
||||
console.debug("receivedUpdate: Version changed for", aChannelID,
|
||||
aLatestVersion);
|
||||
record.version = aLatestVersion;
|
||||
return record;
|
||||
}
|
||||
debug("No significant version change for " + aChannelID + ": " +
|
||||
aLatestVersion);
|
||||
console.debug("receivedUpdate: No significant version change for",
|
||||
aChannelID, aLatestVersion);
|
||||
return null;
|
||||
});
|
||||
},
|
||||
|
||||
// begin Push protocol handshake
|
||||
_wsOnStart: function(context) {
|
||||
debug("wsOnStart()");
|
||||
console.debug("wsOnStart()");
|
||||
this._releaseWakeLock();
|
||||
|
||||
if (this._currentState != STATE_WAITING_FOR_WS_START) {
|
||||
debug("NOT in STATE_WAITING_FOR_WS_START. Current state " +
|
||||
this._currentState + ". Skipping");
|
||||
console.error("wsOnStart: NOT in STATE_WAITING_FOR_WS_START. Current",
|
||||
"state", this._currentState, "Skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1167,12 +1169,12 @@ this.PushServiceWebSocket = {
|
|||
* NS_BASE_STREAM_CLOSED, even on a successful close.
|
||||
*/
|
||||
_wsOnStop: function(context, statusCode) {
|
||||
debug("wsOnStop()");
|
||||
console.debug("wsOnStop()");
|
||||
this._releaseWakeLock();
|
||||
|
||||
if (statusCode != Cr.NS_OK &&
|
||||
!(statusCode == Cr.NS_BASE_STREAM_CLOSED && this._willBeWokenUpByUDP)) {
|
||||
debug("Socket error " + statusCode);
|
||||
console.debug("wsOnStop: Socket error", statusCode);
|
||||
this._reconnect();
|
||||
return;
|
||||
}
|
||||
|
@ -1181,7 +1183,7 @@ this.PushServiceWebSocket = {
|
|||
},
|
||||
|
||||
_wsOnMessageAvailable: function(context, message) {
|
||||
debug("wsOnMessageAvailable() " + message);
|
||||
console.debug("wsOnMessageAvailable()", message);
|
||||
|
||||
this._waitingForPong = false;
|
||||
|
||||
|
@ -1189,7 +1191,7 @@ this.PushServiceWebSocket = {
|
|||
try {
|
||||
reply = JSON.parse(message);
|
||||
} catch(e) {
|
||||
debug("Parsing JSON failed. text : " + message);
|
||||
console.warn("wsOnMessageAvailable: Invalid JSON", message, e);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1203,7 +1205,7 @@ this.PushServiceWebSocket = {
|
|||
(reply.messageType === undefined) ||
|
||||
(reply.messageType === "ping") ||
|
||||
(typeof reply.messageType != "string")) {
|
||||
debug('Pong received');
|
||||
console.debug("wsOnMessageAvailable: Pong received");
|
||||
this._calculateAdaptivePing(false);
|
||||
doNotHandle = true;
|
||||
}
|
||||
|
@ -1227,15 +1229,16 @@ this.PushServiceWebSocket = {
|
|||
reply.messageType.slice(1).toLowerCase();
|
||||
|
||||
if (handlers.indexOf(handlerName) == -1) {
|
||||
debug("No whitelisted handler " + handlerName + ". messageType: " +
|
||||
reply.messageType);
|
||||
console.warn("wsOnMessageAvailable: No whitelisted handler", handlerName,
|
||||
"for message", reply.messageType);
|
||||
return;
|
||||
}
|
||||
|
||||
let handler = "_handle" + handlerName + "Reply";
|
||||
|
||||
if (typeof this[handler] !== "function") {
|
||||
debug("Handler whitelisted but not implemented! " + handler);
|
||||
console.warn("wsOnMessageAvailable: Handler", handler,
|
||||
"whitelisted but not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1252,11 +1255,11 @@ this.PushServiceWebSocket = {
|
|||
* and stop reconnecting in _wsOnStop().
|
||||
*/
|
||||
_wsOnServerClose: function(context, aStatusCode, aReason) {
|
||||
debug("wsOnServerClose() " + aStatusCode + " " + aReason);
|
||||
console.debug("wsOnServerClose()", aStatusCode, aReason);
|
||||
|
||||
// Switch over to UDP.
|
||||
if (aStatusCode == kUDP_WAKEUP_WS_STATUS_CODE) {
|
||||
debug("Server closed with promise to wake up");
|
||||
console.debug("wsOnServerClose: Server closed with promise to wake up");
|
||||
this._willBeWokenUpByUDP = true;
|
||||
// TODO: there should be no pending requests
|
||||
}
|
||||
|
@ -1269,7 +1272,7 @@ this.PushServiceWebSocket = {
|
|||
for (let channelID in this._registerRequests) {
|
||||
let request = this._registerRequests[channelID];
|
||||
delete this._registerRequests[channelID];
|
||||
request.reject({status: 0, error: "AbortError"});
|
||||
request.reject(new Error("Register request aborted"));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1282,15 +1285,15 @@ this.PushServiceWebSocket = {
|
|||
* This method should be called only if the device is on a mobile network!
|
||||
*/
|
||||
_listenForUDPWakeup: function() {
|
||||
debug("listenForUDPWakeup()");
|
||||
console.debug("listenForUDPWakeup()");
|
||||
|
||||
if (this._udpServer) {
|
||||
debug("UDP Server already running");
|
||||
console.warn("listenForUDPWakeup: UDP Server already running");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prefs.get("udp.wakeupEnabled")) {
|
||||
debug("UDP support disabled");
|
||||
console.debug("listenForUDPWakeup: UDP support disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1301,7 +1304,7 @@ this.PushServiceWebSocket = {
|
|||
this._udpServer = socket.QueryInterface(Ci.nsIUDPSocket);
|
||||
this._udpServer.init(-1, false, Services.scriptSecurityManager.getSystemPrincipal());
|
||||
this._udpServer.asyncListen(this);
|
||||
debug("listenForUDPWakeup listening on " + this._udpServer.port);
|
||||
console.debug("listenForUDPWakeup: Listening on", this._udpServer.port);
|
||||
|
||||
return this._udpServer.port;
|
||||
},
|
||||
|
@ -1311,7 +1314,8 @@ this.PushServiceWebSocket = {
|
|||
* reconnect the WebSocket and get the actual data.
|
||||
*/
|
||||
onPacketReceived: function(aServ, aMessage) {
|
||||
debug("Recv UDP datagram on port: " + this._udpServer.port);
|
||||
console.debug("onPacketReceived: Recv UDP datagram on port",
|
||||
this._udpServer.port);
|
||||
this._beginWSSetup();
|
||||
},
|
||||
|
||||
|
@ -1322,7 +1326,8 @@ this.PushServiceWebSocket = {
|
|||
* notifications.
|
||||
*/
|
||||
onStopListening: function(aServ, aStatus) {
|
||||
debug("UDP Server socket was shutdown. Status: " + aStatus);
|
||||
console.debug("onStopListening: UDP Server socket was shutdown. Status",
|
||||
aStatus);
|
||||
this._udpServer = undefined;
|
||||
this._beginWSSetup();
|
||||
},
|
||||
|
@ -1333,11 +1338,12 @@ var PushNetworkInfo = {
|
|||
* Returns information about MCC-MNC and the IP of the current connection.
|
||||
*/
|
||||
getNetworkInformation: function() {
|
||||
debug("getNetworkInformation()");
|
||||
console.debug("PushNetworkInfo: getNetworkInformation()");
|
||||
|
||||
try {
|
||||
if (!prefs.get("udp.wakeupEnabled")) {
|
||||
debug("UDP support disabled, we do not send any carrier info");
|
||||
console.debug("getNetworkInformation: UDP support disabled, we do not",
|
||||
"send any carrier info");
|
||||
throw new Error("UDP disabled");
|
||||
}
|
||||
|
||||
|
@ -1356,7 +1362,7 @@ var PushNetworkInfo = {
|
|||
let icc = iccService.getIccByServiceId(clientId);
|
||||
let iccInfo = icc && icc.iccInfo;
|
||||
if (iccInfo) {
|
||||
debug("Running on mobile data");
|
||||
console.debug("getNetworkInformation: Running on mobile data");
|
||||
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
|
@ -1370,10 +1376,11 @@ var PushNetworkInfo = {
|
|||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debug("Error recovering mobile network information: " + e);
|
||||
console.error("getNetworkInformation: Error recovering mobile network",
|
||||
"information", e);
|
||||
}
|
||||
|
||||
debug("Running on wifi");
|
||||
console.debug("getNetworkInformation: Running on wifi");
|
||||
return {
|
||||
mcc: 0,
|
||||
mnc: 0,
|
||||
|
@ -1387,7 +1394,7 @@ var PushNetworkInfo = {
|
|||
* with an IP, and optionally a netid).
|
||||
*/
|
||||
getNetworkState: function(callback) {
|
||||
debug("getNetworkState()");
|
||||
console.debug("PushNetworkInfo: getNetworkState()");
|
||||
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error("No callback method. Aborting push agent !");
|
||||
|
@ -1397,7 +1404,7 @@ var PushNetworkInfo = {
|
|||
|
||||
if (networkInfo.ip) {
|
||||
this._getMobileNetworkId(networkInfo, function(netid) {
|
||||
debug("Recovered netID = " + netid);
|
||||
console.debug("getNetworkState: Recovered netID", netid);
|
||||
callback({
|
||||
mcc: networkInfo.mcc,
|
||||
mnc: networkInfo.mnc,
|
||||
|
@ -1419,22 +1426,21 @@ var PushNetworkInfo = {
|
|||
* Callback function to invoke with the netid or null if not found
|
||||
*/
|
||||
_getMobileNetworkId: function(networkInfo, callback) {
|
||||
console.debug("PushNetworkInfo: getMobileNetworkId()");
|
||||
if (typeof callback !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
function queryDNSForDomain(domain) {
|
||||
debug("[_getMobileNetworkId:queryDNSForDomain] Querying DNS for " +
|
||||
domain);
|
||||
console.debug("queryDNSForDomain: Querying DNS for", domain);
|
||||
let netIDDNSListener = {
|
||||
onLookupComplete: function(aRequest, aRecord, aStatus) {
|
||||
if (aRecord) {
|
||||
let netid = aRecord.getNextAddrAsString();
|
||||
debug("[_getMobileNetworkId:queryDNSForDomain] NetID found: " +
|
||||
netid);
|
||||
console.debug("queryDNSForDomain: NetID found", netid);
|
||||
callback(netid);
|
||||
} else {
|
||||
debug("[_getMobileNetworkId:queryDNSForDomain] NetID not found");
|
||||
console.debug("queryDNSForDomain: NetID not found");
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
|
@ -1444,7 +1450,7 @@ var PushNetworkInfo = {
|
|||
return [];
|
||||
}
|
||||
|
||||
debug("[_getMobileNetworkId:queryDNSForDomain] Getting mobile network ID");
|
||||
console.debug("getMobileNetworkId: Getting mobile network ID");
|
||||
|
||||
let netidAddress = "wakeup.mnc" + ("00" + networkInfo.mnc).slice(-3) +
|
||||
".mcc" + ("00" + networkInfo.mcc).slice(-3) + ".3gppnetwork.org";
|
||||
|
|
|
@ -120,7 +120,6 @@ http://creativecommons.org/licenses/publicdomain/
|
|||
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.push.enabled", true],
|
||||
["dom.push.debug", true],
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true]
|
||||
|
|
|
@ -194,7 +194,7 @@ function disableServiceWorkerEvents(...scopes) {
|
|||
*/
|
||||
function setPrefs(prefs = {}) {
|
||||
let defaultPrefs = Object.assign({
|
||||
debug: true,
|
||||
loglevel: 'all',
|
||||
serverURL: 'wss://push.example.org',
|
||||
'connection.enabled': true,
|
||||
userAgentID: '',
|
||||
|
|
|
@ -47,7 +47,7 @@ add_task(function* test_reconnect_retry() {
|
|||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
channelID: request.channelID,
|
||||
pushEndpoint: 'https://example.org/push/' + registers,
|
||||
pushEndpoint: 'https://example.org/push/' + request.channelID,
|
||||
status: 200,
|
||||
}));
|
||||
}
|
||||
|
@ -59,13 +59,14 @@ add_task(function* test_reconnect_retry() {
|
|||
'https://example.com/page/1',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
|
||||
);
|
||||
equal(registration.channelID, channelID, 'Wrong channel ID for retried request');
|
||||
let retryEndpoint = 'https://example.org/push/' + channelID;
|
||||
equal(registration.pushEndpoint, retryEndpoint, 'Wrong endpoint for retried request');
|
||||
|
||||
registration = yield PushNotificationService.register(
|
||||
'https://example.com/page/2',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
|
||||
);
|
||||
notEqual(registration.channelID, channelID, 'Wrong channel ID for new request');
|
||||
notEqual(registration.pushEndpoint, retryEndpoint, 'Wrong endpoint for new request')
|
||||
|
||||
equal(registers, 3, 'Wrong registration count');
|
||||
});
|
||||
|
|
|
@ -91,15 +91,11 @@ add_task(function* test1() {
|
|||
var subscriptionUri = serverURL + '/subscription';
|
||||
var pushEndpoint = serverURL + '/pushEndpoint';
|
||||
var pushReceiptEndpoint = serverURL + '/receiptPushEndpoint';
|
||||
equal(newRecord.subscriptionUri, subscriptionUri,
|
||||
'Wrong subscription ID in registration record');
|
||||
equal(newRecord.pushEndpoint, pushEndpoint,
|
||||
'Wrong push endpoint in registration record');
|
||||
|
||||
equal(newRecord.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in registration record');
|
||||
equal(newRecord.scope, 'https://example.com/retry5xxCode',
|
||||
'Wrong scope in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(subscriptionUri);
|
||||
equal(record.subscriptionUri, subscriptionUri,
|
||||
|
|
|
@ -54,12 +54,8 @@ add_task(function* test_register_case() {
|
|||
);
|
||||
equal(newRecord.pushEndpoint, 'https://example.com/update/case',
|
||||
'Wrong push endpoint in registration record');
|
||||
equal(newRecord.scope, 'https://example.net/case',
|
||||
'Wrong scope in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(newRecord.channelID);
|
||||
equal(record.pushEndpoint, 'https://example.com/update/case',
|
||||
'Wrong push endpoint in database record');
|
||||
let record = yield db.getByPushEndpoint('https://example.com/update/case');
|
||||
equal(record.scope, 'https://example.net/case',
|
||||
'Wrong scope in database record');
|
||||
});
|
||||
|
|
|
@ -49,10 +49,7 @@ add_task(function* test_pushSubscriptionNoConnection() {
|
|||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Error");
|
||||
},
|
||||
'Wrong error for not being able to establish connecion.'
|
||||
'Expected error for not being able to establish connecion.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
|
@ -90,10 +87,7 @@ add_task(function* test_pushSubscriptionMissingLocation() {
|
|||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Return code 201, but the answer is bogus");
|
||||
},
|
||||
'Wrong error for the missing location header.'
|
||||
'Expected error for the missing location header.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
|
@ -117,10 +111,7 @@ add_task(function* test_pushSubscriptionMissingLink() {
|
|||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Return code 201, but the answer is bogus");
|
||||
},
|
||||
'Wrong error for the missing link header.'
|
||||
'Expected error for the missing link header.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
|
@ -144,10 +135,7 @@ add_task(function* test_pushSubscriptionMissingLink1() {
|
|||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Return code 201, but the answer is bogus");
|
||||
},
|
||||
'Wrong error for the missing push endpoint.'
|
||||
'Expected error for the missing push endpoint.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
|
@ -171,10 +159,7 @@ add_task(function* test_pushSubscriptionLocationBogus() {
|
|||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Return code 201, but URI is bogus.");
|
||||
},
|
||||
'Wrong error for the bogus location'
|
||||
'Expected error for the bogus location'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
|
@ -198,10 +183,7 @@ add_task(function* test_pushSubscriptionNot2xxCode() {
|
|||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-response',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes("Error");
|
||||
},
|
||||
'Wrong error for not 201 responce code.'
|
||||
'Expected error for not 201 responce code.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
|
|
|
@ -80,8 +80,6 @@ add_task(function* test_register_flush() {
|
|||
'https://example.com/page/2', '');
|
||||
equal(newRecord.pushEndpoint, 'https://example.org/update/2',
|
||||
'Wrong push endpoint in record');
|
||||
equal(newRecord.scope, 'https://example.com/page/2',
|
||||
'Wrong scope in record');
|
||||
|
||||
let {data: scope} = yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for notification');
|
||||
|
@ -97,8 +95,6 @@ add_task(function* test_register_flush() {
|
|||
strictEqual(prevRecord.version, 3,
|
||||
'Should record version updates sent before register responses');
|
||||
|
||||
let registeredRecord = yield db.getByKeyID(newRecord.channelID);
|
||||
equal(registeredRecord.pushEndpoint, 'https://example.org/update/2',
|
||||
'Wrong new push endpoint');
|
||||
let registeredRecord = yield db.getByPushEndpoint('https://example.org/update/2');
|
||||
ok(!registeredRecord.version, 'Should not record premature updates');
|
||||
});
|
||||
|
|
|
@ -50,10 +50,7 @@ add_task(function* test_register_invalid_channel() {
|
|||
yield rejects(
|
||||
PushNotificationService.register('https://example.com/invalid-channel',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'Invalid channel ID';
|
||||
},
|
||||
'Wrong error for invalid channel ID'
|
||||
'Expected error for invalid channel ID'
|
||||
);
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
|
|
|
@ -52,10 +52,7 @@ add_task(function* test_register_invalid_endpoint() {
|
|||
PushNotificationService.register(
|
||||
'https://example.net/page/invalid-endpoint',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error && error.includes('Invalid pushEndpoint');
|
||||
},
|
||||
'Wrong error for invalid endpoint'
|
||||
'Expected error for invalid endpoint'
|
||||
);
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
|
|
|
@ -51,10 +51,7 @@ add_task(function* test_register_invalid_json() {
|
|||
yield rejects(
|
||||
PushNotificationService.register('https://example.net/page/invalid-json',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'TimeoutError';
|
||||
},
|
||||
'Wrong error for invalid JSON response'
|
||||
'Expected error for invalid JSON response'
|
||||
);
|
||||
|
||||
yield waitForPromise(helloPromise, DEFAULT_TIMEOUT,
|
||||
|
|
|
@ -55,10 +55,7 @@ add_task(function* test_register_no_id() {
|
|||
yield rejects(
|
||||
PushNotificationService.register('https://example.com/incomplete',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'TimeoutError';
|
||||
},
|
||||
'Wrong error for incomplete register response'
|
||||
'Expected error for incomplete register response'
|
||||
);
|
||||
|
||||
yield waitForPromise(helloPromise, DEFAULT_TIMEOUT,
|
||||
|
|
|
@ -55,12 +55,8 @@ add_task(function* test_register_request_queue() {
|
|||
);
|
||||
|
||||
yield waitForPromise(Promise.all([
|
||||
rejects(firstRegister, function(error) {
|
||||
return error == 'TimeoutError';
|
||||
}, 'Should time out the first request'),
|
||||
rejects(secondRegister, function(error) {
|
||||
return error == 'TimeoutError';
|
||||
}, 'Should time out the second request')
|
||||
rejects(firstRegister, 'Should time out the first request'),
|
||||
rejects(secondRegister, 'Should time out the second request')
|
||||
]), DEFAULT_TIMEOUT, 'Queued requests did not time out');
|
||||
|
||||
yield waitForPromise(helloPromise, DEFAULT_TIMEOUT,
|
||||
|
|
|
@ -77,10 +77,7 @@ add_task(function* test_register_rollback() {
|
|||
yield rejects(
|
||||
PushNotificationService.register('https://example.com/storage-error',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'universe has imploded';
|
||||
},
|
||||
'Wrong error for unregister database failure'
|
||||
'Expected error for unregister database failure'
|
||||
);
|
||||
|
||||
// Should send an out-of-band unregister request.
|
||||
|
|
|
@ -60,22 +60,14 @@ add_task(function* test_register_success() {
|
|||
'https://example.org/1',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
|
||||
);
|
||||
equal(newRecord.channelID, channelID,
|
||||
'Wrong channel ID in registration record');
|
||||
equal(newRecord.pushEndpoint, 'https://example.com/update/1',
|
||||
'Wrong push endpoint in registration record');
|
||||
equal(newRecord.scope, 'https://example.org/1',
|
||||
'Wrong scope in registration record');
|
||||
equal(newRecord.quota, Infinity,
|
||||
'Wrong quota in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
equal(record.channelID, channelID,
|
||||
'Wrong channel ID in database record');
|
||||
equal(record.pushEndpoint, 'https://example.com/update/1',
|
||||
'Wrong push endpoint in database record');
|
||||
equal(record.scope, 'https://example.org/1',
|
||||
'Wrong scope in database record');
|
||||
equal(record.quota, Infinity,
|
||||
'Wrong quota in database record');
|
||||
});
|
||||
|
|
|
@ -64,15 +64,11 @@ add_task(function* test_pushSubscriptionSuccess() {
|
|||
var subscriptionUri = serverURL + '/pushSubscriptionSuccesss';
|
||||
var pushEndpoint = serverURL + '/pushEndpointSuccess';
|
||||
var pushReceiptEndpoint = serverURL + '/receiptPushEndpointSuccess';
|
||||
equal(newRecord.subscriptionUri, subscriptionUri,
|
||||
'Wrong subscription ID in registration record');
|
||||
equal(newRecord.pushEndpoint, pushEndpoint,
|
||||
'Wrong push endpoint in registration record');
|
||||
|
||||
equal(newRecord.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in registration record');
|
||||
equal(newRecord.scope, 'https://example.org/1',
|
||||
'Wrong scope in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(subscriptionUri);
|
||||
equal(record.subscriptionUri, subscriptionUri,
|
||||
|
@ -107,15 +103,11 @@ add_task(function* test_pushSubscriptionMissingLink2() {
|
|||
var subscriptionUri = serverURL + '/subscriptionMissingLink2';
|
||||
var pushEndpoint = serverURL + '/pushEndpointMissingLink2';
|
||||
var pushReceiptEndpoint = '';
|
||||
equal(newRecord.subscriptionUri, subscriptionUri,
|
||||
'Wrong subscription ID in registration record');
|
||||
equal(newRecord.pushEndpoint, pushEndpoint,
|
||||
'Wrong push endpoint in registration record');
|
||||
|
||||
equal(newRecord.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in registration record');
|
||||
equal(newRecord.scope, 'https://example.org/no_receiptEndpoint',
|
||||
'Wrong scope in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(subscriptionUri);
|
||||
equal(record.subscriptionUri, subscriptionUri,
|
||||
|
|
|
@ -77,10 +77,7 @@ add_task(function* test_register_timeout() {
|
|||
yield rejects(
|
||||
PushNotificationService.register('https://example.net/page/timeout',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'TimeoutError';
|
||||
},
|
||||
'Wrong error for request timeout'
|
||||
'Expected error for request timeout'
|
||||
);
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
|
|
|
@ -61,10 +61,7 @@ add_task(function* test_register_wrong_id() {
|
|||
yield rejects(
|
||||
PushNotificationService.register('https://example.com/mismatched',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'TimeoutError';
|
||||
},
|
||||
'Wrong error for mismatched register reply'
|
||||
'Expected error for mismatched register reply'
|
||||
);
|
||||
|
||||
yield waitForPromise(helloPromise, DEFAULT_TIMEOUT,
|
||||
|
|
|
@ -52,15 +52,10 @@ add_task(function* test_register_wrong_type() {
|
|||
}
|
||||
});
|
||||
|
||||
let promise =
|
||||
|
||||
yield rejects(
|
||||
PushNotificationService.register('https://example.com/mistyped',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error == 'TimeoutError';
|
||||
},
|
||||
'Wrong error for non-string channel ID'
|
||||
'Expected error for non-string channel ID'
|
||||
);
|
||||
|
||||
yield waitForPromise(helloPromise, DEFAULT_TIMEOUT,
|
||||
|
|
|
@ -21,9 +21,6 @@ add_task(function* test_registration_missing_scope() {
|
|||
});
|
||||
yield rejects(
|
||||
PushNotificationService.registration('', ''),
|
||||
function(error) {
|
||||
return error.error == 'NotFoundError';
|
||||
},
|
||||
'Record missing page and manifest URLs'
|
||||
);
|
||||
});
|
||||
|
|
|
@ -31,9 +31,6 @@ add_task(function* test_unregister_empty_scope() {
|
|||
yield rejects(
|
||||
PushNotificationService.unregister('',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
|
||||
function(error) {
|
||||
return error.error == 'NotFoundError';
|
||||
},
|
||||
'Wrong error for empty endpoint'
|
||||
'Expected error for empty endpoint'
|
||||
);
|
||||
});
|
||||
|
|
|
@ -77,8 +77,7 @@ add_task(function* test_with_data_enabled() {
|
|||
'https://example.com/page/3',
|
||||
ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
|
||||
);
|
||||
ok(newRecord.p256dhPublicKey, 'Should generate public keys for new records');
|
||||
ok(newRecord.p256dhPrivateKey, 'Should generate private keys for new records');
|
||||
ok(newRecord.p256dhKey, 'Should generate public keys for new records');
|
||||
|
||||
let record = yield db.getByKeyID('eb18f12a-cc42-4f14-accb-3bfc1227f1aa');
|
||||
ok(record.p256dhPublicKey, 'Should add public key to partial record');
|
||||
|
|
|
@ -1590,7 +1590,6 @@ RuntimeService::RemoveSharedWorker(WorkerDomainInfo* aDomainInfo,
|
|||
SharedWorkerInfo* data = iter.UserData();
|
||||
if (data->mWorkerPrivate == aWorkerPrivate) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "njn: RemoveSharedWorker\n");
|
||||
nsAutoCString key;
|
||||
GenerateSharedWorkerKey(data->mScriptSpec, data->mName,
|
||||
aWorkerPrivate->IsInPrivateBrowsing(), key);
|
||||
|
|
|
@ -82,7 +82,7 @@ needs-focus == spellcheck-non-latin-japanese.html spellcheck-non-latin-japanese-
|
|||
needs-focus == spellcheck-non-latin-korean.html spellcheck-non-latin-korean-ref.html
|
||||
== unneeded_scroll.html unneeded_scroll-ref.html
|
||||
skip-if(B2G||Mulet) == caret_on_presshell_reinit.html caret_on_presshell_reinit-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) == caret_on_presshell_reinit-2.html caret_on_presshell_reinit-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(browserIsRemote,255,3) asserts-if(browserIsRemote,0-1) == caret_on_presshell_reinit-2.html caret_on_presshell_reinit-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,2824) == 642800.html 642800-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
== selection_visibility_after_reframe.html selection_visibility_after_reframe-ref.html
|
||||
!= selection_visibility_after_reframe-2.html selection_visibility_after_reframe-ref.html
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
#define ANGLE_COMMIT_HASH "ffabe8783f4a"
|
||||
#define ANGLE_COMMIT_HASH "c7fc1b46df29"
|
||||
#define ANGLE_COMMIT_HASH_SIZE 12
|
||||
#define ANGLE_COMMIT_DATE "2015-10-14 14:24:04 -0400"
|
||||
#define ANGLE_COMMIT_DATE "2015-11-09 16:31:17 -0500"
|
||||
|
|
|
@ -304,6 +304,7 @@ LOCAL_INCLUDES += [ '../../include', '../../src', '../../src/third_party/khronos
|
|||
DEFINES['LIBANGLE_IMPLEMENTATION'] = "1"
|
||||
DEFINES['ANGLE_ENABLE_HLSL'] = "1"
|
||||
DEFINES['ANGLE_ENABLE_KEYEDMUTEX'] = "1"
|
||||
DEFINES['ANGLE_DEFAULT_D3D11'] = "0"
|
||||
|
||||
if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
|
||||
OS_LIBS += [ 'd3d9', 'dxguid' ]
|
||||
|
|
Двоичные данные
gfx/angle/src/tests/deqp_support/dEQP-EGL-cases.txt.gz
Двоичные данные
gfx/angle/src/tests/deqp_support/dEQP-EGL-cases.txt.gz
Двоичный файл не отображается.
Двоичные данные
gfx/angle/src/tests/deqp_support/dEQP-GLES2-cases.txt.gz
Двоичные данные
gfx/angle/src/tests/deqp_support/dEQP-GLES2-cases.txt.gz
Двоичный файл не отображается.
Двоичные данные
gfx/angle/src/tests/deqp_support/dEQP-GLES3-cases.txt.gz
Двоичные данные
gfx/angle/src/tests/deqp_support/dEQP-GLES3-cases.txt.gz
Двоичный файл не отображается.
|
@ -290,11 +290,19 @@ using mozilla::gfx::PointTyped;
|
|||
* \li\b apz.test.logging_enabled
|
||||
* Enable logging of APZ test data (see bug 961289).
|
||||
*
|
||||
* \li\b apz.touch_move_tolerance
|
||||
* See the description for apz.touch_start_tolerance below. This is a similar
|
||||
* threshold, except it is used to suppress touchmove events from being delivered
|
||||
* to content for NON-scrollable frames (or more precisely, for APZCs where
|
||||
* ArePointerEventsConsumable returns false).\n
|
||||
* Units: (real-world, i.e. screen) inches
|
||||
*
|
||||
* \li\b apz.touch_start_tolerance
|
||||
* Constant describing the tolerance in distance we use, multiplied by the
|
||||
* device DPI, before we start panning the screen. This is to prevent us from
|
||||
* accidentally processing taps as touch moves, and from very short/accidental
|
||||
* touches moving the screen.\n
|
||||
* touches moving the screen. touchmove events are also not delivered to content
|
||||
* within this distance on scrollable frames.\n
|
||||
* Units: (real-world, i.e. screen) inches
|
||||
*
|
||||
* \li\b apz.use_paint_duration
|
||||
|
|
|
@ -831,7 +831,8 @@ TouchBlockState::TouchActionAllowsPanningXY() const
|
|||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::UpdateSlopState(const MultiTouchInput& aInput)
|
||||
TouchBlockState::UpdateSlopState(const MultiTouchInput& aInput,
|
||||
bool aApzcCanConsumeEvents)
|
||||
{
|
||||
if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
|
||||
// this is by definition the first event in this block. If it's the first
|
||||
|
@ -844,10 +845,12 @@ TouchBlockState::UpdateSlopState(const MultiTouchInput& aInput)
|
|||
return false;
|
||||
}
|
||||
if (mInSlop) {
|
||||
ScreenCoord threshold = aApzcCanConsumeEvents
|
||||
? AsyncPanZoomController::GetTouchStartTolerance()
|
||||
: ScreenCoord(gfxPrefs::APZTouchMoveTolerance() * APZCTreeManager::GetDPI());
|
||||
bool stayInSlop = (aInput.mType == MultiTouchInput::MULTITOUCH_MOVE) &&
|
||||
(aInput.mTouches.Length() == 1) &&
|
||||
((aInput.mTouches[0].mScreenPoint - mSlopOrigin).Length() <
|
||||
AsyncPanZoomController::GetTouchStartTolerance());
|
||||
((aInput.mTouches[0].mScreenPoint - mSlopOrigin).Length() < threshold);
|
||||
if (!stayInSlop) {
|
||||
// we're out of the slop zone, and will stay out for the remainder of
|
||||
// this block
|
||||
|
|
|
@ -434,11 +434,13 @@ public:
|
|||
* Notifies the input block of an incoming touch event so that the block can
|
||||
* update its internal slop state. "Slop" refers to the area around the
|
||||
* initial touchstart where we drop touchmove events so that content doesn't
|
||||
* see them.
|
||||
* see them. The |aApzcCanConsumeEvents| parameter is factored into how large
|
||||
* the slop area is - if this is true the slop area is larger.
|
||||
* @return true iff the provided event is a touchmove in the slop area and
|
||||
* so should not be sent to content.
|
||||
*/
|
||||
bool UpdateSlopState(const MultiTouchInput& aInput);
|
||||
bool UpdateSlopState(const MultiTouchInput& aInput,
|
||||
bool aApzcCanConsumeEvents);
|
||||
|
||||
bool HasEvents() const override;
|
||||
void DropEvents() override;
|
||||
|
|
|
@ -160,12 +160,15 @@ InputQueue::ReceiveTouchInput(const RefPtr<AsyncPanZoomController>& aTarget,
|
|||
INPQ_LOG("dropping event due to block %p being in fast motion\n", block);
|
||||
result = nsEventStatus_eConsumeNoDefault;
|
||||
} else if (target && target->ArePointerEventsConsumable(block, aEvent.AsMultiTouchInput().mTouches.Length())) {
|
||||
if (block->UpdateSlopState(aEvent.AsMultiTouchInput())) {
|
||||
if (block->UpdateSlopState(aEvent.AsMultiTouchInput(), true)) {
|
||||
INPQ_LOG("dropping event due to block %p being in slop\n", block);
|
||||
result = nsEventStatus_eConsumeNoDefault;
|
||||
} else {
|
||||
result = nsEventStatus_eConsumeDoDefault;
|
||||
}
|
||||
} else if (block->UpdateSlopState(aEvent.AsMultiTouchInput(), false)) {
|
||||
INPQ_LOG("dropping event due to block %p being in mini-slop\n", block);
|
||||
result = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
if (!MaybeHandleCurrentBlock(block, aEvent)) {
|
||||
block->AddEvent(aEvent.AsMultiTouchInput());
|
||||
|
|
|
@ -195,3 +195,12 @@ TEST_F(VsyncTester, ChildRefreshDriverGetVsyncNotifications)
|
|||
vsyncDispatcher = nullptr;
|
||||
testVsyncObserver = nullptr;
|
||||
}
|
||||
|
||||
// Test that we can read the vsync rate
|
||||
TEST_F(VsyncTester, VsyncSourceHasVsyncRate)
|
||||
{
|
||||
VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
|
||||
TimeDuration vsyncRate = globalDisplay.GetVsyncRate();
|
||||
ASSERT_NE(vsyncRate, TimeDuration::Forever());
|
||||
ASSERT_GT(vsyncRate.ToMilliseconds(), 0);
|
||||
}
|
||||
|
|
|
@ -181,6 +181,7 @@ private:
|
|||
DECL_GFX_PREF(Live, "apz.printtree", APZPrintTree, bool, false);
|
||||
DECL_GFX_PREF(Live, "apz.smooth_scroll_repaint_interval", APZSmoothScrollRepaintInterval, int32_t, 75);
|
||||
DECL_GFX_PREF(Live, "apz.test.logging_enabled", APZTestLoggingEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "apz.touch_move_tolerance", APZTouchMoveTolerance, float, 0.0);
|
||||
DECL_GFX_PREF(Live, "apz.touch_start_tolerance", APZTouchStartTolerance, float, 1.0f/4.5f);
|
||||
DECL_GFX_PREF(Live, "apz.use_paint_duration", APZUsePaintDuration, bool, true);
|
||||
DECL_GFX_PREF(Live, "apz.velocity_bias", APZVelocityBias, float, 1.0f);
|
||||
|
|
|
@ -1137,12 +1137,16 @@ MacroAssembler::call(Label* label)
|
|||
CodeOffsetLabel
|
||||
MacroAssembler::callWithPatch()
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
addLongJump(nextOffset());
|
||||
ma_liPatchable(ScratchRegister, ImmWord(0));
|
||||
return call(ScratchRegister);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
BufferOffset li(callerOffset - 6 * sizeof(uint32_t));
|
||||
Assembler::UpdateLoad64Value(editSrc(li), calleeOffset);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -149,10 +149,10 @@ typedef HashMap<CrossCompartmentKey, ReadBarrieredValue,
|
|||
// set.
|
||||
//
|
||||
// * PendingMetadata: This object has been allocated and is still pending its
|
||||
// metadata. This should never be the case in an allocation
|
||||
// path, as a constructor function was supposed to have set
|
||||
// the metadata of the previous object *before* allocating
|
||||
// another object.
|
||||
// metadata. This should never be the case when we begin an
|
||||
// allocation, as a constructor function was supposed to have
|
||||
// set the metadata of the previous object *before*
|
||||
// allocating another object.
|
||||
//
|
||||
// The js::AutoSetNewObjectMetadata RAII class provides an ergonomic way for
|
||||
// constructor functions to navigate state transitions, and its instances
|
||||
|
|
|
@ -1453,10 +1453,9 @@ MoveChildrenTo(nsPresContext* aPresContext,
|
|||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument,
|
||||
nsIPresShell *aPresShell,
|
||||
nsStyleSet* aStyleSet)
|
||||
: nsFrameManager(aPresShell, aStyleSet)
|
||||
nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument* aDocument,
|
||||
nsIPresShell* aPresShell)
|
||||
: nsFrameManager(aPresShell)
|
||||
, mDocument(aDocument)
|
||||
, mRootElementFrame(nullptr)
|
||||
, mRootElementStyleFrame(nullptr)
|
||||
|
|
|
@ -55,8 +55,7 @@ public:
|
|||
|
||||
friend class mozilla::RestyleManager;
|
||||
|
||||
nsCSSFrameConstructor(nsIDocument *aDocument, nsIPresShell* aPresShell,
|
||||
nsStyleSet* aStyleSet);
|
||||
nsCSSFrameConstructor(nsIDocument* aDocument, nsIPresShell* aPresShell);
|
||||
~nsCSSFrameConstructor(void) {
|
||||
NS_ASSERTION(mUpdateCount == 0, "Dying in the middle of our own update?");
|
||||
}
|
||||
|
|
|
@ -78,7 +78,6 @@ static const PLDHashTableOps PlaceholderMapOps = {
|
|||
|
||||
nsFrameManagerBase::nsFrameManagerBase()
|
||||
: mPresShell(nullptr)
|
||||
, mStyleSet(nullptr)
|
||||
, mRootFrame(nullptr)
|
||||
, mPlaceholderMap(&PlaceholderMapOps, sizeof(PlaceholderMapEntry))
|
||||
, mUndisplayedMap(nullptr)
|
||||
|
|
|
@ -82,11 +82,9 @@ class nsFrameManager : public nsFrameManagerBase
|
|||
typedef mozilla::layout::FrameChildListID ChildListID;
|
||||
|
||||
public:
|
||||
nsFrameManager(nsIPresShell *aPresShell, nsStyleSet* aStyleSet) {
|
||||
explicit nsFrameManager(nsIPresShell* aPresShell) {
|
||||
mPresShell = aPresShell;
|
||||
mStyleSet = aStyleSet;
|
||||
MOZ_ASSERT(mPresShell, "need a pres shell");
|
||||
MOZ_ASSERT(mStyleSet, "need a style set");
|
||||
}
|
||||
~nsFrameManager();
|
||||
|
||||
|
|
|
@ -53,8 +53,6 @@ protected:
|
|||
|
||||
// weak link, because the pres shell owns us
|
||||
nsIPresShell* MOZ_NON_OWNING_REF mPresShell;
|
||||
// the pres shell owns the style set
|
||||
nsStyleSet* mStyleSet;
|
||||
nsIFrame* mRootFrame;
|
||||
PLDHashTable mPlaceholderMap;
|
||||
UndisplayedMap* mUndisplayedMap;
|
||||
|
|
|
@ -1959,12 +1959,7 @@ nsLayoutUtils::GetNearestScrollableFrame(nsIFrame* aFrame, uint32_t aFlags)
|
|||
if ((aFlags & SCROLLABLE_FIXEDPOS_FINDS_ROOT) &&
|
||||
f->StyleDisplay()->mPosition == NS_STYLE_POSITION_FIXED &&
|
||||
nsLayoutUtils::IsReallyFixedPos(f)) {
|
||||
nsIPresShell* ps = f->PresContext()->PresShell();
|
||||
// We may want to do this in every document (not just root documents) at
|
||||
// some point in the future.
|
||||
if (ps->GetDocument() && ps->GetDocument()->IsRootDisplayDocument()) {
|
||||
return ps->GetRootScrollFrameAsScrollable();
|
||||
}
|
||||
return f->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
|
|
@ -860,7 +860,7 @@ PresShell::Init(nsIDocument* aDocument,
|
|||
mViewManager = aViewManager;
|
||||
|
||||
// Create our frame constructor.
|
||||
mFrameConstructor = new nsCSSFrameConstructor(mDocument, this, aStyleSet);
|
||||
mFrameConstructor = new nsCSSFrameConstructor(mDocument, this);
|
||||
|
||||
mFrameManager = mFrameConstructor;
|
||||
|
||||
|
@ -6988,7 +6988,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
|
|||
aFrame = targetContent->GetPrimaryFrame();
|
||||
if (!aFrame) {
|
||||
PushCurrentEventInfo(aFrame, targetContent);
|
||||
nsresult rv = HandleEventInternal(aEvent, aEventStatus);
|
||||
nsresult rv = HandleEventInternal(aEvent, aEventStatus, true);
|
||||
PopCurrentEventInfo();
|
||||
return rv;
|
||||
}
|
||||
|
@ -7645,7 +7645,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
|
|||
mCurrentEventFrame = frame;
|
||||
}
|
||||
if (GetCurrentEventFrame()) {
|
||||
rv = HandleEventInternal(aEvent, aEventStatus);
|
||||
rv = HandleEventInternal(aEvent, aEventStatus, true);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -7658,7 +7658,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
|
|||
|
||||
if (!NS_EVENT_NEEDS_FRAME(aEvent)) {
|
||||
mCurrentEventFrame = nullptr;
|
||||
return HandleEventInternal(aEvent, aEventStatus);
|
||||
return HandleEventInternal(aEvent, aEventStatus, true);
|
||||
}
|
||||
else if (aEvent->HasKeyEventMessage()) {
|
||||
// Keypress events in new blank tabs should not be completely thrown away.
|
||||
|
@ -7761,7 +7761,7 @@ PresShell::HandlePositionedEvent(nsIFrame* aTargetFrame,
|
|||
}
|
||||
|
||||
if (GetCurrentEventFrame()) {
|
||||
rv = HandleEventInternal(aEvent, aEventStatus);
|
||||
rv = HandleEventInternal(aEvent, aEventStatus, true);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -7787,13 +7787,15 @@ PresShell::HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame,
|
|||
NS_ENSURE_STATE(!aContent || aContent->GetCrossShadowCurrentDoc() == mDocument);
|
||||
|
||||
PushCurrentEventInfo(aFrame, aContent);
|
||||
nsresult rv = HandleEventInternal(aEvent, aStatus);
|
||||
nsresult rv = HandleEventInternal(aEvent, aStatus, false);
|
||||
PopCurrentEventInfo();
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
|
||||
PresShell::HandleEventInternal(WidgetEvent* aEvent,
|
||||
nsEventStatus* aStatus,
|
||||
bool aIsHandlingNativeEvent)
|
||||
{
|
||||
RefPtr<EventStateManager> manager = mPresContext->EventStateManager();
|
||||
nsresult rv = NS_OK;
|
||||
|
@ -7939,6 +7941,14 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
|
|||
}
|
||||
}
|
||||
|
||||
if (!mIsDestroying && aIsHandlingNativeEvent) {
|
||||
// Ensure that notifications to IME should be sent before getting next
|
||||
// native event from the event queue.
|
||||
// XXX Should we check the event message or event class instead of
|
||||
// using aIsHandlingNativeEvent?
|
||||
manager->TryToFlushPendingNotificationsToIME();
|
||||
}
|
||||
|
||||
switch (aEvent->mMessage) {
|
||||
case eKeyPress:
|
||||
case eKeyDown:
|
||||
|
|
|
@ -580,7 +580,7 @@ protected:
|
|||
mCurrentEventContent = aTarget;
|
||||
nsresult rv = NS_OK;
|
||||
if (GetCurrentEventFrame()) {
|
||||
rv = HandleEventInternal(aEvent, aStatus);
|
||||
rv = HandleEventInternal(aEvent, aStatus, true);
|
||||
}
|
||||
PopCurrentEventInfo();
|
||||
return rv;
|
||||
|
@ -669,8 +669,14 @@ protected:
|
|||
nsEventStatus* aEventStatus);
|
||||
void PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent);
|
||||
void PopCurrentEventInfo();
|
||||
/**
|
||||
* @param aIsHandlingNativeEvent true when the caller (perhaps) handles
|
||||
* an event which is caused by native
|
||||
* event. Otherwise, false.
|
||||
*/
|
||||
nsresult HandleEventInternal(mozilla::WidgetEvent* aEvent,
|
||||
nsEventStatus* aStatus);
|
||||
nsEventStatus* aStatus,
|
||||
bool aIsHandlingNativeEvent);
|
||||
nsresult HandlePositionedEvent(nsIFrame* aTargetFrame,
|
||||
mozilla::WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus);
|
||||
|
|
|
@ -471,13 +471,16 @@ nsMediaQuery::AppendToString(nsAString& aString) const
|
|||
aString.Append('(');
|
||||
|
||||
const nsMediaExpression &expr = mExpressions[i];
|
||||
const nsMediaFeature *feature = expr.mFeature;
|
||||
if (feature->mReqFlags & nsMediaFeature::eHasWebkitPrefix) {
|
||||
aString.AppendLiteral("-webkit-");
|
||||
}
|
||||
if (expr.mRange == nsMediaExpression::eMin) {
|
||||
aString.AppendLiteral("min-");
|
||||
} else if (expr.mRange == nsMediaExpression::eMax) {
|
||||
aString.AppendLiteral("max-");
|
||||
}
|
||||
|
||||
const nsMediaFeature *feature = expr.mFeature;
|
||||
aString.Append(nsDependentAtomString(*feature->mName));
|
||||
|
||||
if (expr.mValue.GetUnit() != eCSSUnit_Null) {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -132,4 +132,3 @@ load border-image-visited-link.html
|
|||
load font-face-truncated-src.html
|
||||
load large_border_image_width.html
|
||||
load long-url-list-stack-overflow.html
|
||||
load 1221902.html
|
||||
|
|
|
@ -3410,22 +3410,35 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
|
|||
|
||||
// case insensitive from CSS - must be lower cased
|
||||
nsContentUtils::ASCIIToLower(mToken.mIdent);
|
||||
const char16_t *featureString;
|
||||
if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) {
|
||||
nsDependentString featureString(mToken.mIdent, 0);
|
||||
uint8_t satisfiedReqFlags = 0;
|
||||
|
||||
// Strip off "-webkit-" prefix from featureString:
|
||||
if (sWebkitPrefixedAliasesEnabled &&
|
||||
StringBeginsWith(featureString, NS_LITERAL_STRING("-webkit-"))) {
|
||||
satisfiedReqFlags |= nsMediaFeature::eHasWebkitPrefix;
|
||||
featureString.Rebind(featureString, 8);
|
||||
}
|
||||
|
||||
// Strip off "min-"/"max-" prefix from featureString:
|
||||
if (StringBeginsWith(featureString, NS_LITERAL_STRING("min-"))) {
|
||||
expr->mRange = nsMediaExpression::eMin;
|
||||
featureString = mToken.mIdent.get() + 4;
|
||||
} else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) {
|
||||
featureString.Rebind(featureString, 4);
|
||||
} else if (StringBeginsWith(featureString, NS_LITERAL_STRING("max-"))) {
|
||||
expr->mRange = nsMediaExpression::eMax;
|
||||
featureString = mToken.mIdent.get() + 4;
|
||||
featureString.Rebind(featureString, 4);
|
||||
} else {
|
||||
expr->mRange = nsMediaExpression::eEqual;
|
||||
featureString = mToken.mIdent.get();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString);
|
||||
const nsMediaFeature *feature = nsMediaFeatures::features;
|
||||
for (; feature->mName; ++feature) {
|
||||
if (*(feature->mName) == mediaFeatureAtom) {
|
||||
// See if name matches & all requirement flags are satisfied:
|
||||
// (We check requirements by turning off all of the flags that have been
|
||||
// satisfied, and then see if the result is 0.)
|
||||
if (*(feature->mName) == mediaFeatureAtom &&
|
||||
!(feature->mReqFlags & ~satisfiedReqFlags)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -420,6 +420,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::width,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eLength,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetWidth
|
||||
},
|
||||
|
@ -427,6 +428,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::height,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eLength,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetHeight
|
||||
},
|
||||
|
@ -434,6 +436,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::deviceWidth,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eLength,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetDeviceWidth
|
||||
},
|
||||
|
@ -441,6 +444,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::deviceHeight,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eLength,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetDeviceHeight
|
||||
},
|
||||
|
@ -448,6 +452,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::orientation,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eEnumerated,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ kOrientationKeywords },
|
||||
GetOrientation
|
||||
},
|
||||
|
@ -455,6 +460,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::aspectRatio,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eIntRatio,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetAspectRatio
|
||||
},
|
||||
|
@ -462,6 +468,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::deviceAspectRatio,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eIntRatio,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetDeviceAspectRatio
|
||||
},
|
||||
|
@ -469,6 +476,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::color,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetColor
|
||||
},
|
||||
|
@ -476,6 +484,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::colorIndex,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetColorIndex
|
||||
},
|
||||
|
@ -483,6 +492,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::monochrome,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetMonochrome
|
||||
},
|
||||
|
@ -490,6 +500,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::resolution,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eResolution,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetResolution
|
||||
},
|
||||
|
@ -497,6 +508,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::scan,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eEnumerated,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ kScanKeywords },
|
||||
GetScan
|
||||
},
|
||||
|
@ -504,15 +516,28 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::grid,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetGrid
|
||||
},
|
||||
|
||||
// Webkit extensions that we support for de-facto web compatibility
|
||||
// -webkit-{min|max}-device-pixel-ratio:
|
||||
{
|
||||
&nsGkAtoms::devicePixelRatio,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eFloat,
|
||||
nsMediaFeature::eHasWebkitPrefix,
|
||||
{ nullptr },
|
||||
GetDevicePixelRatio
|
||||
},
|
||||
|
||||
// Mozilla extensions
|
||||
{
|
||||
&nsGkAtoms::_moz_device_pixel_ratio,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eFloat,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetDevicePixelRatio
|
||||
},
|
||||
|
@ -520,6 +545,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_device_orientation,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eEnumerated,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ kOrientationKeywords },
|
||||
GetDeviceOrientation
|
||||
},
|
||||
|
@ -527,6 +553,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_is_resource_document,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetIsResourceDocument
|
||||
},
|
||||
|
@ -534,6 +561,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_color_picker_available,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::color_picker_available },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -541,6 +569,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_scrollbar_start_backward,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::scrollbar_start_backward },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -548,6 +577,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_scrollbar_start_forward,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::scrollbar_start_forward },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -555,6 +585,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_scrollbar_end_backward,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::scrollbar_end_backward },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -562,6 +593,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_scrollbar_end_forward,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::scrollbar_end_forward },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -569,6 +601,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_scrollbar_thumb_proportional,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::scrollbar_thumb_proportional },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -576,6 +609,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_images_in_menus,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::images_in_menus },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -583,6 +617,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_images_in_buttons,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::images_in_buttons },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -590,6 +625,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_overlay_scrollbars,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::overlay_scrollbars },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -597,6 +633,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_windows_default_theme,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::windows_default_theme },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -604,6 +641,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_mac_graphite_theme,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::mac_graphite_theme },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -611,6 +649,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_mac_lion_theme,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::mac_lion_theme },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -618,6 +657,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_mac_yosemite_theme,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::mac_yosemite_theme },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -625,6 +665,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_windows_compositor,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::windows_compositor },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -632,6 +673,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_windows_classic,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::windows_classic },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -639,6 +681,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_windows_glass,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::windows_glass },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -646,6 +689,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_touch_enabled,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::touch_enabled },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -653,6 +697,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_menubar_drag,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::menubar_drag },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -660,6 +705,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_windows_theme,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eIdent,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetWindowsTheme
|
||||
},
|
||||
|
@ -667,6 +713,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_os_version,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eIdent,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetOperatinSystemVersion
|
||||
},
|
||||
|
@ -675,6 +722,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_swipe_animation_enabled,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::swipe_animation_enabled },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -683,6 +731,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_physical_home_button,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ &nsGkAtoms::physical_home_button },
|
||||
GetSystemMetric
|
||||
},
|
||||
|
@ -694,6 +743,7 @@ nsMediaFeatures::features[] = {
|
|||
&nsGkAtoms::_moz_is_glyph,
|
||||
nsMediaFeature::eMinMaxNotAllowed,
|
||||
nsMediaFeature::eBoolInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
GetIsGlyph
|
||||
},
|
||||
|
@ -702,6 +752,7 @@ nsMediaFeatures::features[] = {
|
|||
nullptr,
|
||||
nsMediaFeature::eMinMaxAllowed,
|
||||
nsMediaFeature::eInteger,
|
||||
nsMediaFeature::eNoRequirements,
|
||||
{ nullptr },
|
||||
nullptr
|
||||
},
|
||||
|
|
|
@ -46,6 +46,15 @@ struct nsMediaFeature {
|
|||
};
|
||||
ValueType mValueType;
|
||||
|
||||
enum RequirementFlags : uint8_t {
|
||||
// Bitfield of requirements that must be satisfied in order for this
|
||||
// media feature to be active.
|
||||
eNoRequirements = 0,
|
||||
eHasWebkitPrefix = 1 // Feature name must start w/ "-webkit-", even
|
||||
// before any "min-"/"max-" qualifier.
|
||||
};
|
||||
uint8_t mReqFlags;
|
||||
|
||||
union {
|
||||
// In static arrays, it's the first member that's initialized. We
|
||||
// need that to be void* so we can initialize both other types.
|
||||
|
|
|
@ -289,3 +289,4 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(bug 870262,
|
|||
skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(bug 870262, :visited support) b2g-debug(bug 870262, :visited support) b2g-desktop(bug 870262, :visited support)
|
||||
[test_visited_reftests.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(bug 870262, :visited support) b2g-debug(bug 870262, :visited support) b2g-desktop(bug 870262, :visited support)
|
||||
[test_webkit_device_pixel_ratio.html]
|
||||
|
|
|
@ -114,6 +114,29 @@ function run() {
|
|||
"expression " + e + " should not be parseable");
|
||||
}
|
||||
|
||||
// Helper to share code between -moz & -webkit device-pixel-ratio versions:
|
||||
function test_device_pixel_ratio(equal_name, min_name, max_name) {
|
||||
var real_dpr = 1.0 * getScreenPixelsPerCSSPixel();
|
||||
var high_dpr = 1.1 * getScreenPixelsPerCSSPixel();
|
||||
var low_dpr = 0.9 * getScreenPixelsPerCSSPixel();
|
||||
should_apply("all and (" + max_name + ": " + real_dpr + ")");
|
||||
should_apply("all and (" + min_name + ": " + real_dpr + ")");
|
||||
should_not_apply("not all and (" + max_name + ": " + real_dpr + ")");
|
||||
should_not_apply("not all and (" + min_name + ": " + real_dpr + ")");
|
||||
should_apply("all and (" + min_name + ": " + low_dpr + ")");
|
||||
should_apply("all and (" + max_name + ": " + high_dpr + ")");
|
||||
should_not_apply("all and (" + max_name + ": " + low_dpr + ")");
|
||||
should_not_apply("all and (" + min_name + ": " + high_dpr + ")");
|
||||
should_apply("not all and (" + max_name + ": " + low_dpr + ")");
|
||||
should_apply("not all and (" + min_name + ": " + high_dpr + ")");
|
||||
should_apply("(" + equal_name + ": " + real_dpr + ")");
|
||||
should_not_apply("(" + equal_name + ": " + high_dpr + ")");
|
||||
should_not_apply("(" + equal_name + ": " + low_dpr + ")");
|
||||
should_apply("(" + equal_name + ")");
|
||||
expression_should_not_be_parseable(min_name);
|
||||
expression_should_not_be_parseable(max_name);
|
||||
}
|
||||
|
||||
function test_serialization(q, test_application, should_apply) {
|
||||
style.setAttribute("media", q);
|
||||
var ser1 = style.sheet.media.mediaText;
|
||||
|
@ -399,25 +422,23 @@ function run() {
|
|||
should_apply("not all and (max-device-aspect-ratio: " + low_dar_2 + ")");
|
||||
expression_should_not_be_parseable("max-device-aspect-ratio");
|
||||
|
||||
var real_dpr = 1.0 * getScreenPixelsPerCSSPixel();
|
||||
var high_dpr = 1.1 * getScreenPixelsPerCSSPixel();
|
||||
var low_dpr = 0.9 * getScreenPixelsPerCSSPixel();
|
||||
should_apply("all and (max--moz-device-pixel-ratio: " + real_dpr + ")");
|
||||
should_apply("all and (min--moz-device-pixel-ratio: " + real_dpr + ")");
|
||||
should_not_apply("not all and (max--moz-device-pixel-ratio: " + real_dpr + ")");
|
||||
should_not_apply("not all and (min--moz-device-pixel-ratio: " + real_dpr + ")");
|
||||
should_apply("all and (min--moz-device-pixel-ratio: " + low_dpr + ")");
|
||||
should_apply("all and (max--moz-device-pixel-ratio: " + high_dpr + ")");
|
||||
should_not_apply("all and (max--moz-device-pixel-ratio: " + low_dpr + ")");
|
||||
should_not_apply("all and (min--moz-device-pixel-ratio: " + high_dpr + ")");
|
||||
should_apply("not all and (max--moz-device-pixel-ratio: " + low_dpr + ")");
|
||||
should_apply("not all and (min--moz-device-pixel-ratio: " + high_dpr + ")");
|
||||
should_apply("(-moz-device-pixel-ratio: " + real_dpr + ")");
|
||||
should_not_apply("(-moz-device-pixel-ratio: " + high_dpr + ")");
|
||||
should_not_apply("(-moz-device-pixel-ratio: " + low_dpr + ")");
|
||||
should_apply("(-moz-device-pixel-ratio)");
|
||||
expression_should_not_be_parseable("min--moz-device-pixel-ratio");
|
||||
expression_should_not_be_parseable("max--moz-device-pixel-ratio");
|
||||
// Tests for -moz- & -webkit versions of "device-pixel-ratio"
|
||||
// (Note that the vendor prefixes go in different places.)
|
||||
test_device_pixel_ratio("-moz-device-pixel-ratio",
|
||||
"min--moz-device-pixel-ratio",
|
||||
"max--moz-device-pixel-ratio");
|
||||
test_device_pixel_ratio("-webkit-device-pixel-ratio",
|
||||
"-webkit-min-device-pixel-ratio",
|
||||
"-webkit-max-device-pixel-ratio");
|
||||
|
||||
// Make sure that we don't accidentally start accepting *unprefixed*
|
||||
// "device-pixel-ratio" expressions:
|
||||
expression_should_be_parseable("-webkit-device-pixel-ratio: 1.0");
|
||||
expression_should_not_be_parseable("device-pixel-ratio: 1.0");
|
||||
expression_should_be_parseable("-webkit-min-device-pixel-ratio: 1.0");
|
||||
expression_should_not_be_parseable("min-device-pixel-ratio: 1.0");
|
||||
expression_should_be_parseable("-webkit-max-device-pixel-ratio: 1.0");
|
||||
expression_should_not_be_parseable("max-device-pixel-ratio: 1.0");
|
||||
|
||||
features = [ "max-aspect-ratio", "device-aspect-ratio" ];
|
||||
for (i in features) {
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1176968
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1176968</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<style>.zoom-test { visibility: hidden; }</style>
|
||||
<style><!-- placeholder for dynamic additions --></style>
|
||||
</head>
|
||||
<body onload="run()">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1176968">Mozilla Bug 1176968</a>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<pre id="test">
|
||||
<div id="zoom1" class="zoom-test"></div>
|
||||
<div id="zoom2" class="zoom-test"></div>
|
||||
<div id="zoom3" class="zoom-test"></div>
|
||||
<script class="testbody" type="application/javascript">
|
||||
|
||||
/** Test for Bug 1176968 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function run() {
|
||||
function zoom(factor) {
|
||||
var previous = SpecialPowers.getFullZoom(window);
|
||||
SpecialPowers.setFullZoom(window, factor);
|
||||
return previous;
|
||||
}
|
||||
|
||||
function isVisible(divName) {
|
||||
return window.getComputedStyle(document.getElementById(divName), null).visibility == "visible";
|
||||
}
|
||||
|
||||
function getScreenPixelsPerCSSPixel() {
|
||||
return SpecialPowers.DOMWindowUtils.screenPixelsPerCSSPixel;
|
||||
}
|
||||
|
||||
var screenPixelsPerCSSPixel = getScreenPixelsPerCSSPixel();
|
||||
var baseRatio = 1.0 * screenPixelsPerCSSPixel;
|
||||
var doubleRatio = 2.0 * screenPixelsPerCSSPixel;
|
||||
var halfRatio = 0.5 * screenPixelsPerCSSPixel;
|
||||
var styleElem = document.getElementsByTagName("style")[1];
|
||||
styleElem.textContent =
|
||||
["@media all and (-webkit-device-pixel-ratio: " + baseRatio + ") {",
|
||||
"#zoom1 { visibility: visible; }",
|
||||
"}",
|
||||
"@media all and (-webkit-device-pixel-ratio: " + doubleRatio + ") {",
|
||||
"#zoom2 { visibility: visible; }",
|
||||
"}",
|
||||
"@media all and (-webkit-device-pixel-ratio: " + halfRatio + ") {",
|
||||
"#zoom3 { visibility: visible; }",
|
||||
"}"
|
||||
].join("\n");
|
||||
|
||||
ok(isVisible("zoom1"), "Base ratio rule should apply at base zoom level");
|
||||
ok(!isVisible("zoom2") && !isVisible("zoom3"), "no other rules should apply");
|
||||
var origZoom = zoom(2);
|
||||
ok(isVisible("zoom2"), "Double ratio rule should apply at double zoom level");
|
||||
ok(!isVisible("zoom1") && !isVisible("zoom3"), "no other rules should apply");
|
||||
zoom(0.5);
|
||||
ok(isVisible("zoom3"), "Half ratio rule should apply at half zoom level");
|
||||
ok(!isVisible("zoom1") && !isVisible("zoom2"), "no other rules should apply");
|
||||
zoom(origZoom);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -9,6 +9,7 @@
|
|||
#include "media/stagefright/MetaData.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mp4_demuxer/MoofParser.h"
|
||||
#include "mp4_demuxer/MP4Metadata.h"
|
||||
|
||||
|
@ -142,7 +143,9 @@ MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
|
|||
{
|
||||
#ifdef MOZ_RUST_MP4PARSE
|
||||
// Try in rust first.
|
||||
try_rust(mSource);
|
||||
bool rust_mp4parse_success = try_rust(mSource);
|
||||
Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_SUCCESS,
|
||||
rust_mp4parse_success);
|
||||
#endif
|
||||
size_t tracks = mPrivate->mMetadataExtractor->countTracks();
|
||||
uint32_t total = 0;
|
||||
|
|
|
@ -588,6 +588,7 @@ pref("apz.printtree", false);
|
|||
|
||||
pref("apz.test.logging_enabled", false);
|
||||
pref("apz.touch_start_tolerance", "0.2222222"); // 0.2222222 came from 1.0/4.5
|
||||
pref("apz.touch_move_tolerance", "0.0");
|
||||
pref("apz.use_paint_duration", true);
|
||||
pref("apz.velocity_bias", "1.0");
|
||||
pref("apz.velocity_relevance_time_ms", 150);
|
||||
|
@ -4479,7 +4480,7 @@ pref("dom.mozAlarms.enabled", false);
|
|||
|
||||
pref("dom.push.enabled", false);
|
||||
|
||||
pref("dom.push.debug", false);
|
||||
pref("dom.push.loglevel", "off");
|
||||
|
||||
pref("dom.push.serverURL", "wss://push.services.mozilla.com/");
|
||||
pref("dom.push.userAgentID", "");
|
||||
|
|
|
@ -130,7 +130,6 @@ XPIDL_SOURCES += [
|
|||
'nsIUploadChannel.idl',
|
||||
'nsIUploadChannel2.idl',
|
||||
'nsIURI.idl',
|
||||
'nsIURIChecker.idl',
|
||||
'nsIURIClassifier.idl',
|
||||
'nsIURIWithPrincipal.idl',
|
||||
'nsIURL.idl',
|
||||
|
@ -244,7 +243,6 @@ UNIFIED_SOURCES += [
|
|||
'nsTransportUtils.cpp',
|
||||
'nsUDPSocket.cpp',
|
||||
'nsUnicharStreamLoader.cpp',
|
||||
'nsURIChecker.cpp',
|
||||
'nsURLHelper.cpp',
|
||||
'nsURLParsers.cpp',
|
||||
'OfflineObserver.cpp',
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "nsIRequest.idl"
|
||||
|
||||
interface nsIURI;
|
||||
interface nsIChannel;
|
||||
interface nsIRequestObserver;
|
||||
|
||||
/**
|
||||
* nsIURIChecker
|
||||
*
|
||||
* The URI checker is a component that can be used to verify the existence
|
||||
* of a resource at the location specified by a given URI. It will use
|
||||
* protocol specific methods to verify the URI (e.g., use of HEAD request
|
||||
* for HTTP URIs).
|
||||
*/
|
||||
[scriptable, uuid(4660c1a1-be2d-4c78-9baf-c22984176c28)]
|
||||
interface nsIURIChecker : nsIRequest
|
||||
{
|
||||
/**
|
||||
* Initializes the URI checker. After this method is called, it is valid
|
||||
* to further configure the URI checker by calling its nsIRequest methods.
|
||||
* This method creates the channel that will be used to verify the URI.
|
||||
* In the case of the HTTP protocol, only a HEAD request will be issued.
|
||||
*
|
||||
* @param aURI
|
||||
* The URI to be checked.
|
||||
*/
|
||||
void init(in nsIURI aURI);
|
||||
|
||||
/**
|
||||
* Returns the base channel that will be used to verify the URI.
|
||||
*/
|
||||
readonly attribute nsIChannel baseChannel;
|
||||
|
||||
/**
|
||||
* Begin asynchronous checking URI for validity. Notification will be
|
||||
* asynchronous through the nsIRequestObserver callback interface. When
|
||||
* OnStartRequest is fired, the baseChannel attribute will have been
|
||||
* updated to reflect the final channel used (corresponding to any redirects
|
||||
* that may have been followed).
|
||||
*
|
||||
* Our interpretations of the nsIRequestObserver status codes:
|
||||
* NS_BINDING_SUCCEEDED: link is valid
|
||||
* NS_BINDING_FAILED: link is invalid (gave an error)
|
||||
* NS_BINDING_ABORTED: timed out, or cancelled
|
||||
*
|
||||
* @param aObserver
|
||||
* The object to notify when the link is verified. We will
|
||||
* call aObserver.OnStartRequest followed immediately by
|
||||
* aObserver.OnStopRequest. It is recommended that the caller use
|
||||
* OnStopRequest to act on the link's status. The underlying request
|
||||
* will not be cancelled until after OnStopRequest has been called.
|
||||
* @param aContext
|
||||
* A closure that will be passed back to the nsIRequestObserver
|
||||
* methods.
|
||||
*/
|
||||
void asyncCheck(in nsIRequestObserver aObserver, in nsISupports aContext);
|
||||
};
|
|
@ -1,351 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "nsURIChecker.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIAuthPrompt.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static bool
|
||||
ServerIsNES3x(nsIHttpChannel *httpChannel)
|
||||
{
|
||||
nsAutoCString server;
|
||||
httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Server"), server);
|
||||
// case sensitive string comparison is OK here. the server string
|
||||
// is a well-known value, so we should not have to worry about it
|
||||
// being case-smashed or otherwise case-mutated.
|
||||
return StringBeginsWith(server,
|
||||
NS_LITERAL_CSTRING("Netscape-Enterprise/3."));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsURIChecker,
|
||||
nsIURIChecker,
|
||||
nsIRequest,
|
||||
nsIRequestObserver,
|
||||
nsIStreamListener,
|
||||
nsIChannelEventSink,
|
||||
nsIInterfaceRequestor)
|
||||
|
||||
nsURIChecker::nsURIChecker()
|
||||
: mStatus(NS_OK)
|
||||
, mIsPending(false)
|
||||
, mAllowHead(true)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
nsURIChecker::SetStatusAndCallBack(nsresult aStatus)
|
||||
{
|
||||
mStatus = aStatus;
|
||||
mIsPending = false;
|
||||
|
||||
if (mObserver) {
|
||||
mObserver->OnStartRequest(this, mObserverContext);
|
||||
mObserver->OnStopRequest(this, mObserverContext, mStatus);
|
||||
mObserver = nullptr;
|
||||
mObserverContext = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsURIChecker::CheckStatus()
|
||||
{
|
||||
NS_ASSERTION(mChannel, "no channel");
|
||||
|
||||
nsresult status;
|
||||
nsresult rv = mChannel->GetStatus(&status);
|
||||
// DNS errors and other obvious problems will return failure status
|
||||
if (NS_FAILED(rv) || NS_FAILED(status))
|
||||
return NS_BINDING_FAILED;
|
||||
|
||||
// If status is zero, it might still be an error if it's http:
|
||||
// http has data even when there's an error like a 404.
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
|
||||
if (!httpChannel)
|
||||
return NS_BINDING_SUCCEEDED;
|
||||
|
||||
uint32_t responseStatus;
|
||||
rv = httpChannel->GetResponseStatus(&responseStatus);
|
||||
if (NS_FAILED(rv))
|
||||
return NS_BINDING_FAILED;
|
||||
|
||||
// If it's between 200-299, it's valid:
|
||||
if (responseStatus / 100 == 2)
|
||||
return NS_BINDING_SUCCEEDED;
|
||||
|
||||
// If we got a 404 (not found), we need some extra checking:
|
||||
// toplevel urls from Netscape Enterprise Server 3.6, like the old AOL-
|
||||
// hosted http://www.mozilla.org, generate a 404 and will have to be
|
||||
// retried without the head.
|
||||
if (responseStatus == 404) {
|
||||
if (mAllowHead && ServerIsNES3x(httpChannel)) {
|
||||
mAllowHead = false;
|
||||
|
||||
// save the current value of mChannel in case we can't issue
|
||||
// the new request for some reason.
|
||||
nsCOMPtr<nsIChannel> lastChannel = mChannel;
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
uint32_t loadFlags;
|
||||
|
||||
rv = lastChannel->GetOriginalURI(getter_AddRefs(uri));
|
||||
nsresult tmp = lastChannel->GetLoadFlags(&loadFlags);
|
||||
if (NS_FAILED(tmp)) {
|
||||
rv = tmp;
|
||||
}
|
||||
|
||||
// XXX we are carrying over the load flags, but what about other
|
||||
// parameters that may have been set on lastChannel??
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = Init(uri);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = mChannel->SetLoadFlags(loadFlags);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = AsyncCheck(mObserver, mObserverContext);
|
||||
// if we succeeded in loading the new channel, then we
|
||||
// want to return without notifying our observer.
|
||||
if (NS_SUCCEEDED(rv))
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
}
|
||||
}
|
||||
// it is important to update this so our observer will be able
|
||||
// to access our baseChannel attribute if they want.
|
||||
mChannel = lastChannel;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, assume the resource does not exist.
|
||||
return NS_BINDING_FAILED;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIURIChecker methods:
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::Init(nsIURI *aURI)
|
||||
{
|
||||
nsresult rv;
|
||||
rv = NS_NewChannel(getter_AddRefs(mChannel),
|
||||
aURI,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mAllowHead) {
|
||||
mAllowHead = false;
|
||||
// See if it's an http channel, which needs special treatment:
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
|
||||
if (httpChannel) {
|
||||
// We can have an HTTP channel that has a non-HTTP URL if
|
||||
// we're doing FTP via an HTTP proxy, for example. See for
|
||||
// example bug 148813
|
||||
bool isReallyHTTP = false;
|
||||
aURI->SchemeIs("http", &isReallyHTTP);
|
||||
if (!isReallyHTTP)
|
||||
aURI->SchemeIs("https", &isReallyHTTP);
|
||||
if (isReallyHTTP) {
|
||||
httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
|
||||
// set back to true so we'll know that this request is issuing
|
||||
// a HEAD request. this is used down in OnStartRequest to
|
||||
// handle cases where we need to repeat the request as a normal
|
||||
// GET to deal with server borkage.
|
||||
mAllowHead = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::AsyncCheck(nsIRequestObserver *aObserver,
|
||||
nsISupports *aObserverContext)
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// Hook us up to listen to redirects and the like (this creates a reference
|
||||
// cycle!)
|
||||
mChannel->SetNotificationCallbacks(this);
|
||||
|
||||
// and start the request:
|
||||
nsresult rv = mChannel->AsyncOpen2(this);
|
||||
if (NS_FAILED(rv))
|
||||
mChannel = nullptr;
|
||||
else {
|
||||
// ok, wait for OnStartRequest to fire.
|
||||
mIsPending = true;
|
||||
mObserver = aObserver;
|
||||
mObserverContext = aObserverContext;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::GetBaseChannel(nsIChannel **aChannel)
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ADDREF(*aChannel = mChannel);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIRequest methods:
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::GetName(nsACString &aName)
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
||||
return mChannel->GetName(aName);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::IsPending(bool *aPendingRet)
|
||||
{
|
||||
*aPendingRet = mIsPending;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::GetStatus(nsresult* aStatusRet)
|
||||
{
|
||||
*aStatusRet = mStatus;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::Cancel(nsresult status)
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
||||
return mChannel->Cancel(status);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::Suspend()
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
||||
return mChannel->Suspend();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::Resume()
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
||||
return mChannel->Resume();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::GetLoadGroup(nsILoadGroup **aLoadGroup)
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
||||
return mChannel->GetLoadGroup(aLoadGroup);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::SetLoadGroup(nsILoadGroup *aLoadGroup)
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
||||
return mChannel->SetLoadGroup(aLoadGroup);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::GetLoadFlags(nsLoadFlags *aLoadFlags)
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
||||
return mChannel->GetLoadFlags(aLoadFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::SetLoadFlags(nsLoadFlags aLoadFlags)
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
||||
return mChannel->SetLoadFlags(aLoadFlags);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIRequestObserver methods:
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::OnStartRequest(nsIRequest *aRequest, nsISupports *aCtxt)
|
||||
{
|
||||
NS_ASSERTION(aRequest == mChannel, "unexpected request");
|
||||
|
||||
nsresult rv = CheckStatus();
|
||||
if (rv != NS_BASE_STREAM_WOULD_BLOCK)
|
||||
SetStatusAndCallBack(rv);
|
||||
|
||||
// cancel the request (we don't care to look at the data).
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
|
||||
nsresult statusCode)
|
||||
{
|
||||
// NOTE: we may have kicked off a subsequent request, so we should not do
|
||||
// any cleanup unless this request matches the one we are currently using.
|
||||
if (mChannel == request) {
|
||||
// break reference cycle between us and the channel (see comment in
|
||||
// AsyncCheckURI)
|
||||
mChannel = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIStreamListener methods:
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::OnDataAvailable(nsIRequest *aRequest, nsISupports *aCtxt,
|
||||
nsIInputStream *aInput, uint64_t aOffset,
|
||||
uint32_t aCount)
|
||||
{
|
||||
NS_NOTREACHED("nsURIChecker::OnDataAvailable");
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIInterfaceRequestor methods:
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
if (mObserver && aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
|
||||
nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mObserver);
|
||||
if (req)
|
||||
return req->GetInterface(aIID, aResult);
|
||||
}
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIChannelEventSink methods:
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsURIChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
||||
nsIChannel *aNewChannel,
|
||||
uint32_t aFlags,
|
||||
nsIAsyncVerifyRedirectCallback *callback)
|
||||
{
|
||||
// We have a new channel
|
||||
mChannel = aNewChannel;
|
||||
callback->OnRedirectVerifyCallback(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsURIChecker_h__
|
||||
#define nsURIChecker_h__
|
||||
|
||||
#include "nsIURIChecker.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsURIChecker : public nsIURIChecker,
|
||||
public nsIStreamListener,
|
||||
public nsIChannelEventSink,
|
||||
public nsIInterfaceRequestor
|
||||
{
|
||||
virtual ~nsURIChecker() {}
|
||||
|
||||
public:
|
||||
nsURIChecker();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIURICHECKER
|
||||
NS_DECL_NSIREQUEST
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsCOMPtr<nsIRequestObserver> mObserver;
|
||||
nsCOMPtr<nsISupports> mObserverContext;
|
||||
nsresult mStatus;
|
||||
bool mIsPending;
|
||||
bool mAllowHead;
|
||||
|
||||
void SetStatusAndCallBack(nsresult aStatus);
|
||||
nsresult CheckStatus();
|
||||
};
|
||||
|
||||
#endif // nsURIChecker_h__
|
|
@ -283,17 +283,6 @@
|
|||
{0x8b, 0x06, 0x8a, 0xaf, 0xe1, 0x86, 0x9b, 0xd8} \
|
||||
}
|
||||
|
||||
// component implementing nsIURIChecker.
|
||||
#define NS_URICHECKER_CONTRACT_ID \
|
||||
"@mozilla.org/network/urichecker;1"
|
||||
#define NS_URICHECKER_CID \
|
||||
{ /* cf3a0e06-1dd1-11b2-a904-ac1d6da77a02 */ \
|
||||
0xcf3a0e06, \
|
||||
0x1dd1, \
|
||||
0x11b2, \
|
||||
{0xa9, 0x04, 0xac, 0x1d, 0x6d, 0xa7, 0x7a, 0x02} \
|
||||
}
|
||||
|
||||
// component implementing nsIIncrementalDownload.
|
||||
#define NS_INCREMENTALDOWNLOAD_CONTRACTID \
|
||||
"@mozilla.org/network/incremental-download;1"
|
||||
|
|
|
@ -368,11 +368,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(RtspHandler)
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "nsURIChecker.h"
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsURIChecker)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "nsURLParsers.h"
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsNoAuthURLParser)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsAuthURLParser)
|
||||
|
@ -727,7 +722,6 @@ NS_DEFINE_NAMED_CID(NS_PARTIALLOCALFILEINPUTSTREAM_CID);
|
|||
NS_DEFINE_NAMED_CID(NS_ATOMICLOCALFILEOUTPUTSTREAM_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_SAFELOCALFILEOUTPUTSTREAM_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_LOCALFILESTREAM_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_URICHECKER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_INCREMENTALDOWNLOAD_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_STDURLPARSER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_NOAUTHURLPARSER_CID);
|
||||
|
@ -877,7 +871,6 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
|
|||
{ &kNS_ATOMICLOCALFILEOUTPUTSTREAM_CID, false, nullptr, nsAtomicFileOutputStreamConstructor },
|
||||
{ &kNS_SAFELOCALFILEOUTPUTSTREAM_CID, false, nullptr, nsSafeFileOutputStreamConstructor },
|
||||
{ &kNS_LOCALFILESTREAM_CID, false, nullptr, nsFileStreamConstructor },
|
||||
{ &kNS_URICHECKER_CID, false, nullptr, nsURICheckerConstructor },
|
||||
{ &kNS_INCREMENTALDOWNLOAD_CID, false, nullptr, net_NewIncrementalDownload },
|
||||
{ &kNS_STDURLPARSER_CID, false, nullptr, nsStdURLParserConstructor },
|
||||
{ &kNS_NOAUTHURLPARSER_CID, false, nullptr, nsNoAuthURLParserConstructor },
|
||||
|
@ -1029,7 +1022,6 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
|
|||
{ NS_ATOMICLOCALFILEOUTPUTSTREAM_CONTRACTID, &kNS_ATOMICLOCALFILEOUTPUTSTREAM_CID },
|
||||
{ NS_SAFELOCALFILEOUTPUTSTREAM_CONTRACTID, &kNS_SAFELOCALFILEOUTPUTSTREAM_CID },
|
||||
{ NS_LOCALFILESTREAM_CONTRACTID, &kNS_LOCALFILESTREAM_CID },
|
||||
{ NS_URICHECKER_CONTRACT_ID, &kNS_URICHECKER_CID },
|
||||
{ NS_INCREMENTALDOWNLOAD_CONTRACTID, &kNS_INCREMENTALDOWNLOAD_CID },
|
||||
{ NS_STDURLPARSER_CONTRACTID, &kNS_STDURLPARSER_CID },
|
||||
{ NS_NOAUTHURLPARSER_CONTRACTID, &kNS_NOAUTHURLPARSER_CID },
|
||||
|
|
|
@ -874,8 +874,8 @@ class RecursiveMakeBackend(CommonBackend):
|
|||
if isinstance(path, SourcePath):
|
||||
if path.full_path.startswith(backend_file.srcdir):
|
||||
return '$(srcdir)' + path.full_path[len(backend_file.srcdir):]
|
||||
if path.full_path.startswith(self.environment.topsrcdir):
|
||||
return '$(topsrcdir)' + path.full_path[len(self.environment.topsrcdir):]
|
||||
if path.full_path.startswith(backend_file.topsrcdir):
|
||||
return '$(topsrcdir)' + path.full_path[len(backend_file.topsrcdir):]
|
||||
elif isinstance(path, ObjDirPath):
|
||||
if path.full_path.startswith(backend_file.objdir):
|
||||
return path.full_path[len(backend_file.objdir) + 1:]
|
||||
|
|
|
@ -975,7 +975,7 @@ nss_addEscape(const char* string, char quote)
|
|||
} // unnamed namespace
|
||||
|
||||
SECStatus
|
||||
InitializeNSS(const char* dir, bool readOnly)
|
||||
InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules)
|
||||
{
|
||||
// The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
|
||||
// module by NSS_Initialize because we will load it in InstallLoadableRoots
|
||||
|
@ -986,6 +986,9 @@ InitializeNSS(const char* dir, bool readOnly)
|
|||
if (readOnly) {
|
||||
flags |= NSS_INIT_READONLY;
|
||||
}
|
||||
if (!loadPKCS11Modules) {
|
||||
flags |= NSS_INIT_NOMODDB;
|
||||
}
|
||||
return ::NSS_Initialize(dir, "", "", SECMOD_DB, flags);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ enum class ValidityCheckingMode {
|
|||
CheckForEV = 1,
|
||||
};
|
||||
|
||||
SECStatus InitializeNSS(const char* dir, bool readOnly);
|
||||
SECStatus InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules);
|
||||
|
||||
void DisableMD5();
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "nsISiteSecurityService.h"
|
||||
#include "nsITokenPasswordDialogs.h"
|
||||
#include "nsIWindowWatcher.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "nsNSSHelper.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
@ -989,14 +990,27 @@ nsNSSComponent::InitializeNSS()
|
|||
|
||||
SECStatus init_rv = SECFailure;
|
||||
bool nocertdb = Preferences::GetBool("security.nocertdb", false);
|
||||
bool inSafeMode = true;
|
||||
nsCOMPtr<nsIXULRuntime> runtime(do_GetService("@mozilla.org/xre/runtime;1"));
|
||||
// There might not be an nsIXULRuntime in embedded situations. This will
|
||||
// default to assuming we are in safe mode (as a result, no external PKCS11
|
||||
// modules will be loaded).
|
||||
if (runtime) {
|
||||
rv = runtime->GetInSafeMode(&inSafeMode);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("inSafeMode: %u\n", inSafeMode));
|
||||
|
||||
if (!nocertdb && !profileStr.IsEmpty()) {
|
||||
// First try to initialize the NSS DB in read/write mode.
|
||||
init_rv = ::mozilla::psm::InitializeNSS(profileStr.get(), false);
|
||||
// Only load PKCS11 modules if we're not in safe mode.
|
||||
init_rv = ::mozilla::psm::InitializeNSS(profileStr.get(), false, !inSafeMode);
|
||||
// If that fails, attempt read-only mode.
|
||||
if (init_rv != SECSuccess) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("could not init NSS r/w in %s\n", profileStr.get()));
|
||||
init_rv = ::mozilla::psm::InitializeNSS(profileStr.get(), true);
|
||||
init_rv = ::mozilla::psm::InitializeNSS(profileStr.get(), true, !inSafeMode);
|
||||
}
|
||||
if (init_rv != SECSuccess) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("could not init in r/o either\n"));
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
// This test loads a testing PKCS #11 module. After simulating a shutdown and
|
||||
// startup, the test verifies that the module is automatically loaded again.
|
||||
// After simulating a shutdown and restart in safe mode, the test verifies that
|
||||
// the module is not automatically loaded again.
|
||||
|
||||
var { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
|
||||
|
||||
// Registers an nsIXULRuntime so the test can control whether or not the
|
||||
// platform thinks it's in safe mode.
|
||||
function registerXULRuntime() {
|
||||
let xulRuntime = {
|
||||
inSafeMode: false,
|
||||
logConsoleErrors: true,
|
||||
OS: "XPCShell",
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
invalidateCachesOnRestart: function invalidateCachesOnRestart() {
|
||||
// Do nothing
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULRuntime])
|
||||
};
|
||||
|
||||
let xulRuntimeFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return xulRuntime.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
const XULRUNTIME_CONTRACTID = "@mozilla.org/xre/runtime;1";
|
||||
const XULRUNTIME_CID = Components.ID("{f0f0b230-5525-4127-98dc-7bca39059e70}");
|
||||
registrar.registerFactory(XULRUNTIME_CID, "XULRuntime", XULRUNTIME_CONTRACTID,
|
||||
xulRuntimeFactory);
|
||||
return xulRuntime;
|
||||
}
|
||||
|
||||
// Loads the PKCS11 test module.
|
||||
function loadTestModule() {
|
||||
let pkcs11 = Cc["@mozilla.org/security/pkcs11;1"].getService(Ci.nsIPKCS11);
|
||||
let libraryName = ctypes.libraryName("pkcs11testmodule");
|
||||
let libraryFile = Services.dirsvc.get("CurWorkD", Ci.nsILocalFile);
|
||||
libraryFile.append("pkcs11testmodule");
|
||||
libraryFile.append(libraryName);
|
||||
ok(libraryFile.exists(), "The pkcs11testmodule file should exist");
|
||||
pkcs11.addModule("PKCS11 Test Module", libraryFile.path, 0, 0);
|
||||
}
|
||||
|
||||
// Tests finding the PKCS11 test module. 'shouldSucceed' is a boolean
|
||||
// indicating whether or not the test module is expected to be found.
|
||||
function testFindTestModule(shouldSucceed) {
|
||||
let pkcs11ModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"]
|
||||
.getService(Ci.nsIPKCS11ModuleDB);
|
||||
try {
|
||||
let module = pkcs11ModuleDB.findModuleByName("PKCS11 Test Module");
|
||||
ok(shouldSucceed, "Success expected: findModuleByName should not throw");
|
||||
ok(module, "module should be non-null");
|
||||
} catch (e) {
|
||||
ok(!shouldSucceed, "Failure expected: findModuleByName should throw");
|
||||
}
|
||||
}
|
||||
|
||||
function simulateShutdown() {
|
||||
let psmComponent = Cc["@mozilla.org/psm;1"].getService(Ci.nsIObserver);
|
||||
psmComponent.observe(null, "profile-change-net-teardown", null);
|
||||
psmComponent.observe(null, "profile-before-change", null);
|
||||
psmComponent.observe(null, "xpcom-shutdown", null);
|
||||
}
|
||||
|
||||
function simulateStartup() {
|
||||
let psmComponent = Cc["@mozilla.org/psm;1"].getService(Ci.nsIObserver);
|
||||
psmComponent.observe(null, "profile-do-change", null);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
let xulRuntime = registerXULRuntime();
|
||||
loadTestModule();
|
||||
|
||||
// After having loaded the test module, it should be available.
|
||||
testFindTestModule(true);
|
||||
simulateShutdown();
|
||||
simulateStartup();
|
||||
// After shutting down and starting up, the test module should automatically
|
||||
// be loaded again.
|
||||
testFindTestModule(true);
|
||||
|
||||
simulateShutdown();
|
||||
// Simulate starting in safe mode.
|
||||
xulRuntime.inSafeMode = true;
|
||||
simulateStartup();
|
||||
// When starting in safe mode, the test module should not be loaded.
|
||||
testFindTestModule(false);
|
||||
}
|
|
@ -11,3 +11,6 @@ skip-if = os == "win"
|
|||
[test_pkcs11_no_events_after_removal.js]
|
||||
# Bug 1049969: this test doesn't work on windows
|
||||
skip-if = os == "win"
|
||||
[test_pkcs11_safe_mode.js]
|
||||
# Bug 1049969: this test doesn't work on windows
|
||||
skip-if = os == "win"
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
__all__ = ['parse', 'ParseError', 'ExpressionParser']
|
||||
|
||||
|
@ -264,7 +266,9 @@ class ExpressionParser(object):
|
|||
self.token = self.iter.next()
|
||||
return self.expression()
|
||||
except:
|
||||
raise ParseError("could not parse: %s; variables: %s" % (self.text, self.valuemapping))
|
||||
extype, ex, tb = sys.exc_info()
|
||||
formatted = ''.join(traceback.format_exception_only(extype, ex))
|
||||
raise ParseError("could not parse: %s\nexception: %svariables: %s" % (self.text, formatted, self.valuemapping)), None, tb
|
||||
|
||||
__call__ = parse
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
[oninstall-script-error.https.html]
|
||||
type: testharness
|
||||
[install handler throws an error that is cancelled]
|
||||
expected: FAIL
|
||||
|
||||
[install handler throws an error and prevents default]
|
||||
expected: FAIL
|
||||
|
|
@ -45,15 +45,21 @@ function make_test(name, script, expect_install) {
|
|||
script: 'resources/oninstall-throw-error-from-nested-event-worker.js',
|
||||
expect_install: true
|
||||
},
|
||||
|
||||
// The following two cases test what happens when the ServiceWorkerGlobalScope
|
||||
// 'error' event handler cancels the resulting error event. Since the
|
||||
// original 'install' event handler through, the installation should still
|
||||
// be stopped in this case. See:
|
||||
// https://github.com/slightlyoff/ServiceWorker/issues/778
|
||||
{
|
||||
name: 'install handler throws an error that is cancelled',
|
||||
script: 'resources/oninstall-throw-error-then-cancel-worker.js',
|
||||
expect_install: true
|
||||
expect_install: false
|
||||
},
|
||||
{
|
||||
name: 'install handler throws an error and prevents default',
|
||||
script: 'resources/oninstall-throw-error-then-prevent-default-worker.js',
|
||||
expect_install: true
|
||||
expect_install: false
|
||||
}
|
||||
].forEach(function(test_case) {
|
||||
make_test(test_case.name, test_case.script, test_case.expect_install);
|
||||
|
|
|
@ -6274,6 +6274,12 @@
|
|||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'reconfigure thread' request to go round trip."
|
||||
},
|
||||
"MEDIA_RUST_MP4PARSE_SUCCESS": {
|
||||
"expires_in_version": "50",
|
||||
"kind": "boolean",
|
||||
"description": "(Bug 1220885) Whether the rust mp4 demuxer successfully parsed a stream segment.",
|
||||
"cpp_guard": "MOZ_RUST_MP4PARSE"
|
||||
},
|
||||
"MEDIA_WMF_DECODE_ERROR": {
|
||||
"expires_in_version": "50",
|
||||
"kind": "enumerated",
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче