Merge from mozilla-central.

This commit is contained in:
Andreas Gal
2010-10-22 19:46:37 -07:00
1477 changed files with 56495 additions and 16042 deletions

View File

@@ -68,6 +68,10 @@ ifndef LIBXUL_SDK
tier_base_dirs += \
memory \
$(NULL)
ifeq ($(OS_TARGET),Android)
tier_base_dirs += other-licenses/android
endif
endif
ifdef COMPILE_ENVIRONMENT
@@ -168,7 +172,7 @@ EXTRA_BUILDID := -$(MOZ_SYMBOLS_EXTRA_BUILDID)
endif
SYMBOL_INDEX_NAME = \
$(MOZ_APP_NAME)-$(MOZ_APP_VERSION)-$(OS_ARCH)-$(BUILDID)$(EXTRA_BUILDID)-symbols.txt
$(MOZ_APP_NAME)-$(MOZ_APP_VERSION)-$(OS_TARGET)-$(BUILDID)$(EXTRA_BUILDID)-symbols.txt
buildsymbols:
ifdef MOZ_CRASHREPORTER

View File

@@ -109,8 +109,7 @@ public:
virtual already_AddRefed<nsAccessible>
CreateHTMLLabelAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0;
virtual already_AddRefed<nsAccessible>
CreateHTMLLIAccessible(nsIContent* aContent, nsIPresShell* aPresShell,
const nsAString& aBulletText) = 0;
CreateHTMLLIAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0;
virtual already_AddRefed<nsAccessible>
CreateHTMLListboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0;
virtual already_AddRefed<nsAccessible>
@@ -141,27 +140,19 @@ public:
virtual void RemoveNativeRootAccessible(nsAccessible* aRootAccessible) = 0;
/**
* Used to describe sort of changes leading to accessible tree invalidation.
* Notification used to update the accessible tree when new content is
* inserted.
*/
enum {
NODE_APPEND = 0x01,
NODE_REMOVE = 0x02,
NODE_SIGNIFICANT_CHANGE = 0x03,
FRAME_SHOW = 0x04,
FRAME_HIDE = 0x05,
FRAME_SIGNIFICANT_CHANGE = 0x06
};
virtual void ContentRangeInserted(nsIPresShell* aPresShell,
nsIContent* aContainer,
nsIContent* aStartChild,
nsIContent* aEndChild) = 0;
/**
* Invalidate the accessible tree when DOM tree or frame tree is changed.
*
* @param aPresShell [in] the presShell where changes occurred
* @param aContent [in] the affected DOM content
* @param aChangeType [in] the change type (see constants declared above)
* Notification used to update the accessible tree when content is removed.
*/
virtual nsresult InvalidateSubtreeFor(nsIPresShell *aPresShell,
nsIContent *aContent,
PRUint32 aChangeType) = 0;
virtual void ContentRemoved(nsIPresShell* aPresShell, nsIContent* aContainer,
nsIContent* aChild) = 0;
/**
* Notify accessibility that anchor jump has been accomplished to the given
@@ -175,6 +166,12 @@ public:
*/
virtual void PresShellDestroyed(nsIPresShell *aPresShell) = 0;
/**
* Recreate an accessible for the given content node in the presshell.
*/
virtual void RecreateAccessible(nsIPresShell* aPresShell,
nsIContent* aContent) = 0;
/**
* Fire accessible event of the given type for the given target.
*

View File

@@ -75,5 +75,7 @@ endif
include $(topsrcdir)/config/rules.mk
GARBAGE += $(LIB_PREFIX)accessibility_toolkit_s.$(LIB_SUFFIX) $(LIB_PREFIX)accessibility_toolkit_s.$(LIB_SUFFIX).fake
libs::
$(INSTALL) $(PLATFORM_DIR)/$(LIB_PREFIX)accessibility_toolkit_s.$(LIB_SUFFIX) $(wildcard $(PLATFORM_DIR)/$(LIB_PREFIX)accessibility_toolkit_s.$(LIB_SUFFIX).fake) .

View File

@@ -64,19 +64,15 @@
// AccEvent constructors
AccEvent::AccEvent(PRUint32 aEventType, nsAccessible* aAccessible,
PRBool aIsAsync, EIsFromUserInput aIsFromUserInput,
EEventRule aEventRule) :
mEventType(aEventType), mEventRule(aEventRule), mIsAsync(aIsAsync),
mAccessible(aAccessible)
EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) :
mEventType(aEventType), mEventRule(aEventRule), mAccessible(aAccessible)
{
CaptureIsFromUserInput(aIsFromUserInput);
}
AccEvent::AccEvent(PRUint32 aEventType, nsINode* aNode,
PRBool aIsAsync, EIsFromUserInput aIsFromUserInput,
EEventRule aEventRule) :
mEventType(aEventType), mEventRule(aEventRule), mIsAsync(aIsAsync),
mNode(aNode)
EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) :
mEventType(aEventType), mEventRule(aEventRule), mNode(aNode)
{
CaptureIsFromUserInput(aIsFromUserInput);
}
@@ -217,36 +213,6 @@ AccEvent::CaptureIsFromUserInput(EIsFromUserInput aIsFromUserInput)
}
////////////////////////////////////////////////////////////////////////////////
// AccReorderEvent
////////////////////////////////////////////////////////////////////////////////
AccReorderEvent::
AccReorderEvent(nsAccessible* aAccTarget, PRBool aIsAsynch,
PRBool aIsUnconditional, nsINode* aReasonNode) :
AccEvent(::nsIAccessibleEvent::EVENT_REORDER, aAccTarget,
aIsAsynch, eAutoDetect, AccEvent::eCoalesceFromSameSubtree),
mUnconditionalEvent(aIsUnconditional), mReasonNode(aReasonNode)
{
}
PRBool
AccReorderEvent::IsUnconditionalEvent()
{
return mUnconditionalEvent;
}
PRBool
AccReorderEvent::HasAccessibleInReasonSubtree()
{
if (!mReasonNode)
return PR_FALSE;
nsAccessible *accessible = GetAccService()->GetAccessible(mReasonNode);
return accessible || nsAccUtils::HasAccessibleChildren(mReasonNode);
}
////////////////////////////////////////////////////////////////////////////////
// AccStateChangeEvent
////////////////////////////////////////////////////////////////////////////////
@@ -257,9 +223,8 @@ AccReorderEvent::HasAccessibleInReasonSubtree()
AccStateChangeEvent::
AccStateChangeEvent(nsAccessible* aAccessible,
PRUint32 aState, PRBool aIsExtraState,
PRBool aIsEnabled, PRBool aIsAsynch,
EIsFromUserInput aIsFromUserInput):
AccEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible, aIsAsynch,
PRBool aIsEnabled, EIsFromUserInput aIsFromUserInput):
AccEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible,
aIsFromUserInput, eAllowDupes),
mState(aState), mIsExtraState(aIsExtraState), mIsEnabled(aIsEnabled)
{
@@ -315,11 +280,11 @@ AccStateChangeEvent::CreateXPCOMObject()
AccTextChangeEvent::
AccTextChangeEvent(nsAccessible* aAccessible, PRInt32 aStart,
nsAString& aModifiedText, PRBool aIsInserted,
PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput)
EIsFromUserInput aIsFromUserInput)
: AccEvent(aIsInserted ?
static_cast<PRUint32>(nsIAccessibleEvent::EVENT_TEXT_INSERTED) :
static_cast<PRUint32>(nsIAccessibleEvent::EVENT_TEXT_REMOVED),
aAccessible, aIsAsynch, aIsFromUserInput, eAllowDupes)
aAccessible, aIsFromUserInput, eAllowDupes)
, mStart(aStart)
, mIsInserted(aIsInserted)
, mModifiedText(aModifiedText)
@@ -335,37 +300,62 @@ AccTextChangeEvent::CreateXPCOMObject()
}
////////////////////////////////////////////////////////////////////////////////
// AccMutationEvent
////////////////////////////////////////////////////////////////////////////////
AccMutationEvent::
AccMutationEvent(PRUint32 aEventType, nsAccessible* aTarget,
nsINode* aTargetNode, EIsFromUserInput aIsFromUserInput) :
AccEvent(aEventType, aTarget, aIsFromUserInput, eCoalesceFromSameSubtree)
{
mNode = aTargetNode;
}
////////////////////////////////////////////////////////////////////////////////
// AccHideEvent
////////////////////////////////////////////////////////////////////////////////
AccHideEvent::
AccHideEvent(nsAccessible* aTarget, nsINode* aTargetNode,
PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput) :
AccEvent(nsIAccessibleEvent::EVENT_HIDE, aTarget, aIsAsynch,
aIsFromUserInput, eCoalesceFromSameSubtree)
EIsFromUserInput aIsFromUserInput) :
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget, aTargetNode,
aIsFromUserInput)
{
mNode = aTargetNode;
mParent = mAccessible->GetCachedParent();
mNextSibling = mAccessible->GetCachedNextSibling();
mPrevSibling = mAccessible->GetCachedPrevSibling();
}
////////////////////////////////////////////////////////////////////////////////
// AccShowEvent
////////////////////////////////////////////////////////////////////////////////
AccShowEvent::
AccShowEvent(nsAccessible* aTarget, nsINode* aTargetNode,
EIsFromUserInput aIsFromUserInput) :
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget, aTargetNode,
aIsFromUserInput)
{
}
////////////////////////////////////////////////////////////////////////////////
// AccCaretMoveEvent
////////////////////////////////////////////////////////////////////////////////
AccCaretMoveEvent::
AccCaretMoveEvent(nsAccessible* aAccessible, PRInt32 aCaretOffset) :
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible, PR_TRUE), // Currently always asynch
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible),
mCaretOffset(aCaretOffset)
{
}
AccCaretMoveEvent::
AccCaretMoveEvent(nsINode* aNode) :
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aNode, PR_TRUE), // Currently always asynch
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aNode),
mCaretOffset(-1)
{
}
@@ -385,9 +375,8 @@ AccCaretMoveEvent::CreateXPCOMObject()
AccTableChangeEvent::
AccTableChangeEvent(nsAccessible* aAccessible, PRUint32 aEventType,
PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols,
PRBool aIsAsynch) :
AccEvent(aEventType, aAccessible, aIsAsynch),
PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols) :
AccEvent(aEventType, aAccessible),
mRowOrColIndex(aRowOrColIndex), mNumRowsOrCols(aNumRowsOrCols)
{
}

View File

@@ -41,6 +41,8 @@
#ifndef _AccEvent_H_
#define _AccEvent_H_
#include "nsIAccessibleEvent.h"
#include "nsAccessible.h"
class nsAccEvent;
@@ -90,11 +92,10 @@ public:
// Initialize with an nsIAccessible
AccEvent(PRUint32 aEventType, nsAccessible* aAccessible,
PRBool aIsAsynch = PR_FALSE,
EIsFromUserInput aIsFromUserInput = eAutoDetect,
EEventRule aEventRule = eRemoveDupes);
// Initialize with an nsIDOMNode
AccEvent(PRUint32 aEventType, nsINode* aNode, PRBool aIsAsynch = PR_FALSE,
AccEvent(PRUint32 aEventType, nsINode* aNode,
EIsFromUserInput aIsFromUserInput = eAutoDetect,
EEventRule aEventRule = eRemoveDupes);
virtual ~AccEvent() {}
@@ -102,7 +103,6 @@ public:
// AccEvent
PRUint32 GetEventType() const { return mEventType; }
EEventRule GetEventRule() const { return mEventRule; }
PRBool IsAsync() const { return mIsAsync; }
PRBool IsFromUserInput() const { return mIsFromUserInput; }
nsAccessible *GetAccessible();
@@ -119,10 +119,11 @@ public:
*/
enum EventGroup {
eGenericEvent,
eReorderEvent,
eStateChangeEvent,
eTextChangeEvent,
eMutationEvent,
eHideEvent,
eShowEvent,
eCaretMoveEvent,
eTableChangeEvent
};
@@ -154,7 +155,6 @@ protected:
PRBool mIsFromUserInput;
PRUint32 mEventType;
EEventRule mEventRule;
PRPackedBool mIsAsync;
nsRefPtr<nsAccessible> mAccessible;
nsCOMPtr<nsINode> mNode;
@@ -162,39 +162,6 @@ protected:
};
/**
* Accessible reorder event.
*/
class AccReorderEvent : public AccEvent
{
public:
AccReorderEvent(nsAccessible* aAccTarget, PRBool aIsAsynch,
PRBool aIsUnconditional, nsINode* aReasonNode);
// AccEvent
static const EventGroup kEventGroup = eReorderEvent;
virtual unsigned int GetEventGroups() const
{
return AccEvent::GetEventGroups() | (1U << eReorderEvent);
}
// AccReorderEvent
/**
* Return true if event is unconditional, i.e. must be fired.
*/
PRBool IsUnconditionalEvent();
/**
* Return true if changed DOM node has accessible in its tree.
*/
PRBool HasAccessibleInReasonSubtree();
private:
PRBool mUnconditionalEvent;
nsCOMPtr<nsINode> mReasonNode;
};
/**
* Accessible state change event.
*/
@@ -203,7 +170,7 @@ class AccStateChangeEvent: public AccEvent
public:
AccStateChangeEvent(nsAccessible* aAccessible,
PRUint32 aState, PRBool aIsExtraState,
PRBool aIsEnabled, PRBool aIsAsynch = PR_FALSE,
PRBool aIsEnabled,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
AccStateChangeEvent(nsINode* aNode, PRUint32 aState, PRBool aIsExtraState,
@@ -239,8 +206,7 @@ class AccTextChangeEvent: public AccEvent
{
public:
AccTextChangeEvent(nsAccessible* aAccessible, PRInt32 aStart,
nsAString& aModifiedText,
PRBool aIsInserted, PRBool aIsAsynch = PR_FALSE,
nsAString& aModifiedText, PRBool aIsInserted,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
// AccEvent
@@ -269,31 +235,75 @@ private:
/**
* Accessible hide events.
* Base class for show and hide accessible events.
*/
class AccHideEvent : public AccEvent
class AccMutationEvent: public AccEvent
{
public:
AccMutationEvent(PRUint32 aEventType, nsAccessible* aTarget,
nsINode* aTargetNode, EIsFromUserInput aIsFromUserInput);
// Event
static const EventGroup kEventGroup = eMutationEvent;
virtual unsigned int GetEventGroups() const
{
return AccEvent::GetEventGroups() | (1U << eMutationEvent);
}
// MutationEvent
bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; }
bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
protected:
nsRefPtr<AccTextChangeEvent> mTextChangeEvent;
friend class nsAccEventQueue;
};
/**
* Accessible hide event.
*/
class AccHideEvent: public AccMutationEvent
{
public:
AccHideEvent(nsAccessible* aTarget, nsINode* aTargetNode,
PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput);
EIsFromUserInput aIsFromUserInput);
// Event
static const EventGroup kEventGroup = eHideEvent;
virtual unsigned int GetEventGroups() const
{
return AccEvent::GetEventGroups() | (1U << eHideEvent);
return AccMutationEvent::GetEventGroups() | (1U << eHideEvent);
}
protected:
nsRefPtr<nsAccessible> mParent;
nsRefPtr<nsAccessible> mNextSibling;
nsRefPtr<nsAccessible> mPrevSibling;
nsRefPtr<AccTextChangeEvent> mTextChangeEvent;
friend class nsAccEventQueue;
};
/**
* Accessible show event.
*/
class AccShowEvent: public AccMutationEvent
{
public:
AccShowEvent(nsAccessible* aTarget, nsINode* aTargetNode,
EIsFromUserInput aIsFromUserInput);
// Event
static const EventGroup kEventGroup = eShowEvent;
virtual unsigned int GetEventGroups() const
{
return AccMutationEvent::GetEventGroups() | (1U << eShowEvent);
}
};
/**
* Accessible caret move event.
*/
@@ -327,8 +337,7 @@ class AccTableChangeEvent : public AccEvent
{
public:
AccTableChangeEvent(nsAccessible* aAccessible, PRUint32 aEventType,
PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols,
PRBool aIsAsynch);
PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols);
// AccEvent
virtual already_AddRefed<nsAccEvent> CreateXPCOMObject();

View File

@@ -72,8 +72,7 @@ nsAccDocManager::GetDocAccessible(nsIDocument *aDocument)
// Ensure CacheChildren is called before we query cache.
nsAccessNode::GetApplicationAccessible()->EnsureChildren();
nsDocAccessible *docAcc =
mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument);
if (docAcc)
return docAcc;
@@ -81,10 +80,10 @@ nsAccDocManager::GetDocAccessible(nsIDocument *aDocument)
}
nsAccessible*
nsAccDocManager::FindAccessibleInCache(void *aUniqueID) const
nsAccDocManager::FindAccessibleInCache(nsINode* aNode) const
{
nsSearchAccessibleInCacheArg arg;
arg.mUniqueID = aUniqueID;
arg.mNode = aNode;
mDocAccessibleCache.EnumerateRead(SearchAccessibleInDocCache,
static_cast<void*>(&arg));
@@ -136,8 +135,7 @@ nsAccDocManager::Shutdown()
void
nsAccDocManager::ShutdownDocAccessible(nsIDocument *aDocument)
{
nsDocAccessible* docAccessible =
mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
nsDocAccessible* docAccessible = mDocAccessibleCache.GetWeak(aDocument);
if (!docAccessible)
return;
@@ -146,7 +144,7 @@ nsAccDocManager::ShutdownDocAccessible(nsIDocument *aDocument)
// are removed automatically when chrome event target goes away.
docAccessible->Shutdown();
mDocAccessibleCache.Remove(static_cast<void*>(aDocument));
mDocAccessibleCache.Remove(aDocument);
}
////////////////////////////////////////////////////////////////////////////////
@@ -215,8 +213,7 @@ nsAccDocManager::OnStateChange(nsIWebProgress *aWebProgress,
if (!IsEventTargetDocument(document))
return NS_OK;
nsDocAccessible *docAcc =
mDocAccessibleCache.GetWeak(static_cast<void*>(document));
nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(document);
if (!docAcc)
return NS_OK;
@@ -346,9 +343,7 @@ nsAccDocManager::HandleDOMDocumentLoad(nsIDocument *aDocument,
{
// Document accessible can be created before we were notified the DOM document
// was loaded completely. However if it's not created yet then create it.
nsDocAccessible *docAcc =
mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument);
if (!docAcc) {
docAcc = CreateDocOrRootAccessible(aDocument);
NS_ASSERTION(docAcc, "Can't create document accessible!");
@@ -365,16 +360,8 @@ nsAccDocManager::HandleDOMDocumentLoad(nsIDocument *aDocument,
// documents
// b) document load event on sub documents causes screen readers to act is if
// entire page is reloaded.
if (!IsEventTargetDocument(aDocument)) {
// XXX: AT doesn't update their virtual buffer once frame is loaded and it
// has dynamic content added after frame load. There's something wrong how
// we handle this changes.
if (!nsCoreUtils::IsRootDocument(aDocument)) {
docAcc->InvalidateCacheSubtree(nsnull,
nsIAccessibilityService::NODE_SIGNIFICANT_CHANGE);
}
if (!IsEventTargetDocument(aDocument))
return;
}
// Fire complete/load stopped if the load event type is given.
if (aLoadEventType) {
@@ -437,9 +424,10 @@ nsAccDocManager::AddListeners(nsIDocument *aDocument,
nsDocAccessible*
nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument)
{
// Ignore temporary, hiding and svg resource documents.
// Ignore temporary, hiding, resource documents and documents without
// docshell.
if (aDocument->IsInitialDocument() || !aDocument->IsVisible() ||
aDocument->GetDisplayDocument())
aDocument->IsResourceDoc() || !aDocument->IsActive())
return nsnull;
// Ignore documents without presshell.
@@ -492,7 +480,7 @@ nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument)
return nsnull;
// Cache and addref document accessible.
if (!mDocAccessibleCache.Put(static_cast<void*>(aDocument), docAcc)) {
if (!mDocAccessibleCache.Put(aDocument, docAcc)) {
delete docAcc;
return nsnull;
}
@@ -502,7 +490,7 @@ nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument)
// while initialized.
if (!outerDocAcc->AppendChild(docAcc) ||
!GetAccService()->InitAccessible(docAcc, nsAccUtils::GetRoleMapEntry(aDocument))) {
mDocAccessibleCache.Remove(static_cast<void*>(aDocument));
mDocAccessibleCache.Remove(aDocument);
return nsnull;
}
@@ -545,7 +533,7 @@ nsAccDocManager::ShutdownDocAccessiblesInTree(nsIDocShellTreeItem *aTreeItem,
// nsAccDocManager static
PLDHashOperator
nsAccDocManager::ClearDocCacheEntry(const void* aKey,
nsAccDocManager::ClearDocCacheEntry(const nsIDocument* aKey,
nsRefPtr<nsDocAccessible>& aDocAccessible,
void* aUserArg)
{
@@ -559,7 +547,7 @@ nsAccDocManager::ClearDocCacheEntry(const void* aKey,
}
PLDHashOperator
nsAccDocManager::SearchAccessibleInDocCache(const void* aKey,
nsAccDocManager::SearchAccessibleInDocCache(const nsIDocument* aKey,
nsDocAccessible* aDocAccessible,
void* aUserArg)
{
@@ -569,7 +557,7 @@ nsAccDocManager::SearchAccessibleInDocCache(const void* aKey,
if (aDocAccessible) {
nsSearchAccessibleInCacheArg* arg =
static_cast<nsSearchAccessibleInCacheArg*>(aUserArg);
arg->mAccessible = aDocAccessible->GetCachedAccessible(arg->mUniqueID);
arg->mAccessible = aDocAccessible->GetCachedAccessible(arg->mNode);
if (arg->mAccessible)
return PL_DHASH_STOP;
}

View File

@@ -73,7 +73,7 @@ public:
* Search through all document accessibles for an accessible with the given
* unique id.
*/
nsAccessible *FindAccessibleInCache(void *aUniqueID) const;
nsAccessible* FindAccessibleInCache(nsINode* aNode) const;
/**
* Shutdown document accessibles in the tree starting from the given one.
@@ -87,7 +87,7 @@ public:
*/
inline nsDocAccessible* GetDocAccessibleFromCache(nsIDocument* aDocument) const
{
return mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
return mDocAccessibleCache.GetWeak(aDocument);
}
protected:
@@ -162,14 +162,14 @@ private:
void ShutdownDocAccessiblesInTree(nsIDocShellTreeItem *aTreeItem,
nsIDocument *aDocument);
typedef nsRefPtrHashtable<nsVoidPtrHashKey, nsDocAccessible>
typedef nsRefPtrHashtable<nsPtrHashKey<const nsIDocument>, nsDocAccessible>
nsDocAccessibleHashtable;
/**
* Shutdown and remove the document accessible from cache.
*/
static PLDHashOperator
ClearDocCacheEntry(const void* aKey,
ClearDocCacheEntry(const nsIDocument* aKey,
nsRefPtr<nsDocAccessible>& aDocAccessible,
void* aUserArg);
@@ -184,11 +184,11 @@ private:
struct nsSearchAccessibleInCacheArg
{
nsAccessible *mAccessible;
void *mUniqueID;
nsINode* mNode;
};
static PLDHashOperator
SearchAccessibleInDocCache(const void* aKey,
SearchAccessibleInDocCache(const nsIDocument* aKey,
nsDocAccessible* aDocAccessible,
void* aUserArg);

View File

@@ -74,9 +74,11 @@ nsAccTreeWalker::
if (aContent)
mState = new WalkState(aContent);
mChildType = aWalkAnonContent ? nsIContent::eAllChildren :
mChildFilter = aWalkAnonContent ? nsIContent::eAllChildren :
nsIContent::eAllButXBL;
mChildFilter |= nsIContent::eSkipPlaceholderContent;
MOZ_COUNT_CTOR(nsAccTreeWalker);
}
@@ -99,7 +101,7 @@ nsAccTreeWalker::GetNextChildInternal(PRBool aNoWalkUp)
return nsnull;
if (!mState->childList)
mState->childList = mState->content->GetChildren(mChildType);
mState->childList = mState->content->GetChildren(mChildFilter);
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));

View File

@@ -91,7 +91,7 @@ private:
void PopState();
nsCOMPtr<nsIWeakReference> mWeakShell;
PRInt32 mChildType;
PRInt32 mChildFilter;
WalkState* mState;
};

View File

@@ -152,9 +152,12 @@ nsAccessNode::Shutdown()
}
// nsIAccessNode
NS_IMETHODIMP nsAccessNode::GetUniqueID(void **aUniqueID)
NS_IMETHODIMP
nsAccessNode::GetUniqueID(void **aUniqueID)
{
*aUniqueID = static_cast<void*>(GetNode());
NS_ENSURE_ARG_POINTER(aUniqueID);
*aUniqueID = UniqueID();
return NS_OK;
}
@@ -310,6 +313,12 @@ nsAccessNode::GetFrame()
return mContent ? mContent->GetPrimaryFrame() : nsnull;
}
bool
nsAccessNode::IsPrimaryForNode() const
{
return true;
}
////////////////////////////////////////////////////////////////////////////////
// nsIAccessNode

View File

@@ -181,6 +181,20 @@ public:
*/
nsIWeakReference* GetWeakShell() const { return mWeakShell; }
/**
* Return the unique identifier of the accessible.
*/
void* UniqueID() { return static_cast<void*>(this); }
/**
* Return true if the accessible is primary accessible for the given DOM node.
*
* Accessible hierarchy may be complex for single DOM node, in this case
* these accessibles share the same DOM node. The primary accessible "owns"
* that DOM node in terms it gets stored in the accessible to node map.
*/
virtual bool IsPrimaryForNode() const;
protected:
nsPresContext* GetPresContext();

View File

@@ -74,6 +74,7 @@
#include "nsRootAccessibleWrap.h"
#include "nsTextFragment.h"
#include "mozilla/Services.h"
#include "nsIEventStateManager.h"
#ifdef MOZ_XUL
#include "nsXULAlertAccessible.h"
@@ -232,12 +233,10 @@ nsAccessibilityService::CreateHTMLButtonAccessible(nsIContent* aContent,
already_AddRefed<nsAccessible>
nsAccessibilityService::CreateHTMLLIAccessible(nsIContent* aContent,
nsIPresShell* aPresShell,
const nsAString& aBulletText)
nsIPresShell* aPresShell)
{
nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(aPresShell));
nsAccessible* accessible = new nsHTMLLIAccessible(aContent, weakShell,
aBulletText);
nsAccessible* accessible = new nsHTMLLIAccessible(aContent, weakShell);
NS_IF_ADDREF(accessible);
return accessible;
}
@@ -472,6 +471,62 @@ nsAccessibilityService::CreateHTMLCaptionAccessible(nsIContent* aContent,
return accessible;
}
void
nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell,
nsIContent* aContainer,
nsIContent* aStartChild,
nsIContent* aEndChild)
{
#ifdef DEBUG_A11Y
nsAutoString tag;
aStartChild->Tag()->ToString(tag);
nsIAtom* id = aStartChild->GetID();
nsCAutoString strid;
if (id)
id->ToUTF8String(strid);
nsAutoString ctag;
aContainer->Tag()->ToString(ctag);
nsIAtom* cid = aContainer->GetID();
nsCAutoString strcid;
if (cid)
cid->ToUTF8String(strcid);
printf("\ncontent inserted: %s@id='%s', container: %s@id='%s', end node: %p\n\n",
NS_ConvertUTF16toUTF8(tag).get(), strid.get(),
NS_ConvertUTF16toUTF8(ctag).get(), strcid.get(), aEndChild);
#endif
// XXX: bug 606082. aContainer is null when root element is inserted into
// document, we need to handle this and update the tree, also we need to
// update a content node of the document accessible.
if (aContainer) {
nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument());
if (docAccessible)
docAccessible->UpdateTree(aContainer, aStartChild, aEndChild, PR_TRUE);
}
}
void
nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
nsIContent* aContainer,
nsIContent* aChild)
{
#ifdef DEBUG_A11Y
nsAutoString id;
aChild->Tag()->ToString(id);
printf("\ncontent removed: %s\n", NS_ConvertUTF16toUTF8(id).get());
#endif
// XXX: bug 606082. aContainer is null when root element is inserted into
// document, we need to handle this and update the tree, perhaps destroy
// the document accessible.
if (aContainer) {
nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument());
if (docAccessible)
docAccessible->UpdateTree(aContainer, aChild, aChild->GetNextSibling(),
PR_FALSE);
}
}
void
nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell)
{
@@ -491,14 +546,22 @@ nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell)
ShutdownDocAccessible(doc);
}
void
nsAccessibilityService::RecreateAccessible(nsIPresShell* aPresShell,
nsIContent* aContent)
{
nsDocAccessible* document = GetDocAccessible(aPresShell->GetDocument());
if (document)
document->RecreateAccessible(aContent);
}
// nsAccessibilityService protected
nsAccessible *
nsAccessibilityService::GetCachedAccessible(nsINode *aNode,
nsIWeakReference *aWeakShell)
{
nsDocAccessible *docAccessible = GetDocAccessible(aNode->GetOwnerDoc());
return docAccessible ?
docAccessible->GetCachedAccessible(static_cast<void*>(aNode)) : nsnull;
return docAccessible ? docAccessible->GetCachedAccessible(aNode) : nsnull;
}
////////////////////////////////////////////////////////////////////////////////
@@ -696,7 +759,7 @@ nsAccessibilityService::GetAccessibleFromCache(nsIDOMNode* aNode,
// "unofficially" shutdown document (i.e. not from nsAccDocManager) can still
// exist in the document cache.
nsCOMPtr<nsINode> node(do_QueryInterface(aNode));
nsAccessible* accessible = FindAccessibleInCache(static_cast<void*>(node));
nsAccessible* accessible = FindAccessibleInCache(node);
if (!accessible) {
nsCOMPtr<nsIDocument> document(do_QueryInterface(node));
if (document)
@@ -734,7 +797,7 @@ nsAccessibilityService::GetAccessible(nsINode* aNode)
}
nsAccessible*
nsAccessibilityService::GetCachedContainerAccessible(nsINode* aNode)
nsAccessibilityService::GetCachedAccessibleOrContainer(nsINode* aNode)
{
if (!aNode)
return nsnull;
@@ -751,8 +814,8 @@ nsAccessibilityService::GetCachedContainerAccessible(nsINode* aNode)
nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(presShell));
nsAccessible *accessible = nsnull;
while ((currNode = currNode->GetNodeParent()) &&
!(accessible = GetCachedAccessible(currNode, weakShell)));
while (!(accessible = GetCachedAccessible(currNode, weakShell)) &&
(currNode = currNode->GetNodeParent()));
return accessible;
}
@@ -859,8 +922,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
nsWeakFrame weakFrame = content->GetPrimaryFrame();
// Check frame to see if it is hidden.
if (!weakFrame.GetFrame() ||
!weakFrame.GetFrame()->GetStyleVisibility()->IsVisible()) {
if (!weakFrame.GetFrame()) {
if (aIsHidden)
*aIsHidden = PR_TRUE;
@@ -1197,23 +1259,24 @@ nsAccessibilityService::GetAccessibleByRule(nsINode* aNode,
if (!aNode || !aWeakShell)
return nsnull;
nsAccessible* cachedAcc = GetCachedAccessible(aNode, aWeakShell);
if (cachedAcc) {
if (aWhatToGet & eGetAccForNode)
if (aWhatToGet & eGetAccForNode) {
nsAccessible* cachedAcc = GetCachedAccessible(aNode, aWeakShell);
if (cachedAcc && cachedAcc->IsBoundToParent())
return cachedAcc;
// XXX: while nsAccessible::GetParent() tries to repair broken tree and
// may not return cached parent then we use GetAccessibleOrContainer().
return GetAccessibleByRule(aNode->GetNodeParent(), aWeakShell,
eGetAccForNodeOrContainer);
}
// Go up looking for the nearest accessible container stored in cache.
// Go up looking for the nearest accessible container having cached children.
nsTArray<nsINode*> nodes;
nsINode* node = aNode;
while ((node = node->GetNodeParent()) &&
!(cachedAcc = GetCachedAccessible(node, aWeakShell)))
nsAccessible* cachedAcc = nsnull;
while ((node = node->GetNodeParent())) {
cachedAcc = GetCachedAccessible(node, aWeakShell);
if (cachedAcc && cachedAcc->IsBoundToParent())
break;
nodes.AppendElement(node);
}
// Node is not in accessible document.
if (!cachedAcc)
@@ -1634,8 +1697,7 @@ nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
// Normally for li, it is created by the list item frame (in nsBlockFrame)
// which knows about the bullet frame; however, in this case the list item
// must have been styled using display: foo
nsAccessible* accessible = new nsHTMLLIAccessible(aContent, aWeakShell,
EmptyString());
nsAccessible* accessible = new nsHTMLLIAccessible(aContent, aWeakShell);
NS_IF_ADDREF(accessible);
return accessible;
}
@@ -1716,29 +1778,6 @@ nsAccessibilityService::RemoveNativeRootAccessible(nsAccessible* aAccessible)
#endif
}
// Called from layout when the frame tree owned by a node changes significantly
nsresult
nsAccessibilityService::InvalidateSubtreeFor(nsIPresShell *aShell,
nsIContent *aChangeContent,
PRUint32 aChangeType)
{
NS_ASSERTION(aChangeType == nsIAccessibilityService::FRAME_SIGNIFICANT_CHANGE ||
aChangeType == nsIAccessibilityService::FRAME_SHOW ||
aChangeType == nsIAccessibilityService::FRAME_HIDE ||
aChangeType == nsIAccessibilityService::NODE_SIGNIFICANT_CHANGE ||
aChangeType == nsIAccessibilityService::NODE_APPEND ||
aChangeType == nsIAccessibilityService::NODE_REMOVE,
"Incorrect aEvent passed in");
NS_ENSURE_ARG_POINTER(aShell);
nsDocAccessible *docAccessible = GetDocAccessible(aShell->GetDocument());
if (docAccessible)
docAccessible->InvalidateCacheSubtree(aChangeContent, aChangeType);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// NS_GetAccessibilityService
////////////////////////////////////////////////////////////////////////////////

View File

@@ -84,8 +84,7 @@ public:
virtual already_AddRefed<nsAccessible>
CreateHTMLLabelAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
virtual already_AddRefed<nsAccessible>
CreateHTMLLIAccessible(nsIContent* aContent, nsIPresShell* aPresShell,
const nsAString& aBulletText);
CreateHTMLLIAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
virtual already_AddRefed<nsAccessible>
CreateHTMLListboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
virtual already_AddRefed<nsAccessible>
@@ -111,14 +110,21 @@ public:
virtual nsAccessible* AddNativeRootAccessible(void* aAtkAccessible);
virtual void RemoveNativeRootAccessible(nsAccessible* aRootAccessible);
virtual nsresult InvalidateSubtreeFor(nsIPresShell *aPresShell,
nsIContent *aContent,
PRUint32 aChangeType);
virtual void ContentRangeInserted(nsIPresShell* aPresShell,
nsIContent* aContainer,
nsIContent* aStartChild,
nsIContent* aEndChild);
virtual void ContentRemoved(nsIPresShell* aPresShell, nsIContent* aContainer,
nsIContent* aChild);
virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget);
virtual void PresShellDestroyed(nsIPresShell* aPresShell);
virtual void RecreateAccessible(nsIPresShell* aPresShell,
nsIContent* aContent);
virtual void FireAccessibleEvent(PRUint32 aEvent, nsAccessible* aTarget);
// nsAccessibiltiyService
@@ -179,12 +185,22 @@ public:
return GetAccessibleByRule(aNode, aWeakShell, eGetAccForContainer);
}
/**
* Return cached accessible for the given DOM node or cached container
* accessible if there's no cached accessible for the given node.
*/
nsAccessible* GetCachedAccessibleOrContainer(nsINode* aNode);
/**
* Return the first cached accessible parent of a DOM node.
*
* @param aDOMNode [in] the DOM node to get an accessible for
*/
nsAccessible* GetCachedContainerAccessible(nsINode *aNode);
inline nsAccessible* GetCachedContainerAccessible(nsINode *aNode)
{
return aNode ?
GetCachedAccessibleOrContainer(aNode->GetNodeParent()) : nsnull;
}
/**
* Initialize an accessible and cache it. The method should be called for

View File

@@ -686,16 +686,16 @@ nsAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
if (aExtraState)
*aExtraState = 0;
PRInt32 intrinsicState = mContent->IntrinsicState();
nsEventStates intrinsicState = mContent->IntrinsicState();
if (intrinsicState & NS_EVENT_STATE_INVALID)
if (intrinsicState.HasState(NS_EVENT_STATE_INVALID))
*aState |= nsIAccessibleStates::STATE_INVALID;
if (intrinsicState & NS_EVENT_STATE_REQUIRED)
if (intrinsicState.HasState(NS_EVENT_STATE_REQUIRED))
*aState |= nsIAccessibleStates::STATE_REQUIRED;
PRBool disabled = mContent->IsHTML() ?
(intrinsicState & NS_EVENT_STATE_DISABLED) :
(intrinsicState.HasState(NS_EVENT_STATE_DISABLED)) :
(mContent->AttrValueIs(kNameSpaceID_None,
nsAccessibilityAtoms::disabled,
nsAccessibilityAtoms::_true,
@@ -985,39 +985,49 @@ void nsAccessible::GetBoundsRect(nsRect& aTotalBounds, nsIFrame** aBoundingFrame
/* void getBounds (out long x, out long y, out long width, out long height); */
NS_IMETHODIMP nsAccessible::GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height)
NS_IMETHODIMP
nsAccessible::GetBounds(PRInt32* aX, PRInt32* aY,
PRInt32* aWidth, PRInt32* aHeight)
{
// This routine will get the entire rectange for all the frames in this node
NS_ENSURE_ARG_POINTER(aX);
*aX = 0;
NS_ENSURE_ARG_POINTER(aY);
*aY = 0;
NS_ENSURE_ARG_POINTER(aWidth);
*aWidth = 0;
NS_ENSURE_ARG_POINTER(aHeight);
*aHeight = 0;
if (IsDefunct())
return NS_ERROR_FAILURE;
// Flush layout so that all the frame construction, reflow, and styles are
// up-to-date since we rely on frames, and styles when calculating state.
// We don't flush the display because we don't care about painting.
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
presShell->FlushPendingNotifications(Flush_Layout);
// This routine will get the entire rectangle for all the frames in this node.
// -------------------------------------------------------------------------
// Primary Frame for node
// Another frame, same node <- Example
// Another frame, same node
nsPresContext *presContext = GetPresContext();
if (!presContext)
{
*x = *y = *width = *height = 0;
return NS_ERROR_FAILURE;
}
nsRect unionRectTwips;
nsIFrame* aBoundingFrame = nsnull;
GetBoundsRect(unionRectTwips, &aBoundingFrame); // Unions up all primary frames for this node and all siblings after it
if (!aBoundingFrame) {
*x = *y = *width = *height = 0;
return NS_ERROR_FAILURE;
}
nsIFrame* boundingFrame = nsnull;
GetBoundsRect(unionRectTwips, &boundingFrame); // Unions up all primary frames for this node and all siblings after it
NS_ENSURE_STATE(boundingFrame);
*x = presContext->AppUnitsToDevPixels(unionRectTwips.x);
*y = presContext->AppUnitsToDevPixels(unionRectTwips.y);
*width = presContext->AppUnitsToDevPixels(unionRectTwips.width);
*height = presContext->AppUnitsToDevPixels(unionRectTwips.height);
nsPresContext* presContext = presShell->GetPresContext();
*aX = presContext->AppUnitsToDevPixels(unionRectTwips.x);
*aY = presContext->AppUnitsToDevPixels(unionRectTwips.y);
*aWidth = presContext->AppUnitsToDevPixels(unionRectTwips.width);
*aHeight = presContext->AppUnitsToDevPixels(unionRectTwips.height);
// We have the union of the rectangle, now we need to put it in absolute screen coords
nsIntRect orgRectPixels = aBoundingFrame->GetScreenRectExternal();
*x += orgRectPixels.x;
*y += orgRectPixels.y;
nsIntRect orgRectPixels = boundingFrame->GetScreenRectExternal();
*aX += orgRectPixels.x;
*aY += orgRectPixels.y;
return NS_OK;
}
@@ -2619,16 +2629,11 @@ nsAccessible::Init()
if (!nsAccessNodeWrap::Init())
return PR_FALSE;
nsDocAccessible *docAcc =
nsDocAccessible* document =
GetAccService()->GetDocAccessible(mContent->GetOwnerDoc());
NS_ASSERTION(docAcc, "Cannot cache new nsAccessible!");
if (!docAcc)
return PR_FALSE;
NS_ASSERTION(document, "Cannot cache new nsAccessible!");
void *uniqueID = nsnull;
GetUniqueID(&uniqueID);
return docAcc->CacheAccessible(uniqueID, this);
return document ? document->CacheAccessible(this) : PR_FALSE;
}
void
@@ -2637,10 +2642,8 @@ nsAccessible::Shutdown()
// Invalidate the child count and pointers to other accessibles, also make
// sure none of its children point to this parent
InvalidateChildren();
if (mParent) {
mParent->InvalidateChildren();
UnbindFromParent();
}
if (mParent)
mParent->RemoveChild(this);
nsAccessNodeWrap::Shutdown();
}
@@ -2689,14 +2692,14 @@ nsAccessible::BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent)
{
NS_PRECONDITION(aParent, "This method isn't used to set null parent!");
if (mParent && mParent != aParent) {
// Adopt a child -- we allow this now. the new parent
// may be a dom node which wasn't previously accessible but now is.
// The old parent's children now need to be invalidated, since
// it no longer owns the child, the new parent does
NS_ASSERTION(PR_FALSE, "Adopting child!");
if (mParent)
if (mParent) {
if (mParent != aParent) {
NS_ERROR("Adopting child!");
mParent->InvalidateChildren();
} else {
NS_ERROR("Binding to the same parent!");
return;
}
}
mParent = aParent;
@@ -2897,13 +2900,10 @@ nsAccessible::IsInCache()
{
nsDocAccessible *docAccessible =
GetAccService()->GetDocAccessible(mContent->GetOwnerDoc());
if (!docAccessible)
return nsnull;
if (docAccessible)
return docAccessible->GetCachedAccessibleByUniqueID(UniqueID()) ? PR_TRUE : PR_FALSE;
void *uniqueID = nsnull;
GetUniqueID(&uniqueID);
return docAccessible->GetCachedAccessible(uniqueID) ? PR_TRUE : PR_FALSE;
return PR_FALSE;
}
#endif

View File

@@ -51,6 +51,7 @@
#include "nsStringGlue.h"
#include "nsTArray.h"
#include "nsRefPtrHashtable.h"
#include "nsDataHashtable.h"
class AccGroupInfo;
class EmbeddedObjCollector;
@@ -66,6 +67,8 @@ class nsIView;
typedef nsRefPtrHashtable<nsVoidPtrHashKey, nsAccessible>
nsAccessibleHashtable;
typedef nsDataHashtable<nsPtrHashKey<const nsINode>, nsAccessible*>
NodeToAccessibleMap;
// see nsAccessible::GetAttrValue
#define NS_OK_NO_ARIA_VALUE \
@@ -206,6 +209,7 @@ public:
* nsnull if none.
*/
virtual void SetRoleMapEntry(nsRoleMapEntry *aRoleMapEntry);
const nsRoleMapEntry* GetRoleMapEntry() const { return mRoleMapEntry; }
/**
* Cache children if necessary. Return true if the accessible is defunct.
@@ -290,7 +294,9 @@ public:
mParent->mChildren.SafeElementAt(mIndexInParent - 1, nsnull).get() : nsnull;
}
PRUint32 GetCachedChildCount() const { return mChildren.Length(); }
nsAccessible* GetCachedChildAt(PRUint32 aIndex) const { return mChildren.ElementAt(aIndex); }
PRBool AreChildrenCached() const { return mChildrenFlags != eChildrenUninitialized; }
bool IsBoundToParent() const { return mParent; }
#ifdef DEBUG
/**

View File

@@ -363,6 +363,12 @@ nsApplicationAccessible::Shutdown()
mAppInfo = nsnull;
}
bool
nsApplicationAccessible::IsPrimaryForNode() const
{
return false;
}
////////////////////////////////////////////////////////////////////////////////
// nsAccessible public methods
@@ -470,7 +476,7 @@ nsApplicationAccessible::GetSiblingAtOffset(PRInt32 aOffset, nsresult* aError)
}
////////////////////////////////////////////////////////////////////////////////
// nsIAccessNode
// nsIAccessNode and nsAccessNode
NS_IMETHODIMP
nsApplicationAccessible::GetDOMNode(nsIDOMNode **aDOMNode)
@@ -524,14 +530,6 @@ nsApplicationAccessible::GetOwnerWindow(void **aOwnerWindow)
return NS_OK;
}
NS_IMETHODIMP
nsApplicationAccessible::GetUniqueID(void **aUniqueID)
{
NS_ENSURE_ARG_POINTER(aUniqueID);
*aUniqueID = static_cast<void *>(this);
return NS_OK;
}
NS_IMETHODIMP
nsApplicationAccessible::GetComputedStyleValue(const nsAString &aPseudoElt,
const nsAString &aPropertyName,
@@ -556,3 +554,4 @@ nsApplicationAccessible::GetLanguage(nsAString &aLanguage)
aLanguage.Truncate();
return NS_OK;
}

View File

@@ -71,7 +71,20 @@ public:
NS_DECL_ISUPPORTS_INHERITED
// nsIAccessNode
NS_DECL_NSIACCESSNODE
NS_SCRIPTABLE NS_IMETHOD GetDOMNode(nsIDOMNode** aDOMNode);
NS_SCRIPTABLE NS_IMETHOD GetDocument(nsIAccessibleDocument** aDocument);
NS_SCRIPTABLE NS_IMETHOD GetRootDocument(nsIAccessibleDocument** aRootDocument);
NS_SCRIPTABLE NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML);
NS_SCRIPTABLE NS_IMETHOD ScrollTo(PRUint32 aScrollType);
NS_SCRIPTABLE NS_IMETHOD ScrollToPoint(PRUint32 aCoordinateType, PRInt32 aX, PRInt32 aY);
NS_IMETHOD GetOwnerWindow(void **aOwnerWindow);
NS_SCRIPTABLE NS_IMETHOD GetComputedStyleValue(const nsAString& aPseudoElt,
const nsAString& aPropertyName,
nsAString& aValue NS_OUTPARAM);
NS_SCRIPTABLE NS_IMETHOD GetComputedStyleCSSValue(const nsAString& aPseudoElt,
const nsAString& aPropertyName,
nsIDOMCSSPrimitiveValue** aValue NS_OUTPARAM);
NS_SCRIPTABLE NS_IMETHOD GetLanguage(nsAString& aLanguage);
// nsIAccessible
NS_IMETHOD GetParent(nsIAccessible **aParent);
@@ -109,6 +122,7 @@ public:
virtual PRBool IsDefunct();
virtual PRBool Init();
virtual void Shutdown();
virtual bool IsPrimaryForNode() const;
// nsAccessible
virtual nsresult GetARIAState(PRUint32 *aState, PRUint32 *aExtraState);

View File

@@ -285,8 +285,7 @@ nsCaretAccessible::SpellcheckSelectionChanged(nsIDOMDocument *aDoc,
NS_ENSURE_STATE(textAcc);
nsRefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED,
textAcc, nsnull);
new AccEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED, textAcc);
nsEventShell::FireEvent(event);
return NS_OK;

File diff suppressed because it is too large Load Diff

View File

@@ -170,12 +170,9 @@ public:
* @param aEventType [in] the nsIAccessibleEvent event type
* @param aDOMNode [in] DOM node the accesible event should be fired for
* @param aAllowDupes [in] rule to process an event (see EEventRule constants)
* @param aIsAsynch [in] set to PR_TRUE if this is not being called from
* code synchronous with a DOM event
*/
nsresult FireDelayedAccessibleEvent(PRUint32 aEventType, nsINode *aNode,
AccEvent::EEventRule aAllowDupes = AccEvent::eRemoveDupes,
PRBool aIsAsynch = PR_FALSE,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
/**
@@ -186,51 +183,45 @@ public:
nsresult FireDelayedAccessibleEvent(AccEvent* aEvent);
/**
* Find the accessible object in the accessibility cache that corresponds to
* the given node or the first ancestor of it that has an accessible object
* associated with it. Clear that accessible object's parent's cache of
* accessible children and remove the accessible object and any descendants
* from the accessible cache. Fires proper events. New accessible objects will
* be created and cached again on demand.
*
* @param aContent [in] the child that is changing
* @param aEvent [in] the event from nsIAccessibleEvent that caused
* the change.
*/
void InvalidateCacheSubtree(nsIContent *aContent, PRUint32 aEvent);
/**
* Return the cached accessible by the given unique ID if it's in subtree of
* Return the cached accessible by the given DOM node if it's in subtree of
* this document accessible or the document accessible itself, otherwise null.
*
* @note the unique ID matches with the uniqueID attribute on nsIAccessNode
*
* @param aUniqueID [in] the unique ID used to cache the node.
*
* @return the accessible object
*/
nsAccessible* GetCachedAccessible(void *aUniqueID);
nsAccessible* GetCachedAccessible(nsINode* aNode);
/**
* Return the cached accessible by the given unique ID within this document.
*
* @note the unique ID matches with the uniqueID() of nsAccessNode
*
* @param aUniqueID [in] the unique ID used to cache the node.
*/
nsAccessible* GetCachedAccessibleByUniqueID(void* aUniqueID)
{
return UniqueID() == aUniqueID ?
this : mAccessibleCache.GetWeak(aUniqueID);
}
/**
* Return the cached accessible by the given unique ID looking through
* this and nested documents.
*/
nsAccessible* GetCachedAccessibleInSubtree(void* aUniqueID);
nsAccessible* GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID);
/**
* Cache the accessible.
*
* @param aUniquID [in] the unique identifier of accessible
* @param aAccessible [in] accessible to cache
*
* @return true if accessible being cached, otherwise false
*/
PRBool CacheAccessible(void *aUniqueID, nsAccessible *aAccessible);
PRBool CacheAccessible(nsAccessible *aAccessible);
/**
* Remove the given accessible from document cache.
* Shutdown the accessible and remove it from document cache.
*/
void RemoveAccessNodeFromCache(nsAccessible *aAccessible);
void ShutdownAccessible(nsAccessible *aAccessible);
/**
* Process the event when the queue of pending events is untwisted. Fire
@@ -238,6 +229,17 @@ public:
*/
void ProcessPendingEvent(AccEvent* aEvent);
/**
* Update the accessible tree.
*/
void UpdateTree(nsIContent* aContainerNode, nsIContent* aStartChildNode,
nsIContent* aEndChildNode, PRBool aIsInsert);
/**
* Recreate an accessible, results in hide/show events pair.
*/
void RecreateAccessible(nsINode* aNode);
protected:
virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
@@ -264,20 +266,6 @@ protected:
mChildDocuments.RemoveElement(aChildDocument);
}
/**
* Invalidate parent-child relations for any cached accessible in the DOM
* subtree. Accessible objects aren't destroyed.
*
* @param aStartNode [in] the root of the subrtee to invalidate accessible
* child/parent refs in
*/
void InvalidateChildrenInSubtree(nsINode *aStartNode);
/**
* Traverse through DOM tree and shutdown accessible objects.
*/
void RefreshNodes(nsINode *aStartNode);
static void ScrollTimerCallback(nsITimer *aTimer, void *aClosure);
/**
@@ -310,27 +298,6 @@ protected:
CharacterDataChangeInfo* aInfo,
PRBool aIsInserted);
/**
* Create a text change event for a changed node.
*
* @param aContainerAccessible [in] the parent accessible for the node
* @param aChangeNode [in] the node that is being inserted or
* removed, or shown/hidden
* @param aAccessible [in] the accessible for that node, or nsnull
* if none exists
* @param aIsInserting [in] is aChangeNode being created or shown
* (vs. removed or hidden)
* @param aIsAsync [in] whether casual change is async
* @param aIsFromUserInput [in] the event is known to be from user input
*/
already_AddRefed<AccEvent>
CreateTextChangeEventForNode(nsAccessible *aContainerAccessible,
nsIContent *aChangeNode,
nsAccessible *aAccessible,
PRBool aIsInserting,
PRBool aIsAsynch,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
/**
* Used to define should the event be fired on a delay.
*/
@@ -339,34 +306,47 @@ protected:
eDelayedEvent
};
/**
* Fire show/hide events for either the current node if it has an accessible,
* or the first-line accessible descendants of the given node.
*
* @param aDOMNode [in] the given node
* @param aAvoidOnThisNode [in] call with PR_TRUE the first time to
* prevent event firing on root node for change
* @param aEventType [in] event type to fire an event
* @param aDelayedOrNormal [in] whether to fire the event on a delay
* @param aIsAsyncChange [in] whether casual change is async
* @param aIsFromUserInput [in] the event is known to be from user input
*/
nsresult FireShowHideEvents(nsINode *aDOMNode, PRBool aAvoidOnThisNode,
PRUint32 aEventType,
EEventFiringType aDelayedOrNormal,
PRBool aIsAsyncChange,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
/**
* Fire a value change event for the the given accessible if it is a text
* field (has a ROLE_ENTRY).
*/
void FireValueChangeForTextFields(nsAccessible *aAccessible);
/**
* Helper for UpdateTree() method. Go down to DOM subtree and updates
* accessible tree. Return one of these flags.
*/
enum EUpdateTreeFlags {
eNoAccessible = 0,
eAccessible = 1,
eAlertAccessible = 2
};
PRUint32 UpdateTreeInternal(nsAccessible* aContainer,
nsIContent* aStartNode,
nsIContent* aEndNode,
PRBool aIsInsert,
PRBool aFireEvents,
EIsFromUserInput aFromUserInput);
/**
* Remove accessibles in subtree from node to accessible map.
*/
void UncacheChildrenInSubtree(nsAccessible* aRoot);
/**
* Shutdown any cached accessible in the subtree.
*
* @param aAccessible [in] the root of the subrtee to invalidate accessible
* child/parent refs in
*/
void ShutdownChildrenInSubtree(nsAccessible *aAccessible);
/**
* Cache of accessibles within this document accessible.
*/
nsAccessibleHashtable mAccessibleCache;
NodeToAccessibleMap mNodeToAccessibleMap;
nsCOMPtr<nsIDocument> mDocument;
nsCOMPtr<nsITimer> mScrollWatchTimer;

View File

@@ -68,12 +68,12 @@ nsEventShell::FireEvent(AccEvent* aEvent)
void
nsEventShell::FireEvent(PRUint32 aEventType, nsAccessible *aAccessible,
PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput)
EIsFromUserInput aIsFromUserInput)
{
NS_ENSURE_TRUE(aAccessible,);
nsRefPtr<AccEvent> event = new AccEvent(aEventType, aAccessible,
aIsAsynch, aIsFromUserInput);
aIsFromUserInput);
FireEvent(event);
}
@@ -147,9 +147,9 @@ nsAccEventQueue::Push(AccEvent* aEvent)
// Associate text change with hide event if it wasn't stolen from hiding
// siblings during coalescence.
AccHideEvent* hideEvent = downcast_accEvent(aEvent);
if (hideEvent && !hideEvent->mTextChangeEvent)
CreateTextChangeEventFor(hideEvent);
AccMutationEvent* showOrHideEvent = downcast_accEvent(aEvent);
if (showOrHideEvent && !showOrHideEvent->mTextChangeEvent)
CreateTextChangeEventFor(showOrHideEvent);
// Process events.
PrepareFlush();
@@ -209,10 +209,10 @@ nsAccEventQueue::WillRefresh(mozilla::TimeStamp aTime)
if (accEvent->mEventRule != AccEvent::eDoNotEmit) {
mDocument->ProcessPendingEvent(accEvent);
AccHideEvent* hideEvent = downcast_accEvent(accEvent);
if (hideEvent) {
if (hideEvent->mTextChangeEvent)
mDocument->ProcessPendingEvent(hideEvent->mTextChangeEvent);
AccMutationEvent* showOrhideEvent = downcast_accEvent(accEvent);
if (showOrhideEvent) {
if (showOrhideEvent->mTextChangeEvent)
mDocument->ProcessPendingEvent(showOrhideEvent->mTextChangeEvent);
}
}
@@ -267,7 +267,7 @@ nsAccEventQueue::CoalesceEvents()
// accessibles can't be created at this point because of lazy frame
// construction (bug 570275).
// Coalesce hide events for sibling targets.
// Coalesce hide and show events for sibling targets.
if (tailEvent->mEventType == nsIAccessibleEvent::EVENT_HIDE) {
AccHideEvent* tailHideEvent = downcast_accEvent(tailEvent);
AccHideEvent* thisHideEvent = downcast_accEvent(thisEvent);
@@ -278,6 +278,20 @@ nsAccEventQueue::CoalesceEvents()
if (tailEvent->mEventRule != AccEvent::eDoNotEmit)
CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent);
return;
}
} else if (tailEvent->mEventType == nsIAccessibleEvent::EVENT_SHOW) {
if (thisEvent->mAccessible->GetParent() ==
tailEvent->mAccessible->GetParent()) {
tailEvent->mEventRule = thisEvent->mEventRule;
// Coalesce text change events for show events.
if (tailEvent->mEventRule != AccEvent::eDoNotEmit) {
AccShowEvent* tailShowEvent = downcast_accEvent(tailEvent);
AccShowEvent* thisShowEvent = downcast_accEvent(thisEvent);
CoalesceTextChangeEventsFor(tailShowEvent, thisShowEvent);
}
return;
}
}
@@ -286,70 +300,22 @@ nsAccEventQueue::CoalesceEvents()
if (!thisEvent->mNode->IsInDoc())
continue;
// Coalesce show and reorder events by sibling targets.
// Coalesce earlier event for the same target.
if (thisEvent->mNode == tailEvent->mNode) {
thisEvent->mEventRule = AccEvent::eDoNotEmit;
return;
}
// Coalesce events by sibling targets (this is a case for reorder
// events).
if (thisEvent->mNode->GetNodeParent() ==
tailEvent->mNode->GetNodeParent()) {
tailEvent->mEventRule = thisEvent->mEventRule;
return;
}
// Specifies if this event target can be descendant of tail node.
PRBool thisCanBeDescendantOfTail = PR_FALSE;
// Coalesce depending on whether this event was coalesced or not.
if (thisEvent->mEventRule == AccEvent::eDoNotEmit) {
// If this event was coalesced then do not emit tail event iff tail
// event has the same target or its target is contained by this event
// target. Note, we don't need to check whether tail event target
// contains this event target since this event was coalesced already.
// As well we don't need to apply the calculated rule for siblings of
// tail node because tail event rule was applied to possible tail
// node siblings while this event was coalesced.
if (thisEvent->mNode == tailEvent->mNode) {
thisEvent->mEventRule = AccEvent::eDoNotEmit;
return;
}
} else {
// If this event wasn't coalesced already then try to coalesce it or
// tail event. If this event is coalesced by tail event then continue
// search through events other events that can be coalesced by tail
// event.
// If tail and this events have the same target then coalesce tail
// event because more early event we should fire early and then stop
// processing.
if (thisEvent->mNode == tailEvent->mNode) {
// Coalesce reorder events by special way since reorder events can
// be conditional events (be or not be fired in the end).
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
CoalesceReorderEventsFromSameSource(thisEvent, tailEvent);
if (tailEvent->mEventRule != AccEvent::eDoNotEmit)
continue;
}
else {
tailEvent->mEventRule = AccEvent::eDoNotEmit;
}
return;
}
// This and tail events can be anywhere in the tree, make assumptions
// for mutation events.
// More older show event target (thisNode) can't be contained by
// recent.
// show event target (tailNode), i.e be a descendant of tailNode.
// XXX: target of older show event caused by DOM node appending can be
// contained by target of recent show event caused by style change.
// XXX: target of older show event caused by style change can be
// contained by target of recent show event caused by style change.
thisCanBeDescendantOfTail =
tailEvent->mEventType != nsIAccessibleEvent::EVENT_SHOW ||
tailEvent->mIsAsync;
}
// This and tail events can be anywhere in the tree, make assumptions
// for mutation events.
// Coalesce tail event if tail node is descendant of this node. Stop
// processing if tail event is coalesced since all possible descendants
@@ -359,54 +325,20 @@ nsAccEventQueue::CoalesceEvents()
// this check for hide events.
if (tailEvent->mEventType != nsIAccessibleEvent::EVENT_HIDE &&
nsCoreUtils::IsAncestorOf(thisEvent->mNode, tailEvent->mNode)) {
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
CoalesceReorderEventsFromSameTree(thisEvent, tailEvent);
if (tailEvent->mEventRule != AccEvent::eDoNotEmit)
continue;
return;
}
tailEvent->mEventRule = AccEvent::eDoNotEmit;
return;
}
#ifdef DEBUG
if (tailEvent->mEventType == nsIAccessibleEvent::EVENT_HIDE &&
nsCoreUtils::IsAncestorOf(thisEvent->mNode, tailEvent->mNode)) {
NS_NOTREACHED("More older hide event target is an ancestor of recent hide event target!");
}
#endif
// If this node is a descendant of tail node then coalesce this event,
// check other events in the queue.
if (thisCanBeDescendantOfTail &&
nsCoreUtils::IsAncestorOf(tailEvent->mNode, thisEvent->mNode)) {
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
CoalesceReorderEventsFromSameTree(tailEvent, thisEvent);
if (tailEvent->mEventRule != AccEvent::eDoNotEmit)
continue;
return;
}
// Do not emit thisEvent, also apply this result to sibling nodes of
// thisNode.
// check other events in the queue. Do not emit thisEvent, also apply
// this result to sibling nodes of thisNode.
if (nsCoreUtils::IsAncestorOf(tailEvent->mNode, thisEvent->mNode)) {
thisEvent->mEventRule = AccEvent::eDoNotEmit;
ApplyToSiblings(0, index, thisEvent->mEventType,
thisEvent->mNode, AccEvent::eDoNotEmit);
continue;
}
#ifdef DEBUG
if (!thisCanBeDescendantOfTail &&
nsCoreUtils::IsAncestorOf(tailEvent->mNode, thisEvent->mNode)) {
NS_NOTREACHED("Older event target is a descendant of recent event target!");
}
#endif
} // for (index)
} break; // case eCoalesceFromSameSubtree
@@ -455,48 +387,13 @@ nsAccEventQueue::ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
for (PRUint32 index = aStart; index < aEnd; index ++) {
AccEvent* accEvent = mEvents[index];
if (accEvent->mEventType == aEventType &&
accEvent->mEventRule != AccEvent::eDoNotEmit &&
accEvent->mEventRule != AccEvent::eDoNotEmit && accEvent->mNode &&
accEvent->mNode->GetNodeParent() == aNode->GetNodeParent()) {
accEvent->mEventRule = aEventRule;
}
}
}
void
nsAccEventQueue::CoalesceReorderEventsFromSameSource(AccEvent* aAccEvent1,
AccEvent* aAccEvent2)
{
// Do not emit event2 if event1 is unconditional.
AccReorderEvent* reorderEvent1 = downcast_accEvent(aAccEvent1);
if (reorderEvent1->IsUnconditionalEvent()) {
aAccEvent2->mEventRule = AccEvent::eDoNotEmit;
return;
}
// Do not emit event1 if event2 is unconditional.
AccReorderEvent* reorderEvent2 = downcast_accEvent(aAccEvent2);
if (reorderEvent2->IsUnconditionalEvent()) {
aAccEvent1->mEventRule = AccEvent::eDoNotEmit;
return;
}
// Do not emit event2 if event1 is valid, otherwise do not emit event1.
if (reorderEvent1->HasAccessibleInReasonSubtree())
aAccEvent2->mEventRule = AccEvent::eDoNotEmit;
else
aAccEvent1->mEventRule = AccEvent::eDoNotEmit;
}
void
nsAccEventQueue::CoalesceReorderEventsFromSameTree(AccEvent* aAccEvent,
AccEvent* aDescendantAccEvent)
{
// Do not emit descendant event if this event is unconditional.
AccReorderEvent* reorderEvent = downcast_accEvent(aAccEvent);
if (reorderEvent->IsUnconditionalEvent())
aDescendantAccEvent->mEventRule = AccEvent::eDoNotEmit;
}
void
nsAccEventQueue::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
AccHideEvent* aThisEvent)
@@ -523,7 +420,35 @@ nsAccEventQueue::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
}
void
nsAccEventQueue::CreateTextChangeEventFor(AccHideEvent* aEvent)
nsAccEventQueue::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
AccShowEvent* aThisEvent)
{
AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
if (!textEvent)
return;
if (aTailEvent->mAccessible->GetIndexInParent() ==
aThisEvent->mAccessible->GetIndexInParent() + 1) {
// If tail target was inserted after this target, i.e. tail target is next
// sibling of this target.
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText,
0, PR_UINT32_MAX);
} else if (aTailEvent->mAccessible->GetIndexInParent() ==
aThisEvent->mAccessible->GetIndexInParent() -1) {
// If tail target was inserted before this target, i.e. tail target is
// previous sibling of this target.
nsAutoString startText;
aTailEvent->mAccessible->AppendTextTo(startText, 0, PR_UINT32_MAX);
textEvent->mModifiedText = startText + textEvent->mModifiedText;
textEvent->mStart -= startText.Length();
}
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
}
void
nsAccEventQueue::CreateTextChangeEventFor(AccMutationEvent* aEvent)
{
nsRefPtr<nsHyperTextAccessible> textAccessible = do_QueryObject(
GetAccService()->GetContainerAccessible(aEvent->mNode,
@@ -551,7 +476,6 @@ nsAccEventQueue::CreateTextChangeEventFor(AccHideEvent* aEvent)
return;
aEvent->mTextChangeEvent =
new AccTextChangeEvent(textAccessible, offset, text, PR_FALSE,
aEvent->mIsAsync,
new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(),
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
}

View File

@@ -66,11 +66,8 @@ public:
*
* @param aEventType [in] the event type
* @param aAccessible [in] the event target
* @param aIsAsync [in, optional] specifies whether the origin change
* this event is fired owing to is async.
*/
static void FireEvent(PRUint32 aEventType, nsAccessible *aAccessible,
PRBool aIsAsynch = PR_FALSE,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
/**
@@ -144,12 +141,6 @@ private:
PRUint32 aEventType, nsINode* aNode,
AccEvent::EEventRule aEventRule);
/**
* Do not emit one of two given reorder events fired for the same DOM node.
*/
void CoalesceReorderEventsFromSameSource(AccEvent* aAccEvent1,
AccEvent* aAccEvent2);
/**
* Do not emit one of two given reorder events fired for DOM nodes in the case
* when one DOM node is in parent chain of second one.
@@ -162,14 +153,15 @@ private:
*/
void CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
AccHideEvent* aThisEvent);
void CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
AccShowEvent* aThisEvent);
/**
* Create text change event caused by hide event. When a node is hidden or
* removed, the text in an ancestor hyper text will lose characters. Create
* text change event unless the node is being removed or frame is being
* destroyed.
* Create text change event caused by hide or show event. When a node is
* hidden/removed or shown/appended, the text in an ancestor hyper text will
* lose or get new characters.
*/
void CreateTextChangeEventFor(AccHideEvent* aEvent);
void CreateTextChangeEventFor(AccMutationEvent* aEvent);
/**
* Indicates whether we're waiting on a refresh notification from our

View File

@@ -319,7 +319,6 @@ nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
nsINode *aNode,
nsIDOMEvent *aFocusEvent,
PRBool aForceEvent,
PRBool aIsAsynch,
EIsFromUserInput aIsFromUserInput)
{
// Implementors: only fire delayed/async events from this method.
@@ -398,7 +397,7 @@ nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
if (mCurrentARIAMenubar) {
nsRefPtr<AccEvent> menuStartEvent =
new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
menuBarAccessible, PR_FALSE, aIsFromUserInput,
menuBarAccessible, aIsFromUserInput,
AccEvent::eAllowDupes);
if (menuStartEvent) {
FireDelayedAccessibleEvent(menuStartEvent);
@@ -411,7 +410,7 @@ nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
else if (mCurrentARIAMenubar) {
nsRefPtr<AccEvent> menuEndEvent =
new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mCurrentARIAMenubar,
PR_FALSE, aIsFromUserInput, AccEvent::eAllowDupes);
aIsFromUserInput, AccEvent::eAllowDupes);
if (menuEndEvent) {
FireDelayedAccessibleEvent(menuEndEvent);
}
@@ -434,13 +433,11 @@ nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
gLastFocusedNode = finalFocusNode;
NS_IF_ADDREF(gLastFocusedNode);
gLastFocusedFrameType = (focusFrame && focusFrame->GetStyleVisibility()->IsVisible()) ? focusFrame->GetType() : 0;
// Coalesce focus events from the same document, because DOM focus event might
// be fired for the document node and then for the focused DOM element.
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
finalFocusNode, AccEvent::eCoalesceFromSameDocument,
aIsAsynch, aIsFromUserInput);
aIsFromUserInput);
return PR_TRUE;
}
@@ -663,7 +660,6 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
}
else if (eventType.EqualsLiteral("blur")) {
NS_IF_RELEASE(gLastFocusedNode);
gLastFocusedFrameType = nsnull;
gLastFocusedAccessiblesState = 0;
}
else if (eventType.EqualsLiteral("AlertActive")) {
@@ -732,16 +728,16 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
if (fireFocus) {
// Always asynch, always from user input.
FireAccessibleFocusEvent(accessible, targetNode, aEvent, PR_TRUE,
PR_TRUE, eFromUserInput);
eFromUserInput);
}
}
else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always asynch, always from user input
else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always from user input
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
accessible, PR_TRUE, eFromUserInput);
accessible, eFromUserInput);
}
else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always asynch, always from user input
else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always from user input
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
accessible, PR_TRUE, eFromUserInput);
accessible, eFromUserInput);
FireCurrentFocusEvent();
}
else if (eventType.EqualsLiteral("ValueChange")) {

View File

@@ -106,7 +106,6 @@ public:
nsINode *aFocusNode,
nsIDOMEvent *aFocusEvent,
PRBool aForceEvent = PR_FALSE,
PRBool aIsAsynch = PR_FALSE,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
/**

View File

@@ -237,6 +237,11 @@ nsresult
nsTextEquivUtils::AppendFromAccessible(nsAccessible *aAccessible,
nsAString *aString)
{
// Ignore hidden accessible for name computation.
nsIFrame* frame = aAccessible->GetFrame();
if (!frame || !frame->GetStyleVisibility()->IsVisible())
return NS_OK;
//XXX: is it necessary to care the accessible is not a document?
if (aAccessible->IsContent()) {
nsresult rv = AppendTextEquivFromTextContent(aAccessible->GetContent(),

View File

@@ -41,7 +41,7 @@
#include "nsCoreUtils.h"
#include "nsILink.h"
#include "nsIEventStateManager.h"
////////////////////////////////////////////////////////////////////////////////
// nsHTMLLinkAccessible
@@ -82,20 +82,22 @@ nsHTMLLinkAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
*aState |= nsIAccessibleStates::STATE_SELECTABLE;
}
nsLinkState linkState = mContent->GetLinkState();
if (linkState == eLinkState_NotLink || linkState == eLinkState_Unknown) {
// This is a either named anchor (a link with also a name attribute) or
// it doesn't have any attributes. Check if 'click' event handler is
// registered, otherwise bail out.
PRBool isOnclick = nsCoreUtils::HasClickListener(mContent);
if (!isOnclick)
return NS_OK;
nsEventStates state = mContent->IntrinsicState();
if (state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED |
NS_EVENT_STATE_UNVISITED)) {
*aState |= nsIAccessibleStates::STATE_LINKED;
if (state.HasState(NS_EVENT_STATE_VISITED))
*aState |= nsIAccessibleStates::STATE_TRAVERSED;
return NS_OK;
}
*aState |= nsIAccessibleStates::STATE_LINKED;
if (linkState == eLinkState_Visited)
*aState |= nsIAccessibleStates::STATE_TRAVERSED;
// This is a either named anchor (a link with also a name attribute) or
// it doesn't have any attributes. Check if 'click' event handler is
// registered, otherwise bail out.
if (nsCoreUtils::HasClickListener(mContent))
*aState |= nsIAccessibleStates::STATE_LINKED;
return NS_OK;
}
@@ -186,6 +188,7 @@ nsHTMLLinkAccessible::IsLinked()
if (IsDefunct())
return PR_FALSE;
nsLinkState linkState = mContent->GetLinkState();
return linkState != eLinkState_NotLink && linkState != eLinkState_Unknown;
nsEventStates state = mContent->IntrinsicState();
return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED |
NS_EVENT_STATE_UNVISITED);
}

View File

@@ -665,6 +665,9 @@ nsHTMLComboboxAccessible::
{
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLComboboxAccessible: nsAccessible
PRUint32
nsHTMLComboboxAccessible::NativeRole()
{
@@ -859,6 +862,9 @@ nsHTMLComboboxListAccessible::
{
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLComboboxAccessible: nsAccessNode
nsIFrame*
nsHTMLComboboxListAccessible::GetFrame()
{
@@ -874,6 +880,15 @@ nsHTMLComboboxListAccessible::GetFrame()
return nsnull;
}
bool
nsHTMLComboboxListAccessible::IsPrimaryForNode() const
{
return false;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLComboboxAccessible: nsAccessible
/**
* As a nsHTMLComboboxListAccessible we can have the following states:
* STATE_FOCUSED
@@ -899,14 +914,6 @@ nsHTMLComboboxListAccessible::GetStateInternal(PRUint32 *aState,
return NS_OK;
}
NS_IMETHODIMP nsHTMLComboboxListAccessible::GetUniqueID(void **aUniqueID)
{
// Since mContent is same for all tree item, use |this| pointer as the unique
// Id.
*aUniqueID = static_cast<void*>(this);
return NS_OK;
}
/**
* Gets the bounds for the areaFrame.
* Walks the Frame tree and checks for proper frames.

View File

@@ -226,11 +226,9 @@ public:
nsIWeakReference* aShell);
virtual ~nsHTMLComboboxListAccessible() {}
// nsIAccessible
NS_IMETHOD GetUniqueID(void **aUniqueID);
// nsAccessNode
virtual nsIFrame* GetFrame();
virtual bool IsPrimaryForNode() const;
// nsAccessible
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);

View File

@@ -46,6 +46,7 @@
#include "nsIFrame.h"
#include "nsPresContext.h"
#include "nsBlockFrame.h"
#include "nsISelection.h"
#include "nsISelectionController.h"
#include "nsComponentManagerUtils.h"
@@ -252,13 +253,12 @@ nsHTMLOutputAccessible::GetAttributesInternal(nsIPersistentProperties* aAttribut
////////////////////////////////////////////////////////////////////////////////
nsHTMLLIAccessible::
nsHTMLLIAccessible(nsIContent *aContent, nsIWeakReference *aShell,
const nsAString& aBulletText) :
nsHTMLLIAccessible(nsIContent* aContent, nsIWeakReference* aShell) :
nsHyperTextAccessibleWrap(aContent, aShell)
{
if (!aBulletText.IsEmpty()) {
mBulletAccessible = new nsHTMLListBulletAccessible(mContent, mWeakShell,
aBulletText);
nsBlockFrame* blockFrame = do_QueryFrame(GetFrame());
if (blockFrame && !blockFrame->BulletIsEmptyExternal()) {
mBulletAccessible = new nsHTMLListBulletAccessible(mContent, mWeakShell);
if (mBulletAccessible)
mBulletAccessible->Init();
}
@@ -329,21 +329,14 @@ nsHTMLLIAccessible::CacheChildren()
////////////////////////////////////////////////////////////////////////////////
nsHTMLListBulletAccessible::
nsHTMLListBulletAccessible(nsIContent *aContent, nsIWeakReference *aShell,
const nsAString& aBulletText) :
nsLeafAccessible(aContent, aShell), mBulletText(aBulletText)
nsHTMLListBulletAccessible(nsIContent* aContent, nsIWeakReference* aShell) :
nsLeafAccessible(aContent, aShell)
{
mBulletText += ' '; // Otherwise bullets are jammed up against list text
}
NS_IMETHODIMP
nsHTMLListBulletAccessible::GetUniqueID(void **aUniqueID)
{
// Since mContent is same as for list item, use |this| pointer as the unique
// id.
*aUniqueID = static_cast<void*>(this);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLListBulletAccessible: nsAccessNode
void
nsHTMLListBulletAccessible::Shutdown()
@@ -352,11 +345,32 @@ nsHTMLListBulletAccessible::Shutdown()
nsLeafAccessible::Shutdown();
}
bool
nsHTMLListBulletAccessible::IsPrimaryForNode() const
{
return false;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLListBulletAccessible: nsAccessible
NS_IMETHODIMP
nsHTMLListBulletAccessible::GetName(nsAString &aName)
{
// Native anonymous content, ARIA can't be used.
aName = mBulletText;
aName.Truncate();
if (IsDefunct())
return NS_ERROR_FAILURE;
// Native anonymous content, ARIA can't be used. Get list bullet text.
nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
if (blockFrame) {
blockFrame->GetBulletText(aName);
// Append space otherwise bullets are jammed up against list text.
aName.Append(' ');
}
return NS_OK;
}
@@ -381,11 +395,17 @@ nsresult
nsHTMLListBulletAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset,
PRUint32 aLength)
{
PRUint32 maxLength = mBulletText.Length() - aStartOffset;
if (aLength > maxLength) {
aLength = maxLength;
nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
if (blockFrame) {
nsAutoString bulletText;
blockFrame->GetBulletText(bulletText);
PRUint32 maxLength = bulletText.Length() - aStartOffset;
if (aLength > maxLength)
aLength = maxLength;
aText += Substring(bulletText, aStartOffset, aLength);
}
aText += Substring(mBulletText, aStartOffset, aLength);
return NS_OK;
}

View File

@@ -130,17 +130,14 @@ public:
class nsHTMLListBulletAccessible : public nsLeafAccessible
{
public:
nsHTMLListBulletAccessible(nsIContent *aContent, nsIWeakReference *aShell,
const nsAString& aBulletText);
// nsIAccessNode
NS_IMETHOD GetUniqueID(void **aUniqueID);
nsHTMLListBulletAccessible(nsIContent* aContent, nsIWeakReference* aShell);
// nsIAccessible
NS_IMETHOD GetName(nsAString& aName);
// nsAccessNode
virtual void Shutdown();
virtual bool IsPrimaryForNode() const;
// nsAccessible
virtual PRUint32 NativeRole();
@@ -180,8 +177,7 @@ public:
class nsHTMLLIAccessible : public nsHyperTextAccessibleWrap
{
public:
nsHTMLLIAccessible(nsIContent *aContent, nsIWeakReference *aShell,
const nsAString& aBulletText);
nsHTMLLIAccessible(nsIContent* aContent, nsIWeakReference* aShell);
// nsISupports
NS_DECL_ISUPPORTS_INHERITED

View File

@@ -257,9 +257,7 @@ __try{
// accessibility application can compare this to the childID we
// return for events such as focus events, to correlate back to
// data nodes in their internal object model.
void *uniqueID;
GetUniqueID(&uniqueID);
*aUniqueID = - NS_PTR_TO_INT32(uniqueID);
*aUniqueID = - NS_PTR_TO_INT32(UniqueID());
*aNumChildren = GetNode()->GetChildCount();

View File

@@ -1354,12 +1354,7 @@ STDMETHODIMP
nsAccessibleWrap::get_uniqueID(long *uniqueID)
{
__try {
void *id = nsnull;
nsresult rv = GetUniqueID(&id);
if (NS_FAILED(rv))
return GetHRESULT(rv);
*uniqueID = - reinterpret_cast<long>(id);
*uniqueID = - reinterpret_cast<long>(UniqueID());
return S_OK;
} __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
@@ -1575,60 +1570,53 @@ nsAccessibleWrap::FirePlatformEvent(AccEvent* aEvent)
if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
eventType == nsIAccessibleEvent::EVENT_FOCUS) {
UpdateSystemCaret();
} else if (eventType == nsIAccessibleEvent::EVENT_REORDER) {
// If the accessible children are changed then drop the IEnumVariant current
// position of the accessible.
UnattachIEnumVariant();
}
PRInt32 childID = GetChildIDFor(accessible); // get the id for the accessible
if (!childID)
return NS_OK; // Can't fire an event without a child ID
// See if we're in a scrollable area with its own window
nsAccessible *newAccessible = nsnull;
if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
// Don't use frame from current accessible when we're hiding that
// accessible.
newAccessible = accessible->GetCachedParent();
} else {
newAccessible = accessible;
}
HWND hWnd = GetHWNDFor(newAccessible);
HWND hWnd = GetHWNDFor(accessible);
NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
// Gecko uses two windows for every scrollable area. One window contains
// scrollbars and the child window contains only the client area.
// Details of the 2 window system:
// * Scrollbar window: caret drawing window & return value for WindowFromAccessibleObject()
// * Client area window: text drawing window & MSAA event window
nsAutoString tag;
nsCAutoString id;
nsIContent* cnt = accessible->GetContent();
if (cnt) {
cnt->Tag()->ToString(tag);
nsIAtom* aid = cnt->GetID();
if (aid)
aid->ToUTF8String(id);
}
#ifdef DEBUG_A11Y
printf("\n\nMSAA event: event: %d, target: %s@id='%s', childid: %d, hwnd: %d\n\n",
eventType, NS_ConvertUTF16toUTF8(tag).get(), id.get(),
childID, hWnd);
#endif
// Fire MSAA event for client area window.
NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
// If the accessible children are changed then drop the IEnumVariant current
// position of the accessible.
if (eventType == nsIAccessibleEvent::EVENT_REORDER)
UnattachIEnumVariant();
return NS_OK;
}
//------- Helper methods ---------
PRInt32 nsAccessibleWrap::GetChildIDFor(nsIAccessible* aAccessible)
PRInt32 nsAccessibleWrap::GetChildIDFor(nsAccessible* aAccessible)
{
// A child ID of the window is required, when we use NotifyWinEvent,
// so that the 3rd party application can call back and get the IAccessible
// the event occurred on.
void *uniqueID = nsnull;
nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
if (!accessNode) {
return 0;
}
accessNode->GetUniqueID(&uniqueID);
// Yes, this means we're only compatibible with 32 bit
// MSAA is only available for 32 bit windows, so it's okay
return - NS_PTR_TO_INT32(uniqueID);
// XXX: bug 606080
return aAccessible ? - NS_PTR_TO_INT32(aAccessible->UniqueID()) : 0;
}
HWND

View File

@@ -310,7 +310,7 @@ public: // construction, destruction
virtual nsresult HandleAccEvent(AccEvent* aEvent);
// Helper methods
static PRInt32 GetChildIDFor(nsIAccessible* aAccessible);
static PRInt32 GetChildIDFor(nsAccessible* aAccessible);
static HWND GetHWNDFor(nsAccessible *aAccessible);
static HRESULT ConvertToIA2Attributes(nsIPersistentProperties *aAttributes,
BSTR *aIA2Attributes);

View File

@@ -109,7 +109,7 @@ nsDocAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
if (aVarChild.vt == VT_I4 && aVarChild.lVal < 0) {
// Convert child ID to unique ID.
void* uniqueID = reinterpret_cast<void*>(-aVarChild.lVal);
return GetCachedAccessibleInSubtree(uniqueID);
return GetCachedAccessibleByUniqueIDInSubtree(uniqueID);
}
return nsAccessibleWrap::GetXPAccessibleFor(aVarChild);

View File

@@ -509,15 +509,13 @@ nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount)
if (accessible) {
nsRefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_HIDE, accessible, PR_FALSE);
new AccEvent(nsIAccessibleEvent::EVENT_HIDE, accessible);
nsEventShell::FireEvent(event);
accessible->Shutdown();
// Remove accessible from document cache and tree cache.
// Shutdown and remove accessible from document cache and tree cache.
nsDocAccessible *docAccessible = GetDocAccessible();
if (docAccessible)
docAccessible->RemoveAccessNodeFromCache(accessible);
docAccessible->ShutdownAccessible(accessible);
mAccessibleCache.Remove(key);
}
@@ -539,12 +537,10 @@ nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount)
nsAccessible *accessible = mAccessibleCache.GetWeak(key);
if (accessible) {
accessible->Shutdown();
// Remove accessible from document cache and tree cache.
// Shutdown and remove accessible from document cache and tree cache.
nsDocAccessible *docAccessible = GetDocAccessible();
if (docAccessible)
docAccessible->RemoveAccessNodeFromCache(accessible);
docAccessible->ShutdownAccessible(accessible);
mAccessibleCache.Remove(key);
}
@@ -610,7 +606,7 @@ nsXULTreeAccessible::TreeViewChanged()
// AT because it should be expensive to fire destroy events for each tree item
// in cache.
nsRefPtr<AccEvent> eventDestroy =
new AccEvent(nsIAccessibleEvent::EVENT_HIDE, this, PR_FALSE);
new AccEvent(nsIAccessibleEvent::EVENT_HIDE, this);
if (!eventDestroy)
return;
@@ -621,7 +617,7 @@ nsXULTreeAccessible::TreeViewChanged()
mTree->GetView(getter_AddRefs(mTreeView));
nsRefPtr<AccEvent> eventCreate =
new AccEvent(nsIAccessibleEvent::EVENT_SHOW, this, PR_FALSE);
new AccEvent(nsIAccessibleEvent::EVENT_SHOW, this);
if (!eventCreate)
return;
@@ -662,18 +658,6 @@ NS_IMPL_ISUPPORTS_INHERITED1(nsXULTreeItemAccessibleBase,
nsAccessible,
nsXULTreeItemAccessibleBase)
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeItemAccessibleBase: nsIAccessNode implementation
NS_IMETHODIMP
nsXULTreeItemAccessibleBase::GetUniqueID(void **aUniqueID)
{
// Since mContent is same for all tree items and tree itself, use |this|
// pointer as the unique ID.
*aUniqueID = static_cast<void*>(this);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeItemAccessibleBase: nsIAccessible implementation
@@ -888,6 +872,12 @@ nsXULTreeItemAccessibleBase::Shutdown()
nsAccessibleWrap::Shutdown();
}
bool
nsXULTreeItemAccessibleBase::IsPrimaryForNode() const
{
return false;
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeItemAccessibleBase: nsAccessible public methods

View File

@@ -181,9 +181,6 @@ public:
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
// nsIAccessNode
NS_IMETHOD GetUniqueID(void **aUniqueID);
// nsIAccessible
NS_IMETHOD GetFocusedChild(nsIAccessible **aFocusedChild);
@@ -207,6 +204,7 @@ public:
// nsAccessNode
virtual PRBool IsDefunct();
virtual void Shutdown();
virtual bool IsPrimaryForNode() const;
// nsAccessible
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);

View File

@@ -804,18 +804,6 @@ NS_IMPL_ISUPPORTS_INHERITED2(nsXULTreeGridCellAccessible,
nsIAccessibleTableCell,
nsXULTreeGridCellAccessible)
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeGridCellAccessible: nsIAccessNode implementation
NS_IMETHODIMP
nsXULTreeGridCellAccessible::GetUniqueID(void **aUniqueID)
{
NS_ENSURE_ARG_POINTER(aUniqueID);
*aUniqueID = static_cast<void*>(this);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeGridCellAccessible: nsIAccessible implementation
@@ -1124,6 +1112,12 @@ nsXULTreeGridCellAccessible::Init()
return PR_TRUE;
}
bool
nsXULTreeGridCellAccessible::IsPrimaryForNode() const
{
return false;
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeGridCellAccessible: nsAccessible public implementation

View File

@@ -141,9 +141,6 @@ public:
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
// nsIAccessNode
NS_IMETHOD GetUniqueID(void **aUniqueID);
// nsIAccessible
NS_IMETHOD GetFocusedChild(nsIAccessible **aFocusedChild);
@@ -161,6 +158,7 @@ public:
// nsAccessNode
virtual PRBool IsDefunct();
virtual PRBool Init();
virtual bool IsPrimaryForNode() const;
// nsAccessible
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);

View File

@@ -52,6 +52,7 @@ DIRS = \
states \
table \
tree \
treeupdate \
$(null)
include $(DEPTH)/config/autoconf.mk

View File

@@ -51,6 +51,7 @@ _TEST_FILES =\
test_general.html \
test_general.xul \
test_inputs.html \
test_link.html \
test_tree.xul \
test_treegrid.xul \
$(NULL)

View File

@@ -19,18 +19,55 @@
src="../actions.js"></script>
<script type="application/javascript">
function getAnchorTargetDocumentAcc()
{
var thisTabDocAcc = getTabDocAccessible();
var thisDocTabPanelAcc = thisTabDocAcc.parent.parent;
var tabPanelsAcc = thisDocTabPanelAcc.parent;
var newDocTabPanelAcc = tabPanelsAcc.lastChild.firstChild;
return newDocTabPanelAcc.firstChild;
}
function linkChecker(aID)
{
this.type = EVENT_DOCUMENT_LOAD_COMPLETE;
this.__defineGetter__("target", getAnchorTargetDocumentAcc);
this.check = function linkChecker_check()
{
var anchorTargetWindow =
getAccessible(getAnchorTargetDocumentAcc(), [nsIAccessibleDocument]).
window;
anchorTargetWindow.close();
}
this.getID = function linkChecker_getID()
{
return "link '" + aID + "' states check ";
}
}
//gA11yEventDumpID = "eventdump"; // debug stuff
function doTest()
{
var actionsArray = [
{
ID: "link1",
actionName: "jump",
events: CLICK_EVENTS
events: CLICK_EVENTS,
eventSeq: [
new linkChecker("link1")
]
},
{
ID: "img1",
actionName: "click",
events: CLICK_EVENTS
targetID: "link1",
actionName: "jump",
events: CLICK_EVENTS,
eventSeq: [
new linkChecker("link1")
]
},
{
ID: "link2",
@@ -39,7 +76,8 @@
},
{
ID: "img2",
actionName: "click",
targetID: "link2",
actionName: "jump",
events: CLICK_EVENTS
},
{
@@ -49,7 +87,8 @@
},
{
ID: "img3",
actionName: "click",
targetID: "link3",
actionName: "jump",
events: CLICK_EVENTS
},
{
@@ -59,7 +98,8 @@
},
{
ID: "img4",
actionName: "click",
targetID: "link4",
actionName: "jump",
events: CLICK_EVENTS
}
];
@@ -83,7 +123,7 @@
<pre id="test">
</pre>
<a href="http://mozilla.org" id="link1">
<a href="about:mozilla" id="link1" target="_blank">
<img src="../moz.png" id="img1">
</a>
<a id="link2" onmousedown="">
@@ -95,5 +135,7 @@
<a id="link4" onmouseup="">
<img src="../moz.png" id="img4">
</a>
<div id="eventdump"></div>
</body>
</html>

View File

@@ -429,11 +429,8 @@
ID = "area14";
defAttrs = buildDefaultTextAttrs(ID, kInputFontSize);
// XXX: While we expose text leaf accessibles for placeholder we grab its
// style, bug 545817.
// attrs = { color: "rgb(109, 109, 109)" };
//testTextAttrs(ID, 0, attrs, defAttrs, 0, 0);
todo(false, "enable commented tests when bug 545817 is fixed");
attrs = { };
testTextAttrs(ID, 0, attrs, defAttrs, 0, 0);
//////////////////////////////////////////////////////////////////////////
// area15, embed char tests, "*plain*plain**bold*bold*"

View File

@@ -50,42 +50,6 @@ const nsIDOMXULElement = Components.interfaces.nsIDOMXULElement;
const nsIPropertyElement = Components.interfaces.nsIPropertyElement;
////////////////////////////////////////////////////////////////////////////////
// States
const STATE_BUSY = nsIAccessibleStates.STATE_BUSY;
const STATE_CHECKED = nsIAccessibleStates.STATE_CHECKED;
const STATE_CHECKABLE = nsIAccessibleStates.STATE_CHECKABLE;
const STATE_COLLAPSED = nsIAccessibleStates.STATE_COLLAPSED;
const STATE_EXPANDED = nsIAccessibleStates.STATE_EXPANDED;
const STATE_EXTSELECTABLE = nsIAccessibleStates.STATE_EXTSELECTABLE;
const STATE_FOCUSABLE = nsIAccessibleStates.STATE_FOCUSABLE;
const STATE_FOCUSED = nsIAccessibleStates.STATE_FOCUSED;
const STATE_HASPOPUP = nsIAccessibleStates.STATE_HASPOPUP;
const STATE_INVALID = nsIAccessibleStates.STATE_INVALID;
const STATE_LINKED = nsIAccessibleStates.STATE_LINKED;
const STATE_MIXED = nsIAccessibleStates.STATE_MIXED;
const STATE_MULTISELECTABLE = nsIAccessibleStates.STATE_MULTISELECTABLE;
const STATE_OFFSCREEN = nsIAccessibleStates.STATE_OFFSCREEN;
const STATE_PRESSED = nsIAccessibleStates.STATE_PRESSED;
const STATE_READONLY = nsIAccessibleStates.STATE_READONLY;
const STATE_REQUIRED = nsIAccessibleStates.STATE_REQUIRED;
const STATE_SELECTABLE = nsIAccessibleStates.STATE_SELECTABLE;
const STATE_SELECTED = nsIAccessibleStates.STATE_SELECTED;
const STATE_TRAVERSED = nsIAccessibleStates.STATE_TRAVERSED;
const STATE_UNAVAILABLE = nsIAccessibleStates.STATE_UNAVAILABLE;
const EXT_STATE_ACTIVE = nsIAccessibleStates.EXT_STATE_ACTIVE;
const EXT_STATE_DEFUNCT = nsIAccessibleStates.EXT_STATE_DEFUNCT;
const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE;
const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE;
const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL;
const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE;
const EXT_STATE_SINGLE_LINE = nsIAccessibleStates.EXT_STATE_SINGLE_LINE;
const EXT_STATE_SUPPORTS_AUTOCOMPLETION =
nsIAccessibleStates.EXT_STATE_SUPPORTS_AUTOCOMPLETION;
const EXT_STATE_VERTICAL = nsIAccessibleStates.EXT_STATE_VERTICAL;
////////////////////////////////////////////////////////////////////////////////
// OS detect
const MAC = (navigator.platform.indexOf("Mac") != -1)? true : false;
@@ -96,6 +60,8 @@ const WIN = (navigator.platform.indexOf("Win") != -1)? true : false;
////////////////////////////////////////////////////////////////////////////////
// Accessible general
const STATE_BUSY = nsIAccessibleStates.STATE_BUSY;
const kEmbedChar = String.fromCharCode(0xfffc);
/**
@@ -264,6 +230,19 @@ function isAccessible(aAccOrElmOrID, aInterfaces)
true : false;
}
/**
* Return an accessible that contains the DOM node for the given identifier.
*/
function getContainerAccessible(aAccOrElmOrID)
{
var node = getNode(aAccOrElmOrID);
if (!node)
return null;
while ((node = node.parentNode) && !isAccessible(node));
return node ? getAccessible(node) : null;
}
/**
* Return root accessible for the given identifier.
*/
@@ -274,6 +253,25 @@ function getRootAccessible(aAccOrElmOrID)
return acc ? acc.rootDocument.QueryInterface(nsIAccessible) : null;
}
/**
* Return tab document accessible the given accessible is contained by.
*/
function getTabDocAccessible(aAccOrElmOrID)
{
var acc = getAccessible(aAccOrElmOrID ? aAccOrElmOrID : document,
[nsIAccessNode]);
var docAcc = acc.document.QueryInterface(nsIAccessible);
var containerDocAcc = docAcc.parent.QueryInterface(nsIAccessNode).document;
// Test is running is stand-alone mode.
if (acc.rootDocument == containerDocAcc)
return docAcc;
// In the case of running all tests together.
return containerDocAcc.QueryInterface(nsIAccessible);
}
/**
* Return application accessible.
*/
@@ -542,6 +540,15 @@ function prettyName(aIdentifier)
} catch (e) {
msg += "defunct";
}
if (acc) {
var exp = /native\s*@\s*(0x[a-f0-9]+)/g;
var match = exp.exec(acc.valueOf());
if (match)
msg += ", address: " + match[1];
else
msg += ", address: " + acc.valueOf();
}
msg += "]";
return msg;
@@ -571,14 +578,16 @@ addLoadEvent(initialize);
function getNodePrettyName(aNode)
{
try {
if (aNode.nodeType == nsIDOMNode.ELEMENT_NODE && aNode.hasAttribute("id"))
return " '" + aNode.getAttribute("id") + "' ";
if (aNode.nodeType == nsIDOMNode.DOCUMENT_NODE)
return " 'document node' ";
return " '" + aNode.localName + " node' ";
var name = " '" + aNode.localName;
if (aNode.nodeType == nsIDOMNode.ELEMENT_NODE && aNode.hasAttribute("id"))
name += "@id='" + aNode.getAttribute("id") + "'";
name += " node' "
return name;
} catch (e) {
return "no node info";
return "' no node info '";
}
}

