mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge inbound to central, a=merge
This commit is contained in:
commit
42eb4d290b
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -109,9 +109,6 @@ NotificationController::ScheduleContentInsertion(Accessible* aContainer,
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector: protected
|
||||
|
||||
void
|
||||
NotificationController::ScheduleProcessing()
|
||||
{
|
||||
@ -123,6 +120,9 @@ NotificationController::ScheduleProcessing()
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector: protected
|
||||
|
||||
bool
|
||||
NotificationController::IsUpdatePending()
|
||||
{
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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++)
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
||||
|
@ -1,6 +1,7 @@
|
||||
[DEFAULT]
|
||||
|
||||
[test_ariadialog.html]
|
||||
[test_ariaowns.html]
|
||||
[test_bug852150.xhtml]
|
||||
[test_bug883708.xhtml]
|
||||
[test_bug884251.xhtml]
|
||||
|
234
accessible/tests/mochitest/treeupdate/test_ariaowns.html
Normal file
234
accessible/tests/mochitest/treeupdate/test_ariaowns.html
Normal 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>
|
@ -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)
|
||||
|
@ -200,6 +200,7 @@ public:
|
||||
#endif
|
||||
|
||||
nsIContent* GetNextChild();
|
||||
nsIContent* Parent() const { return mOriginalContent; }
|
||||
|
||||
private:
|
||||
enum IteratorPhase
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
35
dom/workers/test/serviceworkers/redirect_post.sjs
Normal file
35
dom/workers/test/serviceworkers/redirect_post.sjs
Normal 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);
|
||||
}
|
@ -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>
|
||||
|
146
dom/workers/test/serviceworkers/test_file_blob_upload.html
Normal file
146
dom/workers/test/serviceworkers/test_file_blob_upload.html
Normal 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>
|
||||
|
@ -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
|
||||
|
@ -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()->
|
||||
|
@ -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
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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
@ -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) };
|
||||
|
@ -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
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -8,5 +8,5 @@ function foo() {
|
||||
bar();
|
||||
}
|
||||
}
|
||||
for (var i=0; i<10000; i++)
|
||||
for (var i=0; i < 50; i++)
|
||||
foo();
|
||||
|
@ -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>
|
||||
|
420
js/src/jit/mips-shared/Lowering-mips-shared.cpp
Normal file
420
js/src/jit/mips-shared/Lowering-mips-shared.cpp
Normal 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);
|
||||
}
|
98
js/src/jit/mips-shared/Lowering-mips-shared.h
Normal file
98
js/src/jit/mips-shared/Lowering-mips-shared.h
Normal 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 */
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 += [
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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__);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -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'] % {
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -0,0 +1 @@
|
||||
prefs: [dom.caches.enabled:true]
|
@ -1,3 +0,0 @@
|
||||
[cache-add.https.html]
|
||||
type: testharness
|
||||
prefs: [dom.caches.enabled:true]
|
@ -1,3 +0,0 @@
|
||||
[cache-delete.https.html]
|
||||
type: testharness
|
||||
prefs: [dom.caches.enabled:true]
|
@ -1,3 +0,0 @@
|
||||
[cache-storage-keys.https.html]
|
||||
type: testharness
|
||||
prefs: [dom.caches.enabled:true]
|
@ -1,3 +0,0 @@
|
||||
[cache-storage.https.html]
|
||||
type: testharness
|
||||
prefs: [dom.caches.enabled:true]
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user