зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland
This commit is contained in:
Коммит
17a7875e17
|
@ -42,7 +42,23 @@ AccEvent::AccEvent(uint32_t aEventType, Accessible* aAccessible,
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccEvent cycle collection
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(AccEvent, mAccessible)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(AccEvent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AccEvent)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessible)
|
||||
if (AccTreeMutationEvent* tmEvent = downcast_accEvent(tmp)) {
|
||||
tmEvent->SetNextEvent(nullptr);
|
||||
tmEvent->SetPrevEvent(nullptr);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AccEvent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessible)
|
||||
if (AccTreeMutationEvent* tmEvent = downcast_accEvent(tmp)) {
|
||||
CycleCollectionNoteChild(cb, tmEvent->NextEvent(), "mNext");
|
||||
CycleCollectionNoteChild(cb, tmEvent->PrevEvent(), "mPrevEvent");
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AccEvent, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AccEvent, Release)
|
||||
|
|
|
@ -94,6 +94,7 @@ public:
|
|||
eGenericEvent,
|
||||
eStateChangeEvent,
|
||||
eTextChangeEvent,
|
||||
eTreeMutationEvent,
|
||||
eMutationEvent,
|
||||
eReorderEvent,
|
||||
eHideEvent,
|
||||
|
@ -129,6 +130,7 @@ protected:
|
|||
friend class EventQueue;
|
||||
friend class EventTree;
|
||||
friend class ::nsEventShell;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
|
@ -200,17 +202,51 @@ private:
|
|||
nsString mModifiedText;
|
||||
|
||||
friend class EventTree;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
/**
|
||||
* A base class for events related to tree mutation, either an AccMutation
|
||||
* event, or an AccReorderEvent.
|
||||
*/
|
||||
class AccTreeMutationEvent : public AccEvent
|
||||
{
|
||||
public:
|
||||
AccTreeMutationEvent(uint32_t aEventType, Accessible* aTarget) :
|
||||
AccEvent(aEventType, aTarget, eAutoDetect, eCoalesceReorder), mGeneration(0) {}
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eTreeMutationEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eTreeMutationEvent);
|
||||
}
|
||||
|
||||
void SetNextEvent(AccTreeMutationEvent* aNext) { mNextEvent = aNext; }
|
||||
void SetPrevEvent(AccTreeMutationEvent* aPrev) { mPrevEvent = aPrev; }
|
||||
AccTreeMutationEvent* NextEvent() const { return mNextEvent; }
|
||||
AccTreeMutationEvent* PrevEvent() const { return mPrevEvent; }
|
||||
|
||||
/**
|
||||
* A sequence number to know when this event was fired.
|
||||
*/
|
||||
uint32_t EventGeneration() const { return mGeneration; }
|
||||
void SetEventGeneration(uint32_t aGeneration) { mGeneration = aGeneration; }
|
||||
|
||||
private:
|
||||
RefPtr<AccTreeMutationEvent> mNextEvent;
|
||||
RefPtr<AccTreeMutationEvent> mPrevEvent;
|
||||
uint32_t mGeneration;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for show and hide accessible events.
|
||||
*/
|
||||
class AccMutationEvent: public AccEvent
|
||||
class AccMutationEvent: public AccTreeMutationEvent
|
||||
{
|
||||
public:
|
||||
AccMutationEvent(uint32_t aEventType, Accessible* aTarget) :
|
||||
AccEvent(aEventType, aTarget, eAutoDetect, eCoalesceReorder)
|
||||
AccTreeMutationEvent(aEventType, aTarget)
|
||||
{
|
||||
// Don't coalesce these since they are coalesced by reorder event. Coalesce
|
||||
// contained text change events.
|
||||
|
@ -222,7 +258,7 @@ public:
|
|||
static const EventGroup kEventGroup = eMutationEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eMutationEvent);
|
||||
return AccTreeMutationEvent::GetEventGroups() | (1U << eMutationEvent);
|
||||
}
|
||||
|
||||
// MutationEvent
|
||||
|
@ -237,6 +273,7 @@ protected:
|
|||
RefPtr<AccTextChangeEvent> mTextChangeEvent;
|
||||
|
||||
friend class EventTree;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
|
@ -267,6 +304,7 @@ protected:
|
|||
RefPtr<Accessible> mPrevSibling;
|
||||
|
||||
friend class EventTree;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
|
@ -298,19 +336,18 @@ private:
|
|||
/**
|
||||
* Class for reorder accessible event. Takes care about
|
||||
*/
|
||||
class AccReorderEvent : public AccEvent
|
||||
class AccReorderEvent : public AccTreeMutationEvent
|
||||
{
|
||||
public:
|
||||
explicit AccReorderEvent(Accessible* aTarget) :
|
||||
AccEvent(::nsIAccessibleEvent::EVENT_REORDER, aTarget,
|
||||
eAutoDetect, eCoalesceReorder) { }
|
||||
AccTreeMutationEvent(::nsIAccessibleEvent::EVENT_REORDER, aTarget) { }
|
||||
virtual ~AccReorderEvent() { }
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eReorderEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eReorderEvent);
|
||||
return AccTreeMutationEvent::GetEventGroups() | (1U << eReorderEvent);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@ EventTree* const TreeMutation::kNoEventTree = reinterpret_cast<EventTree*>(-1);
|
|||
TreeMutation::TreeMutation(Accessible* aParent, bool aNoEvents) :
|
||||
mParent(aParent), mStartIdx(UINT32_MAX),
|
||||
mStateFlagsCopy(mParent->mStateFlags),
|
||||
mEventTree(aNoEvents ? kNoEventTree : nullptr)
|
||||
mEventTree(aNoEvents ? kNoEventTree : nullptr),
|
||||
mQueueEvents(!aNoEvents)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mIsDone = false;
|
||||
|
@ -62,17 +63,14 @@ TreeMutation::AfterInsertion(Accessible* aChild)
|
|||
mStartIdx = aChild->mIndexInParent + 1;
|
||||
}
|
||||
|
||||
if (!mEventTree) {
|
||||
mEventTree = Controller()->QueueMutation(mParent);
|
||||
if (!mEventTree) {
|
||||
mEventTree = kNoEventTree;
|
||||
}
|
||||
if (!mQueueEvents) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mEventTree != kNoEventTree) {
|
||||
mEventTree->Shown(aChild);
|
||||
Controller()->QueueNameChange(aChild);
|
||||
}
|
||||
RefPtr<AccShowEvent> ev = new AccShowEvent(aChild);
|
||||
DebugOnly<bool> added = Controller()->QueueMutationEvent(ev);
|
||||
MOZ_ASSERT(added);
|
||||
aChild->SetShowEventTarget(true);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -84,16 +82,13 @@ TreeMutation::BeforeRemoval(Accessible* aChild, bool aNoShutdown)
|
|||
mStartIdx = aChild->mIndexInParent;
|
||||
}
|
||||
|
||||
if (!mEventTree) {
|
||||
mEventTree = Controller()->QueueMutation(mParent);
|
||||
if (!mEventTree) {
|
||||
mEventTree = kNoEventTree;
|
||||
}
|
||||
if (!mQueueEvents) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mEventTree != kNoEventTree) {
|
||||
mEventTree->Hidden(aChild, !aNoShutdown);
|
||||
Controller()->QueueNameChange(aChild);
|
||||
RefPtr<AccHideEvent> ev = new AccHideEvent(aChild, !aNoShutdown);
|
||||
if (Controller()->QueueMutationEvent(ev)) {
|
||||
aChild->SetHideEventTarget(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,11 @@ private:
|
|||
uint32_t mStateFlagsCopy;
|
||||
EventTree* mEventTree;
|
||||
|
||||
/*
|
||||
* True if mutation events should be queued.
|
||||
*/
|
||||
bool mQueueEvents;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mIsDone;
|
||||
#endif
|
||||
|
@ -67,15 +72,19 @@ public:
|
|||
mFireReorder(aFireReorder) { }
|
||||
~EventTree() { Clear(); }
|
||||
|
||||
void Shown(Accessible* aChild);
|
||||
|
||||
void Hidden(Accessible* aChild, bool aNeedsShutdown = true);
|
||||
void Shown(Accessible* aTarget);
|
||||
void Hidden(Accessible*, bool);
|
||||
|
||||
/**
|
||||
* Return an event tree node for the given accessible.
|
||||
*/
|
||||
const EventTree* Find(const Accessible* aContainer) const;
|
||||
|
||||
/**
|
||||
* Add a mutation event to this event tree.
|
||||
*/
|
||||
void Mutated(AccMutationEvent* aEv);
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
void Log(uint32_t aLevel = UINT32_MAX) const;
|
||||
#endif
|
||||
|
@ -91,7 +100,6 @@ private:
|
|||
*/
|
||||
EventTree* FindOrInsert(Accessible* aContainer);
|
||||
|
||||
void Mutated(AccMutationEvent* aEv);
|
||||
void Clear();
|
||||
|
||||
UniquePtr<EventTree> mFirst;
|
||||
|
|
|
@ -24,7 +24,7 @@ using namespace mozilla::a11y;
|
|||
NotificationController::NotificationController(DocAccessible* aDocument,
|
||||
nsIPresShell* aPresShell) :
|
||||
EventQueue(aDocument), mObservingState(eNotObservingRefresh),
|
||||
mPresShell(aPresShell)
|
||||
mPresShell(aPresShell), mEventGeneration(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mMoveGuardOnStack = false;
|
||||
|
@ -114,6 +114,289 @@ NotificationController::QueueMutation(Accessible* aContainer)
|
|||
return tree;
|
||||
}
|
||||
|
||||
bool
|
||||
NotificationController::QueueMutationEvent(AccTreeMutationEvent* aEvent)
|
||||
{
|
||||
// We have to allow there to be a hide and then a show event for a target
|
||||
// because of targets getting moved. However we need to coalesce a show and
|
||||
// then a hide for a target which means we need to check for that here.
|
||||
if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE &&
|
||||
aEvent->GetAccessible()->ShowEventTarget()) {
|
||||
AccTreeMutationEvent* showEvent = mMutationMap.GetEvent(aEvent->GetAccessible(), EventMap::ShowEvent);
|
||||
DropMutationEvent(showEvent);
|
||||
return false;
|
||||
}
|
||||
|
||||
AccMutationEvent* mutEvent = downcast_accEvent(aEvent);
|
||||
mEventGeneration++;
|
||||
mutEvent->SetEventGeneration(mEventGeneration);
|
||||
|
||||
if (!mFirstMutationEvent) {
|
||||
mFirstMutationEvent = aEvent;
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
if (mLastMutationEvent) {
|
||||
NS_ASSERTION(!mLastMutationEvent->NextEvent(), "why isn't the last event the end?");
|
||||
mLastMutationEvent->SetNextEvent(aEvent);
|
||||
}
|
||||
|
||||
aEvent->SetPrevEvent(mLastMutationEvent);
|
||||
mLastMutationEvent = aEvent;
|
||||
mMutationMap.PutEvent(aEvent);
|
||||
|
||||
// Because we could be hiding the target of a show event we need to get rid
|
||||
// of any such events. It may be possible to do less than coallesce all
|
||||
// events, however that is easiest.
|
||||
if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
CoalesceMutationEvents();
|
||||
|
||||
// mLastMutationEvent will point to something other than aEvent if and only
|
||||
// if aEvent was just coalesced away. In that case a parent accessible
|
||||
// must already have the required reorder and text change events so we are
|
||||
// done here.
|
||||
if (mLastMutationEvent != aEvent) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to fire a reorder event after all of the events targeted at shown or
|
||||
// hidden children of a container. So either queue a new one, or move an
|
||||
// existing one to the end of the queue if the container already has a
|
||||
// reorder event.
|
||||
Accessible* target = aEvent->GetAccessible();
|
||||
Accessible* container = aEvent->GetAccessible()->Parent();
|
||||
RefPtr<AccReorderEvent> reorder;
|
||||
if (!container->ReorderEventTarget()) {
|
||||
reorder = new AccReorderEvent(container);
|
||||
container->SetReorderEventTarget(true);
|
||||
mMutationMap.PutEvent(reorder);
|
||||
|
||||
// Since this is the first child of container that is changing, the name of
|
||||
// container may be changing.
|
||||
QueueNameChange(target);
|
||||
} else {
|
||||
AccReorderEvent* event = downcast_accEvent(mMutationMap.GetEvent(container, EventMap::ReorderEvent));
|
||||
reorder = event;
|
||||
if (mFirstMutationEvent == event) {
|
||||
mFirstMutationEvent = event->NextEvent();
|
||||
} else {
|
||||
event->PrevEvent()->SetNextEvent(event->NextEvent());
|
||||
}
|
||||
|
||||
event->NextEvent()->SetPrevEvent(event->PrevEvent());
|
||||
event->SetNextEvent(nullptr);
|
||||
}
|
||||
|
||||
reorder->SetEventGeneration(mEventGeneration);
|
||||
reorder->SetPrevEvent(mLastMutationEvent);
|
||||
mLastMutationEvent->SetNextEvent(reorder);
|
||||
mLastMutationEvent = reorder;
|
||||
|
||||
// It is not possible to have a text change event for something other than a
|
||||
// hyper text accessible.
|
||||
if (!container->IsHyperText()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mutEvent);
|
||||
|
||||
nsString text;
|
||||
aEvent->GetAccessible()->AppendTextTo(text);
|
||||
if (text.IsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t offset = container->AsHyperText()->GetChildOffset(target);
|
||||
AccTreeMutationEvent* prevEvent = aEvent->PrevEvent();
|
||||
while (prevEvent && prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
prevEvent = prevEvent->PrevEvent();
|
||||
}
|
||||
|
||||
if (prevEvent && prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE &&
|
||||
mutEvent->IsHide()) {
|
||||
AccHideEvent* prevHide = downcast_accEvent(prevEvent);
|
||||
AccTextChangeEvent* prevTextChange = prevHide->mTextChangeEvent;
|
||||
if (prevTextChange) {
|
||||
if (prevHide->mNextSibling == target) {
|
||||
target->AppendTextTo(prevTextChange->mModifiedText);
|
||||
} else if (prevHide->mPrevSibling == target) {
|
||||
nsString temp;
|
||||
target->AppendTextTo(temp);
|
||||
|
||||
uint32_t extraLen = temp.Length();
|
||||
temp += prevTextChange->mModifiedText;;
|
||||
prevTextChange->mModifiedText = temp;
|
||||
prevTextChange->mStart -= extraLen;
|
||||
}
|
||||
|
||||
prevHide->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
|
||||
}
|
||||
} else if (prevEvent && mutEvent->IsShow() &&
|
||||
prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
AccShowEvent* prevShow = downcast_accEvent(prevEvent);
|
||||
AccTextChangeEvent* prevTextChange = prevShow->mTextChangeEvent;
|
||||
if (prevTextChange) {
|
||||
int32_t index = target->IndexInParent();
|
||||
int32_t prevIndex = prevShow->GetAccessible()->IndexInParent();
|
||||
if (prevIndex + 1 == index) {
|
||||
target->AppendTextTo(prevTextChange->mModifiedText);
|
||||
} else if (index + 1 == prevIndex) {
|
||||
nsString temp;
|
||||
target->AppendTextTo(temp);
|
||||
prevTextChange->mStart -= temp.Length();
|
||||
temp += prevTextChange->mModifiedText;
|
||||
prevTextChange->mModifiedText = temp;
|
||||
}
|
||||
|
||||
prevShow->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mutEvent->mTextChangeEvent) {
|
||||
mutEvent->mTextChangeEvent =
|
||||
new AccTextChangeEvent(container, offset, text, mutEvent->IsShow(),
|
||||
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::DropMutationEvent(AccTreeMutationEvent* aEvent)
|
||||
{
|
||||
// unset the event bits since the event isn't being fired any more.
|
||||
if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
aEvent->GetAccessible()->SetReorderEventTarget(false);
|
||||
} else if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
aEvent->GetAccessible()->SetShowEventTarget(false);
|
||||
} else {
|
||||
AccHideEvent* hideEvent = downcast_accEvent(aEvent);
|
||||
MOZ_ASSERT(hideEvent);
|
||||
|
||||
if (hideEvent->NeedsShutdown()) {
|
||||
mDocument->ShutdownChildrenInSubtree(aEvent->GetAccessible());
|
||||
}
|
||||
}
|
||||
|
||||
// Do the work to splice the event out of the list.
|
||||
if (mFirstMutationEvent == aEvent) {
|
||||
mFirstMutationEvent = aEvent->NextEvent();
|
||||
} else {
|
||||
aEvent->PrevEvent()->SetNextEvent(aEvent->NextEvent());
|
||||
}
|
||||
|
||||
if (mLastMutationEvent == aEvent) {
|
||||
mLastMutationEvent = aEvent->PrevEvent();
|
||||
} else {
|
||||
aEvent->NextEvent()->SetPrevEvent(aEvent->PrevEvent());
|
||||
}
|
||||
|
||||
aEvent->SetPrevEvent(nullptr);
|
||||
aEvent->SetNextEvent(nullptr);
|
||||
mMutationMap.RemoveEvent(aEvent);
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::CoalesceMutationEvents()
|
||||
{
|
||||
AccTreeMutationEvent* event = mFirstMutationEvent;
|
||||
while (event) {
|
||||
AccTreeMutationEvent* nextEvent = event->NextEvent();
|
||||
uint32_t eventType = event->GetEventType();
|
||||
if (event->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
Accessible* acc = event->GetAccessible();
|
||||
while (acc) {
|
||||
if (acc->IsDoc()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// if a parent of the reorder event's target is being hidden that
|
||||
// hide event's target must have a parent that is also a reorder event
|
||||
// target. That means we don't need this reorder event.
|
||||
if (acc->HideEventTarget()) {
|
||||
DropMutationEvent(event);
|
||||
break;
|
||||
}
|
||||
|
||||
Accessible* parent = acc->Parent();
|
||||
if (parent->ReorderEventTarget()) {
|
||||
AccReorderEvent* reorder = downcast_accEvent(mMutationMap.GetEvent(parent, EventMap::ReorderEvent));
|
||||
|
||||
// We want to make sure that a reorder event comes after any show or
|
||||
// hide events targeted at the children of its target. We keep the
|
||||
// invariant that event generation goes up as you are farther in the
|
||||
// queue, so we want to use the spot of the event with the higher
|
||||
// generation number, and keep that generation number.
|
||||
if (reorder && reorder->EventGeneration() < event->EventGeneration()) {
|
||||
// There really should be a show or hide event before the first
|
||||
// reorder event.
|
||||
if (reorder->PrevEvent()) {
|
||||
reorder->PrevEvent()->SetNextEvent(reorder->NextEvent());
|
||||
} else {
|
||||
mFirstMutationEvent = reorder->NextEvent();
|
||||
}
|
||||
|
||||
reorder->NextEvent()->SetPrevEvent(reorder->PrevEvent());
|
||||
event->PrevEvent()->SetNextEvent(reorder);
|
||||
reorder->SetPrevEvent(event->PrevEvent());
|
||||
event->SetPrevEvent(reorder);
|
||||
reorder->SetNextEvent(event);
|
||||
reorder->SetEventGeneration(event->EventGeneration());
|
||||
}
|
||||
DropMutationEvent(event);
|
||||
break;
|
||||
}
|
||||
|
||||
acc = parent;
|
||||
}
|
||||
} else if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
Accessible* parent = event->GetAccessible()->Parent();
|
||||
while (parent) {
|
||||
if (parent->IsDoc()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// if the parent of a show event is being either shown or hidden then
|
||||
// we don't need to fire a show event for a subtree of that change.
|
||||
if (parent->ShowEventTarget() || parent->HideEventTarget()) {
|
||||
DropMutationEvent(event);
|
||||
break;
|
||||
}
|
||||
|
||||
parent = parent->Parent();
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(eventType == nsIAccessibleEvent::EVENT_HIDE, "mutation event list has an invalid event");
|
||||
|
||||
AccHideEvent* hideEvent = downcast_accEvent(event);
|
||||
Accessible* parent = hideEvent->Parent();
|
||||
while (parent) {
|
||||
if (parent->IsDoc()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (parent->HideEventTarget()) {
|
||||
DropMutationEvent(event);
|
||||
break;
|
||||
}
|
||||
|
||||
if (parent->ShowEventTarget()) {
|
||||
AccShowEvent* showEvent = downcast_accEvent(mMutationMap.GetEvent(parent, EventMap::ShowEvent));
|
||||
if (showEvent->EventGeneration() < hideEvent->EventGeneration()) {
|
||||
DropMutationEvent(hideEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parent = parent->Parent();
|
||||
}
|
||||
}
|
||||
|
||||
event = nextEvent;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::ScheduleChildDocBinding(DocAccessible* aDocument)
|
||||
{
|
||||
|
@ -172,6 +455,128 @@ NotificationController::IsUpdatePending()
|
|||
!mDocument->HasLoadState(DocAccessible::eTreeConstructed);
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::ProcessMutationEvents()
|
||||
{
|
||||
// there is no reason to fire a hide event for a child of a show event
|
||||
// target. That can happen if something is inserted into the tree and
|
||||
// removed before the next refresh driver tick, but it should not be
|
||||
// observable outside gecko so it should be safe to coalesce away any such
|
||||
// events. This means that it should be fine to fire all of the hide events
|
||||
// first, and then deal with any shown subtrees.
|
||||
for (AccTreeMutationEvent* event = mFirstMutationEvent;
|
||||
event; event = event->NextEvent()) {
|
||||
if (event->GetEventType() != nsIAccessibleEvent::EVENT_HIDE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsEventShell::FireEvent(event);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
AccMutationEvent* mutEvent = downcast_accEvent(event);
|
||||
if (mutEvent->mTextChangeEvent) {
|
||||
nsEventShell::FireEvent(mutEvent->mTextChangeEvent);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fire menupopup end event before a hide event if a menu goes away.
|
||||
|
||||
// XXX: We don't look into children of hidden subtree to find hiding
|
||||
// menupopup (as we did prior bug 570275) because we don't do that when
|
||||
// menu is showing (and that's impossible until bug 606924 is fixed).
|
||||
// Nevertheless we should do this at least because layout coalesces
|
||||
// the changes before our processing and we may miss some menupopup
|
||||
// events. Now we just want to be consistent in content insertion/removal
|
||||
// handling.
|
||||
if (event->mAccessible->ARIARole() == roles::MENUPOPUP) {
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
|
||||
event->mAccessible);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AccHideEvent* hideEvent = downcast_accEvent(event);
|
||||
if (hideEvent->NeedsShutdown()) {
|
||||
mDocument->ShutdownChildrenInSubtree(event->mAccessible);
|
||||
}
|
||||
}
|
||||
|
||||
// Group the show events by the parent of their target.
|
||||
nsDataHashtable<nsPtrHashKey<Accessible>, nsTArray<AccTreeMutationEvent*>> showEvents;
|
||||
for (AccTreeMutationEvent* event = mFirstMutationEvent;
|
||||
event; event = event->NextEvent()) {
|
||||
if (event->GetEventType() != nsIAccessibleEvent::EVENT_SHOW) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Accessible* parent = event->GetAccessible()->Parent();
|
||||
showEvents.GetOrInsert(parent).AppendElement(event);
|
||||
}
|
||||
|
||||
// We need to fire show events for the children of an accessible in the order
|
||||
// of their indices at this point. So sort each set of events for the same
|
||||
// container by the index of their target.
|
||||
for (auto iter = showEvents.Iter(); !iter.Done(); iter.Next()) {
|
||||
struct AccIdxComparator {
|
||||
bool LessThan(const AccTreeMutationEvent* a, const AccTreeMutationEvent* b) const
|
||||
{
|
||||
int32_t aIdx = a->GetAccessible()->IndexInParent();
|
||||
int32_t bIdx = b->GetAccessible()->IndexInParent();
|
||||
MOZ_ASSERT(aIdx >= 0 && bIdx >= 0 && aIdx != bIdx);
|
||||
return aIdx < bIdx;
|
||||
}
|
||||
bool Equals(const AccTreeMutationEvent* a, const AccTreeMutationEvent* b) const
|
||||
{
|
||||
DebugOnly<int32_t> aIdx = a->GetAccessible()->IndexInParent();
|
||||
DebugOnly<int32_t> bIdx = b->GetAccessible()->IndexInParent();
|
||||
MOZ_ASSERT(aIdx >= 0 && bIdx >= 0 && aIdx != bIdx);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
nsTArray<AccTreeMutationEvent*>& events = iter.Data();
|
||||
events.Sort(AccIdxComparator());
|
||||
for (AccTreeMutationEvent* event: events) {
|
||||
nsEventShell::FireEvent(event);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
AccMutationEvent* mutEvent = downcast_accEvent(event);
|
||||
if (mutEvent->mTextChangeEvent) {
|
||||
nsEventShell::FireEvent(mutEvent->mTextChangeEvent);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we can fire the reorder events after all the show and hide events.
|
||||
for (AccTreeMutationEvent* event = mFirstMutationEvent;
|
||||
event; event = event->NextEvent()) {
|
||||
if (event->GetEventType() != nsIAccessibleEvent::EVENT_REORDER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsEventShell::FireEvent(event);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
Accessible* target = event->GetAccessible();
|
||||
target->Document()->MaybeNotifyOfValueChange(target);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector: private
|
||||
|
||||
|
@ -397,9 +802,39 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||
// events causes script to run.
|
||||
mObservingState = eRefreshProcessing;
|
||||
|
||||
RefPtr<DocAccessible> deathGrip(mDocument);
|
||||
mEventTree.Process(deathGrip);
|
||||
deathGrip = nullptr;
|
||||
CoalesceMutationEvents();
|
||||
ProcessMutationEvents();
|
||||
mEventGeneration = 0;
|
||||
|
||||
// Now that we are done with them get rid of the events we fired.
|
||||
RefPtr<AccTreeMutationEvent> mutEvent = Move(mFirstMutationEvent);
|
||||
mLastMutationEvent = nullptr;
|
||||
mFirstMutationEvent = nullptr;
|
||||
while (mutEvent) {
|
||||
RefPtr<AccTreeMutationEvent> nextEvent = mutEvent->NextEvent();
|
||||
Accessible* target = mutEvent->GetAccessible();
|
||||
|
||||
// We need to be careful here, while it may seem that we can simply 0 all
|
||||
// the pending event bits that is not true. Because accessibles may be
|
||||
// reparented they may be the target of both a hide event and a show event
|
||||
// at the same time.
|
||||
if (mutEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
target->SetShowEventTarget(false);
|
||||
}
|
||||
|
||||
if (mutEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
target->SetHideEventTarget(false);
|
||||
}
|
||||
|
||||
// However it is not possible for a reorder event target to also be the
|
||||
// target of a show or hide, so we can just zero that.
|
||||
target->SetReorderEventTarget(false);
|
||||
|
||||
mutEvent->SetPrevEvent(nullptr);
|
||||
mutEvent->SetNextEvent(nullptr);
|
||||
mMutationMap.RemoveEvent(mutEvent);
|
||||
mutEvent = nextEvent;
|
||||
}
|
||||
|
||||
ProcessEventQueue();
|
||||
|
||||
|
@ -449,3 +884,52 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||
mObservingState = eNotObservingRefresh;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::EventMap::PutEvent(AccTreeMutationEvent* aEvent)
|
||||
{
|
||||
EventType type = GetEventType(aEvent);
|
||||
uint64_t addr = reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
|
||||
MOZ_ASSERT((addr & 0x3) == 0, "accessible is not 4 byte aligned");
|
||||
addr |= type;
|
||||
mTable.Put(addr, aEvent);
|
||||
}
|
||||
|
||||
AccTreeMutationEvent*
|
||||
NotificationController::EventMap::GetEvent(Accessible* aTarget, EventType aType)
|
||||
{
|
||||
uint64_t addr = reinterpret_cast<uintptr_t>(aTarget);
|
||||
MOZ_ASSERT((addr & 0x3) == 0, "target is not 4 byte aligned");
|
||||
|
||||
addr |= aType;
|
||||
return mTable.GetWeak(addr);
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::EventMap::RemoveEvent(AccTreeMutationEvent* aEvent)
|
||||
{
|
||||
EventType type = GetEventType(aEvent);
|
||||
uint64_t addr = reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
|
||||
MOZ_ASSERT((addr & 0x3) == 0, "accessible is not 4 byte aligned");
|
||||
addr |= type;
|
||||
|
||||
MOZ_ASSERT(mTable.GetWeak(addr) == aEvent, "mTable has the wrong event");
|
||||
mTable.Remove(addr);
|
||||
}
|
||||
|
||||
NotificationController::EventMap::EventType
|
||||
NotificationController::EventMap::GetEventType(AccTreeMutationEvent* aEvent)
|
||||
{
|
||||
switch(aEvent->GetEventType())
|
||||
{
|
||||
case nsIAccessibleEvent::EVENT_SHOW:
|
||||
return ShowEvent;
|
||||
case nsIAccessibleEvent::EVENT_HIDE:
|
||||
return HideEvent;
|
||||
case nsIAccessibleEvent::EVENT_REORDER:
|
||||
return ReorderEvent;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("event has invalid type");
|
||||
return ShowEvent;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,6 +159,17 @@ public:
|
|||
const EventTree& RootEventTree() const { return mEventTree; };
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Queue a mutation event to emit if not coalesced away. Returns true if the
|
||||
* event was queued and has not yet been coalesced.
|
||||
*/
|
||||
bool QueueMutationEvent(AccTreeMutationEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Coalesce all queued mutation events.
|
||||
*/
|
||||
void CoalesceMutationEvents();
|
||||
|
||||
/**
|
||||
* Schedule binding the child document to the tree of this document.
|
||||
*/
|
||||
|
@ -291,6 +302,16 @@ private:
|
|||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* get rid of a mutation event that is no longer necessary.
|
||||
*/
|
||||
void DropMutationEvent(AccTreeMutationEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Fire all necessary mutation events.
|
||||
*/
|
||||
void ProcessMutationEvents();
|
||||
|
||||
/**
|
||||
* Indicates whether we're waiting on an event queue processing from our
|
||||
* notification controller to flush events.
|
||||
|
@ -376,6 +397,40 @@ private:
|
|||
|
||||
friend class MoveGuard;
|
||||
friend class EventTree;
|
||||
|
||||
/**
|
||||
* A list of all mutation events we may want to emit. Ordered from the first
|
||||
* event that should be emitted to the last one to emit.
|
||||
*/
|
||||
RefPtr<AccTreeMutationEvent> mFirstMutationEvent;
|
||||
RefPtr<AccTreeMutationEvent> mLastMutationEvent;
|
||||
|
||||
/**
|
||||
* A class to map an accessible and event type to an event.
|
||||
*/
|
||||
class EventMap
|
||||
{
|
||||
public:
|
||||
enum EventType
|
||||
{
|
||||
ShowEvent = 0x0,
|
||||
HideEvent = 0x1,
|
||||
ReorderEvent = 0x2,
|
||||
};
|
||||
|
||||
void PutEvent(AccTreeMutationEvent* aEvent);
|
||||
AccTreeMutationEvent* GetEvent(Accessible* aTarget, EventType aType);
|
||||
void RemoveEvent(AccTreeMutationEvent* aEvent);
|
||||
void Clear() { mTable.Clear(); }
|
||||
|
||||
private:
|
||||
EventType GetEventType(AccTreeMutationEvent* aEvent);
|
||||
|
||||
nsRefPtrHashtable<nsUint64HashKey, AccTreeMutationEvent> mTable;
|
||||
};
|
||||
|
||||
EventMap mMutationMap;
|
||||
uint32_t mEventGeneration;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
|
|
@ -109,7 +109,8 @@ Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc) :
|
|||
mContent(aContent), mDoc(aDoc),
|
||||
mParent(nullptr), mIndexInParent(-1),
|
||||
mRoleMapEntryIndex(aria::NO_ROLE_MAP_ENTRY_INDEX),
|
||||
mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0)
|
||||
mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0),
|
||||
mReorderEventTarget(false), mShowEventTarget(false), mHideEventTarget(false)
|
||||
{
|
||||
mBits.groupInfo = nullptr;
|
||||
mInt.mIndexOfEmbeddedChild = -1;
|
||||
|
@ -2157,9 +2158,9 @@ Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
|
|||
"No move, same index");
|
||||
MOZ_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given");
|
||||
|
||||
EventTree* eventTree = mDoc->Controller()->QueueMutation(this);
|
||||
if (eventTree) {
|
||||
eventTree->Hidden(aChild, false);
|
||||
RefPtr<AccHideEvent> hideEvent = new AccHideEvent(aChild, false);
|
||||
if (mDoc->Controller()->QueueMutationEvent(hideEvent)) {
|
||||
aChild->SetHideEventTarget(true);
|
||||
}
|
||||
|
||||
mEmbeddedObjCollector = nullptr;
|
||||
|
@ -2191,10 +2192,10 @@ Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
|
|||
mChildren[idx]->mInt.mIndexOfEmbeddedChild = -1;
|
||||
}
|
||||
|
||||
if (eventTree) {
|
||||
eventTree->Shown(aChild);
|
||||
mDoc->Controller()->QueueNameChange(aChild);
|
||||
}
|
||||
RefPtr<AccShowEvent> showEvent = new AccShowEvent(aChild);
|
||||
DebugOnly<bool> added = mDoc->Controller()->QueueMutationEvent(showEvent);
|
||||
MOZ_ASSERT(added);
|
||||
aChild->SetShowEventTarget(true);
|
||||
}
|
||||
|
||||
Accessible*
|
||||
|
|
|
@ -956,6 +956,36 @@ public:
|
|||
*/
|
||||
bool IsInsideAlert() const { return mContextFlags & eInsideAlert; }
|
||||
|
||||
/**
|
||||
* Return true if there is a pending reorder event for this accessible.
|
||||
*/
|
||||
bool ReorderEventTarget() const { return mReorderEventTarget; }
|
||||
|
||||
/**
|
||||
* Return true if there is a pending show event for this accessible.
|
||||
*/
|
||||
bool ShowEventTarget() const { return mShowEventTarget; }
|
||||
|
||||
/**
|
||||
* Return true if there is a pending hide event for this accessible.
|
||||
*/
|
||||
bool HideEventTarget() const { return mHideEventTarget; }
|
||||
|
||||
/**
|
||||
* Set if there is a pending reorder event for this accessible.
|
||||
*/
|
||||
void SetReorderEventTarget(bool aTarget) { mReorderEventTarget = aTarget; }
|
||||
|
||||
/**
|
||||
* Set if this accessible is a show event target.
|
||||
*/
|
||||
void SetShowEventTarget(bool aTarget) { mShowEventTarget = aTarget; }
|
||||
|
||||
/**
|
||||
* Set if this accessible is a hide event target.
|
||||
*/
|
||||
void SetHideEventTarget(bool aTarget) { mHideEventTarget = aTarget; }
|
||||
|
||||
protected:
|
||||
virtual ~Accessible();
|
||||
|
||||
|
@ -1132,6 +1162,9 @@ protected:
|
|||
uint32_t mContextFlags : kContextFlagsBits;
|
||||
uint32_t mType : kTypeBits;
|
||||
uint32_t mGenericTypes : kGenericTypesBits;
|
||||
uint32_t mReorderEventTarget : 1;
|
||||
uint32_t mShowEventTarget : 1;
|
||||
uint32_t mHideEventTarget : 1;
|
||||
|
||||
void StaticAsserts() const;
|
||||
|
||||
|
|
|
@ -1860,8 +1860,8 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer,
|
|||
"container", aContainer, "child", iter.Child(), nullptr);
|
||||
#endif
|
||||
|
||||
mt.AfterInsertion(iter.Child());
|
||||
CreateSubtree(iter.Child());
|
||||
mt.AfterInsertion(iter.Child());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1907,10 +1907,10 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer, nsIContent* aNode)
|
|||
if (!aContainer->InsertAfter(child, walker.Prev())) {
|
||||
return;
|
||||
}
|
||||
CreateSubtree(child);
|
||||
mt.AfterInsertion(child);
|
||||
mt.Done();
|
||||
|
||||
CreateSubtree(child);
|
||||
FireEventsOnInsertion(aContainer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent)
|
|||
{
|
||||
Accessible* parent = aShowEvent->Parent();
|
||||
uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID());
|
||||
uint32_t idxInParent = aShowEvent->InsertionIndex();
|
||||
uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent();
|
||||
nsTArray<AccessibleData> shownTree;
|
||||
ShowEventData data(parentID, idxInParent, shownTree);
|
||||
SerializeTree(aShowEvent->GetAccessible(), data.NewTree());
|
||||
|
|
|
@ -1705,7 +1705,9 @@ function invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg, aIsAsync)
|
|||
*/
|
||||
function orderChecker()
|
||||
{
|
||||
this.__proto__ = new invokerChecker(null, null, null, false);
|
||||
// XXX it doesn't actually work to inherit from invokerChecker, but maybe we
|
||||
// should fix that?
|
||||
// this.__proto__ = new invokerChecker(null, null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -55,8 +55,8 @@
|
|||
// related show event.
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, this.menuNode),
|
||||
new asyncInvokerChecker(EVENT_MENUPOPUP_START, this.menuNode),
|
||||
new invokerChecker(EVENT_REORDER, getNode(aParentMenuID))
|
||||
new invokerChecker(EVENT_REORDER, getNode(aParentMenuID)),
|
||||
new invokerChecker(EVENT_MENUPOPUP_START, this.menuNode)
|
||||
];
|
||||
|
||||
this.invoke = function showMenu_invoke()
|
||||
|
|
|
@ -419,8 +419,8 @@
|
|||
this.lb = getAccessible("t5_lb");
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, this.o),
|
||||
new invokerChecker(EVENT_HIDE, this.b),
|
||||
new invokerChecker(EVENT_HIDE, this.o),
|
||||
new invokerChecker(EVENT_REORDER, "t5"),
|
||||
new unexpectedInvokerChecker(EVENT_HIDE, this.ofc),
|
||||
new unexpectedInvokerChecker(EVENT_REORDER, this.o),
|
||||
|
@ -539,8 +539,8 @@
|
|||
new invokerChecker(EVENT_HIDE, getNode('t8_c1_child')),
|
||||
new invokerChecker(EVENT_HIDE, 't8_c2_moved'),
|
||||
new invokerChecker(EVENT_SHOW, 't8_c2_moved'),
|
||||
new invokerChecker(EVENT_REORDER, 't8_c2'),
|
||||
new invokerChecker(EVENT_REORDER, 't8_c1'),
|
||||
new invokerChecker(EVENT_REORDER, 't8_c2')
|
||||
];
|
||||
|
||||
this.invoke = function test8_invoke()
|
||||
|
@ -578,13 +578,13 @@
|
|||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode('t9_c1_child')),
|
||||
new invokerChecker(EVENT_HIDE, getNode('t9_c2_child')),
|
||||
new invokerChecker(EVENT_HIDE, 't9_c3_moved'),
|
||||
new invokerChecker(EVENT_HIDE, 't9_c2_moved'),
|
||||
new invokerChecker(EVENT_SHOW, 't9_c2_moved'),
|
||||
new invokerChecker(EVENT_REORDER, 't9_c1'),
|
||||
new invokerChecker(EVENT_HIDE, getNode('t9_c2_child')),
|
||||
new invokerChecker(EVENT_REORDER, 't9_c2'),
|
||||
new invokerChecker(EVENT_REORDER, 't9_c3'),
|
||||
new invokerChecker(EVENT_REORDER, 't9_c2'),
|
||||
new invokerChecker(EVENT_REORDER, 't9_c1'),
|
||||
new unexpectedInvokerChecker(EVENT_SHOW, 't9_c3_moved')
|
||||
];
|
||||
|
||||
|
@ -624,12 +624,12 @@
|
|||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode('t10_c1_child')),
|
||||
new invokerChecker(EVENT_HIDE, getNode('t10_c2_child')),
|
||||
new invokerChecker(EVENT_HIDE, getNode('t10_c2_moved')),
|
||||
new invokerChecker(EVENT_HIDE, getNode('t10_c3_moved')),
|
||||
new invokerChecker(EVENT_SHOW, getNode('t10_c2_moved')),
|
||||
new invokerChecker(EVENT_REORDER, 't10_c1'),
|
||||
new invokerChecker(EVENT_HIDE, getNode('t10_c2_child')),
|
||||
new invokerChecker(EVENT_REORDER, 't10_c2'),
|
||||
new invokerChecker(EVENT_REORDER, 't10_c1'),
|
||||
new invokerChecker(EVENT_REORDER, 't10_c3')
|
||||
];
|
||||
|
||||
|
@ -659,9 +659,11 @@
|
|||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode('t11_c1_child')),
|
||||
new invokerChecker(EVENT_SHOW, 't11_c2_child'),
|
||||
new invokerChecker(EVENT_HIDE, getNode('t11_c2')),
|
||||
new invokerChecker(EVENT_SHOW, 't11_c2'),
|
||||
new orderChecker(),
|
||||
new asyncInvokerChecker(EVENT_SHOW, 't11_c2_child'),
|
||||
new asyncInvokerChecker(EVENT_SHOW, 't11_c2'),
|
||||
new orderChecker(),
|
||||
new invokerChecker(EVENT_REORDER, 't11'),
|
||||
new unexpectedInvokerChecker(EVENT_HIDE, 't11_c2_child'),
|
||||
new unexpectedInvokerChecker(EVENT_REORDER, 't11_c1'),
|
||||
|
@ -689,7 +691,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
// Do tests.
|
||||
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
gA11yEventDumpToConsole = true; // debug stuff
|
||||
//enableLogging("eventTree");
|
||||
|
||||
var gQueue = null;
|
||||
|
|
|
@ -341,8 +341,8 @@
|
|||
this.containerNode = getNode(aContainerID);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, function(aNode) { return aNode.lastChild; }, this.containerNode),
|
||||
new invokerChecker(EVENT_SHOW, function(aNode) { return aNode.firstChild; }, this.containerNode),
|
||||
new invokerChecker(EVENT_SHOW, function(aNode) { return aNode.lastChild; }, this.containerNode),
|
||||
new invokerChecker(EVENT_REORDER, this.containerNode)
|
||||
];
|
||||
|
||||
|
|
|
@ -27,11 +27,11 @@
|
|||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
|
||||
// no hide for t1_subdiv because it is contained by hidden t1_checkbox
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_checkbox")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_checkbox")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||
];
|
||||
|
||||
|
@ -73,10 +73,12 @@
|
|||
function removeARIAOwns()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
|
||||
new orderChecker(),
|
||||
new asyncInvokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||
new asyncInvokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
|
||||
new orderChecker(),
|
||||
new invokerChecker(EVENT_REORDER, getNode("t1_container")),
|
||||
new unexpectedInvokerChecker(EVENT_REORDER, getNode("t1_checkbox"))
|
||||
];
|
||||
|
@ -108,8 +110,8 @@
|
|||
function setARIAOwns()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||
|
@ -447,6 +449,9 @@
|
|||
this.eventSeq = [];
|
||||
for (var id of aIdList) {
|
||||
this.eventSeq.push(new invokerChecker(EVENT_HIDE, getNode(id)));
|
||||
}
|
||||
|
||||
for (var id of aIdList) {
|
||||
this.eventSeq.push(new invokerChecker(EVENT_SHOW, getNode(id)));
|
||||
}
|
||||
this.eventSeq.push(new invokerChecker(EVENT_REORDER, getNode(aContainer)));
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
this.containerNode = getNode("outerDiv");
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("child")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("childDoc")),
|
||||
new invokerChecker(EVENT_SHOW, "newChildDoc"),
|
||||
new invokerChecker(EVENT_HIDE, getNode("child")),
|
||||
new invokerChecker(EVENT_REORDER, this.containerNode)
|
||||
];
|
||||
|
||||
|
@ -47,7 +47,7 @@
|
|||
}
|
||||
|
||||
//enableLogging("tree");
|
||||
//gA11yEventDumpToConsole = true;
|
||||
gA11yEventDumpToConsole = true;
|
||||
var gQueue = null;
|
||||
function doTest()
|
||||
{
|
||||
|
|
|
@ -101,8 +101,9 @@
|
|||
this.imgNode.setAttribute("src", "../moz.png");
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, getAccessible, this.imgNode),
|
||||
new invokerChecker(EVENT_SHOW, getAccessible, this.textNode),
|
||||
new asyncInvokerChecker(EVENT_SHOW, getAccessible, this.textNode),
|
||||
new asyncInvokerChecker(EVENT_SHOW, getAccessible, this.imgNode),
|
||||
new orderChecker(),
|
||||
new invokerChecker(EVENT_REORDER, this.topNode)
|
||||
];
|
||||
|
||||
|
|
|
@ -735,7 +735,7 @@ TextPropertyEditor.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.isDisplayGrid) {
|
||||
if (this.isDisplayGrid()) {
|
||||
this.ruleView.highlighters._hideGridHighlighter();
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ support-files =
|
|||
html_cause-test-page.html
|
||||
html_content-type-test-page.html
|
||||
html_content-type-without-cache-test-page.html
|
||||
html_brotli-test-page.html
|
||||
html_image-tooltip-test-page.html
|
||||
html_cors-test-page.html
|
||||
html_custom-get-page.html
|
||||
|
@ -68,6 +69,7 @@ skip-if = true # Bug 1309191 - replace with rewritten version in React
|
|||
[browser_net_clear.js]
|
||||
[browser_net_complex-params.js]
|
||||
[browser_net_content-type.js]
|
||||
[browser_net_brotli.js]
|
||||
[browser_net_curl-utils.js]
|
||||
[browser_net_copy_image_as_data_uri.js]
|
||||
subsuite = clipboard
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const BROTLI_URL = HTTPS_EXAMPLE_URL + "html_brotli-test-page.html";
|
||||
const BROTLI_REQUESTS = 1;
|
||||
|
||||
/**
|
||||
* Test brotli encoded response is handled correctly on HTTPS.
|
||||
*/
|
||||
|
||||
add_task(function* () {
|
||||
let { L10N } = require("devtools/client/netmonitor/l10n");
|
||||
|
||||
let { tab, monitor } = yield initNetMonitor(BROTLI_URL);
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, Editor, NetMonitorView } = monitor.panelWin;
|
||||
let { RequestsMenu } = NetMonitorView;
|
||||
|
||||
RequestsMenu.lazyUpdate = false;
|
||||
|
||||
let wait = waitForNetworkEvents(monitor, BROTLI_REQUESTS);
|
||||
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
|
||||
content.wrappedJSObject.performRequests();
|
||||
});
|
||||
yield wait;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
"GET", HTTPS_CONTENT_TYPE_SJS + "?fmt=br", {
|
||||
status: 200,
|
||||
statusText: "Connected",
|
||||
type: "plain",
|
||||
fullMimeType: "text/plain",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 10),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 64),
|
||||
time: true
|
||||
});
|
||||
|
||||
let onEvent = waitForResponseBodyDisplayed();
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.getElementById("details-pane-toggle"));
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.querySelectorAll("#details-pane tab")[3]);
|
||||
yield onEvent;
|
||||
yield testResponseTab("br");
|
||||
|
||||
yield teardown(monitor);
|
||||
|
||||
function* testResponseTab(type) {
|
||||
let tabEl = document.querySelectorAll("#details-pane tab")[3];
|
||||
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
|
||||
|
||||
is(tabEl.getAttribute("selected"), "true",
|
||||
"The response tab in the network details pane should be selected.");
|
||||
|
||||
function checkVisibility(box) {
|
||||
is(tabpanel.querySelector("#response-content-info-header")
|
||||
.hasAttribute("hidden"), true,
|
||||
"The response info header doesn't have the intended visibility.");
|
||||
is(tabpanel.querySelector("#response-content-json-box")
|
||||
.hasAttribute("hidden"), box != "json",
|
||||
"The response content json box doesn't have the intended visibility.");
|
||||
is(tabpanel.querySelector("#response-content-textarea-box")
|
||||
.hasAttribute("hidden"), box != "textarea",
|
||||
"The response content textarea box doesn't have the intended visibility.");
|
||||
is(tabpanel.querySelector("#response-content-image-box")
|
||||
.hasAttribute("hidden"), box != "image",
|
||||
"The response content image box doesn't have the intended visibility.");
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case "br": {
|
||||
checkVisibility("textarea");
|
||||
|
||||
let expected = "X".repeat(64);
|
||||
let editor = yield NetMonitorView.editor("#response-content-textarea");
|
||||
is(editor.getText(), expected,
|
||||
"The text shown in the source editor is incorrect for the brotli request.");
|
||||
is(editor.getMode(), Editor.modes.text,
|
||||
"The mode active in the source editor is incorrect for the brotli request.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function waitForResponseBodyDisplayed() {
|
||||
return monitor.panelWin.once(monitor.panelWin.EVENTS.RESPONSE_BODY_DISPLAYED);
|
||||
}
|
||||
});
|
|
@ -7,16 +7,10 @@
|
|||
* Tests if different response content types are handled correctly.
|
||||
*/
|
||||
|
||||
function* content_type_test(isHTTPS) {
|
||||
add_task(function* () {
|
||||
let { L10N } = require("devtools/client/netmonitor/l10n");
|
||||
|
||||
let pageURL = isHTTPS ? HTTPS_CONTENT_TYPE_WITHOUT_CACHE_URL
|
||||
: CONTENT_TYPE_WITHOUT_CACHE_URL;
|
||||
let imageURL = isHTTPS ? HTTPS_TEST_IMAGE
|
||||
: TEST_IMAGE;
|
||||
let sjsURL = isHTTPS ? HTTPS_CONTENT_TYPE_SJS
|
||||
: CONTENT_TYPE_SJS;
|
||||
let { tab, monitor } = yield initNetMonitor(pageURL);
|
||||
let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, Editor, NetMonitorView } = monitor.panelWin;
|
||||
|
@ -30,89 +24,71 @@ function* content_type_test(isHTTPS) {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
let okStatus = isHTTPS ? "Connected" : "OK";
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
"GET", sjsURL + "?fmt=xml", {
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=xml", {
|
||||
status: 200,
|
||||
statusText: okStatus,
|
||||
statusText: "OK",
|
||||
type: "xml",
|
||||
fullMimeType: "text/xml; charset=utf-8",
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 42),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
|
||||
"GET", sjsURL + "?fmt=css", {
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=css", {
|
||||
status: 200,
|
||||
statusText: okStatus,
|
||||
statusText: "OK",
|
||||
type: "css",
|
||||
fullMimeType: "text/css; charset=utf-8",
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(2),
|
||||
"GET", sjsURL + "?fmt=js", {
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=js", {
|
||||
status: 200,
|
||||
statusText: okStatus,
|
||||
statusText: "OK",
|
||||
type: "js",
|
||||
fullMimeType: "application/javascript; charset=utf-8",
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(3),
|
||||
"GET", sjsURL + "?fmt=json", {
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=json", {
|
||||
status: 200,
|
||||
statusText: okStatus,
|
||||
statusText: "OK",
|
||||
type: "json",
|
||||
fullMimeType: "application/json; charset=utf-8",
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
|
||||
time: true
|
||||
});
|
||||
if (!isHTTPS) {
|
||||
// 404 doesn't work on HTTPS test harness.
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4),
|
||||
"GET", sjsURL + "?fmt=bogus", {
|
||||
status: 404,
|
||||
statusText: "Not Found",
|
||||
type: "html",
|
||||
fullMimeType: "text/html; charset=utf-8",
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 24),
|
||||
time: true
|
||||
});
|
||||
}
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=bogus", {
|
||||
status: 404,
|
||||
statusText: "Not Found",
|
||||
type: "html",
|
||||
fullMimeType: "text/html; charset=utf-8",
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 24),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(5),
|
||||
"GET", imageURL, {
|
||||
"GET", TEST_IMAGE, {
|
||||
fuzzyUrl: true,
|
||||
status: 200,
|
||||
statusText: okStatus,
|
||||
statusText: "OK",
|
||||
type: "png",
|
||||
fullMimeType: "image/png",
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 580),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(6),
|
||||
"GET", sjsURL + "?fmt=gzip", {
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=gzip", {
|
||||
status: 200,
|
||||
statusText: okStatus,
|
||||
statusText: "OK",
|
||||
type: "plain",
|
||||
fullMimeType: "text/plain",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 73),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 10.73),
|
||||
time: true
|
||||
});
|
||||
if (isHTTPS) {
|
||||
// Brotli is enabled only on HTTPS.
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(6),
|
||||
"GET", sjsURL + "?fmt=gzip", {
|
||||
status: 200,
|
||||
statusText: okStatus,
|
||||
type: "plain",
|
||||
fullMimeType: "text/plain",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 73),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 10.73),
|
||||
time: true
|
||||
});
|
||||
}
|
||||
|
||||
let onEvent = waitForResponseBodyDisplayed();
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
|
@ -140,11 +116,6 @@ function* content_type_test(isHTTPS) {
|
|||
yield selectIndexAndWaitForTabUpdated(6);
|
||||
yield testResponseTab("gzip");
|
||||
|
||||
if (isHTTPS) {
|
||||
yield selectIndexAndWaitForTabUpdated(7);
|
||||
yield testResponseTab("br");
|
||||
}
|
||||
|
||||
yield teardown(monitor);
|
||||
|
||||
function* testResponseTab(type) {
|
||||
|
@ -269,17 +240,6 @@ function* content_type_test(isHTTPS) {
|
|||
"The mode active in the source editor is incorrect for the gzip request.");
|
||||
break;
|
||||
}
|
||||
case "br": {
|
||||
checkVisibility("textarea");
|
||||
|
||||
let expected = "X".repeat(64);
|
||||
let editor = yield NetMonitorView.editor("#response-content-textarea");
|
||||
is(editor.getText(), expected,
|
||||
"The text shown in the source editor is incorrect for the brotli request.");
|
||||
is(editor.getMode(), Editor.modes.text,
|
||||
"The mode active in the source editor is incorrect for the brotli request.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,12 +252,4 @@ function* content_type_test(isHTTPS) {
|
|||
function waitForResponseBodyDisplayed() {
|
||||
return monitor.panelWin.once(monitor.panelWin.EVENTS.RESPONSE_BODY_DISPLAYED);
|
||||
}
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
yield* content_type_test(false);
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
yield* content_type_test(true);
|
||||
});
|
||||
|
|
|
@ -20,7 +20,6 @@ const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
|
|||
const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
|
||||
const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html";
|
||||
const CONTENT_TYPE_WITHOUT_CACHE_URL = EXAMPLE_URL + "html_content-type-without-cache-test-page.html";
|
||||
const HTTPS_CONTENT_TYPE_WITHOUT_CACHE_URL = HTTPS_EXAMPLE_URL + "html_content-type-without-cache-test-page.html";
|
||||
const CONTENT_TYPE_WITHOUT_CACHE_REQUESTS = 8;
|
||||
const CYRILLIC_URL = EXAMPLE_URL + "html_cyrillic-test-page.html";
|
||||
const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html";
|
||||
|
@ -58,7 +57,6 @@ const HSTS_BASE_URL = EXAMPLE_URL;
|
|||
const HSTS_PAGE_URL = CUSTOM_GET_URL;
|
||||
|
||||
const TEST_IMAGE = EXAMPLE_URL + "test-image.png";
|
||||
const HTTPS_TEST_IMAGE = HTTPS_EXAMPLE_URL + "test-image.png";
|
||||
const TEST_IMAGE_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
|
||||
|
||||
const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js";
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<title>Network Monitor test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>Brotli test</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
function get(aAddress, aCallback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", aAddress, true);
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState == this.DONE) {
|
||||
aCallback();
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
function performRequests() {
|
||||
get("sjs_content-type-test-server.sjs?fmt=br", function() {
|
||||
// Done.
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -18,5 +18,6 @@ DevToolsModules(
|
|||
'jsterm.js',
|
||||
'panel.js',
|
||||
'utils.js',
|
||||
'webconsole-connection-proxy.js',
|
||||
'webconsole.js',
|
||||
)
|
||||
|
|
|
@ -0,0 +1,503 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cc, Ci, Cu} = require("chrome");
|
||||
|
||||
const {Utils: WebConsoleUtils} =
|
||||
require("devtools/client/webconsole/utils");
|
||||
const BrowserLoaderModule = {};
|
||||
Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
|
||||
|
||||
const promise = require("promise");
|
||||
const Services = require("Services");
|
||||
|
||||
const STRINGS_URI = "devtools/client/locales/webconsole.properties";
|
||||
var l10n = new WebConsoleUtils.L10n(STRINGS_URI);
|
||||
|
||||
const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
|
||||
// Web Console connection proxy
|
||||
|
||||
/**
|
||||
* The WebConsoleConnectionProxy handles the connection between the Web Console
|
||||
* and the application we connect to through the remote debug protocol.
|
||||
*
|
||||
* @constructor
|
||||
* @param object webConsoleFrame
|
||||
* The WebConsoleFrame object that owns this connection proxy.
|
||||
* @param RemoteTarget target
|
||||
* The target that the console will connect to.
|
||||
*/
|
||||
function WebConsoleConnectionProxy(webConsoleFrame, target) {
|
||||
this.webConsoleFrame = webConsoleFrame;
|
||||
this.target = target;
|
||||
|
||||
this._onPageError = this._onPageError.bind(this);
|
||||
this._onLogMessage = this._onLogMessage.bind(this);
|
||||
this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
|
||||
this._onNetworkEvent = this._onNetworkEvent.bind(this);
|
||||
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
|
||||
this._onFileActivity = this._onFileActivity.bind(this);
|
||||
this._onReflowActivity = this._onReflowActivity.bind(this);
|
||||
this._onServerLogCall = this._onServerLogCall.bind(this);
|
||||
this._onTabNavigated = this._onTabNavigated.bind(this);
|
||||
this._onAttachConsole = this._onAttachConsole.bind(this);
|
||||
this._onCachedMessages = this._onCachedMessages.bind(this);
|
||||
this._connectionTimeout = this._connectionTimeout.bind(this);
|
||||
this._onLastPrivateContextExited =
|
||||
this._onLastPrivateContextExited.bind(this);
|
||||
}
|
||||
|
||||
WebConsoleConnectionProxy.prototype = {
|
||||
/**
|
||||
* The owning Web Console Frame instance.
|
||||
*
|
||||
* @see WebConsoleFrame
|
||||
* @type object
|
||||
*/
|
||||
webConsoleFrame: null,
|
||||
|
||||
/**
|
||||
* The target that the console connects to.
|
||||
* @type RemoteTarget
|
||||
*/
|
||||
target: null,
|
||||
|
||||
/**
|
||||
* The DebuggerClient object.
|
||||
*
|
||||
* @see DebuggerClient
|
||||
* @type object
|
||||
*/
|
||||
client: null,
|
||||
|
||||
/**
|
||||
* The WebConsoleClient object.
|
||||
*
|
||||
* @see WebConsoleClient
|
||||
* @type object
|
||||
*/
|
||||
webConsoleClient: null,
|
||||
|
||||
/**
|
||||
* Tells if the connection is established.
|
||||
* @type boolean
|
||||
*/
|
||||
connected: false,
|
||||
|
||||
/**
|
||||
* Timer used for the connection.
|
||||
* @private
|
||||
* @type object
|
||||
*/
|
||||
_connectTimer: null,
|
||||
|
||||
_connectDefer: null,
|
||||
_disconnecter: null,
|
||||
|
||||
/**
|
||||
* The WebConsoleActor ID.
|
||||
*
|
||||
* @private
|
||||
* @type string
|
||||
*/
|
||||
_consoleActor: null,
|
||||
|
||||
/**
|
||||
* Tells if the window.console object of the remote web page is the native
|
||||
* object or not.
|
||||
* @private
|
||||
* @type boolean
|
||||
*/
|
||||
_hasNativeConsoleAPI: false,
|
||||
|
||||
/**
|
||||
* Initialize a debugger client and connect it to the debugger server.
|
||||
*
|
||||
* @return object
|
||||
* A promise object that is resolved/rejected based on the success of
|
||||
* the connection initialization.
|
||||
*/
|
||||
connect: function () {
|
||||
if (this._connectDefer) {
|
||||
return this._connectDefer.promise;
|
||||
}
|
||||
|
||||
this._connectDefer = promise.defer();
|
||||
|
||||
let timeout = Services.prefs.getIntPref(PREF_CONNECTION_TIMEOUT);
|
||||
this._connectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this._connectTimer.initWithCallback(this._connectionTimeout,
|
||||
timeout, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
|
||||
let connPromise = this._connectDefer.promise;
|
||||
connPromise.then(() => {
|
||||
this._connectTimer.cancel();
|
||||
this._connectTimer = null;
|
||||
}, () => {
|
||||
this._connectTimer = null;
|
||||
});
|
||||
|
||||
let client = this.client = this.target.client;
|
||||
|
||||
if (this.target.isWorkerTarget) {
|
||||
// XXXworkers: Not Console API yet inside of workers (Bug 1209353).
|
||||
} else {
|
||||
client.addListener("logMessage", this._onLogMessage);
|
||||
client.addListener("pageError", this._onPageError);
|
||||
client.addListener("consoleAPICall", this._onConsoleAPICall);
|
||||
client.addListener("fileActivity", this._onFileActivity);
|
||||
client.addListener("reflowActivity", this._onReflowActivity);
|
||||
client.addListener("serverLogCall", this._onServerLogCall);
|
||||
client.addListener("lastPrivateContextExited",
|
||||
this._onLastPrivateContextExited);
|
||||
}
|
||||
this.target.on("will-navigate", this._onTabNavigated);
|
||||
this.target.on("navigate", this._onTabNavigated);
|
||||
|
||||
this._consoleActor = this.target.form.consoleActor;
|
||||
if (this.target.isTabActor) {
|
||||
let tab = this.target.form;
|
||||
this.webConsoleFrame.onLocationChange(tab.url, tab.title);
|
||||
}
|
||||
this._attachConsole();
|
||||
|
||||
return connPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Connection timeout handler.
|
||||
* @private
|
||||
*/
|
||||
_connectionTimeout: function () {
|
||||
let error = {
|
||||
error: "timeout",
|
||||
message: l10n.getStr("connectionTimeout"),
|
||||
};
|
||||
|
||||
this._connectDefer.reject(error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Attach to the Web Console actor.
|
||||
* @private
|
||||
*/
|
||||
_attachConsole: function () {
|
||||
let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
|
||||
"FileActivity"];
|
||||
this.client.attachConsole(this._consoleActor, listeners,
|
||||
this._onAttachConsole);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "attachConsole" response handler.
|
||||
*
|
||||
* @private
|
||||
* @param object response
|
||||
* The JSON response object received from the server.
|
||||
* @param object webConsoleClient
|
||||
* The WebConsoleClient instance for the attached console, for the
|
||||
* specific tab we work with.
|
||||
*/
|
||||
_onAttachConsole: function (response, webConsoleClient) {
|
||||
if (response.error) {
|
||||
console.error("attachConsole failed: " + response.error + " " +
|
||||
response.message);
|
||||
this._connectDefer.reject(response);
|
||||
return;
|
||||
}
|
||||
|
||||
this.webConsoleClient = webConsoleClient;
|
||||
this._hasNativeConsoleAPI = response.nativeConsoleAPI;
|
||||
|
||||
// There is no way to view response bodies from the Browser Console, so do
|
||||
// not waste the memory.
|
||||
let saveBodies = !this.webConsoleFrame.isBrowserConsole;
|
||||
this.webConsoleFrame.setSaveRequestAndResponseBodies(saveBodies);
|
||||
|
||||
this.webConsoleClient.on("networkEvent", this._onNetworkEvent);
|
||||
this.webConsoleClient.on("networkEventUpdate", this._onNetworkEventUpdate);
|
||||
|
||||
let msgs = ["PageError", "ConsoleAPI"];
|
||||
this.webConsoleClient.getCachedMessages(msgs, this._onCachedMessages);
|
||||
|
||||
this.webConsoleFrame._onUpdateListeners();
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatch a message add on the new frontend and emit an event for tests.
|
||||
*/
|
||||
dispatchMessageAdd: function(packet) {
|
||||
this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet);
|
||||
},
|
||||
|
||||
/**
|
||||
* Batched dispatch of messages.
|
||||
*/
|
||||
dispatchMessagesAdd: function(packets) {
|
||||
this.webConsoleFrame.newConsoleOutput.dispatchMessagesAdd(packets);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "cachedMessages" response handler.
|
||||
*
|
||||
* @private
|
||||
* @param object response
|
||||
* The JSON response object received from the server.
|
||||
*/
|
||||
_onCachedMessages: function (response) {
|
||||
if (response.error) {
|
||||
console.error("Web Console getCachedMessages error: " + response.error +
|
||||
" " + response.message);
|
||||
this._connectDefer.reject(response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._connectTimer) {
|
||||
// This happens if the promise is rejected (eg. a timeout), but the
|
||||
// connection attempt is successful, nonetheless.
|
||||
console.error("Web Console getCachedMessages error: invalid state.");
|
||||
}
|
||||
|
||||
let messages =
|
||||
response.messages.concat(...this.webConsoleClient.getNetworkEvents());
|
||||
messages.sort((a, b) => a.timeStamp - b.timeStamp);
|
||||
|
||||
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
|
||||
this.dispatchMessagesAdd(messages);
|
||||
} else {
|
||||
this.webConsoleFrame.displayCachedMessages(messages);
|
||||
if (!this._hasNativeConsoleAPI) {
|
||||
this.webConsoleFrame.logWarningAboutReplacedAPI();
|
||||
}
|
||||
}
|
||||
|
||||
this.connected = true;
|
||||
this._connectDefer.resolve(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "pageError" message type handler. We redirect any page errors to the UI
|
||||
* for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onPageError: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
|
||||
this.dispatchMessageAdd(packet);
|
||||
return;
|
||||
}
|
||||
this.webConsoleFrame.handlePageError(packet.pageError);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "logMessage" message type handler. We redirect any message to the UI
|
||||
* for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onLogMessage: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
this.webConsoleFrame.handleLogMessage(packet);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "consoleAPICall" message type handler. We redirect any message to
|
||||
* the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onConsoleAPICall: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
|
||||
this.dispatchMessageAdd(packet);
|
||||
} else {
|
||||
this.webConsoleFrame.handleConsoleAPICall(packet.message);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "networkEvent" message type handler. We redirect any message to
|
||||
* the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object networkInfo
|
||||
* The network request information.
|
||||
*/
|
||||
_onNetworkEvent: function (type, networkInfo) {
|
||||
if (this.webConsoleFrame) {
|
||||
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
|
||||
this.dispatchMessageAdd(networkInfo);
|
||||
} else {
|
||||
this.webConsoleFrame.handleNetworkEvent(networkInfo);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "networkEventUpdate" message type handler. We redirect any message to
|
||||
* the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
* @param object networkInfo
|
||||
* The network request information.
|
||||
*/
|
||||
_onNetworkEventUpdate: function (type, { packet, networkInfo }) {
|
||||
if (this.webConsoleFrame) {
|
||||
this.webConsoleFrame.handleNetworkEventUpdate(networkInfo, packet);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "fileActivity" message type handler. We redirect any message to
|
||||
* the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onFileActivity: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
this.webConsoleFrame.handleFileActivity(packet.uri);
|
||||
}
|
||||
},
|
||||
|
||||
_onReflowActivity: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
this.webConsoleFrame.handleReflowActivity(packet);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "serverLogCall" message type handler. We redirect any message to
|
||||
* the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onServerLogCall: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
this.webConsoleFrame.handleConsoleAPICall(packet.message);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "lastPrivateContextExited" message type handler. When this message is
|
||||
* received the Web Console UI is cleared.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onLastPrivateContextExited: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
this.webConsoleFrame.jsterm.clearPrivateMessages();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "will-navigate" and "navigate" event handlers. We redirect any message
|
||||
* to the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string event
|
||||
* Event type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onTabNavigated: function (event, packet) {
|
||||
if (!this.webConsoleFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.webConsoleFrame.handleTabNavigated(event, packet);
|
||||
},
|
||||
|
||||
/**
|
||||
* Release an object actor.
|
||||
*
|
||||
* @param string actor
|
||||
* The actor ID to send the request to.
|
||||
*/
|
||||
releaseActor: function (actor) {
|
||||
if (this.client) {
|
||||
this.client.release(actor);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Disconnect the Web Console from the remote server.
|
||||
*
|
||||
* @return object
|
||||
* A promise object that is resolved when disconnect completes.
|
||||
*/
|
||||
disconnect: function () {
|
||||
if (this._disconnecter) {
|
||||
return this._disconnecter.promise;
|
||||
}
|
||||
|
||||
this._disconnecter = promise.defer();
|
||||
|
||||
if (!this.client) {
|
||||
this._disconnecter.resolve(null);
|
||||
return this._disconnecter.promise;
|
||||
}
|
||||
|
||||
this.client.removeListener("logMessage", this._onLogMessage);
|
||||
this.client.removeListener("pageError", this._onPageError);
|
||||
this.client.removeListener("consoleAPICall", this._onConsoleAPICall);
|
||||
this.client.removeListener("fileActivity", this._onFileActivity);
|
||||
this.client.removeListener("reflowActivity", this._onReflowActivity);
|
||||
this.client.removeListener("serverLogCall", this._onServerLogCall);
|
||||
this.client.removeListener("lastPrivateContextExited",
|
||||
this._onLastPrivateContextExited);
|
||||
this.webConsoleClient.off("networkEvent", this._onNetworkEvent);
|
||||
this.webConsoleClient.off("networkEventUpdate", this._onNetworkEventUpdate);
|
||||
this.target.off("will-navigate", this._onTabNavigated);
|
||||
this.target.off("navigate", this._onTabNavigated);
|
||||
|
||||
this.client = null;
|
||||
this.webConsoleClient = null;
|
||||
this.target = null;
|
||||
this.connected = false;
|
||||
this.webConsoleFrame = null;
|
||||
this._disconnecter.resolve(null);
|
||||
|
||||
return this._disconnecter.promise;
|
||||
},
|
||||
};
|
||||
|
||||
exports.WebConsoleConnectionProxy = WebConsoleConnectionProxy;
|
|
@ -16,15 +16,12 @@ Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderMo
|
|||
|
||||
const promise = require("promise");
|
||||
const Services = require("Services");
|
||||
const ErrorDocs = require("devtools/server/actors/errordocs");
|
||||
const Telemetry = require("devtools/client/shared/telemetry");
|
||||
|
||||
loader.lazyServiceGetter(this, "clipboardHelper",
|
||||
"@mozilla.org/widget/clipboardhelper;1",
|
||||
"nsIClipboardHelper");
|
||||
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
|
||||
loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup", true);
|
||||
loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/framework/sidebar", true);
|
||||
loader.lazyRequireGetter(this, "ConsoleOutput", "devtools/client/webconsole/console-output", true);
|
||||
loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/console-output", true);
|
||||
loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/main", true);
|
||||
|
@ -37,6 +34,7 @@ loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client
|
|||
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
|
||||
loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts", true);
|
||||
loader.lazyRequireGetter(this, "ZoomKeys", "devtools/client/shared/zoom-keys");
|
||||
loader.lazyRequireGetter(this, "WebConsoleConnectionProxy", "devtools/client/webconsole/webconsole-connection-proxy", true);
|
||||
|
||||
const {PluralForm} = require("devtools/shared/plural-form");
|
||||
const STRINGS_URI = "devtools/client/locales/webconsole.properties";
|
||||
|
@ -183,10 +181,6 @@ const THROTTLE_UPDATES = 1000;
|
|||
// The preference prefix for all of the Web Console filters.
|
||||
const FILTER_PREFS_PREFIX = "devtools.webconsole.filter.";
|
||||
|
||||
// The minimum font size.
|
||||
const MIN_FONT_SIZE = 10;
|
||||
|
||||
const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
|
||||
const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
|
||||
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
|
||||
const PREF_NEW_FRONTEND_ENABLED = "devtools.webconsole.new-frontend-enabled";
|
||||
|
@ -3053,486 +3047,6 @@ CommandController.prototype = {
|
|||
}
|
||||
};
|
||||
|
||||
// Web Console connection proxy
|
||||
|
||||
/**
|
||||
* The WebConsoleConnectionProxy handles the connection between the Web Console
|
||||
* and the application we connect to through the remote debug protocol.
|
||||
*
|
||||
* @constructor
|
||||
* @param object webConsoleFrame
|
||||
* The WebConsoleFrame object that owns this connection proxy.
|
||||
* @param RemoteTarget target
|
||||
* The target that the console will connect to.
|
||||
*/
|
||||
function WebConsoleConnectionProxy(webConsoleFrame, target) {
|
||||
this.webConsoleFrame = webConsoleFrame;
|
||||
this.target = target;
|
||||
|
||||
this._onPageError = this._onPageError.bind(this);
|
||||
this._onLogMessage = this._onLogMessage.bind(this);
|
||||
this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
|
||||
this._onNetworkEvent = this._onNetworkEvent.bind(this);
|
||||
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
|
||||
this._onFileActivity = this._onFileActivity.bind(this);
|
||||
this._onReflowActivity = this._onReflowActivity.bind(this);
|
||||
this._onServerLogCall = this._onServerLogCall.bind(this);
|
||||
this._onTabNavigated = this._onTabNavigated.bind(this);
|
||||
this._onAttachConsole = this._onAttachConsole.bind(this);
|
||||
this._onCachedMessages = this._onCachedMessages.bind(this);
|
||||
this._connectionTimeout = this._connectionTimeout.bind(this);
|
||||
this._onLastPrivateContextExited =
|
||||
this._onLastPrivateContextExited.bind(this);
|
||||
}
|
||||
|
||||
WebConsoleConnectionProxy.prototype = {
|
||||
/**
|
||||
* The owning Web Console Frame instance.
|
||||
*
|
||||
* @see WebConsoleFrame
|
||||
* @type object
|
||||
*/
|
||||
webConsoleFrame: null,
|
||||
|
||||
/**
|
||||
* The target that the console connects to.
|
||||
* @type RemoteTarget
|
||||
*/
|
||||
target: null,
|
||||
|
||||
/**
|
||||
* The DebuggerClient object.
|
||||
*
|
||||
* @see DebuggerClient
|
||||
* @type object
|
||||
*/
|
||||
client: null,
|
||||
|
||||
/**
|
||||
* The WebConsoleClient object.
|
||||
*
|
||||
* @see WebConsoleClient
|
||||
* @type object
|
||||
*/
|
||||
webConsoleClient: null,
|
||||
|
||||
/**
|
||||
* Tells if the connection is established.
|
||||
* @type boolean
|
||||
*/
|
||||
connected: false,
|
||||
|
||||
/**
|
||||
* Timer used for the connection.
|
||||
* @private
|
||||
* @type object
|
||||
*/
|
||||
_connectTimer: null,
|
||||
|
||||
_connectDefer: null,
|
||||
_disconnecter: null,
|
||||
|
||||
/**
|
||||
* The WebConsoleActor ID.
|
||||
*
|
||||
* @private
|
||||
* @type string
|
||||
*/
|
||||
_consoleActor: null,
|
||||
|
||||
/**
|
||||
* Tells if the window.console object of the remote web page is the native
|
||||
* object or not.
|
||||
* @private
|
||||
* @type boolean
|
||||
*/
|
||||
_hasNativeConsoleAPI: false,
|
||||
|
||||
/**
|
||||
* Initialize a debugger client and connect it to the debugger server.
|
||||
*
|
||||
* @return object
|
||||
* A promise object that is resolved/rejected based on the success of
|
||||
* the connection initialization.
|
||||
*/
|
||||
connect: function () {
|
||||
if (this._connectDefer) {
|
||||
return this._connectDefer.promise;
|
||||
}
|
||||
|
||||
this._connectDefer = promise.defer();
|
||||
|
||||
let timeout = Services.prefs.getIntPref(PREF_CONNECTION_TIMEOUT);
|
||||
this._connectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this._connectTimer.initWithCallback(this._connectionTimeout,
|
||||
timeout, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
|
||||
let connPromise = this._connectDefer.promise;
|
||||
connPromise.then(() => {
|
||||
this._connectTimer.cancel();
|
||||
this._connectTimer = null;
|
||||
}, () => {
|
||||
this._connectTimer = null;
|
||||
});
|
||||
|
||||
let client = this.client = this.target.client;
|
||||
|
||||
if (this.target.isWorkerTarget) {
|
||||
// XXXworkers: Not Console API yet inside of workers (Bug 1209353).
|
||||
} else {
|
||||
client.addListener("logMessage", this._onLogMessage);
|
||||
client.addListener("pageError", this._onPageError);
|
||||
client.addListener("consoleAPICall", this._onConsoleAPICall);
|
||||
client.addListener("fileActivity", this._onFileActivity);
|
||||
client.addListener("reflowActivity", this._onReflowActivity);
|
||||
client.addListener("serverLogCall", this._onServerLogCall);
|
||||
client.addListener("lastPrivateContextExited",
|
||||
this._onLastPrivateContextExited);
|
||||
}
|
||||
this.target.on("will-navigate", this._onTabNavigated);
|
||||
this.target.on("navigate", this._onTabNavigated);
|
||||
|
||||
this._consoleActor = this.target.form.consoleActor;
|
||||
if (this.target.isTabActor) {
|
||||
let tab = this.target.form;
|
||||
this.webConsoleFrame.onLocationChange(tab.url, tab.title);
|
||||
}
|
||||
this._attachConsole();
|
||||
|
||||
return connPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Connection timeout handler.
|
||||
* @private
|
||||
*/
|
||||
_connectionTimeout: function () {
|
||||
let error = {
|
||||
error: "timeout",
|
||||
message: l10n.getStr("connectionTimeout"),
|
||||
};
|
||||
|
||||
this._connectDefer.reject(error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Attach to the Web Console actor.
|
||||
* @private
|
||||
*/
|
||||
_attachConsole: function () {
|
||||
let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
|
||||
"FileActivity"];
|
||||
this.client.attachConsole(this._consoleActor, listeners,
|
||||
this._onAttachConsole);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "attachConsole" response handler.
|
||||
*
|
||||
* @private
|
||||
* @param object response
|
||||
* The JSON response object received from the server.
|
||||
* @param object webConsoleClient
|
||||
* The WebConsoleClient instance for the attached console, for the
|
||||
* specific tab we work with.
|
||||
*/
|
||||
_onAttachConsole: function (response, webConsoleClient) {
|
||||
if (response.error) {
|
||||
console.error("attachConsole failed: " + response.error + " " +
|
||||
response.message);
|
||||
this._connectDefer.reject(response);
|
||||
return;
|
||||
}
|
||||
|
||||
this.webConsoleClient = webConsoleClient;
|
||||
this._hasNativeConsoleAPI = response.nativeConsoleAPI;
|
||||
|
||||
// There is no way to view response bodies from the Browser Console, so do
|
||||
// not waste the memory.
|
||||
let saveBodies = !this.webConsoleFrame.isBrowserConsole;
|
||||
this.webConsoleFrame.setSaveRequestAndResponseBodies(saveBodies);
|
||||
|
||||
this.webConsoleClient.on("networkEvent", this._onNetworkEvent);
|
||||
this.webConsoleClient.on("networkEventUpdate", this._onNetworkEventUpdate);
|
||||
|
||||
let msgs = ["PageError", "ConsoleAPI"];
|
||||
this.webConsoleClient.getCachedMessages(msgs, this._onCachedMessages);
|
||||
|
||||
this.webConsoleFrame._onUpdateListeners();
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatch a message add on the new frontend and emit an event for tests.
|
||||
*/
|
||||
dispatchMessageAdd: function(packet) {
|
||||
this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet);
|
||||
},
|
||||
|
||||
/**
|
||||
* Batched dispatch of messages.
|
||||
*/
|
||||
dispatchMessagesAdd: function(packets) {
|
||||
this.webConsoleFrame.newConsoleOutput.dispatchMessagesAdd(packets);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "cachedMessages" response handler.
|
||||
*
|
||||
* @private
|
||||
* @param object response
|
||||
* The JSON response object received from the server.
|
||||
*/
|
||||
_onCachedMessages: function (response) {
|
||||
if (response.error) {
|
||||
console.error("Web Console getCachedMessages error: " + response.error +
|
||||
" " + response.message);
|
||||
this._connectDefer.reject(response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._connectTimer) {
|
||||
// This happens if the promise is rejected (eg. a timeout), but the
|
||||
// connection attempt is successful, nonetheless.
|
||||
console.error("Web Console getCachedMessages error: invalid state.");
|
||||
}
|
||||
|
||||
let messages =
|
||||
response.messages.concat(...this.webConsoleClient.getNetworkEvents());
|
||||
messages.sort((a, b) => a.timeStamp - b.timeStamp);
|
||||
|
||||
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
|
||||
this.dispatchMessagesAdd(messages);
|
||||
} else {
|
||||
this.webConsoleFrame.displayCachedMessages(messages);
|
||||
if (!this._hasNativeConsoleAPI) {
|
||||
this.webConsoleFrame.logWarningAboutReplacedAPI();
|
||||
}
|
||||
}
|
||||
|
||||
this.connected = true;
|
||||
this._connectDefer.resolve(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "pageError" message type handler. We redirect any page errors to the UI
|
||||
* for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onPageError: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
|
||||
this.dispatchMessageAdd(packet);
|
||||
return;
|
||||
}
|
||||
this.webConsoleFrame.handlePageError(packet.pageError);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "logMessage" message type handler. We redirect any message to the UI
|
||||
* for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onLogMessage: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
this.webConsoleFrame.handleLogMessage(packet);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "consoleAPICall" message type handler. We redirect any message to
|
||||
* the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onConsoleAPICall: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
|
||||
this.dispatchMessageAdd(packet);
|
||||
} else {
|
||||
this.webConsoleFrame.handleConsoleAPICall(packet.message);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "networkEvent" message type handler. We redirect any message to
|
||||
* the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object networkInfo
|
||||
* The network request information.
|
||||
*/
|
||||
_onNetworkEvent: function (type, networkInfo) {
|
||||
if (this.webConsoleFrame) {
|
||||
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
|
||||
this.dispatchMessageAdd(networkInfo);
|
||||
} else {
|
||||
this.webConsoleFrame.handleNetworkEvent(networkInfo);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "networkEventUpdate" message type handler. We redirect any message to
|
||||
* the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
* @param object networkInfo
|
||||
* The network request information.
|
||||
*/
|
||||
_onNetworkEventUpdate: function (type, { packet, networkInfo }) {
|
||||
if (this.webConsoleFrame) {
|
||||
this.webConsoleFrame.handleNetworkEventUpdate(networkInfo, packet);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "fileActivity" message type handler. We redirect any message to
|
||||
* the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onFileActivity: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
this.webConsoleFrame.handleFileActivity(packet.uri);
|
||||
}
|
||||
},
|
||||
|
||||
_onReflowActivity: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
this.webConsoleFrame.handleReflowActivity(packet);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "serverLogCall" message type handler. We redirect any message to
|
||||
* the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onServerLogCall: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
this.webConsoleFrame.handleConsoleAPICall(packet.message);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "lastPrivateContextExited" message type handler. When this message is
|
||||
* received the Web Console UI is cleared.
|
||||
*
|
||||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onLastPrivateContextExited: function (type, packet) {
|
||||
if (this.webConsoleFrame && packet.from == this._consoleActor) {
|
||||
this.webConsoleFrame.jsterm.clearPrivateMessages();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "will-navigate" and "navigate" event handlers. We redirect any message
|
||||
* to the UI for displaying.
|
||||
*
|
||||
* @private
|
||||
* @param string event
|
||||
* Event type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onTabNavigated: function (event, packet) {
|
||||
if (!this.webConsoleFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.webConsoleFrame.handleTabNavigated(event, packet);
|
||||
},
|
||||
|
||||
/**
|
||||
* Release an object actor.
|
||||
*
|
||||
* @param string actor
|
||||
* The actor ID to send the request to.
|
||||
*/
|
||||
releaseActor: function (actor) {
|
||||
if (this.client) {
|
||||
this.client.release(actor);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Disconnect the Web Console from the remote server.
|
||||
*
|
||||
* @return object
|
||||
* A promise object that is resolved when disconnect completes.
|
||||
*/
|
||||
disconnect: function () {
|
||||
if (this._disconnecter) {
|
||||
return this._disconnecter.promise;
|
||||
}
|
||||
|
||||
this._disconnecter = promise.defer();
|
||||
|
||||
if (!this.client) {
|
||||
this._disconnecter.resolve(null);
|
||||
return this._disconnecter.promise;
|
||||
}
|
||||
|
||||
this.client.removeListener("logMessage", this._onLogMessage);
|
||||
this.client.removeListener("pageError", this._onPageError);
|
||||
this.client.removeListener("consoleAPICall", this._onConsoleAPICall);
|
||||
this.client.removeListener("fileActivity", this._onFileActivity);
|
||||
this.client.removeListener("reflowActivity", this._onReflowActivity);
|
||||
this.client.removeListener("serverLogCall", this._onServerLogCall);
|
||||
this.client.removeListener("lastPrivateContextExited",
|
||||
this._onLastPrivateContextExited);
|
||||
this.webConsoleClient.off("networkEvent", this._onNetworkEvent);
|
||||
this.webConsoleClient.off("networkEventUpdate", this._onNetworkEventUpdate);
|
||||
this.target.off("will-navigate", this._onTabNavigated);
|
||||
this.target.off("navigate", this._onTabNavigated);
|
||||
|
||||
this.client = null;
|
||||
this.webConsoleClient = null;
|
||||
this.target = null;
|
||||
this.connected = false;
|
||||
this.webConsoleFrame = null;
|
||||
this._disconnecter.resolve(null);
|
||||
|
||||
return this._disconnecter.promise;
|
||||
},
|
||||
};
|
||||
|
||||
// Context Menu
|
||||
|
||||
/*
|
||||
|
|
|
@ -372,78 +372,6 @@ nsPluginCrashedEvent::Run()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
class nsStopPluginRunnable : public Runnable, public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
nsStopPluginRunnable(nsPluginInstanceOwner* aInstanceOwner,
|
||||
nsObjectLoadingContent* aContent)
|
||||
: mInstanceOwner(aInstanceOwner)
|
||||
, mContent(aContent)
|
||||
{
|
||||
NS_ASSERTION(aInstanceOwner, "need an owner");
|
||||
NS_ASSERTION(aContent, "need a nsObjectLoadingContent");
|
||||
}
|
||||
|
||||
// Runnable
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
// nsITimerCallback
|
||||
NS_IMETHOD Notify(nsITimer* timer) override;
|
||||
|
||||
protected:
|
||||
~nsStopPluginRunnable() override = default;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
RefPtr<nsPluginInstanceOwner> mInstanceOwner;
|
||||
nsCOMPtr<nsIObjectLoadingContent> mContent;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(nsStopPluginRunnable, Runnable, nsITimerCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStopPluginRunnable::Notify(nsITimer *aTimer)
|
||||
{
|
||||
return Run();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStopPluginRunnable::Run()
|
||||
{
|
||||
// InitWithCallback calls Release before AddRef so we need to hold a
|
||||
// strong ref on 'this' since we fall through to this scope if it fails.
|
||||
nsCOMPtr<nsITimerCallback> kungFuDeathGrip = this;
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
if (appShell) {
|
||||
uint32_t currentLevel = 0;
|
||||
appShell->GetEventloopNestingLevel(¤tLevel);
|
||||
if (currentLevel > mInstanceOwner->GetLastEventloopNestingLevel()) {
|
||||
if (!mTimer)
|
||||
mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (mTimer) {
|
||||
// Fire 100ms timer to try to tear down this plugin as quickly as
|
||||
// possible once the nesting level comes back down.
|
||||
nsresult rv = mTimer->InitWithCallback(this, 100,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
NS_ERROR("Failed to setup a timer to stop the plugin later (at a safe "
|
||||
"time). Stopping the plugin now, this might crash.");
|
||||
}
|
||||
}
|
||||
|
||||
mTimer = nullptr;
|
||||
|
||||
static_cast<nsObjectLoadingContent*>(mContent.get())->
|
||||
DoStopPlugin(mInstanceOwner, false, true);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// You can't take the address of bitfield members, so we have two separate
|
||||
// classes for these :-/
|
||||
|
||||
|
@ -3070,29 +2998,6 @@ nsObjectLoadingContent::GetSrcURI(nsIURI** aURI)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
DoDelayedStop(nsPluginInstanceOwner* aInstanceOwner,
|
||||
nsObjectLoadingContent* aContent,
|
||||
bool aDelayedStop)
|
||||
{
|
||||
// Don't delay stopping QuickTime (bug 425157), Flip4Mac (bug 426524),
|
||||
// XStandard (bug 430219), CMISS Zinc (bug 429604).
|
||||
if (aDelayedStop
|
||||
#if !(defined XP_WIN || defined MOZ_X11)
|
||||
&& !aInstanceOwner->MatchPluginName("QuickTime")
|
||||
&& !aInstanceOwner->MatchPluginName("Flip4Mac")
|
||||
&& !aInstanceOwner->MatchPluginName("XStandard plugin")
|
||||
&& !aInstanceOwner->MatchPluginName("CMISS Zinc Plugin")
|
||||
#endif
|
||||
) {
|
||||
nsCOMPtr<nsIRunnable> evt =
|
||||
new nsStopPluginRunnable(aInstanceOwner, aContent);
|
||||
NS_DispatchToCurrentThread(evt);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
nsObjectLoadingContent::LoadFallback(FallbackType aType, bool aNotify) {
|
||||
EventStates oldState = ObjectState();
|
||||
|
@ -3156,16 +3061,13 @@ nsObjectLoadingContent::LoadFallback(FallbackType aType, bool aNotify) {
|
|||
}
|
||||
|
||||
void
|
||||
nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner,
|
||||
bool aDelayedStop,
|
||||
bool aForcedReentry)
|
||||
nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner)
|
||||
{
|
||||
// DoStopPlugin can process events -- There may be pending
|
||||
// CheckPluginStopEvent events which can drop in underneath us and destroy the
|
||||
// instance we are about to destroy. We prevent that with the mPluginStopping
|
||||
// flag. (aForcedReentry is only true from the callback of an earlier delayed
|
||||
// stop)
|
||||
if (mIsStopping && !aForcedReentry) {
|
||||
// instance we are about to destroy. We prevent that with the mIsStopping
|
||||
// flag.
|
||||
if (mIsStopping) {
|
||||
return;
|
||||
}
|
||||
mIsStopping = true;
|
||||
|
@ -3174,10 +3076,6 @@ nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner,
|
|||
RefPtr<nsNPAPIPluginInstance> inst;
|
||||
aInstanceOwner->GetInstance(getter_AddRefs(inst));
|
||||
if (inst) {
|
||||
if (DoDelayedStop(aInstanceOwner, this, aDelayedStop)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
aInstanceOwner->HidePluginWindow();
|
||||
#endif
|
||||
|
@ -3230,27 +3128,11 @@ nsObjectLoadingContent::StopPluginInstance()
|
|||
// the instance owner until the plugin is stopped.
|
||||
mInstanceOwner->SetFrame(nullptr);
|
||||
|
||||
bool delayedStop = false;
|
||||
#ifdef XP_WIN
|
||||
// Force delayed stop for Real plugin only; see bug 420886, 426852.
|
||||
RefPtr<nsNPAPIPluginInstance> inst;
|
||||
mInstanceOwner->GetInstance(getter_AddRefs(inst));
|
||||
if (inst) {
|
||||
const char* mime = nullptr;
|
||||
if (NS_SUCCEEDED(inst->GetMIMEType(&mime)) && mime) {
|
||||
if (nsPluginHost::GetSpecialType(nsDependentCString(mime)) ==
|
||||
nsPluginHost::eSpecialType_RealPlayer) {
|
||||
delayedStop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RefPtr<nsPluginInstanceOwner> ownerGrip(mInstanceOwner);
|
||||
mInstanceOwner = nullptr;
|
||||
|
||||
// This can/will re-enter
|
||||
DoStopPlugin(ownerGrip, delayedStop);
|
||||
DoStopPlugin(ownerGrip);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -331,8 +331,7 @@ class nsObjectLoadingContent : public nsImageLoadingContent
|
|||
|
||||
void CreateStaticClone(nsObjectLoadingContent* aDest) const;
|
||||
|
||||
void DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner, bool aDelayedStop,
|
||||
bool aForcedReentry = false);
|
||||
void DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner);
|
||||
|
||||
nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
|
|
|
@ -490,7 +490,7 @@ TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun
|
|||
*out_error = DoTexOrSubImage(false, gl, target, level, dui, xOffset, yOffset,
|
||||
zOffset, mWidth, mHeight, mDepth, nullptr);
|
||||
if (*out_error)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const ScopedLazyBind bindPBO(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER,
|
||||
|
@ -587,7 +587,7 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun
|
|||
yOffset, zOffset, mWidth, mHeight, mDepth,
|
||||
nullptr);
|
||||
if (*out_error)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
do {
|
||||
|
@ -650,7 +650,7 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun
|
|||
webgl->ErrorOutOfMemory("%s: GetAsSourceSurface or GetDataSurface failed after"
|
||||
" blit failed for TexUnpackImage.",
|
||||
funcName);
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const TexUnpackSurface surfBlob(webgl, target, mWidth, mHeight, mDepth, dataSurf,
|
||||
|
@ -751,8 +751,10 @@ TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* f
|
|||
}
|
||||
|
||||
gfx::DataSourceSurface::ScopedMap map(mSurf, gfx::DataSourceSurface::MapType::READ);
|
||||
if (!map.IsMapped())
|
||||
if (!map.IsMapped()) {
|
||||
webgl->ErrorOutOfMemory("%s: Failed to map source surface for upload.", funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto srcBytes = map.GetData();
|
||||
const auto srcStride = map.GetStride();
|
||||
|
|
|
@ -74,6 +74,10 @@ public:
|
|||
|
||||
virtual bool Validate(WebGLContext* webgl, const char* funcName,
|
||||
const webgl::PackingInfo& pi) = 0;
|
||||
|
||||
// Returns false when we've generated a WebGL error.
|
||||
// Returns true but with a non-zero *out_error if we still need to generate a WebGL
|
||||
// error.
|
||||
virtual bool TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
|
||||
WebGLTexture* tex, TexImageTarget target, GLint level,
|
||||
const webgl::DriverUnpackInfo* dui, GLint xOffset,
|
||||
|
|
|
@ -1609,10 +1609,22 @@ WebGLFramebuffer::BlitFramebuffer(WebGLContext* webgl,
|
|||
bool colorFormatsMatch = true;
|
||||
bool colorTypesMatch = true;
|
||||
|
||||
const auto fnNarrowComponentType = [&](const webgl::FormatInfo* format) {
|
||||
switch (format->componentType) {
|
||||
case webgl::ComponentType::NormInt:
|
||||
case webgl::ComponentType::NormUInt:
|
||||
return webgl::ComponentType::Float;
|
||||
|
||||
default:
|
||||
return format->componentType;
|
||||
}
|
||||
};
|
||||
|
||||
const auto fnCheckColorFormat = [&](const webgl::FormatInfo* dstFormat) {
|
||||
dstHasColor = true;
|
||||
colorFormatsMatch &= (dstFormat == srcColorFormat);
|
||||
colorTypesMatch &= (dstFormat->componentType == srcColorFormat->componentType);
|
||||
colorTypesMatch &= ( fnNarrowComponentType(dstFormat) ==
|
||||
fnNarrowComponentType(srcColorFormat) );
|
||||
};
|
||||
|
||||
if (dstFB) {
|
||||
|
|
|
@ -1239,8 +1239,11 @@ WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
|
|||
const GLint zOffset = 0;
|
||||
|
||||
GLenum glError;
|
||||
blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target, level,
|
||||
driverUnpackInfo, xOffset, yOffset, zOffset, &glError);
|
||||
if (!blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target, level,
|
||||
driverUnpackInfo, xOffset, yOffset, zOffset, &glError))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (glError == LOCAL_GL_OUT_OF_MEMORY) {
|
||||
mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.",
|
||||
|
@ -1324,8 +1327,11 @@ WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint lev
|
|||
const bool needsRespec = false;
|
||||
|
||||
GLenum glError;
|
||||
blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target, level,
|
||||
driverUnpackInfo, xOffset, yOffset, zOffset, &glError);
|
||||
if (!blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target, level,
|
||||
driverUnpackInfo, xOffset, yOffset, zOffset, &glError))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (glError == LOCAL_GL_OUT_OF_MEMORY) {
|
||||
mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.",
|
||||
|
|
|
@ -1849,11 +1849,6 @@ nsPluginHost::GetSpecialType(const nsACString & aMIMEType)
|
|||
return eSpecialType_Silverlight;
|
||||
}
|
||||
|
||||
if (aMIMEType.LowerCaseEqualsASCII("audio/x-pn-realaudio-plugin")) {
|
||||
NS_WARNING("You are loading RealPlayer");
|
||||
return eSpecialType_RealPlayer;
|
||||
}
|
||||
|
||||
if (aMIMEType.LowerCaseEqualsASCII("application/vnd.unity")) {
|
||||
return eSpecialType_Unity;
|
||||
}
|
||||
|
|
|
@ -205,8 +205,6 @@ public:
|
|||
// Some IPC quirks
|
||||
eSpecialType_Silverlight,
|
||||
// Native widget quirks
|
||||
eSpecialType_RealPlayer,
|
||||
// Native widget quirks
|
||||
eSpecialType_Unity };
|
||||
static SpecialType GetSpecialType(const nsACString & aMIMEType);
|
||||
|
||||
|
|
|
@ -129,9 +129,7 @@ public:
|
|||
nsPluginHost::SpecialType mPluginType;
|
||||
};
|
||||
|
||||
static bool sInMessageDispatch = false;
|
||||
static bool sInPreviousMessageDispatch = false;
|
||||
static UINT sLastMsg = 0;
|
||||
|
||||
static bool ProcessFlashMessageDelayed(nsPluginNativeWindowWin * aWin, nsNPAPIPluginInstance * aInst,
|
||||
HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
|
@ -201,16 +199,6 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam
|
|||
RefPtr<nsNPAPIPluginInstance> inst;
|
||||
win->GetPluginInstance(inst);
|
||||
|
||||
// Real may go into a state where it recursivly dispatches the same event
|
||||
// when subclassed. If this is Real, lets examine the event and drop it
|
||||
// on the floor if we get into this recursive situation. See bug 192914.
|
||||
if (win->mPluginType == nsPluginHost::eSpecialType_RealPlayer) {
|
||||
if (sInMessageDispatch && msg == sLastMsg)
|
||||
return true;
|
||||
// Cache the last message sent
|
||||
sLastMsg = msg;
|
||||
}
|
||||
|
||||
bool enablePopups = false;
|
||||
|
||||
// Activate/deactivate mouse capture on the plugin widget
|
||||
|
@ -280,10 +268,6 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam
|
|||
|
||||
case WM_SETFOCUS:
|
||||
case WM_KILLFOCUS: {
|
||||
// RealPlayer can crash, don't process the message for those,
|
||||
// see bug 328675.
|
||||
if (win->mPluginType == nsPluginHost::eSpecialType_RealPlayer && msg == sLastMsg)
|
||||
return TRUE;
|
||||
// Make sure setfocus and killfocus get through to the widget procedure
|
||||
// even if they are eaten by the plugin. Also make sure we aren't calling
|
||||
// recursively.
|
||||
|
@ -313,7 +297,6 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam
|
|||
}
|
||||
}
|
||||
|
||||
sInMessageDispatch = true;
|
||||
LRESULT res;
|
||||
WNDPROC proc = (WNDPROC)win->GetWindowProc();
|
||||
if (PluginWndProc == proc) {
|
||||
|
@ -323,7 +306,6 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam
|
|||
} else {
|
||||
res = CallWindowProc(proc, hWnd, msg, wParam, lParam);
|
||||
}
|
||||
sInMessageDispatch = false;
|
||||
|
||||
if (inst) {
|
||||
// Popups are enabled (were enabled before the call to
|
||||
|
|
|
@ -146,5 +146,25 @@ ImageResource::EvaluateAnimation()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageResource::SendOnUnlockedDraw(uint32_t aFlags)
|
||||
{
|
||||
if (!mProgressTracker) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(aFlags & FLAG_ASYNC_NOTIFY)) {
|
||||
mProgressTracker->OnUnlockedDraw();
|
||||
} else {
|
||||
NotNull<RefPtr<ImageResource>> image = WrapNotNull(this);
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction([=]() -> void {
|
||||
RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
|
||||
if (tracker) {
|
||||
tracker->OnUnlockedDraw();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -305,6 +305,8 @@ protected:
|
|||
virtual nsresult StartAnimation() = 0;
|
||||
virtual nsresult StopAnimation() = 0;
|
||||
|
||||
void SendOnUnlockedDraw(uint32_t aFlags);
|
||||
|
||||
// Member data shared by all implementations of this abstract class
|
||||
RefPtr<ProgressTracker> mProgressTracker;
|
||||
RefPtr<ImageURL> mURI;
|
||||
|
|
|
@ -587,8 +587,8 @@ RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (IsUnlocked() && mProgressTracker) {
|
||||
mProgressTracker->OnUnlockedDraw();
|
||||
if (IsUnlocked()) {
|
||||
SendOnUnlockedDraw(aFlags);
|
||||
}
|
||||
|
||||
RefPtr<layers::ImageContainer> container = mImageContainer.get();
|
||||
|
@ -1343,10 +1343,11 @@ RasterImage::Draw(gfxContext* aContext,
|
|||
return DrawResult::BAD_ARGS;
|
||||
}
|
||||
|
||||
if (IsUnlocked() && mProgressTracker) {
|
||||
mProgressTracker->OnUnlockedDraw();
|
||||
if (IsUnlocked()) {
|
||||
SendOnUnlockedDraw(aFlags);
|
||||
}
|
||||
|
||||
|
||||
// If we're not using SamplingFilter::GOOD, we shouldn't high-quality scale or
|
||||
// downscale during decode.
|
||||
uint32_t flags = aSamplingFilter == SamplingFilter::GOOD
|
||||
|
|
|
@ -835,8 +835,8 @@ VectorImage::Draw(gfxContext* aContext,
|
|||
return DrawResult::TEMPORARY_ERROR;
|
||||
}
|
||||
|
||||
if (mAnimationConsumers == 0 && mProgressTracker) {
|
||||
mProgressTracker->OnUnlockedDraw();
|
||||
if (mAnimationConsumers == 0) {
|
||||
SendOnUnlockedDraw(aFlags);
|
||||
}
|
||||
|
||||
AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing);
|
||||
|
|
|
@ -374,7 +374,7 @@ nsICODecoder::ReadBIH(const char* aData)
|
|||
// tells it to skip this, and pass in the required data (dataOffset) that
|
||||
// would have been present in the header.
|
||||
uint32_t dataOffset = bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE;
|
||||
if (mDirEntry.mBitCount <= 8) {
|
||||
if (mBPP <= 8) {
|
||||
// The color table is present only if BPP is <= 8.
|
||||
uint16_t numColors = GetNumColors();
|
||||
if (numColors == (uint16_t)-1) {
|
||||
|
|
|
@ -340,12 +340,20 @@ nsPNGDecoder::InitInternal()
|
|||
png_set_check_for_invalid_index(mPNG, 0);
|
||||
#endif
|
||||
|
||||
#if defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_sRGB_PROFILE_CHECKS) && \
|
||||
PNG_sRGB_PROFILE_CHECKS >= 0
|
||||
#ifdef PNG_SET_OPTION_SUPPORTED
|
||||
#if defined(PNG_sRGB_PROFILE_CHECKS) && PNG_sRGB_PROFILE_CHECKS >= 0
|
||||
// Skip checking of sRGB ICC profiles
|
||||
png_set_option(mPNG, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
|
||||
#endif
|
||||
|
||||
#ifdef PNG_MAXIMUM_INFLATE_WINDOW
|
||||
// Force a larger zlib inflate window as some images in the wild have
|
||||
// incorrectly set metadata (specifically CMF bits) which prevent us from
|
||||
// decoding them otherwise.
|
||||
png_set_option(mPNG, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// use this as libpng "progressive pointer" (retrieve in callbacks)
|
||||
png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this),
|
||||
nsPNGDecoder::info_callback,
|
||||
|
|
|
@ -558,6 +558,15 @@ ImageTestCase CorruptICOWithBadBMPHeightTestCase()
|
|||
IntSize(100, 100), TEST_CASE_HAS_ERROR);
|
||||
}
|
||||
|
||||
ImageTestCase CorruptICOWithBadBppTestCase()
|
||||
{
|
||||
// This test case is an ICO with a BPP (15) in the ICO header which differs
|
||||
// from that in the BMP header itself (1). It should ignore the ICO BPP when
|
||||
// the BMP BPP is available and thus correctly decode the image.
|
||||
return ImageTestCase("corrupt-with-bad-ico-bpp.ico", "image/x-icon",
|
||||
IntSize(100, 100), TEST_CASE_IS_TRANSPARENT);
|
||||
}
|
||||
|
||||
ImageTestCase TransparentPNGTestCase()
|
||||
{
|
||||
return ImageTestCase("transparent.png", "image/png", IntSize(32, 32),
|
||||
|
|
|
@ -392,6 +392,7 @@ ImageTestCase CorruptTestCase();
|
|||
ImageTestCase CorruptBMPWithTruncatedHeader();
|
||||
ImageTestCase CorruptICOWithBadBMPWidthTestCase();
|
||||
ImageTestCase CorruptICOWithBadBMPHeightTestCase();
|
||||
ImageTestCase CorruptICOWithBadBppTestCase();
|
||||
|
||||
ImageTestCase TransparentPNGTestCase();
|
||||
ImageTestCase TransparentGIFTestCase();
|
||||
|
|
|
@ -364,6 +364,11 @@ TEST_F(ImageDecoders, CorruptICOWithBadBMPHeightMultiChunk)
|
|||
CheckDecoderMultiChunk(CorruptICOWithBadBMPHeightTestCase());
|
||||
}
|
||||
|
||||
TEST_F(ImageDecoders, CorruptICOWithBadBppSingleChunk)
|
||||
{
|
||||
CheckDecoderSingleChunk(CorruptICOWithBadBppTestCase());
|
||||
}
|
||||
|
||||
TEST_F(ImageDecoders, AnimatedGIFWithFRAME_FIRST)
|
||||
{
|
||||
ImageTestCase testCase = GreenFirstFrameAnimatedGIFTestCase();
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.2 KiB |
|
@ -35,6 +35,7 @@ TEST_HARNESS_FILES.gtest += [
|
|||
'animated-with-extra-image-sub-blocks.gif',
|
||||
'corrupt-with-bad-bmp-height.ico',
|
||||
'corrupt-with-bad-bmp-width.ico',
|
||||
'corrupt-with-bad-ico-bpp.ico',
|
||||
'corrupt.jpg',
|
||||
'downscaled.bmp',
|
||||
'downscaled.gif',
|
||||
|
|
|
@ -110,6 +110,10 @@ resumption value has one of the following forms:
|
|||
the `new` expression returns the frame's `this` value. Similarly, if
|
||||
the function is the constructor for a subclass, then a non-object
|
||||
value may result in a TypeError.
|
||||
If the frame is a generator or async function, then <i>value</i> must
|
||||
conform to the iterator protocol: it must be a non-proxy object of the form
|
||||
<code>{ done: <i>boolean</i>, value: <i>v</i> }</code>, where
|
||||
both `done` and `value` are ordinary properties.
|
||||
|
||||
<code>{ throw: <i>value</i> }</code>
|
||||
: Throw <i>value</i> as an exception from the current bytecode
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
load(libdir + "asserts.js");
|
||||
|
||||
var g = newGlobal();
|
||||
var dbg = Debugger(g);
|
||||
|
||||
g.eval(`
|
||||
async function f() {
|
||||
return e;
|
||||
}
|
||||
`);
|
||||
|
||||
// To continue testing after uncaught exception, remember the exception and
|
||||
// return normal completeion.
|
||||
var currentFrame;
|
||||
var uncaughtException;
|
||||
dbg.uncaughtExceptionHook = function(e) {
|
||||
uncaughtException = e;
|
||||
return {
|
||||
return: currentFrame.eval("({ done: true, value: 'uncaught' })").return
|
||||
};
|
||||
};
|
||||
function testUncaughtException() {
|
||||
uncaughtException = undefined;
|
||||
var val = g.eval(`
|
||||
var val;
|
||||
f().then(v => { val = v });
|
||||
drainJobQueue();
|
||||
val;
|
||||
`);
|
||||
assertEq(val, "uncaught");
|
||||
assertEq(uncaughtException instanceof TypeError, true);
|
||||
}
|
||||
|
||||
// Just continue
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
return undefined;
|
||||
};
|
||||
g.eval(`
|
||||
var E;
|
||||
f().catch(e => { exc = e });
|
||||
drainJobQueue();
|
||||
assertEq(exc instanceof ReferenceError, true);
|
||||
`);
|
||||
|
||||
// Should return object.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: "foo"
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// The object should have `done` property and `value` property.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({})").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// The object should have `done` property.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ value: 10 })").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// The object should have `value` property.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ done: true })").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// `done` property should be a boolean value.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ done: 10, value: 10 })").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// `done` property shouldn't be an accessor.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ get done() { return true; }, value: 10 })").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// `value` property shouldn't be an accessor.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ done: true, get value() { return 10; } })").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// The object shouldn't be a Proxy.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("new Proxy({ done: true, value: 10 }, {})").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// Correct resumption value.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ done: true, value: 10 })").return
|
||||
};
|
||||
};
|
||||
var val = g.eval(`
|
||||
var val;
|
||||
f().then(v => { val = v });
|
||||
drainJobQueue();
|
||||
val;
|
||||
`);
|
||||
assertEq(val, 10);
|
|
@ -0,0 +1,117 @@
|
|||
load(libdir + "asserts.js");
|
||||
|
||||
var g = newGlobal();
|
||||
var dbg = Debugger(g);
|
||||
|
||||
g.eval(`
|
||||
function* f() {
|
||||
e;
|
||||
}
|
||||
`);
|
||||
|
||||
// To continue testing after uncaught exception, remember the exception and
|
||||
// return normal completeion.
|
||||
var currentFrame;
|
||||
var uncaughtException;
|
||||
dbg.uncaughtExceptionHook = function(e) {
|
||||
uncaughtException = e;
|
||||
return {
|
||||
return: currentFrame.eval("({ done: true, value: 'uncaught' })").return
|
||||
};
|
||||
};
|
||||
function testUncaughtException() {
|
||||
uncaughtException = undefined;
|
||||
var obj = g.eval(`f().next()`);
|
||||
assertEq(obj.done, true);
|
||||
assertEq(obj.value, 'uncaught');
|
||||
assertEq(uncaughtException instanceof TypeError, true);
|
||||
}
|
||||
|
||||
// Just continue
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
return undefined;
|
||||
};
|
||||
assertThrowsInstanceOf(() => g.eval(`f().next();`), g.ReferenceError);
|
||||
|
||||
// Should return object.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: "foo"
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// The object should have `done` property and `value` property.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({})").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// The object should have `done` property.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ value: 10 })").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// The object should have `value` property.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ done: true })").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// `done` property should be a boolean value.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ done: 10, value: 10 })").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// `done` property shouldn't be an accessor.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ get done() { return true; }, value: 10 })").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// `value` property shouldn't be an accessor.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ done: true, get value() { return 10; } })").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// The object shouldn't be a Proxy.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("new Proxy({ done: true, value: 10 }, {})").return
|
||||
};
|
||||
};
|
||||
testUncaughtException();
|
||||
|
||||
// Correct resumption value.
|
||||
dbg.onExceptionUnwind = function(frame) {
|
||||
currentFrame = frame;
|
||||
return {
|
||||
return: frame.eval("({ done: true, value: 10 })").return
|
||||
};
|
||||
};
|
||||
var obj = g.eval(`f().next()`);
|
||||
assertEq(obj.done, true);
|
||||
assertEq(obj.value, 10);
|
|
@ -7,7 +7,7 @@ load(libdir + 'iteration.js')
|
|||
var g = newGlobal();
|
||||
g.debuggeeGlobal = this;
|
||||
g.eval("var dbg = new Debugger(debuggeeGlobal);" +
|
||||
"dbg.onDebuggerStatement = function () { return {return: '!'}; };");
|
||||
"dbg.onDebuggerStatement = function (frame) { return { return: frame.eval(\"({ done: true, value: '!' })\").return }; };");
|
||||
|
||||
function* gen() {
|
||||
yield '1';
|
||||
|
@ -16,6 +16,6 @@ function* gen() {
|
|||
}
|
||||
var iter = gen();
|
||||
assertIteratorNext(iter, '1');
|
||||
assertEq(iter.next(), '!');
|
||||
assertIteratorDone(iter, '!');
|
||||
iter.next();
|
||||
assertEq(0, 1);
|
||||
|
|
|
@ -432,10 +432,12 @@ MSG_DEF(JSMSG_SC_SAB_DISABLED, 0, JSEXN_TYPEERR, "SharedArrayBuffer not
|
|||
|
||||
// Debugger
|
||||
MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_AWAIT, 0, JSEXN_TYPEERR, "await expression received invalid value")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_LINE, 0, JSEXN_TYPEERR, "invalid line number")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_OFFSET, 0, JSEXN_TYPEERR, "invalid script offset")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 2, JSEXN_TYPEERR, "{0} does not refer to {1}")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_RESUMPTION, 0, JSEXN_TYPEERR, "debugger resumption value must be undefined, {throw: val}, {return: val}, or null")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_YIELD, 0, JSEXN_TYPEERR, "generator yielded invalid value")
|
||||
MSG_DEF(JSMSG_DEBUG_CANT_DEBUG_GLOBAL, 0, JSEXN_TYPEERR, "passing non-debuggable global to addDebuggee")
|
||||
MSG_DEF(JSMSG_DEBUG_CCW_REQUIRED, 1, JSEXN_TYPEERR, "{0}: argument must be an object from a different compartment")
|
||||
MSG_DEF(JSMSG_DEBUG_COMPARTMENT_MISMATCH, 2, JSEXN_TYPEERR, "{0}: descriptor .{1} property is an object in a different compartment than the target object")
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "jscompartment.h"
|
||||
|
||||
#include "builtin/Promise.h"
|
||||
#include "vm/GeneratorObject.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
|
@ -232,3 +233,8 @@ js::IsWrappedAsyncFunction(JSFunction* fun)
|
|||
return fun->maybeNative() == WrappedAsyncFunction;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
js::CheckAsyncResumptionValue(JSContext* cx, HandleValue v)
|
||||
{
|
||||
return CheckStarGeneratorResumptionValue(cx, v);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ MOZ_MUST_USE bool
|
|||
AsyncFunctionAwaitedRejected(JSContext* cx, Handle<PromiseObject*> resultPromise,
|
||||
HandleValue generatorVal, HandleValue reason);
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
CheckAsyncResumptionValue(JSContext* cx, HandleValue v);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* vm_AsyncFunction_h */
|
||||
|
|
|
@ -32,7 +32,9 @@
|
|||
#include "js/Vector.h"
|
||||
#include "proxy/ScriptedProxyHandler.h"
|
||||
#include "vm/ArgumentsObject.h"
|
||||
#include "vm/AsyncFunction.h"
|
||||
#include "vm/DebuggerMemory.h"
|
||||
#include "vm/GeneratorObject.h"
|
||||
#include "vm/SPSProfiler.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
|
@ -1557,6 +1559,24 @@ static bool
|
|||
CheckResumptionValue(JSContext* cx, AbstractFramePtr frame, const Maybe<HandleValue>& maybeThisv,
|
||||
JSTrapStatus status, MutableHandleValue vp)
|
||||
{
|
||||
if (status == JSTRAP_RETURN && frame && frame.isFunctionFrame()) {
|
||||
// Don't let a { return: ... } resumption value make a generator or
|
||||
// async function violate the iterator protocol. The return value from
|
||||
// such a frame must have the form { done: <bool>, value: <anything> }.
|
||||
RootedFunction callee(cx, frame.callee());
|
||||
if (callee->isAsync()) {
|
||||
if (!CheckAsyncResumptionValue(cx, vp)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_AWAIT);
|
||||
return false;
|
||||
}
|
||||
} else if (callee->isStarGenerator()) {
|
||||
if (!CheckStarGeneratorResumptionValue(cx, vp)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maybeThisv.isSome()) {
|
||||
const HandleValue& thisv = maybeThisv.ref();
|
||||
if (status == JSTRAP_RETURN && vp.isPrimitive()) {
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "vm/GeneratorObject.h"
|
||||
|
||||
#include "jsobj.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
|
@ -334,3 +336,32 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
|
|||
global->setReservedSlot(STAR_GENERATOR_FUNCTION_PROTO, ObjectValue(*genFunctionProto));
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
js::CheckStarGeneratorResumptionValue(JSContext* cx, HandleValue v)
|
||||
{
|
||||
// yield/return value should be an Object.
|
||||
if (!v.isObject())
|
||||
return false;
|
||||
|
||||
JSObject* obj = &v.toObject();
|
||||
|
||||
// It should have `done` data property with boolean value.
|
||||
Value doneVal;
|
||||
if (!GetPropertyPure(cx, obj, NameToId(cx->names().done), &doneVal))
|
||||
return false;
|
||||
if (!doneVal.isBoolean())
|
||||
return false;
|
||||
|
||||
// It should have `value` data property, but the type doesn't matter
|
||||
JSObject* ignored;
|
||||
Shape* shape;
|
||||
if (!LookupPropertyPure(cx, obj, NameToId(cx->names().value), &ignored, &shape))
|
||||
return false;
|
||||
if (!shape)
|
||||
return false;
|
||||
if (!shape->hasDefaultGetter())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -216,6 +216,9 @@ bool GeneratorThrowOrClose(JSContext* cx, AbstractFramePtr frame, Handle<Generat
|
|||
HandleValue val, uint32_t resumeKind);
|
||||
void SetReturnValueForClosingGenerator(JSContext* cx, AbstractFramePtr frame);
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
CheckStarGeneratorResumptionValue(JSContext* cx, HandleValue v);
|
||||
|
||||
} // namespace js
|
||||
|
||||
template<>
|
||||
|
|
|
@ -147,7 +147,6 @@ using namespace mozilla::gfx;
|
|||
#define GRID_ENABLED_PREF_NAME "layout.css.grid.enabled"
|
||||
#define GRID_TEMPLATE_SUBGRID_ENABLED_PREF_NAME "layout.css.grid-template-subgrid-value.enabled"
|
||||
#define WEBKIT_PREFIXES_ENABLED_PREF_NAME "layout.css.prefixes.webkit"
|
||||
#define DISPLAY_CONTENTS_ENABLED_PREF_NAME "layout.css.display-contents.enabled"
|
||||
#define TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME "layout.css.text-align-unsafe-value.enabled"
|
||||
#define FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME "layout.css.float-logical-values.enabled"
|
||||
#define BG_CLIP_TEXT_ENABLED_PREF_NAME "layout.css.background-clip-text.enabled"
|
||||
|
@ -312,36 +311,6 @@ WebkitPrefixEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
|
|||
}
|
||||
}
|
||||
|
||||
// When the pref "layout.css.display-contents.enabled" changes, this function is
|
||||
// invoked to let us update kDisplayKTable, to selectively disable or restore
|
||||
// the entries for "contents" in that table.
|
||||
static void
|
||||
DisplayContentsEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
|
||||
{
|
||||
NS_ASSERTION(strcmp(aPrefName, DISPLAY_CONTENTS_ENABLED_PREF_NAME) == 0,
|
||||
"Did you misspell " DISPLAY_CONTENTS_ENABLED_PREF_NAME " ?");
|
||||
|
||||
static bool sIsDisplayContentsKeywordIndexInitialized;
|
||||
static int32_t sIndexOfContentsInDisplayTable;
|
||||
bool isDisplayContentsEnabled =
|
||||
Preferences::GetBool(DISPLAY_CONTENTS_ENABLED_PREF_NAME, false);
|
||||
|
||||
if (!sIsDisplayContentsKeywordIndexInitialized) {
|
||||
// First run: find the position of "contents" in kDisplayKTable.
|
||||
sIndexOfContentsInDisplayTable =
|
||||
nsCSSProps::FindIndexOfKeyword(eCSSKeyword_contents,
|
||||
nsCSSProps::kDisplayKTable);
|
||||
sIsDisplayContentsKeywordIndexInitialized = true;
|
||||
}
|
||||
|
||||
// OK -- now, stomp on or restore the "contents" entry in kDisplayKTable,
|
||||
// depending on whether the pref is enabled vs. disabled.
|
||||
if (sIndexOfContentsInDisplayTable >= 0) {
|
||||
nsCSSProps::kDisplayKTable[sIndexOfContentsInDisplayTable].mKeyword =
|
||||
isDisplayContentsEnabled ? eCSSKeyword_contents : eCSSKeyword_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// When the pref "layout.css.text-align-unsafe-value.enabled" changes, this
|
||||
// function is called to let us update kTextAlignKTable & kTextAlignLastKTable,
|
||||
// to selectively disable or restore the entries for "unsafe" in those tables.
|
||||
|
@ -7536,8 +7505,6 @@ static const PrefCallbacks kPrefCallbacks[] = {
|
|||
WebkitPrefixEnabledPrefChangeCallback },
|
||||
{ TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME,
|
||||
TextAlignUnsafeEnabledPrefChangeCallback },
|
||||
{ DISPLAY_CONTENTS_ENABLED_PREF_NAME,
|
||||
DisplayContentsEnabledPrefChangeCallback },
|
||||
{ FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME,
|
||||
FloatLogicalValuesEnabledPrefChangeCallback },
|
||||
{ BG_CLIP_TEXT_ENABLED_PREF_NAME,
|
||||
|
|
|
@ -2,36 +2,36 @@
|
|||
# Tests for CSS Display spec features.
|
||||
# http://dev.w3.org/csswg/css-display
|
||||
|
||||
fuzzy-if(Android,8,604) pref(layout.css.display-contents.enabled,true) == display-contents-acid.html display-contents-acid.html
|
||||
random pref(layout.css.display-contents.enabled,true) == display-contents-acid-dyn-1.html display-contents-acid-dyn-1.html
|
||||
random pref(layout.css.display-contents.enabled,true) == display-contents-acid-dyn-2.html display-contents-acid-dyn-2.html
|
||||
random pref(layout.css.display-contents.enabled,true) == display-contents-acid-dyn-3.html display-contents-acid-dyn-3.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-generated-content.html display-contents-generated-content.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-generated-content-2.html display-contents-generated-content-2.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-style-inheritance-1.html display-contents-style-inheritance-1.html
|
||||
skip pref(layout.css.display-contents.enabled,true) == display-contents-style-inheritance-1-stylechange.html display-contents-style-inheritance-1-stylechange.html
|
||||
skip pref(layout.css.display-contents.enabled,true) fuzzy-if(winWidget,12,100) == display-contents-style-inheritance-1-dom-mutations.html display-contents-style-inheritance-1-dom-mutations.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-tables.xhtml display-contents-tables.xhtml
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-tables-2.xhtml display-contents-tables-2.xhtml
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-tables-3.xhtml display-contents-tables-3.xhtml
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-visibility-hidden.html display-contents-visibility-hidden.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-visibility-hidden-2.html display-contents-visibility-hidden-2.html
|
||||
random pref(layout.css.display-contents.enabled,true) == display-contents-495385-2d.html display-contents-495385-2d.html
|
||||
skip-if(B2G||Mulet) fuzzy-if(Android,7,3935) pref(layout.css.display-contents.enabled,true) == display-contents-xbl.xhtml display-contents-xbl.xhtml
|
||||
fuzzy-if(Android,8,604) == display-contents-acid.html display-contents-acid.html
|
||||
random == display-contents-acid-dyn-1.html display-contents-acid-dyn-1.html
|
||||
random == display-contents-acid-dyn-2.html display-contents-acid-dyn-2.html
|
||||
random == display-contents-acid-dyn-3.html display-contents-acid-dyn-3.html
|
||||
== display-contents-generated-content.html display-contents-generated-content.html
|
||||
== display-contents-generated-content-2.html display-contents-generated-content-2.html
|
||||
== display-contents-style-inheritance-1.html display-contents-style-inheritance-1.html
|
||||
skip == display-contents-style-inheritance-1-stylechange.html display-contents-style-inheritance-1-stylechange.html
|
||||
skip fuzzy-if(winWidget,12,100) == display-contents-style-inheritance-1-dom-mutations.html display-contents-style-inheritance-1-dom-mutations.html
|
||||
== display-contents-tables.xhtml display-contents-tables.xhtml
|
||||
== display-contents-tables-2.xhtml display-contents-tables-2.xhtml
|
||||
== display-contents-tables-3.xhtml display-contents-tables-3.xhtml
|
||||
== display-contents-visibility-hidden.html display-contents-visibility-hidden.html
|
||||
== display-contents-visibility-hidden-2.html display-contents-visibility-hidden-2.html
|
||||
random == display-contents-495385-2d.html display-contents-495385-2d.html
|
||||
skip-if(B2G||Mulet) fuzzy-if(Android,7,3935) == display-contents-xbl.xhtml display-contents-xbl.xhtml
|
||||
# Initial mulet triage: parity with B2G/B2G Desktop
|
||||
# fuzzy-if(Android,7,1186) pref(layout.css.display-contents.enabled,true) pref(dom.webcomponents.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1.html
|
||||
skip-if(B2G||Mulet) pref(layout.css.display-contents.enabled,true) == display-contents-xbl-2.xul display-contents-xbl-2.xul
|
||||
# fuzzy-if(Android,7,1186) pref(dom.webcomponents.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1.html
|
||||
skip-if(B2G||Mulet) == display-contents-xbl-2.xul display-contents-xbl-2.xul
|
||||
# Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) asserts(1) pref(layout.css.display-contents.enabled,true) == display-contents-xbl-3.xul display-contents-xbl-3.xul
|
||||
skip-if(B2G||Mulet) asserts(1) == display-contents-xbl-3.xul display-contents-xbl-3.xul
|
||||
# bug 1089223
|
||||
# Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip pref(layout.css.display-contents.enabled,true) == display-contents-xbl-4.xul display-contents-xbl-4.xul
|
||||
skip == display-contents-xbl-4.xul display-contents-xbl-4.xul
|
||||
# fails (not just asserts) due to bug 1089223
|
||||
asserts(0-1) fuzzy-if(Android,8,3216) pref(layout.css.display-contents.enabled,true) == display-contents-fieldset.html display-contents-fieldset.html
|
||||
asserts(0-1) fuzzy-if(Android,8,3216) == display-contents-fieldset.html display-contents-fieldset.html
|
||||
# bug 1089223
|
||||
skip-if(B2G||Mulet) asserts(1) pref(layout.css.display-contents.enabled,true) == display-contents-xbl-5.xul display-contents-xbl-5.xul
|
||||
skip-if(B2G||Mulet) asserts(1) == display-contents-xbl-5.xul display-contents-xbl-5.xul
|
||||
# bug 1089223
|
||||
# Initial mulet triage: parity with B2G/B2G Desktop
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-list-item-child.html display-contents-list-item-child.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-writing-mode-1.html display-contents-writing-mode-1.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-writing-mode-2.html display-contents-writing-mode-2.html
|
||||
== display-contents-list-item-child.html display-contents-list-item-child.html
|
||||
== display-contents-writing-mode-1.html display-contents-writing-mode-1.html
|
||||
== display-contents-writing-mode-2.html display-contents-writing-mode-2.html
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
# Tests for CSS Display spec features.
|
||||
# http://dev.w3.org/csswg/css-display
|
||||
|
||||
fuzzy-if(Android,8,604) pref(layout.css.display-contents.enabled,true) == display-contents-acid.html display-contents-acid-ref.html
|
||||
fuzzy-if(Android,8,604) pref(layout.css.display-contents.enabled,true) == display-contents-acid-dyn-1.html display-contents-acid-ref.html
|
||||
fuzzy-if(Android,8,604) pref(layout.css.display-contents.enabled,true) == display-contents-acid-dyn-2.html display-contents-acid-ref.html
|
||||
fuzzy-if(Android,8,604) pref(layout.css.display-contents.enabled,true) == display-contents-acid-dyn-3.html display-contents-acid-ref.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-generated-content.html display-contents-generated-content-ref.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-generated-content-2.html display-contents-generated-content-ref.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-style-inheritance-1.html display-contents-style-inheritance-1-ref.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-style-inheritance-1-stylechange.html display-contents-style-inheritance-1-ref.html
|
||||
pref(layout.css.display-contents.enabled,true) fuzzy-if(winWidget,12,100) == display-contents-style-inheritance-1-dom-mutations.html display-contents-style-inheritance-1-ref.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-tables.xhtml display-contents-tables-ref.xhtml
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-tables-2.xhtml display-contents-tables-ref.xhtml
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-tables-3.xhtml display-contents-tables-3-ref.xhtml
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-visibility-hidden.html display-contents-visibility-hidden-ref.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-visibility-hidden-2.html display-contents-visibility-hidden-ref.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-495385-2d.html display-contents-495385-2d-ref.html
|
||||
fuzzy-if(Android,7,3935) pref(layout.css.display-contents.enabled,true) == display-contents-xbl.xhtml display-contents-xbl-ref.html
|
||||
fuzzy-if(Android,7,1186) pref(layout.css.display-contents.enabled,true) pref(dom.webcomponents.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1-ref.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-xbl-2.xul display-contents-xbl-2-ref.xul
|
||||
asserts(1) pref(layout.css.display-contents.enabled,true) == display-contents-xbl-3.xul display-contents-xbl-3-ref.xul # bug 1089223
|
||||
skip pref(layout.css.display-contents.enabled,true) == display-contents-xbl-4.xul display-contents-xbl-4-ref.xul # fails (not just asserts) due to bug 1089223
|
||||
asserts(0-1) fuzzy-if(Android,8,3216) pref(layout.css.display-contents.enabled,true) == display-contents-fieldset.html display-contents-fieldset-ref.html # bug 1089223
|
||||
asserts(1) pref(layout.css.display-contents.enabled,true) == display-contents-xbl-5.xul display-contents-xbl-3-ref.xul # bug 1089223
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-list-item-child.html display-contents-list-item-child-ref.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-writing-mode-1.html display-contents-writing-mode-1-ref.html
|
||||
pref(layout.css.display-contents.enabled,true) == display-contents-writing-mode-2.html display-contents-writing-mode-2-ref.html
|
||||
fuzzy-if(Android,8,604) == display-contents-acid.html display-contents-acid-ref.html
|
||||
fuzzy-if(Android,8,604) == display-contents-acid-dyn-1.html display-contents-acid-ref.html
|
||||
fuzzy-if(Android,8,604) == display-contents-acid-dyn-2.html display-contents-acid-ref.html
|
||||
fuzzy-if(Android,8,604) == display-contents-acid-dyn-3.html display-contents-acid-ref.html
|
||||
== display-contents-generated-content.html display-contents-generated-content-ref.html
|
||||
== display-contents-generated-content-2.html display-contents-generated-content-ref.html
|
||||
== display-contents-style-inheritance-1.html display-contents-style-inheritance-1-ref.html
|
||||
== display-contents-style-inheritance-1-stylechange.html display-contents-style-inheritance-1-ref.html
|
||||
fuzzy-if(winWidget,12,100) == display-contents-style-inheritance-1-dom-mutations.html display-contents-style-inheritance-1-ref.html
|
||||
== display-contents-tables.xhtml display-contents-tables-ref.xhtml
|
||||
== display-contents-tables-2.xhtml display-contents-tables-ref.xhtml
|
||||
== display-contents-tables-3.xhtml display-contents-tables-3-ref.xhtml
|
||||
== display-contents-visibility-hidden.html display-contents-visibility-hidden-ref.html
|
||||
== display-contents-visibility-hidden-2.html display-contents-visibility-hidden-ref.html
|
||||
== display-contents-495385-2d.html display-contents-495385-2d-ref.html
|
||||
fuzzy-if(Android,7,3935) == display-contents-xbl.xhtml display-contents-xbl-ref.html
|
||||
fuzzy-if(Android,7,1186) pref(dom.webcomponents.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1-ref.html
|
||||
== display-contents-xbl-2.xul display-contents-xbl-2-ref.xul
|
||||
asserts(1) == display-contents-xbl-3.xul display-contents-xbl-3-ref.xul # bug 1089223
|
||||
skip == display-contents-xbl-4.xul display-contents-xbl-4-ref.xul # fails (not just asserts) due to bug 1089223
|
||||
asserts(0-1) fuzzy-if(Android,8,3216) == display-contents-fieldset.html display-contents-fieldset-ref.html # bug 1089223
|
||||
asserts(1) == display-contents-xbl-5.xul display-contents-xbl-3-ref.xul # bug 1089223
|
||||
== display-contents-list-item-child.html display-contents-list-item-child-ref.html
|
||||
== display-contents-writing-mode-1.html display-contents-writing-mode-1-ref.html
|
||||
== display-contents-writing-mode-2.html display-contents-writing-mode-2-ref.html
|
||||
|
|
|
@ -1330,8 +1330,6 @@ KTableEntry nsCSSProps::kDisplayKTable[] = {
|
|||
{ eCSSKeyword__webkit_inline_box, StyleDisplay::WebkitInlineBox },
|
||||
{ eCSSKeyword__webkit_flex, StyleDisplay::Flex },
|
||||
{ eCSSKeyword__webkit_inline_flex, StyleDisplay::InlineFlex },
|
||||
// The next entry is controlled by the layout.css.display-contents.enabled
|
||||
// pref.
|
||||
{ eCSSKeyword_contents, StyleDisplay::Contents },
|
||||
{ eCSSKeyword_UNKNOWN, -1 }
|
||||
};
|
||||
|
|
|
@ -6671,9 +6671,7 @@ if (IsCSSPropertyPrefEnabled("layout.css.grid.enabled")) {
|
|||
};
|
||||
}
|
||||
|
||||
if (IsCSSPropertyPrefEnabled("layout.css.display-contents.enabled")) {
|
||||
gCSSProperties["display"].other_values.push("contents");
|
||||
}
|
||||
gCSSProperties["display"].other_values.push("contents");
|
||||
|
||||
if (IsCSSPropertyPrefEnabled("layout.css.contain.enabled")) {
|
||||
gCSSProperties["contain"] = {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#define PNG_LINKAGE_DATA extern
|
||||
#define PNG_LINKAGE_FUNCTION extern
|
||||
#define PNG_MAX_GAMMA_8 11
|
||||
#define PNG_SET_OPTION_SUPPORTED
|
||||
#define PNG_sRGB_PROFILE_CHECKS -1
|
||||
#define PNG_USER_CHUNK_CACHE_MAX 128
|
||||
#define PNG_USER_CHUNK_MALLOC_MAX 4000000L
|
||||
|
|
|
@ -2619,9 +2619,6 @@ pref("layout.css.grid-template-subgrid-value.enabled", false);
|
|||
// Is support for CSS contain enabled?
|
||||
pref("layout.css.contain.enabled", false);
|
||||
|
||||
// Is support for CSS display:contents enabled?
|
||||
pref("layout.css.display-contents.enabled", true);
|
||||
|
||||
// Is support for CSS box-decoration-break enabled?
|
||||
pref("layout.css.box-decoration-break.enabled", true);
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ mochitest:
|
|||
instance-size: xlarge
|
||||
chunks:
|
||||
by-test-platform:
|
||||
android-4.3-arm7-api-15/debug: 26
|
||||
android-4.3-arm7-api-15/debug: 32
|
||||
default: 20
|
||||
loopback-video: true
|
||||
e10s: false
|
||||
|
|
|
@ -130,6 +130,7 @@ class ChecksumsGenerator(BaseScript, VirtualenvMixin, SigningMixin, VCSMixin, Bu
|
|||
r"^.*\.mar$",
|
||||
r"^.*Setup.*\.exe$",
|
||||
r"^.*\.xpi$",
|
||||
r"^.*fennec.*\.apk$",
|
||||
]
|
||||
|
||||
def _get_bucket_name(self):
|
||||
|
|
|
@ -49,6 +49,7 @@ class ReleasePusher(BaseScript, VirtualenvMixin):
|
|||
r"^.*/host.*$",
|
||||
r"^.*/mar-tools/.*$",
|
||||
r"^.*robocop.apk$",
|
||||
r"^.*bouncer.apk$",
|
||||
r"^.*contrib.*",
|
||||
r"^.*/beetmover-checksums/.*$",
|
||||
],
|
||||
|
|
|
@ -115,6 +115,22 @@ public:
|
|||
return ent->mData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add key to the table if not already present, and return a reference to its
|
||||
* value. If key is not already in the table then the value is default
|
||||
* constructed.
|
||||
*/
|
||||
DataType& GetOrInsert(const KeyType& aKey)
|
||||
{
|
||||
EntryType* ent = this->GetEntry(aKey);
|
||||
if (ent) {
|
||||
return ent->mData;
|
||||
}
|
||||
|
||||
ent = this->PutEntry(aKey);
|
||||
return ent->mData;
|
||||
}
|
||||
|
||||
/**
|
||||
* put a new value for the associated key
|
||||
* @param aKey the key to put
|
||||
|
|
Загрузка…
Ссылка в новой задаче