View File

@@ -8,6 +8,8 @@ const EVENT_DOCUMENT_LOAD_STOPPED = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_STOPP
const EVENT_HIDE = nsIAccessibleEvent.EVENT_HIDE;
const EVENT_FOCUS = nsIAccessibleEvent.EVENT_FOCUS;
const EVENT_NAME_CHANGE = nsIAccessibleEvent.EVENT_NAME_CHANGE;
const EVENT_MENUPOPUP_START = nsIAccessibleEvent.EVENT_MENUPOPUP_START;
const EVENT_MENUPOPUP_END = nsIAccessibleEvent.EVENT_MENUPOPUP_END;
const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
const EVENT_SCROLLING_START = nsIAccessibleEvent.EVENT_SCROLLING_START;
const EVENT_SELECTION_ADD = nsIAccessibleEvent.EVENT_SELECTION_ADD;
@@ -554,7 +556,9 @@ function eventQueue(aEventType)
styledNode.textContent = "matched";
// Dump matched events into console.
dump("\n*****\nEQ matched: " + eventTypeToString(currType) + "\n*****\n");
if (gA11yEventDumpToConsole)
dump("\n*****\nEQ matched: " + eventTypeToString(currType) + "\n*****\n");
} else {
styledNode.textContent = "expected";
}
@@ -932,6 +936,9 @@ var gA11yEventObserver =
info += ". Listeners count: " + listenersArray.length;
eventFromDumpArea = false;
if (gA11yEventDumpToConsole)
dump("\n" + info + "\n");
dumpInfoToDOM(info);
}
}

