Merge tracemonkey to mozilla-central. (a=blockers)

This commit is contained in:
Chris Leary 2011-01-31 19:43:36 -08:00
commit 09f6be30b5
433 changed files with 21369 additions and 7105 deletions

View File

@ -59,7 +59,7 @@ getLinkCB(AtkHypertext *aText, gint aLinkIndex)
if (!accWrap)
return nsnull;
nsRefPtr<nsHyperTextAccessible> hyperText = do_QueryObject(accWrap);
nsHyperTextAccessible* hyperText = accWrap->AsHyperText();
NS_ENSURE_TRUE(hyperText, nsnull);
nsAccessible* hyperLink = hyperText->GetLinkAt(aLinkIndex);
@ -82,7 +82,7 @@ getLinkCountCB(AtkHypertext *aText)
if (!accWrap)
return -1;
nsRefPtr<nsHyperTextAccessible> hyperText = do_QueryObject(accWrap);
nsHyperTextAccessible* hyperText = accWrap->AsHyperText();
NS_ENSURE_TRUE(hyperText, -1);
return hyperText->GetLinkCount();
@ -95,7 +95,7 @@ getLinkIndexCB(AtkHypertext *aText, gint aCharIndex)
if (!accWrap)
return -1;
nsRefPtr<nsHyperTextAccessible> hyperText = do_QueryObject(accWrap);
nsHyperTextAccessible* hyperText = accWrap->AsHyperText();
NS_ENSURE_TRUE(hyperText, -1);
PRInt32 index = -1;

View File

@ -373,7 +373,7 @@ getCharacterCountCB(AtkText *aText)
if (!accWrap)
return 0;
nsRefPtr<nsHyperTextAccessible> textAcc(do_QueryObject(accWrap));
nsHyperTextAccessible* textAcc = accWrap->AsHyperText();
return textAcc->IsDefunct() ?
0 : static_cast<gint>(textAcc->CharacterCount());
}

View File

@ -279,7 +279,7 @@ AccStateChangeEvent::CreateXPCOMObject()
// XXX revisit this when coalescence is faster (eCoalesceFromSameSubtree)
AccTextChangeEvent::
AccTextChangeEvent(nsAccessible* aAccessible, PRInt32 aStart,
nsAString& aModifiedText, PRBool aIsInserted,
const nsAString& aModifiedText, PRBool aIsInserted,
EIsFromUserInput aIsFromUserInput)
: AccEvent(aIsInserted ?
static_cast<PRUint32>(nsIAccessibleEvent::EVENT_TEXT_INSERTED) :
@ -321,7 +321,7 @@ AccHideEvent::
AccHideEvent(nsAccessible* aTarget, nsINode* aTargetNode) :
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget, aTargetNode)
{
mParent = mAccessible->GetCachedParent();
mParent = mAccessible->GetParent();
mNextSibling = mAccessible->GetCachedNextSibling();
mPrevSibling = mAccessible->GetCachedPrevSibling();
}

View File

@ -206,7 +206,7 @@ class AccTextChangeEvent: public AccEvent
{
public:
AccTextChangeEvent(nsAccessible* aAccessible, PRInt32 aStart,
nsAString& aModifiedText, PRBool aIsInserted,
const nsAString& aModifiedText, PRBool aIsInserted,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
// AccEvent

View File

@ -81,6 +81,8 @@ CPPSRCS = \
EXPORTS = \
a11yGeneric.h \
nsAccDocManager.h \
nsAccessibilityService.h \
nsAccessible.h \
nsAccessNode.h \
nsARIAMap.h \

View File

@ -36,16 +36,15 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsEventShell.h"
#include "nsAccUtils.h"
#include "nsCoreUtils.h"
#include "nsDocAccessible.h"
#include "NotificationController.h"
#include "nsAccessibilityService.h"
#include "nsAccUtils.h"
#include "nsCoreUtils.h"
#include "nsDocAccessible.h"
#include "nsEventShell.h"
#include "nsTextAccessible.h"
////////////////////////////////////////////////////////////////////////////////
// NotificationCollector
@ -56,6 +55,8 @@ NotificationController::NotificationController(nsDocAccessible* aDocument,
mObservingState(eNotObservingRefresh), mDocument(aDocument),
mPresShell(aPresShell), mTreeConstructedState(eTreeConstructionPending)
{
mTextHash.Init();
// Schedule initial accessible tree construction.
ScheduleProcessing();
}
@ -113,6 +114,7 @@ NotificationController::Shutdown()
mDocument = nsnull;
mPresShell = nsnull;
mTextHash.Clear();
mContentInsertions.Clear();
mNotifications.Clear();
mEvents.Clear();
@ -181,7 +183,8 @@ NotificationController::IsUpdatePending()
do_QueryInterface(mPresShell);
return presShell->IsLayoutFlushObserver() ||
mObservingState == eRefreshProcessingForUpdate ||
mContentInsertions.Length() != 0 || mNotifications.Length() != 0;
mContentInsertions.Length() != 0 || mNotifications.Length() != 0 ||
mTextHash.Count() != 0;
}
////////////////////////////////////////////////////////////////////////////////
@ -208,6 +211,11 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
if (!mDocument->IsBoundToParent())
return;
#ifdef DEBUG_NOTIFICATIONS
printf("\ninitial tree created, document: %p, document node: %p\n",
mDocument.get(), mDocument->GetDocumentNode());
#endif
mTreeConstructedState = eTreeConstructed;
mDocument->CacheChildrenInSubtree(mDocument);
@ -235,6 +243,10 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
return;
}
// Process rendered text change notifications.
mTextHash.EnumerateEntries(TextEnumerator, mDocument);
mTextHash.Clear();
// Bind hanging child documents.
PRUint32 childDocCount = mHangingChildDocuments.Length();
for (PRUint32 idx = 0; idx < childDocCount; idx++) {
@ -243,7 +255,7 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
nsIContent* ownerContent = mDocument->GetDocumentNode()->
FindContentForSubDocument(childDoc->GetDocumentNode());
if (ownerContent) {
nsAccessible* outerDocAcc = mDocument->GetCachedAccessible(ownerContent);
nsAccessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
if (mDocument->AppendChildDocument(childDoc)) {
// Fire reorder event to notify new accessible document has been
@ -483,13 +495,11 @@ NotificationController::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
return;
if (aThisEvent->mNextSibling == aTailEvent->mAccessible) {
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText,
0, PR_UINT32_MAX);
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
} else if (aThisEvent->mPrevSibling == aTailEvent->mAccessible) {
PRUint32 oldLen = textEvent->GetLength();
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText,
0, PR_UINT32_MAX);
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
textEvent->mStart -= textEvent->GetLength() - oldLen;
}
@ -508,15 +518,14 @@ NotificationController::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
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);
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
} 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);
aTailEvent->mAccessible->AppendTextTo(startText);
textEvent->mModifiedText = startText + textEvent->mModifiedText;
textEvent->mStart -= startText.Length();
}
@ -527,9 +536,13 @@ NotificationController::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
void
NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent)
{
nsRefPtr<nsHyperTextAccessible> textAccessible = do_QueryObject(
nsAccessible* container =
GetAccService()->GetContainerAccessible(aEvent->mNode,
aEvent->mAccessible->GetWeakShell()));
aEvent->mAccessible->GetWeakShell());
if (!container)
return;
nsHyperTextAccessible* textAccessible = container->AsHyperText();
if (!textAccessible)
return;
@ -548,7 +561,7 @@ NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent)
PRInt32 offset = textAccessible->GetChildOffset(aEvent->mAccessible);
nsAutoString text;
aEvent->mAccessible->AppendTextTo(text, 0, PR_UINT32_MAX);
aEvent->mAccessible->AppendTextTo(text);
if (text.IsEmpty())
return;
@ -557,6 +570,396 @@ NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent)
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
}
////////////////////////////////////////////////////////////////////////////////
// Notification controller: text leaf accessible text update
/**
* Used to find a difference between old and new text and fire text change
* events.
*/
class TextUpdater
{
public:
TextUpdater(nsDocAccessible* aDocument, nsTextAccessible* aTextLeaf) :
mDocument(aDocument), mTextLeaf(aTextLeaf) { }
~TextUpdater() { mDocument = nsnull; mTextLeaf = nsnull; }
/**
* Update text of the text leaf accessible, fire text change events for its
* container hypertext accessible.
*/
void Run(const nsAString& aNewText);
private:
TextUpdater();
TextUpdater(const TextUpdater&);
TextUpdater& operator = (const TextUpdater&);
/**
* Fire text change events based on difference between strings.
*/
void FindDiffNFireEvents(const nsDependentSubstring& aStr1,
const nsDependentSubstring& aStr2,
PRUint32** aMatrix,
PRUint32 aStartOffset);
/**
* Change type used to describe the diff between strings.
*/
enum ChangeType {
eNoChange,
eInsertion,
eRemoval,
eSubstitution
};
/**
* Helper to fire text change events.
*/
inline void MayFireEvent(nsAString* aInsertedText, nsAString* aRemovedText,
PRUint32 aOffset, ChangeType* aChange)
{
if (*aChange == eNoChange)
return;
if (*aChange == eRemoval || *aChange == eSubstitution) {
FireEvent(*aRemovedText, aOffset, PR_FALSE);
aRemovedText->Truncate();
}
if (*aChange == eInsertion || *aChange == eSubstitution) {
FireEvent(*aInsertedText, aOffset, PR_TRUE);
aInsertedText->Truncate();
}
*aChange = eNoChange;
}
/**
* Fire text change event.
*/
void FireEvent(const nsAString& aModText, PRUint32 aOffset, PRBool aType);
private:
nsDocAccessible* mDocument;
nsTextAccessible* mTextLeaf;
};
void
TextUpdater::Run(const nsAString& aNewText)
{
NS_ASSERTION(mTextLeaf, "No text leaf accessible?");
const nsString& oldText = mTextLeaf->Text();
PRUint32 oldLen = oldText.Length(), newLen = aNewText.Length();
PRUint32 minLen = oldLen < newLen ? oldLen : newLen;
// Skip coinciding begin substrings.
PRUint32 skipIdx = 0;
for (; skipIdx < minLen; skipIdx++) {
if (aNewText[skipIdx] != oldText[skipIdx])
break;
}
// No change, text append or removal to/from the end.
if (skipIdx == minLen) {
if (oldLen == newLen)
return;
// If text has been appended to the end, fire text inserted event.
if (oldLen < newLen) {
FireEvent(Substring(aNewText, oldLen), oldLen, PR_TRUE);
mTextLeaf->SetText(aNewText);
return;
}
// Text has been removed from the end, fire text removed event.
FireEvent(Substring(oldText, newLen), newLen, PR_FALSE);
mTextLeaf->SetText(aNewText);
return;
}
// Trim coinciding substrings from the end.
PRUint32 endIdx = minLen;
if (oldLen < newLen) {
PRUint32 delta = newLen - oldLen;
for (; endIdx > skipIdx; endIdx--) {
if (aNewText[endIdx + delta] != oldText[endIdx])
break;
}
} else {
PRUint32 delta = oldLen - newLen;
for (; endIdx > skipIdx; endIdx--) {
if (aNewText[endIdx] != oldText[endIdx + delta])
break;
}
}
PRUint32 oldEndIdx = oldLen - minLen + endIdx;
PRUint32 newEndIdx = newLen - minLen + endIdx;
// Find the difference starting from start character, we can skip initial and
// final coinciding characters since they don't affect on the Levenshtein
// distance.
const nsDependentSubstring& str1 =
Substring(oldText, skipIdx, oldEndIdx - skipIdx);
const nsDependentSubstring& str2 =
Substring(aNewText, skipIdx, newEndIdx - skipIdx);
// Compute the matrix.
PRUint32 len1 = str1.Length() + 1, len2 = str2.Length() + 1;
PRUint32** matrix = new PRUint32*[len1];
for (PRUint32 i = 0; i < len1; i++)
matrix[i] = new PRUint32[len2];
matrix[0][0] = 0;
for (PRUint32 i = 1; i < len1; i++)
matrix[i][0] = i;
for (PRUint32 j = 1; j < len2; j++)
matrix[0][j] = j;
for (PRUint32 i = 1; i < len1; i++) {
for (PRUint32 j = 1; j < len2; j++) {
if (str1[i - 1] != str2[j - 1]) {
PRUint32 left = matrix[i - 1][j];
PRUint32 up = matrix[i][j - 1];
PRUint32 upleft = matrix[i - 1][j - 1];
matrix[i][j] =
(left < up ? (upleft < left ? upleft : left) :
(upleft < up ? upleft : up)) + 1;
} else {
matrix[i][j] = matrix[i - 1][j - 1];
}
}
}
FindDiffNFireEvents(str1, str2, matrix, skipIdx);
for (PRUint32 i = 0; i < len1; i++)
delete[] matrix[i];
delete[] matrix;
mTextLeaf->SetText(aNewText);
}
void
TextUpdater::FindDiffNFireEvents(const nsDependentSubstring& aStr1,
const nsDependentSubstring& aStr2,
PRUint32** aMatrix,
PRUint32 aStartOffset)
{
// Find the difference.
ChangeType change = eNoChange;
nsAutoString insertedText;
nsAutoString removedText;
PRUint32 offset = 0;
PRInt32 i = aStr1.Length(), j = aStr2.Length();
while (i >= 0 && j >= 0) {
if (aMatrix[i][j] == 0) {
MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change);
return;
}
// move up left
if (i >= 1 && j >= 1) {
// no change
if (aStr1[i - 1] == aStr2[j - 1]) {
MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change);
i--; j--;
continue;
}
// substitution
if (aMatrix[i][j] == aMatrix[i - 1][j - 1] + 1) {
if (change != eSubstitution)
MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change);
offset = j - 1;
insertedText.Append(aStr1[i - 1]);
removedText.Append(aStr2[offset]);
change = eSubstitution;
i--; j--;
continue;
}
}
// move up, insertion
if (j >= 1 && aMatrix[i][j] == aMatrix[i][j - 1] + 1) {
if (change != eInsertion)
MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change);
offset = j - 1;
insertedText.Insert(aStr2[offset], 0);
change = eInsertion;
j--;
continue;
}
// move left, removal
if (i >= 1 && aMatrix[i][j] == aMatrix[i - 1][j] + 1) {
if (change != eRemoval) {
MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change);
offset = j;
}
removedText.Insert(aStr1[i - 1], 0);
change = eRemoval;
i--;
continue;
}
NS_NOTREACHED("Huh?");
return;
}
MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change);
}
void
TextUpdater::FireEvent(const nsAString& aModText, PRUint32 aOffset,
PRBool aIsInserted)
{
nsAccessible* parent = mTextLeaf->GetParent();
NS_ASSERTION(parent, "No parent for text leaf!");
nsHyperTextAccessible* hyperText = parent->AsHyperText();
NS_ASSERTION(hyperText, "Text leaf parnet is not hyper text!");
PRInt32 textLeafOffset = hyperText->GetChildOffset(mTextLeaf, PR_TRUE);
NS_ASSERTION(textLeafOffset != -1,
"Text leaf hasn't offset within hyper text!");
// Fire text change event.
nsRefPtr<AccEvent> textChangeEvent =
new AccTextChangeEvent(hyperText, textLeafOffset + aOffset, aModText,
aIsInserted);
mDocument->FireDelayedAccessibleEvent(textChangeEvent);
// Fire value change event.
if (hyperText->Role() == nsIAccessibleRole::ROLE_ENTRY) {
nsRefPtr<AccEvent> valueChangeEvent =
new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, hyperText,
eAutoDetect, AccEvent::eRemoveDupes);
mDocument->FireDelayedAccessibleEvent(valueChangeEvent);
}
}
PLDHashOperator
NotificationController::TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry,
void* aUserArg)
{
nsDocAccessible* document = static_cast<nsDocAccessible*>(aUserArg);
nsIContent* textNode = aEntry->GetKey();
nsAccessible* textAcc = document->GetAccessible(textNode);
// If the text node is not in tree or doesn't have frame then this case should
// have been handled already by content removal notifications.
nsINode* containerNode = textNode->GetNodeParent();
if (!containerNode) {
NS_ASSERTION(!textAcc,
"Text node was removed but accessible is kept alive!");
return PL_DHASH_NEXT;
}
nsIFrame* textFrame = textNode->GetPrimaryFrame();
if (!textFrame) {
NS_ASSERTION(!textAcc,
"Text node isn't rendered but accessible is kept alive!");
return PL_DHASH_NEXT;
}
nsIContent* containerElm = containerNode->IsElement() ?
containerNode->AsElement() : nsnull;
nsAutoString text;
textFrame->GetRenderedText(&text);
// Remove text accessible if rendered text is empty.
if (textAcc) {
if (text.IsEmpty()) {
#ifdef DEBUG_NOTIFICATIONS
PRUint32 index = containerNode->IndexOf(textNode);
nsCAutoString tag;
nsCAutoString id;
if (containerElm) {
containerElm->Tag()->ToUTF8String(tag);
nsIAtom* atomid = containerElm->GetID();
if (atomid)
atomid->ToUTF8String(id);
}
printf("\npending text node removal: container: %s@id='%s', index in container: %d\n\n",
tag.get(), id.get(), index);
#endif
document->ContentRemoved(containerElm, textNode);
return PL_DHASH_NEXT;
}
// Update text of the accessible and fire text change events.
#ifdef DEBUG_TEXTCHANGE
PRUint32 index = containerNode->IndexOf(textNode);
nsCAutoString tag;
nsCAutoString id;
if (containerElm) {
containerElm->Tag()->ToUTF8String(tag);
nsIAtom* atomid = containerElm->GetID();
if (atomid)
atomid->ToUTF8String(id);
}
printf("\ntext may be changed: container: %s@id='%s', index in container: %d, old text '%s', new text: '%s'\n\n",
tag.get(), id.get(), index,
NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get(),
NS_ConvertUTF16toUTF8(text).get());
#endif
TextUpdater updater(document, textAcc->AsTextLeaf());
updater.Run(text);
return PL_DHASH_NEXT;
}
// Append an accessible if rendered text is not empty.
if (!text.IsEmpty()) {
#ifdef DEBUG_NOTIFICATIONS
PRUint32 index = containerNode->IndexOf(textNode);
nsCAutoString tag;
nsCAutoString id;
if (containerElm) {
containerElm->Tag()->ToUTF8String(tag);
nsIAtom* atomid = containerElm->GetID();
if (atomid)
atomid->ToUTF8String(id);
}
printf("\npending text node insertion: container: %s@id='%s', index in container: %d\n\n",
tag.get(), id.get(), index);
#endif
nsAccessible* container = document->GetAccessibleOrContainer(containerNode);
nsTArray<nsCOMPtr<nsIContent> > insertedContents;
insertedContents.AppendElement(textNode);
document->ProcessContentInserted(container, &insertedContents);
}
return PL_DHASH_NEXT;
}
////////////////////////////////////////////////////////////////////////////////
// NotificationController: content inserted notification
@ -612,7 +1015,7 @@ NotificationController::ContentInsertion::Process()
catomid->ToUTF8String(cid);
}
printf("\npending content insertion process: %s@id='%s', container: %s@id='%s', inserted content amount: %d\n\n",
printf("\npending content insertion: %s@id='%s', container: %s@id='%s', inserted content amount: %d\n\n",
tag.get(), id.get(), ctag.get(), cid.get(), mInsertedContent.Length());
#endif
@ -622,3 +1025,4 @@ NotificationController::ContentInsertion::Process()
mContainer = nsnull;
mInsertedContent.Clear();
}

View File

@ -51,6 +51,7 @@ class nsIContent;
#ifdef DEBUG_NOTIFICATIONS
#define DEBUG_CONTENTMUTATION
#define DEBUG_TEXTCHANGE
#endif
/**
@ -149,6 +150,26 @@ public:
*/
void ScheduleChildDocBinding(nsDocAccessible* aDocument);
/**
* Schedule the accessible tree update because of rendered text changes.
*/
inline void ScheduleTextUpdate(nsIContent* aTextNode)
{
// Ignore the notification if initial tree construction hasn't been done yet.
if (mTreeConstructedState != eTreeConstructionPending &&
mTextHash.PutEntry(aTextNode)) {
ScheduleProcessing();
}
}
/**
* Cancel pending text update.
*/
inline void CancelTextUpdate(nsIContent* aTextNode)
{
mTextHash.RemoveEntry(aTextNode);
}
/**
* Pend accessible tree update for content insertion.
*/
@ -183,6 +204,23 @@ public:
ScheduleProcessing();
}
/**
* Schedule the generic notification to process asynchronously.
*
* @note The caller must guarantee that the given instance still exists when
* the notification is processed.
*/
template<class Class, class Arg>
inline void ScheduleNotification(Class* aInstance,
typename TNotification<Class, Arg>::Callback aMethod,
Arg* aArg)
{
nsRefPtr<Notification> notification =
new TNotification<Class, Arg>(aInstance, aMethod, aArg);
if (notification && mNotifications.AppendElement(notification))
ScheduleProcessing();
}
protected:
nsAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
@ -323,6 +361,41 @@ private:
*/
nsTArray<nsRefPtr<ContentInsertion> > mContentInsertions;
template<class T>
class nsCOMPtrHashKey : public PLDHashEntryHdr
{
public:
typedef T* KeyType;
typedef const T* KeyTypePointer;
nsCOMPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
nsCOMPtrHashKey(const nsPtrHashKey<T> &aToCopy) : mKey(aToCopy.mKey) {}
~nsCOMPtrHashKey() { }
KeyType GetKey() const { return mKey; }
PRBool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
static PLDHashNumber HashKey(KeyTypePointer aKey)
{ return NS_PTR_TO_INT32(aKey) >> 2; }
enum { ALLOW_MEMMOVE = PR_TRUE };
protected:
nsCOMPtr<T> mKey;
};
/**
* A pending accessible tree update notifications for rendered text changes.
*/
nsTHashtable<nsCOMPtrHashKey<nsIContent> > mTextHash;
/**
* Update the accessible tree for pending rendered text change notifications.
*/
static PLDHashOperator TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry,
void* aUserArg);
/**
* Other notifications like DOM events. Don't make this an nsAutoTArray; we
* use SwapElements() on it.

View File

@ -716,6 +716,7 @@ nsAttributeCharacteristics nsARIAMap::gWAIUnivAttrMap[] = {
{&nsAccessibilityAtoms::aria_flowto, ATTR_BYPASSOBJ },
{&nsAccessibilityAtoms::aria_grabbed, ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_haspopup, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_hidden, ATTR_VALTOKEN },/* always expose obj attr */
{&nsAccessibilityAtoms::aria_invalid, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_label, ATTR_BYPASSOBJ },
{&nsAccessibilityAtoms::aria_labelledby, ATTR_BYPASSOBJ },

View File

@ -332,7 +332,6 @@ nsAccDocManager::HandleDOMDocumentLoad(nsIDocument *aDocument,
nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument);
if (!docAcc) {
docAcc = CreateDocOrRootAccessible(aDocument);
NS_ASSERTION(docAcc, "Can't create document accessible!");
if (!docAcc)
return;
}
@ -523,7 +522,7 @@ nsAccDocManager::SearchAccessibleInDocCache(const nsIDocument* aKey,
if (aDocAccessible) {
nsSearchAccessibleInCacheArg* arg =
static_cast<nsSearchAccessibleInCacheArg*>(aUserArg);
arg->mAccessible = aDocAccessible->GetCachedAccessible(arg->mNode);
arg->mAccessible = aDocAccessible->GetAccessible(arg->mNode);
if (arg->mAccessible)
return PL_DHASH_STOP;
}

View File

@ -38,14 +38,14 @@
#ifndef nsAccDocManager_h_
#define nsAccDocManager_h_
#include "nsAccessible.h"
#include "nsIDocument.h"
#include "nsIDOMEventListener.h"
#include "nsRefPtrHashtable.h"
#include "nsIWebProgress.h"
#include "nsIWebProgressListener.h"
#include "nsWeakReference.h"
class nsAccessible;
class nsDocAccessible;
//#define DEBUG_ACCDOCMGR

View File

@ -44,12 +44,12 @@
#include "nsAccessibilityService.h"
#include "nsAccessibilityAtoms.h"
#include "nsAccessible.h"
#include "nsAccTreeWalker.h"
#include "nsARIAMap.h"
#include "nsDocAccessible.h"
#include "nsHyperTextAccessible.h"
#include "nsHTMLTableAccessible.h"
#include "nsTextAccessible.h"
#include "nsXULTreeGridAccessible.h"
#include "nsIDOMXULContainerElement.h"
@ -404,7 +404,7 @@ nsAccUtils::IsARIASelected(nsAccessible *aAccessible)
nsAccessibilityAtoms::_true, eCaseMatters);
}
already_AddRefed<nsHyperTextAccessible>
nsHyperTextAccessible*
nsAccUtils::GetTextAccessibleFromSelection(nsISelection* aSelection)
{
// Get accessible from selection's focus DOM point (the DOM point where
@ -432,8 +432,7 @@ nsAccUtils::GetTextAccessibleFromSelection(nsISelection* aSelection)
}
do {
nsHyperTextAccessible* textAcc = nsnull;
CallQueryInterface(accessible, &textAcc);
nsHyperTextAccessible* textAcc = accessible->AsHyperText();
if (textAcc)
return textAcc;
@ -639,30 +638,16 @@ nsAccUtils::TextLength(nsAccessible *aAccessible)
if (!IsText(aAccessible))
return 1;
nsIFrame *frame = aAccessible->GetFrame();
if (frame && frame->GetType() == nsAccessibilityAtoms::textFrame) {
// Ensure that correct text length is calculated (with non-rendered
// whitespace chars not counted).
nsIContent *content = frame->GetContent();
if (content) {
PRUint32 length;
nsresult rv = nsHyperTextAccessible::
ContentToRenderedOffset(frame, content->TextLength(), &length);
if (NS_FAILED(rv)) {
NS_NOTREACHED("Failed to get rendered offset!");
return 0;
}
return length;
}
}
nsTextAccessible* textLeaf = aAccessible->AsTextLeaf();
if (textLeaf)
return textLeaf->Text().Length();
// For list bullets (or anything other accessible which would compute its own
// text. They don't have their own frame.
// XXX In the future, list bullets may have frame and anon content, so
// we should be able to remove this at that point
nsAutoString text;
aAccessible->AppendTextTo(text, 0, PR_UINT32_MAX); // Get all the text
aAccessible->AppendTextTo(text); // Get all the text
return text.Length();
}

View File

