Merge inbound to central, a=merge

This commit is contained in:
Wes Kocher 2015-09-15 17:16:24 -07:00
commit 42eb4d290b
79 changed files with 3631 additions and 1319 deletions

View File

@ -104,8 +104,9 @@ AccReorderEvent::IsShowHideEventTarget(const Accessible* aTarget) const
////////////////////////////////////////////////////////////////////////////////
AccHideEvent::
AccHideEvent(Accessible* aTarget, nsINode* aTargetNode) :
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget, aTargetNode)
AccHideEvent(Accessible* aTarget, nsINode* aTargetNode, bool aNeedsShutdown) :
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget, aTargetNode),
mNeedsShutdown(aNeedsShutdown)
{
mNextSibling = mAccessible->NextSibling();
mPrevSibling = mAccessible->PrevSibling();

View File

@ -250,7 +250,8 @@ protected:
class AccHideEvent: public AccMutationEvent
{
public:
AccHideEvent(Accessible* aTarget, nsINode* aTargetNode);
AccHideEvent(Accessible* aTarget, nsINode* aTargetNode,
bool aNeedsShutdown = true);
// Event
static const EventGroup kEventGroup = eHideEvent;
@ -263,8 +264,10 @@ public:
Accessible* TargetParent() const { return mParent; }
Accessible* TargetNextSibling() const { return mNextSibling; }
Accessible* TargetPrevSibling() const { return mPrevSibling; }
bool NeedsShutdown() const { return mNeedsShutdown; }
protected:
bool mNeedsShutdown;
nsRefPtr<Accessible> mNextSibling;
nsRefPtr<Accessible> mPrevSibling;

View File

@ -546,8 +546,10 @@ EventQueue::ProcessEventQueue()
}
}
if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE)
AccHideEvent* hideEvent = downcast_accEvent(event);
if (hideEvent && hideEvent->NeedsShutdown()) {
mDocument->ShutdownChildrenInSubtree(event->mAccessible);
}
if (!mDocument)
return;

View File

@ -109,9 +109,6 @@ NotificationController::ScheduleContentInsertion(Accessible* aContainer,
}
}
////////////////////////////////////////////////////////////////////////////////
// NotificationCollector: protected
void
NotificationController::ScheduleProcessing()
{
@ -123,6 +120,9 @@ NotificationController::ScheduleProcessing()
}
}
////////////////////////////////////////////////////////////////////////////////
// NotificationCollector: protected
bool
NotificationController::IsUpdatePending()
{

View File

@ -8,6 +8,8 @@
#include "EventQueue.h"
#include "mozilla/IndexSequence.h"
#include "mozilla/Tuple.h"
#include "nsCycleCollectionParticipant.h"
#include "nsRefreshDriver.h"
@ -54,32 +56,32 @@ private:
* longer than the document accessible owning the notification controller
* that this notification is processed by.
*/
template<class Class, class Arg>
template<class Class, class ... Args>
class TNotification : public Notification
{
public:
typedef void (Class::*Callback)(Arg*);
typedef void (Class::*Callback)(Args* ...);
TNotification(Class* aInstance, Callback aCallback, Arg* aArg) :
mInstance(aInstance), mCallback(aCallback), mArg(aArg) { }
TNotification(Class* aInstance, Callback aCallback, Args* ... aArgs) :
mInstance(aInstance), mCallback(aCallback), mArgs(aArgs...) { }
virtual ~TNotification() { mInstance = nullptr; }
virtual void Process() override
{
(mInstance->*mCallback)(mArg);
mInstance = nullptr;
mCallback = nullptr;
mArg = nullptr;
}
{ ProcessHelper(typename IndexSequenceFor<Args...>::Type()); }
private:
TNotification(const TNotification&);
TNotification& operator = (const TNotification&);
template <size_t... Indices>
void ProcessHelper(IndexSequence<Indices...>)
{
(mInstance->*mCallback)(Get<Indices>(mArgs)...);
}
Class* mInstance;
Callback mCallback;
nsRefPtr<Arg> mArg;
Tuple<nsRefPtr<Args> ...> mArgs;
};
/**
@ -131,6 +133,12 @@ public:
nsIContent* aStartChildNode,
nsIContent* aEndChildNode);
/**
* Start to observe refresh to make notifications and events processing after
* layout.
*/
void ScheduleProcessing();
/**
* Process the generic notification synchronously if there are no pending
* layout changes and no notifications are pending or being processed right
@ -165,13 +173,12 @@ public:
* @note The caller must guarantee that the given instance still exists when
* the notification is processed.
*/
template<class Class, class Arg>
template<class Class>
inline void ScheduleNotification(Class* aInstance,
typename TNotification<Class, Arg>::Callback aMethod,
Arg* aArg)
typename TNotification<Class>::Callback aMethod)
{
nsRefPtr<Notification> notification =
new TNotification<Class, Arg>(aInstance, aMethod, aArg);
new TNotification<Class>(aInstance, aMethod);
if (notification && mNotifications.AppendElement(notification))
ScheduleProcessing();
}
@ -187,12 +194,6 @@ protected:
nsCycleCollectingAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
/**
* Start to observe refresh to make notifications and events processing after
* layout.
*/
void ScheduleProcessing();
/**
* Return true if the accessible tree state update is pending.
*/

View File