View File

@@ -54,7 +54,8 @@ _TEST_FILES =\
test_aria_statechange.html \
test_attrs.html \
test_caretmove.html \
$(warning test_coalescence.html temporarily disabled) \
test_coalescence.html \
test_contextmenu.html \
test_docload.html \
test_docload.xul \
test_dragndrop.html \

View File

@@ -364,6 +364,11 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=513213"
title="coalesce events when new event is appended to the queue">
Mozilla Bug 513213
</a><br>
<a target="_blank"
title="Rework accessible tree update code"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
Mozilla Bug 570275
</a>
<p id="display"></p>

View File

@@ -0,0 +1,141 @@
<html>
<head>
<title>Context menu tests</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript"
src="../states.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript">
////////////////////////////////////////////////////////////////////////////
// Invokers
function showContextMenu(aID)
{
this.DOMNode = getNode(aID);
this.eventSeq = [
new invokerChecker(EVENT_MENUPOPUP_START, getContextMenuNode()),
];
this.invoke = function showContextMenu_invoke()
{
synthesizeMouse(this.DOMNode, 4, 4, { type: "contextmenu", button: 2 });
}
this.getID = function showContextMenu_getID()
{
return "show context menu";
}
}
function selectMenuItem()
{
this.eventSeq = [
new invokerChecker(EVENT_FOCUS, getFocusedMenuItem)
];
this.invoke = function selectMenuItem_invoke()
{
synthesizeKey("VK_DOWN", { });
}
this.getID = function selectMenuItem_getID()
{
return "select first menuitem";
}
}
function closeContextMenu(aID)
{
this.eventSeq = [
new invokerChecker(EVENT_MENUPOPUP_END,
getAccessible(getContextMenuNode()))
];
this.invoke = function closeContextMenu_invoke()
{
synthesizeKey("VK_ESCAPE", { });
}
this.getID = function closeContextMenu_getID()
{
return "close context menu";
}
}
function getContextMenuNode()
{
return getRootAccessible().DOMDocument.
getElementById("contentAreaContextMenu");
}
function getFocusedMenuItem()
{
var menu = getAccessible(getAccessible(getContextMenuNode()));
for (var idx = 0; idx < menu.childCount; idx++) {
var item = menu.getChildAt(idx);
if (hasState(item, STATE_FOCUSED))
return getAccessible(item, [nsIAccessNode]);
}
return null;
}
////////////////////////////////////////////////////////////////////////////
// Do tests
var gQueue = null;
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true;
function doTests()
{
gQueue = new eventQueue();
gQueue.push(new showContextMenu("input"));
gQueue.push(new selectMenuItem());
gQueue.push(new closeContextMenu());
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTests);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=580535"
title="Broken accessibility in context menus">
Mozilla Bug 580535
</a><br>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<input id="input">
<div id="eventdump"></div>
</body>
</html>