@ -225,7 +225,7 @@ public:
* @param aSelection [in] the given selection
* @return text accessible
*/
static already_AddRefed<nsHyperTextAccessible>
static nsHyperTextAccessible*
GetTextAccessibleFromSelection(nsISelection* aSelection);
/**

View File

@ -166,6 +166,11 @@ public:
{
return GetNode() && GetNode()->IsNodeOfType(nsINode::eCONTENT);
}
bool IsElement() const
{
nsINode* node = GetNode();
return node && node->IsElement();
}
PRBool IsDocument() const
{
return GetNode() && GetNode()->IsNodeOfType(nsINode::eDOCUMENT);

View File

@ -254,6 +254,7 @@ ACCESSIBILITY_ATOM(aria_expanded, "aria-expanded")
ACCESSIBILITY_ATOM(aria_flowto, "aria-flowto")
ACCESSIBILITY_ATOM(aria_grabbed, "aria-grabbed")
ACCESSIBILITY_ATOM(aria_haspopup, "aria-haspopup")
ACCESSIBILITY_ATOM(aria_hidden, "aria-hidden")
ACCESSIBILITY_ATOM(aria_invalid, "aria-invalid")
ACCESSIBILITY_ATOM(aria_label, "aria-label")
ACCESSIBILITY_ATOM(aria_labelledby, "aria-labelledby")

View File

@ -528,6 +528,15 @@ nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
docAccessible->ContentRemoved(aContainer, aChild);
}
void
nsAccessibilityService::UpdateText(nsIPresShell* aPresShell,
nsIContent* aContent)
{
nsDocAccessible* document = GetDocAccessible(aPresShell->GetDocument());
if (document)
document->UpdateText(aContent);
}
void
nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell)
{
@ -555,8 +564,10 @@ nsAccessibilityService::RecreateAccessible(nsIPresShell* aPresShell,
nsIContent* aContent)
{
nsDocAccessible* document = GetDocAccessible(aPresShell->GetDocument());
if (document)
document->RecreateAccessible(aContent);
if (document) {
document->HandleNotification<nsDocAccessible, nsIContent>
(document, &nsDocAccessible::RecreateAccessible, aContent);
}
}
////////////////////////////////////////////////////////////////////////////////
@ -784,22 +795,19 @@ nsAccessible*
nsAccessibilityService::GetAccessible(nsINode* aNode)
{
nsDocAccessible* document = GetDocAccessible(aNode->GetOwnerDoc());
return document ? document->GetCachedAccessible(aNode) : nsnull;
return document ? document->GetAccessible(aNode) : nsnull;
}
nsAccessible*
nsAccessibilityService::GetAccessibleOrContainer(nsINode* aNode,
nsIWeakReference* aWeakShell)
{
if (!aNode || !aNode->IsInDoc())
if (!aNode)
return nsnull;
nsINode* currNode = aNode;
nsAccessible* accessible = nsnull;
while (!(accessible = GetAccessibleInWeakShell(currNode, aWeakShell)) &&
(currNode = currNode->GetNodeParent()));
return accessible;
// XXX: weak shell is ignored until multiple shell documents are supported.
nsDocAccessible* document = GetDocAccessible(aNode->GetOwnerDoc());
return document ? document->GetAccessibleOrContainer(aNode) : nsnull;
}
static PRBool HasRelatedContent(nsIContent *aContent)
@ -907,27 +915,24 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
// Attempt to create an accessible based on what we know.
nsRefPtr<nsAccessible> newAcc;
if (content->IsNodeOfType(nsINode::eTEXT)) {
// --- Create HTML for visible text frames ---
nsIFrame* f = weakFrame.GetFrame();
if (f && f->IsEmpty()) {
nsAutoString renderedWhitespace;
f->GetRenderedText(&renderedWhitespace, nsnull, nsnull, 0, 1);
if (renderedWhitespace.IsEmpty()) {
// Really empty -- nothing is rendered
if (aIsSubtreeHidden)
*aIsSubtreeHidden = true;
return nsnull;
}
}
if (weakFrame.IsAlive()) {
newAcc = weakFrame.GetFrame()->CreateAccessible();
if (docAcc->BindToDocument(newAcc, nsnull))
return newAcc.forget();
// Create accessible for visible text frames.
if (content->IsNodeOfType(nsINode::eTEXT)) {
nsAutoString text;
weakFrame->GetRenderedText(&text, nsnull, nsnull, 0, PR_UINT32_MAX);
if (text.IsEmpty()) {
if (aIsSubtreeHidden)
*aIsSubtreeHidden = true;
return nsnull;
}
newAcc = weakFrame->CreateAccessible();
if (docAcc->BindToDocument(newAcc, nsnull)) {
newAcc->AsTextLeaf()->SetText(text);
return newAcc.forget();
}
return nsnull;
}
@ -1206,7 +1211,7 @@ nsAccessibilityService::HasUniversalAriaProperty(nsIContent *aContent)
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_flowto) ||
nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_grabbed) ||
nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_haspopup) ||
// purposely ignore aria-hidden; since we use gecko for detecting this anyways
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_hidden) ||
nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_invalid) ||
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label) ||
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_labelledby) ||

View File

@ -118,6 +118,8 @@ public:
virtual void ContentRemoved(nsIPresShell* aPresShell, nsIContent* aContainer,
nsIContent* aChild);
virtual void UpdateText(nsIPresShell* aPresShell, nsIContent* aContent);
virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget);
virtual void PresShellDestroyed(nsIPresShell* aPresShell);

View File

@ -185,7 +185,7 @@ nsresult nsAccessible::QueryInterface(REFNSIID aIID, void** aInstancePtr)
nsAccessible::nsAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
nsAccessNodeWrap(aContent, aShell),
mParent(nsnull), mIndexInParent(-1), mChildrenFlags(eChildrenUninitialized),
mParent(nsnull), mIndexInParent(-1), mFlags(eChildrenUninitialized),
mIndexOfEmbeddedChild(-1), mRoleMapEntry(nsnull)
{
#ifdef NS_DEBUG_X
@ -2641,16 +2641,18 @@ nsAccessible::GetSelected(PRBool *aSelected)
}
nsresult
nsAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset, PRUint32 aLength)
void
nsAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset,
PRUint32 aLength)
{
// Return text representation of non-text accessible within hypertext
// accessible. Text accessible overrides this method to return enclosed text.
if (aStartOffset != 0)
return NS_OK;
if (aStartOffset != 0 || aLength == 0)
return;
nsIFrame *frame = GetFrame();
NS_ENSURE_STATE(frame);
if (!frame)
return;
if (frame->GetType() == nsAccessibilityAtoms::brFrame) {
aText += kForcedNewLineChar;
@ -2661,8 +2663,6 @@ nsAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset, PRUint32 aLe
} else {
aText += kEmbeddedObjectChar;
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
@ -2758,7 +2758,7 @@ nsAccessible::InvalidateChildren()
mEmbeddedObjCollector = nsnull;
mChildren.Clear();
mChildrenFlags = eChildrenUninitialized;
SetChildrenFlag(eChildrenUninitialized);
}
PRBool
@ -2771,7 +2771,7 @@ nsAccessible::AppendChild(nsAccessible* aChild)
return PR_FALSE;
if (!nsAccUtils::IsEmbeddedObject(aChild))
mChildrenFlags = eMixedChildren;
SetChildrenFlag(eMixedChildren);
aChild->BindToParent(this, mChildren.Length() - 1);
return PR_TRUE;
@ -2792,7 +2792,7 @@ nsAccessible::InsertChildAt(PRUint32 aIndex, nsAccessible* aChild)
}
if (nsAccUtils::IsText(aChild))
mChildrenFlags = eMixedChildren;
SetChildrenFlag(eMixedChildren);
mEmbeddedObjCollector = nsnull;
@ -2828,37 +2828,6 @@ nsAccessible::RemoveChild(nsAccessible* aChild)
return PR_TRUE;
}
nsAccessible*
nsAccessible::GetParent()
{
if (mParent)
return mParent;
if (IsDefunct())
return nsnull;
// XXX: mParent can be null randomly because supposedly we get layout
// notification and invalidate parent-child relations, this accessible stays
// unattached. This should gone after bug 572951.
NS_WARNING("Bad accessible tree!");
#ifdef DEBUG
nsDocAccessible *docAccessible = GetDocAccessible();
NS_ASSERTION(docAccessible, "No document accessible for valid accessible!");
#endif
nsAccessible* parent = GetAccService()->GetContainerAccessible(mContent,
mWeakShell);
NS_ASSERTION(parent, "No accessible parent for valid accessible!");
if (!parent)
return nsnull;
// Repair parent-child relations.
parent->EnsureChildren();
NS_ASSERTION(parent == mParent, "Wrong children repair!");
return parent;
}
nsAccessible*
nsAccessible::GetChildAt(PRUint32 aIndex)
{
@ -2892,10 +2861,8 @@ nsAccessible::GetIndexOf(nsAccessible* aChild)
}
PRInt32
nsAccessible::GetIndexInParent()
nsAccessible::GetIndexInParent() const
{
// XXX: call GetParent() to repair the tree if it's broken.
GetParent();
return mIndexInParent;
}
@ -2905,7 +2872,7 @@ nsAccessible::GetEmbeddedChildCount()
if (EnsureChildren())
return -1;
if (mChildrenFlags == eMixedChildren) {
if (IsChildrenFlag(eMixedChildren)) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ? mEmbeddedObjCollector->Count() : -1;
@ -2920,7 +2887,7 @@ nsAccessible::GetEmbeddedChildAt(PRUint32 aIndex)
if (EnsureChildren())
return nsnull;
if (mChildrenFlags == eMixedChildren) {
if (IsChildrenFlag(eMixedChildren)) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ?
@ -2936,7 +2903,7 @@ nsAccessible::GetIndexOfEmbeddedChild(nsAccessible* aChild)
if (EnsureChildren())
return -1;
if (mChildrenFlags == eMixedChildren) {
if (IsChildrenFlag(eMixedChildren)) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ?
@ -2954,8 +2921,7 @@ nsAccessible::IsHyperLink()
{
// Every embedded accessible within hypertext accessible implements
// hyperlink interface.
nsRefPtr<nsHyperTextAccessible> hyperText = do_QueryObject(GetParent());
return hyperText && nsAccUtils::IsEmbeddedObject(this);
return mParent && mParent->IsHyperText() && nsAccUtils::IsEmbeddedObject(this);
}
PRUint32
@ -2963,7 +2929,7 @@ nsAccessible::StartOffset()
{
NS_PRECONDITION(IsHyperLink(), "StartOffset is called not on hyper link!");
nsRefPtr<nsHyperTextAccessible> hyperText(do_QueryObject(GetParent()));
nsHyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nsnull;
return hyperText ? hyperText->GetChildOffset(this) : 0;
}
@ -2972,7 +2938,7 @@ nsAccessible::EndOffset()
{
NS_PRECONDITION(IsHyperLink(), "EndOffset is called on not hyper link!");
nsRefPtr<nsHyperTextAccessible> hyperText(do_QueryObject(GetParent()));
nsHyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nsnull;
return hyperText ? (hyperText->GetChildOffset(this) + 1) : 0;
}
@ -3180,12 +3146,12 @@ nsAccessible::CacheChildren()
}
void
nsAccessible::TestChildCache(nsAccessible *aCachedChild)
nsAccessible::TestChildCache(nsAccessible* aCachedChild) const
{
#ifdef DEBUG
PRInt32 childCount = mChildren.Length();
if (childCount == 0) {
NS_ASSERTION(mChildrenFlags == eChildrenUninitialized,
NS_ASSERTION(IsChildrenFlag(eChildrenUninitialized),
"No children but initialized!");
return;
}
@ -3203,19 +3169,19 @@ nsAccessible::TestChildCache(nsAccessible *aCachedChild)
}
// nsAccessible public
PRBool
bool
nsAccessible::EnsureChildren()
{
if (IsDefunct()) {
mChildrenFlags = eChildrenUninitialized;
return PR_TRUE;
SetChildrenFlag(eChildrenUninitialized);
return true;
}
if (mChildrenFlags != eChildrenUninitialized)
return PR_FALSE;
if (!IsChildrenFlag(eChildrenUninitialized))
return false;
// State is embedded children until text leaf accessible is appended.
mChildrenFlags = eEmbeddedChildren; // Prevent reentry
SetChildrenFlag(eEmbeddedChildren); // Prevent reentry
// Notify the document about caching status.
nsDocAccessible* document = GetDocAccessible();
@ -3227,7 +3193,7 @@ nsAccessible::EnsureChildren()
if (document)
document->NotifyOfCachingEnd(this);
return PR_FALSE;
return false;
}
nsAccessible*

View File

@ -53,11 +53,13 @@
#include "nsTArray.h"
#include "nsRefPtrHashtable.h"
class AccEvent;
class AccGroupInfo;
class EmbeddedObjCollector;
class nsAccessible;
class AccEvent;
class nsHyperTextAccessible;
struct nsRoleMapEntry;
class nsTextAccessible;
struct nsRect;
class nsIContent;
@ -237,7 +239,7 @@ public:
/**
* Cache children if necessary. Return true if the accessible is defunct.
*/
PRBool EnsureChildren();
bool EnsureChildren();
/**
* Set the child count to -1 (unknown) and null out cached child pointers.
@ -260,7 +262,7 @@ public:
/**
* Return parent accessible.
*/
virtual nsAccessible* GetParent();
nsAccessible* GetParent() const { return mParent; }
/**
* Return child accessible at the given index.
@ -280,7 +282,7 @@ public:
/**
* Return index in parent accessible.
*/
virtual PRInt32 GetIndexInParent();
virtual PRInt32 GetIndexInParent() const;
/**
* Return true if accessible has children;
@ -305,7 +307,6 @@ public:
/**
* Return cached accessible of parent-child relatives.
*/
nsAccessible* GetCachedParent() const { return mParent; }
nsAccessible* GetCachedNextSibling() const
{
return mParent ?
@ -318,8 +319,9 @@ public:
}
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; }
inline bool AreChildrenCached() const
{ return !IsChildrenFlag(eChildrenUninitialized); }
bool IsBoundToParent() const { return !!mParent; }
//////////////////////////////////////////////////////////////////////////////
// Miscellaneous methods
@ -339,18 +341,29 @@ public:
* Returns text of accessible if accessible has text role otherwise empty
* string.
*
* @param aText returned text of the accessible
* @param aStartOffset start offset inside of the accesible
* @param aLength required lenght of text
* @param aText [in] returned text of the accessible
* @param aStartOffset [in, optional] start offset inside of the accessible,
* if missed entire text is appended
* @param aLength [in, optional] required length of text, if missed
* then text form start offset till the end is appended
*/
virtual nsresult AppendTextTo(nsAString& aText, PRUint32 aStartOffset,
PRUint32 aLength);
virtual void AppendTextTo(nsAString& aText, PRUint32 aStartOffset = 0,
PRUint32 aLength = PR_UINT32_MAX);
/**
* Assert if child not in parent's cache if the cache was initialized at this
* point.
*/
void TestChildCache(nsAccessible *aCachedChild);
void TestChildCache(nsAccessible* aCachedChild) const;
//////////////////////////////////////////////////////////////////////////////
// Downcasting
inline bool IsHyperText() const { return mFlags & eHyperTextAccessible; }
nsHyperTextAccessible* AsHyperText();
inline bool IsTextLeaf() const { return mFlags & eTextLeafAccessible; }
nsTextAccessible* AsTextLeaf();
//////////////////////////////////////////////////////////////////////////////
// HyperLinkAccessible
@ -466,6 +479,36 @@ protected:
virtual nsAccessible* GetSiblingAtOffset(PRInt32 aOffset,
nsresult *aError = nsnull);
/**
* Flags used to describe the state and type of children.
*/
enum ChildrenFlags {
eChildrenUninitialized = 0, // children aren't initialized
eMixedChildren = 1 << 0, // text leaf children are presented
eEmbeddedChildren = 1 << 1 // all children are embedded objects
};
/**
* Return true if the children flag is set.
*/
inline bool IsChildrenFlag(ChildrenFlags aFlag) const
{ return (mFlags & kChildrenFlagsMask) == aFlag; }
/**
* Set children flag.
*/
inline void SetChildrenFlag(ChildrenFlags aFlag)
{ mFlags = (mFlags & ~kChildrenFlagsMask) | aFlag; }
/**
* Flags describing the accessible itself.
* @note keep these flags in sync with ChildrenFlags
*/
enum AccessibleTypes {
eHyperTextAccessible = 1 << 2,
eTextLeafAccessible = 1 << 3
};
//////////////////////////////////////////////////////////////////////////////
// Miscellaneous helpers
@ -580,12 +623,10 @@ protected:
nsTArray<nsRefPtr<nsAccessible> > mChildren;
PRInt32 mIndexInParent;
enum ChildrenFlags {
eChildrenUninitialized = 0x00,
eMixedChildren = 0x01,
eEmbeddedChildren = 0x02
};
ChildrenFlags mChildrenFlags;
static const PRUint32 kChildrenFlagsMask =
eChildrenUninitialized | eMixedChildren | eEmbeddedChildren;
PRUint32 mFlags;
nsAutoPtr<EmbeddedObjCollector> mEmbeddedObjCollector;
PRInt32 mIndexOfEmbeddedChild;

View File

@ -410,12 +410,6 @@ nsApplicationAccessible::InvalidateChildren()
// and RemoveChild() method calls.
}
nsAccessible*
nsApplicationAccessible::GetParent()
{
return nsnull;
}
////////////////////////////////////////////////////////////////////////////////
// nsAccessible protected methods

View File

@ -131,8 +131,6 @@ public:
virtual void InvalidateChildren();
virtual nsAccessible* GetParent();
protected:
// nsAccessible

View File

@ -269,7 +269,7 @@ nsCaretAccessible::NormalSelectionChanged(nsISelection* aSelection)
return; // No selection
}
nsRefPtr<nsHyperTextAccessible> textAcc =
nsHyperTextAccessible* textAcc =
nsAccUtils::GetTextAccessibleFromSelection(aSelection);
if (!textAcc)
return;
@ -287,7 +287,7 @@ nsCaretAccessible::NormalSelectionChanged(nsISelection* aSelection)
}
mLastCaretOffset = caretOffset;
mLastTextAccessible.swap(textAcc);
mLastTextAccessible = textAcc;
nsRefPtr<AccEvent> event =
new AccCaretMoveEvent(mLastTextAccessible->GetNode());
@ -304,7 +304,7 @@ nsCaretAccessible::SpellcheckSelectionChanged(nsISelection* aSelection)
// misspelled word). If spellchecking is disabled (for example,
// @spellcheck="false" on html:body) then we won't fire any event.
nsRefPtr<nsHyperTextAccessible> textAcc =
nsHyperTextAccessible* textAcc =
nsAccUtils::GetTextAccessibleFromSelection(aSelection);
if (!textAcc)
return;

View File