@ -6,6 +6,7 @@
#include "TreeWalker.h"
#include "Accessible.h"
#include "AccIterator.h"
#include "nsAccessibilityService.h"
#include "DocAccessible.h"
@ -50,20 +51,16 @@ TreeWalker::NextChild()
if (mStateStack.IsEmpty())
return nullptr;
dom::AllChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
ChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
while (top) {
while (nsIContent* childNode = top->GetNextChild()) {
bool isSubtreeHidden = false;
Accessible* accessible = mFlags & eWalkCache ?
mDoc->GetAccessible(childNode) :
GetAccService()->GetOrCreateAccessible(childNode, mContext,
&isSubtreeHidden);
if (accessible)
return accessible;
Accessible* child = nullptr;
bool skipSubtree = false;
while (nsIContent* childNode = Next(top, &child, &skipSubtree)) {
if (child)
return child;
// Walk down into subtree to find accessibles.
if (!isSubtreeHidden && childNode->IsElement())
if (!skipSubtree && childNode->IsElement())
top = PushState(childNode);
}
@ -82,9 +79,8 @@ TreeWalker::NextChild()
return nullptr;
nsIContent* parent = parentNode->AsElement();
top = mStateStack.AppendElement(dom::AllChildrenIterator(parent,
mChildFilter));
while (nsIContent* childNode = top->GetNextChild()) {
top = PushState(parent);
while (nsIContent* childNode = Next(top)) {
if (childNode == mAnchorNode) {
mAnchorNode = parent;
return NextChild();
@ -101,7 +97,47 @@ TreeWalker::NextChild()
return nullptr;
}
dom::AllChildrenIterator*
nsIContent*
TreeWalker::Next(ChildrenIterator* aIter, Accessible** aAccesible,
bool* aSkipSubtree)
{
nsIContent* childEl = aIter->mDOMIter.GetNextChild();
if (!aAccesible)
return childEl;
*aAccesible = nullptr;
*aSkipSubtree = false;
if (childEl) {
Accessible* accessible = mFlags & eWalkCache ?
mDoc->GetAccessible(childEl) :
GetAccService()->GetOrCreateAccessible(childEl, mContext, aSkipSubtree);
// Ignore the accessible and its subtree if it was repositioned by means of
// aria-owns.
if (accessible) {
if (accessible->IsRepositioned()) {
*aSkipSubtree = true;
} else {
*aAccesible = accessible;
}
}
return childEl;
}
// At last iterate over ARIA owned children.
Accessible* parent = mDoc->GetAccessible(aIter->mDOMIter.Parent());
if (parent) {
Accessible* child = mDoc->ARIAOwnedAt(parent, aIter->mARIAOwnsIdx++);
if (child) {
*aAccesible = child;
return child->GetContent();
}
}
return nullptr;
}
TreeWalker::ChildrenIterator*
TreeWalker::PopState()
{
size_t length = mStateStack.Length();

View File

@ -57,27 +57,37 @@ private:
TreeWalker(const TreeWalker&);
TreeWalker& operator =(const TreeWalker&);
struct ChildrenIterator {
ChildrenIterator(nsIContent* aNode, uint32_t aFilter) :
mDOMIter(aNode, aFilter), mARIAOwnsIdx(0) { }
dom::AllChildrenIterator mDOMIter;
uint32_t mARIAOwnsIdx;
};
nsIContent* Next(ChildrenIterator* aIter, Accessible** aAccessible = nullptr,
bool* aSkipSubtree = nullptr);
/**
* Create new state for the given node and push it on top of stack.
*
* @note State stack is used to navigate up/down the DOM subtree during
* accessible children search.
*/
dom::AllChildrenIterator* PushState(nsIContent* aContent)
ChildrenIterator* PushState(nsIContent* aContent)
{
return mStateStack.AppendElement(dom::AllChildrenIterator(aContent,
mChildFilter));
return mStateStack.AppendElement(ChildrenIterator(aContent, mChildFilter));
}
/**
* Pop state from stack.
*/
dom::AllChildrenIterator* PopState();
ChildrenIterator* PopState();
DocAccessible* mDoc;
Accessible* mContext;
nsIContent* mAnchorNode;
nsAutoTArray<dom::AllChildrenIterator, 20> mStateStack;
nsAutoTArray<ChildrenIterator, 20> mStateStack;
int32_t mChildFilter;
uint32_t mFlags;
};

View File

@ -1974,8 +1974,8 @@ Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
if (mParent) {
if (mParent != aParent) {
NS_ERROR("Adopting child!");
mParent->RemoveChild(this);
mParent->InvalidateChildrenGroupInfo();
mParent->RemoveChild(this);
} else {
NS_ERROR("Binding to the same parent!");
return;

View File

@ -160,6 +160,8 @@ public:
return DOMNode.forget();
}
nsIContent* GetContent() const { return mContent; }
mozilla::dom::Element* Elm() const
{ return mContent && mContent->IsElement() ? mContent->AsElement() : nullptr; }
/**
* Return node type information of DOM node associated with the accessible.
@ -899,6 +901,19 @@ public:
mStateFlags &= ~eSurvivingInUpdate;
}
/**
* Get/set repositioned bit indicating that the accessible was moved in
* the accessible tree, i.e. the accessible tree structure differs from DOM.
*/
bool IsRepositioned() const { return mStateFlags & eRepositioned; }
void SetRepositioned(bool aRepositioned)
{
if (aRepositioned)
mStateFlags |= eRepositioned;
else
mStateFlags &= ~eRepositioned;
}
/**
* Return true if this accessible has a parent whose name depends on this
* accessible.
@ -914,7 +929,6 @@ public:
void SetARIAHidden(bool aIsDefined);
protected:
virtual ~Accessible();
/**
@ -990,8 +1004,9 @@ protected:
eSubtreeMutating = 1 << 6, // subtree is being mutated
eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
eRepositioned = 1 << 9, // accessible was moved in tree
eLastStateFlag = eSurvivingInUpdate
eLastStateFlag = eRepositioned
};
/**
@ -1106,7 +1121,7 @@ protected:
int32_t mIndexInParent;
static const uint8_t kChildrenFlagsBits = 2;
static const uint8_t kStateFlagsBits = 9;
static const uint8_t kStateFlagsBits = 10;
static const uint8_t kContextFlagsBits = 2;
static const uint8_t kTypeBits = 6;
static const uint8_t kGenericTypesBits = 14;

View File

@ -707,7 +707,7 @@ DocAccessible::AttributeWillChange(nsIDocument* aDocument,
// because dependent IDs cache doesn't contain IDs from non accessible
// elements.
if (aModType != nsIDOMMutationEvent::ADDITION)
RemoveDependentIDsFor(aElement, aAttribute);
RemoveDependentIDsFor(accessible, aAttribute);
// Store the ARIA attribute old value so that it can be used after
// attribute change. Note, we assume there's no nested ARIA attribute
@ -769,7 +769,7 @@ DocAccessible::AttributeChanged(nsIDocument* aDocument,
// dependent IDs cache when its accessible is created.
if (aModType == nsIDOMMutationEvent::MODIFICATION ||
aModType == nsIDOMMutationEvent::ADDITION) {
AddDependentIDsFor(aElement, aAttribute);
AddDependentIDsFor(accessible, aAttribute);
}
}
@ -1241,9 +1241,7 @@ DocAccessible::BindToDocument(Accessible* aAccessible,
aAccessible->SetRoleMapEntry(aRoleMapEntry);
nsIContent* content = aAccessible->GetContent();
if (content && content->IsElement())
AddDependentIDsFor(content->AsElement());
AddDependentIDsFor(aAccessible);
}
void
@ -1338,6 +1336,49 @@ DocAccessible::ProcessInvalidationList()
}
mInvalidationList.Clear();
// Alter the tree according to aria-owns (seize the trees).
for (uint32_t idx = 0; idx < mARIAOwnsInvalidationList.Length(); idx++) {
Accessible* owner = mARIAOwnsInvalidationList[idx].mOwner;
Accessible* child = GetAccessible(mARIAOwnsInvalidationList[idx].mChild);
if (!child) {
continue;
}
// XXX: update context flags
{
Accessible* oldParent = child->Parent();
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
nsRefPtr<AccMutationEvent> hideEvent =
new AccHideEvent(child, child->GetContent(), false);
FireDelayedEvent(hideEvent);
reorderEvent->AddSubMutationEvent(hideEvent);
AutoTreeMutation mut(oldParent);
oldParent->RemoveChild(child);
MaybeNotifyOfValueChange(oldParent);
FireDelayedEvent(reorderEvent);
}
{
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
nsRefPtr<AccMutationEvent> showEvent =
new AccShowEvent(child, child->GetContent());
FireDelayedEvent(showEvent);
reorderEvent->AddSubMutationEvent(showEvent);
AutoTreeMutation mut(owner);
owner->AppendChild(child);
MaybeNotifyOfValueChange(owner);
FireDelayedEvent(reorderEvent);
}
child->SetRepositioned(true);
}
mARIAOwnsInvalidationList.Clear();
}
Accessible*
@ -1496,26 +1537,29 @@ DocAccessible::ProcessLoad()
}
void
DocAccessible::AddDependentIDsFor(dom::Element* aRelProviderElm,
nsIAtom* aRelAttr)
DocAccessible::AddDependentIDsFor(Accessible* aRelProvider, nsIAtom* aRelAttr)
{
dom::Element* relProviderEl = aRelProvider->Elm();
if (!relProviderEl)
return;
for (uint32_t idx = 0; idx < kRelationAttrsLen; idx++) {
nsIAtom* relAttr = *kRelationAttrs[idx];
if (aRelAttr && aRelAttr != relAttr)
continue;
if (relAttr == nsGkAtoms::_for) {
if (!aRelProviderElm->IsAnyOfHTMLElements(nsGkAtoms::label,
nsGkAtoms::output))
if (!relProviderEl->IsAnyOfHTMLElements(nsGkAtoms::label,
nsGkAtoms::output))
continue;
} else if (relAttr == nsGkAtoms::control) {
if (!aRelProviderElm->IsAnyOfXULElements(nsGkAtoms::label,
nsGkAtoms::description))
if (!relProviderEl->IsAnyOfXULElements(nsGkAtoms::label,
nsGkAtoms::description))
continue;
}
IDRefsIterator iter(this, aRelProviderElm, relAttr);
IDRefsIterator iter(this, relProviderEl, relAttr);
while (true) {
const nsDependentSubstring id = iter.NextID();
if (id.IsEmpty())
@ -1531,7 +1575,7 @@ DocAccessible::AddDependentIDsFor(dom::Element* aRelProviderElm,
if (providers) {
AttrRelProvider* provider =
new AttrRelProvider(relAttr, aRelProviderElm);
new AttrRelProvider(relAttr, relProviderEl);
if (provider) {
providers->AppendElement(provider);
@ -1540,8 +1584,48 @@ DocAccessible::AddDependentIDsFor(dom::Element* aRelProviderElm,
// children invalidation (this happens immediately after the caching
// is finished).
nsIContent* dependentContent = iter.GetElem(id);
if (dependentContent && !HasAccessible(dependentContent)) {
mInvalidationList.AppendElement(dependentContent);
if (dependentContent) {
if (!HasAccessible(dependentContent)) {
mInvalidationList.AppendElement(dependentContent);
}
if (relAttr == nsGkAtoms::aria_owns) {
// Dependent content cannot point to other aria-owns content or
// their parents. Ignore it if so.
// XXX: note, this alg may make invalid the scenario when X owns Y
// and Y owns Z, we should have something smarter to handle that.
bool isvalid = true;
for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
Accessible* owner = it.Key();
nsIContent* parentEl = owner->GetContent();
while (parentEl && parentEl != dependentContent) {
parentEl = parentEl->GetParent();
}
if (parentEl) {
isvalid = false;
break;
}
}
if (isvalid) {
// ARIA owns also cannot refer to itself or a parent.
nsIContent* parentEl = relProviderEl;
while (parentEl && parentEl != dependentContent) {
parentEl = parentEl->GetParent();
}
if (parentEl) {
isvalid = false;
}
if (isvalid) {
nsTArray<nsIContent*>* list =
mARIAOwnsHash.LookupOrAdd(aRelProvider);
list->AppendElement(dependentContent);
mARIAOwnsInvalidationList.AppendElement(
ARIAOwnsPair(aRelProvider, dependentContent));
}
}
}
}
}
}
@ -1552,18 +1636,25 @@ DocAccessible::AddDependentIDsFor(dom::Element* aRelProviderElm,
if (aRelAttr)
break;
}
// Make sure to schedule the tree update if needed.
mNotificationController->ScheduleProcessing();
}
void
DocAccessible::RemoveDependentIDsFor(dom::Element* aRelProviderElm,
DocAccessible::RemoveDependentIDsFor(Accessible* aRelProvider,
nsIAtom* aRelAttr)
{
dom::Element* relProviderElm = aRelProvider->Elm();
if (!relProviderElm)
return;
for (uint32_t idx = 0; idx < kRelationAttrsLen; idx++) {
nsIAtom* relAttr = *kRelationAttrs[idx];
if (aRelAttr && aRelAttr != *kRelationAttrs[idx])
continue;
IDRefsIterator iter(this, aRelProviderElm, relAttr);
IDRefsIterator iter(this, relProviderElm, relAttr);
while (true) {
const nsDependentSubstring id = iter.NextID();
if (id.IsEmpty())
@ -1574,7 +1665,7 @@ DocAccessible::RemoveDependentIDsFor(dom::Element* aRelProviderElm,
for (uint32_t jdx = 0; jdx < providers->Length(); ) {
AttrRelProvider* provider = (*providers)[jdx];
if (provider->mRelAttr == relAttr &&
provider->mContent == aRelProviderElm)
provider->mContent == relProviderElm)
providers->RemoveElement(provider);
else
jdx++;
@ -1584,6 +1675,59 @@ DocAccessible::RemoveDependentIDsFor(dom::Element* aRelProviderElm,
}
}
// aria-owns has gone, put the children back.
if (relAttr == nsGkAtoms::aria_owns) {
nsTArray<nsIContent*>* children = mARIAOwnsHash.Get(aRelProvider);
if (children) {
nsTArray<Accessible*> containers;
// Remove ARIA owned elements from where they belonged.
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aRelProvider);
{
AutoTreeMutation mut(aRelProvider);
for (uint32_t idx = 0; idx < children->Length(); idx++) {
nsIContent* childEl = children->ElementAt(idx);
Accessible* child = GetAccessible(childEl);
if (child && child->IsRepositioned()) {
{
nsRefPtr<AccMutationEvent> hideEvent =
new AccHideEvent(child, childEl, false);
FireDelayedEvent(hideEvent);
reorderEvent->AddSubMutationEvent(hideEvent);
aRelProvider->RemoveChild(child);
}
// Collect DOM-order containers to update their trees.
child->SetRepositioned(false);
Accessible* container = GetContainerAccessible(childEl);
if (!containers.Contains(container)) {
containers.AppendElement(container);
}
}
}
}
mARIAOwnsHash.Remove(aRelProvider);
for (uint32_t idx = 0; idx < mARIAOwnsInvalidationList.Length();) {
if (mARIAOwnsInvalidationList[idx].mOwner == aRelProvider) {
mARIAOwnsInvalidationList.RemoveElementAt(idx);
continue;
}
idx++;
}
MaybeNotifyOfValueChange(aRelProvider);
FireDelayedEvent(reorderEvent);
// Reinserted previously ARIA owned elements into the tree
// (restore a DOM-like order).
for (uint32_t idx = 0; idx < containers.Length(); idx++) {
UpdateTreeOnInsertion(containers[idx]);
}
}
}
// If the relation attribute is given then we don't have anything else to
// check.
if (aRelAttr)
@ -1806,6 +1950,11 @@ DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNod
}
}
// We may not have an integral DOM tree to remove all aria-owns relations
// from the tree. Validate all relations after timeout to workaround that.
mNotificationController->ScheduleNotification<DocAccessible>
(this, &DocAccessible::ValidateARIAOwned);
// Content insertion/removal is not cause of accessible tree change.
if (updateFlags == eNoAccessible)
return;
@ -1889,6 +2038,21 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
return updateFlags;
}
void
DocAccessible::ValidateARIAOwned()
{
for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
nsTArray<nsIContent*>* childEls = it.UserData();
for (uint32_t idx = 0; idx < childEls->Length(); idx++) {
nsIContent* childEl = childEls->ElementAt(idx);
Accessible* child = GetAccessible(childEl);
if (child && !child->GetFrame()) {
UpdateTreeOnRemoval(child->Parent(), childEl);
}
}
}
}
void
DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
Accessible** aFocusedAcc)
@ -1926,10 +2090,7 @@ void
DocAccessible::UncacheChildrenInSubtree(Accessible* aRoot)
{
aRoot->mStateFlags |= eIsNotInDocument;
nsIContent* rootContent = aRoot->GetContent();
if (rootContent && rootContent->IsElement())
RemoveDependentIDsFor(rootContent->AsElement());
RemoveDependentIDsFor(aRoot);
uint32_t count = aRoot->ContentChildCount();
for (uint32_t idx = 0; idx < count; idx++)

View File

@ -32,7 +32,7 @@ class DocManager;
class NotificationController;
class DocAccessibleChild;
class RelatedAccIterator;
template<class Class, class Arg>
template<class Class, class ... Args>
class TNotification;
class DocAccessible : public HyperTextAccessibleWrap,
@ -281,6 +281,22 @@ public:
*/
Accessible* GetAccessibleOrDescendant(nsINode* aNode) const;
/**
* Returns aria-owns seized child at the given index.
*/
Accessible* ARIAOwnedAt(Accessible* aParent, uint32_t aIndex) const
{
nsTArray<nsIContent*>* childrenEl = mARIAOwnsHash.Get(aParent);
if (childrenEl) {
nsIContent* childEl = childrenEl->SafeElementAt(aIndex);
Accessible* child = GetAccessible(childEl);
if (child && child->IsRepositioned()) {
return child;
}
}
return nullptr;
}
/**
* Return true if the given ID is referred by relation attribute.
*
@ -406,7 +422,7 @@ protected:
* @param aRelProvider [in] accessible that element has relation attribute
* @param aRelAttr [in, optional] relation attribute
*/
void AddDependentIDsFor(dom::Element* aRelProviderElm,
void AddDependentIDsFor(Accessible* aRelProvider,
nsIAtom* aRelAttr = nullptr);
/**
@ -417,7 +433,7 @@ protected:
* @param aRelProvider [in] accessible that element has relation attribute
* @param aRelAttr [in, optional] relation attribute
*/
void RemoveDependentIDsFor(dom::Element* aRelProviderElm,
void RemoveDependentIDsFor(Accessible* aRelProvider,
nsIAtom* aRelAttr = nullptr);
/**
@ -491,6 +507,11 @@ protected:
uint32_t UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
AccReorderEvent* aReorderEvent);
/**
* Validates all aria-owns connections and updates the tree accordingly.
*/
void ValidateARIAOwned();
/**
* Create accessible tree.
*
@ -646,6 +667,25 @@ protected:
*/
nsTArray<nsIContent*> mInvalidationList;
/**
* Holds a list of aria-owns relations.
*/
nsClassHashtable<nsPtrHashKey<Accessible>, nsTArray<nsIContent*> >
mARIAOwnsHash;
struct ARIAOwnsPair {
ARIAOwnsPair(Accessible* aOwner, nsIContent* aChild) :
mOwner(aOwner), mChild(aChild) { }
ARIAOwnsPair(const ARIAOwnsPair& aPair) :
mOwner(aPair.mOwner), mChild(aPair.mChild) { }
ARIAOwnsPair& operator =(const ARIAOwnsPair& aPair)
{ mOwner = aPair.mOwner; mChild = aPair.mChild; return *this; }
Accessible* mOwner;
nsIContent* mChild;
};
nsTArray<ARIAOwnsPair> mARIAOwnsInvalidationList;
/**
* Used to process notification from core and accessible events.
*/

View File

@ -452,26 +452,39 @@ function testAccessibleTree(aAccOrElmOrID, aAccTree, aFlags)
var children = acc.children;
var childCount = children.length;
if (accTree.children.length != childCount) {
for (var i = 0; i < Math.max(accTree.children.length, childCount); i++) {
var accChild;
try {
accChild = children.queryElementAt(i, nsIAccessible);
if (!accTree.children[i]) {
testChild = accTree.children[i];
if (!testChild) {
ok(false, prettyName(acc) + " has an extra child at index " + i +
" : " + prettyName(accChild));
continue;
}
if (accChild.role !== accTree.children[i].role) {
var key = Object.keys(testChild)[0];
var roleName = "ROLE_" + key;
if (roleName in nsIAccessibleRole) {
testChild = {
role: nsIAccessibleRole[roleName],
children: testChild[key]
};
}
if (accChild.role !== testChild.role) {
ok(false, prettyName(accTree) + " and " + prettyName(acc) +
" have different children at index " + i + " : " +
prettyName(accTree.children[i]) + ", " + prettyName(accChild));
prettyName(testChild) + ", " + prettyName(accChild));
}
info("Matching " + prettyName(accTree) + " and " + prettyName(acc) +
" child at index " + i + " : " + prettyName(accChild));
} catch (e) {
ok(false, prettyName(accTree) + " has an extra child at index " + i +
" : " + prettyName(accTree.children[i]));
" : " + prettyName(testChild) + ", " + e);
throw e;
}
}
} else {

View File

@ -87,19 +87,16 @@
tree =
{ SECTION: [ // container
{ LABEL: [ // label, has aria-owns
{ TEXT_LEAF: [ ] }
{ TEXT_LEAF: [ ] },
{ LABEL: [ // label, referenced by aria-owns
{ TEXT_LEAF: [ ] }
] },
] },
{ TEXT_LEAF: [ ] },
{ LABEL: [ // label, referenced by aria-owns
{ TEXT_LEAF: [ ] }
] },
{ TEXT_LEAF: [ ] },
{ LABEL: [ // label, has aria-owns
{ TEXT_LEAF: [ ] }
] },
{ TEXT_LEAF: [ ] },
{ LABEL: [ // label, referenced by aria-owns
{ TEXT_LEAF: [ ] }
{ TEXT_LEAF: [ ] },
{ LABEL: [ // label, referenced by aria-owns
{ TEXT_LEAF: [ ] }
] }
] }
] };
testAccessibleTree("airaglobalprop_cnt", tree);
@ -172,12 +169,11 @@
</ul>
</div>
<div id="airaglobalprop_cnt">
<label role="presentation" aria-owns="ariaowned">has aria-owns</label>
<label role="presentation" id="ariaowned">referred by aria-owns</label>
<label role="none" aria-owns="ariaowned2">has aria-owns</label>
<label role="none" id="ariaowned2">referred by aria-owns</label>
</div>
<div id="airaglobalprop_cnt"><label
role="presentation" aria-owns="ariaowned">has aria-owns</label><label
role="presentation" id="ariaowned">referred by aria-owns</label><label
role="none" aria-owns="ariaowned2">has aria-owns</label><label
role="none" id="ariaowned2">referred by aria-owns</label></div>
</body>
</html>

View File

@ -1,6 +1,7 @@
[DEFAULT]
[test_ariadialog.html]
[test_ariaowns.html]
[test_bug852150.xhtml]
[test_bug883708.xhtml]
[test_bug884251.xhtml]

View File

@ -0,0 +1,234 @@
<!DOCTYPE html>
<html>
<head>
<title>@aria-owns attribute testing</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript">
////////////////////////////////////////////////////////////////////////////
// Invokers
////////////////////////////////////////////////////////////////////////////
function removeARIAOwns()
{
this.eventSeq = [
new invokerChecker(EVENT_HIDE, getNode("t2_checkbox")),
new invokerChecker(EVENT_HIDE, getNode("t2_button")),
new invokerChecker(EVENT_SHOW, getNode("t2_button")),
new invokerChecker(EVENT_SHOW, getNode("t2_checkbox")),
new invokerChecker(EVENT_REORDER, getNode("container2"))
];
this.invoke = function removeARIAOwns_invoke()
{
// children are swapped
var tree =
{ SECTION: [
{ CHECKBUTTON: [
{ SECTION: [] }
] },
{ PUSHBUTTON: [ ] }
] };
testAccessibleTree("container2", tree);
getNode("container2").removeAttribute("aria-owns");
}
this.finalCheck = function removeARIAOwns_finalCheck()
{
// children follow the DOM order
var tree =
{ SECTION: [
{ PUSHBUTTON: [ ] },
{ CHECKBUTTON: [
{ SECTION: [] }
] }
] };
testAccessibleTree("container2", tree);
}
this.getID = function removeARIAOwns_getID()
{
return "Remove @aria-owns attribute";
}
}
function setARIAOwns()
{
this.eventSeq = [
new invokerChecker(EVENT_HIDE, getNode("t2_button")),
new invokerChecker(EVENT_SHOW, getNode("t2_button")),
new invokerChecker(EVENT_HIDE, getNode("t2_subdiv")),
new invokerChecker(EVENT_SHOW, getNode("t2_subdiv")),
new invokerChecker(EVENT_REORDER, getNode("container2"))
];
this.invoke = function setARIAOwns_invoke()
{
getNode("container2").setAttribute("aria-owns", "t2_button t2_subdiv");
}
this.finalCheck = function setARIAOwns_finalCheck()
{
// children are swapped again, button and subdiv are appended to
// the children.
var tree =
{ SECTION: [
{ CHECKBUTTON: [ ] }, // div
{ PUSHBUTTON: [ ] }, // button
{ SECTION: [ ] } // subdiv
] };
testAccessibleTree("container2", tree);
}
this.getID = function setARIAOwns_getID()
{
return "Set @aria-owns attribute";
}
}
function appendEl()
{
this.eventSeq = [
new invokerChecker(EVENT_SHOW, getNode, "child3"),
new invokerChecker(EVENT_REORDER, getNode("container2"))
];
this.invoke = function appendEl_invoke()
{
var div = document.createElement("div");
div.setAttribute("id", "child3");
div.setAttribute("role", "radio")
getNode("container2").appendChild(div);
}
this.finalCheck = function appendEl_finalCheck()
{
// children are invalidated, they includes aria-owns swapped kids and
// newly inserted child.
var tree =
{ SECTION: [
{ CHECKBUTTON: [ ] },
{ RADIOBUTTON: [ ] },
{ PUSHBUTTON: [ ] }, // ARIA owned
{ SECTION: [ ] } // ARIA owned
] };
testAccessibleTree("container2", tree);
}
this.getID = function appendEl_getID()
{
return "Append child under @aria-owns element";
}
}
function removeEl()
{
this.eventSeq = [
new invokerChecker(EVENT_HIDE, getNode, "t2_checkbox"),
new invokerChecker(EVENT_SHOW, getNode, "t2_checkbox"),
new invokerChecker(EVENT_REORDER, getNode("container2"))
];
this.invoke = function removeEl_invoke()
{
// remove a container of t2_subdiv
getNode("t2_span").parentNode.removeChild(getNode("t2_span"));
}
this.finalCheck = function removeEl_finalCheck()
{
// subdiv should go away
var tree =
{ SECTION: [
{ CHECKBUTTON: [ ] },
{ RADIOBUTTON: [ ] },
{ PUSHBUTTON: [ ] } // ARIA owned
] };
testAccessibleTree("container2", tree);
}
this.getID = function removeEl_getID()
{
return "Remove a container of ARIA ownded element";
}
}
////////////////////////////////////////////////////////////////////////////
// Test
////////////////////////////////////////////////////////////////////////////
gA11yEventDumpToConsole = true;
enableLogging("tree"); // debug stuff
var gQueue = null;
function doTest()
{
// nested and recursive aria-owns
var tree =
{ SECTION: [ // container
{ SECTION: [ // child
{ SECTION: [ // mid div
{ SECTION: [] } // grandchild
] }
] }
] };
testAccessibleTree("container", tree);
// dynamic tests
gQueue = new eventQueue();
gQueue.push(new removeARIAOwns());
gQueue.push(new setARIAOwns());
gQueue.push(new appendEl());
gQueue.push(new removeEl());
gQueue.invoke(); // SimpleTest.finish() will be called in the end
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<div id="container" aria-owns="child" aria-label="container"></div>
<div id="child" aria-label="child">
<div aria-owns="grandchild" aria-label="midchild"></div>
</div>
<div id="grandchild" aria-owns="container" aria-label="grandchild"></div>
<div id="container2" aria-owns="t2_checkbox t2_button">
<div role="button" id="t2_button"></div>
<div role="checkbox" id="t2_checkbox">
<span id="t2_span">
<div id="t2_subdiv"></div>
</span>
</div>
</div>
</body>
</html>

View File

@ -15,6 +15,10 @@
# If UPLOAD_HOST and UPLOAD_USER are not set, this script will simply write out
# the properties file.
#
# If UPLOAD_HOST is "localhost", then files are simply copied to UPLOAD_PATH.
# In this case, UPLOAD_TO_TEMP and POST_UPLOAD_CMD are not supported, and no
# properties are written out.
#
# And will use the following optional environment variables if set:
# UPLOAD_SSH_KEY : path to a ssh private key to use
# UPLOAD_PORT : port to use for ssh
@ -32,6 +36,7 @@ import re
import json
import errno
import hashlib
import shutil
from optparse import OptionParser
from subprocess import check_call, check_output, STDOUT
import redo
@ -106,7 +111,7 @@ def DoSCPFile(file, remote_path, user, host, port=None, ssh_key=None):
raise Exception("Command %s returned non-zero exit code" % cmdline)
def GetRemotePath(path, local_file, base_path):
def GetBaseRelativePath(path, local_file, base_path):
"""Given a remote path to upload to, a full path to a local file, and an
optional full path that is a base path of the local file, construct the
full remote path to place the file in. If base_path is not None, include
@ -214,7 +219,7 @@ def UploadFiles(user, host, path, files, verbose=False, port=None, ssh_key=None,
if not os.path.isfile(file):
raise IOError("File not found: %s" % file)
# first ensure that path exists remotely
remote_path = GetRemotePath(path, file, base_path)
remote_path = GetBaseRelativePath(path, file, base_path)
DoSSHCommand("mkdir -p " + remote_path, user, host, port=port, ssh_key=ssh_key)
if verbose:
print "Uploading " + file
@ -236,6 +241,25 @@ def UploadFiles(user, host, path, files, verbose=False, port=None, ssh_key=None,
print "Upload complete"
return properties
def CopyFilesLocally(path, files, verbose=False, base_path=None, package=None):
"""Copy each file in the list of files to `path`. The `base_path` argument is treated
as it is by UploadFiles."""
if not path.endswith("/"):
path += "/"
if base_path is not None:
base_path = os.path.abspath(base_path)
for file in files:
file = os.path.abspath(file)
if not os.path.isfile(file):
raise IOError("File not found: %s" % file)
# first ensure that path exists remotely
target_path = GetBaseRelativePath(path, file, base_path)
if not os.path.exists(target_path):
os.makedirs(target_path)
if verbose:
print "Copying " + file + " to " + target_path
shutil.copy(file, target_path)
def WriteProperties(files, properties_file, url_properties, package):
properties = url_properties
for file in files:
@ -280,16 +304,30 @@ if __name__ == '__main__':
if not options.properties_file:
print "You must specify a --properties-file"
sys.exit(1)
if host == "localhost":
if upload_to_temp_dir:
print "Cannot use UPLOAD_TO_TEMP with UPLOAD_HOST=localhost"
sys.exit(1)
if post_upload_command:
# POST_UPLOAD_COMMAND is difficult to extract from the mozharness
# scripts, so just ignore it until it's no longer used anywhere
print "Ignoring POST_UPLOAD_COMMAND with UPLOAD_HOST=localhost"
try:
url_properties = UploadFiles(user, host, path, args, base_path=options.base_path,
port=port, ssh_key=key, upload_to_temp_dir=upload_to_temp_dir,
post_upload_command=post_upload_command,
package=options.package,
verbose=True)
WriteProperties(args, options.properties_file, url_properties, options.package)
if host == "localhost":
CopyFilesLocally(path, args, base_path=options.base_path,
package=options.package,
verbose=True)
else:
url_properties = UploadFiles(user, host, path, args,
base_path=options.base_path, port=port, ssh_key=key,
upload_to_temp_dir=upload_to_temp_dir,
post_upload_command=post_upload_command,
package=options.package, verbose=True)
WriteProperties(args, options.properties_file, url_properties, options.package)
except IOError, (strerror):
print strerror
sys.exit(1)
except Exception, (err):
print err
sys.exit(2)

View File

@ -200,6 +200,7 @@ public:
#endif
nsIContent* GetNextChild();
nsIContent* Parent() const { return mOriginalContent; }
private:
enum IteratorPhase

View File

@ -136,7 +136,7 @@ static_assert(LOW_DATA_THRESHOLD_USECS > AMPLE_AUDIO_USECS,
} // namespace detail
// Amount of excess usecs of data to add in to the "should we buffer" calculation.
static const uint32_t EXHAUSTED_DATA_MARGIN_USECS = 60000;
static const uint32_t EXHAUSTED_DATA_MARGIN_USECS = 100000;
// If we enter buffering within QUICK_BUFFER_THRESHOLD_USECS seconds of starting
// decoding, we'll enter "quick buffering" mode, which exits a lot sooner than
@ -699,6 +699,7 @@ MediaDecoderStateMachine::OnAudioPopped(const nsRefPtr<MediaData>& aSample)
mPlaybackOffset = std::max(mPlaybackOffset.Ref(), aSample->mOffset);
UpdateNextFrameStatus();
DispatchAudioDecodeTaskIfNeeded();
MaybeStartBuffering();
}
void
@ -709,6 +710,7 @@ MediaDecoderStateMachine::OnVideoPopped(const nsRefPtr<MediaData>& aSample)
mPlaybackOffset = std::max(mPlaybackOffset.Ref(), aSample->mOffset);
UpdateNextFrameStatus();
DispatchVideoDecodeTaskIfNeeded();
MaybeStartBuffering();
}
void
@ -1097,6 +1099,34 @@ void MediaDecoderStateMachine::MaybeStartPlayback()
DispatchDecodeTasksIfNeeded();
}
void
MediaDecoderStateMachine::MaybeStartBuffering()
{
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
if (mState == DECODER_STATE_DECODING &&
mPlayState == MediaDecoder::PLAY_STATE_PLAYING &&
mResource->IsExpectingMoreData()) {
bool shouldBuffer;
if (mReader->UseBufferingHeuristics()) {
shouldBuffer = HasLowDecodedData(EXHAUSTED_DATA_MARGIN_USECS) &&
(JustExitedQuickBuffering() || HasLowUndecodedData());
} else {
MOZ_ASSERT(mReader->IsWaitForDataSupported());
shouldBuffer = (OutOfDecodedAudio() && mAudioWaitRequest.Exists()) ||
(OutOfDecodedVideo() && mVideoWaitRequest.Exists());
}
if (shouldBuffer) {
StartBuffering();
// Don't go straight back to the state machine loop since that might
// cause us to start decoding again and we could flip-flop between
// decoding and quick-buffering.
ScheduleStateMachineIn(USECS_PER_S);
}
}
}
void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
{
MOZ_ASSERT(OnTaskQueue());
@ -2612,30 +2642,6 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
RenderVideoFrames(sVideoQueueSendToCompositorSize, clockTime, nowTime);
// Check to see if we don't have enough data to play up to the next frame.
// If we don't, switch to buffering mode.
if (mState == DECODER_STATE_DECODING &&
mPlayState == MediaDecoder::PLAY_STATE_PLAYING &&
mResource->IsExpectingMoreData()) {
bool shouldBuffer;
if (mReader->UseBufferingHeuristics()) {
shouldBuffer = HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_USECS) &&
(JustExitedQuickBuffering() || HasLowUndecodedData());
} else {
MOZ_ASSERT(mReader->IsWaitForDataSupported());
shouldBuffer = (OutOfDecodedAudio() && mAudioWaitRequest.Exists()) ||
(OutOfDecodedVideo() && mVideoWaitRequest.Exists());
}
if (shouldBuffer) {
StartBuffering();
// Don't go straight back to the state machine loop since that might
// cause us to start decoding again and we could flip-flop between
// decoding and quick-buffering.
ScheduleStateMachineIn(USECS_PER_S);
return;
}
}
// Cap the current time to the larger of the audio and video end time.
// This ensures that if we're running off the system clock, we don't
// advance the clock to after the media end time.

View File

@ -522,6 +522,10 @@ protected:
// Must be called with the decode monitor held.
void MaybeStartPlayback();
// Check to see if we don't have enough data to play up to the next frame.
// If we don't, switch to buffering mode.
void MaybeStartBuffering();
// Moves the decoder into decoding state. Called on the state machine
// thread. The decoder monitor must be held.
void StartDecoding();

View File

@ -27,10 +27,10 @@ skip-if = buildapp == 'b2g' # Bug 1137683
skip-if = buildapp == 'b2g' # Bug 1137683
[test_fetch_basic_http.html]
[test_fetch_basic_http_sw_reroute.html]
skip-if = e10s || buildapp == 'b2g' || buildapp == 'mulet' # Bug 1093357 for e10s, bug 1137683 for b2g, bug 1174872 for mulet
skip-if = buildapp == 'b2g' || buildapp == 'mulet' # Bug 1137683 for b2g, bug 1174872 for mulet
[test_fetch_cors.html]
[test_fetch_cors_sw_reroute.html]
skip-if = e10s || buildapp == 'b2g' # Bug 1093357 for e10s, bug 1137683 for b2g
skip-if = buildapp == 'b2g' # Bug 1137683 for b2g
[test_formdataparsing.html]
[test_formdataparsing_sw_reroute.html]
skip-if = buildapp == 'b2g' # Bug 1137683

View File

@ -3867,6 +3867,82 @@ private:
}
};
namespace {
class ContinueDispatchFetchEventRunnable : public nsRunnable
{
WorkerPrivate* mWorkerPrivate;
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
nsAutoPtr<ServiceWorkerClientInfo> mClientInfo;
bool mIsReload;
public:
ContinueDispatchFetchEventRunnable(WorkerPrivate* aWorkerPrivate,
nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
bool aIsReload)
: mWorkerPrivate(aWorkerPrivate)
, mChannel(aChannel)
, mServiceWorker(aServiceWorker)
, mClientInfo(aClientInfo)
, mIsReload(aIsReload)
{
}
void
HandleError()
{
MOZ_ASSERT(NS_IsMainThread());
NS_WARNING("Unexpected error while dispatching fetch event!");
DebugOnly<nsresult> rv = mChannel->ResetInterception();
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to resume intercepted network request");
}
NS_IMETHOD
Run() override
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIChannel> channel;
nsresult rv = mChannel->GetChannel(getter_AddRefs(channel));
if (NS_WARN_IF(NS_FAILED(rv))) {
HandleError();
return NS_OK;
}
// The channel might have encountered an unexpected error while ensuring
// the upload stream is cloneable. Check here and reset the interception
// if that happens.
nsresult status;
rv = channel->GetStatus(&status);
if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(status))) {
HandleError();
return NS_OK;
}
nsRefPtr<FetchEventRunnable> event =
new FetchEventRunnable(mWorkerPrivate, mChannel, mServiceWorker,
mClientInfo, mIsReload);
rv = event->Init();
if (NS_WARN_IF(NS_FAILED(rv))) {
HandleError();
return NS_OK;
}
AutoJSAPI jsapi;
jsapi.Init();
if (NS_WARN_IF(!event->Dispatch(jsapi.cx()))) {
HandleError();
return NS_OK;
}
return NS_OK;
}
};
} // anonymous namespace
NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)
void
@ -3940,21 +4016,28 @@ ServiceWorkerManager::DispatchFetchEvent(const OriginAttributes& aOriginAttribut
nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
new nsMainThreadPtrHolder<ServiceWorker>(sw));
// clientInfo is null if we don't have a controlled document
nsRefPtr<FetchEventRunnable> event =
new FetchEventRunnable(sw->GetWorkerPrivate(), handle, serviceWorkerHandle,
clientInfo, aIsReload);
aRv = event->Init();
nsCOMPtr<nsIRunnable> continueRunnable =
new ContinueDispatchFetchEventRunnable(sw->GetWorkerPrivate(), handle,
serviceWorkerHandle, clientInfo,
aIsReload);
nsCOMPtr<nsIChannel> innerChannel;
aRv = aChannel->GetChannel(getter_AddRefs(innerChannel));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
AutoJSAPI api;
api.Init();
if (NS_WARN_IF(!event->Dispatch(api.cx()))) {
aRv.Throw(NS_ERROR_FAILURE);
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(innerChannel);
// If there is no upload stream, then continue immediately
if (!uploadChannel) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(continueRunnable->Run()));
return;
}
// Otherwise, ensure the upload stream can be cloned directly. This may
// require some async copying, so provide a callback.
aRv = uploadChannel->EnsureUploadStreamIsCloneable(continueRunnable);
}
bool

View File

@ -160,6 +160,8 @@ support-files =
fetch/plugin/worker.js
fetch/plugin/plugins.html
eventsource/*
sw_clients/file_blob_upload_frame.html
redirect_post.sjs
[test_app_protocol.html]
skip-if = release_build
@ -251,3 +253,4 @@ skip-if = toolkit == "android" || toolkit == "gonk"
[test_escapedSlashes.html]
[test_eventsource_intercept.html]
[test_not_intercept_plugin.html]
[test_file_blob_upload.html]

View File

@ -0,0 +1,35 @@
const CC = Components.Constructor;
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream",
"setInputStream");
function handleRequest(request, response)
{
var query = {};
request.queryString.split('&').forEach(function (val) {
var [name, value] = val.split('=');
query[name] = unescape(value);
});
var bodyStream = new BinaryInputStream(request.bodyInputStream);
var bodyBytes = [];
while ((bodyAvail = bodyStream.available()) > 0)
Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail));
var body = decodeURIComponent(
escape(String.fromCharCode.apply(null, bodyBytes)));
var currentHop = query.hop ? parseInt(query.hop) : 0;
var obj = JSON.parse(body);
if (currentHop < obj.hops) {
var newURL = '/tests/dom/workers/test/serviceworkers/redirect_post.sjs?hop=' +
(1 + currentHop);
response.setStatusLine(null, 307, 'redirect');
response.setHeader('Location', newURL);
return;
}
response.setHeader('Content-Type', 'application/json');
response.write(body);
}

View File

@ -0,0 +1,77 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>test file blob upload with SW interception</title>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
if (!parent) {
dump("sw_clients/file_blob_upload_frame.html shouldn't be launched directly!");
}
function makeFileBlob(obj) {
return new Promise(function(resolve, reject) {
var request = indexedDB.open(window.location.pathname, 1);
request.onerror = reject;
request.onupgradeneeded = function(evt) {
var db = evt.target.result;
db.onerror = reject;
var objectStore = db.createObjectStore('test', { autoIncrement: true });
var index = objectStore.createIndex('test', 'index');
};
request.onsuccess = function(evt) {
var db = evt.target.result;
db.onerror = reject;
var blob = new Blob([JSON.stringify(obj)],
{ type: 'application/json' });
var data = { blob: blob, index: 5 };
objectStore = db.transaction('test', 'readwrite').objectStore('test');
objectStore.add(data).onsuccess = function(evt) {
var key = evt.target.result;
objectStore = db.transaction('test').objectStore('test');
objectStore.get(key).onsuccess = function(evt) {
resolve(evt.target.result.blob);
};
};
};
});
}
navigator.serviceWorker.ready.then(function() {
parent.postMessage({ status: 'READY' }, '*');
});
var URL = '/tests/dom/workers/test/serviceworkers/redirect_post.sjs';
addEventListener('message', function(evt) {
if (evt.data.type = 'TEST') {
makeFileBlob(evt.data.body).then(function(blob) {
return fetch(URL, { method: 'POST', body: blob });
}).then(function(response) {
return response.json();
}).then(function(result) {
parent.postMessage({ status: 'OK', result: result }, '*');
}).catch(function(e) {
parent.postMessage({ status: 'ERROR', result: e.toString() }, '*');
});
}
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,146 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1203680 - Test interception of file blob uploads</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
var registration;
var iframe;
function start() {
return navigator.serviceWorker.register("empty.js",
{ scope: "./sw_clients/" })
.then((swr) => registration = swr);
}
function unregister() {
if (iframe) {
iframe.remove();
iframe = null;
}
return registration.unregister().then(function(result) {
ok(result, "Unregister should return true.");
}, function(e) {
ok(false, "Unregistering the SW failed with " + e + "\n");
});
}
function withFrame() {
var content = document.getElementById("content");
ok(content, "Parent exists.");
iframe = document.createElement("iframe");
iframe.setAttribute('src', "sw_clients/file_blob_upload_frame.html");
content.appendChild(iframe);
return new Promise(function(resolve, reject) {
window.addEventListener('message', function readyCallback(evt) {
window.removeEventListener('message', readyCallback);
if (evt.data.status === 'READY') {
resolve();
} else {
reject(evt.data.result);
}
});
});
}
function postBlob(body) {
return new Promise(function(resolve, reject) {
window.addEventListener('message', function postBlobCallback(evt) {
window.removeEventListener('message', postBlobCallback);
if (evt.data.status === 'OK') {
is(JSON.stringify(body), JSON.stringify(evt.data.result),
'body echoed back correctly');
resolve();
} else {
reject(evt.data.result);
}
});
iframe.contentWindow.postMessage({ type: 'TEST', body: body }, '*');
});
}
function generateMessage(length) {
var lorem =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis egestas '
'vehicula tortor eget ultrices. Sed et luctus est. Nunc eu orci ligula. '
'In vel ornare eros, eget lacinia diam. Praesent vel metus mattis, '
'cursus nulla sit amet, rhoncus diam. Aliquam nulla tortor, aliquet et '
'viverra non, dignissim vel tellus. Praesent sed ex in dolor aliquet '
'aliquet. In at facilisis sem, et aliquet eros. Maecenas feugiat nisl '
'quis elit blandit posuere. Duis viverra odio sed eros consectetur, '
'viverra mattis ligula volutpat.';
var result = '';
while (result.length < length) {
var remaining = length - result.length;
if (remaining < lorem.length) {
result += lorem.slice(0, remaining);
} else {
result += lorem;
}
}
return result;
}
var smallBody = generateMessage(64);
var mediumBody = generateMessage(1024);
// TODO: Test large bodies over the default pipe size. Currently stalls
// due to bug 1134372.
//var largeBody = generateMessage(100 * 1024);
function runTest() {
start()
.then(withFrame)
.then(function() {
return postBlob({ hops: 0, message: smallBody });
})
.then(function() {
return postBlob({ hops: 1, message: smallBody });
})
.then(function() {
return postBlob({ hops: 10, message: smallBody });
})
.then(function() {
return postBlob({ hops: 0, message: mediumBody });
})
.then(function() {
return postBlob({ hops: 1, message: mediumBody });
})
.then(function() {
return postBlob({ hops: 10, message: mediumBody });
})
.then(unregister)
.catch(function(e) {
ok(false, "Some test failed with error " + e);
}).then(SimpleTest.finish);
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.interception.enabled", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, runTest);
</script>
</pre>
</body>
</html>

View File

@ -148,7 +148,7 @@ BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
bool
BasicCompositor::SupportsEffect(EffectTypes aEffect)
{
return static_cast<EffectTypes>(aEffect) != EffectTypes::YCBCR;
return aEffect != EffectTypes::YCBCR && aEffect != EffectTypes::COMPONENT_ALPHA;
}
static void

View File

@ -312,6 +312,10 @@ ImageHost::Composite(LayerComposite* aLayer,
return;
}
if (!GetCompositor()->SupportsEffect(effect->mType)) {
return;
}
if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
if (mImageContainer) {
aLayer->GetLayerManager()->

View File

@ -46,6 +46,13 @@ const FLOAT sBlendFactor[] = { 0, 0, 0, 0 };
struct DeviceAttachmentsD3D11
{
DeviceAttachmentsD3D11(ID3D11Device* device)
: mDevice(device),
mInitOkay(true)
{}
bool CreateShaders();
typedef EnumeratedArray<MaskType, MaskType::NumMaskTypes, RefPtr<ID3D11VertexShader>>
VertexShaderArray;
typedef EnumeratedArray<MaskType, MaskType::NumMaskTypes, RefPtr<ID3D11PixelShader>>
@ -57,6 +64,7 @@ struct DeviceAttachmentsD3D11
VertexShaderArray mVSQuadShader;
PixelShaderArray mSolidColorShader;
PixelShaderArray mRGBAShader;
PixelShaderArray mRGBAShaderPremul;
PixelShaderArray mRGBShader;
PixelShaderArray mYCbCrShader;
PixelShaderArray mComponentAlphaShader;
@ -67,6 +75,8 @@ struct DeviceAttachmentsD3D11
RefPtr<ID3D11SamplerState> mPointSamplerState;
RefPtr<ID3D11BlendState> mPremulBlendState;
RefPtr<ID3D11BlendState> mNonPremulBlendState;
RefPtr<ID3D11BlendState> mPremulBlendMultiplyState;
RefPtr<ID3D11BlendState> mPremulBlendScreenState;
RefPtr<ID3D11BlendState> mComponentBlendState;
RefPtr<ID3D11BlendState> mDisabledBlendState;
RefPtr<IDXGIResource> mSyncTexture;
@ -93,6 +103,34 @@ struct DeviceAttachmentsD3D11
RefPtr<ID3D11Buffer> mVRDistortionVertices[2]; // one for each eye
RefPtr<ID3D11Buffer> mVRDistortionIndices[2];
uint32_t mVRDistortionIndexCount[2];
private:
void InitVertexShader(const ShaderBytes& aShader, VertexShaderArray& aArray, MaskType aMaskType) {
InitVertexShader(aShader, byRef(aArray[aMaskType]));
}
void InitPixelShader(const ShaderBytes& aShader, PixelShaderArray& aArray, MaskType aMaskType) {
InitPixelShader(aShader, byRef(aArray[aMaskType]));
}
void InitVertexShader(const ShaderBytes& aShader, ID3D11VertexShader** aOut) {
if (!mInitOkay) {
return;
}
if (FAILED(mDevice->CreateVertexShader(aShader.mData, aShader.mLength, nullptr, aOut))) {
mInitOkay = false;
}
}
void InitPixelShader(const ShaderBytes& aShader, ID3D11PixelShader** aOut) {
if (!mInitOkay) {
return;
}
if (FAILED(mDevice->CreatePixelShader(aShader.mData, aShader.mLength, nullptr, aOut))) {
mInitOkay = false;
}
}
// Only used during initialization.
RefPtr<ID3D11Device> mDevice;
bool mInitOkay;
};
CompositorD3D11::CompositorD3D11(nsIWidget* aWidget)
@ -171,7 +209,7 @@ CompositorD3D11::Initialize()
if (FAILED(mDevice->GetPrivateData(sDeviceAttachmentsD3D11,
&size,
&mAttachments))) {
mAttachments = new DeviceAttachmentsD3D11;
mAttachments = new DeviceAttachmentsD3D11(mDevice);
mDevice->SetPrivateData(sDeviceAttachmentsD3D11,
sizeof(mAttachments),
&mAttachments);
@ -202,7 +240,7 @@ CompositorD3D11::Initialize()
return false;
}
if (!CreateShaders()) {
if (!mAttachments->CreateShaders()) {
return false;
}
@ -256,6 +294,30 @@ CompositorD3D11::Initialize()
return false;
}
D3D11_RENDER_TARGET_BLEND_DESC rtBlendMultiplyPremul = {
TRUE,
D3D11_BLEND_DEST_COLOR, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
D3D11_COLOR_WRITE_ENABLE_ALL
};
blendDesc.RenderTarget[0] = rtBlendMultiplyPremul;
hr = mDevice->CreateBlendState(&blendDesc, byRef(mAttachments->mPremulBlendMultiplyState));
if (FAILED(hr)) {
return false;
}
D3D11_RENDER_TARGET_BLEND_DESC rtBlendScreenPremul = {
TRUE,
D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_COLOR, D3D11_BLEND_OP_ADD,
D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
D3D11_COLOR_WRITE_ENABLE_ALL
};
blendDesc.RenderTarget[0] = rtBlendScreenPremul;
hr = mDevice->CreateBlendState(&blendDesc, byRef(mAttachments->mPremulBlendScreenState));
if (FAILED(hr)) {
return false;
}
D3D11_RENDER_TARGET_BLEND_DESC rtBlendNonPremul = {
TRUE,
D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
@ -420,6 +482,8 @@ CompositorD3D11::GetTextureFactoryIdentifier()
MOZ_CRASH();
}
}
ident.mSupportedBlendModes += gfx::CompositionOp::OP_SCREEN;
ident.mSupportedBlendModes += gfx::CompositionOp::OP_MULTIPLY;
return ident;
}
@ -561,14 +625,6 @@ CompositorD3D11::SetPSForEffect(Effect* aEffect, MaskType aMaskType, gfx::Surfac
case EffectTypes::SOLID_COLOR:
mContext->PSSetShader(mAttachments->mSolidColorShader[aMaskType], nullptr, 0);
return;
case EffectTypes::RENDER_TARGET:
mContext->PSSetShader(mAttachments->mRGBAShader[aMaskType], nullptr, 0);
return;
case EffectTypes::RGB:
mContext->PSSetShader((aFormat == SurfaceFormat::B8G8R8A8 || aFormat == SurfaceFormat::R8G8B8A8)
? mAttachments->mRGBAShader[aMaskType]
: mAttachments->mRGBShader[aMaskType], nullptr, 0);
return;
case EffectTypes::YCBCR:
mContext->PSSetShader(mAttachments->mYCbCrShader[aMaskType], nullptr, 0);
return;
@ -576,6 +632,7 @@ CompositorD3D11::SetPSForEffect(Effect* aEffect, MaskType aMaskType, gfx::Surfac
mContext->PSSetShader(mAttachments->mComponentAlphaShader[aMaskType], nullptr, 0);
return;
default:
// Note: the RGB and RENDER_TARGET cases are handled in-line in DrawQuad().
NS_WARNING("No shader to load");
return;
}
@ -842,6 +899,11 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
const Rect* pTexCoordRect = nullptr;
gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
if (Effect* effect = aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()) {
blendMode = static_cast<EffectBlendMode*>(effect)->mBlendMode;
}
switch (aEffectChain.mPrimaryEffect->mType) {
case EffectTypes::SOLID_COLOR: {
SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, SurfaceFormat::UNKNOWN);
@ -852,6 +914,8 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
mPSConstants.layerColor[1] = color.g * color.a * aOpacity;
mPSConstants.layerColor[2] = color.b * color.a * aOpacity;
mPSConstants.layerColor[3] = color.a * aOpacity;
restoreBlendMode = SetBlendMode(blendMode);
}
break;
case EffectTypes::RGB:
@ -869,15 +933,27 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
return;
}
SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, texturedEffect->mTexture->GetFormat());
SurfaceFormat format = texturedEffect->mTexture->GetFormat();
bool useRGBAShader = (format == SurfaceFormat::B8G8R8A8 || format == SurfaceFormat::R8G8B8A8) ||
(texturedEffect->mType == EffectTypes::RENDER_TARGET);
bool premultiplied = texturedEffect->mPremultiplied;
if (!premultiplied && useRGBAShader &&
(blendMode == CompositionOp::OP_MULTIPLY ||
blendMode == CompositionOp::OP_SCREEN))
{
mContext->PSSetShader(mAttachments->mRGBAShaderPremul[maskType], nullptr, 0);
premultiplied = true;
} else if (useRGBAShader) {
mContext->PSSetShader(mAttachments->mRGBAShader[maskType], nullptr, 0);
} else {
mContext->PSSetShader(mAttachments->mRGBShader[maskType], nullptr, 0);
}
ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
mContext->PSSetShaderResources(0, 1, &srView);
if (!texturedEffect->mPremultiplied) {
mContext->OMSetBlendState(mAttachments->mNonPremulBlendState, sBlendFactor, 0xFFFFFFFF);
restoreBlendMode = true;
}
restoreBlendMode = SetBlendMode(blendMode, premultiplied);
SetSamplerForFilter(texturedEffect->mFilter);
}
@ -914,6 +990,8 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
sourceCb->GetShaderResourceView(),
sourceCr->GetShaderResourceView() };
mContext->PSSetShaderResources(0, 3, srViews);
restoreBlendMode = SetBlendMode(blendMode);
}
break;
case EffectTypes::COMPONENT_ALPHA:
@ -941,6 +1019,7 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
sourceOnWhite->GetShaderResourceView() };
mContext->PSSetShaderResources(0, 2, srViews);
// Note: component alpha is never used in blend containers.
mContext->OMSetBlendState(mAttachments->mComponentBlendState, sBlendFactor, 0xFFFFFFFF);
restoreBlendMode = true;
}
@ -982,6 +1061,39 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
}
}
bool
CompositorD3D11::SetBlendMode(gfx::CompositionOp aOp, bool aPremultiplied)
{
if (aOp == gfx::CompositionOp::OP_OVER && aPremultiplied) {
return false;
}
ID3D11BlendState* blendState = nullptr;
switch (aOp) {
case gfx::CompositionOp::OP_OVER:
MOZ_ASSERT(!aPremultiplied);
blendState = mAttachments->mNonPremulBlendState;
break;
case gfx::CompositionOp::OP_MULTIPLY:
// Premultiplication is handled in the shader.
MOZ_ASSERT(aPremultiplied);
blendState = mAttachments->mPremulBlendMultiplyState;
break;
case gfx::CompositionOp::OP_SCREEN:
// Premultiplication is handled in the shader.
MOZ_ASSERT(aPremultiplied);
blendState = mAttachments->mPremulBlendScreenState;
break;
default:
MOZ_ASSERT_UNREACHABLE("Unsupported blend mode!");
return false;
}
mContext->OMSetBlendState(blendState, sBlendFactor, 0xFFFFFFFF);
return true;
}
void
CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
const Rect* aClipRectIn,
@ -1236,86 +1348,37 @@ CompositorD3D11::UpdateRenderTarget()
}
bool
CompositorD3D11::CreateShaders()
DeviceAttachmentsD3D11::CreateShaders()
{
HRESULT hr;
InitVertexShader(sLayerQuadVS, mVSQuadShader, MaskType::MaskNone);
InitVertexShader(sLayerQuadMaskVS, mVSQuadShader, MaskType::Mask2d);
InitVertexShader(sLayerQuadMask3DVS, mVSQuadShader, MaskType::Mask3d);
hr = mDevice->CreateVertexShader(LayerQuadVS,
sizeof(LayerQuadVS),
nullptr,
byRef(mAttachments->mVSQuadShader[MaskType::MaskNone]));
if (FAILED(hr)) {
return false;
}
hr = mDevice->CreateVertexShader(LayerQuadMaskVS,
sizeof(LayerQuadMaskVS),
nullptr,
byRef(mAttachments->mVSQuadShader[MaskType::Mask2d]));
if (FAILED(hr)) {
return false;
}
hr = mDevice->CreateVertexShader(LayerQuadMask3DVS,
sizeof(LayerQuadMask3DVS),
nullptr,
byRef(mAttachments->mVSQuadShader[MaskType::Mask3d]));
if (FAILED(hr)) {
return false;
}
#define LOAD_PIXEL_SHADER(x) hr = mDevice->CreatePixelShader(x, sizeof(x), nullptr, byRef(mAttachments->m##x[MaskType::MaskNone])); \
if (FAILED(hr)) { \
return false; \
} \
hr = mDevice->CreatePixelShader(x##Mask, sizeof(x##Mask), nullptr, byRef(mAttachments->m##x[MaskType::Mask2d])); \
if (FAILED(hr)) { \
return false; \
}
LOAD_PIXEL_SHADER(SolidColorShader);
LOAD_PIXEL_SHADER(RGBShader);
LOAD_PIXEL_SHADER(RGBAShader);
LOAD_PIXEL_SHADER(YCbCrShader);
InitPixelShader(sSolidColorShader, mSolidColorShader, MaskType::MaskNone);
InitPixelShader(sSolidColorShaderMask, mSolidColorShader, MaskType::Mask2d);
InitPixelShader(sRGBShader, mRGBShader, MaskType::MaskNone);
InitPixelShader(sRGBShaderMask, mRGBShader, MaskType::Mask2d);
InitPixelShader(sRGBAShader, mRGBAShader, MaskType::MaskNone);
InitPixelShader(sRGBAShaderMask, mRGBAShader, MaskType::Mask2d);
InitPixelShader(sRGBAShaderMask3D, mRGBAShader, MaskType::Mask3d);
InitPixelShader(sRGBAShaderPremul, mRGBAShaderPremul, MaskType::MaskNone);
InitPixelShader(sRGBAShaderMaskPremul, mRGBAShaderPremul, MaskType::Mask2d);
InitPixelShader(sRGBAShaderMask3DPremul, mRGBAShaderPremul, MaskType::Mask3d);
InitPixelShader(sYCbCrShader, mYCbCrShader, MaskType::MaskNone);
InitPixelShader(sYCbCrShaderMask, mYCbCrShader, MaskType::Mask2d);
if (gfxPrefs::ComponentAlphaEnabled()) {
LOAD_PIXEL_SHADER(ComponentAlphaShader);
InitPixelShader(sComponentAlphaShader, mComponentAlphaShader, MaskType::MaskNone);
InitPixelShader(sComponentAlphaShaderMask, mComponentAlphaShader, MaskType::Mask2d);
}
#undef LOAD_PIXEL_SHADER
hr = mDevice->CreatePixelShader(RGBAShaderMask3D,
sizeof(RGBAShaderMask3D),
nullptr,
byRef(mAttachments->mRGBAShader[MaskType::Mask3d]));
if (FAILED(hr)) {
return false;
}
/* VR stuff */
hr = mDevice->CreateVertexShader(Oculus050VRDistortionVS,
sizeof(Oculus050VRDistortionVS),
nullptr,
byRef(mAttachments->mVRDistortionVS[VRHMDType::Oculus050]));
if (FAILED(hr)) {
return false;
}
hr = mDevice->CreatePixelShader(Oculus050VRDistortionPS,
sizeof(Oculus050VRDistortionPS),
nullptr,
byRef(mAttachments->mVRDistortionPS[VRHMDType::Oculus050]));
if (FAILED(hr)) {
return false;
}
InitVertexShader(sOculus050VRDistortionVS, byRef(mVRDistortionVS[VRHMDType::Oculus050]));
InitPixelShader(sOculus050VRDistortionPS, byRef(mVRDistortionPS[VRHMDType::Oculus050]));
// These are shared
// XXX rename Oculus050 shaders to something more generic
mAttachments->mVRDistortionVS[VRHMDType::Cardboard] = mAttachments->mVRDistortionVS[VRHMDType::Oculus050];
mAttachments->mVRDistortionPS[VRHMDType::Cardboard] = mAttachments->mVRDistortionPS[VRHMDType::Oculus050];
return true;
mVRDistortionVS[VRHMDType::Cardboard] = mVRDistortionVS[VRHMDType::Oculus050];
mVRDistortionPS[VRHMDType::Cardboard] = mVRDistortionPS[VRHMDType::Oculus050];
return mInitOkay;
}
bool

View File

@ -164,11 +164,11 @@ private:
void EnsureSize();
bool VerifyBufferSize();
void UpdateRenderTarget();
bool CreateShaders();
bool UpdateConstantBuffers();
void SetSamplerForFilter(gfx::Filter aFilter);
void SetPSForEffect(Effect *aEffect, MaskType aMaskType, gfx::SurfaceFormat aFormat);
void PaintToTarget();
bool SetBlendMode(gfx::CompositionOp aOp, bool aPremultipled = true);
virtual gfx::IntSize GetWidgetSize() const override { return mSize; }

View File

@ -163,6 +163,13 @@ float4 RGBAShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity * mask;
}
float4 RGBAShaderMaskPremul(const VS_MASK_OUTPUT aVertex) : SV_Target
{
float4 result = RGBAShaderMask(aVertex);
result.rgb *= result.a;
return result;
}
float4 RGBAShaderMask3D(const VS_MASK_3D_OUTPUT aVertex) : SV_Target
{
float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
@ -170,6 +177,13 @@ float4 RGBAShaderMask3D(const VS_MASK_3D_OUTPUT aVertex) : SV_Target
return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity * mask;
}
float4 RGBAShaderMask3DPremul(const VS_MASK_3D_OUTPUT aVertex) : SV_Target
{
float4 result = RGBAShaderMask3D(aVertex);
result.rgb *= result.a;
return result;
}
float4 RGBShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
{
float4 result;
@ -248,6 +262,13 @@ float4 RGBAShader(const VS_OUTPUT aVertex) : SV_Target
return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
}
float4 RGBAShaderPremul(const VS_OUTPUT aVertex) : SV_Target
{
float4 result = RGBAShader(aVertex);
result.rgb *= result.a;
return result;
}
float4 RGBShader(const VS_OUTPUT aVertex) : SV_Target
{
float4 result;

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,6 @@
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
//
//
// fxc -nologo -Tvs_4_0_level_9_3 CompositorD3D11VR.hlsl
// -EOculusVRDistortionVS -VnOculusVRDistortionVS -FhtmpShaderHeader
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@ -28,24 +24,24 @@
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------ ------
// POSITION 0 xy 0 NONE float xy
// TEXCOORD 0 xy 1 NONE float xy
// TEXCOORD 1 xy 2 NONE float xy
// TEXCOORD 2 xy 3 NONE float xy
// COLOR 0 xyzw 4 NONE float xyzw
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// POSITION 0 xy 0 NONE float xy
// TEXCOORD 0 xy 1 NONE float xy
// TEXCOORD 1 xy 2 NONE float xy
// TEXCOORD 2 xy 3 NONE float xy
// COLOR 0 xyzw 4 NONE float xyzw
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------ ------
// SV_Position 0 xyzw 0 POS float xyzw
// TEXCOORD 0 xyz 1 NONE float xyz
// TEXCOORD 1 xyz 2 NONE float xyz
// TEXCOORD 2 xyz 3 NONE float xyz
// COLOR 0 xyzw 4 NONE float xyzw
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_Position 0 xyzw 0 POS float xyzw
// TEXCOORD 0 xyz 1 NONE float xyz
// TEXCOORD 1 xyz 2 NONE float xyz
// TEXCOORD 2 xyz 3 NONE float xyz
// COLOR 0 xyzw 4 NONE float xyzw
//
//
// Constant buffer to DX9 shader constant mappings:
@ -110,10 +106,10 @@ ret
const BYTE Oculus050VRDistortionVS[] =
{
68, 88, 66, 67, 206, 154,
203, 64, 121, 47, 121, 169,
222, 206, 108, 175, 167, 227,
154, 37, 1, 0, 0, 0,
68, 88, 66, 67, 3, 61,
196, 122, 10, 53, 44, 234,
18, 242, 195, 238, 42, 90,
72, 193, 1, 0, 0, 0,
244, 5, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
108, 1, 0, 0, 44, 3,
@ -249,7 +245,7 @@ const BYTE Oculus050VRDistortionVS[] =
116, 0, 0, 0, 10, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 10, 0,
0, 0, 0, 0, 0, 0,
0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -307,9 +303,9 @@ const BYTE Oculus050VRDistortionVS[] =
83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
32, 57, 46, 50, 57, 46,
57, 53, 50, 46, 51, 49,
49, 49, 0, 171, 171, 171,
32, 54, 46, 51, 46, 57,
54, 48, 48, 46, 49, 54,
51, 56, 52, 0, 171, 171,
73, 83, 71, 78, 152, 0,
0, 0, 5, 0, 0, 0,
8, 0, 0, 0, 128, 0,
@ -365,13 +361,10 @@ const BYTE Oculus050VRDistortionVS[] =
79, 79, 82, 68, 0, 67,
79, 76, 79, 82, 0, 171
};
ShaderBytes sOculus050VRDistortionVS = { Oculus050VRDistortionVS, sizeof(Oculus050VRDistortionVS) };
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
//
//
// fxc -nologo -Tps_4_0_level_9_3 CompositorD3D11VR.hlsl
// -EOculusVRDistortionPS -VnOculusVRDistortionPS -FhtmpShaderHeader
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Resource Bindings:
@ -385,20 +378,20 @@ const BYTE Oculus050VRDistortionVS[] =
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------ ------
// SV_Position 0 xyzw 0 POS float
// TEXCOORD 0 xyz 1 NONE float xy
// TEXCOORD 1 xyz 2 NONE float xy
// TEXCOORD 2 xyz 3 NONE float xy
// COLOR 0 xyzw 4 NONE float x
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_Position 0 xyzw 0 POS float
// TEXCOORD 0 xyz 1 NONE float xy
// TEXCOORD 1 xyz 2 NONE float xy
// TEXCOORD 2 xyz 3 NONE float xy
// COLOR 0 xyzw 4 NONE float x
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------ ------
// SV_Target 0 xyzw 0 TARGET float xyzw
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_Target 0 xyzw 0 TARGET float xyzw
//
//
// Sampler/Resource to DX9 shader sampler mappings:
@ -449,15 +442,15 @@ ret
const BYTE Oculus050VRDistortionPS[] =
{
68, 88, 66, 67, 48, 161,
127, 216, 149, 107, 53, 57,
164, 84, 84, 154, 58, 227,
125, 61, 1, 0, 0, 0,
124, 4, 0, 0, 6, 0,
68, 88, 66, 67, 108, 219,
61, 216, 27, 0, 27, 222,
242, 132, 183, 21, 166, 141,
130, 39, 1, 0, 0, 0,
128, 4, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
60, 1, 0, 0, 132, 2,
0, 0, 0, 3, 0, 0,
164, 3, 0, 0, 72, 4,
168, 3, 0, 0, 76, 4,
0, 0, 65, 111, 110, 57,
252, 0, 0, 0, 252, 0,
0, 0, 0, 2, 255, 255,
@ -577,7 +570,7 @@ const BYTE Oculus050VRDistortionPS[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
82, 68, 69, 70, 156, 0,
82, 68, 69, 70, 160, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 28, 0, 0, 0,
@ -602,43 +595,44 @@ const BYTE Oculus050VRDistortionPS[] =
76, 32, 83, 104, 97, 100,
101, 114, 32, 67, 111, 109,
112, 105, 108, 101, 114, 32,
57, 46, 50, 57, 46, 57,
53, 50, 46, 51, 49, 49,
49, 0, 73, 83, 71, 78,
156, 0, 0, 0, 5, 0,
0, 0, 8, 0, 0, 0,
128, 0, 0, 0, 0, 0,
54, 46, 51, 46, 57, 54,
48, 48, 46, 49, 54, 51,
56, 52, 0, 171, 171, 171,
73, 83, 71, 78, 156, 0,
0, 0, 5, 0, 0, 0,
8, 0, 0, 0, 128, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
15, 0, 0, 0, 140, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
0, 0, 1, 0, 0, 0,
7, 3, 0, 0, 140, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 3, 0,
0, 0, 2, 0, 0, 0,
7, 3, 0, 0, 140, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 3, 0,
0, 0, 3, 0, 0, 0,
7, 3, 0, 0, 149, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
0, 0, 4, 0, 0, 0,
15, 1, 0, 0, 83, 86,
95, 80, 111, 115, 105, 116,
105, 111, 110, 0, 84, 69,
88, 67, 79, 79, 82, 68,
0, 67, 79, 76, 79, 82,
0, 171, 79, 83, 71, 78,
44, 0, 0, 0, 1, 0,
0, 0, 8, 0, 0, 0,
32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
0, 0, 15, 0, 0, 0,
140, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 1, 0,
0, 0, 7, 3, 0, 0,
140, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 2, 0,
0, 0, 7, 3, 0, 0,
140, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 3, 0,
0, 0, 7, 3, 0, 0,
149, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 4, 0,
0, 0, 15, 1, 0, 0,
83, 86, 95, 80, 111, 115,
105, 116, 105, 111, 110, 0,
84, 69, 88, 67, 79, 79,
82, 68, 0, 67, 79, 76,
79, 82, 0, 171, 79, 83,
71, 78, 44, 0, 0, 0,
1, 0, 0, 0, 8, 0,
0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 15, 0,
0, 0, 83, 86, 95, 84,
97, 114, 103, 101, 116, 0,
171, 171
83, 86, 95, 84, 97, 114,
103, 101, 116, 0, 171, 171
};
ShaderBytes sOculus050VRDistortionPS = { Oculus050VRDistortionPS, sizeof(Oculus050VRDistortionPS) };

View File

@ -14,11 +14,13 @@ fi
makeShaderVS() {
fxc -nologo $FXC_FLAGS -Tvs_4_0_level_9_3 $SRC -E$1 -Vn$1 -Fh$tempfile
echo "ShaderBytes s$1 = { $1, sizeof($1) };" >> $tempfile;
cat $tempfile >> $DEST
}
makeShaderPS() {
fxc -nologo $FXC_FLAGS -Tps_4_0_level_9_3 $SRC -E$1 -Vn$1 -Fh$tempfile
echo "ShaderBytes s$1 = { $1, sizeof($1) };" >> $tempfile;
cat $tempfile >> $DEST
}
@ -26,10 +28,12 @@ SRC=CompositorD3D11.hlsl
DEST=CompositorD3D11Shaders.h
rm -f $DEST
echo "struct ShaderBytes { const void* mData; size_t mLength; };" >> $DEST;
makeShaderVS LayerQuadVS
makeShaderPS SolidColorShader
makeShaderPS RGBShader
makeShaderPS RGBAShader
makeShaderPS RGBAShaderPremul
makeShaderPS ComponentAlphaShader
makeShaderPS YCbCrShader
makeShaderVS LayerQuadMaskVS
@ -37,7 +41,9 @@ makeShaderVS LayerQuadMask3DVS
makeShaderPS SolidColorShaderMask
makeShaderPS RGBShaderMask
makeShaderPS RGBAShaderMask
makeShaderPS RGBAShaderMaskPremul
makeShaderPS RGBAShaderMask3D
makeShaderPS RGBAShaderMask3DPremul
makeShaderPS YCbCrShaderMask
makeShaderPS ComponentAlphaShaderMask

View File

@ -6,6 +6,7 @@
#include "SoftwareVsyncSource.h"
#include "base/task.h"
#include "gfxPlatform.h"
#include "nsThreadUtils.h"
SoftwareVsyncSource::SoftwareVsyncSource()
@ -27,7 +28,7 @@ SoftwareDisplay::SoftwareDisplay()
{
// Mimic 60 fps
MOZ_ASSERT(NS_IsMainThread());
const double rate = 1000 / 60.0;
const double rate = 1000.0 / (double) gfxPlatform::GetSoftwareVsyncRate();
mVsyncRate = mozilla::TimeDuration::FromMilliseconds(rate);
mVsyncThread = new base::Thread("SoftwareVsyncThread");
MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "Could not start software vsync thread");

View File

@ -581,7 +581,11 @@ gfxPlatform::Init()
RegisterStrongMemoryReporter(new GfxMemoryImageReporter());
if (XRE_IsParentProcess()) {
gPlatform->mVsyncSource = gPlatform->CreateHardwareVsyncSource();
if (gfxPlatform::ForceSoftwareVsync()) {
gPlatform->mVsyncSource = (gPlatform)->gfxPlatform::CreateHardwareVsyncSource();
} else {
gPlatform->mVsyncSource = gPlatform->CreateHardwareVsyncSource();
}
}
}
@ -2328,6 +2332,13 @@ gfxPlatform::UsesOffMainThreadCompositing()
return result;
}
/***
* The preference "layout.frame_rate" has 3 meanings depending on the value:
*
* -1 = Auto (default), use hardware vsync or software vsync @ 60 hz if hw vsync fails.
* 0 = ASAP mode - used during talos testing.
* X = Software vsync at a rate of X times per second.
*/
already_AddRefed<mozilla::gfx::VsyncSource>
gfxPlatform::CreateHardwareVsyncSource()
{
@ -2347,6 +2358,29 @@ gfxPlatform::IsInLayoutAsapMode()
return Preferences::GetInt("layout.frame_rate", -1) == 0;
}
/* static */ bool
gfxPlatform::ForceSoftwareVsync()
{
return Preferences::GetInt("layout.frame_rate", -1) > 0;
}
/* static */ int
gfxPlatform::GetSoftwareVsyncRate()
{
int preferenceRate = Preferences::GetInt("layout.frame_rate",
gfxPlatform::GetDefaultFrameRate());
if (preferenceRate <= 0) {
return gfxPlatform::GetDefaultFrameRate();
}
return preferenceRate;
}
/* static */ int
gfxPlatform::GetDefaultFrameRate()
{
return 60;
}
static nsString
DetectBadApzWheelInputPrefs()
{

View File

@ -607,6 +607,21 @@ public:
*/
static bool IsInLayoutAsapMode();
/**
* Returns the software vsync rate to use.
*/
static int GetSoftwareVsyncRate();
/**
* Returns whether or not a custom vsync rate is set.
*/
static bool ForceSoftwareVsync();
/**
* Returns the default frame rate for the refresh driver / software vsync.
*/
static int GetDefaultFrameRate();
/**
* Used to test which input types are handled via APZ.
*/

View File

@ -8,5 +8,5 @@ function foo() {
bar();
}
}
for (var i=0; i<10000; i++)
for (var i=0; i < 50; i++)
foo();

View File

@ -2450,12 +2450,26 @@ CodeGeneratorARM::visitUDiv(LUDiv* ins)
masm.ma_udiv(lhs, rhs, output);
// Check for large unsigned result - represent as double.
if (!ins->mir()->isTruncated()) {
MOZ_ASSERT(ins->mir()->fallible());
masm.ma_cmp(output, Imm32(0));
bailoutIf(Assembler::LessThan, ins->snapshot());
}
masm.bind(&done);
// Check for non-zero remainder if not truncating to int.
if (!ins->mir()->canTruncateRemainder()) {
MOZ_ASSERT(ins->mir()->fallible());
{
ScratchRegisterScope scratch(masm);
masm.ma_mul(rhs, output, scratch);
masm.ma_cmp(scratch, lhs);
}
bailoutIf(Assembler::NotEqual, ins->snapshot());
}
if (done.used())
masm.bind(&done);
}
void
@ -2470,12 +2484,15 @@ CodeGeneratorARM::visitUMod(LUMod* ins)
masm.ma_umod(lhs, rhs, output);
// Check for large unsigned result - represent as double.
if (!ins->mir()->isTruncated()) {
MOZ_ASSERT(ins->mir()->fallible());
masm.ma_cmp(output, Imm32(0));
bailoutIf(Assembler::LessThan, ins->snapshot());
}
masm.bind(&done);
if (done.used())
masm.bind(&done);
}
template<class T>

View File

@ -0,0 +1,420 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 "jit/mips-shared/Lowering-mips-shared.h"
#include "mozilla/MathAlgorithms.h"
#include "jit/MIR.h"
#include "jit/shared/Lowering-shared-inl.h"
using namespace js;
using namespace js::jit;
using mozilla::FloorLog2;
LAllocation
LIRGeneratorMIPSShared::useByteOpRegister(MDefinition* mir)
{
return useRegister(mir);
}
LAllocation
LIRGeneratorMIPSShared::useByteOpRegisterOrNonDoubleConstant(MDefinition* mir)
{
return useRegisterOrNonDoubleConstant(mir);
}
LDefinition
LIRGeneratorMIPSShared::tempByteOpRegister()
{
return temp();
}
// x = !y
void
LIRGeneratorMIPSShared::lowerForALU(LInstructionHelper<1, 1, 0>* ins,
MDefinition* mir, MDefinition* input)
{
ins->setOperand(0, useRegister(input));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
// z = x+y
void
LIRGeneratorMIPSShared::lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs)
{
ins->setOperand(0, useRegister(lhs));
ins->setOperand(1, useRegisterOrConstant(rhs));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
void
LIRGeneratorMIPSShared::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* input)
{
ins->setOperand(0, useRegister(input));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
template<size_t Temps>
void
LIRGeneratorMIPSShared::lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs)
{
ins->setOperand(0, useRegister(lhs));
ins->setOperand(1, useRegister(rhs));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
template void LIRGeneratorMIPSShared::lowerForFPU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
template void LIRGeneratorMIPSShared::lowerForFPU(LInstructionHelper<1, 2, 1>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
void
LIRGeneratorMIPSShared::lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
MDefinition* lhs, MDefinition* rhs)
{
baab->setOperand(0, useRegisterAtStart(lhs));
baab->setOperand(1, useRegisterOrConstantAtStart(rhs));
add(baab, mir);
}
void
LIRGeneratorMIPSShared::lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs)
{
ins->setOperand(0, useRegister(lhs));
ins->setOperand(1, useRegisterOrConstant(rhs));
define(ins, mir);
}
void
LIRGeneratorMIPSShared::lowerDivI(MDiv* div)
{
if (div->isUnsigned()) {
lowerUDiv(div);
return;
}
// Division instructions are slow. Division by constant denominators can be
// rewritten to use other instructions.
if (div->rhs()->isConstant()) {
int32_t rhs = div->rhs()->toConstant()->value().toInt32();
// Check for division by a positive power of two, which is an easy and
// important case to optimize. Note that other optimizations are also
// possible; division by negative powers of two can be optimized in a
// similar manner as positive powers of two, and division by other
// constants can be optimized by a reciprocal multiplication technique.
int32_t shift = FloorLog2(rhs);
if (rhs > 0 && 1 << shift == rhs) {
LDivPowTwoI* lir = new(alloc()) LDivPowTwoI(useRegister(div->lhs()), shift, temp());
if (div->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, div);
return;
}
}
LDivI* lir = new(alloc()) LDivI(useRegister(div->lhs()), useRegister(div->rhs()), temp());
if (div->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, div);
}
void
LIRGeneratorMIPSShared::lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs)
{
LMulI* lir = new(alloc()) LMulI;
if (mul->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
lowerForALU(lir, mul, lhs, rhs);
}
void
LIRGeneratorMIPSShared::lowerModI(MMod* mod)
{
if (mod->isUnsigned()) {
lowerUMod(mod);
return;
}
if (mod->rhs()->isConstant()) {
int32_t rhs = mod->rhs()->toConstant()->value().toInt32();
int32_t shift = FloorLog2(rhs);
if (rhs > 0 && 1 << shift == rhs) {
LModPowTwoI* lir = new(alloc()) LModPowTwoI(useRegister(mod->lhs()), shift);
if (mod->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, mod);
return;
} else if (shift < 31 && (1 << (shift + 1)) - 1 == rhs) {
LModMaskI* lir = new(alloc()) LModMaskI(useRegister(mod->lhs()),
temp(LDefinition::GENERAL),
temp(LDefinition::GENERAL),
shift + 1);
if (mod->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, mod);
return;
}
}
LModI* lir = new(alloc()) LModI(useRegister(mod->lhs()), useRegister(mod->rhs()),
temp(LDefinition::GENERAL));
if (mod->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, mod);
}
void
LIRGeneratorMIPSShared::visitPowHalf(MPowHalf* ins)
{
MDefinition* input = ins->input();
MOZ_ASSERT(input->type() == MIRType_Double);
LPowHalfD* lir = new(alloc()) LPowHalfD(useRegisterAtStart(input));
defineReuseInput(lir, ins, 0);
}
LTableSwitch*
LIRGeneratorMIPSShared::newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
MTableSwitch* tableswitch)
{
return new(alloc()) LTableSwitch(in, inputCopy, temp(), tableswitch);
}
LTableSwitchV*
LIRGeneratorMIPSShared::newLTableSwitchV(MTableSwitch* tableswitch)
{
return new(alloc()) LTableSwitchV(temp(), tempDouble(), temp(), tableswitch);
}
void
LIRGeneratorMIPSShared::visitGuardShape(MGuardShape* ins)
{
MOZ_ASSERT(ins->obj()->type() == MIRType_Object);
LDefinition tempObj = temp(LDefinition::OBJECT);
LGuardShape* guard = new(alloc()) LGuardShape(useRegister(ins->obj()), tempObj);
assignSnapshot(guard, ins->bailoutKind());
add(guard, ins);
redefine(ins, ins->obj());
}
void
LIRGeneratorMIPSShared::visitGuardObjectGroup(MGuardObjectGroup* ins)
{
MOZ_ASSERT(ins->obj()->type() == MIRType_Object);
LDefinition tempObj = temp(LDefinition::OBJECT);
LGuardObjectGroup* guard = new(alloc()) LGuardObjectGroup(useRegister(ins->obj()), tempObj);
assignSnapshot(guard, ins->bailoutKind());
add(guard, ins);
redefine(ins, ins->obj());
}
void
LIRGeneratorMIPSShared::lowerUrshD(MUrsh* mir)
{
MDefinition* lhs = mir->lhs();
MDefinition* rhs = mir->rhs();
MOZ_ASSERT(lhs->type() == MIRType_Int32);
MOZ_ASSERT(rhs->type() == MIRType_Int32);
LUrshD* lir = new(alloc()) LUrshD(useRegister(lhs), useRegisterOrConstant(rhs), temp());
define(lir, mir);
}
void
LIRGeneratorMIPSShared::visitAsmJSNeg(MAsmJSNeg* ins)
{
if (ins->type() == MIRType_Int32) {
define(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins);
} else if (ins->type() == MIRType_Float32) {
define(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins);
} else {
MOZ_ASSERT(ins->type() == MIRType_Double);
define(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins);
}
}
void
LIRGeneratorMIPSShared::lowerUDiv(MDiv* div)
{
MDefinition* lhs = div->getOperand(0);
MDefinition* rhs = div->getOperand(1);
LUDivOrMod* lir = new(alloc()) LUDivOrMod;
lir->setOperand(0, useRegister(lhs));
lir->setOperand(1, useRegister(rhs));
if (div->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, div);
}
void
LIRGeneratorMIPSShared::lowerUMod(MMod* mod)
{
MDefinition* lhs = mod->getOperand(0);
MDefinition* rhs = mod->getOperand(1);
LUDivOrMod* lir = new(alloc()) LUDivOrMod;
lir->setOperand(0, useRegister(lhs));
lir->setOperand(1, useRegister(rhs));
if (mod->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, mod);
}
void
LIRGeneratorMIPSShared::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins)
{
MOZ_ASSERT(ins->input()->type() == MIRType_Int32);
LAsmJSUInt32ToDouble* lir = new(alloc()) LAsmJSUInt32ToDouble(useRegisterAtStart(ins->input()));
define(lir, ins);
}
void
LIRGeneratorMIPSShared::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins)
{
MOZ_ASSERT(ins->input()->type() == MIRType_Int32);
LAsmJSUInt32ToFloat32* lir = new(alloc()) LAsmJSUInt32ToFloat32(useRegisterAtStart(ins->input()));
define(lir, ins);
}
void
LIRGeneratorMIPSShared::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
LAllocation ptrAlloc;
// For MIPS it is best to keep the 'ptr' in a register if a bounds check
// is needed.
if (ptr->isConstantValue() && !ins->needsBoundsCheck()) {
// A bounds check is only skipped for a positive index.
MOZ_ASSERT(ptr->constantValue().toInt32() >= 0);
ptrAlloc = LAllocation(ptr->constantVp());
} else
ptrAlloc = useRegisterAtStart(ptr);
define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins);
}
void
LIRGeneratorMIPSShared::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
LAllocation ptrAlloc;
if (ptr->isConstantValue() && !ins->needsBoundsCheck()) {
MOZ_ASSERT(ptr->constantValue().toInt32() >= 0);
ptrAlloc = LAllocation(ptr->constantVp());
} else
ptrAlloc = useRegisterAtStart(ptr);
add(new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins);
}
void
LIRGeneratorMIPSShared::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins)
{
define(new(alloc()) LAsmJSLoadFuncPtr(useRegister(ins->index())), ins);
}
void
LIRGeneratorMIPSShared::visitSubstr(MSubstr* ins)
{
LSubstr* lir = new (alloc()) LSubstr(useRegister(ins->string()),
useRegister(ins->begin()),
useRegister(ins->length()),
temp(),
temp(),
tempByteOpRegister());
define(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGeneratorMIPSShared::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPSShared::visitSimdBinaryArith(MSimdBinaryArith* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPSShared::visitSimdSelect(MSimdSelect* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPSShared::visitSimdSplatX4(MSimdSplatX4* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPSShared::visitSimdValueX4(MSimdValueX4* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPSShared::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPSShared::visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPSShared::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPSShared::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPSShared::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPSShared::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPSShared::visitRandom(MRandom* ins)
{
LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
defineReturn(lir, ins);
}

View File

@ -0,0 +1,98 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 jit_mips_shared_Lowering_mips_shared_h
#define jit_mips_shared_Lowering_mips_shared_h
#include "jit/shared/Lowering-shared.h"
namespace js {
namespace jit {
class LIRGeneratorMIPSShared : public LIRGeneratorShared
{
protected:
LIRGeneratorMIPSShared(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph)
: LIRGeneratorShared(gen, graph, lirGraph)
{ }
protected:
// x86 has constraints on what registers can be formatted for 1-byte
// stores and loads; on MIPS all registers are okay.
LAllocation useByteOpRegister(MDefinition* mir);
LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir);
LDefinition tempByteOpRegister();
bool needTempForPostBarrier() { return false; }
void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
MDefinition* rhs);
void lowerUrshD(MUrsh* mir);
void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* input);
void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* src);
template<size_t Temps>
void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
MDefinition* lhs, MDefinition* rhs)
{
return lowerForFPU(ins, mir, lhs, rhs);
}
void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir,
MDefinition* lhs, MDefinition* rhs)
{
return lowerForFPU(ins, mir, lhs, rhs);
}
void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
MDefinition* lhs, MDefinition* rhs);
void lowerDivI(MDiv* div);
void lowerModI(MMod* mod);
void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
void lowerUDiv(MDiv* div);
void lowerUMod(MMod* mod);
void visitPowHalf(MPowHalf* ins);
void visitAsmJSNeg(MAsmJSNeg* ins);
LTableSwitch* newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
MTableSwitch* ins);
LTableSwitchV* newLTableSwitchV(MTableSwitch* ins);
public:
void lowerPhi(MPhi* phi);
void visitGuardShape(MGuardShape* ins);
void visitGuardObjectGroup(MGuardObjectGroup* ins);
void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins);
void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins);
void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins);
void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins);
void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
void visitSimdBinaryArith(MSimdBinaryArith* ins);
void visitSimdSelect(MSimdSelect* ins);
void visitSimdSplatX4(MSimdSplatX4* ins);
void visitSimdValueX4(MSimdValueX4* ins);
void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);
};
} // namespace jit
} // namespace js
#endif /* jit_mips_shared_Lowering_mips_shared_h */

View File

@ -4,11 +4,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/MathAlgorithms.h"
#include "jit/mips32/Lowering-mips32.h"
#include "jit/Lowering.h"
#include "jit/mips32/Assembler-mips32.h"
#include "jit/MIR.h"
#include "jit/shared/Lowering-shared-inl.h"
@ -16,8 +15,6 @@
using namespace js;
using namespace js::jit;
using mozilla::FloorLog2;
void
LIRGeneratorMIPS::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1,
Register reg2, bool useAtStart)
@ -30,24 +27,6 @@ LIRGeneratorMIPS::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Reg
lir->setOperand(n + 1, LUse(reg2, VirtualRegisterOfPayload(mir), useAtStart));
}
LAllocation
LIRGeneratorMIPS::useByteOpRegister(MDefinition* mir)
{
return useRegister(mir);
}
LAllocation
LIRGeneratorMIPS::useByteOpRegisterOrNonDoubleConstant(MDefinition* mir)
{
return useRegisterOrNonDoubleConstant(mir);
}
LDefinition
LIRGeneratorMIPS::tempByteOpRegister()
{
return temp();
}
void
LIRGeneratorMIPS::visitBox(MBox* box)
{
@ -147,57 +126,6 @@ LIRGeneratorMIPS::visitReturn(MReturn* ret)
add(ins);
}
// x = !y
void
LIRGeneratorMIPS::lowerForALU(LInstructionHelper<1, 1, 0>* ins,
MDefinition* mir, MDefinition* input)
{
ins->setOperand(0, useRegister(input));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
// z = x+y
void
LIRGeneratorMIPS::lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs)
{
ins->setOperand(0, useRegister(lhs));
ins->setOperand(1, useRegisterOrConstant(rhs));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
void
LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* input)
{
ins->setOperand(0, useRegister(input));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
template<size_t Temps>
void
LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs)
{
ins->setOperand(0, useRegister(lhs));
ins->setOperand(1, useRegister(rhs));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
template void LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
template void LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 1>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
void
LIRGeneratorMIPS::lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
MDefinition* lhs, MDefinition* rhs)
{
baab->setOperand(0, useRegisterAtStart(lhs));
baab->setOperand(1, useRegisterOrConstantAtStart(rhs));
add(baab, mir);
}
void
LIRGeneratorMIPS::defineUntypedPhi(MPhi* phi, size_t lirIndex)
{
@ -228,253 +156,6 @@ LIRGeneratorMIPS::lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition,
payload->setOperand(inputPosition, LUse(VirtualRegisterOfPayload(operand), LUse::ANY));
}
void
LIRGeneratorMIPS::lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs)
{
ins->setOperand(0, useRegister(lhs));
ins->setOperand(1, useRegisterOrConstant(rhs));
define(ins, mir);
}
void
LIRGeneratorMIPS::lowerDivI(MDiv* div)
{
if (div->isUnsigned()) {
lowerUDiv(div);
return;
}
// Division instructions are slow. Division by constant denominators can be
// rewritten to use other instructions.
if (div->rhs()->isConstant()) {
int32_t rhs = div->rhs()->toConstant()->value().toInt32();
// Check for division by a positive power of two, which is an easy and
// important case to optimize. Note that other optimizations are also
// possible; division by negative powers of two can be optimized in a
// similar manner as positive powers of two, and division by other
// constants can be optimized by a reciprocal multiplication technique.
int32_t shift = FloorLog2(rhs);
if (rhs > 0 && 1 << shift == rhs) {
LDivPowTwoI* lir = new(alloc()) LDivPowTwoI(useRegister(div->lhs()), shift, temp());
if (div->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, div);
return;
}
}
LDivI* lir = new(alloc()) LDivI(useRegister(div->lhs()), useRegister(div->rhs()), temp());
if (div->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, div);
}
void
LIRGeneratorMIPS::lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs)
{
LMulI* lir = new(alloc()) LMulI;
if (mul->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
lowerForALU(lir, mul, lhs, rhs);
}
void
LIRGeneratorMIPS::lowerModI(MMod* mod)
{
if (mod->isUnsigned()) {
lowerUMod(mod);
return;
}
if (mod->rhs()->isConstant()) {
int32_t rhs = mod->rhs()->toConstant()->value().toInt32();
int32_t shift = FloorLog2(rhs);
if (rhs > 0 && 1 << shift == rhs) {
LModPowTwoI* lir = new(alloc()) LModPowTwoI(useRegister(mod->lhs()), shift);
if (mod->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, mod);
return;
} else if (shift < 31 && (1 << (shift + 1)) - 1 == rhs) {
LModMaskI* lir = new(alloc()) LModMaskI(useRegister(mod->lhs()),
temp(LDefinition::GENERAL),
temp(LDefinition::GENERAL),
shift + 1);
if (mod->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, mod);
return;
}
}
LModI* lir = new(alloc()) LModI(useRegister(mod->lhs()), useRegister(mod->rhs()),
temp(LDefinition::GENERAL));
if (mod->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, mod);
}
void
LIRGeneratorMIPS::visitPowHalf(MPowHalf* ins)
{
MDefinition* input = ins->input();
MOZ_ASSERT(input->type() == MIRType_Double);
LPowHalfD* lir = new(alloc()) LPowHalfD(useRegisterAtStart(input));
defineReuseInput(lir, ins, 0);
}
LTableSwitch*
LIRGeneratorMIPS::newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
MTableSwitch* tableswitch)
{
return new(alloc()) LTableSwitch(in, inputCopy, temp(), tableswitch);
}
LTableSwitchV*
LIRGeneratorMIPS::newLTableSwitchV(MTableSwitch* tableswitch)
{
return new(alloc()) LTableSwitchV(temp(), tempDouble(), temp(), tableswitch);
}
void
LIRGeneratorMIPS::visitGuardShape(MGuardShape* ins)
{
MOZ_ASSERT(ins->obj()->type() == MIRType_Object);
LDefinition tempObj = temp(LDefinition::OBJECT);
LGuardShape* guard = new(alloc()) LGuardShape(useRegister(ins->obj()), tempObj);
assignSnapshot(guard, ins->bailoutKind());
add(guard, ins);
redefine(ins, ins->obj());
}
void
LIRGeneratorMIPS::visitGuardObjectGroup(MGuardObjectGroup* ins)
{
MOZ_ASSERT(ins->obj()->type() == MIRType_Object);
LDefinition tempObj = temp(LDefinition::OBJECT);
LGuardObjectGroup* guard = new(alloc()) LGuardObjectGroup(useRegister(ins->obj()), tempObj);
assignSnapshot(guard, ins->bailoutKind());
add(guard, ins);
redefine(ins, ins->obj());
}
void
LIRGeneratorMIPS::lowerUrshD(MUrsh* mir)
{
MDefinition* lhs = mir->lhs();
MDefinition* rhs = mir->rhs();
MOZ_ASSERT(lhs->type() == MIRType_Int32);
MOZ_ASSERT(rhs->type() == MIRType_Int32);
LUrshD* lir = new(alloc()) LUrshD(useRegister(lhs), useRegisterOrConstant(rhs), temp());
define(lir, mir);
}
void
LIRGeneratorMIPS::visitAsmJSNeg(MAsmJSNeg* ins)
{
if (ins->type() == MIRType_Int32) {
define(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins);
} else if (ins->type() == MIRType_Float32) {
define(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins);
} else {
MOZ_ASSERT(ins->type() == MIRType_Double);
define(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins);
}
}
void
LIRGeneratorMIPS::lowerUDiv(MDiv* div)
{
MDefinition* lhs = div->getOperand(0);
MDefinition* rhs = div->getOperand(1);
LUDivOrMod* lir = new(alloc()) LUDivOrMod;
lir->setOperand(0, useRegister(lhs));
lir->setOperand(1, useRegister(rhs));
if (div->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, div);
}
void
LIRGeneratorMIPS::lowerUMod(MMod* mod)
{
MDefinition* lhs = mod->getOperand(0);
MDefinition* rhs = mod->getOperand(1);
LUDivOrMod* lir = new(alloc()) LUDivOrMod;
lir->setOperand(0, useRegister(lhs));
lir->setOperand(1, useRegister(rhs));
if (mod->fallible())
assignSnapshot(lir, Bailout_DoubleOutput);
define(lir, mod);
}
void
LIRGeneratorMIPS::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins)
{
MOZ_ASSERT(ins->input()->type() == MIRType_Int32);
LAsmJSUInt32ToDouble* lir = new(alloc()) LAsmJSUInt32ToDouble(useRegisterAtStart(ins->input()));
define(lir, ins);
}
void
LIRGeneratorMIPS::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins)
{
MOZ_ASSERT(ins->input()->type() == MIRType_Int32);
LAsmJSUInt32ToFloat32* lir = new(alloc()) LAsmJSUInt32ToFloat32(useRegisterAtStart(ins->input()));
define(lir, ins);
}
void
LIRGeneratorMIPS::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
LAllocation ptrAlloc;
// For MIPS it is best to keep the 'ptr' in a register if a bounds check
// is needed.
if (ptr->isConstantValue() && !ins->needsBoundsCheck()) {
// A bounds check is only skipped for a positive index.
MOZ_ASSERT(ptr->constantValue().toInt32() >= 0);
ptrAlloc = LAllocation(ptr->constantVp());
} else
ptrAlloc = useRegisterAtStart(ptr);
define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins);
}
void
LIRGeneratorMIPS::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
LAllocation ptrAlloc;
if (ptr->isConstantValue() && !ins->needsBoundsCheck()) {
MOZ_ASSERT(ptr->constantValue().toInt32() >= 0);
ptrAlloc = LAllocation(ptr->constantVp());
} else
ptrAlloc = useRegisterAtStart(ptr);
add(new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins);
}
void
LIRGeneratorMIPS::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins)
{
define(new(alloc()) LAsmJSLoadFuncPtr(useRegister(ins->index())), ins);
}
void
LIRGeneratorMIPS::lowerTruncateDToInt32(MTruncateToInt32* ins)
{
@ -492,89 +173,3 @@ LIRGeneratorMIPS::lowerTruncateFToInt32(MTruncateToInt32* ins)
define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
}
void
LIRGeneratorMIPS::visitSubstr(MSubstr* ins)
{
LSubstr* lir = new (alloc()) LSubstr(useRegister(ins->string()),
useRegister(ins->begin()),
useRegister(ins->length()),
temp(),
temp(),
tempByteOpRegister());
define(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGeneratorMIPS::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitSimdBinaryArith(MSimdBinaryArith* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitSimdSelect(MSimdSelect* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitSimdSplatX4(MSimdSplatX4* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitSimdValueX4(MSimdValueX4* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins)
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitRandom(MRandom* ins)
{
LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
defineReturn(lir, ins);
}

View File

@ -7,16 +7,16 @@
#ifndef jit_mips32_Lowering_mips32_h
#define jit_mips32_Lowering_mips32_h
#include "jit/shared/Lowering-shared.h"
#include "jit/mips-shared/Lowering-mips-shared.h"
namespace js {
namespace jit {
class LIRGeneratorMIPS : public LIRGeneratorShared
class LIRGeneratorMIPS : public LIRGeneratorMIPSShared
{
protected:
LIRGeneratorMIPS(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph)
: LIRGeneratorShared(gen, graph, lirGraph)
: LIRGeneratorMIPSShared(gen, graph, lirGraph)
{ }
protected:
@ -25,87 +25,20 @@ class LIRGeneratorMIPS : public LIRGeneratorShared
void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register reg2,
bool useAtStart = false);
// x86 has constraints on what registers can be formatted for 1-byte
// stores and loads; on MIPS all registers are okay.
LAllocation useByteOpRegister(MDefinition* mir);
LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir);
LDefinition tempByteOpRegister();
inline LDefinition tempToUnbox() {
return LDefinition::BogusTemp();
}
bool needTempForPostBarrier() { return false; }
void lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex);
void defineUntypedPhi(MPhi* phi, size_t lirIndex);
void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
MDefinition* rhs);
void lowerUrshD(MUrsh* mir);
void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* input);
void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* src);
template<size_t Temps>
void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
MDefinition* lhs, MDefinition* rhs)
{
return lowerForFPU(ins, mir, lhs, rhs);
}
void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir,
MDefinition* lhs, MDefinition* rhs)
{
return lowerForFPU(ins, mir, lhs, rhs);
}
void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
MDefinition* lhs, MDefinition* rhs);
void lowerTruncateDToInt32(MTruncateToInt32* ins);
void lowerTruncateFToInt32(MTruncateToInt32* ins);
void lowerDivI(MDiv* div);
void lowerModI(MMod* mod);
void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
void lowerUDiv(MDiv* div);
void lowerUMod(MMod* mod);
void visitPowHalf(MPowHalf* ins);
void visitAsmJSNeg(MAsmJSNeg* ins);
LTableSwitch* newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
MTableSwitch* ins);
LTableSwitchV* newLTableSwitchV(MTableSwitch* ins);
public:
void visitBox(MBox* box);
void visitUnbox(MUnbox* unbox);
void visitReturn(MReturn* ret);
void lowerPhi(MPhi* phi);
void visitGuardShape(MGuardShape* ins);
void visitGuardObjectGroup(MGuardObjectGroup* ins);
void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins);
void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins);
void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins);
void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins);
void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
void visitSimdBinaryArith(MSimdBinaryArith* ins);
void visitSimdSelect(MSimdSelect* ins);
void visitSimdSplatX4(MSimdSplatX4* ins);
void visitSimdValueX4(MSimdValueX4* ins);
void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);
};
typedef LIRGeneratorMIPS LIRGeneratorSpecific;

View File

@ -468,6 +468,7 @@ elif CONFIG['JS_CODEGEN_MIPS32']:
UNIFIED_SOURCES += [
'jit/mips-shared/Architecture-mips-shared.cpp',
'jit/mips-shared/Assembler-mips-shared.cpp',
'jit/mips-shared/Lowering-mips-shared.cpp',
]
if CONFIG['JS_CODEGEN_MIPS32']:
UNIFIED_SOURCES += [

View File

@ -78,7 +78,6 @@ using namespace mozilla::layout;
static PRLogModuleInfo *gLog = nullptr;
#define LOG(...) MOZ_LOG(gLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
#define DEFAULT_FRAME_RATE 60
#define DEFAULT_THROTTLED_FRAME_RATE 1
#define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
// after 10 minutes, stop firing off inactive timers
@ -765,7 +764,7 @@ nsRefreshDriver::Shutdown()
/* static */ int32_t
nsRefreshDriver::DefaultInterval()
{
return NSToIntRound(1000.0 / DEFAULT_FRAME_RATE);
return NSToIntRound(1000.0 / gfxPlatform::GetDefaultFrameRate());
}
// Compute the interval to use for the refresh driver timer, in milliseconds.
@ -781,7 +780,7 @@ nsRefreshDriver::GetRegularTimerInterval(bool *outIsDefault) const
{
int32_t rate = Preferences::GetInt("layout.frame_rate", -1);
if (rate < 0) {
rate = DEFAULT_FRAME_RATE;
rate = gfxPlatform::GetDefaultFrameRate();
if (outIsDefault) {
*outIsDefault = true;
}

View File

@ -174,20 +174,21 @@ class NrIceTurnServer : public NrIceStunServer {
class NrIceProxyServer {
public:
NrIceProxyServer() :
host_(), port_(0) {
NrIceProxyServer(const std::string& host, uint16_t port,
const std::string& alpn) :
host_(host), port_(port), alpn_(alpn) {
}
NrIceProxyServer(const std::string& host, uint16_t port) :
host_(host), port_(port) {
}
NrIceProxyServer() : NrIceProxyServer("", 0, "") {}
const std::string& host() const { return host_; }
uint16_t port() const { return port_; }
const std::string& alpn() const { return alpn_; }
private:
std::string host_;
uint16_t port_;
std::string alpn_;
};

View File

@ -46,9 +46,13 @@ const uint16_t kProxyPort = 9999;
const std::string kHelloMessage = "HELLO";
const std::string kGarbageMessage = "xxxxxxxxxx";
std::string connect_message(const std::string &host, uint16_t port, const std::string &tail = "") {
std::string connect_message(const std::string &host, uint16_t port, const std::string &alpn, const std::string &tail) {
std::stringstream ss;
ss << "CONNECT " << host << ":" << port << " HTTP/1.0\r\n\r\n" << tail;
ss << "CONNECT " << host << ":" << port << " HTTP/1.0\r\n";
if (!alpn.empty()) {
ss << "ALPN: " << alpn << "\r\n";
}
ss << "\r\n" << tail;
return ss.str();
}
@ -116,8 +120,6 @@ class ProxyTunnelSocketTest : public ::testing::Test {
}
void SetUp() {
nsRefPtr<DummySocket> dummy(new DummySocket());
nr_resolver_ = resolver_impl_.get_nr_resolver();
int r = nr_str_port_to_transport_addr(
@ -132,7 +134,18 @@ class ProxyTunnelSocketTest : public ::testing::Test {
nr_proxy_tunnel_config_set_resolver(config_, nr_resolver_);
nr_proxy_tunnel_config_set_proxy(config_, kProxyAddr.c_str(), kProxyPort);
r = nr_socket_proxy_tunnel_create(
Configure();
}
// This reconfigures the socket with the updated information in config_.
void Configure() {
if (nr_socket_) {
EXPECT_EQ(0, nr_socket_destroy(&nr_socket_));
EXPECT_EQ(nullptr, nr_socket_);
}
nsRefPtr<DummySocket> dummy(new DummySocket());
int r = nr_socket_proxy_tunnel_create(
config_,
dummy->get_nr_socket(),
&nr_socket_);
@ -141,6 +154,16 @@ class ProxyTunnelSocketTest : public ::testing::Test {
socket_impl_ = dummy.forget(); // Now owned by nr_socket_.
}
void Connect(int expectedReturn = 0) {
int r = nr_socket_connect(nr_socket_, &remote_addr_);
EXPECT_EQ(expectedReturn, r);
size_t written = 0;
r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
EXPECT_EQ(0, r);
EXPECT_EQ(kHelloMessage.size(), written);
}
nr_socket *socket() { return nr_socket_; }
protected:
@ -166,56 +189,67 @@ TEST_F(ProxyTunnelSocketTest, TestConnectProxyAddress) {
}
TEST_F(ProxyTunnelSocketTest, TestConnectProxyRequest) {
int r = nr_socket_connect(nr_socket_, &remote_addr_);
ASSERT_EQ(0, r);
Connect();
size_t written = 0;
r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
ASSERT_EQ(0, r);
std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
}
std::string msg = connect_message(kRemoteAddr, kRemotePort, kHelloMessage);
TEST_F(ProxyTunnelSocketTest, TestAlpnConnect) {
const std::string alpn = "this,is,alpn";
int r = nr_proxy_tunnel_config_set_alpn(config_, alpn.c_str());
EXPECT_EQ(0, r);
Configure();
Connect();
std::string msg = connect_message(kRemoteAddr, kRemotePort, alpn, kHelloMessage);
socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
}
TEST_F(ProxyTunnelSocketTest, TestNullAlpnConnect) {
int r = nr_proxy_tunnel_config_set_alpn(config_, nullptr);
EXPECT_EQ(0, r);
Configure();
Connect();
std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
}
TEST_F(ProxyTunnelSocketTest, TestConnectProxyHostRequest) {
int r = nr_socket_destroy(&nr_socket_);
ASSERT_EQ(0, r);
nsRefPtr<DummySocket> dummy(new DummySocket());
nr_proxy_tunnel_config_set_proxy(config_, kProxyHost.c_str(), kProxyPort);
Configure();
// Because kProxyHost is a domain name and not an IP address,
// nr_socket_connect will need to resolve an IP address before continuing. It
// does that, and assumes that resolving the IP will take some time, so it
// returns R_WOULDBLOCK.
//
// However, In this test setup, the resolution happens inline immediately, so
// nr_socket_connect is called recursively on the inner socket in
// nr_socket_proxy_tunnel_resolved_cb. That also completes. Thus, the socket
// is actually successfully connected after this call, even though
// nr_socket_connect reports an error.
//
// Arguably nr_socket_proxy_tunnel_connect() is busted, because it shouldn't
// report an error when it doesn't need any further assistance from the
// calling code, but that's pretty minor.
Connect(R_WOULDBLOCK);
r = nr_socket_proxy_tunnel_create(
config_,
dummy->get_nr_socket(),
&nr_socket_);
ASSERT_EQ(0, r);
socket_impl_ = dummy.forget(); // Now owned by nr_socket_.
r = nr_socket_connect(nr_socket_, &remote_addr_);
ASSERT_EQ(R_WOULDBLOCK, r);
size_t written = 0;
r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
ASSERT_EQ(0, r);
std::string msg = connect_message(kRemoteAddr, kRemotePort, kHelloMessage);
std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
}
TEST_F(ProxyTunnelSocketTest, TestConnectProxyWrite) {
int r = nr_socket_connect(nr_socket_, &remote_addr_);
ASSERT_EQ(0, r);
size_t written = 0;
r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
ASSERT_EQ(0, r);
Connect();
socket_impl_->ClearWriteBuffer();
r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
ASSERT_EQ(0, r);
size_t written = 0;
int r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
EXPECT_EQ(0, r);
EXPECT_EQ(kHelloMessage.size(), written);
socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(kHelloMessage.c_str()),
kHelloMessage.size());

View File

@ -41,7 +41,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define MAX_HTTP_CONNECT_ADDR_SIZE 256
#define MAX_HTTP_CONNECT_BUFFER_SIZE 1024
#define ENDLN "\r\n\r\n"
#define MAX_ALPN_LENGTH 64
#ifndef CRLF
#define CRLF "\r\n"
#endif
#define END_HEADERS CRLF CRLF
typedef struct nr_socket_proxy_tunnel_ {
nr_proxy_tunnel_config *config;
@ -94,7 +98,8 @@ static int send_http_connect(nr_socket_proxy_tunnel *sock)
int port;
int printed;
char addr[MAX_HTTP_CONNECT_ADDR_SIZE];
char mesg[MAX_HTTP_CONNECT_ADDR_SIZE + 64];
char mesg[MAX_HTTP_CONNECT_ADDR_SIZE + MAX_ALPN_LENGTH + 128];
size_t offset = 0;
size_t bytes_sent;
r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect");
@ -107,16 +112,32 @@ static int send_http_connect(nr_socket_proxy_tunnel *sock)
ABORT(r);
}
printed = snprintf(mesg, sizeof(mesg), "CONNECT %s:%d HTTP/1.0%s", addr, port, ENDLN);
if (printed < 0 || ((size_t)printed >= sizeof(mesg))) {
printed = snprintf(mesg + offset, sizeof(mesg) - offset,
"CONNECT %s:%d HTTP/1.0", addr, port);
offset += printed;
if (printed < 0 || (offset >= sizeof(mesg))) {
ABORT(R_FAILED);
}
if ((r=nr_socket_write(sock->inner, mesg, strlen(mesg), &bytes_sent, 0))) {
if (sock->config->alpn) {
printed = snprintf(mesg + offset, sizeof(mesg) - offset,
CRLF "ALPN: %s", sock->config->alpn);
offset += printed;
if (printed < 0 || (offset >= sizeof(mesg))) {
ABORT(R_FAILED);
}
}
if (offset + sizeof(END_HEADERS) >= sizeof(mesg)) {
ABORT(R_FAILED);
}
memcpy(mesg + offset, END_HEADERS, strlen(END_HEADERS));
offset += strlen(END_HEADERS);
if ((r=nr_socket_write(sock->inner, mesg, offset, &bytes_sent, 0))) {
ABORT(r);
}
if (bytes_sent < strlen(mesg)) {
if (bytes_sent < offset) {
/* TODO(bug 1116583): buffering and wait for */
r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect should be buffering %lu", (unsigned long)bytes_sent);
ABORT(R_IO_ERROR);
@ -133,10 +154,10 @@ static char *find_http_terminator(char *response, size_t len)
{
char *term = response;
char *end = response + len;
int N = strlen(ENDLN);
int N = strlen(END_HEADERS);
for (; term = memchr(term, '\r', end - term); ++term) {
if (end - term >= N && memcmp(term, ENDLN, N) == 0) {
if (end - term >= N && memcmp(term, END_HEADERS, N) == 0) {
return term;
}
}
@ -367,7 +388,7 @@ int nr_socket_proxy_tunnel_read(void *obj, void * restrict buf, size_t maxlen,
ABORT(R_FAILED);
}
ptr = http_term + strlen(ENDLN);
ptr = http_term + strlen(END_HEADERS);
pending = sock->buffered_bytes - (ptr - sock->buffer);
if (pending == 0) {
@ -431,6 +452,7 @@ int nr_proxy_tunnel_config_destroy(nr_proxy_tunnel_config **configpp)
*configpp = 0;
RFREE(configp->proxy_host);
RFREE(configp->alpn);
RFREE(configp);
return 0;
@ -471,6 +493,34 @@ int nr_proxy_tunnel_config_set_resolver(nr_proxy_tunnel_config *config,
return 0;
}
int nr_proxy_tunnel_config_set_alpn(nr_proxy_tunnel_config *config,
const char *alpn)
{
r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_alpn");
if (alpn && (strlen(alpn) > MAX_ALPN_LENGTH)) {
return R_BAD_ARGS;
}
if (config->alpn) {
RFREE(config->alpn);
}
config->alpn = NULL;
if (alpn) {
char *alpndup = r_strdup(alpn);
if (!alpndup) {
return R_NO_MEMORY;
}
config->alpn = alpndup;
}
return 0;
}
int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_config **copypp)
{
int r,_status;
@ -485,6 +535,9 @@ int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_
if ((r=nr_proxy_tunnel_config_set_resolver(copy, config->resolver)))
ABORT(r);
if ((r=nr_proxy_tunnel_config_set_alpn(copy, config->alpn)))
ABORT(r);
*copypp = copy;
_status=0;

View File

@ -44,6 +44,7 @@ typedef struct nr_proxy_tunnel_config_ {
nr_resolver *resolver;
char *proxy_host;
UINT2 proxy_port;
char *alpn;
} nr_proxy_tunnel_config;
int nr_proxy_tunnel_config_create(nr_proxy_tunnel_config **config);
@ -56,6 +57,9 @@ int nr_proxy_tunnel_config_set_proxy(nr_proxy_tunnel_config *config,
int nr_proxy_tunnel_config_set_resolver(nr_proxy_tunnel_config *config,
nr_resolver *resolver);
int nr_proxy_tunnel_config_set_alpn(nr_proxy_tunnel_config *config,
const char *alpn);
int nr_socket_proxy_tunnel_create(nr_proxy_tunnel_config *config,
nr_socket *inner,
nr_socket **socketpp);

View File

@ -205,9 +205,15 @@ PeerConnectionMedia::ProtocolProxyQueryHandler::SetProxyOnPcm(
if (pcm_->mIceCtx.get()) {
assert(httpsProxyPort >= 0 && httpsProxyPort < (1 << 16));
// Note that this could check if PrivacyRequested() is set on the PC and
// remove "webrtc" from the ALPN list. But that would only work if the PC
// was constructed with a peerIdentity constraint, not when isolated
// streams are added. If we ever need to signal to the proxy that the
// media is isolated, then we would need to restructure this code.
pcm_->mProxyServer.reset(
new NrIceProxyServer(httpsProxyHost.get(),
static_cast<uint16_t>(httpsProxyPort)));
static_cast<uint16_t>(httpsProxyPort),
"webrtc,c-webrtc"));
} else {
CSFLogError(logTag, "%s: Failed to set proxy server (ICE ctx unavailable)",
__FUNCTION__);

View File

@ -121,7 +121,7 @@ private:
reinterpret_cast<uintptr_t>(chunk) + static_cast<uintptr_t>(offset));
if (!VirtualAlloc(addr, length, MEM_COMMIT, PAGE_READWRITE))
MOZ_CRASH();
return true;
return false;
}

View File

@ -1460,10 +1460,6 @@ pref("network.http.packaged-apps-developer-mode", false);
pref("network.ftp.data.qos", 0);
pref("network.ftp.control.qos", 0);
// If this pref is false only one xpcom event will be served per poll
// iteration. This is the original behavior.
// If it is true multiple events will be served.
pref("network.sts.serve_multiple_events_per_poll_iteration", true);
// The max time to spend on xpcom events between two polls in ms.
pref("network.sts.max_time_for_events_between_two_polls", 100);
// </http>

View File

@ -6,8 +6,9 @@
#include "nsISupports.idl"
interface nsIInputStream;
interface nsIRunnable;
[scriptable, uuid(62e6529a-5cf6-491a-82ef-b3a8273cdd19)]
[scriptable, uuid(2f712b52-19c5-4e0c-9e8f-b5c7c3b67049)]
interface nsIUploadChannel2 : nsISupports
{
/**
@ -47,7 +48,19 @@ interface nsIUploadChannel2 : nsISupports
readonly attribute boolean uploadStreamHasHeaders;
/**
* Clones the upload stream and returns an equivalent stream.
* Ensure the upload stream, if any, is cloneable. This may involve
* async copying, so a callback runnable must be provided. It will
* invoked on the current thread when the upload stream is ready
* for cloning. If the stream is already cloneable, then the callback
* will be invoked synchronously.
*/
[noscript]
void ensureUploadStreamIsCloneable(in nsIRunnable aCallback);
/**
* Clones the upload stream. May return failure if the upload stream
* is not cloneable. If this is not acceptable, use the
* ensureUploadStreamIsCloneable() method first.
*/
[noscript]
nsIInputStream cloneUploadStream();

View File

@ -44,7 +44,6 @@ PRThread *gSocketThread = nullptr;
#define SOCKET_LIMIT_TARGET 550U
#define SOCKET_LIMIT_MIN 50U
#define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
#define SERVE_MULTIPLE_EVENTS_PREF "network.sts.serve_multiple_events_per_poll_iteration"
#define MAX_TIME_BETWEEN_TWO_POLLS "network.sts.max_time_for_events_between_two_polls"
#define TELEMETRY_PREF "toolkit.telemetry.enabled"
@ -106,7 +105,6 @@ nsSocketTransportService::nsSocketTransportService()
, mKeepaliveRetryIntervalS(1)
, mKeepaliveProbeCount(kDefaultTCPKeepCount)
, mKeepaliveEnabledPref(false)
, mServeMultipleEventsPerPollIter(true)
, mServingPendingQueue(false)
, mMaxTimePerPollIter(100)
, mTelemetryEnabledPref(false)
@ -543,7 +541,6 @@ nsSocketTransportService::Init()
tmpPrefService->AddObserver(KEEPALIVE_IDLE_TIME_PREF, this, false);
tmpPrefService->AddObserver(KEEPALIVE_RETRY_INTERVAL_PREF, this, false);
tmpPrefService->AddObserver(KEEPALIVE_PROBE_COUNT_PREF, this, false);
tmpPrefService->AddObserver(SERVE_MULTIPLE_EVENTS_PREF, this, false);
tmpPrefService->AddObserver(MAX_TIME_BETWEEN_TWO_POLLS, this, false);
tmpPrefService->AddObserver(TELEMETRY_PREF, this, false);
}
@ -885,57 +882,51 @@ nsSocketTransportService::Run()
}
if (pendingEvents) {
if (mServeMultipleEventsPerPollIter) {
if (!mServingPendingQueue) {
nsresult rv = Dispatch(NS_NewRunnableMethod(this,
&nsSocketTransportService::MarkTheLastElementOfPendingQueue),
nsIEventTarget::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Could not dispatch a new event on the "
"socket thread.");
} else {
mServingPendingQueue = true;
}
if (mTelemetryEnabledPref) {
startOfIteration = startOfNextIteration;
// Everything that comes after this point will
// be served in the next iteration. If no even
// arrives, startOfNextIteration will be reset at the
// beginning of each for-loop.
startOfNextIteration = TimeStamp::NowLoRes();
}
if (!mServingPendingQueue) {
nsresult rv = Dispatch(NS_NewRunnableMethod(this,
&nsSocketTransportService::MarkTheLastElementOfPendingQueue),
nsIEventTarget::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Could not dispatch a new event on the "
"socket thread.");
} else {
mServingPendingQueue = true;
}
TimeStamp eventQueueStart = TimeStamp::NowLoRes();
do {
NS_ProcessNextEvent(thread);
numberOfPendingEvents++;
pendingEvents = false;
thread->HasPendingEvents(&pendingEvents);
} while (pendingEvents && mServingPendingQueue &&
((TimeStamp::NowLoRes() -
eventQueueStart).ToMilliseconds() <
mMaxTimePerPollIter));
if (mTelemetryEnabledPref && !mServingPendingQueue &&
!startOfIteration.IsNull()) {
Telemetry::AccumulateTimeDelta(
Telemetry::STS_POLL_AND_EVENTS_CYCLE,
startOfIteration + pollDuration,
TimeStamp::NowLoRes());
Telemetry::Accumulate(
Telemetry::STS_NUMBER_OF_PENDING_EVENTS,
numberOfPendingEvents);
numberOfPendingEventsLastCycle += numberOfPendingEvents;
numberOfPendingEvents = 0;
pollDuration = 0;
if (mTelemetryEnabledPref) {
startOfIteration = startOfNextIteration;
// Everything that comes after this point will
// be served in the next iteration. If no even
// arrives, startOfNextIteration will be reset at the
// beginning of each for-loop.
startOfNextIteration = TimeStamp::NowLoRes();
}
} else {
}
TimeStamp eventQueueStart = TimeStamp::NowLoRes();
do {
NS_ProcessNextEvent(thread);
numberOfPendingEvents++;
pendingEvents = false;
thread->HasPendingEvents(&pendingEvents);
} while (pendingEvents && mServingPendingQueue &&
((TimeStamp::NowLoRes() -
eventQueueStart).ToMilliseconds() <
mMaxTimePerPollIter));
if (mTelemetryEnabledPref && !mServingPendingQueue &&
!startOfIteration.IsNull()) {
Telemetry::AccumulateTimeDelta(
Telemetry::STS_POLL_AND_EVENTS_CYCLE,
startOfIteration + pollDuration,
TimeStamp::NowLoRes());
Telemetry::Accumulate(
Telemetry::STS_NUMBER_OF_PENDING_EVENTS,
numberOfPendingEvents);
numberOfPendingEventsLastCycle += numberOfPendingEvents;
numberOfPendingEvents = 0;
pollDuration = 0;
}
}
} while (pendingEvents);
@ -1206,13 +1197,6 @@ nsSocketTransportService::UpdatePrefs()
OnKeepaliveEnabledPrefChange();
}
bool serveMultiplePref = false;
rv = tmpPrefService->GetBoolPref(SERVE_MULTIPLE_EVENTS_PREF,
&serveMultiplePref);
if (NS_SUCCEEDED(rv)) {
mServeMultipleEventsPerPollIter = serveMultiplePref;
}
int32_t maxTimePref;
rv = tmpPrefService->GetIntPref(MAX_TIME_BETWEEN_TWO_POLLS,
&maxTimePref);

View File

@ -236,7 +236,6 @@ private:
// True if TCP keepalive is enabled globally.
bool mKeepaliveEnabledPref;
bool mServeMultipleEventsPerPollIter;
mozilla::Atomic<bool> mServingPendingQueue;
int32_t mMaxTimePerPollIter;
mozilla::Atomic<bool, mozilla::Relaxed> mTelemetryEnabledPref;

View File

@ -19,6 +19,7 @@
#include "nsIPrincipal.h"
#include "nsIScriptError.h"
#include "nsISeekableStream.h"
#include "nsIStorageStream.h"
#include "nsITimedChannel.h"
#include "nsIEncodedChannel.h"
#include "nsIApplicationCacheChannel.h"
@ -574,17 +575,98 @@ HttpBaseChannel::SetUploadStream(nsIInputStream *stream,
return NS_OK;
}
static void
EnsureStreamBuffered(nsCOMPtr<nsIInputStream>& aStream)
namespace {
void
CopyComplete(void* aClosure, nsresult aStatus) {
// Called on the STS thread by NS_AsyncCopy
auto channel = static_cast<HttpBaseChannel*>(aClosure);
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethodWithArg<nsresult>(
channel, &HttpBaseChannel::EnsureUploadStreamIsCloneableComplete, aStatus);
NS_DispatchToMainThread(runnable.forget());
}
} // anonymous namespace
NS_IMETHODIMP
HttpBaseChannel::EnsureUploadStreamIsCloneable(nsIRunnable* aCallback)
{
if (!NS_InputStreamIsBuffered(aStream)) {
nsCOMPtr<nsIInputStream> bufferedStream;
nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
aStream,
4096);
NS_ENSURE_SUCCESS_VOID(rv);
aStream.swap(bufferedStream);
MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
NS_ENSURE_ARG_POINTER(aCallback);
// We could in theory allow multiple callers to use this method,
// but the complexity does not seem worth it yet. Just fail if
// this is called more than once simultaneously.
NS_ENSURE_FALSE(mUploadCloneableCallback, NS_ERROR_UNEXPECTED);
// If the CloneUploadStream() will succeed, then synchronously invoke
// the callback to indicate we're already cloneable.
if (!mUploadStream || NS_InputStreamIsCloneable(mUploadStream)) {
aCallback->Run();
return NS_OK;
}
nsCOMPtr<nsIStorageStream> storageStream;
nsresult rv = NS_NewStorageStream(4096, UINT32_MAX,
getter_AddRefs(storageStream));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInputStream> newUploadStream;
rv = storageStream->NewInputStream(0, getter_AddRefs(newUploadStream));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIOutputStream> sink;
rv = storageStream->GetOutputStream(0, getter_AddRefs(sink));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInputStream> source;
if (NS_InputStreamIsBuffered(mUploadStream)) {
source = mUploadStream;
} else {
rv = NS_NewBufferedInputStream(getter_AddRefs(source), mUploadStream, 4096);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIEventTarget> target =
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
mUploadCloneableCallback = aCallback;
rv = NS_AsyncCopy(source, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS,
4096, // copy segment size
CopyComplete, this);
if (NS_WARN_IF(NS_FAILED(rv))) {
mUploadCloneableCallback = nullptr;
return rv;
}
// Since we're consuming the old stream, replace it with the new
// stream immediately.
mUploadStream = newUploadStream;
// Explicity hold the stream alive until copying is complete. This will
// be released in EnsureUploadStreamIsCloneableComplete().
AddRef();
return NS_OK;
}
void
HttpBaseChannel::EnsureUploadStreamIsCloneableComplete(nsresult aStatus)
{
MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
MOZ_ASSERT(mUploadCloneableCallback);
if (NS_SUCCEEDED(mStatus)) {
mStatus = aStatus;
}
mUploadCloneableCallback->Run();
mUploadCloneableCallback = nullptr;
// Release the reference we grabbed in EnsureUploadStreamIsCloneable() now
// that the copying is complete.
Release();
}
NS_IMETHODIMP
@ -598,21 +680,9 @@ HttpBaseChannel::CloneUploadStream(nsIInputStream** aClonedStream)
}
nsCOMPtr<nsIInputStream> clonedStream;
nsCOMPtr<nsIInputStream> replacementStream;
nsresult rv = NS_CloneInputStream(mUploadStream, getter_AddRefs(clonedStream),
getter_AddRefs(replacementStream));
nsresult rv = NS_CloneInputStream(mUploadStream, getter_AddRefs(clonedStream));
NS_ENSURE_SUCCESS(rv, rv);
if (replacementStream) {
mUploadStream.swap(replacementStream);
// Ensure that the replacement stream is buffered.
EnsureStreamBuffered(mUploadStream);
}
// Ensure that the cloned stream is buffered.
EnsureStreamBuffered(clonedStream);
clonedStream.forget(aClonedStream);
return NS_OK;

View File

@ -269,6 +269,10 @@ public: /* Necko internal use only... */
nsresult DoApplyContentConversions(nsIStreamListener *aNextListener,
nsIStreamListener **aNewNextListener);
// Callback on main thread when NS_AsyncCopy() is finished populating
// the new mUploadStream.
void EnsureUploadStreamIsCloneableComplete(nsresult aStatus);
protected:
nsCOMArray<nsISecurityConsoleMessage> mSecurityConsoleMessages;
@ -329,6 +333,7 @@ protected:
nsHttpRequestHead mRequestHead;
nsCOMPtr<nsIInputStream> mUploadStream;
nsCOMPtr<nsIRunnable> mUploadCloneableCallback;
nsAutoPtr<nsHttpResponseHead> mResponseHead;
nsRefPtr<nsHttpConnectionInfo> mConnectionInfo;
nsCOMPtr<nsIProxyInfo> mProxyInfo;

View File

@ -2270,8 +2270,7 @@ HttpChannelChild::OverrideWithSynthesizedResponse(nsAutoPtr<nsHttpResponseHead>&
mResponseHead = aResponseHead;
mSynthesizedResponse = true;
uint16_t status = mResponseHead->Status();
if (status != 200 && status != 404) {
if (WillRedirect(mResponseHead)) {
// Continue with the original cross-process request
nsresult rv = ContinueAsyncOpen();
NS_ENSURE_SUCCESS_VOID(rv);

View File

@ -516,8 +516,10 @@ nsCORSListenerProxy::OnStartRequest(nsIRequest* aRequest,
do_QueryInterface(channel);
if (httpChannelChild) {
rv = httpChannelChild->RemoveCorsPreflightCacheEntry(uri, mRequestingPrincipal);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
if (NS_FAILED(rv)) {
// Only warn here to ensure we fall through the request Cancel()
// and outer listener OnStartRequest() calls.
NS_WARNING("Failed to remove CORS preflight cache entry!");
}
}
}

View File

@ -38,9 +38,13 @@ config = {
"taskcluster": {
'graph_server': 'graphs.mozilla.org',
'symbol_server_host': "symbolpush.mozilla.org",
'stage_server': 'stage.mozilla.org',
'stage_server': 'ignored',
# use the relengapi proxy to talk to tooltool
"tooltool_servers": ['http://relengapi/tooltool/'],
"tooltool_url": 'http://relengapi/tooltool/',
'upload_env': {
'UPLOAD_HOST': 'localhost',
'UPLOAD_PATH': '/home/worker/artifacts',
},
},
}

View File

@ -826,15 +826,18 @@ or run without that action (ie: --no-{action})"
mach_env = {}
if c.get('upload_env'):
mach_env.update(c['upload_env'])
mach_env['UPLOAD_HOST'] = mach_env['UPLOAD_HOST'] % {
'stage_server': c['stage_server']
}
mach_env['UPLOAD_USER'] = mach_env['UPLOAD_USER'] % {
'stage_username': c['stage_username']
}
mach_env['UPLOAD_SSH_KEY'] = mach_env['UPLOAD_SSH_KEY'] % {
'stage_ssh_key': c['stage_ssh_key']
}
if 'UPLOAD_HOST' in mach_env:
mach_env['UPLOAD_HOST'] = mach_env['UPLOAD_HOST'] % {
'stage_server': c['stage_server']
}
if 'UPLOAD_USER' in mach_env:
mach_env['UPLOAD_USER'] = mach_env['UPLOAD_USER'] % {
'stage_username': c['stage_username']
}
if 'UPLOAD_SSH_KEY' in mach_env:
mach_env['UPLOAD_SSH_KEY'] = mach_env['UPLOAD_SSH_KEY'] % {
'stage_ssh_key': c['stage_ssh_key']
}
if self.query_is_nightly():
mach_env['LATEST_MAR_DIR'] = c['latest_mar_dir'] % {

View File

@ -25,25 +25,17 @@ echo "running as" $(id)
: WORKSPACE ${WORKSPACE:=/home/worker/workspace}
# files to be "uploaded" (moved to ~/artifacts) from obj-firefox/dist
: DIST_UPLOADS ${DIST_UPLOADS:=""}
# files which will be be prefixed with target before being sent to artifacts
# e.g. DIST_TARGET_UPLOADS="a.zip" runs mv v2.0.a.zip mv artifacts/target.a.zip
: DIST_TARGET_UPLOADS ${DIST_TARGET_UPLOADS:=""}
set -v
# Don't run the upload step; this is passed through mozharness to mach. Once
# the mozharness scripts are not run in Buildbot anymore, this can be moved to
# Mozharness (or the upload step removed from mach entirely)
export MOZ_AUTOMATION_UPLOAD=0
export MOZ_CRASHREPORTER_NO_REPORT=1
export MOZ_OBJDIR=obj-firefox
export MOZ_SYMBOLS_EXTRA_BUILDID=linux64
export POST_SYMBOL_UPLOAD_CMD=/usr/local/bin/post-symbol-upload.py
export TINDERBOX_OUTPUT=1
# use "simple" package names so that they can be hard-coded in the task's
# extras.locations
export MOZ_SIMPLE_PACKAGE_NAME=target
# Ensure that in tree libraries can be found
export LIBRARY_PATH=$LIBRARY_PATH:$WORKSPACE/src/obj-firefox:$WORKSPACE/src/gcc/lib64
@ -136,20 +128,3 @@ python2.7 $WORKSPACE/build/src/testing/${MOZHARNESS_SCRIPT} ${config_cmds} \
--no-action=generate-build-stats \
--branch=${MH_BRANCH} \
--build-pool=${MH_BUILD_POOL}
mkdir -p /home/worker/artifacts
# upload auxiliary files
cd $WORKSPACE/build/src/obj-firefox/dist
for file in $DIST_UPLOADS
do
mv $file $HOME/artifacts/$file
done
# Discard version numbers from packaged files, they just make it hard to write
# the right filename in the task payload where artifacts are declared
for file in $DIST_TARGET_UPLOADS
do
mv *.$file $HOME/artifacts/target.$file
done

View File

@ -30,8 +30,6 @@ task:
# image paths
TOOLTOOL_CACHE: '/home/worker/tooltool-cache'
NEED_XVFB: true
DIST_UPLOADS: 'jsshell-linux-x86_64.zip'
DIST_TARGET_UPLOADS: 'x-test.linux-x86_64.tar.bz2 linux-x86_64.tar.bz2 linux-x86_64.json tests.zip crashreporter-symbols.zip'
maxRunTime: 36000
@ -47,7 +45,7 @@ task:
platform: linux64
# Rather then enforcing particular conventions we require that all build
# tasks provide the "build" extra field to specify where the build and tests
# files are located.
# files are located, relative to the task's artifacts URL
locations:
build: 'public/build/target.linux-x86_64.tar.bz2'
build: 'public/build/target.tar.bz2'
tests: 'public/build/target.tests.zip'

View File

@ -34,13 +34,8 @@ task:
MOZHARNESS_CONFIG: 'builds/releng_base_mac_64_cross_builds.py balrog/production.py'
MH_BRANCH: {{project}}
MH_BUILD_POOL: taskcluster
#TODO: bug 1164617 - remove Docker image hacks
LIBRARY_PATH: ""
CPLUS_INCLUDE_PATH: ""
# image paths
TOOLTOOL_CACHE: '/home/worker/tooltool-cache'
DIST_UPLOADS: 'jsshell-mac64.zip'
DIST_TARGET_UPLOADS: 'mac64.dmg mac64.json tests.zip crashreporter-symbols.zip'
maxRunTime: 36000
@ -58,5 +53,5 @@ task:
# tasks provide the "build" extra field to specify where the build and tests
# files are located.
locations:
build: 'public/build/target.mac64.dmg'
build: 'public/build/target.dmg'
tests: 'public/build/target.tests.zip'

View File

@ -0,0 +1 @@
prefs: [dom.caches.enabled:true]

View File

@ -1,3 +0,0 @@
[cache-add.https.html]
type: testharness
prefs: [dom.caches.enabled:true]

View File

@ -1,3 +0,0 @@
[cache-delete.https.html]
type: testharness
prefs: [dom.caches.enabled:true]

View File

@ -1,3 +0,0 @@
[cache-storage-keys.https.html]
type: testharness
prefs: [dom.caches.enabled:true]

View File

@ -1,3 +0,0 @@
[cache-storage.https.html]
type: testharness
prefs: [dom.caches.enabled:true]

View File

@ -1,6 +0,0 @@
[sandboxed-iframes.https.html]
type: testharness
prefs: [dom.caches.enabled:true]
[Sandboxed iframe without allow-same-origin is denied access]
expected: FAIL

View File

@ -4,10 +4,15 @@
window.onmessage = function(e) {
var id = e.data.id;
try {
self.caches;
window.parent.postMessage({id: id, result: 'allowed'}, '*');
var name = 'checkallowed';
self.caches.open(name).then(function (cache) {
self.caches.delete(name);
window.parent.postMessage({id: id, result: 'allowed'}, '*');
}).catch(function(e) {
window.parent.postMessage({id: id, result: 'denied', name: e.name, message: e.message}, '*');
});
} catch (e) {
window.parent.postMessage({id: id, result: 'denied', name: e.name, message: e.message}, '*');
window.parent.postMessage({id: id, result: 'unexpecteddenied', name: e.name, message: e.message}, '*');
}
};
</script>

View File

@ -29,16 +29,22 @@
# include <stdio.h>
# include <stdarg.h>
# define F_OK 00
# define W_OK 02
# define R_OK 04
# ifndef F_OK
# define F_OK 00
# endif
# ifndef W_OK
# define W_OK 02
# endif
# ifndef R_OK
# define R_OK 04
# endif
# define S_ISDIR(s) (((s) & _S_IFMT) == _S_IFDIR)
# define S_ISREG(s) (((s) & _S_IFMT) == _S_IFREG)
# define access _access
# define putenv _putenv
# if _MSC_VER < 1900
# if defined(_MSC_VER) && _MSC_VER < 1900
# define stat _stat
# endif
# define DELETE_DIR L"tobedeleted"

View File

@ -11,10 +11,16 @@
# include <io.h>
typedef WCHAR NS_tchar;
# define NS_main wmain
# define F_OK 00
# define W_OK 02
# define R_OK 04
# if _MSC_VER < 1900
# ifndef F_OK
# define F_OK 00
# endif
# ifndef W_OK
# define W_OK 02
# endif
# ifndef R_OK
# define R_OK 04
# endif
# if defined(_MSC_VER) && _MSC_VER < 1900
# define stat _stat
# endif
# define NS_T(str) L ## str

View File

@ -124,7 +124,7 @@ protected:
nsIFrame* aFrame);
void DrawMenuIcon(CGContextRef cgContext, const CGRect& aRect,
mozilla::EventStates inState, nsIFrame* aFrame,
const NSSize& aIconSize, const NSString* aImageName,
const NSSize& aIconSize, NSString* aImageName,
bool aCenterHorizontally);
void DrawButton(CGContextRef context, ThemeButtonKind inKind,
const HIRect& inBoxRect, bool inIsDefault,

View File

@ -1113,17 +1113,17 @@ static const NSSize kCheckmarkSize = NSMakeSize(11, 11);
static const NSSize kMenuarrowSize = nsCocoaFeatures::OnLionOrLater() ?
NSMakeSize(9, 10) : NSMakeSize(8, 10);
static const NSSize kMenuScrollArrowSize = NSMakeSize(10, 8);
static const NSString* kCheckmarkImage = @"image.MenuOnState";
static const NSString* kMenuarrowRightImage = @"image.MenuSubmenu";
static const NSString* kMenuarrowLeftImage = @"image.MenuSubmenuLeft";
static const NSString* kMenuDownScrollArrowImage = @"image.MenuScrollDown";
static const NSString* kMenuUpScrollArrowImage = @"image.MenuScrollUp";
static NSString* kCheckmarkImage = @"MenuOnState";
static NSString* kMenuarrowRightImage = @"MenuSubmenu";
static NSString* kMenuarrowLeftImage = @"MenuSubmenuLeft";
static NSString* kMenuDownScrollArrowImage = @"MenuScrollDown";
static NSString* kMenuUpScrollArrowImage = @"MenuScrollUp";
static const CGFloat kMenuIconIndent = 6.0f;
void
nsNativeThemeCocoa::DrawMenuIcon(CGContextRef cgContext, const CGRect& aRect,
EventStates inState, nsIFrame* aFrame,
const NSSize& aIconSize, const NSString* aImageName,
const NSSize& aIconSize, NSString* aImageName,
bool aCenterHorizontally)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
@ -1153,10 +1153,16 @@ nsNativeThemeCocoa::DrawMenuIcon(CGContextRef cgContext, const CGRect& aRect,
NSString* backgroundTypeKey = !otherKeysAndValues ? @"kCUIBackgroundTypeMenu" :
!isDisabled && isActive ? @"backgroundTypeDark" : @"backgroundTypeLight";
NSString* imageName = aImageName;
if (!nsCocoaFeatures::OnElCapitanOrLater()) {
// Pre-10.11, image names are prefixed with "image."
imageName = [@"image." stringByAppendingString:aImageName];
}
NSMutableArray* keys = [NSMutableArray arrayWithObjects:@"backgroundTypeKey",
@"imageNameKey", @"state", @"widget", @"is.flipped", nil];
NSMutableArray* values = [NSMutableArray arrayWithObjects: backgroundTypeKey,
aImageName, state, @"image", [NSNumber numberWithBool:YES], nil];
imageName, state, @"image", [NSNumber numberWithBool:YES], nil];
if (otherKeysAndValues) { // Earlier versions used one more key-value pair.
[keys insertObject:@"imageIsGrayscaleKey" atIndex:1];

View File

@ -15,6 +15,7 @@
#include "nsIMemoryReporter.h"
#include "nsIServiceManager.h"
#include "nsIFile.h"
#include "mozilla/Atomics.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Module.h"
#include "mozilla/ModuleLoader.h"
@ -124,7 +125,7 @@ public:
private:
mozilla::Mutex mMutex;
volatile PRThread* mOwnerThread;
mozilla::Atomic<PRThread*, mozilla::Relaxed> mOwnerThread;
};
typedef mozilla::BaseAutoLock<SafeMutex> SafeMutexAutoLock;

View File

@ -460,7 +460,15 @@ nsStorageInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
goto out;
}
mSegmentNum++;
// We have data in the stream, but if mSegmentEnd is zero, then we
// were likely constructed prior to any data being written into
// the stream. Therefore, if mSegmentEnd is non-zero, we should
// move into the next segment; otherwise, we should stay in this
// segment so our input state can be updated and we can properly
// perform the initial read.
if (mSegmentEnd > 0) {
mSegmentNum++;
}
mReadCursor = 0;
mSegmentEnd = XPCOM_MIN(mSegmentSize, available);
availableInSegment = mSegmentEnd;

View File

@ -857,6 +857,17 @@ NS_FillArray(FallibleTArray<char>& aDest, nsIInputStream* aInput,
return rv;
}
bool
NS_InputStreamIsCloneable(nsIInputStream* aSource)
{
if (!aSource) {
return false;
}
nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(aSource);
return cloneable && cloneable->GetCloneable();
}
nsresult
NS_CloneInputStream(nsIInputStream* aSource, nsIInputStream** aCloneOut,
nsIInputStream** aReplacementOut)

View File

@ -264,6 +264,12 @@ extern NS_METHOD
NS_FillArray(FallibleTArray<char>& aDest, nsIInputStream* aInput,
uint32_t aKeep, uint32_t* aNewBytes);
/**
* Return true if the given stream can be directly cloned.
*/
extern bool
NS_InputStreamIsCloneable(nsIInputStream* aSource);
/**
* Clone the provided source stream in the most efficient way possible. This
* first attempts to QI to nsICloneableInputStream to use Clone(). If that is

View File

@ -14,6 +14,7 @@
#include "nsIStorageStream.h"
namespace {
void
WriteData(nsIOutputStream* aOut, nsTArray<char>& aData, uint32_t aNumBytes,
nsACString& aDataWritten)
@ -25,6 +26,7 @@ WriteData(nsIOutputStream* aOut, nsTArray<char>& aData, uint32_t aNumBytes,
}
} // namespace
TEST(StorageStreams, Main)
{
// generate some test data we will write in 4k chunks to the stream
@ -89,3 +91,40 @@ TEST(StorageStreams, Main)
testing::ConsumeAndValidateStream(in, dataWritten);
in = nullptr;
}
TEST(StorageStreams, EarlyInputStream)
{
// generate some test data we will write in 4k chunks to the stream
nsTArray<char> kData;
testing::CreateData(4096, kData);
// track how much data was written so we can compare at the end
nsAutoCString dataWritten;
nsresult rv;
nsCOMPtr<nsIStorageStream> stor;
rv = NS_NewStorageStream(kData.Length(), UINT32_MAX, getter_AddRefs(stor));
EXPECT_TRUE(NS_SUCCEEDED(rv));
// Get input stream before writing data into the output stream
nsCOMPtr<nsIInputStream> in;
rv = stor->NewInputStream(0, getter_AddRefs(in));
EXPECT_TRUE(NS_SUCCEEDED(rv));
// Write data to output stream
nsCOMPtr<nsIOutputStream> out;
rv = stor->GetOutputStream(0, getter_AddRefs(out));
EXPECT_TRUE(NS_SUCCEEDED(rv));
WriteData(out, kData, kData.Length(), dataWritten);
WriteData(out, kData, kData.Length(), dataWritten);
rv = out->Close();
EXPECT_TRUE(NS_SUCCEEDED(rv));
out = nullptr;
// Should be able to consume input stream
testing::ConsumeAndValidateStream(in, dataWritten);
in = nullptr;
}