View File

@@ -58,11 +58,34 @@
}
}
function showNFocusAlertDialog()
{
this.ID = "alertdialog";
this.DOMNode = getNode(this.ID);
this.invoke = function showNFocusAlertDialog_invoke()
{
document.getElementById(this.ID).style.display = 'block';
document.getElementById(this.ID).focus();
todo(false, "Enable show event handling when bug 422744 is fixed.");
}
this.eventSeq = [
new invokerChecker(EVENT_FOCUS, this.DOMNode),
// new invokerChecker(EVENT_SHOW, this.DOMNode)
];
this.getID = function showNFocusAlertDialog_getID()
{
return "Show and focus alert dialog " + prettyName(this.ID);
}
}
/**
* Do tests.
*/
// gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpID = "eventdump"; // debug stuff
var gQueue = null;
@@ -82,6 +105,8 @@
gQueue.push(new focusElmWhileSubdocIsFocused("button"));
gQueue.push(new showNFocusAlertDialog());
gQueue.invoke(); // Will call SimpleTest.finish();
}
@@ -102,6 +127,11 @@
title=" Inconsistent focus events when returning to a document frame">
Mozilla Bug 352220
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=580464"
title="Accessible focus incorrect after JS focus() but correct after switching apps or using menu bar">
Mozilla Bug 580464
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
@@ -112,6 +142,13 @@
<button id="button">button</button>
<iframe id="editabledoc" src="focus.html"></iframe>
<div id="alertdialog" style="display: none" tabindex="-1" role="alertdialog" aria-labelledby="title2" aria-describedby="desc2">
<div id="title2">Blah blah</div>
<div id="desc2">Woof woof woof.</div>
<button>Close</button>
</div>
<div id="eventdump"></div>
</body>
</html>

View File

@@ -41,7 +41,7 @@
* Do tests.
*/
// gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpID = "eventdump"; // debug stuff
var gQueue = null;

View File

@@ -230,16 +230,17 @@
this.invoke = function cloneAndReplaceInDOM_invoke()
{
var newElm = this.DOMNode.cloneNode(true);
newElm.removeAttribute('id');
this.eventSeq[1][1] = newElm;
this.DOMNode.parentNode.replaceChild(newElm, this.DOMNode);
this.DOMNode.parentNode.replaceChild(this.newElm, this.DOMNode);
}
this.getID = function cloneAndReplaceInDOM_getID()
{
return aNodeOrID + " clone and replace in DOM.";
}
this.newElm = this.DOMNode.cloneNode(true);
this.newElm.removeAttribute('id');
this.setTarget(kShowEvent, this.newElm);
}
/**
@@ -279,7 +280,7 @@
* Do tests.
*/
var gQueue = null;
// gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpID = "eventdump"; // debug stuff
function doTests()
{
@@ -292,13 +293,6 @@
gQueue.push(new changeStyle(id, "display", "none", kHideEvents));
gQueue.push(new changeStyle(id, "display", "inline", kShowEvents));
// Show/hide events by changing of visibility style of accessible DOM node
// from 'visible' to 'hidden', 'hidden' to 'visible'.
var id = "link2";
getAccessible(id);
gQueue.push(new changeStyle(id, "visibility", "hidden", kHideEvents));
gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
// Show/hide events by changing of display style of accessible DOM node
// from 'inline' to 'block', 'block' to 'inline'.
var id = "link3";
@@ -306,12 +300,6 @@
gQueue.push(new changeStyle(id, "display", "block", kHideAndShowEvents));
gQueue.push(new changeStyle(id, "display", "inline", kHideAndShowEvents));
// Show/hide events by changing of visibility style of accessible DOM node
// from 'collapse' to 'visible', 'visible' to 'collapse'.
var id = "link4";
gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
gQueue.push(new changeStyle(id, "visibility", "collapse", kHideEvents));
// Show/hide events by adding new accessible DOM node and removing old one.
var id = "link5";
gQueue.push(new cloneAndAppendToDOM(id));
@@ -347,16 +335,13 @@
// Show/hide events by creating new accessible DOM node and replacing
// old one.
// XXX: bug 472810
// gQueue.push(new cloneAndReplaceInDOM("link6"));
getAccessible("link6"); // ensure accessible is created
gQueue.push(new cloneAndReplaceInDOM("link6"));
// Show/hide events by changing class name on the parent node.
gQueue.push(new changeClass("container2", "link7", "", kShowEvents));
gQueue.push(new changeClass("container2", "link7", "displayNone",
kHideEvents));
gQueue.push(new changeClass("container3", "link8", "", kShowEvents));
gQueue.push(new changeClass("container3", "link8", "visibilityHidden",
kHideEvents));
gQueue.invoke(); // Will call SimpleTest.finish();
}
@@ -372,11 +357,16 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=469985"
title=" turn the test from bug 354745 into mochitest">
Mozilla Bug 469985
</a>
</a><br>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=472662"
title="no reorder event when html:link display property is changed from 'none' to 'inline'">
Mozilla Bug 472662
</a><br>
<a target="_blank"
title="Rework accessible tree update code"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
Mozilla Bug 570275
</a>
<p id="display"></p>

View File

@@ -84,22 +84,36 @@
/**
* Insert inaccessible child node containing accessibles.
*/
function insertChildSpan(aID)
function insertChildSpan(aID, aInsertAllTogether)
{
this.__proto__ = new textInsertInvoker(aID, 0, 5, "33322");
this.invoke = function insertChildSpan_invoke()
{
// <span><span>333</span><span>22</span></span>
var topSpan = document.createElement("span");
var fSpan = document.createElement("span");
fSpan.textContent = "333";
topSpan.appendChild(fSpan);
var sSpan = document.createElement("span");
sSpan.textContent = "22";
topSpan.appendChild(sSpan);
if (aInsertAllTogether) {
var topSpan = document.createElement("span");
var fSpan = document.createElement("span");
fSpan.textContent = "333";
topSpan.appendChild(fSpan);
var sSpan = document.createElement("span");
sSpan.textContent = "22";
topSpan.appendChild(sSpan);
this.DOMNode.insertBefore(topSpan, this.DOMNode.childNodes[0]);
this.DOMNode.insertBefore(topSpan, this.DOMNode.childNodes[0]);
} else {
var topSpan = document.createElement("span");
this.DOMNode.insertBefore(topSpan, this.DOMNode.childNodes[0]);
var fSpan = document.createElement("span");
fSpan.textContent = "333";
topSpan.appendChild(fSpan);
var sSpan = document.createElement("span");
sSpan.textContent = "22";
topSpan.appendChild(sSpan);
}
}
this.getID = function insertChildSpan_getID()
@@ -271,7 +285,8 @@
// Text remove event on inaccessible child HTML span removal containing
// accessible text nodes.
gQueue.push(new removeChildSpan("p"));
gQueue.push(new insertChildSpan("p"));
gQueue.push(new insertChildSpan("p"), true);
gQueue.push(new insertChildSpan("p"), false);
// Remove embedded character.
gQueue.push(new removeChildDiv("div"));
@@ -323,6 +338,11 @@
title="Cache text offsets within hypertext accessible">
Mozilla Bug 575052
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275"
title="Rework accessible tree update code">
Mozilla Bug 570275
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>

View File

@@ -15,6 +15,7 @@ const ROLE_COMBOBOX = nsIAccessibleRole.ROLE_COMBOBOX;
const ROLE_COMBOBOX_LIST = nsIAccessibleRole.ROLE_COMBOBOX_LIST;
const ROLE_COMBOBOX_OPTION = nsIAccessibleRole.ROLE_COMBOBOX_OPTION;
const ROLE_COLUMNHEADER = nsIAccessibleRole.ROLE_COLUMNHEADER;
const ROLE_DIALOG = nsIAccessibleRole.ROLE_DIALOG;
const ROLE_DOCUMENT = nsIAccessibleRole.ROLE_DOCUMENT;
const ROLE_EMBEDDED_OBJECT = nsIAccessibleRole.ROLE_EMBEDDED_OBJECT;
const ROLE_ENTRY = nsIAccessibleRole.ROLE_ENTRY;
@@ -100,3 +101,23 @@ function getRole(aAccOrElmOrID)
return role;
}
/**
* Analogy of SimpleTest.is function used to check the role.
*/
function isRole(aIdentifier, aRole, aMsg)
{
var role = getRole(aIdentifier);
if (role == - 1)
return;
if (role == aRole) {
ok(true, aMsg);
return;
}
var got = roleToString(role);
var expected = roleToString(aRole);
ok(false, aMsg + "got '" + got + "', expected '" + expected + "'");
}

View File

@@ -6,6 +6,46 @@
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// State constants
// const STATE_BUSY is defined in common.js
const STATE_CHECKED = nsIAccessibleStates.STATE_CHECKED;
const STATE_CHECKABLE = nsIAccessibleStates.STATE_CHECKABLE;
const STATE_COLLAPSED = nsIAccessibleStates.STATE_COLLAPSED;
const STATE_EXPANDED = nsIAccessibleStates.STATE_EXPANDED;
const STATE_EXTSELECTABLE = nsIAccessibleStates.STATE_EXTSELECTABLE;
const STATE_FOCUSABLE = nsIAccessibleStates.STATE_FOCUSABLE;
const STATE_FOCUSED = nsIAccessibleStates.STATE_FOCUSED;
const STATE_HASPOPUP = nsIAccessibleStates.STATE_HASPOPUP;
const STATE_INVALID = nsIAccessibleStates.STATE_INVALID;
const STATE_LINKED = nsIAccessibleStates.STATE_LINKED;
const STATE_MIXED = nsIAccessibleStates.STATE_MIXED;
const STATE_MULTISELECTABLE = nsIAccessibleStates.STATE_MULTISELECTABLE;
const STATE_OFFSCREEN = nsIAccessibleStates.STATE_OFFSCREEN;
const STATE_PRESSED = nsIAccessibleStates.STATE_PRESSED;
const STATE_READONLY = nsIAccessibleStates.STATE_READONLY;
const STATE_REQUIRED = nsIAccessibleStates.STATE_REQUIRED;
const STATE_SELECTABLE = nsIAccessibleStates.STATE_SELECTABLE;
const STATE_SELECTED = nsIAccessibleStates.STATE_SELECTED;
const STATE_TRAVERSED = nsIAccessibleStates.STATE_TRAVERSED;
const STATE_UNAVAILABLE = nsIAccessibleStates.STATE_UNAVAILABLE;
const EXT_STATE_ACTIVE = nsIAccessibleStates.EXT_STATE_ACTIVE;
const EXT_STATE_DEFUNCT = nsIAccessibleStates.EXT_STATE_DEFUNCT;
const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE;
const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE;
const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL;
const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE;
const EXT_STATE_SINGLE_LINE = nsIAccessibleStates.EXT_STATE_SINGLE_LINE;
const EXT_STATE_SUPPORTS_AUTOCOMPLETION =
nsIAccessibleStates.EXT_STATE_SUPPORTS_AUTOCOMPLETION;
const EXT_STATE_VERTICAL = nsIAccessibleStates.EXT_STATE_VERTICAL;
////////////////////////////////////////////////////////////////////////////////
// Test functions
/**
* Tests the states and extra states of the given accessible.
* Also tests for unwanted states and extra states.
@@ -149,6 +189,16 @@ function getStates(aAccOrElmOrID)
return [state.value, extraState.value];
}
/**
* Return true if the accessible has given states.
*/
function hasState(aAccOrElmOrID, aState, aExtraState)
{
var [state, exstate] = getStates(aAccOrElmOrID);
return (aState ? state & aState : true) &&
(aExtraState ? exstate & aExtraState : true);
}
////////////////////////////////////////////////////////////////////////////////
// Private implementation details