@ -588,8 +588,8 @@ NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor)
}
// nsDocAccessible public method
nsAccessible *
nsDocAccessible::GetCachedAccessible(nsINode *aNode)
nsAccessible*
nsDocAccessible::GetAccessible(nsINode* aNode) const
{
nsAccessible* accessible = mNodeToAccessibleMap.Get(aNode);
@ -599,7 +599,7 @@ nsDocAccessible::GetCachedAccessible(nsINode *aNode)
if (GetNode() != aNode)
return nsnull;
accessible = this;
accessible = const_cast<nsDocAccessible*>(this);
}
#ifdef DEBUG
@ -607,7 +607,7 @@ nsDocAccessible::GetCachedAccessible(nsINode *aNode)
// It will assert if not all the children were created
// when they were first cached, and no invalidation
// ever corrected parent accessible's child cache.
nsAccessible* parent(accessible->GetCachedParent());
nsAccessible* parent(accessible->GetParent());
if (parent)
parent->TestChildCache(accessible);
#endif
@ -942,7 +942,7 @@ nsDocAccessible::AttributeWillChange(nsIDocument *aDocument,
// elements.
if (aModType == nsIDOMMutationEvent::MODIFICATION ||
aModType == nsIDOMMutationEvent::REMOVAL) {
nsAccessible* accessible = GetCachedAccessible(aElement);
nsAccessible* accessible = GetAccessible(aElement);
if (accessible)
RemoveDependentIDsFor(accessible, aAttribute);
}
@ -967,7 +967,7 @@ nsDocAccessible::AttributeChanged(nsIDocument *aDocument,
// (which is treated as attribute change on this document accessible).
// Note: we don't bail if all the content hasn't finished loading because
// these attributes are changing for a loaded part of the content.
nsAccessible* accessible = GetCachedAccessible(aElement);
nsAccessible* accessible = GetAccessible(aElement);
if (!accessible && (mContent != aElement))
return;
@ -1138,7 +1138,8 @@ nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
// For aria drag and drop changes we fire a generic attribute change event;
// at least until native API comes up with a more meaningful event.
if (aAttribute == nsAccessibilityAtoms::aria_grabbed ||
aAttribute == nsAccessibilityAtoms::aria_dropeffect) {
aAttribute == nsAccessibilityAtoms::aria_dropeffect ||
aAttribute == nsAccessibilityAtoms::aria_hidden) {
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED,
aContent);
}
@ -1247,14 +1248,12 @@ void nsDocAccessible::CharacterDataWillChange(nsIDocument *aDocument,
nsIContent* aContent,
CharacterDataChangeInfo* aInfo)
{
FireTextChangeEventForText(aContent, aInfo, PR_FALSE);
}
void nsDocAccessible::CharacterDataChanged(nsIDocument *aDocument,
nsIContent* aContent,
CharacterDataChangeInfo* aInfo)
{
FireTextChangeEventForText(aContent, aInfo, PR_TRUE);
}
void
@ -1308,16 +1307,16 @@ nsDocAccessible::GetNativeWindow() const
}
nsAccessible*
nsDocAccessible::GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID)
nsDocAccessible::GetAccessibleByUniqueIDInSubtree(void* aUniqueID)
{
nsAccessible* child = GetCachedAccessibleByUniqueID(aUniqueID);
nsAccessible* child = GetAccessibleByUniqueID(aUniqueID);
if (child)
return child;
PRUint32 childDocCount = mChildDocuments.Length();
for (PRUint32 childDocIdx= 0; childDocIdx < childDocCount; childDocIdx++) {
nsDocAccessible* childDocument = mChildDocuments.ElementAt(childDocIdx);
child = childDocument->GetCachedAccessibleByUniqueIDInSubtree(aUniqueID);
child = childDocument->GetAccessibleByUniqueIDInSubtree(aUniqueID);
if (child)
return child;
}
@ -1325,6 +1324,20 @@ nsDocAccessible::GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID)
return nsnull;
}
nsAccessible*
nsDocAccessible::GetAccessibleOrContainer(nsINode* aNode)
{
if (!aNode || !aNode->IsInDoc())
return nsnull;
nsINode* currNode = aNode;
nsAccessible* accessible = nsnull;
while (!(accessible = GetAccessible(currNode)) &&
(currNode = currNode->GetNodeParent()));
return accessible;
}
bool
nsDocAccessible::BindToDocument(nsAccessible* aAccessible,
nsRoleMapEntry* aRoleMapEntry)
@ -1354,7 +1367,9 @@ nsDocAccessible::BindToDocument(nsAccessible* aAccessible,
}
aAccessible->SetRoleMapEntry(aRoleMapEntry);
AddDependentIDsFor(aAccessible);
if (aAccessible->IsElement())
AddDependentIDsFor(aAccessible);
return true;
}
@ -1390,8 +1405,7 @@ nsDocAccessible::ContentInserted(nsIContent* aContainerNode,
// Update the whole tree of this document accessible when the container is
// null (document element is inserted or removed).
nsAccessible* container = aContainerNode ?
GetAccService()->GetAccessibleOrContainer(aContainerNode, mWeakShell) :
this;
GetAccessibleOrContainer(aContainerNode) : this;
mNotificationController->ScheduleContentInsertion(container,
aStartChildNode,
@ -1406,70 +1420,28 @@ nsDocAccessible::ContentRemoved(nsIContent* aContainerNode,
// Update the whole tree of this document accessible when the container is
// null (document element is removed).
nsAccessible* container = aContainerNode ?
GetAccService()->GetAccessibleOrContainer(aContainerNode, mWeakShell) :
this;
GetAccessibleOrContainer(aContainerNode) : this;
UpdateTree(container, aChildNode, PR_FALSE);
}
void
nsDocAccessible::RecreateAccessible(nsINode* aNode)
nsDocAccessible::RecreateAccessible(nsIContent* aContent)
{
// XXX: we shouldn't recreate whole accessible subtree that happens when
// hide event is handled, instead we should subclass hide and show events
// to handle them separately and implement their coalescence with normal hide
// and show events.
// XXX: we shouldn't recreate whole accessible subtree, instead we should
// subclass hide and show events to handle them separately and implement their
// coalescence with normal hide and show events. Note, in this case they
// should be coalesced with normal show/hide events.
nsAccessible* parent = nsnull;
// Check if the node is in DOM still.
nsIContent* parentContent = aContent->GetParent();
if (parentContent && parentContent->IsInDoc()) {
nsAccessible* container = GetAccessibleOrContainer(parentContent);
// Fire hide event for old accessible.
nsAccessible* oldAccessible =
GetAccService()->GetAccessibleInWeakShell(aNode, mWeakShell);
if (oldAccessible) {
parent = oldAccessible->GetParent();
nsRefPtr<AccEvent> hideEvent = new AccHideEvent(oldAccessible, aNode);
if (hideEvent)
FireDelayedAccessibleEvent(hideEvent);
// Unbind old accessible from tree.
parent->RemoveChild(oldAccessible);
if (oldAccessible->IsPrimaryForNode() &&
mNodeToAccessibleMap.Get(oldAccessible->GetNode()) == oldAccessible)
mNodeToAccessibleMap.Remove(oldAccessible->GetNode());
} else {
// Not accessible node may not have container accessible if we recreate
// an accessible asynchronously.
// XXX: asynchronous RecreateAccessible notifications should be coalesced
// with accessible tree mutation notifications. We could trigger
// ContentRemoved/ContentInserted pair for that but it moves us away from
// the idea to not recreate the whole subtree.
parent = GetAccService()->GetContainerAccessible(aNode, mWeakShell);
if (!parent)
return;
}
// Get new accessible and fire show event.
parent->UpdateChildren();
nsAccessible* newAccessible =
GetAccService()->GetAccessibleInWeakShell(aNode, mWeakShell);
if (newAccessible) {
nsRefPtr<AccEvent> showEvent = new AccShowEvent(newAccessible, aNode);
if (showEvent)
FireDelayedAccessibleEvent(showEvent);
}
// Fire reorder event.
if (oldAccessible || newAccessible) {
nsRefPtr<AccEvent> reorderEvent =
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, parent->GetNode(),
eAutoDetect, AccEvent::eCoalesceFromSameSubtree);
if (reorderEvent)
FireDelayedAccessibleEvent(reorderEvent);
// Remove and reinsert.
UpdateTree(container, aContent, PR_FALSE);
container->UpdateChildren();
UpdateTree(container, aContent, PR_TRUE);
}
}
@ -1491,8 +1463,7 @@ nsDocAccessible::NotifyOfCachingEnd(nsAccessible* aAccessible)
// invalidation list.
for (PRUint32 idx = 0; idx < mInvalidationList.Length(); idx++) {
nsIContent* content = mInvalidationList[idx];
nsAccessible* container =
GetAccService()->GetContainerAccessible(content, mWeakShell);
nsAccessible* container = GetContainerAccessible(content);
// Make sure we keep children updated. While we're inside of caching loop
// then we must exist it with cached children.
@ -1573,7 +1544,7 @@ nsDocAccessible::AddDependentIDsFor(nsAccessible* aRelProvider,
// children invalidation (this happens immediately after the caching
// is finished).
nsIContent* dependentContent = iter.GetElem(id);
if (dependentContent && !GetCachedAccessible(dependentContent)) {
if (dependentContent && !HasAccessible(dependentContent)) {
mInvalidationList.AppendElement(dependentContent);
}
}
@ -1639,7 +1610,7 @@ nsDocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
// Recreate the accessible when role is changed because we might require a
// different accessible class for the new role or the accessible may expose
// a different sets of interfaces (COM restriction).
HandleNotification<nsDocAccessible, nsINode>
HandleNotification<nsDocAccessible, nsIContent>
(this, &nsDocAccessible::RecreateAccessible, aElement);
return true;
@ -1647,10 +1618,13 @@ nsDocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
if (aAttribute == nsAccessibilityAtoms::href ||
aAttribute == nsAccessibilityAtoms::onclick) {
// Not worth the expense to ensure which namespace these are in
// It doesn't kill use to recreate the accessible even if the attribute was used
// in the wrong namespace or an element that doesn't support it
HandleNotification<nsDocAccessible, nsINode>
// Not worth the expense to ensure which namespace these are in. It doesn't
// kill use to recreate the accessible even if the attribute was used in
// the wrong namespace or an element that doesn't support it.
// Recreate accessible asynchronously to allow the content to handle
// the attribute change.
mNotificationController->ScheduleNotification<nsDocAccessible, nsIContent>
(this, &nsDocAccessible::RecreateAccessible, aElement);
return true;
@ -1661,7 +1635,7 @@ nsDocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
// This affects whether the accessible supports SelectAccessible.
// COM says we cannot change what interfaces are supported on-the-fly,
// so invalidate this object. A new one will be created on demand.
HandleNotification<nsDocAccessible, nsINode>
HandleNotification<nsDocAccessible, nsIContent>
(this, &nsDocAccessible::RecreateAccessible, aElement);
return true;
@ -1670,78 +1644,6 @@ nsDocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
return false;
}
void
nsDocAccessible::FireValueChangeForTextFields(nsAccessible *aAccessible)
{
if (aAccessible->Role() != nsIAccessibleRole::ROLE_ENTRY)
return;
// Dependent value change event for text changes in textfields
nsRefPtr<AccEvent> valueChangeEvent =
new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible,
eAutoDetect, AccEvent::eRemoveDupes);
FireDelayedAccessibleEvent(valueChangeEvent);
}
void
nsDocAccessible::FireTextChangeEventForText(nsIContent *aContent,
CharacterDataChangeInfo* aInfo,
PRBool aIsInserted)
{
if (!IsContentLoaded())
return;
PRInt32 contentOffset = aInfo->mChangeStart;
PRUint32 contentLength = aIsInserted ?
aInfo->mReplaceLength: // text has been added
aInfo->mChangeEnd - contentOffset; // text has been removed
if (contentLength == 0)
return;
nsAccessible *accessible = GetAccService()->GetAccessible(aContent);
if (!accessible)
return;
nsRefPtr<nsHyperTextAccessible> textAccessible =
do_QueryObject(accessible->GetParent());
if (!textAccessible)
return;
// Get offset within hypertext accessible and invalidate cached offsets after
// this child accessible.
PRInt32 offset = textAccessible->GetChildOffset(accessible, PR_TRUE);
// Get added or removed text.
nsIFrame* frame = aContent->GetPrimaryFrame();
if (!frame)
return;
PRUint32 textOffset = 0;
nsresult rv = textAccessible->ContentToRenderedOffset(frame, contentOffset,
&textOffset);
if (NS_FAILED(rv))
return;
nsAutoString text;
rv = accessible->AppendTextTo(text, textOffset, contentLength);
if (NS_FAILED(rv))
return;
if (text.IsEmpty())
return;
// Normally we only fire delayed events created from the node, not an
// accessible object. See the AccTextChangeEvent constructor for details
// about this exceptional case.
nsRefPtr<AccEvent> event =
new AccTextChangeEvent(textAccessible, offset + textOffset, text,
aIsInserted);
FireDelayedAccessibleEvent(event);
FireValueChangeForTextFields(textAccessible);
}
// nsDocAccessible public member
nsresult
nsDocAccessible::FireDelayedAccessibleEvent(PRUint32 aEventType, nsINode *aNode,
@ -1823,8 +1725,7 @@ nsDocAccessible::ProcessAnchorJump(nsIContent* aTargetNode)
{
// If the jump target is not accessible then fire an event for nearest
// accessible in parent chain.
nsAccessible* target = GetAccService()->GetAccessibleOrContainer(aTargetNode,
mWeakShell);
nsAccessible* target = GetAccessibleOrContainer(aTargetNode);
if (!target)
return;
@ -1840,7 +1741,7 @@ nsDocAccessible::ProcessContentInserted(nsAccessible* aContainer,
const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent)
{
// Process the notification if the container accessible is still in tree.
if (!GetCachedAccessible(aContainer->GetNode()))
if (!HasAccessible(aContainer->GetNode()))
return;
if (aContainer == this) {
@ -1871,8 +1772,7 @@ nsDocAccessible::ProcessContentInserted(nsAccessible* aContainer,
// means there's no container.
for (PRUint32 idx = 0; idx < aInsertedContent->Length(); idx++) {
nsAccessible* directContainer =
GetAccService()->GetContainerAccessible(aInsertedContent->ElementAt(idx),
mWeakShell);
GetContainerAccessible(aInsertedContent->ElementAt(idx));
if (directContainer)
UpdateTree(directContainer, aInsertedContent->ElementAt(idx), PR_TRUE);
}
@ -1883,8 +1783,7 @@ nsDocAccessible::UpdateTree(nsAccessible* aContainer, nsIContent* aChildNode,
PRBool aIsInsert)
{
PRUint32 updateFlags =
UpdateTreeInternal(aContainer, aChildNode, aChildNode->GetNextSibling(),
aIsInsert);
UpdateTreeInternal(aChildNode, aChildNode->GetNextSibling(), aIsInsert);
// Content insertion/removal is not cause of accessible tree change.
if (updateFlags == eNoAccessible)
@ -1927,8 +1826,7 @@ nsDocAccessible::UpdateTree(nsAccessible* aContainer, nsIContent* aChildNode,
}
PRUint32
nsDocAccessible::UpdateTreeInternal(nsAccessible* aContainer,
nsIContent* aStartNode,
nsDocAccessible::UpdateTreeInternal(nsIContent* aStartNode,
nsIContent* aEndNode,
PRBool aIsInsert)
{
@ -1942,11 +1840,11 @@ nsDocAccessible::UpdateTreeInternal(nsAccessible* aContainer,
if (aIsInsert && !node->GetPrimaryFrame())
continue;
nsAccessible* accessible = GetCachedAccessible(node);
nsAccessible* accessible = GetAccessible(node);
if (!accessible) {
updateFlags |= UpdateTreeInternal(aContainer, node->GetFirstChild(),
nsnull, aIsInsert);
updateFlags |= UpdateTreeInternal(node->GetFirstChild(), nsnull,
aIsInsert);
continue;
}
@ -2044,6 +1942,9 @@ nsDocAccessible::UncacheChildrenInSubtree(nsAccessible* aRoot)
for (PRUint32 idx = 0; idx < count; idx++)
UncacheChildrenInSubtree(aRoot->GetCachedChildAt(idx));
if (aRoot->IsTextLeaf())
mNotificationController->CancelTextUpdate(aRoot->GetContent());
if (aRoot->IsPrimaryForNode() &&
mNodeToAccessibleMap.Get(aRoot->GetNode()) == aRoot)
mNodeToAccessibleMap.Remove(aRoot->GetNode());

View File

@ -230,7 +230,15 @@ public:
*
* @return the accessible object
*/
nsAccessible* GetCachedAccessible(nsINode* aNode);
nsAccessible* GetAccessible(nsINode* aNode) const;
/**
* Return whether the given DOM node has an accessible or not.
*/
inline bool HasAccessible(nsINode* aNode)
{
return GetAccessible(aNode);
}
/**
* Return the cached accessible by the given unique ID within this document.
@ -239,7 +247,7 @@ public:
*
* @param aUniqueID [in] the unique ID used to cache the node.
*/
nsAccessible* GetCachedAccessibleByUniqueID(void* aUniqueID)
inline nsAccessible* GetAccessibleByUniqueID(void* aUniqueID)
{
return UniqueID() == aUniqueID ?
this : mAccessibleCache.GetWeak(aUniqueID);
@ -249,7 +257,21 @@ public:
* Return the cached accessible by the given unique ID looking through
* this and nested documents.
*/
nsAccessible* GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID);
nsAccessible* GetAccessibleByUniqueIDInSubtree(void* aUniqueID);
/**
* Return an accessible for the given DOM node or container accessible if
* the node is not accessible.
*/
nsAccessible* GetAccessibleOrContainer(nsINode* aNode);
/**
* Return a container accessible for the given DOM node.
*/
inline nsAccessible* GetContainerAccessible(nsINode* aNode)
{
return aNode ? GetAccessibleOrContainer(aNode->GetNodeParent()) : nsnull;
}
/**
* Return true if the given ID is referred by relation attribute.
@ -287,10 +309,21 @@ public:
*/
void ContentRemoved(nsIContent* aContainerNode, nsIContent* aChildNode);
/**
* Updates accessible tree when rendered text is changed.
*/
inline void UpdateText(nsIContent* aTextNode)
{
NS_ASSERTION(mNotificationController, "The document was shut down!");
if (mNotificationController)
mNotificationController->ScheduleTextUpdate(aTextNode);
}
/**
* Recreate an accessible, results in hide/show events pair.
*/
void RecreateAccessible(nsINode* aNode);
void RecreateAccessible(nsIContent* aContent);
/**
* Used to notify the document that the accessible caching is started or
@ -382,25 +415,6 @@ protected:
*/
void ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute);
/**
* Fire text changed event for character data changed. The method is used
* from nsIMutationObserver methods.
*
* @param aContent the text node holding changed data
* @param aInfo info structure describing how the data was changed
* @param aIsInserted the flag pointed whether removed or inserted
* characters should be cause of event
*/
void FireTextChangeEventForText(nsIContent *aContent,
CharacterDataChangeInfo* aInfo,
PRBool aIsInserted);
/**
* Fire a value change event for the the given accessible if it is a text
* field (has a ROLE_ENTRY).
*/
void FireValueChangeForTextFields(nsAccessible *aAccessible);
/**
* Process the event when the queue of pending events is untwisted. Fire
* accessible events as result of the processing.
@ -434,8 +448,7 @@ protected:
eAlertAccessible = 2
};
PRUint32 UpdateTreeInternal(nsAccessible* aContainer,
nsIContent* aStartNode,
PRUint32 UpdateTreeInternal(nsIContent* aStartNode,
nsIContent* aEndNode,
PRBool aIsInsert);

View File

@ -190,7 +190,7 @@ nsOuterDocAccessible::InvalidateChildren()
// then allow nsAccDocManager to handle this case since the document
// accessible is created and appended as a child when it's requested.
mChildrenFlags = eChildrenUninitialized;
SetChildrenFlag(eChildrenUninitialized);
}
PRBool

View File

@ -39,7 +39,7 @@
#include "nsRelUtils.h"
#include "nsAccessibilityService.h"
#include "nsAccessNode.h"
#include "nsAccessible.h"
#include "nsCoreUtils.h"
#include "nsIDOMDocument.h"

View File

@ -48,6 +48,7 @@ nsTextAccessible::
nsTextAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
nsLinkableAccessible(aContent, aShell)
{
mFlags |= eTextLeafAccessible;
}
PRUint32
@ -56,13 +57,11 @@ nsTextAccessible::NativeRole()
return nsIAccessibleRole::ROLE_TEXT_LEAF;
}
nsresult
nsTextAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset, PRUint32 aLength)
void
nsTextAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset,
PRUint32 aLength)
{
nsIFrame *frame = GetFrame();
if (!frame) return NS_ERROR_FAILURE;//NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
return frame->GetRenderedText(&aText, nsnull, nsnull, aStartOffset, aLength);
aText.Append(Substring(mText, aStartOffset, aLength));
}
void

View File

@ -51,15 +51,31 @@ public:
// nsAccessible
virtual PRUint32 NativeRole();
virtual nsresult AppendTextTo(nsAString& aText, PRUint32 aStartOffset,
PRUint32 aLength);
virtual void AppendTextTo(nsAString& aText, PRUint32 aStartOffset = 0,
PRUint32 aLength = PR_UINT32_MAX);
// nsTextAccessible
void SetText(const nsAString& aText) { mText = aText; }
const nsString& Text() const { return mText; }
protected:
// nsAccessible
virtual void CacheChildren();
protected:
nsString mText;
};
////////////////////////////////////////////////////////////////////////////////
// nsAccessible downcast method
inline nsTextAccessible*
nsAccessible::AsTextLeaf()
{
return mFlags & eTextLeafAccessible ?
static_cast<nsTextAccessible*>(this) : nsnull;
}
#endif

View File

@ -67,8 +67,8 @@ NS_IMETHODIMP
nsHTMLTextAccessible::GetName(nsAString& aName)
{
// Text node, ARIA can't be used.
aName.Truncate();
return AppendTextTo(aName, 0, PR_UINT32_MAX);
aName = mText;
return NS_OK;
}
PRUint32
@ -390,7 +390,7 @@ nsHTMLListBulletAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraS
return NS_OK;
}
nsresult
void
nsHTMLListBulletAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset,
PRUint32 aLength)
{
@ -405,7 +405,6 @@ nsHTMLListBulletAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset
aText += Substring(bulletText, aStartOffset, aLength);
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -142,8 +142,8 @@ public:
// nsAccessible
virtual PRUint32 NativeRole();
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
virtual nsresult AppendTextTo(nsAString& aText, PRUint32 aStartOffset,
PRUint32 aLength);
virtual void AppendTextTo(nsAString& aText, PRUint32 aStartOffset = 0,
PRUint32 aLength = PR_UINT32_MAX);
protected:
// XXX: Ideally we'd get the bullet text directly from the bullet frame via

View File

@ -80,6 +80,7 @@ nsHyperTextAccessible::
nsHyperTextAccessible(nsIContent *aNode, nsIWeakReference *aShell) :
nsAccessibleWrap(aNode, aShell)
{
mFlags |= eHyperTextAccessible;
}
NS_IMPL_ADDREF_INHERITED(nsHyperTextAccessible, nsAccessibleWrap)

View File

@ -397,5 +397,16 @@ private:
NS_DEFINE_STATIC_IID_ACCESSOR(nsHyperTextAccessible,
NS_HYPERTEXTACCESSIBLE_IMPL_CID)
////////////////////////////////////////////////////////////////////////////////
// nsAccessible downcasting method
inline nsHyperTextAccessible*
nsAccessible::AsHyperText()
{
return mFlags & eHyperTextAccessible ?
static_cast<nsHyperTextAccessible*>(this) : nsnull;
}
#endif // _nsHyperTextAccessible_H_

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 GetCachedAccessibleByUniqueIDInSubtree(uniqueID);
return GetAccessibleByUniqueIDInSubtree(uniqueID);
}
return nsAccessibleWrap::GetXPAccessibleFor(aVarChild);

View File

@ -987,6 +987,12 @@ nsXULTreeItemAccessibleBase::GetStateInternal(PRUint32 *aState,
return NS_OK;
}
PRInt32
nsXULTreeItemAccessibleBase::GetIndexInParent() const
{
return mParent ? mParent->GetCachedChildCount() + mRow : -1;
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeItemAccessibleBase: nsAccessible protected methods

View File

@ -208,8 +208,7 @@ public:
// nsAccessible
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
virtual PRInt32 GetIndexInParent()
{ return mParent ? mParent->GetCachedChildCount() + mRow : -1; }
virtual PRInt32 GetIndexInParent() const;
// nsXULTreeItemAccessibleBase
NS_DECLARE_STATIC_IID_ACCESSOR(NS_XULTREEITEMBASEACCESSIBLE_IMPL_CID)

View File

@ -1205,6 +1205,12 @@ nsXULTreeGridCellAccessible::GetStateInternal(PRUint32 *aStates,
return NS_OK;
}
PRInt32
nsXULTreeGridCellAccessible::GetIndexInParent() const
{
return GetColumnIndex();
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeGridCellAccessible: public implementation

View File

@ -164,7 +164,7 @@ public:
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
virtual PRUint32 NativeRole();
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
virtual PRInt32 GetIndexInParent() { return GetColumnIndex(); }
virtual PRInt32 GetIndexInParent() const;
// nsXULTreeGridCellAccessible
NS_DECLARE_STATIC_IID_ACCESSOR(NS_XULTREEGRIDCELLACCESSIBLE_IMPL_CID)

View File

@ -32,8 +32,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=558036
testAttrs("checkedOption", {"checkable" : "true"}, true);
testAttrs("checkedRadio", {"checkable" : "true"}, true);
testAttrs("checkedTreeitem", {"checkable" : "true"}, true);
testAttrs("grabbed", {"grabbed" : "true"}, true);
testAttrs("dropeffect", {"dropeffect" : "copy"}, true);
testAttrs("grabbed", {"grabbed" : "true"}, true);
testAttrs("hidden", {"hidden" : "true"}, true);
testAttrs("sortAscending", {"sort" : "ascending"}, true);
testAttrs("sortDescending", {"sort" : "descending"}, true);
testAttrs("sortNone", {"sort" : "none"}, true);
@ -134,8 +135,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=558036
<div id="checkedOption" role="option" aria-checked="true"></div>
<div id="checkedRadio" role="radio" aria-checked="true"></div>
<div id="checkedTreeitem" role="treeitem" aria-checked="true"></div>
<div id="grabbed" aria-grabbed="true"></div>
<div id="dropeffect" aria-dropeffect="copy"></div>
<div id="grabbed" aria-grabbed="true"></div>
<div id="hidden" aria-hidden="true"></div>
<div id="sortAscending" role="columnheader" aria-sort="ascending"></div>
<div id="sortDescending" role="columnheader" aria-sort="descending"></div>
<div id="sortNone" role="columnheader" aria-sort="none"></div>

View File

@ -36,7 +36,7 @@ function editableTextTest(aID)
/**
* setTextContents test.
*/
this.setTextContents = function setTextContents(aStr)
this.setTextContents = function setTextContents(aStr, aResValue)
{
var testID = "setTextContents '" + aStr + "' for " + prettyName(aID);
@ -46,15 +46,15 @@ function editableTextTest(aID)
acc.setTextContents(aStr);
}
this.sheduleTest(aID, null, [0, aStr.length, aStr],
setTextContentsInvoke, getValueChecker(aID, aResValue),
testID);
this.scheduleTest(aID, null, [0, aStr.length, aStr],
setTextContentsInvoke, getValueChecker(aID, aResValue),
testID);
}
/**
* insertText test.
*/
this.insertText = function insertText(aStr, aPos, aResStr)
this.insertText = function insertText(aStr, aPos, aResStr, aResPos)
{
var testID = "insertText '" + aStr + "' at " + aPos + " for " +
prettyName(aID);
@ -65,7 +65,8 @@ function editableTextTest(aID)
acc.insertText(aStr, aPos);
}
this.scheduleTest(aID, null, [aPos, aPos + aStr.length, aStr],
var resPos = (aResPos != undefined) ? aResPos : aPos;
this.scheduleTest(aID, null, [resPos, resPos + aStr.length, aStr],
insertTextInvoke, getValueChecker(aID, aResStr), testID);
}
@ -110,7 +111,8 @@ function editableTextTest(aID)
/**
* cutText test.
*/
this.cutText = function cutText(aStartPos, aEndPos, aResStr)
this.cutText = function cutText(aStartPos, aEndPos, aResStr,
aResStartPos, aResEndPos)
{
var testID = "cutText from " + aStartPos + " to " + aEndPos + " for " +
prettyName(aID);
@ -121,7 +123,9 @@ function editableTextTest(aID)
acc.cutText(aStartPos, aEndPos);
}
this.scheduleTest(aID, [aStartPos, aEndPos, getTextFromClipboard], null,
var resStartPos = (aResStartPos != undefined) ? aResStartPos : aStartPos;
var resEndPos = (aResEndPos != undefined) ? aResEndPos : aEndPos;
this.scheduleTest(aID, [resStartPos, resEndPos, getTextFromClipboard], null,
cutTextInvoke, getValueChecker(aID, aResStr), testID);
}

View File

@ -64,12 +64,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
aTestRun.add(et);
}
//gA11yEventDumpToConsole = true; // debug stuff
function runTest()
{
var testRun = new editableTextTestRun();
addTestEditable("input", testRun);
// addTestEditable("div"); XXX: bug 452599
addTestEditable("div", testRun);
addTestEditable(getNode("frame").contentDocument, testRun);
testRun.run(); // Will call SimpleTest.finish();
@ -94,6 +96,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
<a target="_blank"
title="nsIAccessibleEditableText chrome tests"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=452161">Mozilla Bug 452161</a>
<a target="_blank"
title="Cache rendered text on a11y side"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=626660">
Mozilla Bug 626660
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
@ -101,7 +108,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
<input id="input"/>
<div id="div" contentEditable="true"/>
<div id="div" contentEditable="true"></div>
<iframe id="frame"/>
</body>

View File

@ -22,9 +22,14 @@
{
var et = new editableTextTest("input");
et.insertText("ee", 1, "heeello");
// 'ee' insertion/removal at 1 or 2 offset of 'hello'/'heeello' string
// reports 'ee' text was inserted/removed at 2 offset.
et.insertText("ee", 1, "heeello", 2);
et.copyText(1, 3, "ee");
et.cutText(1, 3, "hello");
et.cutText(1, 3, "hello", 2, 4);
et.insertText("ee", 2, "heeello", 2);
et.cutText(2, 4, "hello", 2, 4);
et.deleteText(1, 3, "hlo");
et.pasteText(1, "heelo");
@ -44,6 +49,11 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=524115">
Mozilla Bug 524115
</a>
<a target="_blank"
title="Cache rendered text on a11y side"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=626660">
Mozilla Bug 626660
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">

View File

@ -12,6 +12,7 @@ const EVENT_MENU_START = nsIAccessibleEvent.EVENT_MENU_START;
const EVENT_MENU_END = nsIAccessibleEvent.EVENT_MENU_END;
const EVENT_MENUPOPUP_START = nsIAccessibleEvent.EVENT_MENUPOPUP_START;
const EVENT_MENUPOPUP_END = nsIAccessibleEvent.EVENT_MENUPOPUP_END;
const EVENT_OBJECT_ATTRIBUTE_CHANGED = nsIAccessibleEvent.EVENT_OBJECT_ATTRIBUTE_CHANGED;
const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
const EVENT_SCROLLING_START = nsIAccessibleEvent.EVENT_SCROLLING_START;
const EVENT_SELECTION_ADD = nsIAccessibleEvent.EVENT_SELECTION_ADD;
@ -906,7 +907,7 @@ function invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
function invokerChecker_targetDescrGetter()
{
if (typeof this.mTarget == "function")
return this.mTarget.toSource() + this.mTargetFuncArg;
return this.mTarget.name + ", arg: " + this.mTargetFuncArg;
return prettyName(this.mTarget);
}

View File

@ -51,6 +51,7 @@ _TEST_FILES =\
focus.html \
scroll.html \
test_aria_alert.html \
test_aria_hidden.html \
test_aria_menu.html \
test_aria_statechange.html \
test_attrs.html \

View File

@ -0,0 +1,79 @@
<html>
<head>
<title>Accessible ARIA hidden attribute</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="../events.js"></script>
<script type="application/javascript">
/**
* Do tests.
*/
var gQueue = null;
function hideNode(aID, bHide)
{
this.node = getNode(aID);
this.accessible = getAccessible(this.node);
this.eventSeq = [
new invokerChecker(EVENT_OBJECT_ATTRIBUTE_CHANGED, this.accessible),
];
this.invoke = function hideNode_invoke()
{
this.node.setAttribute("aria-hidden", bHide);
}
this.getID = function hideNode_getID()
{
return "aria-hidden for " + aID + " " + bHide;
}
}
function doTests()
{
//gA11yEventDumpID = "eventdump"; // debug stuff
gQueue = new eventQueue();
gQueue.push(new hideNode("hideable", "true"));
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=581096"
title="Add support for aria-hidden">
Mozilla Bug 581096
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<div id="eventdump"></div>
<div id="hideable"><div>Hi</div><div>there</div></div>
</body>
</html>

View File

@ -127,11 +127,6 @@
try {
docAcc = docAcc.parent;
} catch (e) {
// XXX: it may randomaly fail on propertypage accessible of browser's
// tabbbrowser if nsIAccessible::parent returns cached parent only.
// This should gone after bug 572951.
// Error: failed | Can't get parent for [ 'panel1277435313424' ,
// role: propertypage]
ok(false, "Can't get parent for " + prettyName(docAcc));
throw e;
}

View File

@ -90,6 +90,8 @@
}
}
//gA11yEventDumpToConsole = true; // debug stuff
var gQueue = null;
function doTest()
{

View File

@ -73,8 +73,7 @@
// Note: if input have label elements then the name isn't calculated
// from them.
testName("btn_labelledby_mixed_input",
"Submit Query Reset Submit Query");
// XXX Bug 567203 "input button Submit Query Reset Submit Query");
"input button Submit Query Reset Submit Query");
// Gets the name from the title of object element.
testName("btn_labelledby_mixed_object", "object");

View File

@ -19,6 +19,7 @@ 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_INVISIBLE = nsIAccessibleStates.STATE_INVISIBLE;
const STATE_LINKED = nsIAccessibleStates.STATE_LINKED;
const STATE_MIXED = nsIAccessibleStates.STATE_MIXED;
const STATE_MULTISELECTABLE = nsIAccessibleStates.STATE_MULTISELECTABLE;

View File

@ -52,11 +52,7 @@
children: [
{
role: ROLE_ENTRY,
children: [
{
role: ROLE_TEXT_LEAF // Text node for the node's value
}
]
children: [ ] // no text leaf accessible for text node
},
{
role: ROLE_COMBOBOX_LIST, // context menu
@ -94,7 +90,8 @@
role: ROLE_ENTRY,
children: [
{
role: ROLE_TEXT_LEAF
role: ROLE_TEXT_LEAF,
name: "http://mochi.test:8888/redirect-a11y.html"
}
]
},
@ -122,11 +119,7 @@
},
{
role: ROLE_ENTRY,
children: [
{
role: ROLE_TEXT_LEAF // Text node for the node's value
}
]
children: [ ] // no text leaf accessible for text node
},
{
role: ROLE_COMBOBOX_LIST, // context menu popup
@ -150,6 +143,11 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292"
title="Ensure accessible children for toolbarbutton types 'menu' and 'menu-button'">
Mozilla Bug 249292
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=626660"
title="Cache rendered text on a11y side">
Mozilla Bug 626660
</a><br/>
<p id="display"></p>
<div id="content" style="display: none">

View File

@ -29,9 +29,7 @@
] }
] },
{ TEXT_LEAF: [ ] }, // body text
{ ENTRY: [ // input under document element
{ TEXT_LEAF: [ ] }
] },
{ ENTRY: [ ] }, // input under document element
{ PARAGRAPH: [ // link under document element
{ TEXT_LEAF: [ ] }, // link content
{ STATICTEXT: [ ] }, // generated content

View File

@ -32,7 +32,7 @@
testAccessibleTree("txc1", accTree);
// input@type="text"
// input@type="text", value
accTree = {
role: ROLE_ENTRY,
children: [
@ -45,6 +45,12 @@
testAccessibleTree("txc2", accTree);
// input@type="text", no value
accTree =
{ ENTRY: [ ] };
testAccessibleTree("txc3", accTree);
// textarea
accTree = {
role: ROLE_ENTRY,
@ -58,7 +64,7 @@
]
};
testAccessibleTree("txc3", accTree);
testAccessibleTree("txc4", accTree);
// input@type="password"
accTree = {
@ -71,7 +77,7 @@
]
};
testAccessibleTree("txc4", accTree);
testAccessibleTree("txc5", accTree);
SimpleTest.finish();
}
@ -92,6 +98,11 @@
title="Create child accessibles for text controls from native anonymous content">
Mozilla Bug 542824
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652"
title="Make sure accessible tree is correct when rendered text is changed">
Mozilla Bug 625652
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
@ -102,11 +113,12 @@
1hellohello
</div>
<input id="txc2" value="hello">
<textarea id="txc3">
<input id="txc3">
<textarea id="txc4">
hello1
hello2
</textarea>
<input id="txc4" type="password" value="hello">
<input id="txc5" type="password" value="hello">
</body>
</html>

View File

@ -54,6 +54,7 @@ _TEST_FILES =\
test_select.html \
test_textleaf.html \
test_visibility.html \
test_whitespace.html \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -323,9 +323,7 @@
{
var tree =
{ DOCUMENT: [
{ ENTRY: [
{ TEXT_LEAF: [ ] }
] }
{ ENTRY: [ ] }
] };
testAccessibleTree(this.docNode, tree);

View File

@ -86,6 +86,46 @@
}
}
function removeTextData(aID)
{
this.containerNode = getNode(aID);
this.textNode = this.containerNode.firstChild;
this.eventSeq = [
new invokerChecker(EVENT_REORDER, this.containerNode)
];
this.invoke = function removeTextData_invoke()
{
var tree = {
role: ROLE_SECTION,
children: [
{
role: ROLE_TEXT_LEAF,
name: "text"
}
]
};
testAccessibleTree(this.containerNode, tree);
this.textNode.data = "";
}
this.finalCheck = function removeTextData_finalCheck()
{
var tree = {
role: ROLE_SECTION,
children: []
};
testAccessibleTree(this.containerNode, tree);
}
this.getID = function removeTextData_finalCheck()
{
return "remove text data of text node inside '" + aID + "'.";
}
}
////////////////////////////////////////////////////////////////////////////
// Test
@ -108,6 +148,9 @@
// and adopts text leaf accessible, text leaf should have an action
gQueue.push(new setOnClickNRoleAttrs("span"));
// text data removal of text node should remove its text accessible
gQueue.push(new removeTextData("container2"));
gQueue.invoke(); // SimpleTest.finish() will be called in the end
}
@ -122,6 +165,11 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=545465">
Mozilla Bug 545465
</a>
<a target="_blank"
title="Make sure accessible tree is correct when rendered text is changed"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652">
Mozilla Bug 625652
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
@ -133,6 +181,8 @@
<span id="span">span</span>
</div>
<div id="container2">text</div>
<div id="eventdump"></div>
</body>
</html>

View File

@ -0,0 +1,188 @@
<!DOCTYPE html>
<html>
<head>
<title>Whitespace text accessible creation/desctruction</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
/**
* Middle image accessible removal results in text accessible removal.
*
* Before:
* DOM: whitespace img1 whitespace img2 whitespace img3 whitespace,
* a11y: img1 whitespace img2 whitespace img3
* After:
* DOM: whitespace img1 whitespace whitespace img3 whitespace,
* a11y: img1 whitespace img3
*/
function removeImg()
{
this.containerNode = getNode("container1");
this.imgNode = getNode("img1");
this.img = getAccessible(this.imgNode);
this.text = this.img.nextSibling;
this.eventSeq = [
new invokerChecker(EVENT_HIDE, this.img),
new invokerChecker(EVENT_HIDE, this.text),
new invokerChecker(EVENT_REORDER, this.containerNode)
];
this.finalCheck = function textLeafUpdate_finalCheck()
{
var tree =
{ SECTION: [
{ GRAPHIC: [] },
{ TEXT_LEAF: [] },
{ GRAPHIC: [] }
] };
testAccessibleTree(this.containerNode, tree);
}
this.invoke = function setOnClickAttr_invoke()
{
var tree =
{ SECTION: [
{ GRAPHIC: [] },
{ TEXT_LEAF: [] },
{ GRAPHIC: [] },
{ TEXT_LEAF: [] },
{ GRAPHIC: [] }
] };
testAccessibleTree(this.containerNode, tree);
this.containerNode.removeChild(this.imgNode);
}
this.getID = function setOnClickAttr_getID()
{
return "remove middle img";
}
}
/**
* Append image making the whitespace visible and thus accessible.
* Note: images and whitespaces are on different leves of accessible trees,
* so that image container accessible update doesn't update the tree
* of whitespace container.
*
* Before:
* DOM: whitespace emptylink whitespace linkwithimg whitespace
* a11y: emptylink linkwithimg
* After:
* DOM: whitespace linkwithimg whitespace linkwithimg whitespace
* a11y: linkwithimg whitespace linkwithimg
*/
function insertImg()
{
this.containerNode = getNode("container2");
this.topNode = this.containerNode.parentNode;
this.textNode = this.containerNode.nextSibling;
this.imgNode = document.createElement("img");
this.imgNode.setAttribute("src", "../moz.png");
this.eventSeq = [
new invokerChecker(EVENT_SHOW, getAccessible, this.imgNode),
new invokerChecker(EVENT_SHOW, getAccessible, this.textNode),
new invokerChecker(EVENT_REORDER, this.topNode)
];
this.invoke = function insertImg_invoke()
{
var tree =
{ SECTION: [
{ LINK: [] },
{ LINK: [
{ GRAPHIC: [] }
] }
] };
testAccessibleTree(this.topNode, tree);
this.containerNode.appendChild(this.imgNode);
}
this.finalCheck = function insertImg_finalCheck()
{
var tree =
{ SECTION: [
{ LINK: [
{ GRAPHIC: [ ] }
] },
{ TEXT_LEAF: [ ] },
{ LINK: [
{ GRAPHIC: [ ] }
] }
] };
testAccessibleTree(this.topNode, tree);
}
this.getID = function appendImg_getID()
{
return "insert img into internal container";
}
}
////////////////////////////////////////////////////////////////////////////
// Test
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true;
var gQueue = null;
function doTest()
{
gQueue = new eventQueue();
gQueue.push(new removeImg());
gQueue.push(new insertImg());
gQueue.invoke(); // SimpleTest.finish() will be called in the end
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
title="Make sure accessible tree is correct when rendered text is changed"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652">
Mozilla Bug 625652
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<div id="container1"> <img src="../moz.png"> <img id="img1" src="../moz.png"> <img src="../moz.png"> </div>
<div> <a id="container2"></a> <a><img src="../moz.png"></a> </div>
<div id="eventdump"></div>
</body>
</html>

View File

@ -430,6 +430,8 @@ pref("dom.max_script_run_time", 20);
// applications, but without it there isn't a really good way to prevent chrome
// spoofing, see bug 337344
pref("dom.disable_window_open_feature.location", true);
// prevent JS from setting status messages
pref("dom.disable_window_status_change", true);
// allow JS to move and resize existing windows
pref("dom.disable_window_move_resize", false);
// prevent JS from monkeying with window focus, etc
@ -784,7 +786,7 @@ pref("browser.sessionstore.postdata", 0);
// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
pref("browser.sessionstore.privacy_level", 0);
// the same as browser.sessionstore.privacy_level, but for saving deferred session data
pref("browser.sessionstore.privacy_level_deferred", 0);
pref("browser.sessionstore.privacy_level_deferred", 1);
// how many tabs can be reopened (per window)
pref("browser.sessionstore.max_tabs_undo", 10);
// how many windows can be reopened (per session) - on non-OS X platforms this

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -22,6 +22,8 @@
*
* Contributor(s):
* Marco Bonardo <mak77@bonardo.net> (original author)
* Mihai Sucan <mihai.sucan@gmail.com>
* Stephen Horlander <shorlander@mozilla.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
@ -42,76 +44,337 @@ html {
font: message-box;
background: -moz-Field;
color: -moz-FieldText;
height: 100%;
}
#topSection,
#bottomSection {
body {
display: inline-block;
position: relative;
margin: 1em auto;
padding: 25px;
width: 560px;
margin: 0;
width: 100%;
height: 100%;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
#brandStart {
background: -moz-linear-gradient(top, #42607C, #1E4262 30%, #1E4262 80%, #143552 98%, #244665);
border-radius: 5.6px;
padding-top: 0.1em;
padding-bottom: 0.1em;
-moz-padding-start: 0.5em;
font-size: 250%;
font-weight: bold;
color: #688196;
margin-top: 18px;
margin-bottom: 6px;
}
#brandStart > span {
color: white;
text-align: center;
height: 19%;
max-height: 256px;
min-height: 92px;
}
#brandStart:before {
content: url("chrome://branding/content/icon128.png");
position: absolute;
top: 0;
#brandStartSpacer {
height: 6.5%;
}
body[dir="ltr"] #brandStart:before {
right: 0;
}
body[dir="rtl"] #brandStart:before {
left: -15px;
#brandStartLogo {
height: 100%;
}
#searchContainer {
border: 1px solid ThreeDShadow;
border-radius: 5.6px;
padding: 3em;
height: 15%;
min-height: 90px;
}
#searchEngineLinks {
font-size: 80%;
#searchContainer:before {
content: " ";
display: block;
height: 23%;
}
#searchEngineLinks > a {
-moz-margin-start: 1em;
#searchForm {
display: table;
width: 100%;
max-width: 1830px;
margin: 0 auto;
}
body[dir="ltr"] #searchEngineLinks {
float: right;
@media all and (max-height: 700px) {
#searchContainer { height: 20% }
}
body[dir="rtl"] #searchEngineLinks {
float: left;
@media all and (max-height: 450px) {
#searchContainer { height: 30% }
}
#searchLogoContainer {
display: table-cell;
width: 30%;
text-align: end;
line-height: 32px;
}
#searchEngineLogo {
margin: 5px;
-moz-margin-end: 2.5%;
vertical-align: middle;
}
#searchInputContainer {
display: table-cell;
width: 38%;
max-width: 700px;
min-width: 150px;
}
#searchText {
margin-bottom: 10px;
width: 100%;
height: 24px;
padding: 3px 6px;
border-radius: 2px;
border: 1px solid rgb(150,150,150);
border-top-color: rgb(100,100,100);
box-shadow: 0 1px 0 rgba(255,255,255,0.5);
font-size: 13px;
}
#aboutMozilla {
#searchButtons {
display: table-cell;
width: 31%;
-moz-padding-start: 13px;
vertical-align: top;
}
@media all and (max-width: 470px) {
#searchLogoContainer { width: 10% }
#searchButtons { width: 11% }
#searchInputContainer { width: 40% }
}
@media all and (min-width: 470px) and (max-width: 600px) {
#searchLogoContainer { width: 15% }
#searchButtons { width: 16%; white-space: nowrap }
#searchInputContainer { width: 45% }
}
@media all and (min-width: 600px) and (max-width: 850px) {
#searchLogoContainer { width: 20% }
#searchButtons { width: 21%; white-space: nowrap }
#searchInputContainer { width: 49% }
}
#searchSubmit {
background: -moz-linear-gradient(#f1f1f1, #dfdfdf);
padding: 4px 8px;
height: 32px;
border: 1px solid #ccc;
border-top-color: #ccc;
border-bottom-color: #999;
-moz-border-start-color: #afafaf;
-moz-border-end-color: #999;
box-shadow: 1px 1px 0 #e7e7e7,
0 1px 0 #fcfcfc inset,
0 -1px 0 #d7d7d7 inset;
font-size: 13px;
color: #000;
}
body[dir=rtl] #searchSubmit {
box-shadow: -1px 1px 0 #e7e7e7,
0 1px 0 #fcfcfc inset,
0 -1px 0 #d7d7d7 inset;
}
#searchSubmit:active {
background: -moz-linear-gradient(#c5c5c5, #c5c5c5);
box-shadow: 1px 1px 0 #e7e7e7;
}
body[dir=rtl] #searchSubmit:active {
box-shadow: -1px 1px 0 #e7e7e7;
}
#searchEngineLinks {
display: inline-block;
-moz-margin-start: 1.5%;
vertical-align: middle;
font-size: 10px;
}
#searchEngineLinks a {
display: block;
white-space: nowrap;
}
#contentContainer {
height: 30%;
background-image: -moz-radial-gradient(center top, ellipse farthest-side, rgba(16,83,130,.5), rgba(16,83,130,0) 75%),
-moz-radial-gradient(center top, ellipse farthest-side, rgba(180,218,244,.5), rgba(180,218,244,0)),
-moz-radial-gradient(center top, ellipse farthest-side, rgba(180,218,244,.3), rgba(180,218,244,0));
background-size: 100% 5px,
100% 50px,
100% 100%;
background-repeat: no-repeat;
}
@media all and (max-height: 400px) {
#contentContainer { height: 20% }
}
#snippetContainer {
position: relative;
top: -24px;
text-align: center;
}
#defaultSnippets {
display: inline-block;
padding: 14px;
width: 30%;
max-width: 600px;
background-image: -moz-linear-gradient(rgba(255,255,255,.8), rgba(255,255,255,.1));
background-color: rgb(250,250,250);
border-radius: 4px;
box-shadow: 0 1px 0 rgba(255,255,255,.8) inset,
0 -2px 0 rgba(0,0,0,.1) inset,
0 0 10px rgba(255,255,255,.5) inset,
0 0 0 1px rgba(0,0,0,.1),
0 2px 4px rgba(0,0,0,.2);
color: rgb(60,60,60);
font-size: 11px;
cursor: pointer;
}
@media all and (max-width: 470px) {
#defaultSnippets { width: 65% }
}
@media all and (min-width: 470px) and (max-width: 850px) {
#defaultSnippets { width: 45% }
}
#defaultSnippets:hover {
background-color: rgb(255,255,255);
box-shadow: 0 1px 0 rgba(255,255,255,.8) inset,
0 -2px 0 rgba(0,0,0,.1) inset,
0 0 10px rgba(255,255,255,.5) inset,
0 0 5px rgba(0,0,0,.1),
0 0 0 1px rgba(0,0,0,.1),
0 2px 4px rgba(0,0,0,.2);
}
#defaultSnippets:hover:active {
background-color: rgb(210,210,210);
box-shadow: 0 2px 3px rgba(0,0,0,.3) inset,
0 1px 0 rgba(255,255,255,.5);
}
#sessionRestoreContainer {
padding-top: 1.5%;
height: 33%;
text-align: center;
}
@media all and (max-height: 370px) {
#sessionRestoreContainer {
position: relative;
top: -5px;
padding-top: 0;
}
}
#restorePreviousSession {
-moz-padding-start: 7.5%;
-moz-padding-end: 10px;
height: 100%;
border: 0;
border-radius: 4px;
box-shadow: 0 0 0 1px rgba(9,37,59,0),
0 1px 2px rgba(9,37,59,0),
0 0 10px rgba(255,255,255,0),
0 -3px 0 rgba(180,194,212,0) inset;
-moz-transition-property: background-color, box-shadow;
-moz-transition-duration: 0.25s;
-moz-transition-timing-function: ease-out;
background: transparent url("chrome://browser/content/aboutHome-restore-icon.png") no-repeat 10px 50%;
background-size: auto 77%;
color: rgb(50,50,50);
font-weight: bold;
}
body[dir=rtl] #restorePreviousSession {
background-image: url("chrome://browser/content/aboutHome-restore-icon-rtl.png");
background-position: 100% 50%;
}
@media all and (max-aspect-ratio: 8/16) {
#restorePreviousSession { -moz-padding-start: 25% }
}
@media all and (min-aspect-ratio: 8/16) and (max-aspect-ratio: 10/16) {
#restorePreviousSession { -moz-padding-start: 18% }
}
@media all and (min-aspect-ratio: 10/16) and (max-aspect-ratio: 12/16) {
#restorePreviousSession { -moz-padding-start: 15% }
}
@media all and (min-aspect-ratio: 12/16) and (max-aspect-ratio: 15/16) {
#restorePreviousSession { -moz-padding-start: 12% }
}
@media all and (min-aspect-ratio: 15/16) and (max-aspect-ratio: 20/16) {
#restorePreviousSession { -moz-padding-start: 9% }
}
@media all and (min-aspect-ratio: 31/16) and (max-aspect-ratio: 51/16) {
#restorePreviousSession { -moz-padding-start: 5% }
}
@media all and (min-aspect-ratio: 51/16) {
#restorePreviousSession { -moz-padding-start: 3% }
}
#restorePreviousSession:disabled {
display: none;
}
#restorePreviousSession:hover {
background-image: url("chrome://browser/content/aboutHome-restore-icon.png"),
-moz-linear-gradient(rgba(255,255,255,.7), rgba(255,255,255,.2));
background-position: 10px 50%, 0 0;
background-size: auto 77%, auto auto;
border-radius: 4px;
box-shadow: 0 0 0 1px rgba(9,37,59,.2),
0 1px 2px rgba(9,37,59,.2),
0 0 10px rgba(255,255,255,.4),
0 -3px 0 rgba(180,194,212,.3) inset;
}
body[dir=rtl] #restorePreviousSession:hover {
background-image: url("chrome://browser/content/aboutHome-restore-icon-rtl.png"),
-moz-linear-gradient(rgba(255,255,255,.7), rgba(255,255,255,.2));
background-position: 100% 50%, 0 0;
}
#restorePreviousSession:hover:active {
background-image: url("chrome://browser/content/aboutHome-restore-icon.png"),
-moz-linear-gradient(rgba(255,255,255,.0), rgba(255,255,255,.2));
background-position: 10px 50%, 0 0;
background-size: auto 77%, auto auto;
background-color: rgba(23,75,115,.1);
box-shadow: 0 0 0 1px rgba(9,37,59,.2),
0 1px 2px rgba(9,37,59,.4) inset,
0 1px 5px rgba(9,37,59,.15) inset;
}
body[dir=rtl] #restorePreviousSession:hover:active {
background-image: url("chrome://browser/content/aboutHome-restore-icon-rtl.png"),
-moz-linear-gradient(rgba(255,255,255,.0), rgba(255,255,255,.2));
background-position: 100% 50%, 0 0;
}
#bottomSection {
position: absolute;
color: rgb(150,150,150);
font-size: 10px;
width: 100%;
text-align: center;
bottom: 2%;
}

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* Marco Bonardo <mak77@bonardo.net> (original author)
* Mihai Sucan <mihai.sucan@gmail.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
@ -40,7 +41,65 @@
// is handled correctly by the engine.
const SEARCH_ENGINES = {
"Google": {
image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAAAYCAMAAABwdHsxAAADAFBMVEVogZYwjM9NtOspS2u0KAfYZBb3rRTiurCMjYrUWhIaPlzDNgfVRgjaVgvoizTI9f2ta17/9Wzrs0v46M/+7VOW6vrJQAsBLochdr+nmF/++o+UDQK88vzaYQarq6scarXT+f68vLzjdRrjewp5d3WqGATkUwaTq9uk7PnUaydkICTJSBD4uCDgZAz9+LiK6PjRZFH33Xv01qbrlxznhgvwx5gCBCbOUhLjfCzhaxHztXG8RhX93UP5yTRGl9QhRGT5wigkRmXniRj70zoKWrT2yUdqjs3UhzXvl0JwU0teu+EBElGD2fH83FZo1vTPilT0vDmd0Z3dbhrWrHLhcQir8PzTTDfPrVGrajv03cf5+vkwUG6z8PrSUw571uzvpCGPTzBsPDMxMEcHagXrkg7voBHf/P8FGHHhzrbyryOQZAeDNCudnZyTJBL2zlT+xByoUCG1wspyY1nRiH7STA3yiB7Jjx/pqT/Pz8/7lhDvfBxTUkl95PY/RE0DI2SQbT7LzJjxdg1OMTzutynnm2v+5DWsNBX+/tVDqOa7tnX57OPGYSHysziEo5a3XBe3l3j+0SXCoaCsjTL++fMgQmLmxXdv0ezsv0ycQxf8p0X1hwrenDU/Tnj9//tkyNj3+fX89fDv22QXHEWQydee7/xEdKG84uRWi5bwaxyK3/L++/iU5PMWM2Q6GS7UXg81l9gmSGgXOVewsKO3x+ECK8h8x3j7/fs5bMBke6kuTmwoRV/SbArKeSrB1snY373x5aSIRz85YIvragDD5+lnorUvSWF80PCQr6o4KGKysrIeQmIgXJ1Vb4j///8+XXjs7ewaUZSutsKFkqY4T3IhXqgYSHtwiJwUNVLExcbk5eU4SGcrcLEsP1wPKV77+/vy8/NBkcBmrN+drLklNGIONHQ5WXVxyuk0VXHHz9dmxuonZahTrNqPobEcP19CVXMURIljzvE/odz29vYcP3VieZHo6OhCYHxOW29y2vNYwe79/f33+PnU2eExgcLX2Nje397NKLzjAAACjElEQVR42q2UMWvbQBTHPRgro6CDKIJOPQSeVH+CDuZ2E3BIVmNkBB5agTF0NYV00y7jdMji0XqxvNljAzmEPASrQ0Vwhk41tPZiC6l3OjmNXYeYujfo3nF3v/fe/+le5hX5HyOTzv1Of88bnXelq0q8C9NRHMcpFrHd3gPTD0qV17uisWeDqE269gyguU88pcrRDowNXrqYTt1/xkQwXi8mZ2Q/TO9vDNa2FVkJLUPocDsYDoe1lB6EURzZ4RrDThHi0RF7mRh+bFGirCEYCJnMLpzUase5XI2aZxhjKM6wS64YJlDZqWxAPEq6ztjFm01KEzGdTxGirgoNFpRfrU7IEg8IiQFoqRlmqYp0p4VGDEO8zB1sYoLyKJlFVCdxjotwntNpISJqYfjKMQKi1fDVvJlqg2G6gWmh98l8iuRoWL1O7Bc53b2TJoSFc88xquwKqig8SGyDFvxRMGyriEdDUF140+DR9HN6hGHOBJIsjinLpvHoV84EIPGCL/ClAzOiojzfKeejz9WX3H6ru98Td6GWaqOi3lbBQQsTlyzzOU1ajjimTgpUkxRDN7Uvtl1kmTGMgT5+28BcAAC/SBSJBl7mWS1kkwRVnpWvs8n1HP6nMswFQmJyp71+DK4mgZ0ssEarFsiyQe0sg9V03acnG2luClgcQx+DKaOyTwTjAUNiS5OKWFGwZbGYw5GsiqKYNI6erp8XTo7TGigAToecVSofjvrEzMuyPBIe9xvXux2MvXCZZroQhNU6a7/mJ5VsK87l+FaD2TLpTvTTbTbj7bb13PiJZ0zAT+u6PtH9nhuKxD270s0hGAfSBOCgaJLHxDrlffcQzOoXSKyYjksOwdDWOLcG2H5i8zf27La6kVLrqwAAAABJRU5ErkJggg=="
image: "data:image/png;base64," +
"iVBORw0KGgoAAAANSUhEUgAAAEYAAAAcCAYAAADcO8kVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ" +
"bWFnZVJlYWR5ccllPAAADHdJREFUeNrsWQl0VNUZvve9NzNJJpnsIkuEJMoqAVJAodCKoFUsAUFQ" +
"qhig0npaRUE8Viv1FFtQWxSwLXVhEawbhOWobOICFCGiEIIQRGIgCSFjMslsb9567+1/Z+7gmIYK" +
"Vivt6Ztzz5y5b+7yf//3f/9/38PoW7gYY+i7uDDG39heJfT/q91LGTiTIcWJkCxzxDmCCBGCkBEO" +
"FDCm5CPs+CGWYvcliRxEzDwgu9I/IzZClonQgT/jC9Eu3GFTz6sdKc57kIzHWKaFjIA2wz++Zhkn" +
"yblMIDkAFIcDDFcQ+vtjGJuaOlKPkB2G4V4U9kcu8zfWlPtPVX/g9zZ7QwE03jDTqzWVndBUc57a" +
"Up91gToce0cf3R05El5u6gYyNQ0BKK/x/nNmjKwwxBmx8/eSNHiWsVLXlBJ/7UdTazcN3gn3bYEw" +
"FmG3pvOobRuScoc+ibEyF6GsUugrgEYuMGD4nqltmJjqFBkt+gcJ/ed0SZIA5crZ+gumrpQ0H319" +
"ogBFh6aJFoGmQguf2n7tu62HnvgJ1cPBcN3m6dAnX4CM4QAQigmxdQthm9EEJ58bY3bOl/CQ2YE5" +
"pu24LdBwZE7De+M+4gBAs/IntETphOHD4FOzNoNPbjuzBkn+48/9qKXywWPcM99Edvh2siPfHeyc" +
"nH8mU/pM2pJLsfshI0KCNRv7viiYYXW7sRnmxTFQhCp3G9/CTqzLsht3jtkrmGJdgGF0xmYpQx5G" +
"KBEInWdWSs4pnm6bLD3i95WJsDG7jmtiXFYwlmF2WXATmCPROE05IGa3G33sxPrsL014tGRMVo5D" +
"uVdirD/8zJBluQgC9qSF2JKcV9cuPwudsbq1YLqCydjYGOkSngYtKq36vJUs6jqhuqXtgCvursty" +
"uHOnSZIMWROnc/dR2J5pYAZO3tF0rOwvAXI/jvKZ/vN6zVNuHQGWjYNx/SWGiohtH9R1Y17HDRvf" +
"4XtUCEoaQwyGbEOr5QZ3HeeLbRwrosnRNB5lHNwpuBn+HK2KWFsLcd34scWpGJd5g6Ener61faoQ" +
"bOXk6OsWpycnP98yYdzMrLINxYks+3h1fvZlHfE6M6LXu0oa4mPko8s7TL70kuSnOmVIMxvW5n2v" +
"00111fF1htzXWiwpnrJAw8FbD60qXtHn9o9LUrJ6r2CUBoOnDpQeKxu0ncPhntgRwKLRcErUVd9t" +
"k1falinlvLLmLr7WHfndsh/t0WOdg9Dt1cOHTyrctWutRGzH5ZbNjcQ0FpEce+lMQwCnpMRqnSQ3" +
"Qu50hFIzMXJnSsjt+aI+fG/kiOwUStcFQuG9AMor0GUI0da6btoyKxIKnWKaXlR/zajFCYWlXNBB" +
"WslMKz+tpOEezkIxJtJzuvfl5ia1DCiQnuki6+MiXzRlR47s9Lwdaa1bCKAc4uscXnX5mwFvzdO6" +
"JnlQSv8lgiOUERZ1QYLG4PqJE+ZItl2y4MDB3wjma8/XnGiuavSuUMNhKNOshdyZkmViD7EAGBrX" +
"K9gzA1CYqPZEfEoAEK91eN3jTELIlRT7jnuhm9M5mxrmJZVNvjUio0VEC3Exr2ryLTbVCJI0/ZfL" +
"e/TI5ZusfbXbKAcjP2706msTQRHiH3pxa2ghgIlkU+9b91zqRA6OK6MIQh+nG8HP6wT4PPzD3n3z" +
"lxoRiohl5eVd/1G/qC2Ug8LBOcMYh5PYd6mqemTRJ8d88axb3r//NTkYT2tQ1e27W3yzo+aamh0k" +
"NoWIcfeJ1Ss8A2EU0xgqflEkYQBGBuYAe3hByAHiNVBcqyRdLzEjYLhpEGFk/CaHXFtZX79RD4WR" +
"Bl4plOWR3MhkbI0DMOHfFhNjaEK6Neas1D9Rg3qVHQFwLHIV9DkN01miaxD6LNUjQpKPMQLHl522" +
"jWAVtQxELTM7agBN+AdcGwYNvJREtDwjrOL5hQWpVf36TTtcVFRhGMaAlxsbpw+prCwt/fRTHoZE" +
"MVS1Sna5r5CUpKExisc0RVFix4BoKEFHlDES78dIcYjdf0FRhapqH5tQxAyTtiOwZHVTk3dWdnaV" +
"zFgv27a5RzfKlt6PAiOZFQWmrUTy2Y3WFntPdgruhXVWxIFRA2ZIBq9QqeP18PvlBPAtRq0gHGNQ" +
"uHbN4ej+qJDDmMZIaaZZYASC/MzTe1RScmmdqlZce/z4CLFfW7RoppWsSP1Wy7R5NeTpfMNnU+s2" +
"pGIZ2KC4oEGoOOCb/7aNpkKbWKsswhhoUrQZBmPdp/hXcWDUQCjIGZFByLB2Su9ogaUaRhAa8hsG" +
"DxXFCmlB8CBKleyhZynXiWkwv6VRpEVYkBtnBGq28bMPZcmjC0rKCxPLFqy4GDWbVwSOPemLGhvP" +
"SMJNlc2+es0fQGYo5HnH59sCoMQLWVU0LV4ISqHjf/obtbQQxCbMnPngRcM25MbCB5giDo+Hl6Xg" +
"qtVd6yqWeu7e91RyR++Rd28OthAUaLZRa+0Rrg+SNxQqD0dDyRx9lmqY6brOVDi7HFHV9/mWvV5z" +
"r63aSCF0yDOlcla7NZrFmA3AeH2E1052/ebi1ZZ6ej3oh8eZ2fe1vtPqOTi495SaHygOOc1/dOFj" +
"QnsYhdMw44lFaMysU6dOBCBvRcCB35fl+0X4am3COCaakdoVjVaoZgW1dESJnSd5hiz/7NU02Qbd" +
"4dpDYdLL7wizOLW5OGoRTAM+G0VCBrg0yDOMXRGJPB8GNpim2efF7Ozi9hgA4Hfxm0b53NbW/Zyy" +
"i7bQlyJBFjIjDF1ViKe29xhEJizP0Flw6S76klhfrX+j8C7dt/8BPRxpsGnGyqKfGRQ7O20OVr80" +
"NVT9bIMIBwhrygMsLr7RcKvT9bUq1zXLumVtdvaAs56V+GK+3UMXEK15HzU1jvANHa47/YIGJ2cT" +
"DmAWSIZtUdT9tiDpNjEQpZ1pJpumqiKih0AfSHTB2X7/2w2GsT4CNM8k5NlnPJ7Eyg+vT0+faVqW" +
"Z2tEu1cYaC3fQxsPnaS/swAYN2K/qnhQHpgAKC6/Xx6Qgtmkilo2Z9WHrFHQnO/Bf/rtoctPlOVM" +
"az35/pKIyhCAh6SUQre4H/M+L7lAqJl+RvKsVeHw0pBlntJME2VQunVzRsaERCfuyMzMfyszMzN+" +
"ak52XTQ2333prxdJzuyRXGSw7KjFEnlUwYF1zrROLbxO4umwcVOWkjV0z51YyXqaEQsR9djYQMX4" +
"TTwVQst8NiVlPqS+Upj0EAyZB9+tcB4ZByJ71V5C7ntcj550Q4KBTl7pvjFVmtbnYvSQ7ACcEZoD" +
"fTUwbgDE490fN6B5o5fRjdAXiDNBGKLwNVMLZnTJLPrDh1hypAFHAkTzXnNqc+GHfG75oYxVYN0k" +
"YEwQXPEAcuF9ZIH/01ku1/ChivJHkNCeMk8sCNXChCdhQr7+6uvC4RU4d8RJ1PRuV64JKdDSU3su" +
"HuHMuKJUcuWMhMU4QHwflWBHgFEb4tXuSs3gEaLV7bdDlXvU6rm7hKH8SobmmawohUNkeSDUghdD" +
"0vfXMrbnYdOoSij6Eg108TFje6EOMwbjwZ0zUHeXA5GGANoz6jm2VwCotikBcN7YpvHEtvrDnoqh" +
"t58kuzpDJcoPhQDO6YGn3+pTK/007QYUoClgOUHpWAUuldPV4VYYn8rXfMDpHN4NS4McOBpsJ7fZ" +
"9utrbNvLWYdzrq5H3PO+Hfmy8GCKaI7U7o/3wq6ObklOIkhykcD+sbuFMeKAcKYos8RvSczhEgLM" +
"EioJknDoTEznWLDNJb5RO2POPBfqf2frdFN3LAz6Im+agU9e+Xzn8HLod+dcueXnDk/vX2DZlQaK" +
"/ebpLV0miPmcCXs1xZySWC9JMA/Fz3/CeXZbgcTCIEVMqiSAkFguxQ0mX06IX9KueIuPpV/xPCS+" +
"ttQGnDMs6Tej8SaseF4LN9c9cnxNj6VxI8Q+3em9Hx+c3PmW1UDztMZtXVLEfdymbGAJ60kJGZQm" +
"tH99bE8YGN/wd/mgxdG7NFDb8/ZohryYA5HguHhI5uYO27vyoqtrmAiXr31JX/V48CuY8R8FJhxE" +
"eeEAQWk9HnYlFmMJoRKG03QLtUJ7/93FvpXXJ7wM/6Za4l71UEu5pWkoucv0Be0tm95vmUdy5t5k" +
"tpbPbe8B2vmsi7+rl2Nf4yVaUlLHSQXu7r8tw1JyT+ivhQBaAhZUxBSC5EPpPtMKVDzi3z/+HZHJ" +
"7K/7IvC/CRhZ6Ep6evGGyXJS3kAsp3SGcgLKc7uSktBhrW7ZFq32r/HHCVbb0P9fBSYOTpIoJ5SE" +
"7GUnpHbrbG8EzsfWfwgwAEfC/ToQIhkhAAAAAElFTkSuQmCC"
, params: "source=hp&channel=np"
, links: {
advanced: "http://www.google.com/advanced_search"
@ -50,7 +109,33 @@ const SEARCH_ENGINES = {
, "Яндекс":
{
image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAArCAMAAAC5Mt3fAAABnlBMVEX/////AAD/AwP/Bgb/CQn/DAz/Dw//EhL/FRX/GBj/Gxv/Hh7/ISH/JCT/Jyf/MDD/MzP/Pz//QkL/SEj/S0v/Tk7/UVH/YGD/Zmb/aWn/bGz/cnL/dXX/eHj/e3v/fn7/hIT/jY3/k5P/lpb/mZn/nJz/n5//paX/q6v/rq7/tLT/w8P/xsb/z8//1dX/2Nj/29v/3t7/4eH/5OT/7e3/8PD/8/P/9vb/+fn//Pz8/Pz5+fn29vbz8/Pw8PDt7e3q6urn5+fk5OTh4eHe3t7b29vY2NjS0tLPz8/MzMzJycnGxsbDw8PAwMC9vb26urq0tLSurq6rq6uoqKilpaWioqKfn5+cnJyZmZmWlpaTk5ONjY2KioqHh4eEhISBgYF+fn57e3t4eHh1dXVycnJvb29sbGxpaWlmZmZjY2NgYGBdXV1aWlpXV1dUVFRRUVFOTk5LS0tISEhFRUVCQkI/Pz88PDw5OTk2NjYzMzMwMDAtLS0qKionJyckJCQhISEeHh4bGxsVFRUSEhIPDw8MDAwJCQkGBgYDAwMAAABXxKaDAAAAAXRSTlMAQObYZgAAA3pJREFUWMPt1ulbG1UUx/HfQVsUrK1a9612c6t16SZBlkpTKLIkiG26KBSLoTVUdpiUQiIJyfe/9sXcYZJJSFsfx1c9b5J57p18cu5y7pWex/OIPS6cPHrkBTOzQ/EZp9rNxcHYjPfMzOzVI50xIifN7OXPz0sn4kO+e9Hs8DkpVuQDs7azihfpajd7SzEjZ8zsdNzIcTM7Gzfyvpldiht5x8z0L5BE6s6t4f2bhzJTmSt1SDSTMvwiSXdhw28aJAj35vAmwGKPJOkeLEvSzyUYk6S+BQBWRh3yoZl9HUGAm5KUhUd+08geMuj/0ZL/tCTXb01Sd8H/VN9j1/meQ043WV1NkarnPQ6QRB7WU9O4jB1yAxiWpAXYuTGeo9gTbHgze/NpkJKfz6AkXYdSn3QfNkMksQ3zkjQO1VFJ6bG9SXnNrO3Mk5AUFGuQDXgg6RqQ3EMmoZKUpFVYiKyDL82s85snI1shMgCkJSXKkAmQxCbMyrWmmhQvO3js25bIBGyEyKTLQHn4I0DS/hBKN6H6Q8Oaftes/tBqRDLwV4jMQiUhScv++srCmjz4VZL0595bdfFpRwOSy2QymdW97lOQDZEFKEqSFmHdISkodsul93C/KvlKeDIShkPmYDpE8rDrVcIOWfDybjlLJTc30XjD7MD5ujmJIEswESKFSIcs7AK3/HoDzDQxvjCzj+snPjJcBUiGyN9QmHvoeZ7nefNuuBZxe6+3OdJ12Kz9QqvV1QvlRIjswGLtD2RhbRi4I0l9NB2uE2Z2rOU+GYdVhci2q1G1iJZhp9cN13yDcbHD7KWLLZEZf3kGyDpsNyAjwLT8PPMNyEdm9knrHe/5JT5A7gP9UUQrUO6XtALVvohx7oBZx6WWSBJWpPodf7sBGXO1fQa/2NTG22Z2vHWBvOvOogDp2YXiQBTRBlR+lIaAYn0qX7WZdX7fEukpuXkeCQ6MLFC4GkVSwJykNWBjoBZ53cxOtTxPbm9B0fM8/9AqPpB0uQSwPDfvlSdCRB5Uk9JQBagszS3mC+E+PNTVEslRFwVJGqsEj7M1SBrISRrdCVovS5KOmrV9Fp7E7ZKkKkyGF4lmiIYeAVD6vd+/SKwEBbN6VdLgll9y0k99+cm5vNytxQ2BfspMXb/Wvd9LY5nfJpLPcDPbB/lv439BspTHg+9XKv417hniH6z+4JNllI0iAAAAAElFTkSuQmCC"
image: "data:image/png;base64," +
"iVBORw0KGgoAAAANSUhEUgAAAEYAAAAcCAYAAADcO8kVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ" +
"bWFnZVJlYWR5ccllPAAABWFJREFUeNrsWWtsVEUUnltLH7tbaeuDbfCBojUoBTGmooLE+Igx+gON" +
"RvEJhEQNUdEYA0Ji4xNf2Bg1iBJJrGBC+CEBNYoxxmh94CMKRE2MitBqoZRi6bbdZT3TfhM/TmZ2" +
"u5jGsOEkX8/0ztzp3HPP4zu3UTabNUfEI9YwgzAxjUbBGkG7IAv0CwYE53rWC+KChFloRh329igN" +
"zD8keJR+P2DvEbgnrjp4eWT65GerSZuU6FWii9Fj5pGHvC6ow/WpdP1C7SV3Bm18eNpDG2a0oA0P" +
"v0qFSn3IMPOKxChsmBJ1/TpBEuNn1NxRB8XOoJSYRabfrCiG0FGiDXMZ9HeC73PfGpkOST0vmYGi" +
"LEraMCdB/5jP46xhnhaj7C3Sal2qjFSDcU8eb4m2m4xpHvKWYwSTBd2Cr1HBrIwVnCXYIdiiNrDh" +
"Wi8YQLVzZ+mDt/ar9acK5gqOE6wTvKvmE4JzsN83ghSu1+AMMcGngr/pnnHYM4nzrRX8EapKm5Fc" +
"3/bwlAn/Jt/EtJdNmdvidjxcpyrjT+D6Fx7LPoA5jf3ktU5metY9rtZcRHNn0vV3cO0rtf6GwN9v" +
"DCXfX6AbVLL1hJJOxIM6UtwnJG7ORuIaMl5W7W297g2MmwR3YLxQcDmty3jOdongCrrXyRTBaoyf" +
"x5qdgjZ4qzfHbCQ3mzXcChcYH8hhIGf0zwQ3Ch6k8/Ae9yEM3hc8LFguWIm5uwIvwYXhPdA2RNbT" +
"/BLoFsECwXsw1gUIZa9h7NvZivGLgkk010eHjv5jbitXD1HiWVMhuB7jDXR9E/R0Qa3nPvvmTxZc" +
"7fGWyQhNK6/R9b8Ev4aSr0HyunWQ3Q/li8/hdh8JTiOD+DpPa7jegHtriUN35zDMRMEJGH9J17dB" +
"18KzO9V9NvndjbH1sB9objp0u+CT4VYlJ5txKLvpDMFsIJ/EwYOs9bsEp+RYeyz0nx7y6ORsGu8K" +
"EM2kx1ts7rkXL+YxNd8I/TOcoCDDOB5jY/Fj/P4cEmVTjr0SlKNCOcjJ8fQgodAcQ/d/i/BLK8Oo" +
"ZtYcLVgGD1wq2K7mx0LvKITHaFlCbny/oI4M43uQDJJkL3KH5RWnB/auh96ax9AGnKQdoZNAyO4T" +
"VHv4VobC+XzPntWUMgpivtwzufbgWbVpSHYh4V0DnrA6YETrCWdgvGUYIboX9KEahqlFcq0GT2HZ" +
"jwrXBW4zJ/C8FYdqmEWUb94aZniUUbXJVbmm0N6/5zjbPnohcfKePiDlSfBJeO0r9Bx8pi7oEw/F" +
"MPMp8S0roARHar+QYS6FXp9nv230dicVcA7LaZoxHo/ncfIbEdi6Qgxje4vFRL5aRqA/uxn6Vc9c" +
"muK/lXqeuQXsPwZMdi0RPedxH1AFva0QwyygavDkCBjlFuy/HJWhksLQgOVyxWqh3mYx7RND2Pi8" +
"0n1+baawmU9e2o6x/XR7raIQVb4mskGQQaO4ydNENlATeTE1kXOQc/agXDpZqhq42dQL2US9G1Wl" +
"G5XEzaWJbyTBddzcTuSmAYTMOKybQWsmeppIbk5nqcbxJ1RHO37B10TeRL3KU543kUKF0J8leqgq" +
"8ae8PdAd6ltPL954LXQV/m4HEbgaYqjT6KNZHWhAKd5+mzpDN4WflUdw5koweitv4lldX2QpxQSc" +
"/UOfx9jvvTHBKP+/RmKRoHwIiYg8pgQJsszTKFYSV2qC0VcShyqnqlEKRpolqsAyFfnpKmLOnOgr" +
"VAVirhYnYzsZLbgSe57nwtL375N8H+Oy3H2qKpAKEL5eVc65E04rD2NW66uWrUDobKnAnPs7PR5+" +
"tLFQHjMS0knhEZLdim/8bxId+RetX/4RYACXlwEEPBQycwAAAABJRU5ErkJggg=="
}
};
@ -198,6 +283,10 @@ function showSnippets()
if (links.length != 1)
return; // Something is messed up in this entry, we support just 1 link.
links[0].href = DEFAULT_SNIPPETS_URLS[randIndex];
defaultSnippetsElt.addEventListener("click", function(aEvent) {
if (aEvent.target.nodeName != "a")
window.location = links[0].href;
}, false);
}
entry.hidden = false;
}

View File

@ -23,6 +23,8 @@
#
# Contributor(s):
# Marco Bonardo <mak77@bonardo.net> (original author)
# Mihai Sucan <mihai.sucan@gmail.com>
# Stephen Horlander <shorlander@mozilla.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
@ -47,6 +49,8 @@
%globalDTD;
<!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
%aboutHomeDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
%browserDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
@ -63,32 +67,45 @@
</head>
<body dir="&locale.dir;" onload="onLoad(event)">
<div id="pageContainer">
<div id="topSection">
<div id="brandStart">
&abouthome.brandStart;
<div id="brandStartSpacer" />
<div id="brandStart">
<img id="brandStartLogo" src="chrome://branding/content/about-logo.png" alt="" />
</div>
<div id="searchContainer">
<form name="searchForm" id="searchForm" onsubmit="onSearchSubmit(event)">
<div id="searchLogoContainer"><img id="searchEngineLogo" /></div>
<div id="searchInputContainer">
<input type="text" name="searchText" value="" id="searchText" maxLength="256" />
</div>
<div id ="searchContainer">
<img id="searchEngineLogo"/>
<form name="searchForm" onsubmit="onSearchSubmit(event)">
<input type="text" name="searchText" value="" id="searchText" maxLength="256"/>
<input type="submit" value="&abouthome.searchEngineButton.label;"/>
<span id="searchEngineLinks">
<a hidden="true" id="searchEngineAdvancedLink">&abouthome.searchEngineLinks.advanced;</a>
<a hidden="true" id="searchEngineAdvancedPreferences">&abouthome.searchEngineLinks.preferences;</a>
</span>
</form>
<div id="searchButtons">
<input id="searchSubmit" type="submit" value="&abouthome.searchEngineButton.label;" />
<span id="searchEngineLinks">
<a hidden="true" id="searchEngineAdvancedLink">&abouthome.searchEngineLinks.advanced;</a>
<a hidden="true" id="searchEngineAdvancedPreferences">&abouthome.searchEngineLinks.preferences;</a>
</span>
</div>
</form>
</div>
<div id="contentContainer">
<div id="snippetContainer">
<div id="defaultSnippets">
<span hidden="true">&abouthome.defaultSnippet1.v1;</span>
<span hidden="true">&abouthome.defaultSnippet2.v1;</span>
</div>
<div id="snippets" hidden="true"/>
</div>
<div id="defaultSnippets">
<span hidden="true">&abouthome.defaultSnippet1.v1;</span>
<span hidden="true">&abouthome.defaultSnippet2.v1;</span>
<div id="sessionRestoreContainer">
<button id="restorePreviousSession">&historyRestoreLastSession.label;</button>
</div>
<div id="snippets" hidden="true"/>
<div id="bottomSection">
<div id="aboutMozilla">
<a href="http://www.mozilla.com/about/">&abouthome.aboutMozilla;</a>
</div>
</div>
<div id="bottomSection">
<div id="aboutMozilla">
<a href="http://www.mozilla.com/about/">&abouthome.aboutMozilla;</a>
</div>
</div>
</body>

View File

@ -98,11 +98,6 @@
label="&helpTroubleshootingInfo.label;"
oncommand="openTroubleshootingPage()"
onclick="checkForMiddleClick(this, event);"/>
<menuitem id="releaseNotes"
accesskey="&helpReleaseNotes.accesskey;"
label="&helpReleaseNotes.label;"
oncommand="openReleaseNotes()"
onclick="checkForMiddleClick(this, event);"/>
<menuitem id="feedbackPage"
accesskey="&helpFeedbackPage.accesskey;"
label="&helpFeedbackPage.label;"

View File

@ -38,6 +38,10 @@
# ***** END LICENSE BLOCK *****
<menubar id="main-menubar"
onpopupshowing="if (event.target.parentNode.parentNode == this &amp;&amp;
!('@mozilla.org/widget/nativemenuservice;1' in Cc))
this.setAttribute('openedwithkey',
event.target.parentNode.openedWithKey);"
style="border:0px;padding:0px;margin:0px;-moz-appearance:none">
<menu id="file-menu" label="&fileMenu.label;"
accesskey="&fileMenu.accesskey;">
@ -429,9 +433,10 @@
#endif
context="placesContext"
openInTabs="children"
oncommand="BookmarksEventHandler.onCommand(event);"
onclick="BookmarksEventHandler.onClick(event);"
onpopupshowing="if (!this.parentNode._placesView)
oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
onpopupshowing="PlacesCommandHook.updateBookmarkAllTabsCommand();
if (!this.parentNode._placesView)
new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
tooltip="bhTooltip" popupsinherittooltip="true">
<menuitem id="menu_bookmarkThisPage"
@ -459,6 +464,7 @@
</menu>
<menuitem id="menu_bookmarkAllTabs"
label="&addCurPagesCmd.label;"
class="show-only-for-keyboard"
command="Browser:BookmarkAllTabs"
key="bookmarkAllTabsKb"/>
<menuitem id="bookmarksShowAll"

