mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Merge tracemonkey to mozilla-central. (a=blockers)
This commit is contained in:
commit
09f6be30b5
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -81,6 +81,8 @@ CPPSRCS = \
|
||||
|
||||
EXPORTS = \
|
||||
a11yGeneric.h \
|
||||
nsAccDocManager.h \
|
||||
nsAccessibilityService.h \
|
||||
nsAccessible.h \
|
||||
nsAccessNode.h \
|
||||
nsARIAMap.h \
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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 },
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -225,7 +225,7 @@ public:
|
||||
* @param aSelection [in] the given selection
|
||||
* @return text accessible
|
||||
*/
|
||||
static already_AddRefed<nsHyperTextAccessible>
|
||||
static nsHyperTextAccessible*
|
||||
GetTextAccessibleFromSelection(nsISelection* aSelection);
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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")
|
||||
|
@ -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) ||
|
||||
|
@ -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);
|
||||
|
@ -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*
|
||||
|
@ -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;
|
||||
|
@ -410,12 +410,6 @@ nsApplicationAccessible::InvalidateChildren()
|
||||
// and RemoveChild() method calls.
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsApplicationAccessible::GetParent()
|
||||
{
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccessible protected methods
|
||||
|
||||
|
@ -131,8 +131,6 @@ public:
|
||||
|
||||
virtual void InvalidateChildren();
|
||||
|
||||
virtual nsAccessible* GetParent();
|
||||
|
||||
protected:
|
||||
|
||||
// nsAccessible
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include "nsRelUtils.h"
|
||||
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccessNode.h"
|
||||
#include "nsAccessible.h"
|
||||
#include "nsCoreUtils.h"
|
||||
|
||||
#include "nsIDOMDocument.h"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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
|
||||
|
@ -80,6 +80,7 @@ nsHyperTextAccessible::
|
||||
nsHyperTextAccessible(nsIContent *aNode, nsIWeakReference *aShell) :
|
||||
nsAccessibleWrap(aNode, aShell)
|
||||
{
|
||||
mFlags |= eHyperTextAccessible;
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsHyperTextAccessible, nsAccessibleWrap)
|
||||
|
@ -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_
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -1205,6 +1205,12 @@ nsXULTreeGridCellAccessible::GetStateInternal(PRUint32 *aStates,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsXULTreeGridCellAccessible::GetIndexInParent() const
|
||||
{
|
||||
return GetColumnIndex();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULTreeGridCellAccessible: public implementation
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 \
|
||||
|
79
accessible/tests/mochitest/events/test_aria_hidden.html
Normal file
79
accessible/tests/mochitest/events/test_aria_hidden.html
Normal 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>
|
@ -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;
|
||||
}
|
||||
|
@ -90,6 +90,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
function doTest()
|
||||
{
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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">
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -54,6 +54,7 @@ _TEST_FILES =\
|
||||
test_select.html \
|
||||
test_textleaf.html \
|
||||
test_visibility.html \
|
||||
test_whitespace.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
@ -323,9 +323,7 @@
|
||||
{
|
||||
var tree =
|
||||
{ DOCUMENT: [
|
||||
{ ENTRY: [
|
||||
{ TEXT_LEAF: [ ] }
|
||||
] }
|
||||
{ ENTRY: [ ] }
|
||||
] };
|
||||
testAccessibleTree(this.docNode, tree);
|
||||
|
||||
|
@ -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>
|
||||
|
188
accessible/tests/mochitest/treeupdate/test_whitespace.html
Normal file
188
accessible/tests/mochitest/treeupdate/test_whitespace.html
Normal 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>
|
@ -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
|
||||
|
BIN
browser/base/content/aboutHome-restore-icon-rtl.png
Normal file
BIN
browser/base/content/aboutHome-restore-icon-rtl.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
BIN
browser/base/content/aboutHome-restore-icon.png
Normal file
BIN
browser/base/content/aboutHome-restore-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
@ -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%;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;"
|
||||
|
@ -38,6 +38,10 @@
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
<menubar id="main-menubar"
|
||||
onpopupshowing="if (event.target.parentNode.parentNode == this &&
|
||||
!('@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"
|
||||
|
@ -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,11 +408,27 @@ 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.
|
||||
* @param url
|
||||
@ -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) {
|
||||
|
@ -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"/>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
@ -4074,6 +4101,21 @@ var XULBrowserWindow = {
|
||||
encodeURIComponent);
|
||||
gURLBar.setOverLink(url);
|
||||
}
|
||||
},
|
||||
|
||||
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.
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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))
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
|
@ -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;
|
||||
|
@ -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,34 +683,35 @@ 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);
|
||||
|
||||
if (typeof complete == "function")
|
||||
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;
|
||||
|
||||
TabItems.pausePainting();
|
||||
$tab.animate({
|
||||
left: box.left,
|
||||
top: box.top,
|
||||
width: box.width,
|
||||
height: box.height
|
||||
}, {
|
||||
// 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
|
||||
});
|
||||
|
||||
$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
|
||||
|
@ -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
|
||||
----------------------------------*/
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
||||
// 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");
|
||||
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");
|
||||
|
||||
// Show all tabs
|
||||
let allTabs = [tab for each (tab in gBrowser.tabs)];
|
||||
gBrowser.showOnlyTheseTabs(allTabs);
|
||||
// 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");
|
||||
|
||||
// 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!");
|
||||
// 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");
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -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 \
|
||||
|
@ -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,37 +57,16 @@ 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;
|
||||
@ -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);
|
||||
|
@ -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"
|
||||
}]
|
||||
|
@ -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"
|
||||
}]
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
142
browser/base/content/test/tabview/browser_tabview_bug624727.js
Normal file
142
browser/base/content/test/tabview/browser_tabview_bug624727.js
Normal 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;
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
@ -2345,7 +2345,13 @@ SessionStoreService.prototype = {
|
||||
// prevent unnecessary flickering
|
||||
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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
153
browser/components/sessionstore/test/browser/browser_599909.js
Normal file
153
browser/components/sessionstore/test/browser/browser_599909.js
Normal 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 O’Shannessy <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"));
|
||||
}
|
@ -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
Loading…
Reference in New Issue
Block a user