View File

@@ -21,11 +21,11 @@
<script type="application/javascript">
function doTest()
{
// strong roles
testStates("link1", STATE_LINKED);
testStates("link2", STATE_LINKED);
testStates("link3", STATE_LINKED);
testStates("link4", STATE_LINKED);
testStates("link5", 0, 0, STATE_LINKED);
SimpleTest.finish();
}
@@ -52,6 +52,7 @@
<a id="link2" onclick="">link</a>
<a id="link3" onmousedown="">link</a>
<a id="link4" onmouseup="">link</a>
<a id="link5">not link</a>
</body>
</html>

View File

@@ -68,7 +68,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
var frame = document.getElementById("frame");
frame.contentDocument.designMode = "on";
testEditable(frame.contentDocument);
SimpleTest.finish();
}

View File

@@ -12,6 +12,8 @@
src="common.js"></script>
<script type="application/javascript"
src="role.js"></script>
<script type="application/javascript"
src="states.js"></script>
<script type="application/javascript"
src="nsIAccessible_selects.js"></script>

View File

@@ -62,7 +62,6 @@ _TEST_FILES =\
test_iframe.html \
test_img.html \
test_list.html \
test_list_invalidate.html \
test_media.html \
test_menu.xul \
test_select.html \

View File

@@ -53,9 +53,6 @@
{
role: ROLE_ENTRY,
children: [
{
role: ROLE_TEXT_LEAF // HTML 5 placeholder attribute value
},
{
role: ROLE_TEXT_LEAF // Text node for the node's value
}
@@ -126,9 +123,6 @@
{
role: ROLE_ENTRY,
children: [
{
role: ROLE_TEXT_LEAF // HTML 5 placeholder attribute value
},
{
role: ROLE_TEXT_LEAF // Text node for the node's value
}

View File

@@ -30,26 +30,22 @@
function focusAnchor(aID)
{
this.DOMNode = getNode(aID);
this.link = getAccessible(this.DOMNode);
this.linkChild = this.link.firstChild;
this.linkChildNode = getAccessible(this.linkChild, [nsIAccessNode]).DOMNode;
this.linkNode = getNode(aID);
this.link = getAccessible(this.linkNode);
this.eventSeq = [
// new invokerChecker(EVENT_HIDE, this.linkChild),
// new invokerChecker(EVENT_SHOW, getAccessible, this.linkChildNode),
new invokerChecker(EVENT_FOCUS, this.link)
];
this.unexpectedEventSeq = [
new invokerChecker(EVENT_HIDE, this.link),
new invokerChecker(EVENT_SHOW, getAccessible, this.DOMNode)
new invokerChecker(EVENT_FOCUS, getAccessible, this.linkNode)
];
this.invoke = function focusAnchor_invoke()
{
todo(false, "enable event hide/show events");
getNode(aID).focus();
this.linkNode.focus();
}
this.check = function focusAnchor_check(aEvent)
{
isnot(this.link, aEvent.accessible,
"Focus should be fired against new link accessible!");
}
this.getID = function focusAnchor_getID()
@@ -58,34 +54,27 @@
}
}
function tabAnchor(aID, aPrevID)
function tabAnchor(aID)
{
this.DOMNode = getNode(aID);
this.link = getAccessible(this.DOMNode);
this.linkChild = this.link.firstChild;
this.linkChildNode = getAccessible(this.linkChild, [nsIAccessNode]).DOMNode;
this.prevLink = getAccessible(aPrevID);
this.prevLinkNode = getAccessible(this.prevLink, [nsIAccessNode]).DOMNode;
this.linkNode = getNode(aID);
this.link = getAccessible(this.linkNode);
this.eventSeq = [
new invokerChecker(EVENT_HIDE, this.prevLink),
new invokerChecker(EVENT_SHOW, getAccessible, this.prevLinkNode),
new invokerChecker(EVENT_HIDE, this.linkChild),
new invokerChecker(EVENT_SHOW, getAccessible, this.linkChildNode),
new invokerChecker(EVENT_FOCUS, this.link)
new invokerChecker(EVENT_FOCUS, getAccessible, this.linkNode)
];
this.unexpectedEventSeq = [
new invokerChecker(EVENT_HIDE, this.link),
new invokerChecker(EVENT_SHOW, getAccessible, this.DOMNode)
];
this.invoke = function focusAnchor_invoke()
this.invoke = function tabAnchor_invoke()
{
synthesizeKey("VK_TAB", { shiftKey: false });
}
this.getID = function focusAnchor_getID()
this.check = function tabAnchor_check(aEvent)
{
isnot(this.link, aEvent.accessible,
"Focus should be fired against new link accessible!");
}
this.getID = function tabAnchor_getID()
{
return "focus a:focus{overflow:scroll} #2";
}
@@ -95,20 +84,20 @@
// Do tests
var gQueue = null;
// gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true;
function doTests()
{
gQueue = new eventQueue();
// CSS 'overflow: scroll' property setting and unsetting causes accessible
// recreation (and fire show/hide events) if the accessible is not
// focused. If it's focused its children are recreated. For example,
// focusing the HTML:a with ':focus {overflow: scroll; }' CSS style
// shouldn't cause of HTML:a accessible recreation. The same time blur
// makes its accessible to be recreated.
gQueue.push(new focusAnchor("a"))
gQueue.push(new tabAnchor("a2", "a"));
// recreation (and fire show/hide events). For example, the focus and
// blur of HTML:a with ':focus {overflow: scroll; }' CSS style causes its
// accessible recreation. The focus event should be fired on new
// accessible.
gQueue.push(new focusAnchor("a"));
gQueue.push(new tabAnchor("a2"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
@@ -125,6 +114,11 @@
title="mochitest for bug 413777: focus the a:focus {overflow: scroll;} shouldn't recreate HTML a accessible">
Mozilla Bug 591163
</a><br>
<a target="_blank"
title="Rework accessible tree update code"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
Mozilla Bug 570275
</a><br>
<p id="display"></p>
<div id="content" style="display: none"></div>

View File

@@ -40,31 +40,57 @@
function doTest()
{
var bulletText = String.fromCharCode(0x2022) + " ";
const discBulletText = String.fromCharCode(0x2022) + " ";
const circleBulletText = String.fromCharCode(0x25e6) + " ";
const squareBulletText = String.fromCharCode(0x25aa) + " ";
// list1
var accTree = {
var discAccTree = {
role: ROLE_LIST,
children: [
new listItemTree(bulletText, "Oranges"),
new listItemTree(bulletText, "Apples"),
new listItemTree(bulletText, "Bananas")
new listItemTree(discBulletText, "Oranges"),
new listItemTree(discBulletText, "Apples"),
new listItemTree(discBulletText, "Bananas")
]
};
testAccessibleTree("list1", accTree);
testAccessibleTree("list1", discAccTree);
// list2
accTree = {
var circleAccTree = {
role: ROLE_LIST,
children: [
new listItemTree(circleBulletText, "Oranges"),
new listItemTree(circleBulletText, "Apples"),
new listItemTree(circleBulletText, "Bananas")
]
};
testAccessibleTree("list2", circleAccTree);
// list3
var squareAccTree = {
role: ROLE_LIST,
children: [
new listItemTree(squareBulletText, "Oranges"),
new listItemTree(squareBulletText, "Apples"),
new listItemTree(squareBulletText, "Bananas")
]
};
testAccessibleTree("list3", squareAccTree);
// list4
var nestedAccTree = {
role: ROLE_LIST,
children: [
new listItemTree("1. ", "Oranges"),
new listItemTree("2. ", "Apples"),
new listItemTree("3. ", "Bananas", accTree)
new listItemTree("3. ", "Bananas", circleAccTree)
]
};
testAccessibleTree("list2", accTree);
testAccessibleTree("list4", nestedAccTree);
SimpleTest.finish();
}
@@ -80,18 +106,35 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045">
Mozilla Bug 342045
</a>
<a target="_blank"
title="Bullets of nested not ordered lists have one and the same character."
href="https://bugzilla.mozilla.org/show_bug.cgi?id=604587">
Mozilla Bug 604587
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<ul id="list1">
<li id="li1">Oranges</li>
<li id="li2">Apples</li>
<li id="li3">Bananas</li>
<li id="l1_li1">Oranges</li>
<li id="l1_li2">Apples</li>
<li id="l1_li3">Bananas</li>
</ul>
<ol id="list2">
<ul id="list2" style="list-style-type: circle">
<li id="l2_li1">Oranges</li>
<li id="l2_li2">Apples</li>
<li id="l2_li3">Bananas</li>
</ul>
<ul id="list3" style="list-style-type: square">
<li id="l3_li1">Oranges</li>
<li id="l3_li2">Apples</li>
<li id="l3_li3">Bananas</li>
</ul>
<ol id="list4">
<li id="li4">Oranges</li>
<li id="li5">Apples</li>
<li id="li6">Bananas<ul>

View File

@@ -70,6 +70,9 @@
var tabsAccTree = {
role: ROLE_PAGETABLIST,
children: [
{
role: ROLE_PUSHBUTTON // tab scroll up button
},
{
role: ROLE_PAGETAB,
children: [
@@ -88,6 +91,9 @@
},
{
role: ROLE_PUSHBUTTON
},
{
role: ROLE_PUSHBUTTON // tab scroll down button
}
]
};

View File

@@ -0,0 +1,56 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Alexander Surkov <surkov.alexander@gmail.com> (original author)
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = accessible/treeupdate
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES =\
test_list_editabledoc.html \
test_list.html \
test_recreation.html \
test_tableinsubtree.html \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)

View File

@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html>
<head>
<title>Test HTML li and listitem bullet accessible insertion into editable document</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript">
////////////////////////////////////////////////////////////////////////////
// Invokers
function addLi(aID)
{
this.listNode = getNode(aID);
this.liNode = document.createElement("li");
this.liNode.textContent = "item";
this.eventSeq = [
new invokerChecker(EVENT_SHOW, getAccessible, this.liNode),
new invokerChecker(EVENT_REORDER, this.listNode)
];
this.invoke = function addLi_invoke()
{
this.listNode.appendChild(this.liNode);
}
this.finalCheck = function addLi_finalCheck()
{
var tree = {
role: ROLE_LIST,
children: [
{
role: ROLE_LISTITEM,
children: [
{
role: ROLE_STATICTEXT,
name: "1. ",
children: []
},
{
role: ROLE_TEXT_LEAF,
children: []
}
]
}
]
};
testAccessibleTree(aID, tree);
}
this.getID = function addLi_getID()
{
return "add li";
}
};
////////////////////////////////////////////////////////////////////////////
// Test
//gA11yEventDumpID = "eventdump"; // debug stuff
var gQueue = null;
function doTest()
{
gQueue = new eventQueue();
gQueue.push(new addLi("list"));
gQueue.invoke(); // SimpleTest.finish() will be called in the end
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body contentEditable="true">
<a target="_blank"
title="Wrong list bullet text of accessible for the first numbered HTML:li in CKEditor"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=557795">Mozilla Bug 557795</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<ol id="list">
</ol>
<div id="eventdump"></div>
</body>
</html>

View File

@@ -0,0 +1,166 @@
<!DOCTYPE html>
<html>
<head>
<title>Test accessible recreation</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript">
////////////////////////////////////////////////////////////////////////////
// Invokers
function recreateAccessible(aID, aWontBeAccessible)
{
this.node = getNode(aID);
this.accessible =
isAccessible(this.node) ? getAccessible(this.node) : null;
this.eventSeq = [ ];
if (this.accessible)
this.eventSeq.push(new invokerChecker(EVENT_HIDE,
this.accessible));
if (!aWontBeAccessible)
this.eventSeq.push(new invokerChecker(EVENT_SHOW, getAccessible,
this.node));
this.eventSeq.push(new invokerChecker(EVENT_REORDER,
getContainerAccessible(this.node)));
if (this.accessible) {
this.unexpectedEventSeq = [
new invokerChecker(EVENT_SHOW, this.accessible)
];
}
}
function changeAttr(aID, aAttr, aValue)
{
this.__proto__ = new recreateAccessible(aID);
this.invoke = function changeAttr_invoke()
{
this.node.setAttribute(aAttr, aValue);
}
this.getID = function changeAttr_getID()
{
return "change " + aAttr + "attribute for " + aID;
}
}
function removeAttr(aID, aAttr)
{
this.__proto__ = new recreateAccessible(aID, true);
this.invoke = function remvoeAttr_invoke()
{
this.node.removeAttribute(aAttr);
}
this.getID = function remvoeAttr_getID()
{
return "remove " + aAttr + "attribute for " + aID;
}
}
function changeRole(aID, aHasAccessible)
{
this.__proto__ = new changeAttr(aID, "role", "button");
}
function removeRole(aID)
{
this.__proto__ = new removeAttr(aID, "role");
}
function changeOnclick(aID)
{
this.__proto__ = new changeAttr(aID, "onclick", "alert(3);");
}
function changeHref(aID)
{
this.__proto__ = new changeAttr(aID, "href", "www");
}
function changeMultiselectable(aID)
{
this.__proto__ = new changeAttr(aID, "aria-multiselectable", "true");
}
////////////////////////////////////////////////////////////////////////////
// Test
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true;
var gQueue = null;
function doTest()
{
gQueue = new eventQueue();
// make the accessible an inaccessible
gQueue.push(new changeRole("span"));
// make the inaccessible an accessible
gQueue.push(new removeRole("span"));
// recreate an accessible by role change
gQueue.push(new changeRole("div1"));
// recreate an accessible by onclick change
gQueue.push(new changeOnclick("div2"));
// recreate an accessible by href change
gQueue.push(new changeHref("anchor"));
// recreate an accessible by aria-multiselectable change
gQueue.push(new changeMultiselectable("div3"));
gQueue.invoke(); // SimpleTest.finish() will be called in the end
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
title="Rework accessible tree update code"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
Mozilla Bug 570275
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<span id="span">span</span>
<div id="div1">div</div>
<div id="div2">div</div>
<a id="anchor">anchor</a>
<div id="div3" role="listbox">list</div>
<div id="eventdump"></div>
</body>
</html>

View File

@@ -0,0 +1,120 @@
<!DOCTYPE html>
<html>
<head>
<title>Table creation in ARIA dialog test</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript">
////////////////////////////////////////////////////////////////////////////
// Invokers
function showARIADialog(aID)
{
this.node = getNode(aID);
this.eventSeq = [
new invokerChecker(EVENT_SHOW, this.node)
];
this.invoke = function showARIADialog_invoke()
{
this.node.style.display = "block";
getNode("input").value = "hello";
getNode("cell").textContent = "cell1";
getNode("input").focus();
}
this.finalCheck = function showARIADialog_finalCheck()
{
var tree = {
role: ROLE_DIALOG,
children: [
{
role: ROLE_TABLE,
children: [
{
role: ROLE_ROW,
children: [
{
role: ROLE_CELL,
children: [ { role: ROLE_TEXT_LEAF } ]
},
{
role: ROLE_CELL,
children: [ { role: ROLE_ENTRY } ]
}
]
}
]
}
]
};
testAccessibleTree(aID, tree);
}
this.getID = function showARIADialog_getID()
{
return "show ARIA dialog";
}
}
////////////////////////////////////////////////////////////////////////////
// Test
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true;
var gQueue = null;
function doTest()
{
gQueue = new eventQueue();
// make the accessible an inaccessible
gQueue.push(new showARIADialog("dialog"));
gQueue.invoke(); // SimpleTest.finish() will be called in the end
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
title="Rework accessible tree update code"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
Mozilla Bug 570275
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<div id="dialog" role="dialog" style="display: none;">
<table>
<tr><td id="cell"></td><td><input id="input"></td>
</table>
</div>
<div id="eventdump"></div>
</body>
</html>

View File

@@ -802,7 +802,11 @@ pref("browser.sessionstore.max_windows_undo", 3);
// number of crashes that can occur before the about:sessionrestore page is displayed
// (this pref has no effect if more than 6 hours have passed since the last crash)
pref("browser.sessionstore.max_resumed_crashes", 1);
// number of tabs to restore concurrently
// The number of tabs that can restore concurrently:
// < 0 = All tabs can restore at the same time
// 0 = Only the selected tab in each window will be restored
// Other tabs won't be restored until they are selected
// N = The number of tabs to restore at the same time
pref("browser.sessionstore.max_concurrent_tabs", 3);
// allow META refresh by default
@@ -944,7 +948,6 @@ pref("dom.ipc.plugins.enabled.i386.flash player.plugin", true);
pref("dom.ipc.plugins.enabled.i386.javaplugin2_npapi.plugin", true);
// x86_64 ipc preferences
pref("dom.ipc.plugins.enabled.x86_64", true);
pref("dom.ipc.plugins.enabled.x86_64.test.plugin", false);
#elifdef MOZ_IPC
pref("dom.ipc.plugins.enabled", true);
#else
@@ -961,7 +964,7 @@ pref("browser.taskbar.lists.frequent.enabled", true);
pref("browser.taskbar.lists.recent.enabled", false);
pref("browser.taskbar.lists.maxListItemCount", 7);
pref("browser.taskbar.lists.tasks.enabled", true);
pref("browser.taskbar.lists.refreshInSeconds", 30);
pref("browser.taskbar.lists.refreshInSeconds", 120);
#endif
#endif

View File

@@ -171,7 +171,7 @@ function showSnippets()
let relocatedScript = document.createElement("script");
relocatedScript.type = "text/javascript;version=1.8";
relocatedScript.text = elt.text;
snippetsElt.replaceChild(relocatedScript, elt);
elt.parentNode.replaceChild(relocatedScript, elt);
});
snippetsElt.hidden = false;
} else {

View File

@@ -96,13 +96,33 @@ var StarUI = {
if (aEvent.originalTarget == this.panel) {
if (!this._element("editBookmarkPanelContent").hidden)
this.quitEditMode();
this._restoreCommandsState();
this._itemId = -1;
this._uri = null;
if (this._batching) {
PlacesUIUtils.ptm.endBatch();
this._batching = false;
}
switch (this._actionOnHide) {
case "cancel": {
PlacesUIUtils.ptm.undoTransaction();
break;
}
case "remove": {
// Remove all bookmarks for the bookmark's url, this also removes
// the tags for the url.
PlacesUIUtils.ptm.beginBatch();
let itemIds = PlacesUtils.getBookmarksForURI(this._uriForRemoval);
for (let i = 0; i < itemIds.length; i++) {
let txn = PlacesUIUtils.ptm.removeItem(itemIds[i]);
PlacesUIUtils.ptm.doTransaction(txn);
}
PlacesUIUtils.ptm.endBatch();
break;
}
}
this._actionOnHide = "";
}
break;
case "keypress":
@@ -185,12 +205,9 @@ var StarUI = {
gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle");
// No description; show the Done, Cancel;
// hide the Edit, Undo buttons
this._element("editBookmarkPanelDescription").textContent = "";
this._element("editBookmarkPanelBottomButtons").hidden = false;
this._element("editBookmarkPanelContent").hidden = false;
this._element("editBookmarkPanelEditButton").hidden = true;
this._element("editBookmarkPanelUndoRemoveButton").hidden = true;
// The remove button is shown only if we're not already batching, i.e.
// if the cancel button/ESC does not remove the bookmark.
@@ -237,42 +254,6 @@ var StarUI = {
}
},
showPageBookmarkedNotification:
function PCH_showPageBookmarkedNotification(aItemId, aAnchorElement, aPosition) {
this._blockCommands(); // un-done in the popuphiding handler
var brandBundle = this._element("bundle_brand");
var brandShortName = brandBundle.getString("brandShortName");
// "Page Bookmarked" title
this._element("editBookmarkPanelTitle").value =
gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle");
// description
this._element("editBookmarkPanelDescription").textContent =
gNavigatorBundle.getFormattedString("editBookmarkPanel.pageBookmarkedDescription",
[brandShortName]);
// show the "Edit.." button and the Remove Bookmark button, hide the
// undo-remove-bookmark button.
this._element("editBookmarkPanelEditButton").hidden = false;
this._element("editBookmarkPanelRemoveButton").hidden = false;
this._element("editBookmarkPanelUndoRemoveButton").hidden = true;
// unset the unstarred state, if set
this._element("editBookmarkPanelStarIcon").removeAttribute("unstarred");
this._itemId = aItemId !== undefined ? aItemId : this._itemId;
if (this.panel.state == "closed") {
// Consume dismiss clicks, see bug 400924
this.panel.popupBoxObject
.setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_CONSUME);
this.panel.openPopup(aAnchorElement, aPosition, -1, -1);
}
else
this.panel.focus();
},
quitEditMode: function SU_quitEditMode() {
this._element("editBookmarkPanelContent").hidden = true;
this._element("editBookmarkPanelBottomButtons").hidden = true;
@@ -284,67 +265,14 @@ var StarUI = {
},
cancelButtonOnCommand: function SU_cancelButtonOnCommand() {
// The order here is important! We have to hide the panel first, otherwise
// changes done as part of Undo may change the panel contents and by
// that force it to commit more transactions
this._actionOnHide = "cancel";
this.panel.hidePopup();
this.endBatch();
PlacesUIUtils.ptm.undoTransaction();
},
removeBookmarkButtonCommand: function SU_removeBookmarkButtonCommand() {
#ifdef ADVANCED_STARRING_UI
// In minimal mode ("page bookmarked" notification), the bookmark
// is removed and the panel is hidden immediately. In full edit mode,
// a "Bookmark Removed" notification along with an Undo button is
// shown
if (this._batching) {
PlacesUIUtils.ptm.endBatch();
PlacesUIUtils.ptm.beginBatch(); // allow undo from within the notification
// "Bookmark Removed" title (the description field is already empty in
// this mode)
this._element("editBookmarkPanelTitle").value =
gNavigatorBundle.getString("editBookmarkPanel.bookmarkedRemovedTitle");
// hide the edit panel
this.quitEditMode();
// Hide the remove bookmark button, show the undo-remove-bookmark
// button.
this._element("editBookmarkPanelUndoRemoveButton").hidden = false;
this._element("editBookmarkPanelRemoveButton").hidden = true;
this._element("editBookmarkPanelStarIcon").setAttribute("unstarred", "true");
this.panel.focus();
}
#endif
// cache its uri so we can get the new itemId in the case of undo
this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
// remove all bookmarks for the bookmark's url, this also removes
// the tags for the url
var itemIds = PlacesUtils.getBookmarksForURI(this._uri);
for (var i=0; i < itemIds.length; i++) {
var txn = PlacesUIUtils.ptm.removeItem(itemIds[i]);
PlacesUIUtils.ptm.doTransaction(txn);
}
#ifdef ADVANCED_STARRING_UI
// hidePopup resets our itemId, thus we call it only after removing
// the bookmark
if (!this._batching)
#endif
this.panel.hidePopup();
},
undoRemoveBookmarkCommand: function SU_undoRemoveBookmarkCommand() {
// restore the bookmark by undoing the last transaction and go back
// to the edit state
this.endBatch();
PlacesUIUtils.ptm.undoTransaction();
this._itemId = PlacesUtils.getMostRecentBookmarkForURI(this._uri);
this.showEditBookmarkPopup();
this._uriForRemoval = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
this._actionOnHide = "remove";
this.panel.hidePopup();
},
beginBatch: function SU_beginBatch() {
@@ -352,13 +280,6 @@ var StarUI = {
PlacesUIUtils.ptm.beginBatch();
this._batching = true;
}
},
endBatch: function SU_endBatch() {
if (this._batching) {
PlacesUIUtils.ptm.endBatch();
this._batching = false;
}
}
}
@@ -429,10 +350,6 @@ var PlacesCommandHook = {
var position = (getComputedStyle(gNavToolbox, "").direction == "rtl") ? 'after_start' : 'after_end';
if (aShowEditUI)
StarUI.showEditBookmarkPopup(itemId, starIcon, position);
#ifdef ADVANCED_STARRING_UI
else
StarUI.showPageBookmarkedNotification(itemId, starIcon, position);
#endif
return;
}
}
@@ -550,15 +467,6 @@ var PlacesCommandHook = {
organizer.PlacesOrganizer.selectLeftPaneQuery(aLeftPaneRoot);
organizer.focus();
}
},
deleteButtonOnCommand: function PCH_deleteButtonCommand() {
PlacesUtils.bookmarks.removeItem(gEditItemOverlay.itemId);
// remove all tags for the associated url
PlacesUtils.tagging.untagURI(gEditItemOverlay._uri, null);
this.panel.hidePopup();
}
};

View File

@@ -106,6 +106,14 @@ toolbar[mode="icons"] > #reload-button[displaystop] {
visibility: collapse;
}
#feed-button > .toolbarbutton-menu-dropmarker {
display: none;
}
#feed-menu > .feed-menuitem:-moz-locale-dir(rtl) {
direction: rtl;
}
#main-window:-moz-lwtheme {
background-repeat: no-repeat;
background-position: top right;
@@ -254,9 +262,7 @@ richlistitem[type~="action"]:-moz-locale-dir(rtl) > .ac-url-box {
/* ::::: Unified Back-/Forward Button ::::: */
#back-button > .toolbarbutton-menu-dropmarker,
#forward-button > .toolbarbutton-menu-dropmarker,
#back-forward-dropmarker > .toolbarbutton-icon,
#back-forward-dropmarker > .toolbarbutton-text {
#forward-button > .toolbarbutton-menu-dropmarker {
display: none;
}
.unified-nav-current {

View File

@@ -253,7 +253,6 @@ function UpdateBackForwardCommands(aWebNavigation) {
}
}
#ifdef XP_MACOSX
/**
* Click-and-Hold implementation for the Back and Forward buttons
* XXXmano: should this live in toolbarbutton.xml?
@@ -310,8 +309,8 @@ function SetClickAndHoldHandlers() {
// the forward buttons.
var unifiedButton = document.getElementById("unified-back-forward-button");
if (unifiedButton && !unifiedButton._clickHandlersAttached) {
var popup = document.getElementById("back-forward-dropmarker")
.firstChild.cloneNode(true);
var popup = document.getElementById("backForwardMenu").cloneNode(true);
popup.removeAttribute("id");
var backButton = document.getElementById("back-button");
backButton.setAttribute("type", "menu");
backButton.appendChild(popup);
@@ -324,7 +323,6 @@ function SetClickAndHoldHandlers() {
unifiedButton._clickHandlersAttached = true;
}
}
#endif
const gSessionHistoryObserver = {
observe: function(subject, topic, data)
@@ -1462,12 +1460,10 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
document.getElementById("textfieldDirection-swap").hidden = false;
}
#ifdef XP_MACOSX
// Setup click-and-hold gestures access to the session history
// menus if global click-and-hold isn't turned on
if (!getBoolPref("ui.click_hold_context_menus", false))
SetClickAndHoldHandlers();
#endif
// Initialize the full zoom setting.
// We do this before the session restore service gets initialized so we can
@@ -1536,11 +1532,6 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
}
}, 10000);
// Delayed initialization of PlacesDBUtils.
// This component checks for database coherence once per day, on
// an idle timer, taking corrective actions where needed.
setTimeout(function() PlacesUtils.startPlacesDBUtils(), 15000);
#ifndef XP_MACOSX
updateEditUIVisibility();
let placesContext = document.getElementById("placesContext");
@@ -3492,12 +3483,6 @@ function BrowserToolboxCustomizeDone(aToolboxChanged) {
gIdentityHandler._cacheElements();
window.XULBrowserWindow.init();
var backForwardDropmarker = document.getElementById("back-forward-dropmarker");
if (backForwardDropmarker)
backForwardDropmarker.disabled =
document.getElementById('Browser:Back').hasAttribute('disabled') &&
document.getElementById('Browser:Forward').hasAttribute('disabled');
#ifndef XP_MACOSX
updateEditUIVisibility();
#endif
@@ -3524,15 +3509,11 @@ function BrowserToolboxCustomizeDone(aToolboxChanged) {
var cmd = document.getElementById("cmd_CustomizeToolbars");
cmd.removeAttribute("disabled");
#ifdef XP_MACOSX
// make sure to re-enable click-and-hold
if (!getBoolPref("ui.click_hold_context_menus", false))
SetClickAndHoldHandlers();
#endif
// XXX Shouldn't have to do this, but I do
if (!gCustomizeSheet)
window.focus();
window.content.focus();
}
function BrowserToolboxCustomizeChange() {
@@ -3653,7 +3634,7 @@ var FullScreen = {
// The user may quit fullscreen during an animation
clearInterval(this._animationInterval);
clearTimeout(this._animationTimeout);
gNavToolbox.style.marginTop = "0px";
gNavToolbox.style.marginTop = "";
if (this._isChromeCollapsed)
this.mouseoverToggle(true);
this._isAnimating = false;
@@ -3786,7 +3767,7 @@ var FullScreen = {
if (animateFrameAmount >= gNavToolbox.boxObject.height) {
// We've animated enough
clearInterval(FullScreen._animationInterval);
gNavToolbox.style.marginTop = "0px";
gNavToolbox.style.marginTop = "";
FullScreen._isAnimating = false;
FullScreen._shouldAnimate = false; // Just to make sure
FullScreen.mouseoverToggle(false);
@@ -4487,7 +4468,7 @@ var TabsProgressListener = {
// document URI is not yet the about:-uri of the error page.
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
/^about:/.test(aBrowser.contentWindow.document.documentURI)) {
/^about:/.test(aWebProgress.DOMWindow.document.documentURI)) {
aBrowser.addEventListener("click", BrowserOnClick, false);
aBrowser.addEventListener("pagehide", function () {
aBrowser.removeEventListener("click", BrowserOnClick, false);
@@ -4639,10 +4620,8 @@ function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
if (popup != aEvent.currentTarget)
return;
var i;
// Empty the menu
for (i = popup.childNodes.length-1; i >= 0; --i) {
for (var i = popup.childNodes.length-1; i >= 0; --i) {
var deadItem = popup.childNodes[i];
if (deadItem.hasAttribute("toolbarId"))
popup.removeChild(deadItem);
@@ -4650,9 +4629,9 @@ function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
var firstMenuItem = aInsertPoint || popup.firstChild;
let toolbarNodes = [document.getElementById("addon-bar")];
for (i = 0; i < gNavToolbox.childNodes.length; ++i)
toolbarNodes.push(gNavToolbox.childNodes[i]);
let toolbarNodes = Array.slice(gNavToolbox.childNodes);
toolbarNodes.push(document.getElementById("addon-bar"));
toolbarNodes.forEach(function(toolbar) {
var toolbarName = toolbar.getAttribute("toolbarname");
if (toolbarName) {
@@ -5754,7 +5733,7 @@ var OfflineApps = {
var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject);
updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject, window);
},
/////////////////////////////////////////////////////////////////////////////
@@ -6583,7 +6562,22 @@ function convertFromUnicode(charset, str)
*/
var FeedHandler = {
/**
* Called when the user clicks on the Subscribe to This Page... menu item.
* The click handler for the Feed icon in the toolbar. Opens the
* subscription page if user is not given a choice of feeds.
* (Otherwise the list of available feeds will be presented to the
* user in a popup menu.)
*/
onFeedButtonClick: function(event) {
event.stopPropagation();
if (event.target.hasAttribute("feed") &&
event.eventPhase == Event.AT_TARGET &&
(event.button == 0 || event.button == 1)) {
this.subscribeToFeed(null, event);
}
},
/** Called when the user clicks on the Subscribe to This Page... menu item.
* Builds a menu of unique feeds associated with the page, and if there
* is only one, shows the feed inline in the browser window.
* @param menuPopup
@@ -6608,6 +6602,13 @@ var FeedHandler = {
while (menuPopup.firstChild)
menuPopup.removeChild(menuPopup.firstChild);
if (feeds.length == 1) {
var feedButton = document.getElementById("feed-button");
if (feedButton)
feedButton.setAttribute("feed", feeds[0].href);
return false;
}
// Build the menu showing the available feed choices for viewing.
for (var i = 0; i < feeds.length; ++i) {
var feedInfo = feeds[i];
@@ -6679,16 +6680,30 @@ var FeedHandler = {
* a page is loaded or the user switches tabs to a page that has feeds.
*/
updateFeeds: function() {
var feedButton = document.getElementById("feed-button");
var feeds = gBrowser.selectedBrowser.feeds;
if (!feeds || feeds.length == 0) {
if (feedButton) {
feedButton.disabled = true;
feedButton.removeAttribute("feed");
}
this._feedMenuitem.setAttribute("disabled", "true");
this._feedMenupopup.setAttribute("hidden", "true");
this._feedMenuitem.removeAttribute("hidden");
} else {
if (feedButton)
feedButton.disabled = false;
if (feeds.length > 1) {
this._feedMenuitem.setAttribute("hidden", "true");
this._feedMenupopup.removeAttribute("hidden");
if (feedButton)
feedButton.removeAttribute("feed");
} else {
if (feedButton)
feedButton.setAttribute("feed", feeds[0].href);
this._feedMenuitem.setAttribute("feed", feeds[0].href);
this._feedMenuitem.removeAttribute("disabled");
this._feedMenuitem.removeAttribute("hidden");
@@ -6709,6 +6724,12 @@ var FeedHandler = {
browserForLink.feeds = [];
browserForLink.feeds.push({ href: link.href, title: link.title });
if (browserForLink == gBrowser.selectedBrowser) {
var feedButton = document.getElementById("feed-button");
if (feedButton)
feedButton.collapsed = false;
}
}
};

View File

@@ -152,9 +152,11 @@
oncommand="gBrowser.removeTab(TabContextMenu.contextTab, { animate: true });"/>
</menupopup>
<!-- bug 415444/582485: event.stopPropagation is here for the cloned version
of this menupopup -->
<menupopup id="backForwardMenu"
onpopupshowing="return FillHistoryMenu(event.target);"
oncommand="gotoHistoryIndex(event);"
oncommand="gotoHistoryIndex(event); event.stopPropagation();"
onclick="checkForMiddleClick(this, event);"/>
<tooltip id="aHTMLTooltip" onpopupshowing="return FillInHTMLTooltip(document.tooltipNode);"/>
@@ -181,21 +183,10 @@
<label id="editBookmarkPanelTitle"/>
<description id="editBookmarkPanelDescription"/>
<hbox>
<button id="editBookmarkPanelUndoRemoveButton"
class="editBookmarkPanelHeaderButton"
hidden="true"
oncommand="StarUI.undoRemoveBookmarkCommand();"
label="&editBookmark.undo.label;"
accesskey="&editBookmark.undo.accessKey;"/>
<button id="editBookmarkPanelRemoveButton"
class="editBookmarkPanelHeaderButton"
oncommand="StarUI.removeBookmarkButtonCommand();"
accesskey="&editBookmark.removeBookmark.accessKey;"/>
<button id="editBookmarkPanelEditButton"
class="editBookmarkPanelHeaderButton"
oncommand="StarUI.editButtonCommand();"
label="&editBookmark.edit.label;"
accesskey="&editBookmark.edit.accessKey;"/>
</hbox>
</vbox>
</row>
@@ -817,9 +808,9 @@
</hbox>
<spacer id="titlebar-spacer" flex="1"/>
<hbox id="titlebar-buttonbox">
<toolbarbutton id="titlebar-min" oncommand="window.minimize();"/>
<toolbarbutton id="titlebar-max" oncommand="onTitlebarMaxClick();"/>
<toolbarbutton id="titlebar-close" command="cmd_closeWindow"/>
<toolbarbutton class="titlebar-button" id="titlebar-min" oncommand="window.minimize();"/>
<toolbarbutton class="titlebar-button" id="titlebar-max" oncommand="onTitlebarMaxClick();"/>
<toolbarbutton class="titlebar-button" id="titlebar-close" command="cmd_closeWindow"/>
</hbox>
</hbox>
</vbox>
@@ -882,21 +873,6 @@
command="Browser:ForwardOrForwardDuplicate"
onclick="checkForMiddleClick(this, event);"
tooltiptext="&forwardButton.tooltip;"/>
<toolbarbutton id="back-forward-dropmarker" type="menu"
disabled="true" tooltiptext="&backForwardMenu.tooltip;"
onbroadcast="if (this.disabled) this.disabled =
document.getElementById('Browser:Back').hasAttribute('disabled') &amp;&amp;
document.getElementById('Browser:Forward').hasAttribute('disabled');">
<!-- bug 415444: event.stopPropagation is here for the cloned version of
this menupopup -->
<menupopup context=""
position="after_start"
onpopupshowing="return FillHistoryMenu(event.target);"
oncommand="gotoHistoryIndex(event); event.stopPropagation();"
onclick="checkForMiddleClick(this, event);"/>
<observes element="Browser:Back" attribute="disabled"/>
<observes element="Browser:Forward" attribute="disabled"/>
</toolbarbutton>
</toolbaritem>
<toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
@@ -1281,6 +1257,19 @@
label="&syncToolbarButton.label;"
oncommand="gSyncUI.handleToolbarButton()"/>
#endif
<toolbarbutton id="feed-button"
type="menu"
class="toolbarbutton-1 chromeclass-toolbar-additional"
disabled="true"
label="&feedButton.label;"
tooltiptext="&feedButton.tooltip;"
onclick="return FeedHandler.onFeedButtonClick(event);">
<menupopup position="after_end"
id="feed-menu"
onpopupshowing="return FeedHandler.buildFeedList(this);"
oncommand="return FeedHandler.subscribeToFeed(null, event);"
onclick="checkForMiddleClick(this, event);"/>
</toolbarbutton>
</toolbarpalette>
</toolbox>

View File

@@ -125,6 +125,10 @@ function onUnloadPermission()
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.removeObserver(permissionObserver, "perm-changed");
var dbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
.getService(nsIIndexedDatabaseManager);
dbManager.cancelGetUsageForURI(gPermURI, onIndexedDBUsageCallback);
}
function initRow(aPartId)
@@ -195,27 +199,16 @@ function setRadioState(aPartId, aValue)
function initIndexedDBRow()
{
var dbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
.getService(nsIIndexedDatabaseManager);
dbManager.getUsageForURI(gPermURI, onIndexedDBUsageCallback);
var status = document.getElementById("indexedDBStatus");
var button = document.getElementById("indexedDBClear");
var usage = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
.getService(nsIIndexedDatabaseManager)
.getUsageForURI(gPermURI);
if (usage) {
if (!("DownloadUtils" in window)) {
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
}
status.value =
gBundle.getFormattedString("indexedDBUsage",
DownloadUtils.convertByteUnits(usage));
status.removeAttribute("hidden");
button.removeAttribute("hidden");
}
else {
status.value = "";
status.setAttribute("hidden", "true");
button.setAttribute("hidden", "true");
}
status.value = "";
status.setAttribute("hidden", "true");
button.setAttribute("hidden", "true");
}
function onIndexedDBClear()
@@ -230,3 +223,25 @@ function onIndexedDBClear()
permissionManager.remove(gPermURI.host, "indexedDB-unlimited");
initIndexedDBRow();
}
function onIndexedDBUsageCallback(uri, usage)
{
if (!uri.equals(gPermURI)) {
throw new Error("Callback received for bad URI: " + uri);
}
if (usage) {
if (!("DownloadUtils" in window)) {
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
}
var status = document.getElementById("indexedDBStatus");
var button = document.getElementById("indexedDBClear");
status.value =
gBundle.getFormattedString("indexedDBUsage",
DownloadUtils.convertByteUnits(usage));
status.removeAttribute("hidden");
button.removeAttribute("hidden");
}
}

View File

@@ -147,12 +147,16 @@ let Change = {
},
_updateStatusWithString: function Change__updateStatusWithString(string, state) {
this._statusRow.hidden = false;
document.getElementById("passphraseStrengthRow").hidden = true;
this._status.value = string;
this._statusIcon.setAttribute("status", state);
let error = state == "error";
this._dialog.getButton("cancel").setAttribute("disabled", !error);
this._dialog.getButton("accept").setAttribute("disabled", !error);
document.getElementById("printSyncKeyButton").disabled = !error;
document.getElementById("saveSyncKeyButton").disabled = !error;
if (state == "success")
window.setTimeout(window.close, 1500);

View File

@@ -135,10 +135,12 @@
</vbox>
<hbox id="passphraseBackupButtons" pack="center">
<button label="&button.syncKeyBackup.print.label;"
<button id="printSyncKeyButton"
label="&button.syncKeyBackup.print.label;"
accesskey="&button.syncKeyBackup.print.accesskey;"
oncommand="gSyncUtils.passphrasePrint('passphraseBox');"/>
<button label="&button.syncKeyBackup.save.label;"
<button id="saveSyncKeyButton"
label="&button.syncKeyBackup.save.label;"
accesskey="&button.syncKeyBackup.save.accesskey;"
oncommand="gSyncUtils.passphraseSave('passphraseBox');"/>
</hbox>

View File

@@ -341,7 +341,7 @@ var gSyncSetup = {
// xxxmpc - hack, sigh
if (el1.value == document.getElementById("weavePassword").value) {
valid = false;
str = Weave.Utils.getErrorString("change.passphrase.ppSameAsPassword");
str = Weave.Utils.getErrorString("change.synckey.sameAsPassword");
}
else {
[valid, str] = gSyncUtils.validatePassphrase(el1);
@@ -349,7 +349,14 @@ var gSyncSetup = {
let feedback = document.getElementById("passphraseFeedbackRow");
this._setFeedback(feedback, valid, str);
if (!valid)
if (!valid) {
// Hide strength meter if we're displaying an error.
document.getElementById("passphraseStrengthRow").hidden = true;
return valid;
}
// No passphrase strength meter for the generated key.
if (!this._haveCustomSyncKey)
return valid;
// Display passphrase strength
@@ -377,8 +384,8 @@ var gSyncSetup = {
this.wizard.getButton("extra1").hidden = true;
break;
case NEW_ACCOUNT_PP_PAGE:
document.getElementById("saveSyncKeyButton").focus();
let el = document.getElementById("weavePassphrase");
el.blur();
if (!el.value)
this.onPassphraseGenerate();
this.checkFields();

View File

@@ -217,7 +217,8 @@
<textbox id="weavePassphrase"
onkeyup="gSyncSetup.onPassphraseChange()"
onchange="gSyncSetup.onPassphraseChange()"
onfocus="this.select()"/>
onfocus="this.select();
gSyncSetup.afterBackup();"/>
<vbox id="passphraseFeedback" pack="center">
<hbox id="passphraseFeedbackRow" hidden="true" align="center">
@@ -252,11 +253,13 @@
<groupbox align="center">
<description>&syncKeyBackup.description;</description>
<hbox>
<button label="&button.syncKeyBackup.print.label;"
<button id="printSyncKeyButton"
label="&button.syncKeyBackup.print.label;"
accesskey="&button.syncKeyBackup.print.accesskey;"
oncommand="gSyncUtils.passphrasePrint('weavePassphrase');
gSyncSetup.afterBackup();"/>
<button label="&button.syncKeyBackup.save.label;"
<button id="saveSyncKeyButton"
label="&button.syncKeyBackup.save.label;"
accesskey="&button.syncKeyBackup.save.accesskey;"
oncommand="gSyncUtils.passphraseSave('weavePassphrase');
gSyncSetup.afterBackup();"/>

View File

@@ -299,7 +299,9 @@ let gSyncUtils = {
let val = el.value;
let error = "";
if (val == Weave.Service.username)
if (val.length < Weave.MIN_PP_LENGTH)
error = "change.synckey.tooShort";
else if (val == Weave.Service.username)
error = "change.synckey.sameAsUsername";
else if (val == Weave.Service.account)
error = "change.synckey.sameAsEmail";
@@ -307,8 +309,6 @@ let gSyncUtils = {
error = "change.synckey.sameAsPassword";
else if (change && val == Weave.Service.passphrase)
error = "change.synckey.sameAsSyncKey";
else if (val.length < Weave.MIN_PP_LENGTH)
error = "change.synckey.tooShort";
else
valid = true;

View File

@@ -1366,19 +1366,12 @@
if (!this._beginRemoveTab(aTab, false, null, true))
return;
/* Don't animate if:
- the caller didn't opt in
- this is the last tab in the window
- this is a pinned tab
- a bunch of other tabs are already closing (arbitrary threshold)
- the fadein attribute hasn't been set yet
- browser.tabs.animate is false */
if (!animate ||
if (!animate /* the caller didn't opt in */ ||
isLastTab ||
aTab.pinned ||
this._removingTabs.length > 3 ||
aTab.getAttribute("fadein") != "true" ||
this._removingTabs.length > 3 /* don't want lots of concurrent animations */ ||
aTab.getAttribute("fadein") != "true" /* fade-in transition hasn't been triggered yet */ ||
window.getComputedStyle(aTab).maxWidth == "1px" /* fade-in transition hasn't moved yet */ ||
!Services.prefs.getBoolPref("browser.tabs.animate")) {
this._endRemoveTab(aTab);
return;

View File

@@ -363,11 +363,11 @@ SearchEventHandlerClass.prototype = {
var matches = matcher.matched();
var others = matcher.matchedTabsFromOtherWindows();
if (event.which == event.DOM_VK_RETURN && (matches.length > 0 || others.length > 0)) {
if (matches.length > 0)
hideSearch(event);
if (matches.length > 0)
matches[0].zoomIn();
else
TabUtils.focus(others[0]);
hideSearch(event);
}
},
@@ -429,7 +429,8 @@ var TabHandlers = {
// by the number of matches within the window.
var item = iQ("<div/>")
.addClass("inlineMatch")
.click(function(){
.click(function(event){
hideSearch(event);
TabUtils.focus(tab);
});

View File

@@ -390,11 +390,13 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
$fav.css({top:4,left:4});
widthRange = new Range(40, 45);
proportion = widthRange.proportion(css.width); // between 0 and 1
$close.show().css({opacity:proportion});
if (proportion <= .1)
$close.hide()
}
if (proportion <= .1)
$close.hide();
else
$close.show().css({opacity:proportion});
var pad = 1 + 5 * proportion;
var alphaRange = new Range(0.1,0.2);
$fav.css({

View File

@@ -149,7 +149,9 @@ _BROWSER_FILES = \
browser_bug579872.js \
browser_bug580956.js \
browser_bug581242.js \
browser_bug581253.js \
browser_bug581947.js \
browser_bug585785.js \
browser_bug585830.js \
browser_bug592338.js \
browser_bug594131.js \

View File

@@ -21,15 +21,19 @@ var gApp = document.getElementById("bundle_brand").getString("brandShortName");
var gVersion = Services.appinfo.version;
function wait_for_notification(aCallback) {
info("Waiting for notification");
PopupNotifications.panel.addEventListener("popupshown", function() {
PopupNotifications.panel.removeEventListener("popupshown", arguments.callee, false);
info("Saw notification");
aCallback(PopupNotifications.panel);
}, false);
}
function wait_for_install_dialog(aCallback) {
info("Waiting for install dialog");
Services.wm.addListener({
onOpenWindow: function(aXULWindow) {
info("Install dialog opened, waiting for load");
Services.wm.removeListener(this);
var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -41,6 +45,7 @@ function wait_for_install_dialog(aCallback) {
// Allow other window load listeners to execute before passing to callback
executeSoon(function() {
info("Saw install dialog");
// Override the countdown timer on the accept button
var button = domwindow.document.documentElement.getButton("accept");
button.disabled = false;
@@ -60,12 +65,6 @@ function wait_for_install_dialog(aCallback) {
var TESTS = [
function test_blocked_install() {
var triggers = encodeURIComponent(JSON.stringify({
"XPI": "unsigned.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
// Wait for the blocked notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -81,8 +80,6 @@ function test_blocked_install() {
// Wait for the install confirmation dialog
wait_for_install_dialog(function(aWindow) {
aWindow.document.documentElement.acceptDialog();
// Wait for the complete notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -100,24 +97,21 @@ function test_blocked_install() {
runNextTest();
});
});
aWindow.document.documentElement.acceptDialog();
});
});
},
function test_whitelisted_install() {
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"XPI": "unsigned.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
},
function test_whitelisted_install() {
// Wait for the install confirmation dialog
wait_for_install_dialog(function(aWindow) {
aWindow.document.documentElement.acceptDialog();
// Wait for the complete notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -136,19 +130,21 @@ function test_whitelisted_install() {
runNextTest();
});
});
});
},
function test_failed_download() {
aWindow.document.documentElement.acceptDialog();
});
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"XPI": "missing.xpi"
"XPI": "unsigned.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
},
function test_failed_download() {
// Wait for the failed notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -162,18 +158,18 @@ function test_failed_download() {
Services.perms.remove("example.com", "install");
runNextTest();
});
},
function test_corrupt_file() {
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"XPI": "corrupt.xpi"
"XPI": "missing.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
},
function test_corrupt_file() {
// Wait for the failed notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -187,18 +183,18 @@ function test_corrupt_file() {
Services.perms.remove("example.com", "install");
runNextTest();
});
},
function test_incompatible() {
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"XPI": "incompatible.xpi"
"XPI": "corrupt.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
},
function test_incompatible() {
// Wait for the failed notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -212,22 +208,20 @@ function test_incompatible() {
Services.perms.remove("example.com", "install");
runNextTest();
});
},
function test_restartless() {
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"XPI": "restartless.xpi"
"XPI": "incompatible.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
},
function test_restartless() {
// Wait for the install confirmation dialog
wait_for_install_dialog(function(aWindow) {
aWindow.document.documentElement.acceptDialog();
// Wait for the complete notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -249,24 +243,23 @@ function test_restartless() {
});
});
});
});
},
function test_multiple() {
aWindow.document.documentElement.acceptDialog();
});
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"Unsigned XPI": "unsigned.xpi",
"Restartless XPI": "restartless.xpi"
"XPI": "restartless.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
},
function test_multiple() {
// Wait for the install confirmation dialog
wait_for_install_dialog(function(aWindow) {
aWindow.document.documentElement.acceptDialog();
// Wait for the complete notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -289,17 +282,24 @@ function test_multiple() {
});
});
});
aWindow.document.documentElement.acceptDialog();
});
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"Unsigned XPI": "unsigned.xpi",
"Restartless XPI": "restartless.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
},
function test_url() {
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "unsigned.xpi");
// Wait for the install confirmation dialog
wait_for_install_dialog(function(aWindow) {
aWindow.document.documentElement.acceptDialog();
// Wait for the complete notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -317,20 +317,15 @@ function test_url() {
runNextTest();
});
});
aWindow.document.documentElement.acceptDialog();
});
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "unsigned.xpi");
},
function test_localfile() {
var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
.getService(Components.interfaces.nsIChromeRegistry);
try {
var path = cr.convertChromeURL(makeURI(CHROMEROOT + "corrupt.xpi")).spec;
} catch (ex) {
var path = CHROMEROOT + "corrupt.xpi";
}
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(path);
// Wait for the complete notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -342,6 +337,16 @@ function test_localfile() {
gBrowser.removeTab(gBrowser.selectedTab);
runNextTest();
});
var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
.getService(Components.interfaces.nsIChromeRegistry);
try {
var path = cr.convertChromeURL(makeURI(CHROMEROOT + "corrupt.xpi")).spec;
} catch (ex) {
var path = CHROMEROOT + "corrupt.xpi";
}
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(path);
},
function test_wronghost() {
@@ -352,8 +357,6 @@ function test_wronghost() {
gBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.loadURI(TESTROOT + "corrupt.xpi");
// Wait for the complete notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -366,24 +369,15 @@ function test_wronghost() {
gBrowser.removeTab(gBrowser.selectedTab);
runNextTest();
});
gBrowser.loadURI(TESTROOT + "corrupt.xpi");
}, true);
gBrowser.loadURI(TESTROOT2 + "enabled.html");
},
function test_reload() {
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"Unsigned XPI": "unsigned.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
// Wait for the install confirmation dialog
wait_for_install_dialog(function(aWindow) {
aWindow.document.documentElement.acceptDialog();
// Wait for the complete notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -418,23 +412,23 @@ function test_reload() {
}, true);
gBrowser.loadURI(TESTROOT2 + "enabled.html");
});
});
},
function test_theme() {
aWindow.document.documentElement.acceptDialog();
});
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"Theme XPI": "theme.xpi"
"Unsigned XPI": "unsigned.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
},
function test_theme() {
// Wait for the install confirmation dialog
wait_for_install_dialog(function(aWindow) {
aWindow.document.documentElement.acceptDialog();
// Wait for the complete notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -460,16 +454,21 @@ function test_theme() {
});
});
});
});
},
function test_renotify_blocked() {
aWindow.document.documentElement.acceptDialog();
});
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"XPI": "unsigned.xpi"
"Theme XPI": "theme.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
},
function test_renotify_blocked() {
// Wait for the blocked notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -479,8 +478,6 @@ function test_renotify_blocked() {
aPanel.removeEventListener("popuphidden", arguments.callee, false);
info("Timeouts after this probably mean bug 589954 regressed");
executeSoon(function () {
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
is(notification.id, "addon-install-blocked-notification",
@@ -497,28 +494,24 @@ function test_renotify_blocked() {
});
});
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
});
}, false);
// hide the panel (this simulates the user dismissing it)
aPanel.hidePopup();
});
},
function test_renotify_installed() {
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"XPI": "unsigned.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
},
function test_renotify_installed() {
// Wait for the install confirmation dialog
wait_for_install_dialog(function(aWindow) {
aWindow.document.documentElement.acceptDialog();
// Wait for the complete notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@@ -530,11 +523,8 @@ function test_renotify_installed() {
// Install another
executeSoon(function () {
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
// Wait for the install confirmation dialog
wait_for_install_dialog(function(aWindow) {
aWindow.document.documentElement.acceptDialog();
info("Timeouts after this probably mean bug 589954 regressed");
// Wait for the complete notification
@@ -543,46 +533,88 @@ function test_renotify_installed() {
is(notification.id, "addon-install-complete-notification", "Should have seen the second install complete");
AddonManager.getAllInstalls(function(aInstalls) {
is(aInstalls.length, 2, "Should be two pending installs");
is(aInstalls.length, 1, "Should be one pending installs");
aInstalls[0].cancel();
aInstalls[1].cancel();
gBrowser.removeTab(gBrowser.selectedTab);
runNextTest();
});
});
aWindow.document.documentElement.acceptDialog();
});
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
});
}, false);
// hide the panel (this simulates the user dismissing it)
aPanel.hidePopup();
});
aWindow.document.documentElement.acceptDialog();
});
var pm = Services.perms;
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
var triggers = encodeURIComponent(JSON.stringify({
"XPI": "unsigned.xpi"
}));
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
}
];
var gTestStart = null;
function runNextTest() {
if (gTestStart)
info("Test part took " + (Date.now() - gTestStart) + "ms");
AddonManager.getAllInstalls(function(aInstalls) {
is(aInstalls.length, 0, "Should be no active installs");
if (TESTS.length == 0) {
Services.prefs.setBoolPref("extensions.logging.enabled", false);
finish();
return;
}
info("Running " + TESTS[0].name);
gTestStart = Date.now();
TESTS.shift()();
});
};
var XPInstallObserver = {
observe: function (aSubject, aTopic, aData) {
var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
info("Observed " + aTopic + " for " + installInfo.installs.length + " installs");
installInfo.installs.forEach(function(aInstall) {
info("Install of " + aInstall.sourceURI.spec + " was in state " + aInstall.state);
});
}
};
function test() {
requestLongerTimeout(2);
waitForExplicitFinish();
Services.prefs.setBoolPref("extensions.logging.enabled", true);
Services.obs.addObserver(XPInstallObserver, "addon-install-started", false);
Services.obs.addObserver(XPInstallObserver, "addon-install-blocked", false);
Services.obs.addObserver(XPInstallObserver, "addon-install-failed", false);
Services.obs.addObserver(XPInstallObserver, "addon-install-complete", false);
registerCleanupFunction(function() {
Services.prefs.clearUserPref("extensions.logging.enabled");
Services.obs.removeObserver(XPInstallObserver, "addon-install-started");
Services.obs.removeObserver(XPInstallObserver, "addon-install-blocked");
Services.obs.removeObserver(XPInstallObserver, "addon-install-failed");
Services.obs.removeObserver(XPInstallObserver, "addon-install-complete");
});
runNextTest();
}