View File

@ -386,30 +386,21 @@ var PlacesCommandHook = {
},
/**
* This function returns a list of nsIURI objects characterizing the
* tabs currently open in the browser. The URIs will appear in the
* list in the order in which their corresponding tabs appeared. However,
* only the first instance of each URI will be returned.
*
* @returns a list of nsIURI objects representing unique locations open
* List of nsIURI objects characterizing the tabs currently open in the
* browser, modulo pinned tabs. The URIs will be in the order in which their
* corresponding tabs appeared and duplicates are discarded.
*/
_getUniqueTabInfo: function BATC__getUniqueTabInfo() {
var tabList = [];
var seenURIs = {};
let tabs = gBrowser.visibleTabs;
for (let i = 0; i < tabs.length; ++i) {
let uri = tabs[i].linkedBrowser.currentURI;
// skip redundant entries
if (uri.spec in seenURIs)
continue;
// add to the set of seen URIs
seenURIs[uri.spec] = null;
tabList.push(uri);
}
return tabList;
get uniqueCurrentPages() {
let uniquePages = {};
let URIs = [];
gBrowser.visibleTabs.forEach(function (tab) {
let spec = tab.linkedBrowser.currentURI.spec;
if (!tab.pinned && !(spec in uniquePages)) {
uniquePages[spec] = null;
URIs.push(tab.linkedBrowser.currentURI);
}
});
return URIs;
},
/**
@ -417,10 +408,26 @@ var PlacesCommandHook = {
* window.
*/
bookmarkCurrentPages: function PCH_bookmarkCurrentPages() {
var tabURIs = this._getUniqueTabInfo();
PlacesUIUtils.showMinimalAddMultiBookmarkUI(tabURIs);
let pages = this.uniqueCurrentPages;
if (pages.length > 1) {
PlacesUIUtils.showMinimalAddMultiBookmarkUI(pages);
}
},
/**
* Updates disabled state for the "Bookmark All Tabs" command.
*/
updateBookmarkAllTabsCommand:
function PCH_updateBookmarkAllTabsCommand() {
// There's nothing to do in non-browser windows.
if (window.location.href != getBrowserURL())
return;
// Disable "Bookmark All Tabs" if there are less than two
// "unique current pages".
goSetCommandEnabled("Browser:BookmarkAllTabs",
this.uniqueCurrentPages.length >= 2);
},
/**
* Adds a Live Bookmark to a feed associated with the current page.
@ -710,8 +717,10 @@ var BookmarksEventHandler = {
* If the click came through a menu, close the menu.
* @param aEvent
* DOMEvent for the click
* @param aView
* The places view which aEvent should be associated with.
*/
onClick: function BEH_onClick(aEvent) {
onClick: function BEH_onClick(aEvent, aView) {
// Only handle middle-click or left-click with modifiers.
#ifdef XP_MACOSX
var modifKey = aEvent.metaKey || aEvent.shiftKey;
@ -741,11 +750,11 @@ var BookmarksEventHandler = {
// is middle-clicked or when a non-bookmark item except for Open in Tabs)
// in a bookmarks menupopup is middle-clicked.
if (target.localName == "menu" || target.localName == "toolbarbutton")
PlacesUIUtils.openContainerNodeInTabs(target._placesNode, aEvent);
PlacesUIUtils.openContainerNodeInTabs(target._placesNode, aEvent, aView);
}
else if (aEvent.button == 1) {
// left-clicks with modifier are already served by onCommand
this.onCommand(aEvent);
this.onCommand(aEvent, aView);
}
},
@ -755,11 +764,13 @@ var BookmarksEventHandler = {
* Opens the item.
* @param aEvent
* DOMEvent for the command
* @param aView
* The places view which aEvent should be associated with.
*/
onCommand: function BEH_onCommand(aEvent) {
onCommand: function BEH_onCommand(aEvent, aView) {
var target = aEvent.originalTarget;
if (target._placesNode)
PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent);
PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent, aView);
},
fillInBHTooltip: function BEH_fillInBHTooltip(aDocument, aEvent) {

View File

@ -91,11 +91,10 @@
<!-- work-around bug 392512 -->
<command id="Browser:AddBookmarkAs"
oncommand="PlacesCommandHook.bookmarkCurrentPage(true, PlacesUtils.bookmarksMenuFolderId);"/>
<!-- The command is disabled for the hidden window. Otherwise its enabled
state is handled by gBookmarkAllTabsHandler. -->
<!-- The command disabled state must be manually updated through
PlacesCommandHook.updateBookmarkAllTabsCommand() -->
<command id="Browser:BookmarkAllTabs"
oncommand="gBookmarkAllTabsHandler.doCommand();"
disabled="true"/>
oncommand="PlacesCommandHook.bookmarkCurrentPages();"/>
<command id="Browser:Home" oncommand="BrowserHome();"/>
<command id="Browser:Back" oncommand="BrowserBack();" disabled="true"/>
<command id="Browser:BackOrBackDuplicate" oncommand="BrowserBack(event);" disabled="true">
@ -304,7 +303,7 @@
<key id="addBookmarkAsKb" key="&bookmarkThisPageCmd.commandkey;" command="Browser:AddBookmarkAs" modifiers="accel"/>
# Accel+Shift+A-F are reserved on GTK2
#ifndef MOZ_WIDGET_GTK2
<key id="bookmarkAllTabsKb" key="&bookmarkThisPageCmd.commandkey;" command="Browser:BookmarkAllTabs" modifiers="accel,shift"/>
<key id="bookmarkAllTabsKb" key="&bookmarkThisPageCmd.commandkey;" oncommand="PlacesCommandHook.bookmarkCurrentPages();" modifiers="accel,shift"/>
<key id="manBookmarkKb" key="&bookmarksCmd.commandkey;" command="Browser:ShowAllBookmarks" modifiers="accel,shift"/>
#else
<key id="manBookmarkKb" key="&bookmarksGtkCmd.commandkey;" command="Browser:ShowAllBookmarks" modifiers="accel,shift"/>

View File

@ -192,6 +192,11 @@ splitmenu {
}
%endif
/* Hide menu elements intended for keyboard access support */
#main-menubar[openedwithkey=false] .show-only-for-keyboard {
display: none;
}
/* ::::: location bar ::::: */
#urlbar {
-moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
@ -513,3 +518,27 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
browser[tabmodalPromptShowing] {
-moz-user-focus: none !important;
}
/* Status panel */
statuspanel {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#statuspanel");
position: fixed;
margin-top: -3em;
left: 0;
}
statuspanel:-moz-locale-dir(ltr)[mirror],
statuspanel:-moz-locale-dir(rtl):not([mirror]) {
left: auto;
right: 0;
}
statuspanel[label=""] {
visibility: collapse;
}
.statuspanel-inner {
height: 3em;
-moz-box-align: end;
}

View File

@ -1550,9 +1550,6 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
PlacesToolbarHelper.init();
// bookmark-all-tabs command
gBookmarkAllTabsHandler.init();
ctrlTab.readPref();
gPrefService.addObserver(ctrlTab.prefName, ctrlTab, false);
gPrefService.addObserver(allTabs.prefName, allTabs, false);
@ -1736,8 +1733,8 @@ function nonBrowserWindowStartup()
'Browser:SendLink', 'cmd_pageSetup', 'cmd_print', 'cmd_find', 'cmd_findAgain',
'viewToolbarsMenu', 'viewSidebarMenuMenu', 'Browser:Reload',
'viewFullZoomMenu', 'pageStyleMenu', 'charsetMenu', 'View:PageSource', 'View:FullScreen',
'viewHistorySidebar', 'Browser:AddBookmarkAs', 'View:PageInfo', 'Tasks:InspectPage',
'Browser:ToggleTabView'];
'viewHistorySidebar', 'Browser:AddBookmarkAs', 'Browser:BookmarkAllTabs',
'View:PageInfo', 'Tasks:InspectPage', 'Browser:ToggleTabView', ];
var element;
for (var id in disabledItems)
@ -2553,6 +2550,19 @@ function BrowserImport()
#endif
}
/**
* Handle load of some pages (about:*) so that we can make modifications
* to the DOM for unprivileged pages.
*/
function BrowserOnAboutPageLoad(document) {
if (/^about:home$/i.test(document.documentURI)) {
let ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
getService(Components.interfaces.nsISessionStore);
if (!ss.canRestoreLastSession)
document.getElementById("sessionRestoreContainer").hidden = true;
}
}
/**
* Handle command events bubbling up from error page content
*/
@ -2675,6 +2685,15 @@ function BrowserOnClick(event) {
);
}
}
else if (/^about:home$/i.test(errorDoc.documentURI)) {
if (ot == errorDoc.getElementById("restorePreviousSession")) {
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
if (ss.canRestoreLastSession)
ss.restoreLastSession();
errorDoc.getElementById("sessionRestoreContainer").hidden = true;
}
}
}
/**
@ -4026,6 +4045,10 @@ var XULBrowserWindow = {
delete this.reloadCommand;
return this.reloadCommand = document.getElementById("Browser:Reload");
},
get statusTextField () {
delete this.statusTextField;
return this.statusTextField = document.getElementById("statusbar-display");
},
get isImage () {
delete this.isImage;
return this.isImage = document.getElementById("isImage");
@ -4051,19 +4074,23 @@ var XULBrowserWindow = {
delete this.throbberElement;
delete this.stopCommand;
delete this.reloadCommand;
delete this.statusTextField;
delete this.statusText;
},
setJSStatus: function (status) {
this.jsStatus = status;
this.updateStatusField();
},
setJSDefaultStatus: function (status) {
this.jsDefaultStatus = status;
this.updateStatusField();
},
setDefaultStatus: function (status) {
this.defaultStatus = status;
this.updateStatusField();
},
setOverLink: function (url, anchorElt) {
@ -4076,6 +4103,21 @@ var XULBrowserWindow = {
}
},
updateStatusField: function () {
var text;
if (this._busyUI)
text = this.status;
if (!text)
text = this.jsStatus || this.jsDefaultStatus || this.defaultStatus;
// check the current value so we don't trigger an attribute change
// and cause needless (slow!) UI updates
if (this.statusText != text) {
this.statusTextField.label = text;
this.statusText = text;
}
},
// Called before links are navigated to to allow us to retarget them if needed.
onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
// Don't modify non-default targets or targets that aren't in top-level app
@ -4177,19 +4219,12 @@ var XULBrowserWindow = {
if (location.spec != "about:blank") {
switch (aStatus) {
case Components.results.NS_BINDING_ABORTED:
msg = gNavigatorBundle.getString("nv_stopped");
break;
case Components.results.NS_ERROR_NET_TIMEOUT:
msg = gNavigatorBundle.getString("nv_timeout");
break;
}
}
}
// If msg is false then we did not have an error (channel may have
// been null, in the case of a stray image load).
if (!msg && (!location || location.spec != "about:blank"))
msg = gNavigatorBundle.getString("nv_done");
this.status = "";
this.setDefaultStatus(msg);
@ -4371,6 +4406,7 @@ var XULBrowserWindow = {
onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
this.status = aMessage;
this.updateStatusField();
},
// Properties used to cache security state used to update the UI
@ -4623,6 +4659,9 @@ var TabsProgressListener = {
aBrowser.removeEventListener("click", BrowserOnClick, false);
aBrowser.removeEventListener("pagehide", arguments.callee, true);
}, true);
// We also want to make changes to page UI for unprivileged about pages.
BrowserOnAboutPageLoad(aWebProgress.DOMWindow.document);
}
},
@ -7146,41 +7185,6 @@ function formatURL(aFormat, aIsPref) {
return aIsPref ? formatter.formatURLPref(aFormat) : formatter.formatURL(aFormat);
}
/**
* This also takes care of updating the command enabled-state when tabs are
* created or removed.
*/
var gBookmarkAllTabsHandler = {
init: function () {
this._command = document.getElementById("Browser:BookmarkAllTabs");
gBrowser.tabContainer.addEventListener("TabOpen", this, true);
gBrowser.tabContainer.addEventListener("TabClose", this, true);
gBrowser.tabContainer.addEventListener("TabShow", this, true);
gBrowser.tabContainer.addEventListener("TabHide", this, true);
this._updateCommandState();
},
_updateCommandState: function BATH__updateCommandState() {
let remainingTabs = gBrowser.visibleTabs.filter(function(tab) {
return gBrowser._removingTabs.indexOf(tab) == -1;
});
if (remainingTabs.length > 1)
this._command.removeAttribute("disabled");
else
this._command.setAttribute("disabled", "true");
},
doCommand: function BATH_doCommand() {
PlacesCommandHook.bookmarkCurrentPages();
},
// nsIDOMEventListener
handleEvent: function(aEvent) {
this._updateCommandState();
}
};
/**
* Utility object to handle manipulations of the identity indicators in the UI
*/
@ -8290,6 +8294,12 @@ var TabContextMenu = {
document.getElementById("context_closeOtherTabs").disabled = unpinnedTabs <= 1;
document.getElementById("context_closeOtherTabs").hidden = this.contextTab.pinned;
// Hide "Bookmark All Tabs" for a pinned tab. Update its state if visible.
let bookmarkAllTabs = document.getElementById("context_bookmarkAllTabs");
bookmarkAllTabs.hidden = this.contextTab.pinned;
if (!bookmarkAllTabs.hidden)
PlacesCommandHook.updateBookmarkAllTabsCommand();
// Hide "Move to Group" if it's a pinned tab.
document.getElementById("context_tabViewMenu").hidden = this.contextTab.pinned;
}

View File

@ -649,8 +649,8 @@
placespopup="true"
context="placesContext"
openInTabs="children"
oncommand="BookmarksEventHandler.onCommand(event);"
onclick="BookmarksEventHandler.onClick(event);"
oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
onpopupshowing="BookmarksMenuButton.onPopupShowing(event);
if (!this.parentNode._placesView)
new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
@ -749,8 +749,8 @@
<hbox flex="1"
id="PlacesToolbar"
context="placesContext"
onclick="BookmarksEventHandler.onClick(event);"
oncommand="BookmarksEventHandler.onCommand(event);"
onclick="BookmarksEventHandler.onClick(event, this._placesView);"
oncommand="BookmarksEventHandler.onCommand(event, this._placesView);"
tooltip="bhTooltip"
popupsinherittooltip="true">
<toolbarbutton class="bookmark-item bookmarks-toolbar-customize"
@ -981,6 +981,7 @@
contentcontextmenu="contentAreaContextMenu"
autocompletepopup="PopupAutoComplete"
onclick="return contentAreaClick(event, false);"/>
<statuspanel id="statusbar-display" label=""/>
</vbox>
<vbox id="browser-border-end" hidden="true" layer="true"/>
</hbox>

View File

@ -103,7 +103,7 @@ let Change = {
}
else {
document.getElementById("generatePassphraseButton").hidden = false;
document.getElementById("generatePassphraseButton").hidden = false;
document.getElementById("passphraseBackupButtons").hidden = false;
this._passphraseBox.setAttribute("readonly", "true");
let pp = Weave.Service.passphrase;
if (Weave.Utils.isPassphrase(pp))

View File

@ -112,6 +112,11 @@
<binding id="notification" extends="chrome://global/content/bindings/notification.xml#notification">
<content>
<xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type">
<xul:toolbarbutton ondblclick="event.stopPropagation();"
class="messageCloseButton tabbable"
xbl:inherits="hidden=hideclose"
tooltiptext="&closeNotification.tooltip;"
oncommand="document.getBindingParent(this).close()"/>
<xul:hbox anonid="details" align="center" flex="1">
<xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image"/>
<xul:description anonid="messageText" class="messageText" xbl:inherits="xbl:text=label"/>
@ -120,13 +125,7 @@
<xul:hbox oncommand="document.getBindingParent(this)._doButtonCommand(event);">
<children/>
</xul:hbox>
<xul:spacer flex="1"/>
</xul:hbox>
<xul:toolbarbutton ondblclick="event.stopPropagation();"
class="messageCloseButton tabbable"
xbl:inherits="hidden=hideclose"
tooltiptext="&closeNotification.tooltip;"
oncommand="document.getBindingParent(this).close()"/>
</xul:hbox>
</content>
<implementation>

View File

@ -3692,4 +3692,39 @@
</handlers>
</binding>
<binding id="statuspanel" display="xul:hbox">
<content>
<xul:hbox class="statuspanel-inner">
<xul:label class="statuspanel-label"
role="status"
xbl:inherits="value=label,mirror"
flex="1"
crop="end"/>
</xul:hbox>
</content>
<implementation>
<property name="label">
<setter>
if (!this.label)
this.removeAttribute("mirror");
this.setAttribute("label", val);
return val;
</setter>
<getter>
return this.getAttribute("label");
</getter>
</property>
</implementation>
<handlers>
<handler event="mouseover">
if (this.hasAttribute("mirror"))
this.removeAttribute("mirror");
else
this.setAttribute("mirror", "true");
</handler>
</handlers>
</binding>
</bindings>

View File

@ -288,7 +288,7 @@ Drag.prototype = {
Trenches.hideGuides();
this.item.isDragging = false;
if (this.parent && !this.parent.locked.close && this.parent != this.item.parent &&
if (this.parent && this.parent != this.item.parent &&
this.parent.isEmpty()) {
this.parent.close();
}

View File

@ -59,7 +59,6 @@
//
// Possible options:
// id - specifies the groupItem's id; otherwise automatically generated
// locked - see <Item.locked>; default is {}
// userSize - see <Item.userSize>; default is null
// bounds - a <Rect>; otherwise based on the locations of the provided elements
// container - a DOM element to use as the container for this groupItem; otherwise will create
@ -79,7 +78,6 @@ function GroupItem(listOfEls, options) {
this.id = options.id || GroupItems.getNextID();
this._isStacked = false;
this.expanded = null;
this.locked = (options.locked ? Utils.copy(options.locked) : {});
this.topChild = null;
this.hidden = false;
this.fadeAwayUndoButtonDelay = 15000;
@ -193,34 +191,26 @@ function GroupItem(listOfEls, options) {
self.$titleShield.show();
})
.focus(function() {
if (self.locked.title) {
(self.$title)[0].blur();
return;
}
(self.$title)[0].select();
})
.keydown(handleKeyDown)
.keyup(handleKeyUp);
if (this.locked.title)
this.$title.addClass('name-locked');
else {
this.$titleShield
.mousedown(function(e) {
self.lastMouseDownTarget = (Utils.isLeftClick(e) ? e.target : null);
})
.mouseup(function(e) {
var same = (e.target == self.lastMouseDownTarget);
self.lastMouseDownTarget = null;
if (!same)
return;
this.$titleShield
.mousedown(function(e) {
self.lastMouseDownTarget = (Utils.isLeftClick(e) ? e.target : null);
})
.mouseup(function(e) {
var same = (e.target == self.lastMouseDownTarget);
self.lastMouseDownTarget = null;
if (!same)
return;
if (!self.isDragging) {
self.$titleShield.hide();
(self.$title)[0].focus();
}
});
}
if (!self.isDragging) {
self.$titleShield.hide();
(self.$title)[0].focus();
}
});
// ___ Stack Expander
this.$expander = iQ("<div/>")
@ -238,13 +228,6 @@ function GroupItem(listOfEls, options) {
self.addAppTab(xulTab);
});
// ___ locking
if (this.locked.bounds)
$container.css({cursor: 'default'});
if (this.locked.close)
this.$closeButton.hide();
// ___ Undo Close
this.$undoContainer = null;
this._undoButtonTimeoutId = null;
@ -263,8 +246,7 @@ function GroupItem(listOfEls, options) {
// ___ Finish Up
this._addHandlers($container);
if (!this.locked.bounds)
this.setResizable(true, immediately);
this.setResizable(true, immediately);
GroupItems.register(this);
@ -318,7 +300,6 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
var data = {
bounds: this.getBounds(),
userSize: null,
locked: Utils.copy(this.locked),
title: this.getTitle(),
id: this.id
};
@ -595,11 +576,10 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
}
});
this.droppable(false);
this._createUndoButton();
} else {
if (!this.locked.close)
this.close();
}
} else
this.close();
this._makeClosestTabActive();
},
@ -630,11 +610,11 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// ----------
// Function: closeIfEmpty
// Closes the group if it's empty, unlocked, has no title, is closable, and
// Closes the group if it's empty, has no title, is closable, and
// autoclose is enabled (see pauseAutoclose()). Returns true if the close
// occurred and false otherwise.
closeIfEmpty: function() {
if (!this._children.length && !this.locked.close && !this.getTitle() &&
if (!this._children.length && !this.getTitle() &&
!GroupItems.getUnclosableGroupItemId() &&
!GroupItems._autoclosePaused) {
this.close();
@ -653,6 +633,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
this.hidden = false;
this.$undoContainer.remove();
this.$undoContainer = null;
this.droppable(true);
iQ(this.container).show().animate({
"-moz-transform": "scale(1)",
@ -1229,16 +1210,14 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
let angleAccum = 0;
children.forEach(function GroupItem__stackArrange_apply(child, index) {
if (!child.locked.bounds) {
child.setZ(zIndex);
zIndex--;
child.setZ(zIndex);
zIndex--;
// Force a recalculation of height because we've changed how the title
// is shown.
child.setBounds(box, !animate, {force:true});
child.setRotation((UI.rtl ? -1 : 1) * angleAccum);
angleAccum += angleDelta;
}
// Force a recalculation of height because we've changed how the title
// is shown.
child.setBounds(box, !animate, {force:true});
child.setRotation((UI.rtl ? -1 : 1) * angleAccum);
angleAccum += angleDelta;
});
self._isStacked = true;
@ -1301,12 +1280,10 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// (and skip one for the dropPos)
if (self._dropSpaceActive && index === dropIndex)
index++;
if (!child.locked.bounds) {
child.setBounds(rects[index], !options.animate);
child.setRotation(0);
if (arrangeOptions.z)
child.setZ(arrangeOptions.z);
}
child.setBounds(rects[index], !options.animate);
child.setRotation(0);
if (arrangeOptions.z)
child.setZ(arrangeOptions.z);
index++;
});
@ -1576,9 +1553,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
iQ(this.container).removeClass("acceptsDrop");
}
if (!this.locked.bounds)
this.draggable();
this.draggable();
this.droppable(true);
this.$expander.click(function() {
@ -2381,10 +2356,8 @@ let GroupItems = {
if (shouldUpdateTabBar)
this._updateTabBar();
else if (shouldShowTabView) {
tab._tabViewTabItem.setZoomPrep(false);
else if (shouldShowTabView)
UI.showTabView();
}
},
// ----------

View File

@ -55,7 +55,6 @@
//
// ... and this property:
// defaultSize - a Point
// locked - an object (see below)
//
// Make sure to call _init() from your subclass's constructor.
function Item() {
@ -85,15 +84,6 @@ function Item() {
// The outermost DOM element that describes this item on screen.
this.container = null;
// Variable: locked
// Affects whether an item can be pushed, closed, renamed, etc
//
// The object may have properties to specify what can't be changed:
// .bounds - true if it can't be pushed, dragged, resized, etc
// .close - true if it can't be closed
// .title - true if it can't be renamed
this.locked = null;
// Variable: parent
// The groupItem that this item is a child of
this.parent = null;
@ -158,7 +148,6 @@ Item.prototype = {
Utils.assert(typeof this.close == 'function', 'Subclass must provide close');
Utils.assert(typeof this.save == 'function', 'Subclass must provide save');
Utils.assert(Utils.isPoint(this.defaultSize), 'Subclass must provide defaultSize');
Utils.assert(this.locked, 'Subclass must provide locked');
Utils.assert(Utils.isRect(this.bounds), 'Subclass must provide bounds');
this.container = container;
@ -361,7 +350,7 @@ Item.prototype = {
var bbc = bb.center();
items.forEach(function Item_pushAway_pushOne_pushEach(item) {
if (item == baseItem || item.locked.bounds)
if (item == baseItem)
return;
var data = item.pushAwayData;
@ -424,7 +413,7 @@ Item.prototype = {
var pageBounds = Items.getSafeWindowBounds();
items.forEach(function Item_pushAway_squish(item) {
var data = item.pushAwayData;
if (data.generation == 0 || item.locked.bounds)
if (data.generation == 0)
return;
let apply = function Item_pushAway_squish_apply(item, posStep, posStep2, sizeStep) {
@ -1063,7 +1052,7 @@ let Items = {
var pageBounds = Items.getSafeWindowBounds();
pairs.forEach(function(pair) {
var item = pair.item;
if (item.locked.bounds || item == ignore)
if (item == ignore)
return;
var bounds = pair.bounds;

View File

@ -92,10 +92,8 @@ function TabItem(tab, options) {
this.tabCanvas = new TabCanvas(this.tab, this.$canvas[0]);
this.defaultSize = new Point(TabItems.tabWidth, TabItems.tabHeight);
this.locked = {};
this._hidden = false;
this.isATabItem = true;
this._zoomPrep = false;
this.sizeExtra = new Point();
this.keepProportional = true;
this._hasBeenDrawn = false;
@ -370,7 +368,8 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
}
}
if (tabData.imageData)
let currentUrl = this.tab.linkedBrowser.currentURI.spec;
if (tabData.imageData && tabData.url == currentUrl)
this.showCachedData(tabData);
} else {
// create tab by double click is handled in UI_init().
@ -428,9 +427,6 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
let rect = new Rect(inRect.left, inRect.top,
validSize.x, validSize.y);
if (this._zoomPrep)
this.bounds.copy(rect);
else {
var css = {};
if (rect.left != this.bounds.left || options.force)
@ -520,7 +516,6 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
}
this._hasBeenDrawn = true;
}
UI.clearShouldResizeItems();
@ -625,17 +620,22 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
return;
var self = this;
var $tabEl = this.$container;
var $tabEl = this.$container, $canvas = this.$canvas;
var childHitResult = { shouldZoom: true };
if (this.parent)
childHitResult = this.parent.childHit(this);
this.shouldHideCachedData = true;
TabItems._update(this.tab);
if (childHitResult.shouldZoom) {
// Zoom in!
var tab = this.tab;
var orig = $tabEl.bounds();
function onZoomDone() {
$canvas.css({ '-moz-transform': null });
$tabEl.removeClass("front");
UI.goToTab(tab);
// tab might not be selected because hideTabView() is invoked after
@ -652,9 +652,13 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
let animateZoom = gPrefBranch.getBoolPref("animate_zoom");
if (animateZoom) {
let transform = this.getZoomTransform();
TabItems.pausePainting();
$tabEl.addClass("front")
.animate(this.getZoomRect(), {
$tabEl.addClass("front");
$canvas
.css({ '-moz-transform-origin': transform.transformOrigin })
.animate({ '-moz-transform': transform.transform }, {
duration: 230,
easing: 'fast',
complete: function() {
@ -662,10 +666,6 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
setTimeout(function() {
TabItems.resumePainting();
$tabEl
.css(orig)
.removeClass("front");
}, 0);
}
});
@ -683,11 +683,12 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// Parameters:
// complete - a function to call after the zoom down animation
zoomOut: function TabItem_zoomOut(complete) {
var $tab = this.$container;
let $tab = this.$container, $canvas = this.$canvas;
var self = this;
let onZoomDone = function onZoomDone() {
self.setZoomPrep(false);
$tab.removeClass("front");
$canvas.css("-moz-transform", null);
GroupItems.setActiveOrphanTab(null);
@ -695,22 +696,22 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
complete();
};
this.shouldHideCachedData = true;
TabItems._update(this.tab);
$tab.addClass("front");
let animateZoom = gPrefBranch.getBoolPref("animate_zoom");
if (animateZoom) {
let box = this.getBounds();
box.width -= this.sizeExtra.x;
if (!this.isStacked)
box.height -= this.sizeExtra.y + TabItems.fontSizeRange.max;
else
box.height -= this.sizeExtra.y;
// The scaleCheat of 2 here is a clever way to speed up the zoom-out
// code. See getZoomTransform() below.
let transform = this.getZoomTransform(2);
$canvas.css({
'-moz-transform': transform.transform,
'-moz-transform-origin': transform.transformOrigin
});
TabItems.pausePainting();
$tab.animate({
left: box.left,
top: box.top,
width: box.width,
height: box.height
}, {
$canvas.animate({ "-moz-transform": "scale(1.0)" }, {
duration: 300,
easing: 'cubic-bezier', // note that this is legal easing, even without parameters
complete: function() {
@ -724,65 +725,42 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
},
// ----------
// Function: getZoomRect
// Returns a faux rect (just an object with top, left, width, height)
// which represents the maximum bounds of the tab thumbnail in the zoom
// animation. Note that this is not just the rect of the window itself,
// due to scaleCheat.
getZoomRect: function TabItem_getZoomRect(scaleCheat) {
let $tabEl = iQ(this.container);
let orig = $tabEl.bounds();
// Function: getZoomTransform
// Returns the transform function which represents the maximum bounds of the
// tab thumbnail in the zoom animation.
getZoomTransform: function TabItem_getZoomTransform(scaleCheat) {
// Taking the bounds of the container (as opposed to the canvas) makes us
// immune to any transformations applied to the canvas.
let { left, top, width, height, right, bottom } = this.$container.bounds();
let { innerWidth: windowWidth, innerHeight: windowHeight } = window;
// The scaleCheat is a clever way to speed up the zoom-in code.
// Because image scaling is slowest on big images, we cheat and stop
// the image at scaled-down size and placed accordingly. Because the
// animation is fast, you can't see the difference but it feels a lot
// zippier. The only trick is choosing the right animation function so
// that you don't see a change in percieved animation speed.
// that you don't see a change in percieved animation speed from frame #1
// (the tab) to frame #2 (the half-size image) to frame #3 (the first frame
// of real animation). Choosing an animation that starts fast is key.
if (!scaleCheat)
scaleCheat = 1.7;
let zoomWidth = orig.width + (window.innerWidth - orig.width) / scaleCheat;
let zoomWidth = width + (window.innerWidth - width) / scaleCheat;
let zoomScaleFactor = zoomWidth / width;
let zoomHeight = height * zoomScaleFactor;
let zoomTop = top * (1 - 1/scaleCheat);
let zoomLeft = left * (1 - 1/scaleCheat);
let xOrigin = (left - zoomLeft) / ((left - zoomLeft) + (zoomLeft + zoomWidth - right)) * 100;
let yOrigin = (top - zoomTop) / ((top - zoomTop) + (zoomTop + zoomHeight - bottom)) * 100;
return {
top: orig.top * (1 - 1/scaleCheat),
left: orig.left * (1 - 1/scaleCheat),
width: zoomWidth,
height: (orig.width ? orig.height * zoomWidth / orig.width : 0)
transformOrigin: xOrigin + "% " + yOrigin + "%",
transform: "scale(" + zoomScaleFactor + ")"
};
},
// ----------
// Function: setZoomPrep
// Either go into or return from (depending on <value>) "zoom prep" mode,
// where the tab fills a large portion of the screen in anticipation of
// the zoom out animation.
setZoomPrep: function TabItem_setZoomPrep(value) {
let animateZoom = gPrefBranch.getBoolPref("animate_zoom");
var $div = this.$container;
if (value && animateZoom) {
this._zoomPrep = true;
// The scaleCheat of 2 here is a clever way to speed up the zoom-out code.
// Because image scaling is slowest on big images, we cheat and start the image
// at half-size and placed accordingly. Because the animation is fast, you can't
// see the difference but it feels a lot zippier. The only trick is choosing the
// right animation function so that you don't see a change in percieved
// animation speed from frame #1 (the tab) to frame #2 (the half-size image) to
// frame #3 (the first frame of real animation). Choosing an animation that starts
// fast is key.
$div
.addClass('front')
.css(this.getZoomRect(2));
} else {
let box = this.getBounds();
this._zoomPrep = false;
$div.removeClass('front');
this.setBounds(box, true, {force: true});
}
}
});
@ -974,7 +952,9 @@ let TabItems = {
// ___ label
let label = tab.label;
let $name = tabItem.$tabTitle;
if (!tabItem.isShowingCachedData() && $name.text() != label)
let isLabelUpdateAllowed = !tabItem.isShowingCachedData() ||
tabItem.shouldHideCachedData;
if (isLabelUpdateAllowed && $name.text() != label)
$name.text(label);
// ___ thumbnail

View File

@ -86,12 +86,14 @@ body {
.front {
z-index: 999999 !important;
border-radius: 0 !important;
box-shadow: none !important;
-moz-transform: none !important;
image-rendering: -moz-crisp-edges;
}
.front canvas {
border: none !important;
padding: 1px !important;
}
/* Groups
----------------------------------*/

View File

@ -515,9 +515,6 @@ let UI = {
TabItems.resumePainting();
});
} else {
if (currentTab && currentTab._tabViewTabItem)
currentTab._tabViewTabItem.setZoomPrep(false);
self.setActiveTab(null);
dispatchEvent(event);
@ -744,9 +741,6 @@ let UI = {
if (closingLastOfGroup || closingUnnamedGroup) {
// for the tab focus event to pick up.
self._closedLastVisibleTab = true;
// remove the zoom prep.
if (tab && tab._tabViewTabItem)
tab._tabViewTabItem.setZoomPrep(false);
self.showTabView();
}
}
@ -886,15 +880,6 @@ let UI = {
if (GroupItems.getActiveGroupItem() || GroupItems.getActiveOrphanTab())
GroupItems._updateTabBar();
}
// ___ prepare for when we return to TabView
if (newItem != oldItem) {
if (oldItem)
oldItem.setZoomPrep(false);
if (newItem)
newItem.setZoomPrep(true);
} else if (oldItem)
oldItem.setZoomPrep(true);
},
// ----------
@ -1243,9 +1228,6 @@ let UI = {
itemBounds.width = 1;
itemBounds.height = 1;
items.forEach(function(item) {
if (item.locked.bounds)
return;
var bounds = item.getBounds();
itemBounds = (itemBounds ? itemBounds.union(bounds) : new Rect(bounds));
});
@ -1273,9 +1255,6 @@ let UI = {
var self = this;
var pairs = [];
items.forEach(function(item) {
if (item.locked.bounds)
return;
var bounds = item.getBounds();
bounds.left += (UI.rtl ? -1 : 1) * (newPageBounds.left - self._pageBounds.left);
bounds.left *= scale;

View File

@ -50,7 +50,7 @@ function test() {
is(gBrowser.visibleTabs.length, 1, "Only one tab is visible");
let uris = PlacesCommandHook._getUniqueTabInfo();
let uris = PlacesCommandHook.uniqueCurrentPages;
is(uris.length, 1, "Only one uri is returned");
is(uris[0].spec, tabTwo.linkedBrowser.currentURI.spec, "It's the correct URI");

View File

@ -36,43 +36,67 @@
* ***** END LICENSE BLOCK ***** */
function test() {
waitForExplicitFinish();
// There should be one tab when we start the test
let [origTab] = gBrowser.visibleTabs;
is(gBrowser.visibleTabs.length, 1, "1 tab should be open");
is(Disabled(), true, "Bookmark All Tabs should be hidden");
is(Disabled(), true, "Bookmark All Tabs should be disabled");
// Add a tab
let testTab = gBrowser.addTab();
let testTab1 = gBrowser.addTab();
is(gBrowser.visibleTabs.length, 2, "2 tabs should be open");
is(Disabled(), false, "Bookmark All Tabs should be available");
is(Disabled(), true, "Bookmark All Tabs should be disabled since there are two tabs with the same address");
// Hide the original tab
gBrowser.selectedTab = testTab;
gBrowser.showOnlyTheseTabs([testTab]);
is(gBrowser.visibleTabs.length, 1, "1 tab should be visible");
is(Disabled(), true, "Bookmark All Tabs should be hidden as there is only one visible tab");
let testTab2 = gBrowser.addTab("about:robots");
is(gBrowser.visibleTabs.length, 3, "3 tabs should be open");
// Wait for tab load, the code checks for currentURI.
testTab2.linkedBrowser.addEventListener("load", function () {
testTab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
is(Disabled(), false, "Bookmark All Tabs should be enabled since there are two tabs with different addresses");
// Add a tab that will get pinned
let pinned = gBrowser.addTab();
gBrowser.pinTab(pinned);
is(gBrowser.visibleTabs.length, 2, "2 tabs should be visible now");
is(Disabled(), false, "Bookmark All Tabs should be available as there are two visible tabs");
// Hide the original tab
gBrowser.selectedTab = testTab2;
gBrowser.showOnlyTheseTabs([testTab2]);
is(gBrowser.visibleTabs.length, 1, "1 tab should be visible");
is(Disabled(), true, "Bookmark All Tabs should be disabled as there is only one visible tab");
// Show all tabs
let allTabs = [tab for each (tab in gBrowser.tabs)];
gBrowser.showOnlyTheseTabs(allTabs);
// Add a tab that will get pinned
let pinned = gBrowser.addTab();
is(gBrowser.visibleTabs.length, 2, "2 tabs should be visible now");
is(Disabled(), false, "Bookmark All Tabs should be available as there are two visible tabs");
gBrowser.pinTab(pinned);
is(Hidden(), false, "Bookmark All Tabs should be visible on a normal tab");
is(Disabled(), true, "Bookmark All Tabs should not be available since one tab is pinned");
gBrowser.selectedTab = pinned;
is(Hidden(), true, "Bookmark All Tabs should be hidden on a pinned tab");
// reset the environment
gBrowser.removeTab(testTab);
gBrowser.removeTab(pinned);
is(gBrowser.visibleTabs.length, 1, "only orig is left and visible");
is(gBrowser.tabs.length, 1, "sanity check that it matches");
is(Disabled(), true, "Bookmark All Tabs should be hidden");
is(gBrowser.selectedTab, origTab, "got the orig tab");
is(origTab.hidden, false, "and it's not hidden -- visible!");
// Show all tabs
let allTabs = [tab for each (tab in gBrowser.tabs)];
gBrowser.showOnlyTheseTabs(allTabs);
// reset the environment
gBrowser.removeTab(testTab2);
gBrowser.removeTab(testTab1);
gBrowser.removeTab(pinned);
is(gBrowser.visibleTabs.length, 1, "only orig is left and visible");
is(gBrowser.tabs.length, 1, "sanity check that it matches");
is(Disabled(), true, "Bookmark All Tabs should be hidden");
is(gBrowser.selectedTab, origTab, "got the orig tab");
is(origTab.hidden, false, "and it's not hidden -- visible!");
finish();
}, true);
}
function Disabled() {
document.popupNode = gBrowser.selectedTab;
TabContextMenu.updateContextMenu(document.getElementById("tabContextMenu"));
let command = document.getElementById("Browser:BookmarkAllTabs");
return command.hasAttribute("disabled") && command.getAttribute("disabled") === "true";
}
function Hidden() {
document.popupNode = gBrowser.selectedTab;
TabContextMenu.updateContextMenu(document.getElementById("tabContextMenu"));
return document.getElementById("context_bookmarkAllTabs").hidden;
}

View File

@ -89,9 +89,13 @@ _BROWSER_FILES = \
browser_tabview_bug622835.js \
browser_tabview_bug622872.js \
browser_tabview_bug624265.js \
browser_tabview_bug624931.js \
browser_tabview_bug624727.js \
browser_tabview_bug624953.js \
browser_tabview_bug625269.js \
browser_tabview_bug625424.js \
browser_tabview_bug626368.js \
browser_tabview_bug627288.js \
browser_tabview_bug627736.js \
browser_tabview_bug628165.js \
browser_tabview_dragdrop.js \

View File

@ -38,20 +38,14 @@
function test() {
waitForExplicitFinish();
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
if (TabView.isVisible())
onTabViewWindowLoaded();
else
TabView.show();
newWindowWithTabView(onTabViewWindowLoaded);
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
function onTabViewWindowLoaded(win) {
ok(win.TabView.isVisible(), "Tab View is visible");
ok(TabView.isVisible(), "Tab View is visible");
let contentWindow = document.getElementById("tab-view").contentWindow;
let [originalTab] = gBrowser.visibleTabs;
let contentWindow = win.document.getElementById("tab-view").contentWindow;
let [originalTab] = win.gBrowser.visibleTabs;
let currentGroup = contentWindow.GroupItems.getActiveGroupItem();
@ -63,38 +57,17 @@ function onTabViewWindowLoaded() {
// Create a bunch of tabs in the group
let tabs = [];
tabs.push(gBrowser.loadOneTab("about:blank#0", {inBackground: true}));
tabs.push(gBrowser.loadOneTab("about:blank#1", {inBackground: true}));
tabs.push(gBrowser.loadOneTab("about:blank#2", {inBackground: true}));
tabs.push(gBrowser.loadOneTab("about:blank#3", {inBackground: true}));
tabs.push(gBrowser.loadOneTab("about:blank#4", {inBackground: true}));
tabs.push(gBrowser.loadOneTab("about:blank#5", {inBackground: true}));
tabs.push(gBrowser.loadOneTab("about:blank#6", {inBackground: true}));
tabs.push(win.gBrowser.loadOneTab("about:blank#0", {inBackground: true}));
tabs.push(win.gBrowser.loadOneTab("about:blank#1", {inBackground: true}));
tabs.push(win.gBrowser.loadOneTab("about:blank#2", {inBackground: true}));
tabs.push(win.gBrowser.loadOneTab("about:blank#3", {inBackground: true}));
tabs.push(win.gBrowser.loadOneTab("about:blank#4", {inBackground: true}));
tabs.push(win.gBrowser.loadOneTab("about:blank#5", {inBackground: true}));
tabs.push(win.gBrowser.loadOneTab("about:blank#6", {inBackground: true}));
ok(!group.shouldStack(group._children.length), "Group should not stack.");
is(group._columns, 3, "There should be three columns.");
// PREPARE FINISH:
group.addSubscriber(group, "close", function() {
group.removeSubscriber(group, "close");
ok(group.isEmpty(), "The group is empty again");
contentWindow.GroupItems.setActiveGroupItem(currentGroup);
isnot(contentWindow.GroupItems.getActiveGroupItem(), null, "There is an active group");
is(gBrowser.tabs.length, 1, "There is only one tab left");
is(gBrowser.visibleTabs.length, 1, "There is also only one visible tab");
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
finish();
};
window.addEventListener("tabviewhidden", onTabViewHidden, false);
gBrowser.selectedTab = originalTab;
TabView.hide();
});
// STAGE 1: move the last tab to the third position
let currentTarget = tabs[6]._tabViewTabItem;
let currentPos = currentTarget.getBounds().center();
@ -163,10 +136,9 @@ function onTabViewWindowLoaded() {
is(dropSpaceActiveValues[0], false, "The group began by not showing a dropSpace");
is(dropSpaceActiveValues[dropSpaceActiveValues.length - 1], true, "In the end, the group was showing a dropSpace");
// Get rid of the group and its children
// The group close will trigger a finish().
group.closeAll();
group.closeHidden();
// Close the window and we're done!
win.close();
finish();
}, 6000, false);
},1000);
@ -181,9 +153,7 @@ function simulateSlowDragDrop(srcElement, offsetX, offsetY, contentWindow, time)
// enter drag mode
let dataTransfer;
// contentWindow.Utils.log('offset', offsetX, offsetY);
let bounds = srcElement.getBoundingClientRect();
// contentWindow.Utils.log('original center', bounds.left + bounds.width / 2, bounds.top + bounds.height / 2);
EventUtils.synthesizeMouse(
srcElement, 2, 2, { type: "mousedown" }, contentWindow);

View File

@ -76,9 +76,9 @@ function test() {
"tabview-groups": '{"nextID":3,"activeGroupId":2}',
"tabview-group":
'{"1":{"bounds":{"left":15,"top":5,"width":280,"height":232},' +
'"userSize":null,"locked":{},"title":"","id":1},' +
'"userSize":null,"title":"","id":1},' +
'"2":{"bounds":{"left":309,"top":5,"width":267,"height":226},' +
'"userSize":null,"locked":{},"title":"","id":2}}',
'"userSize":null,"title":"","id":2}}',
"tabview-ui": '{"pageBounds":{"left":0,"top":0,"width":788,"height":548}}'
}, sizemode:"normal"
}]

View File

@ -75,9 +75,9 @@ function test() {
"tabview-groups": '{"nextID":3,"activeGroupId":2}',
"tabview-group":
'{"1":{"bounds":{"left":15,"top":10,"width":320,"height":375},' +
'"userSize":null,"locked":{},"title":"","id":1},' +
'"userSize":null,"title":"","id":1},' +
'"2":{"bounds":{"left":380,"top":5,"width":320,"height":375},' +
'"userSize":null,"locked":{},"title":"","id":2}}',
'"userSize":null,"title":"","id":2}}',
"tabview-ui": '{"pageBounds":{"left":0,"top":0,"width":875,"height":650}}'
}, sizemode:"normal"
}]

View File

@ -38,28 +38,13 @@
function test() {
waitForExplicitFinish();
window.addEventListener('tabviewshown', onTabViewWindowLoaded, false);
TabView.toggle();
}
function onTabViewWindowLoaded() {
window.removeEventListener('tabviewshown', onTabViewWindowLoaded, false);
let [tab] = gBrowser.tabs;
let groupId = tab._tabViewTabItem.parent.id;
let finishTest = function () {
let onTabViewHidden = function () {
window.removeEventListener('tabviewhidden', onTabViewHidden, false);
finish();
}
window.addEventListener('tabviewhidden', onTabViewHidden, false);
TabView.hide();
}
TabView.moveTabTo(tab, groupId);
is(tab._tabViewTabItem.parent.id, groupId, 'tab did not change its group');
finishTest();
showTabView(function () {
let [tab] = gBrowser.tabs;
let groupId = tab._tabViewTabItem.parent.id;
TabView.moveTabTo(tab, groupId);
is(tab._tabViewTabItem.parent.id, groupId, 'tab did not change its group');
hideTabView(finish);
});
}

View File

@ -1,39 +1,5 @@
/* ***** 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 a test for bug 608037.
*
* 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):
* Raymond Lee <raymond@appcoast.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 ***** */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let tabOne;
let tabTwo;
@ -44,36 +10,16 @@ function test() {
tabOne = gBrowser.addTab("http://mochi.test:8888/");
tabTwo = gBrowser.addTab("http://mochi.test:8888/browser/browser/base/content/test/tabview/dummy_page.html");
// make sure our tabs are loaded so their titles are right
let stillToLoad = 0;
let onLoad = function(event) {
event.target.removeEventListener("load", onLoad, true);
stillToLoad--;
if (!stillToLoad) {
// show the tab view
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
ok(!TabView.isVisible(), "Tab View is hidden");
// make sure the tab one is selected because undoCloseTab() would remove
// the selected tab if it's a blank tab.
gBrowser.selectedTab = tabOne;
TabView.toggle();
}
}
let newTabs = [ tabOne, tabTwo ];
newTabs.forEach(function(tab) {
stillToLoad++;
tab.linkedBrowser.addEventListener("load", onLoad, true);
afterAllTabsLoaded(function () {
// make sure the tab one is selected because undoCloseTab() would remove
// the selected tab if it's a blank tab.
gBrowser.selectedTab = tabOne;
showTabView(onTabViewWindowLoaded);
});
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
let contentWindow = document.getElementById("tab-view").contentWindow;
let contentWindow = TabView.getContentWindow();
let groupItems = contentWindow.GroupItems.groupItems;
is(groupItems.length, 1, "There is only one group");
is(groupItems[0].getChildren().length, 3, "The group has three tab items");
@ -83,21 +29,30 @@ function onTabViewWindowLoaded() {
is(groupItems[0].getChildren().length, 2, "The group has two tab items");
tabTwo = undoCloseTab(0);
tabTwo._tabViewTabItem.addSubscriber(tabTwo, "reconnected", function() {
tabTwo._tabViewTabItem.removeSubscriber(tabTwo, "reconnected");
whenTabIsReconnected(tabTwo, function() {
ok(TabView.isVisible(), "Tab View is still visible after restoring a tab");
is(groupItems[0].getChildren().length, 3, "The group still has three tab items");
// clean up and finish
let endGame = function() {
window.removeEventListener("tabviewhidden", endGame, false);
hideTabView(function () {
gBrowser.removeTab(tabOne);
gBrowser.removeTab(tabTwo);
finish();
}
window.addEventListener("tabviewhidden", endGame, false);
TabView.toggle();
});
});
}
// ----------
function whenTabIsReconnected(tab, callback) {
let tabItem = tab._tabViewTabItem;
if (tabItem._reconnected) {
callback();
return;
}
tabItem.addSubscriber(tabItem, "reconnected", function () {
tabItem.removeSubscriber(tabItem, "reconnected");
callback();
});
}

View File

@ -0,0 +1,142 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let pb = Cc['@mozilla.org/privatebrowsing;1'].
getService(Ci.nsIPrivateBrowsingService);
function test() {
let cw;
let createGroupItem = function () {
let bounds = new cw.Rect(20, 20, 400, 200);
let groupItem = new cw.GroupItem([], {bounds: bounds, immediately: true});
cw.GroupItems.setActiveGroupItem(groupItem);
let groupItemId = groupItem.id;
registerCleanupFunction(function() {
let groupItem = cw.GroupItems.groupItem(groupItemId);
if (groupItem)
groupItem.close();
});
for (let i=0; i<3; i++)
gBrowser.addTab('about:blank');
}
let assertTabViewIsHidden = function (prefix) {
ok(!TabView.isVisible(), prefix + ': tabview is hidden');
}
let assertNumberOfTabs = function (prefix, num) {
is(gBrowser.tabs.length, num, prefix + ': there are ' + num + ' tabs');
}
let assertNumberOfPinnedTabs = function (prefix, num) {
is(gBrowser._numPinnedTabs, num, prefix + ': there are ' + num + ' pinned tabs');
}
let assertNumberOfGroups = function (prefix, num) {
is(cw.GroupItems.groupItems.length, num, prefix + ': there are ' + num + ' groups');
}
let assertOneTabInGroup = function (prefix, groupItem) {
is(groupItem.getChildren().length, 1, prefix + ': group contains one tab');
}
let assertValidPrerequisites = function (prefix) {
assertNumberOfTabs(prefix, 1);
assertNumberOfPinnedTabs(prefix, 0);
assertTabViewIsHidden(prefix);
}
let assertValidSetup = function (prefix) {
assertNumberOfGroups(prefix, 2);
assertNumberOfTabs(prefix, 4);
assertNumberOfPinnedTabs(prefix, 2);
let [group1, group2] = cw.GroupItems.groupItems;
assertOneTabInGroup(prefix, group1);
assertOneTabInGroup(prefix, group2);
}
let testStateAfterEnteringPB = function () {
let prefix = 'enter';
ok(!pb.privateBrowsingEnabled, prefix + ': private browsing is disabled');
registerCleanupFunction(function () pb.privateBrowsingEnabled = false);
togglePrivateBrowsing(function () {
assertTabViewIsHidden(prefix);
showTabView(function () {
assertNumberOfGroups(prefix, 1);
assertNumberOfTabs(prefix, 1);
assertOneTabInGroup(prefix, cw.GroupItems.groupItems[0]);
hideTabView(testStateAfterLeavingPB);
});
});
}
let testStateAfterLeavingPB = function () {
let prefix = 'leave';
ok(pb.privateBrowsingEnabled, prefix + ': private browsing is enabled');
togglePrivateBrowsing(function () {
assertTabViewIsHidden(prefix);
showTabView(function () {
assertValidSetup(prefix);
finishTest();
});
});
}
let finishTest = function () {
// remove pinned tabs
gBrowser.removeTab(gBrowser.tabs[0]);
gBrowser.removeTab(gBrowser.tabs[0]);
cw.GroupItems.groupItems[1].closeAll();
hideTabView(function () {
assertValidPrerequisites('exit');
assertNumberOfGroups('exit', 1);
finish();
});
}
waitForExplicitFinish();
registerCleanupFunction(function () TabView.hide());
assertValidPrerequisites('start');
showTabView(function () {
cw = TabView.getContentWindow();
createGroupItem();
afterAllTabsLoaded(function () {
// setup
let groupItems = cw.GroupItems.groupItems;
let [tabItem1, tabItem2, ] = groupItems[1].getChildren();
gBrowser.pinTab(tabItem1.tab);
gBrowser.pinTab(tabItem2.tab);
assertValidSetup('setup');
hideTabView(testStateAfterEnteringPB);
});
});
}
// ----------
function togglePrivateBrowsing(callback) {
let topic = 'private-browsing-transition-complete';
function pbObserver(aSubject, aTopic, aData) {
if (aTopic != topic)
return;
Services.obs.removeObserver(pbObserver, topic);
afterAllTabsLoaded(callback);
}
Services.obs.addObserver(pbObserver, topic, false);
pb.privateBrowsingEnabled = !pb.privateBrowsingEnabled;
}

View File

@ -0,0 +1,91 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that there is a transform being applied to the tabs as they zoom in
// and out.
let tab, frontChanged, transformChanged;
function test() {
waitForExplicitFinish();
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
TabView.toggle();
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
let contentWindow = document.getElementById("tab-view").contentWindow;
tab = contentWindow.UI.getActiveTab();
ok(tab, "We have an active tab");
frontChanged = transformChanged = false;
tab.$container[0].addEventListener("DOMAttrModified", checkForFrontAddition,
false);
tab.$canvas[0].addEventListener("DOMAttrModified", checkForTransformAddition,
false);
window.addEventListener("tabviewhidden", onTabViewHidden, false);
TabView.toggle();
}
function checkForFrontAddition(aEvent) {
if (aEvent.attrName == "class" &&
aEvent.target.classList.contains("front")) {
frontChanged = true;
}
}
function checkForTransformAddition(aEvent) {
if (aEvent.attrName == "style" && aEvent.target.style.MozTransform) {
transformChanged = true;
}
}
function onTabViewHidden() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
ok(frontChanged, "the CSS class 'front' was added while zooming in");
ok(transformChanged, "the CSS class '-moz-transform' was modified while " +
"zooming in");
frontChanged = transformChanged = false;
tab.$container[0].removeEventListener("DOMAttrModified",
checkForFrontAddition, false);
tab.$container[0].addEventListener("DOMAttrModified", checkForFrontRemoval,
false);
window.addEventListener("tabviewshown", onTabViewShownAgain, false);
TabView.toggle();
}
function checkForFrontRemoval(aEvent) {
if (aEvent.attrName == "class" &&
!aEvent.target.classList.contains("front")) {
frontChanged = true;
}
}
function onTabViewShownAgain() {
window.removeEventListener("tabviewshown", onTabViewShownAgain, false);
ok(frontChanged, "the CSS class 'front' was removed while zooming out");
ok(transformChanged, "the CSS class 'transform' was removed while zooming " +
"out");
tab.$container[0].removeEventListener("DOMAttrModified",
checkForFrontRemoval, false);
tab.$canvas[0].removeEventListener("DOMAttrModified",
checkForTransformAddition, false);
window.addEventListener("tabviewhidden", onTabViewHiddenAgain, false);
TabView.toggle();
}
function onTabViewHiddenAgain() {
window.removeEventListener("tabviewhidden", onTabViewHiddenAgain, false);
finish();
}

View File

@ -0,0 +1,89 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
let win;
let cw;
let getGroupItem = function (index) {
return cw.GroupItems.groupItems[index];
}
let createOrphan = function (callback) {
let tab = win.gBrowser.loadOneTab('about:blank', {inBackground: true});
afterAllTabsLoaded(function () {
let tabItem = tab._tabViewTabItem;
tabItem.parent.remove(tabItem);
callback(tabItem);
});
}
let hideGroupItem = function (groupItem, callback) {
groupItem.addSubscriber(groupItem, 'groupHidden', function () {
groupItem.removeSubscriber(groupItem, 'groupHidden');
callback();
});
groupItem.closeAll();
}
let newWindow = function (test) {
newWindowWithTabView(function (tvwin) {
registerCleanupFunction(function () {
if (!tvwin.closed)
tvwin.close();
});
win = tvwin;
cw = win.TabView.getContentWindow();
test();
});
}
let assertNumberOfTabsInGroupItem = function (groupItem, numTabs) {
is(groupItem.getChildren().length, numTabs,
'there are ' + numTabs + ' tabs in this groupItem');
}
let testDragOnHiddenGroup = function () {
createOrphan(function (orphan) {
let groupItem = getGroupItem(0);
hideGroupItem(groupItem, function () {
let drag = orphan.container;
let drop = groupItem.$undoContainer[0];
assertNumberOfTabsInGroupItem(groupItem, 1);
EventUtils.synthesizeMouseAtCenter(drag, {type: 'mousedown'}, cw);
EventUtils.synthesizeMouseAtCenter(drop, {type: 'mousemove'}, cw);
EventUtils.synthesizeMouseAtCenter(drop, {type: 'mouseup'}, cw);
assertNumberOfTabsInGroupItem(groupItem, 1);
win.close();
newWindow(testDragOnVisibleGroup);
});
});
}
let testDragOnVisibleGroup = function () {
createOrphan(function (orphan) {
let groupItem = getGroupItem(0);
let drag = orphan.container;
let drop = groupItem.container;
assertNumberOfTabsInGroupItem(groupItem, 1);
EventUtils.synthesizeMouseAtCenter(drag, {type: 'mousedown'}, cw);
EventUtils.synthesizeMouseAtCenter(drop, {type: 'mousemove'}, cw);
EventUtils.synthesizeMouseAtCenter(drop, {type: 'mouseup'}, cw);
assertNumberOfTabsInGroupItem(groupItem, 2);
win.close();
finish();
});
}
waitForExplicitFinish();
newWindow(testDragOnHiddenGroup);
}

View File

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
let cw;
let tab;
let testReconnectWithSameUrl = function () {
tab = gBrowser.loadOneTab('http://mochi.test:8888/', {inBackground: true});
afterAllTabsLoaded(function () {
let tabItem = tab._tabViewTabItem;
let data = tabItem.getStorageData(true);
gBrowser.removeTab(tab);
cw.TabItems.pauseReconnecting();
tab = gBrowser.loadOneTab('http://mochi.test:8888/', {inBackground: true});
cw.Storage.saveTab(tab, data);
whenTabAttrModified(tab, function () {
tabItem = tab._tabViewTabItem;
cw.TabItems.resumeReconnecting();
ok(tabItem.isShowingCachedData(), 'tabItem shows cached data');
testChangeUrlAfterReconnect();
});
});
}
let testChangeUrlAfterReconnect = function () {
tab.linkedBrowser.loadURI('http://mochi.test:8888/browser/');
whenTabAttrModified(tab, function () {
cw.TabItems._update(tab);
let tabItem = tab._tabViewTabItem;
let currentLabel = tabItem.$tabTitle.text();
is(currentLabel, 'mochitest index /browser/', 'tab label is up-to-date');
testReconnectWithNewUrl();
});
}
let testReconnectWithNewUrl = function () {
let tabItem = tab._tabViewTabItem;
let data = tabItem.getStorageData(true);
gBrowser.removeTab(tab);
cw.TabItems.pauseReconnecting();
tab = gBrowser.loadOneTab('http://mochi.test:8888/', {inBackground: true});
cw.Storage.saveTab(tab, data);
whenTabAttrModified(tab, function () {
tabItem = tab._tabViewTabItem;
cw.TabItems.resumeReconnecting();
ok(!tabItem.isShowingCachedData(), 'tabItem does not show cached data');
gBrowser.removeTab(tab);
hideTabView(finish);
});
}
waitForExplicitFinish();
showTabView(function () {
cw = TabView.getContentWindow();
testReconnectWithSameUrl();
});
}
// ----------
function whenTabAttrModified(tab, callback) {
let onModified = function (event) {
if (tab === event.target) {
container.removeEventListener('TabAttrModified', onModified, false);
// we need executeSoon here because the tabItem also listens for the
// onTabAttrModified event. so this is to make sure the tabItem logic
// is executed before the test logic.
executeSoon(callback);
}
}
let container = gBrowser.tabContainer;
container.addEventListener('TabAttrModified', onModified, false);
}

View File

@ -51,6 +51,13 @@ function getBrowserURL()
}
function getTopWin(skipPopups) {
// If this is called in a browser window, use that window regardless of
// whether it's the frontmost window, since commands can be executed in
// background windows (bug 626148).
if (top.document.documentElement.getAttribute("windowtype") == "navigator:browser" &&
(!skipPopups || !top.document.documentElement.getAttribute("chromehidden")))
return top;
if (skipPopups) {
return Components.classes["@mozilla.org/browser/browserglue;1"]
.getService(Components.interfaces.nsIBrowserGlue)
@ -449,18 +456,6 @@ function openAdvancedPreferences(tabID)
return openPreferences("paneAdvanced", { "advancedTab" : tabID });
}
/**
* Opens the release notes page for this version of the application.
*/
function openReleaseNotes()
{
var formatter = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
.getService(Components.interfaces.nsIURLFormatter);
var relnotesURL = formatter.formatURLPref("app.releaseNotesURL");
openUILinkIn(relnotesURL, "tab");
}
/**
* Opens the troubleshooting information (about:support) page for this version
* of the application.

View File

@ -20,6 +20,8 @@ browser.jar:
* content/browser/aboutHome.xhtml (content/aboutHome.xhtml)
* content/browser/aboutHome.js (content/aboutHome.js)
* content/browser/aboutHome.css (content/aboutHome.css)
content/browser/aboutHome-restore-icon.png (content/aboutHome-restore-icon.png)
content/browser/aboutHome-restore-icon-rtl.png (content/aboutHome-restore-icon-rtl.png)
content/browser/aboutRobots-icon.png (content/aboutRobots-icon.png)
content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png)
* content/browser/browser.css (content/browser.css)

View File

@ -264,13 +264,13 @@ PlacesController.prototype = {
this.selectAll();
break;
case "placesCmd_open":
PlacesUIUtils.openNodeIn(this._view.selectedNode, "current");
PlacesUIUtils.openNodeIn(this._view.selectedNode, "current", this._view);
break;
case "placesCmd_open:window":
PlacesUIUtils.openNodeIn(this._view.selectedNode, "window");
PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view);
break;
case "placesCmd_open:tab":
PlacesUIUtils.openNodeIn(this._view.selectedNode, "tab");
PlacesUIUtils.openNodeIn(this._view.selectedNode, "tab", this._view);
break;
case "placesCmd_new:folder":
this.newItem("folder");
@ -300,8 +300,16 @@ PlacesController.prototype = {
this.sortFolderByName();
break;
case "placesCmd_createBookmark":
var node = this._view.selectedNode;
PlacesUIUtils.showMinimalAddBookmarkUI(PlacesUtils._uri(node.uri), node.title);
let node = this._view.selectedNode;
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "bookmark"
, hiddenRows: [ "description"
, "keyword"
, "location"
, "loadInSidebar" ]
, uri: PlacesUtils._uri(node.uri)
, title: node.title
}, window.top, true);
break;
}
},
@ -708,8 +716,11 @@ PlacesController.prototype = {
itemId = concreteId;
}
PlacesUIUtils.showItemProperties(itemId, itemType,
isRootItem /* read only */);
PlacesUIUtils.showBookmarkDialog({ action: "edit"
, type: itemType
, itemId: itemId
, readOnly: isRootItem
}, window.top);
},
/**
@ -740,61 +751,15 @@ PlacesController.prototype = {
mss.refreshMicrosummary(selectedNode.itemId);
},
/**
* Gives the user a chance to cancel loading lots of tabs at once
*/
_confirmOpenTabs: function(numTabsToOpen) {
var pref = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
const kWarnOnOpenPref = "browser.tabs.warnOnOpen";
var reallyOpen = true;
if (pref.getBoolPref(kWarnOnOpenPref)) {
if (numTabsToOpen >= pref.getIntPref("browser.tabs.maxOpenBeforeWarn")) {
var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
getService(Ci.nsIPromptService);
// default to true: if it were false, we wouldn't get this far
var warnOnOpen = { value: true };
var messageKey = "tabs.openWarningMultipleBranded";
var openKey = "tabs.openButtonMultiple";
const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService).
createBundle(BRANDING_BUNDLE_URI).
GetStringFromName("brandShortName");
var buttonPressed = promptService.confirmEx(window,
PlacesUIUtils.getString("tabs.openWarningTitle"),
PlacesUIUtils.getFormattedString(messageKey,
[numTabsToOpen, brandShortName]),
(promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0)
+ (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
PlacesUIUtils.getString(openKey),
null, null,
PlacesUIUtils.getFormattedString("tabs.openWarningPromptMeBranded",
[brandShortName]),
warnOnOpen);
reallyOpen = (buttonPressed == 0);
// don't set the pref unless they press OK and it's false
if (reallyOpen && !warnOnOpen.value)
pref.setBoolPref(kWarnOnOpenPref, false);
}
}
return reallyOpen;
},
/**
* Opens the links in the selected folder, or the selected links in new tabs.
*/
openSelectionInTabs: function PC_openLinksInTabs(aEvent) {
var node = this._view.selectedNode;
if (node && PlacesUtils.nodeIsContainer(node))
PlacesUIUtils.openContainerNodeInTabs(this._view.selectedNode, aEvent);
PlacesUIUtils.openContainerNodeInTabs(this._view.selectedNode, aEvent, this._view);
else
PlacesUIUtils.openURINodesInTabs(this._view.selectedNodes, aEvent);
PlacesUIUtils.openURINodesInTabs(this._view.selectedNodes, aEvent, this._view);
},
/**
@ -804,21 +769,19 @@ PlacesController.prototype = {
* the type of the new item (bookmark/livemark/folder)
*/
newItem: function PC_newItem(aType) {
var ip = this._view.insertionPoint;
let ip = this._view.insertionPoint;
if (!ip)
throw Cr.NS_ERROR_NOT_AVAILABLE;
var performed = false;
if (aType == "bookmark")
performed = PlacesUIUtils.showAddBookmarkUI(null, null, null, ip);
else if (aType == "livemark")
performed = PlacesUIUtils.showAddLivemarkUI(null, null, null, null, ip);
else // folder
performed = PlacesUIUtils.showAddFolderUI(null, ip);
let performed =
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: aType
, defaultInsertionPoint: ip
, hiddenRows: [ "folderPicker" ]
}, window);
if (performed) {
// select the new item
var insertedNodeId = PlacesUtils.bookmarks
// Select the new item.
let insertedNodeId = PlacesUtils.bookmarks
.getIdForItemAt(ip.itemId, ip.index);
this._view.selectItems([insertedNodeId], false);
}
@ -830,18 +793,9 @@ PlacesController.prototype = {
* of the folder.
*/
newFolder: function PC_newFolder() {
var ip = this._view.insertionPoint;
if (!ip)
throw Cr.NS_ERROR_NOT_AVAILABLE;
var performed = false;
performed = PlacesUIUtils.showAddFolderUI(null, ip);
if (performed) {
// select the new item
var insertedNodeId = PlacesUtils.bookmarks
.getIdForItemAt(ip.itemId, ip.index);
this._view.selectItems([insertedNodeId], false);
}
Cu.reportError("PlacesController.newFolder is deprecated and will be \
removed in a future release. Use newItem instead.");
this.newItem("folder");
},
/**

View File

@ -343,7 +343,8 @@ var PlacesOrganizer = {
},
openSelectedNode: function PO_openSelectedNode(aEvent) {
PlacesUIUtils.openNodeWithEvent(this._content.selectedNode, aEvent);
PlacesUIUtils.openNodeWithEvent(this._content.selectedNode, aEvent,
this._content.treeBoxObject.view);
},
/**

View File

@ -86,7 +86,7 @@ var SidebarUtils = {
else if (!mouseInGutter && openInTabs &&
aEvent.originalTarget.localName == "treechildren") {
tbo.view.selection.select(row.value);
PlacesUIUtils.openContainerNodeInTabs(aTree.selectedNode, aEvent);
PlacesUIUtils.openContainerNodeInTabs(aTree.selectedNode, aEvent, tbo.view);
}
else if (!mouseInGutter && !isContainer &&
aEvent.originalTarget.localName == "treechildren") {
@ -94,13 +94,18 @@ var SidebarUtils = {
// do this *before* attempting to load the link since openURL uses
// selection as an indication of which link to load.
tbo.view.selection.select(row.value);
PlacesUIUtils.openNodeWithEvent(aTree.selectedNode, aEvent);
PlacesUIUtils.openNodeWithEvent(aTree.selectedNode, aEvent, tbo.view);
}
},
handleTreeKeyPress: function SU_handleTreeKeyPress(aEvent) {
if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
PlacesUIUtils.openNodeWithEvent(aEvent.target.selectedNode, aEvent);
// XXX Bug 627901: Post Fx4, this method should take a tree parameter.
let node = aEvent.target.selectedNode;
if (node) {
let view = PlacesUIUtils.getViewForNode(node);
if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
PlacesUIUtils.openNodeWithEvent(node, aEvent, view);
}
},
/**

View File

@ -316,59 +316,22 @@ var PlacesUIUtils = {
return null;
},
/**
* Methods to show the bookmarkProperties dialog in its various modes.
*
* The showMinimalAdd* methods open the dialog by its alternative URI. Thus
* they persist the dialog dimensions separately from the showAdd* methods.
* Note these variants also do not return the dialog "performed" state since
* they may not open the dialog modally.
*/
_reportDeprecatedAddBookmarkMethod:
function PUIU__reportDeprecatedAddBookmarkMethod() {
// Removes "PUIU_".
let oldFuncName = arguments.callee.caller.name.slice(5);
Cu.reportError(oldFuncName + " is deprecated and will be removed in a \
future release. Use showBookmarkDialog instead");
},
/**
* Shows the "Add Bookmark" dialog.
*
* @param [optional] aURI
* An nsIURI object for which the "add bookmark" dialog is
* to be shown.
* @param [optional] aTitle
* The default title for the new bookmark.
* @param [optional] aDescription
The default description for the new bookmark
* @param [optional] aDefaultInsertionPoint
* The default insertion point for the new item. If set, the folder
* picker would be hidden unless aShowPicker is set to true, in which
* case the dialog only uses the folder identifier from the insertion
* point as the initially selected item in the folder picker.
* @param [optional] aShowPicker
* see above
* @param [optional] aLoadInSidebar
* If true, the dialog will default to load the new item in the
* sidebar (as a web panel).
* @param [optional] aKeyword
* The default keyword for the new bookmark. The keyword field
* will be shown in the dialog if this is used.
* @param [optional] aPostData
* POST data for POST-style keywords.
* @param [optional] aCharSet
* The character set for the bookmarked page.
* @return true if any transaction has been performed.
*
* Notes:
* - the location, description and "loadInSidebar" fields are
* visible only if there is no initial URI (aURI is null).
* - When aDefaultInsertionPoint is not set, the dialog defaults to the
* bookmarks root folder.
* This is here for compatibility reasons, use ShowBookmarkDialog instead.
*/
showAddBookmarkUI: function PUIU_showAddBookmarkUI(aURI,
aTitle,
aDescription,
aDefaultInsertionPoint,
aShowPicker,
aLoadInSidebar,
aKeyword,
aPostData,
aCharSet) {
showAddBookmarkUI: function PUIU_showAddBookmarkUI(
aURI, aTitle, aDescription, aDefaultInsertionPoint, aShowPicker,
aLoadInSidebar, aKeyword, aPostData, aCharSet) {
this._reportDeprecatedAddBookmarkMethod();
var info = {
action: "add",
type: "bookmark"
@ -401,25 +364,18 @@ var PlacesUIUtils = {
info.charSet = aCharSet;
}
return this._showBookmarkDialog(info);
return this.showBookmarkDialog(info);
},
/**
* @see showAddBookmarkUI
* This opens the dialog with only the name and folder pickers visible by
* default.
*
* You can still pass in the various paramaters as the default properties
* for the new bookmark.
*
* The keyword field will be visible only if the aKeyword parameter
* was used.
* This is here for compatibility reasons, use ShowBookmarkDialog instead.
*/
showMinimalAddBookmarkUI:
function PUIU_showMinimalAddBookmarkUI(aURI, aTitle, aDescription,
aDefaultInsertionPoint, aShowPicker,
aLoadInSidebar, aKeyword, aPostData,
aCharSet) {
function PUIU_showMinimalAddBookmarkUI(
aURI, aTitle, aDescription, aDefaultInsertionPoint, aShowPicker,
aLoadInSidebar, aKeyword, aPostData, aCharSet) {
this._reportDeprecatedAddBookmarkMethod();
var info = {
action: "add",
type: "bookmark",
@ -459,30 +415,11 @@ var PlacesUIUtils = {
else
info.hiddenRows.push("keyword");
return this._showBookmarkDialog(info, true);
return this.showBookmarkDialog(info, undefined, true);
},
/**
* Shows the "Add Live Bookmark" dialog.
*
* @param [optional] aFeedURI
* The feed URI for which the dialog is to be shown (nsIURI).
* @param [optional] aSiteURI
* The site URI for the new live-bookmark (nsIURI).
* @param [optional] aDefaultInsertionPoint
* The default insertion point for the new item. If set, the folder
* picker would be hidden unless aShowPicker is set to true, in which
* case the dialog only uses the folder identifier from the insertion
* point as the initially selected item in the folder picker.
* @param [optional] aShowPicker
* see above
* @return true if any transaction has been performed.
*
* Notes:
* - the feedURI and description fields are visible only if there is no
* initial feed URI (aFeedURI is null).
* - When aDefaultInsertionPoint is not set, the dialog defaults to the
* bookmarks root folder.
* This is here for compatibility reasons, use ShowBookmarkDialog instead.
*/
showAddLivemarkUI: function PUIU_showAddLivemarkURI(aFeedURI,
aSiteURI,
@ -490,6 +427,8 @@ var PlacesUIUtils = {
aDescription,
aDefaultInsertionPoint,
aShowPicker) {
this._reportDeprecatedAddBookmarkMethod();
var info = {
action: "add",
type: "livemark"
@ -512,21 +451,19 @@ var PlacesUIUtils = {
if (!aShowPicker)
info.hiddenRows = ["folderPicker"];
}
return this._showBookmarkDialog(info);
return this.showBookmarkDialog(info);
},
/**
* @see showAddLivemarkUI
* This opens the dialog with only the name and folder pickers visible by
* default.
*
* You can still pass in the various paramaters as the default properties
* for the new live-bookmark.
* This is here for compatibility reasons, use ShowBookmarkDialog instead.
*/
showMinimalAddLivemarkUI:
function PUIU_showMinimalAddLivemarkURI(aFeedURI, aSiteURI, aTitle,
aDescription, aDefaultInsertionPoint,
aShowPicker) {
function PUIU_showMinimalAddLivemarkURI(
aFeedURI, aSiteURI, aTitle, aDescription, aDefaultInsertionPoint,
aShowPicker) {
this._reportDeprecatedAddBookmarkMethod();
var info = {
action: "add",
type: "livemark",
@ -550,19 +487,15 @@ var PlacesUIUtils = {
if (!aShowPicker)
info.hiddenRows.push("folderPicker");
}
return this._showBookmarkDialog(info, true);
return this.showBookmarkDialog(info, undefined, true);
},
/**
* Show an "Add Bookmarks" dialog to allow the adding of a folder full
* of bookmarks corresponding to the objects in the uriList. This will
* be called most often as the result of a "Bookmark All Tabs..." command.
*
* @param aURIList List of nsIURI objects representing the locations
* to be bookmarked.
* @return true if any transaction has been performed.
* This is here for compatibility reasons, use ShowBookmarkDialog instead.
*/
showMinimalAddMultiBookmarkUI: function PUIU_showAddMultiBookmarkUI(aURIList) {
this._reportDeprecatedAddBookmarkMethod();
if (aURIList.length == 0)
throw("showAddMultiBookmarkUI expects a list of nsIURI objects");
var info = {
@ -571,46 +504,31 @@ var PlacesUIUtils = {
hiddenRows: ["description"],
URIList: aURIList
};
return this._showBookmarkDialog(info, true);
return this.showBookmarkDialog(info, undefined, true);
},
/**
* Opens the properties dialog for a given item identifier.
*
* @param aItemId
* item identifier for which the properties are to be shown
* @param aType
* item type, either "bookmark" or "folder"
* @param [optional] aReadOnly
* states if properties dialog should be readonly
* @return true if any transaction has been performed.
* This is here for compatibility reasons, use ShowBookmarkDialog instead.
*/
showItemProperties: function PUIU_showItemProperties(aItemId, aType, aReadOnly) {
this._reportDeprecatedAddBookmarkMethod();
var info = {
action: "edit",
type: aType,
itemId: aItemId,
readOnly: aReadOnly
};
return this._showBookmarkDialog(info);
return this.showBookmarkDialog(info);
},
/**
* Shows the "New Folder" dialog.
*
* @param [optional] aTitle
* The default title for the new bookmark.
* @param [optional] aDefaultInsertionPoint
* The default insertion point for the new item. If set, the folder
* picker would be hidden unless aShowPicker is set to true, in which
* case the dialog only uses the folder identifier from the insertion
* point as the initially selected item in the folder picker.
* @param [optional] aShowPicker
* see above
* @return true if any transaction has been performed.
* This is here for compatibility reasons, use ShowBookmarkDialog instead.
*/
showAddFolderUI:
function PUIU_showAddFolderUI(aTitle, aDefaultInsertionPoint, aShowPicker) {
this._reportDeprecatedAddBookmarkMethod();
var info = {
action: "add",
type: "folder",
@ -626,32 +544,43 @@ var PlacesUIUtils = {
if (!aShowPicker)
info.hiddenRows.push("folderPicker");
}
return this._showBookmarkDialog(info);
return this.showBookmarkDialog(info);
},
/**
* Shows the bookmark dialog corresponding to the specified info
* Shows the bookmark dialog corresponding to the specified info.
*
* @param aInfo
* Describes the item to be edited/added in the dialog.
* See documentation at the top of bookmarkProperties.js
* @param aMinimalUI
* [optional] if true, the dialog is opened by its alternative
* chrome: uri.
* @param aWindow
* Owner window for the new dialog.
* @param aMinimalUI [optional]
* Whether to open the dialog in "minimal ui" mode. Do not pass this
* for new callers. It'll be removed in a future release.
*
* @see documentation at the top of bookmarkProperties.js
* @return true if any transaction has been performed, false otherwise.
*/
_showBookmarkDialog: function PUIU__showBookmarkDialog(aInfo, aMinimalUI) {
var dialogURL = aMinimalUI ?
showBookmarkDialog:
function PUIU_showBookmarkDialog(aInfo, aParentWindow, aMinimalUI) {
if (!aParentWindow) {
aParentWindow = this._getWindow(null);
}
// Preserve size attributes differently based on the fact the dialog has
// a folder picker or not.
let minimalUI = "hiddenRows" in aInfo &&
aInfo.hiddenRows.indexOf("folderPicker") != -1;
let dialogURL = aMinimalUI ?
"chrome://browser/content/places/bookmarkProperties2.xul" :
"chrome://browser/content/places/bookmarkProperties.xul";
var features;
if (aMinimalUI)
features = "centerscreen,chrome,modal,resizable=yes";
else
features = "centerscreen,chrome,modal,resizable=no";
this._getCurrentActiveWin().openDialog(dialogURL, "", features, aInfo);
let features =
"centerscreen,chrome,modal,resizable=" + (aMinimalUI ? "yes" : "no");
aParentWindow.openDialog(dialogURL, "", features, aInfo);
return ("performed" in aInfo && aInfo.performed);
},
@ -659,10 +588,6 @@ var PlacesUIUtils = {
return Services.wm.getMostRecentWindow("navigator:browser");
},
_getCurrentActiveWin: function PUIU__getCurrentActiveWin() {
return focusManager.activeWindow;
},
/**
* Returns the closet ancestor places view for the given DOM node
* @param aNode
@ -789,7 +714,8 @@ var PlacesUIUtils = {
/**
* Gives the user a chance to cancel loading lots of tabs at once
*/
_confirmOpenInTabs: function PUIU__confirmOpenInTabs(numTabsToOpen) {
_confirmOpenInTabs:
function PUIU__confirmOpenInTabs(numTabsToOpen, aWindow) {
const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen";
var reallyOpen = true;
@ -807,7 +733,7 @@ var PlacesUIUtils = {
GetStringFromName("brandShortName");
var buttonPressed = Services.prompt.confirmEx(
this._getCurrentActiveWin(),
aWindow,
this.getString("tabs.openWarningTitle"),
this.getFormattedString(messageKey, [numTabsToOpen, brandShortName]),
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
@ -831,7 +757,7 @@ var PlacesUIUtils = {
/** aItemsToOpen needs to be an array of objects of the form:
* {uri: string, isBookmark: boolean}
*/
_openTabset: function PUIU__openTabset(aItemsToOpen, aEvent) {
_openTabset: function PUIU__openTabset(aItemsToOpen, aEvent, aWindow) {
if (!aItemsToOpen.length)
return;
@ -846,13 +772,19 @@ var PlacesUIUtils = {
urls.push(item.uri);
}
var browserWindow = this._getTopBrowserWin();
// Prefer the caller window if it's a browser window, otherwise use
// the top browser window.
var browserWindow =
aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser" ?
aWindow : this._getTopBrowserWin();
// whereToOpenLink doesn't return "window" when there's no browser window
// open (Bug 630255).
var where = browserWindow ?
browserWindow.whereToOpenLink(aEvent, false, true) : "window";
if (where == "window") {
let win = this._getCurrentActiveWin();
win.openDialog(win.getBrowserURL(), "_blank",
"chrome,all,dialog=no", urls.join("|"));
aWindow.openDialog(aWindow.getBrowserURL(), "_blank",
"chrome,all,dialog=no", urls.join("|"));
return;
}
@ -861,22 +793,62 @@ var PlacesUIUtils = {
browserWindow.gBrowser.loadTabs(urls, loadInBackground, replaceCurrentTab);
},
openContainerNodeInTabs: function PUIU_openContainerInTabs(aNode, aEvent) {
var urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode);
if (!this._confirmOpenInTabs(urlsToOpen.length))
return;
/**
* Helper method for methods which are forced to take a view/window
* parameter as an optional parameter. It will be removed post Fx4.
*/
_getWindow: function PUIU__getWindow(aView) {
if (aView) {
// Pratically, this is the case for places trees.
if (aView instanceof Components.interfaces.nsIDOMNode)
return aView.ownerDocument.defaultView;
this._openTabset(urlsToOpen, aEvent);
return Cu.getGlobalForObject(aView);
}
let caller = arguments.callee.caller;
// If a view wasn't expected, the method should have got a window.
if (aView === null) {
Components.utils.reportError("The api has changed. A window should be \
passed to " + caller.name + ". Not \
passing a window will throw in a future \
release.");
}
else {
Components.utils.reportError("The api has changed. A places view \
should be passed to " + caller.name + ". \
Not passing a view will throw in a future \
release.");
}
// This could certainly break in some edge cases (like bug 562998), but
// that's the best we should do for those extreme backwards-compatibility cases.
let topBrowserWin = this._getTopBrowserWin();
return topBrowserWin ? topBrowserWin : focusManager.focusedWindow;
},
openURINodesInTabs: function PUIU_openURINodesInTabs(aNodes, aEvent) {
var urlsToOpen = [];
openContainerNodeInTabs:
function PUIU_openContainerInTabs(aNode, aEvent, aView) {
let window = this._getWindow(aView);
let urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode);
if (!this._confirmOpenInTabs(urlsToOpen.length, window))
return;
this._openTabset(urlsToOpen, aEvent, window);
},
openURINodesInTabs: function PUIU_openURINodesInTabs(aNodes, aEvent, aView) {
let window = this._getWindow(aView);
let urlsToOpen = [];
for (var i=0; i < aNodes.length; i++) {
// skip over separators and folders
// Skip over separators and folders.
if (PlacesUtils.nodeIsURI(aNodes[i]))
urlsToOpen.push({uri: aNodes[i].uri, isBookmark: PlacesUtils.nodeIsBookmark(aNodes[i])});
}
this._openTabset(urlsToOpen, aEvent);
this._openTabset(urlsToOpen, aEvent, window);
},
/**
@ -888,9 +860,13 @@ var PlacesUIUtils = {
* @param aEvent
* The DOM mouse/key event with modifier keys set that track the
* user's preferred destination window or tab.
* @param aView
* The controller associated with aNode.
*/
openNodeWithEvent: function PUIU_openNodeWithEvent(aNode, aEvent) {
this.openNodeIn(aNode, this._getCurrentActiveWin().whereToOpenLink(aEvent));
openNodeWithEvent:
function PUIU_openNodeWithEvent(aNode, aEvent, aView) {
let window = this._getWindow(aView);
this._openNodeIn(aNode, window.whereToOpenLink(aEvent), window);
},
/**
@ -898,10 +874,15 @@ var PlacesUIUtils = {
* web panel.
* see also openUILinkIn
*/
openNodeIn: function PUIU_openNodeIn(aNode, aWhere) {
openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView) {
let window = this._getWindow(aView);
this._openNodeIn(aNode, aWhere, window);
},
_openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow) {
if (aNode && PlacesUtils.nodeIsURI(aNode) &&
this.checkURLSecurity(aNode, this._getCurrentActiveWin())) {
var isBookmark = PlacesUtils.nodeIsBookmark(aNode);
this.checkURLSecurity(aNode, aWindow)) {
let isBookmark = PlacesUtils.nodeIsBookmark(aNode);
if (isBookmark)
this.markPageAsFollowedBookmark(aNode.uri);
@ -913,14 +894,14 @@ var PlacesUIUtils = {
if (aWhere == "current" && isBookmark) {
if (PlacesUtils.annotations
.itemHasAnnotation(aNode.itemId, this.LOAD_IN_SIDEBAR_ANNO)) {
var browserWin = this._getTopBrowserWin();
let browserWin = this._getTopBrowserWin();
if (browserWin) {
browserWin.openWebPanel(aNode.title, aNode.uri);
return;
}
}
}
this._getCurrentActiveWin().openUILinkIn(aNode.uri, aWhere);
aWindow.openUILinkIn(aNode.uri, aWhere);
}
},

View File

@ -127,6 +127,9 @@
<preference id="security.disable_button.openDeviceManager"
name="security.disable_button.openDeviceManager"
type="bool"/>
<preference id="privacy.donottrackheader.enabled"
name="privacy.donottrackheader.enabled"
type="bool"/>
</preferences>
#ifdef HAVE_SHELL_SERVICE
@ -191,6 +194,10 @@
onsyncfrompreference="return gAdvancedPane.readCheckSpelling();"
onsynctopreference="return gAdvancedPane.writeCheckSpelling();"
preference="layout.spellcheckDefault"/>
<checkbox id="privacyDoNotTrackPrefs"
label="&doNotTrack.label;"
accesskey="&doNotTrack.accesskey;"
preference="privacy.donottrackheader.enabled"/>
</groupbox>
#ifdef HAVE_SHELL_SERVICE

View File

@ -877,9 +877,9 @@ SessionStoreService.prototype = {
this._updateCookies([winData]);
}
// save the window if it has multiple tabs or a single tab with entries
// save the window if it has multiple tabs or a single saveable tab
if (winData.tabs.length > 1 ||
(winData.tabs.length == 1 && winData.tabs[0].entries.length > 0)) {
(winData.tabs.length == 1 && this._shouldSaveTabState(winData.tabs[0]))) {
this._closedWindows.unshift(winData);
this._capClosedWindows();
}
@ -984,7 +984,7 @@ SessionStoreService.prototype = {
this._updateTextAndScrollDataForTab(aWindow, aTab.linkedBrowser, tabState);
// store closed-tab data for undo
if (tabState.entries.length > 0) {
if (this._shouldSaveTabState(tabState)) {
let tabTitle = aTab.label;
let tabbrowser = aWindow.gBrowser;
tabTitle = this._replaceLoadingTitle(tabTitle, tabbrowser, aTab);
@ -2346,6 +2346,12 @@ SessionStoreService.prototype = {
if (aOverwriteTabs && tabbrowser.selectedTab._tPos >= newTabCount)
tabbrowser.moveTabTo(tabbrowser.selectedTab, newTabCount - 1);
// unpin all tabs to ensure they are not reordered in the next loop
if (aOverwriteTabs) {
for (let t = tabbrowser._numPinnedTabs - 1; t > -1; t--)
tabbrowser.unpinTab(tabbrowser.tabs[t]);
}
for (var t = 0; t < newTabCount; t++) {
tabs.push(t < openTabCount ?
tabbrowser.tabs[t] :
@ -2357,8 +2363,6 @@ SessionStoreService.prototype = {
if (winData.tabs[t].pinned)
tabbrowser.pinTab(tabs[t]);
else
tabbrowser.unpinTab(tabs[t]);
if (winData.tabs[t].hidden)
tabbrowser.hideTab(tabs[t]);
@ -2553,7 +2557,14 @@ SessionStoreService.prototype = {
// is always visible in the address bar
let activeIndex = (tabData.index || tabData.entries.length) - 1;
let activePageData = tabData.entries[activeIndex] || null;
browser.userTypedValue = activePageData ? activePageData.url || null : null;
let uri = activePageData ? activePageData.url || null : null;
browser.userTypedValue = uri;
// Also make sure currentURI is set so that switch-to-tab works before
// the tab is restored. We'll reset this to about:blank when we try to
// restore the tab to ensure that docshell doeesn't get confused.
if (uri)
browser.docShell.setCurrentURI(this._getURIFromString(uri));
// If the page has a title, set it.
if (activePageData) {
@ -2724,6 +2735,9 @@ SessionStoreService.prototype = {
if (activeIndex >= tabData.entries.length)
activeIndex = tabData.entries.length - 1;
// Reset currentURI.
browser.webNavigation.setCurrentURI(this._getURIFromString("about:blank"));
// Attach data that will be restored on "load" event, after tab is restored.
if (activeIndex > -1) {
// restore those aspects of the currently active documents which are not
@ -3531,6 +3545,24 @@ SessionStoreService.prototype = {
sessionAge && sessionAge >= SIX_HOURS_IN_MS);
},
/**
* Determine if the tab state we're passed is something we should save. This
* is used when closing a tab or closing a window with a single tab
*
* @param aTabState
* The current tab state
* @returns boolean
*/
_shouldSaveTabState: function sss__shouldSaveTabState(aTabState) {
// If the tab has only the transient about:blank history entry, no other
// session history, and no userTypedValue, then we don't actually want to
// store this tab's data.
return aTabState.entries.length &&
!(aTabState.entries.length == 1 &&
aTabState.entries[0].url == "about:blank" &&
!aTabState.userTypedValue);
},
/**
* This is going to take a state as provided at startup (via
* nsISessionStartup.state) and split it into 2 parts. The first part

View File

@ -120,6 +120,7 @@ _BROWSER_TEST_FILES = \
browser_579879.js \
browser_580512.js \
browser_581593.js \
browser_581937.js \
browser_586147.js \
browser_586068-cascaded_restore.js \
browser_589246.js \
@ -131,12 +132,14 @@ _BROWSER_TEST_FILES = \
browser_597315_c.html \
browser_597315_c1.html \
browser_597315_c2.html \
browser_599909.js \
browser_600545.js \
browser_601955.js \
browser_607016.js \
browser_615394-SSWindowState_events.js \
browser_618151.js \
browser_623779.js \
browser_624727.js \
browser_625257.js \
$(NULL)

View File

@ -0,0 +1,72 @@
/* ***** 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 Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mehdi Mulani <mmmulani@uwaterloo.ca>
*
* 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 *****/
// Tests that an about:blank tab with no history will not be saved into
// session store and thus, it will not show up in Recently Closed Tabs.
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
let tab;
function test() {
waitForExplicitFinish();
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", 0);
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
is(ss.getClosedTabCount(window), 0, "should be no closed tabs");
gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen, true);
tab = gBrowser.addTab();
}
function onTabOpen(aEvent) {
gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen, true);
// Let other listeners react to the TabOpen event before removing the tab.
executeSoon(function() {
is(gBrowser.browsers[1].currentURI.spec, "about:blank",
"we will be removing an about:blank tab");
gBrowser.removeTab(tab);
is(ss.getClosedTabCount(window), 0, "should still be no closed tabs");
executeSoon(finish);
});
}

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 Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Paul OShannessy <paul@oshannessy.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 ***** */
const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;
Cu.import("resource://gre/modules/Services.jsm");
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
let stateBackup = ss.getBrowserState();
function cleanup() {
// Reset the pref
try {
Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
} catch (e) {}
ss.setBrowserState(stateBackup);
executeSoon(finish);
}
function test() {
/** Bug 599909 - to-be-reloaded tabs don't show up in switch-to-tab **/
waitForExplicitFinish();
// Set the pref to 0 so we know exactly how many tabs should be restoring at
// any given time. This guarantees that a finishing load won't start another.
Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
let state = { windows: [{ tabs: [
{ entries: [{ url: "http://example.org/#1" }] },
{ entries: [{ url: "http://example.org/#2" }] },
{ entries: [{ url: "http://example.org/#3" }] },
{ entries: [{ url: "http://example.org/#4" }] }
], selected: 1 }] };
let tabsForEnsure = {};
state.windows[0].tabs.forEach(function(tab) {
tabsForEnsure[tab.entries[0].url] = 1;
});
let tabsRestoring = 0;
let tabsRestored = 0;
function handleEvent(aEvent) {
if (aEvent.type == "SSTabRestoring")
tabsRestoring++;
else
tabsRestored++;
if (tabsRestoring < state.windows[0].tabs.length ||
tabsRestored < 1)
return;
gBrowser.tabContainer.removeEventListener("SSTabRestoring", handleEvent, true);
gBrowser.tabContainer.removeEventListener("SSTabRestored", handleEvent, true);
executeSoon(function() {
checkAutocompleteResults(tabsForEnsure, cleanup);
});
}
// currentURI is set before SSTabRestoring is fired, so we can sucessfully check
// after that has fired for all tabs. Since 1 tab will be restored though, we
// also need to wait for 1 SSTabRestored since currentURI will be set, unset, then set.
gBrowser.tabContainer.addEventListener("SSTabRestoring", handleEvent, true);
gBrowser.tabContainer.addEventListener("SSTabRestored", handleEvent, true);
ss.setBrowserState(JSON.stringify(state));
}
// The following was taken from browser/base/content/test/browser_tabMatchesInAwesomebar.js
// so that we could do the same sort of checking.
var gController = Cc["@mozilla.org/autocomplete/controller;1"].
getService(Ci.nsIAutoCompleteController);
function checkAutocompleteResults(aExpected, aCallback) {
gController.input = {
timeout: 10,
textValue: "",
searches: ["history"],
searchParam: "enable-actions",
popupOpen: false,
minResultsForPopup: 0,
invalidate: function() {},
disableAutoComplete: false,
completeDefaultIndex: false,
get popup() { return this; },
onSearchBegin: function() {},
onSearchComplete: function ()
{
info("Found " + gController.matchCount + " matches.");
// Check to see the expected uris and titles match up (in any order)
for (let i = 0; i < gController.matchCount; i++) {
let uri = gController.getValueAt(i).replace(/^moz-action:[^,]+,/i, "");
info("Search for '" + uri + "' in open tabs.");
ok(uri in aExpected, "Registered open page found in autocomplete.");
// Remove the found entry from expected results.
delete aExpected[uri];
}
// Make sure there is no reported open page that is not open.
for (let entry in aExpected) {
ok(false, "'" + entry + "' not found in autocomplete.");
}
executeSoon(aCallback);
},
setSelectedIndex: function() {},
get searchCount() { return this.searches.length; },
getSearchAt: function(aIndex) this.searches[aIndex],
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIAutoCompleteInput,
Ci.nsIAutoCompletePopup,
])
};
info("Searching open pages.");
gController.startSearch(Services.prefs.getCharPref("browser.urlbar.restrict.openpage"));
}

View File

@ -0,0 +1,38 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
function test() {
let assertNumberOfTabs = function (num, msg) {
is(gBrowser.tabs.length, num, msg);
}
let assertNumberOfPinnedTabs = function (num, msg) {
is(gBrowser._numPinnedTabs, num, msg);
}
// check prerequisites
assertNumberOfTabs(1, "we start off with one tab");
assertNumberOfPinnedTabs(0, "no pinned tabs so far");
// setup
gBrowser.addTab("about:blank");
assertNumberOfTabs(2, "there are two tabs, now");
let [tab1, tab2] = gBrowser.tabs;
let linkedBrowser = tab1.linkedBrowser;
gBrowser.pinTab(tab1);
gBrowser.pinTab(tab2);
assertNumberOfPinnedTabs(2, "both tabs are now pinned");
// run the test
ss.setBrowserState(JSON.stringify({ windows: [{ tabs: [{ url: "about:blank" }] }] }));
assertNumberOfTabs(1, "one tab left after setBrowserState()");
assertNumberOfPinnedTabs(0, "there are no pinned tabs");
is(gBrowser.tabs[0].linkedBrowser, linkedBrowser, "first tab's browser got re-used");
waitForExplicitFinish();
waitForSaveState(finish);
}

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