View File

@@ -0,0 +1,80 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
let testURL = "data:text/plain,nothing but plain text";
let testTag = "581253_tag";
let starButton = document.getElementById("star-button");
function test() {
waitForExplicitFinish();
let tab = gBrowser.selectedTab = gBrowser.addTab();
tab.linkedBrowser.addEventListener("load", (function(event) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
let uri = makeURI(testURL);
let bmTxn =
new PlacesCreateBookmarkTransaction(uri,
PlacesUtils.unfiledBookmarksFolderId,
-1, "", null, []);
PlacesUtils.transactionManager.doTransaction(bmTxn);
ok(PlacesUtils.bookmarks.isBookmarked(uri), "the test url is bookmarked");
ok(starButton.getAttribute("starred") == "true",
"star button indicates that the page is bookmarked");
let tagTxn = new PlacesTagURITransaction(uri, [testTag]);
PlacesUtils.transactionManager.doTransaction(tagTxn);
StarUI.panel.addEventListener("popupshown", onPanelShown, false);
starButton.click();
}), true);
content.location = testURL;
}
function onPanelShown(aEvent) {
if (aEvent.target == StarUI.panel) {
StarUI.panel.removeEventListener("popupshown", arguments.callee, false);
let tagsField = document.getElementById("editBMPanel_tagsField");
ok(tagsField.value == testTag, "tags field value was set");
tagsField.focus();
StarUI.panel.addEventListener("popuphidden", onPanelHidden, false);
let removeButton = document.getElementById("editBookmarkPanelRemoveButton");
removeButton.click();
}
}
/**
* Clears history invoking callback when done.
*/
function waitForClearHistory(aCallback)
{
let observer = {
observe: function(aSubject, aTopic, aData)
{
Services.obs.removeObserver(this, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
aCallback(aSubject, aTopic, aData);
}
};
Services.obs.addObserver(observer, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
PlacesUtils.bhistory.removeAllPages();
}
function onPanelHidden(aEvent) {
if (aEvent.target == StarUI.panel) {
StarUI.panel.removeEventListener("popuphidden", arguments.callee, false);
executeSoon(function() {
ok(!PlacesUtils.bookmarks.isBookmarked(makeURI(testURL)),
"the bookmark for the test url has been removed");
ok(!starButton.hasAttribute("starred"),
"star button indicates that the bookmark has been removed");
gBrowser.removeCurrentTab();
waitForClearHistory(finish);
});
}
}

View File

@@ -0,0 +1,9 @@
function test() {
waitForExplicitFinish();
var tab = gBrowser.addTab();
executeSoon(function () {
gBrowser.removeTab(tab, {animate:true});
ok(!tab.parentNode, "tab successfully removed");
finish();
});
}

View File

@@ -71,15 +71,13 @@ function onTabViewLoadedAndShown() {
ok(TabView.isVisible(), "Tab View is visible");
// go into private browsing and make sure Tab View becomes hidden
pb.privateBrowsingEnabled = true;
ok(!TabView.isVisible(), "Tab View is no longer visible");
afterAllTabsLoaded(function() {
togglePBAndThen(function() {
ok(!TabView.isVisible(), "Tab View is no longer visible");
verifyPB();
// exit private browsing and make sure Tab View is shown again
pb.privateBrowsingEnabled = false;
ok(TabView.isVisible(), "Tab View is visible again");
afterAllTabsLoaded(function() {
togglePBAndThen(function() {
ok(TabView.isVisible(), "Tab View is visible again");
verifyNormal();
// exit Tab View
@@ -95,14 +93,12 @@ function onTabViewHidden() {
ok(!TabView.isVisible(), "Tab View is not visible");
// go into private browsing and make sure Tab View remains hidden
pb.privateBrowsingEnabled = true;
ok(!TabView.isVisible(), "Tab View is still not visible");
afterAllTabsLoaded(function() {
togglePBAndThen(function() {
ok(!TabView.isVisible(), "Tab View is still not visible");
verifyPB();
// turn private browsing back off
pb.privateBrowsingEnabled = false;
afterAllTabsLoaded(function() {
togglePBAndThen(function() {
verifyNormal();
// clean up
@@ -139,6 +135,21 @@ function verifyNormal() {
}
}
// ----------
function togglePBAndThen(callback) {
function pbObserver(aSubject, aTopic, aData) {
if (aTopic != "private-browsing-transition-complete")
return;
Services.obs.removeObserver(pbObserver, "private-browsing-transition-complete");
afterAllTabsLoaded(callback);
}
Services.obs.addObserver(pbObserver, "private-browsing-transition-complete", false);
pb.privateBrowsingEnabled = !pb.privateBrowsingEnabled;
}
// ----------
function afterAllTabsLoaded(callback) {
let stillToLoad = 0;

View File

@@ -612,7 +612,7 @@
]]></field>
<field name="_overLinkDelayTimer"><![CDATA[
undefined
null
]]></field>
<method name="setOverLink">
@@ -621,13 +621,13 @@
// If the over-link is already scheduled to appear or hide, cancel it.
if (this._overLinkDelayTimer) {
clearTimeout(this._overLinkDelayTimer);
delete this._overLinkDelayTimer;
this._overLinkDelayTimer = null;
}
// Hide the over-link if aURL is falsey or if the URL bar is focused.
if (!aURL || this.focused) {
this._overLinkDelayTimer = setTimeout(function overLinkOut(self) {
delete self._overLinkDelayTimer;
self._overLinkDelayTimer = null;
let style = window.getComputedStyle(self._overLinkBox, null);
self._overLinkTransitioning = style.opacity != 0;
self.removeAttribute("overlinkstate");
@@ -647,7 +647,7 @@
// part for performance reasons: otherwise mousing over the bookmarks
// menu for example is very laggy.
this._overLinkDelayTimer = setTimeout(function overLinkIn(self) {
delete self._overLinkDelayTimer;
self._overLinkDelayTimer = null;
self._updateOverLink(aURL);
let style = window.getComputedStyle(self._overLinkBox, null);
self._overLinkTransitioning = style.opacity != 1;

View File

@@ -8,7 +8,7 @@ pref("app.update.interval", 28800);
pref("app.update.download.backgroundInterval", 60);
// URL user can browse to manually if for some reason all update installation
// attempts fail.
pref("app.update.url.manual", "http://www.mozilla.org/products/%APP%/");
pref("app.update.url.manual", "http://nightly.mozilla.org/");
// A default value for the "More information about this update" link
// supplied in the "An update is available" page of the update wizard.
pref("app.update.url.details", "http://www.mozilla.org/projects/%APP%/");

View File

@@ -49,7 +49,14 @@ tier_app_dirs += $(MOZ_BRANDING_DIRECTORY)
tier_app_dirs += toolkit/components/console/hudservice
ifdef MOZ_SERVICES_SYNC
tier_app_dirs += services/crypto
tier_app_dirs += services/sync
endif
tier_app_dirs += browser
# Never add other tier_app_dirs after browser. They won't get packaged
# properly on mac.
installer:
@$(MAKE) -C browser/installer installer

View File

@@ -259,7 +259,7 @@ FeedWriter.prototype = {
},
/**
* Calls doCommand for a the given XUL element within the context of the
* Calls doCommand for a given XUL element within the context of the
* content document.
*
* @param aElement

View File

@@ -692,7 +692,12 @@ var gEditItemOverlay = {
// Here we update either the item title or its cached static title
var newTitle = this._element("userEnteredName").label;
if (this._getItemStaticTitle() != newTitle) {
if (!newTitle &&
PlacesUtils.bookmarks.getFolderIdForItem(this._itemId) == PlacesUtils.tagsFolderId) {
// We don't allow setting an empty title for a tag, restore the old one.
this._initNamePicker();
}
else if (this._getItemStaticTitle() != newTitle) {
this._mayUpdateFirstEditField("namePicker");
if (PlacesUtils.microsummaries.hasMicrosummary(this._itemId)) {
// Note: this implicitly also takes care of the microsummary->static
@@ -1003,20 +1008,16 @@ var gEditItemOverlay = {
}
},
/**
* Splits "tagsField" element value, returning an array of valid tag strings.
*
* @return Array of tag strings found in the field value.
*/
_getTagsArrayFromTagField: function EIO__getTagsArrayFromTagField() {
// we don't require the leading space (after each comma)
var tags = this._element("tagsField").value.split(",");
for (var i=0; i < tags.length; i++) {
// remove trailing and leading spaces
tags[i] = tags[i].replace(/^\s+/, "").replace(/\s+$/, "");
// remove empty entries from the array.
if (tags[i] == "") {
tags.splice(i, 1);
i--;
}
}
return tags;
let tags = this._element("tagsField").value;
return tags.trim()
.split(/\s*,\s*/) // Split on commas and remove spaces.
.filter(function (tag) tag.length > 0); // Kill empty tags.
},
newFolder: function EIO_newFolder() {

View File

@@ -1381,7 +1381,7 @@ XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ptm", function() {
},
/**
* Transaction for editing a the description of a bookmark or a folder.
* Transaction for editing the description of a bookmark or a folder.
*
* @param aItemId
* id of the item to edit.

View File

@@ -58,12 +58,11 @@ var dragDirections = { LEFT: 0, UP: 1, RIGHT: 2, DOWN: 3 };
* @param aDirection
* Direction for the dragging gesture, see dragDirections helper object.
*/
function synthesizeDragWithDirection(aElement, aExpectedDragData, aDirection) {
var trapped = false;
function synthesizeDragWithDirection(aElement, aExpectedDragData, aDirection, aCallback) {
// Dragstart listener function.
var trapDrag = function(event) {
trapped = true;
gBookmarksToolbar.addEventListener("dragstart", function(event)
{
info("A dragstart event has been trapped.");
var dataTransfer = event.dataTransfer;
is(dataTransfer.mozItemCount, aExpectedDragData.length,
"Number of dragged items should be the same.");
@@ -88,7 +87,23 @@ function synthesizeDragWithDirection(aElement, aExpectedDragData, aDirection) {
event.preventDefault();
event.stopPropagation();
}
gBookmarksToolbar.removeEventListener("dragstart", arguments.callee, false);
// This is likely to cause a click event, and, in case we are dragging a
// bookmark, an unwanted page visit. Prevent the click event.
aElement.addEventListener("click", prevent, false);
EventUtils.synthesizeMouse(aElement,
startingPoint.x + xIncrement * 9,
startingPoint.y + yIncrement * 9,
{ type: "mouseup" });
aElement.removeEventListener("click", prevent, false);
// Cleanup eventually opened menus.
if (aElement.localName == "menu" && aElement.open)
aElement.open = false;
aCallback()
}, false);
var prevent = function(aEvent) {aEvent.preventDefault();}
@@ -122,26 +137,10 @@ function synthesizeDragWithDirection(aElement, aExpectedDragData, aDirection) {
startingPoint.x + xIncrement * 1,
startingPoint.y + yIncrement * 1,
{ type: "mousemove" });
gBookmarksToolbar.addEventListener("dragstart", trapDrag, false);
EventUtils.synthesizeMouse(aElement,
startingPoint.x + xIncrement * 9,
startingPoint.y + yIncrement * 9,
{ type: "mousemove" });
ok(trapped, "A dragstart event has been trapped.");
gBookmarksToolbar.removeEventListener("dragstart", trapDrag, false);
// This is likely to cause a click event, and, in case we are dragging a
// bookmark, an unwanted page visit. Prevent the click event.
aElement.addEventListener("click", prevent, false);
EventUtils.synthesizeMouse(aElement,
startingPoint.x + xIncrement * 9,
startingPoint.y + yIncrement * 9,
{ type: "mouseup" });
aElement.removeEventListener("click", prevent, false);
// Cleanup eventually opened menus.
if (aElement.localName == "menu" && aElement.open)
aElement.open = false;
}
function getToolbarNodeForItemId(aItemId) {
@@ -190,17 +189,28 @@ var gTests = [
isnot(element._placesNode, null, "Toolbar node has an associated Places node.");
var expectedData = getExpectedDataForPlacesNode(element._placesNode);
ok(true, "Dragging left");
synthesizeDragWithDirection(element, expectedData, dragDirections.LEFT);
ok(true, "Dragging right");
synthesizeDragWithDirection(element, expectedData, dragDirections.RIGHT);
ok(true, "Dragging up");
synthesizeDragWithDirection(element, expectedData, dragDirections.UP);
ok(true, "Dragging down");
synthesizeDragWithDirection(element, new Array(), dragDirections.DOWN);
// Cleanup.
PlacesUtils.bookmarks.removeItem(folderId);
info("Dragging left");
synthesizeDragWithDirection(element, expectedData, dragDirections.LEFT,
function ()
{
info("Dragging right");
synthesizeDragWithDirection(element, expectedData, dragDirections.RIGHT,
function ()
{
info("Dragging up");
synthesizeDragWithDirection(element, expectedData, dragDirections.UP,
function ()
{
info("Dragging down");
synthesizeDragWithDirection(element, new Array(), dragDirections.DOWN,
function () {
// Cleanup.
PlacesUtils.bookmarks.removeItem(folderId);
nextTest();
});
});
});
});
}
},
@@ -221,17 +231,28 @@ var gTests = [
isnot(element._placesNode, null, "Toolbar node has an associated Places node.");
var expectedData = getExpectedDataForPlacesNode(element._placesNode);
ok(true, "Dragging left");
synthesizeDragWithDirection(element, expectedData, dragDirections.LEFT);
ok(true, "Dragging right");
synthesizeDragWithDirection(element, expectedData, dragDirections.RIGHT);
ok(true, "Dragging up");
synthesizeDragWithDirection(element, expectedData, dragDirections.UP);
ok(true, "Dragging down");
synthesizeDragWithDirection(element, expectedData, dragDirections.DOWN);
// Cleanup.
PlacesUtils.bookmarks.removeItem(itemId);
info("Dragging left");
synthesizeDragWithDirection(element, expectedData, dragDirections.LEFT,
function ()
{
info("Dragging right");
synthesizeDragWithDirection(element, expectedData, dragDirections.RIGHT,
function ()
{
info("Dragging up");
synthesizeDragWithDirection(element, expectedData, dragDirections.UP,
function ()
{
info("Dragging down");
synthesizeDragWithDirection(element, expectedData, dragDirections.DOWN,
function () {
// Cleanup.
PlacesUtils.bookmarks.removeItem(folderId);
nextTest();
});
});
});
});
}
},
];
@@ -242,7 +263,7 @@ function nextTest() {
info("Start of test: " + test.desc);
test.run();
setTimeout(nextTest, 0);
waitForFocus(nextTest);
}
else {
// Collapse the personal toolbar if needed.
@@ -256,11 +277,12 @@ let toolbar = document.getElementById("PersonalToolbar");
let wasCollapsed = toolbar.collapsed;
function test() {
waitForExplicitFinish();
// Uncollapse the personal toolbar if needed.
if (wasCollapsed)
setToolbarVisibility(toolbar, true);
waitForExplicitFinish();
nextTest();
waitForFocus(nextTest);
}

View File

@@ -38,6 +38,24 @@
// This test makes sure that the Forget This Site command is hidden for multiple
// selections.
/**
* Clears history invoking callback when done.
*/
function waitForClearHistory(aCallback) {
const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
let observer = {
observe: function(aSubject, aTopic, aData) {
Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
aCallback();
}
};
Services.obs.addObserver(observer, TOPIC_EXPIRATION_FINISHED, false);
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
hs.QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
}
function test() {
// initialization
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
@@ -61,6 +79,7 @@ function test() {
ww.unregisterNotification(observer);
let organizer = aSubject.QueryInterface(Ci.nsIDOMWindow);
SimpleTest.waitForFocus(function() {
executeSoon(function() {
// Select History in the left pane.
organizer.PlacesOrganizer.selectLeftPaneQuery('History');
let PO = organizer.PlacesOrganizer;
@@ -78,7 +97,7 @@ function test() {
// Open the context menu
let contextmenu = doc.getElementById("placesContext");
contextmenu.addEventListener("popupshown", function() {
contextmenu.removeEventListener("popupshown", arguments.callee, false);
contextmenu.removeEventListener("popupshown", arguments.callee, true);
let forgetThisSite = doc.getElementById("placesContext_deleteHost");
let hideForgetThisSite = (selectionCount != 1);
is(forgetThisSite.hidden, hideForgetThisSite,
@@ -86,17 +105,27 @@ function test() {
"be hidden with " + selectionCount + " items selected");
// Close the context menu
contextmenu.hidePopup();
// Wait for the Organizer window to actually be closed
function closeObserver(aSubject, aTopic, aData) {
if (aTopic != "domwindowclosed")
return;
ww.unregisterNotification(closeObserver);
SimpleTest.waitForFocus(function() {
// Proceed
funcNext();
});
}
ww.registerNotification(closeObserver);
// Close Library window.
organizer.close();
// Proceed
funcNext();
}, false);
}, true);
// Get cell coordinates
var x = {}, y = {}, width = {}, height = {};
tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "text",
x, y, width, height);
// Initiate a context menu for the selected cell
EventUtils.synthesizeMouse(tree.body, x + 4, y + 4, {type: "contextmenu"}, organizer);
EventUtils.synthesizeMouse(tree.body, x.value + width.value / 2, y.value + height.value / 2, {type: "contextmenu"}, organizer);
});
}, organizer);
}
@@ -111,9 +140,7 @@ function test() {
testForgetThisSiteVisibility(1, function() {
testForgetThisSiteVisibility(2, function() {
// Cleanup
history.QueryInterface(Ci.nsIBrowserHistory)
.removeAllPages();
finish();
waitForClearHistory(finish);
});
});
}

View File

@@ -79,10 +79,10 @@
hidecolumnpicker="true" seltype="single">
<treecols>
<treecol id="domainCol" label="&cookiedomain.label;" flex="2" primary="true"
class="sortDirectionIndicator" persist="width" onclick="gCookiesWindow.sort('rawHost');" />
persist="width" onclick="gCookiesWindow.sort('rawHost');"/>
<splitter class="tree-splitter"/>
<treecol id="nameCol" label="&cookiename.label;" flex="1"
class="sortDirectionIndicator" persist="width"
persist="width"
onclick="gCookiesWindow.sort('name');"/>
</treecols>
<treechildren id="cookiesChildren"/>

View File

@@ -38,6 +38,24 @@
// This test makes sure that the Forget This Site command is hidden in private
// browsing mode.
/**
* Clears history invoking callback when done.
*/
function waitForClearHistory(aCallback) {
const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
let observer = {
observe: function(aSubject, aTopic, aData) {
Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
aCallback();
}
};
Services.obs.addObserver(observer, TOPIC_EXPIRATION_FINISHED, false);
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
hs.QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
}
function test() {
// initialization
let pb = Cc["@mozilla.org/privatebrowsing;1"].
@@ -60,6 +78,7 @@ function test() {
Services.ww.unregisterNotification(observer);
let organizer = aSubject.QueryInterface(Ci.nsIDOMWindow);
SimpleTest.waitForFocus(function() {
executeSoon(function() {
// Select History in the left pane.
let PO = organizer.PlacesOrganizer;
PO.selectLeftPaneQuery('History');
@@ -76,7 +95,7 @@ function test() {
// Open the context menu
let contextmenu = doc.getElementById("placesContext");
contextmenu.addEventListener("popupshown", function() {
contextmenu.removeEventListener("popupshown", arguments.callee, false);
contextmenu.removeEventListener("popupshown", arguments.callee, true);
let forgetThisSite = doc.getElementById("placesContext_deleteHost");
is(forgetThisSite.hidden, !expected,
"The Forget This Site menu item should " + (expected ? "not " : "") + "be hidden");
@@ -85,17 +104,27 @@ function test() {
"The Forget This Site command should " + (expected ? "not " : "") + "be disabled");
// Close the context menu
contextmenu.hidePopup();
// Wait for the Organizer window to actually be closed
function closeObserver(aSubject, aTopic, aData) {
if (aTopic != "domwindowclosed")
return;
Services.ww.unregisterNotification(closeObserver);
SimpleTest.waitForFocus(function() {
// Proceed
funcNext();
});
}
Services.ww.registerNotification(closeObserver);
// Close Library window.
organizer.close();
// Proceed
funcNext();
}, false);
}, true);
// Get cell coordinates
var x = {}, y = {}, width = {}, height = {};
tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "text",
x, y, width, height);
// Initiate a context menu for the selected cell
EventUtils.synthesizeMouse(tree.body, x + 4, y + 4, {type: "contextmenu"}, organizer);
EventUtils.synthesizeMouse(tree.body, x.value + width.value / 2, y.value + height.value / 2, {type: "contextmenu"}, organizer);
});
}, organizer);
}
@@ -115,9 +144,7 @@ function test() {
pb.privateBrowsingEnabled = false;
testForgetThisSiteVisibility(true, function() {
// Cleanup
history.QueryInterface(Ci.nsIBrowserHistory)
.removeAllPages();
finish();
waitForClearHistory(finish);
});
});
});

View File

@@ -21,7 +21,7 @@
* Contributor(s):
* Dietrich Ayala <dietrich@mozilla.com>
* Ehsan Akhgari <ehsan.akhgari@gmail.com>
* Michael Kraft <morac99-firefox@yahoo.com>
* Michael Kraft <morac99-firefox2@yahoo.com>
* Paul OShannessy <paul@oshannessy.com>
* Nils Maier <maierman@web.de>
*
@@ -65,6 +65,9 @@ const STATE_QUITTING = -1;
const STATE_STOPPED_STR = "stopped";
const STATE_RUNNING_STR = "running";
const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;
const PRIVACY_NONE = 0;
const PRIVACY_ENCRYPTED = 1;
const PRIVACY_FULL = 2;
@@ -217,6 +220,16 @@ SessionStoreService.prototype = {
// Whether we've been initialized
_initialized: false,
// The original "sessionstore.resume_session_once" preference value before it
// was modified by saveState. saveState will set the
// "sessionstore.resume_session_once" to true when the
// the "sessionstore.resume_from_crash" preference is false (crash recovery
// is disabled) so that pinned tabs will be restored in the case of a
// crash. This variable is used to restore the original value so the
// previous session is not always restored when
// "sessionstore.resume_from_crash" is true.
_resume_session_once_on_shutdown: null,
/* ........ Public Getters .............. */
get canRestoreLastSession() {
@@ -443,15 +456,25 @@ SessionStoreService.prototype = {
this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
this._clearingOnShutdown = false;
}
else if (this._resume_session_once_on_shutdown != null) {
// if the sessionstore.resume_session_once preference was changed by
// saveState because crash recovery is disabled then restore the
// preference back to the value it was prior to that. This will prevent
// SessionStore from always restoring the session when crash recovery is
// disabled.
this._prefBranch.setBoolPref("sessionstore.resume_session_once",
this._resume_session_once_on_shutdown);
}
this._loadState = STATE_QUITTING; // just to be sure
this._uninit();
break;
case "browser:purge-session-history": // catch sanitization
let openWindows = {};
this._forEachBrowserWindow(function(aWindow) {
Array.forEach(aWindow.gBrowser.browsers, function(aBrowser) {
delete aBrowser.__SS_data;
delete aBrowser.__SS_needsRestore;
Array.forEach(aWindow.gBrowser.tabs, function(aTab) {
delete aTab.linkedBrowser.__SS_data;
if (aTab.linkedBrowser.__SS_restoreState)
this._resetTabRestoringState(aTab);
});
openWindows[aWindow.__SSi] = true;
});
@@ -550,6 +573,12 @@ SessionStoreService.prototype = {
break;
case "sessionstore.resume_from_crash":
this._resume_from_crash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
// restore original resume_session_once preference if set in saveState
if (this._resume_session_once_on_shutdown != null) {
this._prefBranch.setBoolPref("sessionstore.resume_session_once",
this._resume_session_once_on_shutdown);
this._resume_session_once_on_shutdown = null;
}
// either create the file with crash recovery information or remove it
// (when _loadState is not STATE_RUNNING, that file is used for session resuming instead)
if (!this._resume_from_crash)
@@ -874,9 +903,15 @@ SessionStoreService.prototype = {
delete browser.__SS_data;
// If this tab was in the middle of restoring, we want to restore the next
// tab. If the tab hasn't been restored, we want to remove it from the array.
this._resetTabRestoringState(aTab, true, false);
// If this tab was in the middle of restoring or still needs to be restored,
// we need to reset that state. If the tab was restoring, we will attempt to
// restore the next tab.
let previousState;
if (previousState = browser.__SS_restoreState) {
this._resetTabRestoringState(aTab);
if (previousState == TAB_STATE_RESTORING)
this.restoreNextTab();
}
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
@@ -972,12 +1007,12 @@ SessionStoreService.prototype = {
this._windows[aWindow.__SSi].selected = aWindow.gBrowser.tabContainer.selectedIndex;
let tab = aWindow.gBrowser.selectedTab;
// If __SS_needsRestore is still on the browser, then we haven't restored
// If __SS_restoreState is still on the browser and it is
// TAB_STATE_NEEDS_RESTORE, then then we haven't restored
// this tab yet. Explicitly call restoreTab to kick off the restore.
if (tab.linkedBrowser.__SS_needsRestore) {
this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(tab));
if (tab.linkedBrowser.__SS_restoreState &&
tab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
this.restoreTab(tab);
}
// attempt to update the current URL we send in a crash report
this._updateCrashReportURL(aWindow);
@@ -986,7 +1021,8 @@ SessionStoreService.prototype = {
onTabShow: function sss_onTabShow(aTab) {
// If the tab hasn't been restored yet, move it into the right _tabsToRestore bucket
if (aTab.linkedBrowser.__SS_needsRestore) {
if (aTab.linkedBrowser.__SS_restoreState &&
aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab));
// Just put it at the end of the list of visible tabs;
this._tabsToRestore.visible.push(aTab);
@@ -995,7 +1031,8 @@ SessionStoreService.prototype = {
onTabHide: function sss_onTabHide(aTab) {
// If the tab hasn't been restored yet, move it into the right _tabsToRestore bucket
if (aTab.linkedBrowser.__SS_needsRestore) {
if (aTab.linkedBrowser.__SS_restoreState &&
aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(aTab));
// Just put it at the end of the list of hidden tabs;
this._tabsToRestore.hidden.push(aTab);
@@ -1336,19 +1373,14 @@ SessionStoreService.prototype = {
* Store all session data for a window
* @param aWindow
* Window reference
* @param aPinnedOnly
* Bool collect pinned tabs only
*/
_saveWindowHistory: function sss_saveWindowHistory(aWindow, aPinnedOnly) {
_saveWindowHistory: function sss_saveWindowHistory(aWindow) {
var tabbrowser = aWindow.gBrowser;
var tabs = tabbrowser.tabs;
var tabsData = this._windows[aWindow.__SSi].tabs = [];
for (var i = 0; i < tabs.length; i++) {
if (aPinnedOnly && !tabs[i].pinned)
break;
for (var i = 0; i < tabs.length; i++)
tabsData.push(this._collectTabData(tabs[i]));
}
this._windows[aWindow.__SSi].selected = tabbrowser.mTabBox.selectedIndex + 1;
},
@@ -2011,7 +2043,7 @@ SessionStoreService.prototype = {
if (!this._isWindowLoaded(aWindow)) // window data is still in _statesToRestore
return;
if (aUpdateAll || this._dirtyWindows[aWindow.__SSi] || aWindow == activeWindow) {
this._collectWindowData(aWindow, aPinnedOnly);
this._collectWindowData(aWindow);
}
else { // always update the window features (whose change alone never triggers a save operation)
this._updateWindowFeatures(aWindow);
@@ -2063,8 +2095,15 @@ SessionStoreService.prototype = {
#endif
if (aPinnedOnly) {
// perform a deep copy so that existing session variables are not changed.
total = JSON.parse(this._toJSONString(total));
total = total.filter(function (win) {
win.tabs = win.tabs.filter(function (tab) tab.pinned);
// remove closed tabs
win._closedTabs = [];
// correct selected tab index if it was stripped out
if (win.selected > win.tabs.length)
win.selected = 1;
return win.tabs.length > 0;
});
if (total.length == 0)
@@ -2077,8 +2116,9 @@ SessionStoreService.prototype = {
this.activeWindowSSiCache = activeWindow.__SSi || "";
}
ix = windows.indexOf(this.activeWindowSSiCache);
// We don't want to restore focus to a minimized window.
if (ix != -1 && total[ix].sizemode == "minimized")
// We don't want to restore focus to a minimized window or a window which had all its
// tabs stripped out (doesn't exist).
if (ix != -1 && total[ix] && total[ix].sizemode == "minimized")
ix = -1;
return { windows: total, selectedWindow: ix + 1, _closedWindows: lastClosedWindowsCopy };
@@ -2104,12 +2144,12 @@ SessionStoreService.prototype = {
return { windows: total };
},
_collectWindowData: function sss_collectWindowData(aWindow, aPinnedOnly) {
_collectWindowData: function sss_collectWindowData(aWindow) {
if (!this._isWindowLoaded(aWindow))
return;
// update the internal state data for this window
this._saveWindowHistory(aWindow, aPinnedOnly);
this._saveWindowHistory(aWindow);
this._updateTextAndScrollData(aWindow);
this._updateCookieHosts(aWindow);
this._updateWindowFeatures(aWindow);
@@ -2221,10 +2261,15 @@ SessionStoreService.prototype = {
tabbrowser.showTab(tabs[t]);
}
// If overwriting tabs, we want to remove __SS_restoring from the browser.
// If overwriting tabs, we want to reset each tab's "restoring" state. Since
// we're overwriting those tabs, they should no longer be restoring. The
// tabs will be rebuilt and marked if they need to be restored after loading
// state (in restoreHistoryPrecursor).
if (aOverwriteTabs) {
for (let i = 0; i < tabbrowser.tabs.length; i++)
this._resetTabRestoringState(tabbrowser.tabs[i], false, false);
for (let i = 0; i < tabbrowser.tabs.length; i++) {
if (tabbrowser.browsers[i].__SS_restoreState)
this._resetTabRestoringState(tabbrowser.tabs[i]);
}
}
// We want to set up a counter on the window that indicates how many tabs
@@ -2299,13 +2344,6 @@ SessionStoreService.prototype = {
function sss_restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, aIx, aCount) {
var tabbrowser = aWindow.gBrowser;
// In order for setTabState to add gTabsProgressListener, we want to add it
// here. We'll only attempt to do it when aCount == 0 though so we don't add
// it multiple times.
if (aCount == 0 &&
tabbrowser.mTabsProgressListeners.indexOf(gRestoreTabsProgressListener) == -1)
tabbrowser.addTabsProgressListener(gRestoreTabsProgressListener);
// make sure that all browsers and their histories are available
// - if one's not, resume this check in 100ms (repeat at most 10 times)
for (var t = aIx; t < aTabs.length; t++) {
@@ -2391,7 +2429,7 @@ SessionStoreService.prototype = {
// keep the data around to prevent dataloss in case
// a tab gets closed before it's been properly restored
browser.__SS_data = tabData;
browser.__SS_needsRestore = true;
browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;
if (!tabData.entries || tabData.entries.length == 0) {
// make sure to blank out this tab's content
@@ -2528,23 +2566,47 @@ SessionStoreService.prototype = {
}
},
restoreTab: function(aTab) {
/**
* Restores the specified tab. If the tab can't be restored (eg, no history or
* calling gotoIndex fails), then state changes will be rolled back.
* This method will check if gTabsProgressListener is attached to the tab's
* window, ensuring that we don't get caught without one.
* This method removes the session history listener right before starting to
* attempt a load. This will prevent cases of "stuck" listeners.
* If this method returns false, then it is up to the caller to decide what to
* do. In the common case (restoreNextTab), we will want to then attempt to
* restore the next tab. In the other case (selecting the tab, reloading the
* tab), the caller doesn't actually want to do anything if no page is loaded.
*
* @param aTab
* the tab to restore
*
* @returns true/false indicating whether or not a load actually happened
*/
restoreTab: function sss_restoreTab(aTab) {
let window = aTab.ownerDocument.defaultView;
let browser = aTab.linkedBrowser;
let tabData = browser.__SS_data;
// There are cases within where we haven't actually started a load and so we
// should call restoreNextTab. We don't want to do it immediately though
// since we might not set userTypedValue in a timely fashion.
let shouldRestoreNextTab = false;
// There are cases within where we haven't actually started a load. In that
// that case we'll reset state changes we made and return false to the caller
// can handle appropriately.
let didStartLoad = false;
// Make sure that the tabs progress listener is attached to this window
this._ensureTabsProgressListener(window);
// Make sure that this tab is removed from _tabsToRestore
this._removeTabFromTabsToRestore(aTab);
// Increase our internal count.
this._tabsRestoringCount++;
// Decrement the number of tabs this window needs to restore
window.__SS_tabsToRestore--;
// Set this tab's state to restoring
browser.__SS_restoreState = TAB_STATE_RESTORING;
delete browser.__SS_needsRestore;
// Remove the history listener, since we no longer need it once we start restoring
this._removeSHistoryListener(aTab);
let activeIndex = (tabData.index || tabData.entries.length) - 1;
if (activeIndex >= tabData.entries.length)
@@ -2558,50 +2620,47 @@ SessionStoreService.prototype = {
browser.__SS_restore_pageStyle = tabData.pageStyle || "";
browser.__SS_restore_tab = aTab;
didStartLoad = true;
try {
// Get the tab title (set in restoreHistoryPrecursor) for later
let label = aTab.label;
browser.__SS_restoring = true;
browser.webNavigation.gotoIndex(activeIndex);
// gotoIndex will force the "loading" string, so set the title
aTab.label = label;
}
catch (ex) {
// ignore page load errors
aTab.removeAttribute("busy");
shouldRestoreNextTab = true;
didStartLoad = false;
}
}
else {
// If there is no active index, then we'll never set __SS_restore_data
// and even if there is a load event, we won't kick of the next tab
// (restoreDocument won't get called), so do it ourselves.
shouldRestoreNextTab = true;
}
// Handle userTypedValue. Setting userTypedValue seems to update gURLbar
// as needed. Calling loadURI will cancel form filling in restoreDocument
if (tabData.userTypedValue) {
browser.userTypedValue = tabData.userTypedValue;
if (tabData.userTypedClear) {
browser.__SS_restoring = true;
didStartLoad = true;
browser.loadURI(tabData.userTypedValue, null, null, true);
}
else {
shouldRestoreNextTab = true;
}
}
if (shouldRestoreNextTab)
this.restoreNextTab(true);
// If we didn't start a load, then we won't reset this tab through the usual
// channel (via the progress listener), so reset the tab ourselves.
if (!didStartLoad)
this._resetTabRestoringState(aTab);
return didStartLoad;
},
restoreNextTab: function sss_restoreNextTab(aFromTabFinished) {
/**
* This _attempts_ to restore the next available tab. If the restore fails,
* then we will attempt the next one.
* There are conditions where this won't do anything:
* if we're in the process of quitting
* if there are no tabs to restore
* if we have already reached the limit for number of tabs to restore
*/
restoreNextTab: function sss_restoreNextTab() {
// If we call in here while quitting, we don't actually want to do anything
if (this._loadState == STATE_QUITTING)
return;
if (aFromTabFinished)
this._tabsRestoringCount--;
// If it's not possible to restore anything, then just bail out.
if (this._maxConcurrentTabRestores >= 0 &&
@@ -2619,17 +2678,11 @@ SessionStoreService.prototype = {
if (nextTabArray) {
let tab = nextTabArray.shift();
this.restoreTab(tab);
}
else {
// Remove the progress listener from windows. It will get re-added as needed.
this._forEachBrowserWindow(function(aWindow) {
if (!aWindow.__SS_tabsToRestore) {
// This won't fail since removeTabsProgressListener just filters. It
// doesn't attempt to splice.
aWindow.gBrowser.removeTabsProgressListener(gRestoreTabsProgressListener);
}
});
let didStartLoad = this.restoreTab(tab);
// If we don't start a load in the restored tab (eg, no entries) then we
// want to attempt to restore the next tab.
if (!didStartLoad)
this.restoreNextTab();
}
},
@@ -3016,8 +3069,18 @@ SessionStoreService.prototype = {
if (!oState)
return;
if (pinnedOnly)
this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
if (pinnedOnly) {
// Save original resume_session_once preference for when quiting browser,
// otherwise session will be restored next time browser starts and we
// only want it to be restored in the case of a crash.
if (this._resume_session_once_on_shutdown == null) {
this._resume_session_once_on_shutdown =
this._prefBranch.getBoolPref("sessionstore.resume_session_once");
this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
// flush the preference file so preference will be saved in case of a crash
Services.prefs.savePrefFile(null);
}
}
oState.session = {
state: this._loadState == STATE_RUNNING ? STATE_RUNNING_STR : STATE_STOPPED_STR,
@@ -3164,6 +3227,24 @@ SessionStoreService.prototype = {
return window;
},
/**
* Gets the tab for the given browser. This should be marginally better
* than using tabbrowser's getTabForContentWindow. This assumes the browser
* is the linkedBrowser of a tab, not a dangling browser.
*
* @param aBrowser
* The browser from which to get the tab.
*/
_getTabForBrowser: function sss_getTabForBrowser(aBrowser) {
let window = aBrowser.ownerDocument.defaultView;
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
let tab = window.gBrowser.tabs[i];
if (tab.linkedBrowser == aBrowser)
return tab;
}
},
/**
* Whether or not to resume session, if not recovering from a crash.
* @returns bool
@@ -3549,69 +3630,92 @@ SessionStoreService.prototype = {
/**
* Reset the restoring state for a particular tab. This will be called when
* removing a tab, when a tab needs to be reset (it's being overwritten), or
* when reload occurs. This is multipurpose and is meant to provide a single
* path for very similar functionality.
* removing a tab or when a tab needs to be reset (it's being overwritten).
*
* @param aTab
* The tab that will be "reset"
* @param aRestoreNextTab
* If the tab is currently restoring, should we allow a new restore to
* begin
* @param aRestoreThisTab
* In the process of "resetting" this tab, should we also restore it.
*/
_resetTabRestoringState:
function sss__resetTabRestoringState(aTab, aRestoreNextTab, aRestoreThisTab) {
_resetTabRestoringState: function sss__resetTabRestoringState(aTab) {
let window = aTab.ownerDocument.defaultView;
let browser = aTab.linkedBrowser;
if (browser.__SS_restoring) {
// If the session history listener hasn't been detached, make sure we
// remove it and delete the reference.
if (browser.__SS_shistoryListener) {
browser.webNavigation.sessionHistory.
removeSHistoryListener(browser.__SS_shistoryListener);
delete browser.__SS_shistoryListener;
}
// Keep the tab's previous state for later in this method
let previousState = browser.__SS_restoreState;
delete browser.__SS_restoring;
if (aRestoreNextTab) {
// this._tabsRestoringCount is decremented in restoreNextTab.
this.restoreNextTab(true);
}
else {
// Even if we aren't restoring the next tab, we still need to decrement
// the restoring count. Normally it gets done within restoreNextTab.
this._tabsRestoringCount--;
}
// The browser is no longer in any sort of restoring state.
delete browser.__SS_restoreState;
// We want to decrement window.__SS_tabsToRestore here so that we always
// decrement it AFTER a tab is done restoring or when a tab gets "reset".
window.__SS_tabsToRestore--;
// Remove the progress listener if we should.
this._removeTabsProgressListener(window);
if (previousState == TAB_STATE_RESTORING) {
this._tabsRestoringCount--;
}
else if (browser.__SS_needsRestore) {
// First we'll remove the tab from the hidden/visible array of tabs left
// to restore.
let splicedTabs;
if (aTab.hidden) {
splicedTabs =
this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab), 1);
}
else {
splicedTabs =
this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(aTab), 1);
}
if (aRestoreThisTab && splicedTabs.length) {
// If we want to restore the tab, then we'll do that. This tab still
// needs restore, so we're going to restore this one, not the next one
// as was done above.
this.restoreTab(aTab);
}
else {
// If we don't want to restore the tab, we want to explicitly remove
// __SS_needsRestore and decrement __SS_tabsToRestore. Normally this is
// done in restoreTab.
let window = aTab.ownerDocument.defaultView;
window.__SS_tabsToRestore--;
delete browser.__SS_needsRestore;
}
else if (previousState == TAB_STATE_NEEDS_RESTORE) {
// Make sure the session history listener is removed. This is normally
// done in restoreTab, but this tab is being removed before that gets called.
this._removeSHistoryListener(aTab);
// Make sure that the tab is removed from the list of tabs to restore.
// Again, this is normally done in restoreTab, but that isn't being called
// for this tab.
this._removeTabFromTabsToRestore(aTab);
}
},
/**
* Remove the tab from this._tabsToRestore[visible/hidden]
*
* @param aTab
*/
_removeTabFromTabsToRestore: function sss__removeTabFromTabsToRestore(aTab) {
let arr = this._tabsToRestore[aTab.hidden ? "hidden" : "visible"];
let index = arr.indexOf(aTab);
if (index > -1)
arr.splice(index, 1);
},
/**
* Add the tabs progress listener to the window if it isn't already
*
* @param aWindow
* The window to add our progress listener to
*/
_ensureTabsProgressListener: function sss__ensureTabsProgressListener(aWindow) {
let tabbrowser = aWindow.gBrowser;
if (tabbrowser.mTabsProgressListeners.indexOf(gRestoreTabsProgressListener) == -1)
tabbrowser.addTabsProgressListener(gRestoreTabsProgressListener);
},
/**
* Attempt to remove the tabs progress listener from the window.
*
* @param aWindow
* The window from which to remove our progress listener from
*/
_removeTabsProgressListener: function sss__removeTabsProgressListener(aWindow) {
// If there are no tabs left to restore (or restoring) in this window, then
// we can safely remove the progress listener from this window.
if (!aWindow.__SS_tabsToRestore)
aWindow.gBrowser.removeTabsProgressListener(gRestoreTabsProgressListener);
},
/**
* Remove the session history listener from the tab's browser if there is one.
*
* @param aTab
* The tab who's browser to remove the listener
*/
_removeSHistoryListener: function sss__removeSHistoryListener(aTab) {
let browser = aTab.linkedBrowser;
if (browser.__SS_shistoryListener) {
browser.webNavigation.sessionHistory.
removeSHistoryListener(browser.__SS_shistoryListener);
delete browser.__SS_shistoryListener;
}
},
@@ -3748,16 +3852,14 @@ let gRestoreTabsProgressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
// Ignore state changes on browsers that we've already restored and state
// changes that aren't applicable.
if (aBrowser.__SS_restoring &&
if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
// We need to reset the tab before starting the next restore.
// _resetTabRestoringState will make sure we remove the session history
// listener and will call restoreNextTab.
let window = aBrowser.ownerDocument.defaultView;
let tab = window.gBrowser._getTabForContentWindow(aBrowser.contentWindow);
this.ss._resetTabRestoringState(tab, true, false);
let tab = this.ss._getTabForBrowser(aBrowser);
this.ss._resetTabRestoringState(tab);
this.ss.restoreNextTab();
}
}
}
@@ -3781,10 +3883,9 @@ SessionStoreSHistoryListener.prototype = {
OnHistoryPurge: function(aNumEntries) { return true; },
OnHistoryReload: function(aReloadURI, aReloadFlags) {
// On reload, we want to make sure that session history loads the right
// URI. In order to do that, we will call _resetTabRestoringState.
// __SS_needsRestore will still be on this tab's browser, so we will end up
// calling restoreTab and this tab will be loaded.
this.ss._resetTabRestoringState(this.tab, false, true);
// URI. In order to do that, we will juet call restoreTab. That will remove
// the history listener and load the right URI.
this.ss.restoreTab(this.tab);
// Returning false will stop the load that docshell is attempting.
return false;
}

View File

@@ -20,7 +20,7 @@
#
# Contributor(s):
# Simon Bünzli <zeniko@gmail.com>
# Michael Kraft <morac99-firefox@yahoo.com>
# Michael Kraft <morac99-firefox2@yahoo.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
@@ -118,6 +118,7 @@ _BROWSER_TEST_FILES = \
browser_580512.js \
browser_586147.js \
browser_586068-cascaded_restore.js \
browser_600545.js \
$(NULL)
ifneq ($(OS_ARCH),Darwin)

View File

@@ -41,16 +41,26 @@ let ss = Cc["@mozilla.org/browser/sessionstore;1"].
let stateBackup = ss.getBrowserState();
const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;
function test() {
/** Test for Bug 586068 - Cascade page loads when restoring **/
waitForExplicitFinish();
// This test does a lot of window opening / closing and waiting for loads.
// In order to prevent timeouts, we'll extend the default that mochitest uses.
requestLongerTimeout(4);
runNextTest();
}
// test_reloadCascade, test_reloadReload are generated tests that are run out
// of cycle (since they depend on current state). They're listed in [tests] here
// so that it is obvious when they run in respect to the other tests.
let tests = [test_cascade, test_select, test_multiWindowState,
test_setWindowStateNoOverwrite, test_setWindowStateOverwrite,
test_setBrowserStateInterrupted, test_reload];
test_setBrowserStateInterrupted, test_reload,
/* test_reloadReload, */ test_reloadCascadeSetup,
/* test_reloadCascade */];
function runNextTest() {
// Reset the pref
try {
@@ -59,9 +69,10 @@ function runNextTest() {
// set an empty state & run the next test, or finish
if (tests.length) {
ss.setBrowserState(JSON.stringify({ windows: [{ tabs: [{ url: 'about:blank' }], }] }));
info("running next test");
executeSoon(tests.shift());
ss.setBrowserState(JSON.stringify({ windows: [{ tabs: [{ url: 'about:blank' }] }] }));
let test = tests.shift();
info("running " + test.name);
executeSoon(test);
}
else {
ss.setBrowserState(stateBackup);
@@ -77,7 +88,8 @@ function test_cascade() {
// We have our own progress listener for this test, which we'll attach before our state is set
let progressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
if (aBrowser.__SS_restoring &&
dump("\n\nload: " + aBrowser.currentURI.spec + "\n" + JSON.stringify(countTabs()) + "\n\n");
if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -138,7 +150,7 @@ function test_select() {
// We have our own progress listener for this test, which we'll attach before our state is set
let progressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
if (aBrowser.__SS_restoring &&
if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -206,9 +218,10 @@ function test_multiWindowState() {
// We have our own progress listener for this test, which we'll attach before our state is set
let progressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
// We only care about load events when the tab still has __SS_restoring on it.
// We only care about load events when the tab still has
// __SS_restoreState == TAB_STATE_RESTORING on it.
// Since our listener is attached before the sessionstore one, this works out.
if (aBrowser.__SS_restoring &&
if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -285,9 +298,10 @@ function test_setWindowStateNoOverwrite() {
// We have our own progress listener for this test, which we'll attach before our state is set
let progressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
// We only care about load events when the tab still has __SS_restoring on it.
// We only care about load events when the tab still has
// __SS_restoreState == TAB_STATE_RESTORING on it.
// Since our listener is attached before the sessionstore one, this works out.
if (aBrowser.__SS_restoring &&
if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -328,7 +342,10 @@ function test_setWindowStateNoOverwrite() {
// We don't actually care about load order in this test, just that they all
// do load.
is(loadCount, numTabs, "test_setWindowStateNoOverwrite: all tabs were restored");
is(window.__SS_tabsToRestore, 0,
// window.__SS_tabsToRestore isn't decremented until after the progress
// listener is called. Since we get in here before that, we still expect
// the count to be 1.
is(window.__SS_tabsToRestore, 1,
"test_setWindowStateNoOverwrite: window doesn't think there are more tabs to restore");
let count = countTabs();
is(count[0], 0,
@@ -353,9 +370,10 @@ function test_setWindowStateOverwrite() {
// We have our own progress listener for this test, which we'll attach before our state is set
let progressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
// We only care about load events when the tab still has __SS_restoring on it.
// We only care about load events when the tab still has
// __SS_restoreState == TAB_STATE_RESTORING on it.
// Since our listener is attached before the sessionstore one, this works out.
if (aBrowser.__SS_restoring &&
if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -396,7 +414,10 @@ function test_setWindowStateOverwrite() {
// We don't actually care about load order in this test, just that they all
// do load.
is(loadCount, numTabs, "test_setWindowStateOverwrite: all tabs were restored");
is(window.__SS_tabsToRestore, 0,
// window.__SS_tabsToRestore isn't decremented until after the progress
// listener is called. Since we get in here before that, we still expect
// the count to be 1.
is(window.__SS_tabsToRestore, 1,
"test_setWindowStateOverwrite: window doesn't think there are more tabs to restore");
let count = countTabs();
is(count[0], 0,
@@ -421,9 +442,10 @@ function test_setBrowserStateInterrupted() {
// We have our own progress listener for this test, which we'll attach before our state is set
let progressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
// We only care about load events when the tab still has __SS_restoring on it.
// We only care about load events when the tab still has
// __SS_restoreState == TAB_STATE_RESTORING on it.
// Since our listener is attached before the sessionstore one, this works out.
if (aBrowser.__SS_restoring &&
if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -540,7 +562,7 @@ function test_reload() {
// We have our own progress listener for this test, which we'll attach before our state is set
let progressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
if (aBrowser.__SS_restoring &&
if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -554,7 +576,19 @@ function test_reload() {
{ entries: [{ url: "http://example.org/#3" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#4" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#5" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#6" }], extData: { "uniq": r() } }
{ entries: [{ url: "http://example.org/#6" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#7" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#8" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#9" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#10" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#11" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#12" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#13" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#14" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#15" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#16" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#17" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#18" }], extData: { "uniq": r() } }
], selected: 1 }] };
let loadCount = 0;
@@ -577,7 +611,9 @@ function test_reload() {
if (loadCount == state.windows[0].tabs.length) {
window.gBrowser.removeTabsProgressListener(progressListener);
test_reload2(state);
executeSoon(function() {
_test_reloadAfter("test_reloadReload", state, runNextTest);
});
}
else {
// reload the next tab
@@ -592,17 +628,59 @@ function test_reload() {
}
// This test shouldn't be added to tests. It will be called directly from
// test_reload. This guarantees that we're already in a tested restored state
// and we know what the state should be.
function test_reload2(aState) {
info("starting test_reload2");
// This doesn't actually test anything, just does a cascaded restore with default
// settings. This really just sets up to test that reloads work.
function test_reloadCascadeSetup() {
// We have our own progress listener for this test, which we'll attach before our state is set
let progressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
test_cascadeReloadSetup_progressCallback();
}
}
let state = { windows: [{ tabs: [
{ entries: [{ url: "http://example.com/#1" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.com/#2" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.com/#3" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.com/#4" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.com/#5" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.com/#6" }], extData: { "uniq": r() } }
] }] };
let loadCount = 0;
function test_cascadeReloadSetup_progressCallback() {
loadCount++;
if (loadCount < state.windows[0].tabs.length)
return;
window.gBrowser.removeTabsProgressListener(progressListener);
executeSoon(function() {
_test_reloadAfter("test_reloadCascade", state, runNextTest);
});
}
// This progress listener will get attached before the listener in session store.
window.gBrowser.addTabsProgressListener(progressListener);
ss.setBrowserState(JSON.stringify(state));
}
// This is a generic function that will attempt to reload each test. We do this
// a couple times, so make it utilitarian.
// This test expects that aState contains a single window and that each tab has
// a unique extData value eg. { "uniq": value }.
function _test_reloadAfter(aTestName, aState, aCallback) {
info("starting " + aTestName);
let progressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
test_reload2_progressCallback(aBrowser);
test_reloadAfter_progressCallback(aBrowser);
}
}
@@ -617,7 +695,7 @@ function test_reload2(aState) {
}
let loadCount = 0;
function test_reload2_progressCallback(aBrowser) {
function test_reloadAfter_progressCallback(aBrowser) {
loadCount++;
if (loadCount <= aState.windows[0].tabs.length) {
@@ -629,11 +707,11 @@ function test_reload2(aState) {
tab = window.gBrowser.tabs[i];
}
is(ss.getTabValue(tab, "uniq"), expectedData,
"test_reload2: load " + loadCount + " - correct tab was reloaded");
aTestName + ": load " + loadCount + " - correct tab was reloaded");
if (loadCount == aState.windows[0].tabs.length) {
window.gBrowser.removeTabsProgressListener(progressListener);
runNextTest();
aCallback();
}
else {
// reload the next tab
@@ -662,9 +740,9 @@ function countTabs() {
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
let browser = window.gBrowser.tabs[i].linkedBrowser;
if (browser.__SS_restoring)
if (browser.__SS_restoreState == TAB_STATE_RESTORING)
isRestoring++;
else if (browser.__SS_needsRestore)
else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
needsRestore++;
else
wasRestored++;

View File

@@ -0,0 +1,153 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is sessionstore test code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Michael Kraft <morac99-firefox2@yahoo.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
let stateBackup = ss.getBrowserState();
function test() {
/** Test for Bug 600545 **/
waitForExplicitFinish();
testBug600545();
}
function testBug600545() {
// Set the pref to false to cause non-app tabs to be stripped out on a save
Services.prefs.setBoolPref("browser.sessionstore.resume_from_crash", false);
// Need to wait for SessionStore's saveState function to be called
// so that non-pinned tabs will be stripped from non-active window
function waitForSaveState(aSaveStateCallback) {
let topic = "sessionstore-state-write";
Services.obs.addObserver(function() {
Services.obs.removeObserver(arguments.callee, topic, false);
executeSoon(aSaveStateCallback);
}, topic, false);
};
// Need to wait for all tabs to be restored before reading browser state
function waitForBrowserState(aState, aSetStateCallback) {
let locationChanges = 0;
let tabsRestored = getStateTabCount(aState);
// Used to determine when tabs have been restored
let progressListener = {
onLocationChange: function (aBrowser) {
if (++locationChanges == tabsRestored) {
// Remove the progress listener from this window, it will be removed from
// theWin when that window is closed (in setBrowserState).
window.gBrowser.removeTabsProgressListener(this);
executeSoon(aSetStateCallback);
}
}
}
// We also want to catch the 2nd window, so we need to observe domwindowopened
function windowObserver(aSubject, aTopic, aData) {
let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
if (aTopic == "domwindowopened") {
theWin.addEventListener("load", function() {
theWin.removeEventListener("load", arguments.callee, false);
Services.ww.unregisterNotification(windowObserver);
theWin.gBrowser.addTabsProgressListener(progressListener);
}, false);
}
}
Services.ww.registerNotification(windowObserver);
window.gBrowser.addTabsProgressListener(progressListener);
ss.setBrowserState(JSON.stringify(aState));
}
// This tests the following use case:
// When multiple windows are open and browser.sessionstore.resume_from_crash
// preference is false, tab session data for non-active window is stripped for
// non-pinned tabs. This occurs after "sessionstore-state-write" fires which
// will only fire in this case if there is at least one pinned tab.
let state = { windows: [
{
tabs: [
{ entries: [{ url: "http://example.org#0" }], pinned:true },
{ entries: [{ url: "http://example.com#1" }] },
{ entries: [{ url: "http://example.com#2" }] },
],
selected: 2
},
{
tabs: [
{ entries: [{ url: "http://example.com#3" }] },
{ entries: [{ url: "http://example.com#4" }] },
{ entries: [{ url: "http://example.com#5" }] },
{ entries: [{ url: "http://example.com#6" }] }
],
selected: 3
}
] };
waitForBrowserState(state, function() {
waitForSaveState(function () {
let expectedNumberOfTabs = getStateTabCount(state);
let retrievedState = JSON.parse(ss.getBrowserState());
let actualNumberOfTabs = getStateTabCount(retrievedState);
is(actualNumberOfTabs, expectedNumberOfTabs,
"Number of tabs in retreived session data, matches number of tabs set.");
done();
});
});
}
function done() {
// Reset the pref
try {
Services.prefs.clearUserPref("browser.sessionstore.resume_from_crash");
} catch (e) {}
ss.setBrowserState(stateBackup);
executeSoon(finish);
}
// Count up the number of tabs in the state data
function getStateTabCount(aState) {
let tabCount = 0;
for (let i in aState.windows)
tabCount += aState.windows[i].tabs.length;
return tabCount;
}

Some files were not shown because too many files have changed in this diff Show More