This commit is contained in:
Kyle Huey 2012-05-26 14:22:26 -07:00
Родитель e2e55f231f 498b57c7b3
Коммит 1789ce83da
323 изменённых файлов: 14938 добавлений и 11204 удалений

Просмотреть файл

@ -792,7 +792,7 @@ getChildCountCB(AtkObject *aAtkObj)
return 0;
}
return accWrap->GetEmbeddedChildCount();
return static_cast<gint>(accWrap->EmbeddedChildCount());
}
AtkObject *

Просмотреть файл

@ -53,7 +53,7 @@ AccCollector::GetIndexAt(nsAccessible *aAccessible)
nsAccessible*
AccCollector::EnsureNGetObject(PRUint32 aIndex)
{
PRInt32 childCount = mRoot->GetChildCount();
PRUint32 childCount = mRoot->ChildCount();
while (mRootChildIdx < childCount) {
nsAccessible* child = mRoot->GetChildAt(mRootChildIdx++);
if (!mFilterFunc(child))
@ -70,7 +70,7 @@ AccCollector::EnsureNGetObject(PRUint32 aIndex)
PRInt32
AccCollector::EnsureNGetIndex(nsAccessible* aAccessible)
{
PRInt32 childCount = mRoot->GetChildCount();
PRUint32 childCount = mRoot->ChildCount();
while (mRootChildIdx < childCount) {
nsAccessible* child = mRoot->GetChildAt(mRootChildIdx++);
if (!mFilterFunc(child))

Просмотреть файл

@ -53,7 +53,7 @@ protected:
filters::FilterFuncPtr mFilterFunc;
nsAccessible* mRoot;
PRInt32 mRootChildIdx;
PRUint32 mRootChildIdx;
nsTArray<nsAccessible*> mObjects;

Просмотреть файл

@ -18,8 +18,9 @@ AccGroupInfo::AccGroupInfo(nsAccessible* aItem, role aRole) :
return;
PRInt32 indexInParent = aItem->IndexInParent();
PRInt32 siblingCount = parent->GetChildCount();
if (siblingCount < indexInParent) {
PRUint32 siblingCount = parent->ChildCount();
if (indexInParent == -1 ||
indexInParent >= static_cast<PRInt32>(siblingCount)) {
NS_ERROR("Wrong index in parent! Tree invalidation problem.");
return;
}
@ -28,7 +29,7 @@ AccGroupInfo::AccGroupInfo(nsAccessible* aItem, role aRole) :
// Compute position in set.
mPosInSet = 1;
for (PRInt32 idx = indexInParent - 1; idx >=0 ; idx--) {
for (PRInt32 idx = indexInParent - 1; idx >= 0 ; idx--) {
nsAccessible* sibling = parent->GetChildAt(idx);
roles::Role siblingRole = sibling->Role();
@ -69,7 +70,7 @@ AccGroupInfo::AccGroupInfo(nsAccessible* aItem, role aRole) :
// Compute set size.
mSetSize = mPosInSet;
for (PRInt32 idx = indexInParent + 1; idx < siblingCount; idx++) {
for (PRUint32 idx = indexInParent + 1; idx < siblingCount; idx++) {
nsAccessible* sibling = parent->GetChildAt(idx);
roles::Role siblingRole = sibling->Role();

Просмотреть файл

@ -51,8 +51,8 @@ TextAttrsMgr::GetAttributes(nsIPersistentProperties* aAttributes,
(*aStartHTOffset)--;
}
PRInt32 childCount = mHyperTextAcc->GetChildCount();
for (PRInt32 childIdx = mOffsetAccIdx + 1; childIdx < childCount;
PRUint32 childCount = mHyperTextAcc->ChildCount();
for (PRUint32 childIdx = mOffsetAccIdx + 1; childIdx < childCount;
childIdx++) {
nsAccessible *currAcc = mHyperTextAcc->GetChildAt(childIdx);
if (!nsAccUtils::IsEmbeddedObject(currAcc))
@ -164,8 +164,8 @@ TextAttrsMgr::GetRange(TextAttr* aAttrArray[], PRUint32 aAttrArrayLen,
}
// Navigate forward from anchor accessible to find end offset.
PRInt32 childLen = mHyperTextAcc->GetChildCount();
for (PRInt32 childIdx = mOffsetAccIdx + 1; childIdx < childLen; childIdx++) {
PRUint32 childLen = mHyperTextAcc->ChildCount();
for (PRUint32 childIdx = mOffsetAccIdx + 1; childIdx < childLen; childIdx++) {
nsAccessible *currAcc = mHyperTextAcc->GetChildAt(childIdx);
if (nsAccUtils::IsEmbeddedObject(currAcc))
break;

Просмотреть файл

@ -421,8 +421,8 @@ nsAccUtils::IsTextInterfaceSupportCorrect(nsAccessible* aAccessible)
return true;
bool foundText = false;
PRInt32 childCount = aAccessible->GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
PRUint32 childCount = aAccessible->ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible* child = aAccessible->GetChildAt(childIdx);
if (IsText(child)) {
foundText = true;

Просмотреть файл

@ -486,12 +486,7 @@ nsAccessible::GetFirstChild(nsIAccessible **aFirstChild)
if (IsDefunct())
return NS_ERROR_FAILURE;
PRInt32 childCount = GetChildCount();
NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
if (childCount > 0)
NS_ADDREF(*aFirstChild = GetChildAt(0));
NS_IF_ADDREF(*aFirstChild = FirstChild());
return NS_OK;
}
@ -505,10 +500,7 @@ nsAccessible::GetLastChild(nsIAccessible **aLastChild)
if (IsDefunct())
return NS_ERROR_FAILURE;
PRInt32 childCount = GetChildCount();
NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
NS_IF_ADDREF(*aLastChild = GetChildAt(childCount - 1));
NS_IF_ADDREF(*aLastChild = LastChild());
return NS_OK;
}
@ -521,13 +513,10 @@ nsAccessible::GetChildAt(PRInt32 aChildIndex, nsIAccessible **aChild)
if (IsDefunct())
return NS_ERROR_FAILURE;
PRInt32 childCount = GetChildCount();
NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
// If child index is negative, then return last child.
// XXX: do we really need this?
if (aChildIndex < 0)
aChildIndex = childCount - 1;
aChildIndex = ChildCount() - 1;
nsAccessible* child = GetChildAt(aChildIndex);
if (!child)
@ -547,15 +536,13 @@ nsAccessible::GetChildren(nsIArray **aOutChildren)
if (IsDefunct())
return NS_ERROR_FAILURE;
PRInt32 childCount = GetChildCount();
NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
nsresult rv = NS_OK;
nsCOMPtr<nsIMutableArray> children =
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
PRUint32 childCount = ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsIAccessible* child = GetChildAt(childIdx);
children->AppendElement(child, false);
}
@ -579,8 +566,8 @@ nsAccessible::GetChildCount(PRInt32 *aChildCount)
if (IsDefunct())
return NS_ERROR_FAILURE;
*aChildCount = GetChildCount();
return *aChildCount != -1 ? NS_OK : NS_ERROR_FAILURE;
*aChildCount = ChildCount();
return NS_OK;
}
/* readonly attribute long indexInParent; */
@ -815,9 +802,9 @@ nsAccessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
// where layout won't walk into things for us, such as image map areas and
// sub documents (XXX: subdocuments should be handled by methods of
// OuterDocAccessibles).
PRInt32 childCount = GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible *child = GetChildAt(childIdx);
PRUint32 childCount = ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible* child = GetChildAt(childIdx);
PRInt32 childX, childY, childWidth, childHeight;
child->GetBounds(&childX, &childY, &childWidth, &childHeight);
@ -2670,8 +2657,8 @@ nsAccessible::GetChildAt(PRUint32 aIndex)
return child;
}
PRInt32
nsAccessible::GetChildCount()
PRUint32
nsAccessible::ChildCount() const
{
return mChildren.Length();
}
@ -2688,16 +2675,16 @@ nsAccessible::IndexInParent() const
return mIndexInParent;
}
PRInt32
nsAccessible::GetEmbeddedChildCount()
PRUint32
nsAccessible::EmbeddedChildCount()
{
if (IsChildrenFlag(eMixedChildren)) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ? mEmbeddedObjCollector->Count() : -1;
return mEmbeddedObjCollector->Count();
}
return GetChildCount();
return ChildCount();
}
nsAccessible*
@ -3074,7 +3061,8 @@ nsAccessible::GetSiblingAtOffset(PRInt32 aOffset, nsresult* aError) const
return nsnull;
}
if (aError && mIndexInParent + aOffset >= mParent->GetChildCount()) {
if (aError &&
mIndexInParent + aOffset >= static_cast<PRInt32>(mParent->ChildCount())) {
*aError = NS_OK; // fail peacefully
return nsnull;
}
@ -3249,8 +3237,8 @@ nsAccessible::GetLevelInternal()
// If this listitem is on top of nested lists then expose 'level'
// attribute.
parent = Parent();
PRInt32 siblingCount = parent->GetChildCount();
for (PRInt32 siblingIdx = 0; siblingIdx < siblingCount; siblingIdx++) {
PRUint32 siblingCount = parent->ChildCount();
for (PRUint32 siblingIdx = 0; siblingIdx < siblingCount; siblingIdx++) {
nsAccessible* sibling = parent->GetChildAt(siblingIdx);
nsAccessible* siblingChild = sibling->LastChild();

Просмотреть файл

@ -328,7 +328,7 @@ public:
/**
* Return child accessible count.
*/
virtual PRInt32 GetChildCount();
virtual PRUint32 ChildCount() const;
/**
* Return index of the given child accessible.
@ -353,10 +353,10 @@ public:
inline nsAccessible* PrevSibling() const
{ return GetSiblingAtOffset(-1); }
inline nsAccessible* FirstChild()
{ return GetChildCount() != 0 ? GetChildAt(0) : nsnull; }
{ return GetChildAt(0); }
inline nsAccessible* LastChild()
{
PRUint32 childCount = GetChildCount();
PRUint32 childCount = ChildCount();
return childCount != 0 ? GetChildAt(childCount - 1) : nsnull;
}
@ -364,7 +364,7 @@ public:
/**
* Return embedded accessible children count.
*/
PRInt32 GetEmbeddedChildCount();
PRUint32 EmbeddedChildCount();
/**
* Return embedded accessible child at the given index.

Просмотреть файл

@ -175,9 +175,9 @@ nsTextEquivUtils::AppendFromAccessibleChildren(nsAccessible *aAccessible,
{
nsresult rv = NS_OK_NO_NAME_CLAUSE_HANDLED;
PRInt32 childCount = aAccessible->GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible *child = aAccessible->GetChildAt(childIdx);
PRUint32 childCount = aAccessible->ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible* child = aAccessible->GetChildAt(childIdx);
rv = AppendFromAccessible(child, aString);
NS_ENSURE_SUCCESS(rv, rv);
}

Просмотреть файл

@ -1040,9 +1040,9 @@ ARIAGridCellAccessible::GetAttributesInternal(nsIPersistentProperties* aAttribut
return NS_OK;
PRInt32 colIdx = 0, colCount = 0;
PRInt32 childCount = thisRow->GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible *child = thisRow->GetChildAt(childIdx);
PRUint32 childCount = thisRow->ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible* child = thisRow->GetChildAt(childIdx);
if (child == this)
colIdx = colCount;
@ -1061,9 +1061,9 @@ ARIAGridCellAccessible::GetAttributesInternal(nsIPersistentProperties* aAttribut
return NS_OK;
PRInt32 rowIdx = 0;
childCount = table->GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible *child = table->GetChildAt(childIdx);
childCount = table->ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible* child = table->GetChildAt(childIdx);
if (child == thisRow)
break;

Просмотреть файл

@ -589,8 +589,8 @@ RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode)
if (!popupContainer)
return;
PRInt32 childCount = popupContainer->GetChildCount();
for (PRInt32 idx = 0; idx < childCount; idx++) {
PRUint32 childCount = popupContainer->ChildCount();
for (PRUint32 idx = 0; idx < childCount; idx++) {
nsAccessible* child = popupContainer->GetChildAt(idx);
if (child->IsAutoCompletePopup()) {
popup = child;

Просмотреть файл

@ -51,7 +51,7 @@ nsHTMLImageMapAccessible::NativeRole()
PRUint32
nsHTMLImageMapAccessible::AnchorCount()
{
return GetChildCount();
return ChildCount();
}
nsAccessible*

Просмотреть файл

@ -112,7 +112,7 @@ nsHTMLTableCellAccessible::GetAttributesInternal(nsIPersistentProperties *aAttri
// Pick up object attribute from abbr DOM element (a child of the cell) or
// from abbr DOM attribute.
nsAutoString abbrText;
if (GetChildCount() == 1) {
if (ChildCount() == 1) {
nsAccessible* abbr = FirstChild();
if (abbr->IsAbbreviation()) {
nsTextEquivUtils::
@ -1397,7 +1397,7 @@ nsHTMLTableAccessible::IsProbablyLayoutTable()
}
nsAccessible* cell = mDoc->GetAccessible(cellElm);
if (cell && cell->GetChildCount() == 1 &&
if (cell && cell->ChildCount() == 1 &&
cell->FirstChild()->IsAbbreviation()) {
RETURN_LAYOUT_ANSWER(false,
"has abbr -- legitimate table structures");
@ -1454,7 +1454,7 @@ nsHTMLTableAccessible::IsProbablyLayoutTable()
// Check for styled background color across rows (alternating background
// color is a common feature for data tables).
PRUint32 childCount = GetChildCount();
PRUint32 childCount = ChildCount();
nscolor rowColor, prevRowColor;
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible* child = GetChildAt(childIdx);

Просмотреть файл

@ -140,7 +140,7 @@ nsHyperTextAccessible::NativeState()
states |= states::READONLY;
}
if (GetChildCount() > 0)
if (HasChildren())
states |= states::SELECTABLE_TEXT;
return states;
@ -271,8 +271,8 @@ nsHyperTextAccessible::GetPosAndText(PRInt32& aStartOffset, PRInt32& aEndOffset,
// Loop through children and collect valid offsets, text and bounds
// depending on what we need for out parameters.
PRInt32 childCount = GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
PRUint32 childCount = ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible *childAcc = mChildren[childIdx];
lastAccessible = childAcc;
@ -609,9 +609,9 @@ nsHyperTextAccessible::DOMPointToHypertextOffset(nsINode *aNode,
// If childAccessible is null we will end up adding up the entire length of
// the hypertext, which is good -- it just means our offset node
// came after the last accessible child's node
PRInt32 childCount = GetChildCount();
PRUint32 childCount = ChildCount();
PRInt32 childIdx = 0;
PRUint32 childIdx = 0;
nsAccessible *childAcc = nsnull;
for (; childIdx < childCount; childIdx++) {
childAcc = mChildren[childIdx];
@ -1290,8 +1290,8 @@ nsHyperTextAccessible::GetOffsetAtPoint(PRInt32 aX, PRInt32 aY,
// We have an point in an accessible child of this, now we need to add up the
// offsets before it to what we already have
PRInt32 offset = 0;
PRInt32 childCount = GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
PRUint32 childCount = ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible *childAcc = mChildren[childIdx];
nsIFrame *primaryFrame = childAcc->GetFrame();
@ -2195,7 +2195,7 @@ nsHyperTextAccessible::GetChildIndexAtOffset(PRUint32 aOffset)
}
}
PRUint32 childCount = GetChildCount();
PRUint32 childCount = ChildCount();
while (mOffsets.Length() < childCount) {
nsAccessible* child = GetChildAt(mOffsets.Length());
lastOffset += nsAccUtils::TextLength(child);

Просмотреть файл

@ -78,7 +78,7 @@ public:
*/
inline PRUint32 GetLinkCount()
{
return GetEmbeddedChildCount();
return EmbeddedChildCount();
}
/**
@ -181,7 +181,7 @@ public:
*/
inline PRUint32 CharacterCount()
{
return GetChildOffset(GetChildCount());
return GetChildOffset(ChildCount());
}
/**

Просмотреть файл

@ -198,6 +198,22 @@ var UtteranceGenerator = {
return utterance;
},
entry: function entry(aAccessible, aRoleStr, aStates, aFlags) {
let utterance = [];
let desc = this._getLocalizedStates(aStates);
desc.push(this._getLocalizedRole(
(aStates.ext & Ci.nsIAccessibleStates.EXT_STATE_MULTI_LINE) ?
'textarea' : 'entry'));
utterance.push(desc.join(' '));
let name = (aFlags & INCLUDE_NAME) ? (aAccessible.name || '') : '';
if (name)
utterance.push(name);
return utterance;
},
heading: function heading(aAccessible, aRoleStr, aStates, aFlags) {
let name = (aFlags & INCLUDE_NAME) ? (aAccessible.name || '') : '';
let level = {};

Просмотреть файл

@ -233,8 +233,8 @@ nsAccessibleWrap::GetUnignoredChildren(nsTArray<nsAccessible*>* aChildrenArray)
if (nsAccUtils::MustPrune(this))
return;
PRInt32 childCount = GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
PRUint32 childCount = ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessibleWrap *childAcc =
static_cast<nsAccessibleWrap*>(GetChildAt(childIdx));

Просмотреть файл

@ -187,7 +187,7 @@ __try {
if (nsAccUtils::MustPrune(this))
return S_OK;
*pcountChildren = GetChildCount();
*pcountChildren = ChildCount();
} __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
return S_OK;
@ -1048,10 +1048,10 @@ __try {
mEnumVARIANTPosition += aNumElements;
PRInt32 numChildren = GetChildCount();
if (mEnumVARIANTPosition > numChildren)
PRUint32 childCount = ChildCount();
if (mEnumVARIANTPosition > static_cast<PRInt32>(childCount))
{
mEnumVARIANTPosition = numChildren;
mEnumVARIANTPosition = childCount;
return S_FALSE;
}
} __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }

Просмотреть файл

@ -475,8 +475,8 @@ nsXFormsSelectableAccessible::GetItemByIndex(PRUint32* aIndex,
nsAccessible* aAccessible)
{
nsAccessible* accessible = aAccessible ? aAccessible : this;
PRInt32 childCount = accessible->GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
PRUint32 childCount = accessible->ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible *child = accessible->GetChildAt(childIdx);
nsIContent* childContent = child->GetContent();
nsINodeInfo *nodeInfo = childContent->NodeInfo();

Просмотреть файл

@ -446,8 +446,8 @@ XULGroupboxAccessible::RelationByType(PRUint32 aType)
// The label for xul:groupbox is generated from xul:label that is
// inside the anonymous content of the xul:caption.
// The xul:label has an accessible object but the xul:caption does not
PRInt32 childCount = GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
PRUint32 childCount = ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible *childAcc = GetChildAt(childIdx);
if (childAcc->Role() == roles::LABEL) {
// Ensure that it's our label
@ -601,8 +601,8 @@ XULToolbarButtonAccessible::GetPositionAndSizeInternal(PRInt32* aPosInSet,
if (!parent)
return;
PRInt32 childCount = parent->GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
PRUint32 childCount = parent->ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible* child = parent->GetChildAt(childIdx);
if (IsSeparator(child)) { // end of a group of buttons
if (posInSet)

Просмотреть файл

@ -528,9 +528,9 @@ nsXULListboxAccessible::GetSelectedCells(nsIArray **aCells)
nsAccessible *item = mDoc->GetAccessible(itemContent);
if (item) {
PRInt32 cellCount = item->GetChildCount();
for (PRInt32 cellIdx = 0; cellIdx < cellCount; cellIdx++) {
nsAccessible *cell = mChildren[cellIdx];
PRUint32 cellCount = item->ChildCount();
for (PRUint32 cellIdx = 0; cellIdx < cellCount; cellIdx++) {
nsAccessible* cell = mChildren[cellIdx];
if (cell->Role() == roles::CELL)
selCells->AppendElement(static_cast<nsIAccessible*>(cell), false);
}
@ -1080,8 +1080,8 @@ nsXULListCellAccessible::GetColumnHeaderCells(nsIArray **aHeaderCells)
nsAccessible *list = nsnull;
nsRefPtr<nsAccessible> tableAcc(do_QueryObject(table));
PRInt32 tableChildCount = tableAcc->GetChildCount();
for (PRInt32 childIdx = 0; childIdx < tableChildCount; childIdx++) {
PRUint32 tableChildCount = tableAcc->ChildCount();
for (PRUint32 childIdx = 0; childIdx < tableChildCount; childIdx++) {
nsAccessible *child = tableAcc->GetChildAt(childIdx);
if (child->Role() == roles::LIST) {
list = child;

Просмотреть файл

@ -428,22 +428,19 @@ nsXULTreeAccessible::SelectAll()
nsAccessible*
nsXULTreeAccessible::GetChildAt(PRUint32 aIndex)
{
PRInt32 childCount = nsAccessible::GetChildCount();
if (childCount == -1)
return nsnull;
if (static_cast<PRInt32>(aIndex) < childCount)
PRUint32 childCount = nsAccessible::ChildCount();
if (aIndex < childCount)
return nsAccessible::GetChildAt(aIndex);
return GetTreeItemAccessible(aIndex - childCount);
}
PRInt32
nsXULTreeAccessible::GetChildCount()
PRUint32
nsXULTreeAccessible::ChildCount() const
{
// tree's children count is row count + treecols count.
PRInt32 childCount = nsAccessible::GetChildCount();
if (childCount == -1 || !mTreeView)
// Tree's children count is row count + treecols count.
PRUint32 childCount = nsAccessible::ChildCount();
if (!mTreeView)
return childCount;
PRInt32 rowCount = 0;

Просмотреть файл

@ -23,7 +23,6 @@ const PRUint32 kDefaultTreeCacheSize = 256;
class nsXULTreeAccessible : public nsAccessibleWrap
{
public:
using nsAccessible::GetChildCount;
using nsAccessible::GetChildAt;
nsXULTreeAccessible(nsIContent* aContent, nsDocAccessible* aDoc);
@ -44,7 +43,7 @@ public:
EWhichChildAtPoint aWhichChild);
virtual nsAccessible* GetChildAt(PRUint32 aIndex);
virtual PRInt32 GetChildCount();
virtual PRUint32 ChildCount() const;
// SelectAccessible
virtual bool IsSelect();

Просмотреть файл

@ -667,12 +667,9 @@ nsXULTreeGridRowAccessible::GetChildAt(PRUint32 aIndex)
return GetCellAccessible(column);
}
PRInt32
nsXULTreeGridRowAccessible::GetChildCount()
PRUint32
nsXULTreeGridRowAccessible::ChildCount() const
{
if (IsDefunct())
return -1;
return nsCoreUtils::GetSensibleColumnCount(mTree);
}

Просмотреть файл

@ -54,7 +54,6 @@ protected:
class nsXULTreeGridRowAccessible : public nsXULTreeItemAccessibleBase
{
public:
using nsAccessible::GetChildCount;
using nsAccessible::GetChildAt;
nsXULTreeGridRowAccessible(nsIContent* aContent, nsDocAccessible* aDoc,
@ -76,7 +75,7 @@ public:
EWhichChildAtPoint aWhichChild);
virtual nsAccessible* GetChildAt(PRUint32 aIndex);
virtual PRInt32 GetChildCount();
virtual PRUint32 ChildCount() const;
// nsXULTreeItemAccessibleBase
virtual nsAccessible* GetCellAccessible(nsITreeColumn *aColumn);

Просмотреть файл

@ -1,9 +1,5 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1337190282000">
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1337963207000">
<emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion="*">
@ -160,6 +156,8 @@
<versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
<emItem blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
</emItem>
<emItem blockID="i90" id="videoplugin@player.com">
<versionRange minVersion="0" maxVersion="*">
</versionRange>
@ -284,8 +282,10 @@
<versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
<emItem blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
</emItem>
<emItem blockID="i96" id="youtubeee@youtuber3.com">
<versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
<emItem blockID="i77" id="{fa277cfc-1d75-4949-a1f9-4ac8e41b2dfd}">
<versionRange minVersion="0" maxVersion="*">
</versionRange>
@ -364,6 +364,9 @@
<pluginItem os="Darwin" blockID="p89">
<match name="filename" exp="AdobePDFViewerNPAPI\.plugin" /> <versionRange minVersion="0" maxVersion="10.1.3" severity="1"></versionRange>
</pluginItem>
<pluginItem blockID="p94">
<match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="0" maxVersion="10.2.159.1" severity="0"></versionRange>
</pluginItem>
</pluginItems>
<gfxItems>
@ -386,6 +389,16 @@
<device>0x9807</device>
</devices>
<feature>DIRECT2D</feature> <featureStatus>BLOCKED_DEVICE</featureStatus> <driverVersion>1.0.0.0</driverVersion> <driverVersionComparator>GREATER_THAN_OR_EQUAL</driverVersionComparator> </gfxBlacklistEntry>
<gfxBlacklistEntry blockID="g95"> <os>WINNT 6.1</os> <vendor>0x1002</vendor> <devices>
<device>0x9802</device>
<device>0x9803</device>
<device>0x9803</device>
<device>0x9804</device>
<device>0x9805</device>
<device>0x9806</device>
<device>0x9807</device>
</devices>
<feature>DIRECT3D_9_LAYERS</feature> <featureStatus>BLOCKED_DEVICE</featureStatus> <driverVersion>1.0.0.0</driverVersion> <driverVersionComparator>GREATER_THAN_OR_EQUAL</driverVersionComparator> </gfxBlacklistEntry>
</gfxItems>

Просмотреть файл

@ -40,14 +40,14 @@ window.onload = function() {
sessionData.dispatchEvent(event);
initTreeView();
document.getElementById("errorTryAgain").focus();
};
function initTreeView() {
var tabList = document.getElementById("tabList");
var winLabel = tabList.getAttribute("_window_label");
gTreeData = [];
gStateObject.windows.forEach(function(aWinData, aIx) {
var winState = {
@ -73,7 +73,7 @@ function initTreeView() {
for each (var tab in winState.tabs)
gTreeData.push(tab);
}, this);
tabList.view = treeView;
tabList.view.selection.select(0);
}
@ -82,7 +82,7 @@ function initTreeView() {
function restoreSession() {
document.getElementById("errorTryAgain").disabled = true;
// remove all unselected tabs from the state before restoring it
var ix = gStateObject.windows.length - 1;
for (var t = gTreeData.length - 1; t >= 0; t--) {
@ -99,10 +99,10 @@ function restoreSession() {
}
}
var stateString = JSON.stringify(gStateObject);
var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
var top = getBrowserWindow();
// if there's only this page open, reuse the window for restoring the session
if (top.gBrowser.tabs.length == 1) {
ss.setWindowState(top, stateString, true);
@ -114,7 +114,7 @@ function restoreSession() {
newWindow.addEventListener("load", function() {
newWindow.removeEventListener("load", arguments.callee, true);
ss.setWindowState(newWindow, stateString, true);
var tabbrowser = top.gBrowser;
var tabIndex = tabbrowser.getBrowserIndexForDocument(document);
tabbrowser.removeTab(tabbrowser.tabs[tabIndex]);
@ -133,7 +133,7 @@ function onListClick(aEvent) {
// don't react to right-clicks
if (aEvent.button == 2)
return;
var row = {}, col = {};
treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY, row, col, {});
if (col.value) {
@ -189,9 +189,9 @@ function toggleRowChecked(aIx) {
var item = gTreeData[aIx];
item.checked = !item.checked;
treeView.treeBox.invalidateRow(aIx);
function isChecked(aItem) aItem.checked;
if (treeView.isContainer(aIx)) {
// (un)check all tabs of this window as well
for each (var tab in item.tabs) {
@ -205,7 +205,7 @@ function toggleRowChecked(aIx) {
item.parent.tabs.some(isChecked) ? 0 : false;
treeView.treeBox.invalidateRow(gTreeData.indexOf(item.parent));
}
document.getElementById("errorTryAgain").disabled = !gTreeData.some(isChecked);
}
@ -213,14 +213,14 @@ function restoreSingleTab(aIx, aShifted) {
var tabbrowser = getBrowserWindow().gBrowser;
var newTab = tabbrowser.addTab();
var item = gTreeData[aIx];
var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
var tabState = gStateObject.windows[item.parent.ix]
.tabs[aIx - gTreeData.indexOf(item.parent) - 1];
// ensure tab would be visible on the tabstrip.
tabState.hidden = false;
ss.setTabState(newTab, JSON.stringify(tabState));
// respect the preference as to whether to select the tab (the Shift key inverses)
var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
if (prefBranch.getBoolPref("browser.tabs.loadInBackground") != !aShifted)

Просмотреть файл

@ -29,15 +29,15 @@
<!-- PAGE CONTAINER (for styling purposes only) -->
<div id="errorPageContainer">
<!-- Error Title -->
<div id="errorTitle">
<h1 id="errorTitleText">&restorepage.errorTitle;</h1>
</div>
<!-- LONG CONTENT (the section most likely to require scrolling) -->
<div id="errorLongContent">
<!-- Short Description -->
<div id="errorShortDesc">
<p id="errorShortDescText">&restorepage.problemDesc;</p>

Просмотреть файл

@ -3,5 +3,5 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
browser.jar:
* content/browser/aboutSessionRestore.xhtml (content/aboutSessionRestore.xhtml)
* content/browser/aboutSessionRestore.xhtml (content/aboutSessionRestore.xhtml)
* content/browser/aboutSessionRestore.js (content/aboutSessionRestore.js)

Просмотреть файл

@ -17,7 +17,7 @@ interface nsISessionStartup: nsISupports
readonly attribute jsval state;
/**
* Determine if session should be restored
* Determine if session should be restored
*/
boolean doRestore();

Просмотреть файл

@ -68,7 +68,7 @@ interface nsISessionStore : nsISupports
/**
* @param aWindow is the browser window whose state is to be returned.
*
*
* @returns a JSON string representing a session state with only one window.
*/
AString getWindowState(in nsIDOMWindow aWindow);
@ -82,7 +82,7 @@ interface nsISessionStore : nsISupports
/**
* @param aTab is the tabbrowser tab whose state is to be returned.
*
*
* @returns a JSON string representing the state of the tab
* (note: doesn't contain cookies - if you need them, use getWindowState instead).
*/
@ -160,7 +160,7 @@ interface nsISessionStore : nsISupports
/**
* @param aWindow is the window to get the value for.
* @param aKey is the value's name.
*
*
* @returns A string value or an empty string if none is set.
*/
AString getWindowValue(in nsIDOMWindow aWindow, in AString aKey);
@ -171,7 +171,7 @@ interface nsISessionStore : nsISupports
* @param aStringValue is the value itself (use JSON.stringify/parse before setting JS objects).
*/
void setWindowValue(in nsIDOMWindow aWindow, in AString aKey, in AString aStringValue);
/**
* @param aWindow is the browser window to get the value for.
* @param aKey is the value's name.
@ -181,7 +181,7 @@ interface nsISessionStore : nsISupports
/**
* @param aTab is the tabbrowser tab to get the value for.
* @param aKey is the value's name.
*
*
* @returns A string value or an empty string if none is set.
*/
AString getTabValue(in nsIDOMNode aTab, in AString aKey);

Просмотреть файл

@ -35,7 +35,7 @@ let DocumentUtils = {
let node;
let ret = {id: {}, xpath: {}};
// Limit the number of XPath expressions for performance reasons. See
// bug 477564.
const MAX_TRAVERSED_XPATHS = 100;
@ -121,7 +121,7 @@ let DocumentUtils = {
if ("xpath" in aData) {
for each (let [xpath, value] in Iterator(aData.xpath)) {
let node = XPathGenerator.resolve(aDocument, xpath);
if (node) {
this.restoreFormValue(node, value, aDocument);
}
@ -131,7 +131,7 @@ let DocumentUtils = {
if ("id" in aData) {
for each (let [id, value] in Iterator(aData.id)) {
let node = aDocument.getElementById(id);
if (node) {
this.restoreFormValue(node, value, aDocument);
}
@ -159,7 +159,7 @@ let DocumentUtils = {
aDocument = aDocument || aNode.ownerDocument;
let eventType;
if (typeof aValue == "string" && aNode.type != "file") {
// Don't dispatch an input event if there is no change.
if (aNode.value == aValue) {
@ -173,7 +173,7 @@ let DocumentUtils = {
if (aNode.checked == aValue) {
return;
}
aNode.checked = aValue;
eventType = "change";
} else if (typeof aValue == "number") {
@ -183,11 +183,11 @@ let DocumentUtils = {
if (aNode.selectedIndex == aValue) {
return;
}
if (aValue < aNode.options.length) {
aNode.selectedIndex = aValue;
eventType = "change";
}
}
} else if (aValue && aValue.selectedIndex >= 0 && aValue.value) {
// handle select new format
@ -212,7 +212,7 @@ let DocumentUtils = {
Array.forEach(aNode.options, function(opt, index) {
// don't worry about malformed options with same values
opt.selected = aValue.indexOf(opt.value) > -1;
// Only fire the event here if this wasn't selected by default
if (!opt.defaultSelected) {
eventType = "change";

Просмотреть файл

@ -21,4 +21,21 @@ EXTRA_PP_COMPONENTS = \
libs::
$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/sessionstore
SS_EXTRA_PP_JS_MODULES = \
SessionStore.jsm \
$(NULL)
ifdef SS_EXTRA_PP_JS_MODULES
libs:: $(SS_EXTRA_PP_JS_MODULES)
ifndef NO_DIST_INSTALL
$(EXIT_ON_ERROR) \
$(NSINSTALL) -D $(FINAL_TARGET)/modules/sessionstore; \
for i in $^; do \
dest=$(FINAL_TARGET)/modules/sessionstore/`basename $$i`; \
$(RM) -f $$dest; \
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
done
endif
endif
include $(topsrcdir)/config/rules.mk

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -16,18 +16,18 @@ let XPathGenerator = {
// have we reached the document node already?
if (!aNode.parentNode)
return "";
// Access localName, namespaceURI just once per node since it's expensive.
let nNamespaceURI = aNode.namespaceURI;
let nLocalName = aNode.localName;
let prefix = this.namespacePrefixes[nNamespaceURI] || null;
let tag = (prefix ? prefix + ":" : "") + this.escapeName(nLocalName);
// stop once we've found a tag with an ID
if (aNode.id)
return "//" + tag + "[@id=" + this.quoteArgument(aNode.id) + "]";
// count the number of previous sibling nodes of the same tag
// (and possible also the same name)
let count = 0;
@ -36,7 +36,7 @@ let XPathGenerator = {
if (n.localName == nLocalName && n.namespaceURI == nNamespaceURI &&
(!nName || n.name == nName))
count++;
// recurse until hitting either the document node or an ID'd node
return this.generate(aNode.parentNode) + "/" + tag +
(nName ? "[@name=" + this.quoteArgument(nName) + "]" : "") +
@ -90,7 +90,7 @@ let XPathGenerator = {
ignoreTypes.join("' or translate(@type, " + toLowerCase + ")='") + "')";
let formNodesXPath = "//textarea|//select|//xhtml:textarea|//xhtml:select|" +
"//input[" + ignore + "]|//xhtml:input[" + ignore + "]";
delete this.restorableFormNodes;
return (this.restorableFormNodes = formNodesXPath);
}

Просмотреть файл

@ -1,4 +1,4 @@
/*
/*
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
@ -6,28 +6,28 @@
/**
# * Session Storage and Restoration
# *
# *
# * Overview
# * This service reads user's session file at startup, and makes a determination
# * as to whether the session should be restored. It will restore the session
# * This service reads user's session file at startup, and makes a determination
# * as to whether the session should be restored. It will restore the session
# * under the circumstances described below. If the auto-start Private Browsing
# * mode is active, however, the session is never restored.
# *
# *
# * Crash Detection
# * The session file stores a session.state property, that
# * indicates whether the browser is currently running. When the browser shuts
# * The session file stores a session.state property, that
# * indicates whether the browser is currently running. When the browser shuts
# * down, the field is changed to "stopped". At startup, this field is read, and
# * if its value is "running", then it's assumed that the browser had previously
# * crashed, or at the very least that something bad happened, and that we should
# * restore the session.
# *
# *
# * Forced Restarts
# * In the event that a restart is required due to application update or extension
# * installation, set the browser.sessionstore.resume_session_once pref to true,
# * and the session will be restored the next time the browser starts.
# *
# *
# * Always Resume
# * This service will always resume the session if the integer pref
# * This service will always resume the session if the integer pref
# * browser.startup.page is set to 3.
*/
@ -161,11 +161,11 @@ SessionStartup.prototype = {
*/
observe: function sss_observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "app-startup":
case "app-startup":
Services.obs.addObserver(this, "final-ui-startup", true);
Services.obs.addObserver(this, "quit-application", true);
break;
case "final-ui-startup":
case "final-ui-startup":
Services.obs.removeObserver(this, "final-ui-startup");
Services.obs.removeObserver(this, "quit-application");
this.init();
@ -206,7 +206,7 @@ SessionStartup.prototype = {
var wType = aWindow.document.documentElement.getAttribute("windowtype");
if (wType != "navigator:browser")
return;
/**
* Note: this relies on the fact that nsBrowserContentHandler will return
* a different value the first time its getter is called after an update,

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -132,6 +132,8 @@ _BROWSER_TEST_FILES = \
browser_701377.js \
browser_705597.js \
browser_707862.js \
browser_739531.js \
browser_739531_sample.html \
browser_739805.js \
$(NULL)

Просмотреть файл

@ -15,17 +15,17 @@
<input type="radio" name="group" checked> Radio 3
<h3>Selects</h3>
<select name="any">
<option value="1"> Select 1
<option value="some"> Select 2
<select name="any">
<option value="1"> Select 1
<option value="some"> Select 2
<option>Select 3
</select>
<select multiple="multiple">
<select multiple="multiple">
<option value=1> Multi-select 1
<option value=2> Multi-select 2
<option value=2> Multi-select 2
<option value=3> Multi-select 3
<option value=4> Multi-select 4
</select>
</select>
<h3>Text Areas</h3>
<textarea name="testarea"></textarea>

Просмотреть файл

@ -4,30 +4,30 @@
function test() {
/** Test for Bug 339445 **/
waitForExplicitFinish();
let testURL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_339445_sample.html";
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
let doc = tab.linkedBrowser.contentDocument;
is(doc.getElementById("storageTestItem").textContent, "PENDING",
"sessionStorage value has been set");
let tab2 = gBrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
let doc2 = tab2.linkedBrowser.contentDocument;
is(doc2.getElementById("storageTestItem").textContent, "SUCCESS",
"sessionStorage value has been duplicated");
// clean up
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab);
finish();
}, true);
}, true);

Просмотреть файл

@ -4,7 +4,7 @@
function test() {
/** Test for Bug 345898 **/
function test(aLambda) {
try {
aLambda();

Просмотреть файл

@ -17,7 +17,7 @@ function test() {
file.append("346337_test2.file");
file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
var filePath2 = file.path;
let fieldList = {
"//input[@name='input']": Date.now().toString(),
"//input[@name='spaced 1']": Math.random().toString(),
@ -35,13 +35,13 @@ function test() {
"//input[@type='file'][1]": [filePath1],
"//input[@type='file'][2]": [filePath1, filePath2]
};
function getElementByXPath(aTab, aQuery) {
let doc = aTab.linkedBrowser.contentDocument;
let xptype = Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE;
return doc.evaluate(aQuery, doc, null, xptype, null).singleNodeValue;
}
function setFormValue(aTab, aQuery, aValue) {
let node = getElementByXPath(aTab, aQuery);
if (typeof aValue == "string")
@ -56,7 +56,7 @@ function test() {
Array.forEach(node.options, function(aOpt, aIx)
(aOpt.selected = aValue.indexOf(aIx) > -1));
}
function compareFormValue(aTab, aQuery, aValue) {
let node = getElementByXPath(aTab, aQuery);
if (!node)
@ -77,14 +77,14 @@ function test() {
return Array.every(node.options, function(aOpt, aIx)
(aValue.indexOf(aIx) > -1) == aOpt.selected);
}
// test setup
let tabbrowser = gBrowser;
waitForExplicitFinish();
// make sure we don't save form data at all (except for tab duplication)
gPrefService.setIntPref("browser.sessionstore.privacy_level", 2);
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_346337_sample.html";
let tab = tabbrowser.addTab(testURL);
@ -92,18 +92,18 @@ function test() {
this.removeEventListener("load", arguments.callee, true);
for (let xpath in fieldList)
setFormValue(tab, xpath, fieldList[xpath]);
let tab2 = tabbrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
for (let xpath in fieldList)
ok(compareFormValue(tab2, xpath, fieldList[xpath]),
"The value for \"" + xpath + "\" was correctly restored");
// clean up
tabbrowser.removeTab(tab2);
tabbrowser.removeTab(tab);
tab = undoCloseTab();
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
@ -111,7 +111,7 @@ function test() {
if (fieldList[xpath])
ok(!compareFormValue(tab, xpath, fieldList[xpath]),
"The value for \"" + xpath + "\" was correctly discarded");
if (gPrefService.prefHasUserValue("browser.sessionstore.privacy_level"))
gPrefService.clearUserPref("browser.sessionstore.privacy_level");
// undoCloseTab can reuse a single blank tab, so we have to

Просмотреть файл

@ -15,17 +15,17 @@
<input type="radio" name="group" checked> Radio 3
<h3>Selects</h3>
<select name="any">
<option value="1"> Select 1
<option value="some"> Select 2
<select name="any">
<option value="1"> Select 1
<option value="some"> Select 2
<option>Select 3
</select>
<select multiple="multiple">
<select multiple="multiple">
<option value=1> Multi-select 1
<option value=2> Multi-select 2
<option value=2> Multi-select 2
<option value=3> Multi-select 3
<option value=4> Multi-select 4
</select>
</select>
<h3>Text Areas</h3>
<textarea name="testarea"></textarea>

Просмотреть файл

@ -1,6 +1,6 @@
function test() {
/** Test for Bug 350525 **/
function test(aLambda) {
try {
return aLambda() || true;
@ -16,22 +16,22 @@ function test() {
////////////////////////////
let key = "Unique name: " + Date.now();
let value = "Unique value: " + Math.random();
// test adding
ok(test(function() ss.setWindowValue(window, key, value)), "set a window value");
// test retrieving
is(ss.getWindowValue(window, key), value, "stored window value matches original");
// test deleting
// test deleting
ok(test(function() ss.deleteWindowValue(window, key)), "delete the window value");
// value should not exist post-delete
is(ss.getWindowValue(window, key), "", "window value was deleted");
// test deleting a non-existent value
ok(test(function() ss.deleteWindowValue(window, key)), "delete non-existent window value");
/////////////////////////
// setTabValue, et al. //
/////////////////////////
@ -39,35 +39,35 @@ function test() {
value = "Unique value: " + Date.now();
let tab = gBrowser.addTab();
tab.linkedBrowser.stop();
// test adding
ok(test(function() ss.setTabValue(tab, key, value)), "store a tab value");
// test retrieving
is(ss.getTabValue(tab, key), value, "stored tab value match original");
// test deleting
// test deleting
ok(test(function() ss.deleteTabValue(tab, key)), "delete the tab value");
// value should not exist post-delete
is(ss.getTabValue(tab, key), "", "tab value was deleted");
// test deleting a non-existent value
ok(test(function() ss.deleteTabValue(tab, key)), "delete non-existent tab value");
// clean up
gBrowser.removeTab(tab);
/////////////////////////////////////
// getClosedTabCount, undoCloseTab //
/////////////////////////////////////
// get closed tab count
let count = ss.getClosedTabCount(window);
let max_tabs_undo = gPrefService.getIntPref("browser.sessionstore.max_tabs_undo");
ok(0 <= count && count <= max_tabs_undo,
"getClosedTabCount returns zero or at most max_tabs_undo");
// create a new tab
let testURL = "about:";
tab = gBrowser.addTab(testURL);
@ -75,22 +75,22 @@ function test() {
this.removeEventListener("load", arguments.callee, true);
// make sure that the next closed tab will increase getClosedTabCount
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
// remove tab
gBrowser.removeTab(tab);
// getClosedTabCount
var newcount = ss.getClosedTabCount(window);
ok(newcount > count, "after closing a tab, getClosedTabCount has been incremented");
// undoCloseTab
tab = test(function() ss.undoCloseTab(window, 0));
ok(tab, "undoCloseTab doesn't throw")
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
is(this.currentURI.spec, testURL, "correct tab was reopened");
// clean up
if (gPrefService.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");

Просмотреть файл

@ -6,29 +6,29 @@ function test() {
/** Test for Bug 367052 **/
waitForExplicitFinish();
// make sure that the next closed tab will increase getClosedTabCount
let max_tabs_undo = gPrefService.getIntPref("browser.sessionstore.max_tabs_undo");
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
let closedTabCount = ss.getClosedTabCount(window);
// restore a blank tab
let tab = gBrowser.addTab("about:");
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
let history = tab.linkedBrowser.webNavigation.sessionHistory;
ok(history.count >= 1, "the new tab does have at least one history entry");
ss.setTabState(tab, JSON.stringify({ entries: [] }));
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
ok(history.count == 0, "the tab was restored without any history whatsoever");
gBrowser.removeTab(tab);
ok(ss.getClosedTabCount(window) == closedTabCount,
"The closed blank tab wasn't added to Recently Closed Tabs");
// clean up
if (gPrefService.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");

Просмотреть файл

@ -2,14 +2,14 @@ function test() {
/** Test for Bug 393716 **/
waitForExplicitFinish();
/////////////////
// getTabState //
/////////////////
let key = "Unique key: " + Date.now();
let value = "Unique value: " + Math.random();
let testURL = "about:config";
// create a new tab
let tab = gBrowser.addTab(testURL);
ss.setTabValue(tab, key, value);
@ -18,7 +18,7 @@ function test() {
// get the tab's state
let state = ss.getTabState(tab);
ok(state, "get the tab's state");
// verify the tab state's integrity
state = JSON.parse(state);
ok(state instanceof Object && state.entries instanceof Array && state.entries.length > 0,
@ -27,11 +27,11 @@ function test() {
"Got the expected state object (test URL)");
ok(state.extData && state.extData[key] == value,
"Got the expected state object (test manually set tab value)");
// clean up
gBrowser.removeTab(tab);
}, true);
//////////////////////////////////
// setTabState and duplicateTab //
//////////////////////////////////
@ -39,7 +39,7 @@ function test() {
let value2 = "Value " + Math.random();
let value3 = "Another value: " + Date.now();
let state = { entries: [{ url: testURL }], extData: { key2: value2 } };
// create a new tab
let tab2 = gBrowser.addTab();
// set the tab's state
@ -49,15 +49,15 @@ function test() {
// verify the correctness of the restored tab
ok(ss.getTabValue(tab2, key2) == value2 && this.currentURI.spec == testURL,
"the tab's state was correctly restored");
// add text data
let textbox = this.contentDocument.getElementById("textbox");
textbox.value = value3;
// duplicate the tab
let duplicateTab = ss.duplicateTab(window, tab2);
gBrowser.removeTab(tab2);
duplicateTab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
// verify the correctness of the duplicated tab
@ -65,7 +65,7 @@ function test() {
"correctly duplicated the tab's state");
let textbox = this.contentDocument.getElementById("textbox");
is(textbox.value, value3, "also duplicated text data");
// clean up
gBrowser.removeTab(duplicateTab);
finish();

Просмотреть файл

@ -74,7 +74,7 @@ function test() {
}
]
};
// set browser to test state
ss.setBrowserState(JSON.stringify(testState));
@ -93,7 +93,7 @@ function test() {
is(win.selected, 1, "Selected tab has changed");
is(win.title, REMEMBER, "The window title was correctly updated");
// Test more complicated case
// Test more complicated case
win = closedWindowData[1];
is(win.tabs.length, 3, "2 tabs were removed");
is(countOpenTabsByTitle(win.tabs, FORGET), 0,

Просмотреть файл

@ -4,14 +4,14 @@
function test() {
/** Test for Bug 408470 **/
waitForExplicitFinish();
let pendingCount = 1;
let rootDir = getRootDirectory(gTestPath);
let testUrl = rootDir + "browser_408470_sample.html";
let tab = gBrowser.addTab(testUrl);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
// enable all stylesheets and verify that they're correctly persisted
@ -26,18 +26,18 @@ function test() {
let states = Array.map(newTab.linkedBrowser.contentDocument.styleSheets,
function(aSS) !aSS.disabled);
let correct = states.indexOf(true) == aIx && states.indexOf(true, aIx + 1) == -1;
if (/^fail_/.test(ssTitle))
ok(!correct, "didn't restore stylesheet " + ssTitle);
else
ok(correct, "restored stylesheet " + ssTitle);
gBrowser.removeTab(newTab);
if (--pendingCount == 0)
finish();
}, true);
});
// disable all styles and verify that this is correctly persisted
tab.linkedBrowser.markupDocumentViewer.authorStyleDisabled = true;
let newTab = gBrowser.duplicateTab(tab);
@ -45,12 +45,12 @@ function test() {
newTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
is(newTab.linkedBrowser.markupDocumentViewer.authorStyleDisabled, true,
"disabled all stylesheets");
gBrowser.removeTab(newTab);
if (--pendingCount == 0)
finish();
}, true);
gBrowser.removeTab(tab);
}, true);
}

Просмотреть файл

@ -4,34 +4,34 @@
function test() {
/** Test for Bug 447951 **/
waitForExplicitFinish();
const baseURL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_447951_sample.html#";
let tab = gBrowser.addTab();
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
let tabState = { entries: [] };
let max_entries = gPrefService.getIntPref("browser.sessionhistory.max_entries");
for (let i = 0; i < max_entries; i++)
tabState.entries.push({ url: baseURL + i });
ss.setTabState(tab, JSON.stringify(tabState));
tab.addEventListener("SSTabRestored", function(aEvent) {
tab.removeEventListener("SSTabRestored", arguments.callee, false);
tabState = JSON.parse(ss.getTabState(tab));
is(tabState.entries.length, max_entries, "session history filled to the limit");
is(tabState.entries[0].url, baseURL + 0, "... but not more");
// visit yet another anchor (appending it to session history)
let doc = tab.linkedBrowser.contentDocument;
let event = doc.createEvent("MouseEvents");
event.initMouseEvent("click", true, true, doc.defaultView, 1,
0, 0, 0, 0, false, false, false, false, 0, null);
doc.querySelector("a").dispatchEvent(event);
executeSoon(function() {
tabState = JSON.parse(ss.getTabState(tab));
is(tab.linkedBrowser.currentURI.spec, baseURL + "end",
@ -40,7 +40,7 @@ function test() {
"... and ignored");
is(tabState.entries[0].url, baseURL + 1,
"... and the first item was removed");
// clean up
gBrowser.removeTab(tab);
finish();

Просмотреть файл

@ -4,17 +4,17 @@
function test() {
/** Test for Bug 454908 **/
waitForExplicitFinish();
let fieldValues = {
username: "User " + Math.random(),
passwd: "pwd" + Date.now()
};
// make sure we do save form data
gPrefService.setIntPref("browser.sessionstore.privacy_level", 0);
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_454908_sample.html";
let tab = gBrowser.addTab(testURL);
@ -23,9 +23,9 @@ function test() {
let doc = tab.linkedBrowser.contentDocument;
for (let id in fieldValues)
doc.getElementById(id).value = fieldValues[id];
gBrowser.removeTab(tab);
tab = undoCloseTab();
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
@ -37,7 +37,7 @@ function test() {
else
is(node.value, fieldValues[id], "username was saved/restored");
}
// clean up
if (gPrefService.prefHasUserValue("browser.sessionstore.privacy_level"))
gPrefService.clearUserPref("browser.sessionstore.privacy_level");

Просмотреть файл

@ -4,12 +4,12 @@
function test() {
/** Test for Bug 456342 **/
waitForExplicitFinish();
// make sure we do save form data
gPrefService.setIntPref("browser.sessionstore.privacy_level", 0);
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_456342_sample.xhtml";
let tab = gBrowser.addTab(testURL);
@ -24,10 +24,10 @@ function test() {
formEls[i].value = expectedValue;
gBrowser.removeTab(tab);
let undoItems = JSON.parse(ss.getClosedTabData(window));
let savedFormData = undoItems[0].state.entries[0].formdata;
let countGood = 0, countBad = 0;
for each (let value in savedFormData.id) {
if (value == expectedValue)
@ -44,7 +44,7 @@ function test() {
is(countGood, 4, "Saved text for non-standard input fields");
is(countBad, 0, "Didn't save text for ignored field types");
// clean up
if (gPrefService.prefHasUserValue("browser.sessionstore.privacy_level"))
gPrefService.clearUserPref("browser.sessionstore.privacy_level");

Просмотреть файл

@ -23,7 +23,7 @@
document.getElementsByTagName("iframe")[0].onload =
function() { documentInjected = true; };
frames[0].location = "browser_459906_empty.html";
// ... and ensure that it has time to load
for (var c = 0; !documentInjected && c < 20; c++) {
var r = new XMLHttpRequest();

Просмотреть файл

@ -4,9 +4,9 @@
function test() {
/** Test for Bug 463205 **/
waitForExplicitFinish();
let rootDir = "http://mochi.test:8888/browser/browser/components/sessionstore/test/";
let testURL = rootDir + "browser_463205_sample.html";
@ -18,7 +18,7 @@ function test() {
let frame3URL = "data:text/html,mark2";
let frameCount = 0;
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
// wait for all frames to load completely
@ -50,15 +50,15 @@ function test() {
return;
}
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
function typeText(aTextField, aValue) {
aTextField.value = aValue;
let event = aTextField.ownerDocument.createEvent("UIEvents");
event.initUIEvent("input", true, true, aTextField.ownerDocument.defaultView, 0);
aTextField.dispatchEvent(event);
}
let uniqueValue = "Unique: " + Math.random();
let win = tab.linkedBrowser.contentWindow;
typeText(win.frames[0].document.getElementById("original"), uniqueValue);
@ -112,11 +112,11 @@ function test() {
"subframes must match URL to get text restored");
is(win.frames[1].document.getElementById("original").value, uniqueValue,
"text still gets restored for all other subframes");
// clean up
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab);
finish();
}, true);
}, true);

Просмотреть файл

@ -11,7 +11,7 @@
<script type="application/javascript">
frames[2].addEventListener("DOMContentLoaded", function() {
frames[2].removeEventListener("DOMContentLoaded", arguments.callee, false);
if (frames[2].document.location.href == "data:text/html,mark1") {
frames[2].document.location = "data:text/html,mark2";
}

Просмотреть файл

@ -4,12 +4,12 @@
function test() {
/** Test for Bug 463206 **/
waitForExplicitFinish();
let testURL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_463206_sample.html";
var frameCount = 0;
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
@ -17,20 +17,20 @@ function test() {
if (frameCount++ < 5)
return;
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
function typeText(aTextField, aValue) {
aTextField.value = aValue;
let event = aTextField.ownerDocument.createEvent("UIEvents");
event.initUIEvent("input", true, true, aTextField.ownerDocument.defaultView, 0);
aTextField.dispatchEvent(event);
}
let doc = tab.linkedBrowser.contentDocument;
typeText(doc.getElementById("out1"), Date.now());
typeText(doc.getElementsByName("1|#out2")[0], Math.random());
typeText(doc.defaultView.frames[0].frames[1].document.getElementById("in1"), new Date());
frameCount = 0;
let tab2 = gBrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
@ -53,11 +53,11 @@ function test() {
// "id prefixes aren't mixed up");
is(win.frames[1].frames[0].document.getElementById("in1").value, "",
"id prefixes aren't mixed up");
// clean up
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab);
finish();
}, true);
}, true);

Просмотреть файл

@ -46,7 +46,7 @@ function test() {
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo",
test_state.windows[0]._closedTabs.length);
ss.setWindowState(newWin, JSON.stringify(test_state), true);
let closedTabs = JSON.parse(ss.getClosedTabData(newWin));
is(closedTabs.length, test_state.windows[0]._closedTabs.length,
"Closed tab list has the expected length");

Просмотреть файл

@ -9,7 +9,7 @@
var targetUrl = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_464620_xd.html";
var firstPass;
function setup() {
if (firstPass !== undefined)
return;
@ -19,7 +19,7 @@
}
frames[1].location = targetUrl;
}
function step() {
var x = frames[0].document.getElementById("x");
if (x.value == "")
@ -27,15 +27,15 @@
x.style.display = "none";
frames[0].document.designMode = "on";
}
function xss() {
step();
var documentInjected = false;
document.getElementsByTagName("iframe")[0].onload =
function() { documentInjected = true; };
frames[0].location = targetUrl;
for (var c = 0; !documentInjected && c < 20; c++) {
var r = new XMLHttpRequest();
r.open("GET", location.href, false);
@ -43,7 +43,7 @@
r.send(null);
}
document.getElementById("state").textContent = "done";
var event = document.createEvent("MessageEvent");
event.initMessageEvent("464620_a", true, false, "done", location.href, "", window);
document.dispatchEvent(event);

Просмотреть файл

@ -4,12 +4,12 @@
function test() {
/** Test for Bug 464620 (injection on input) **/
waitForExplicitFinish();
let testURL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_464620_a.html";
var frameCount = 0;
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
@ -17,14 +17,14 @@ function test() {
if (frameCount++ < 4)
return;
this.removeEventListener("load", arguments.callee, true);
executeSoon(function() {
frameCount = 0;
let tab2 = gBrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("464620_a", function(aEvent) {
tab2.linkedBrowser.removeEventListener("464620_a", arguments.callee, true);
is(aEvent.data, "done", "XSS injection was attempted");
// let form restoration complete and take into account the
// setTimeout(..., 0) in sss_restoreDocument_proxy
executeSoon(function() {
@ -34,11 +34,11 @@ function test() {
"cross domain document was loaded");
ok(!/XXX/.test(win.frames[0].document.body.innerHTML),
"no content was injected");
// clean up
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab);
finish();
}, 0);
});

Просмотреть файл

@ -10,7 +10,7 @@
var targetUrl = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_464620_xd.html";
var firstPass;
function setup() {
if (firstPass !== undefined)
return;
@ -21,25 +21,25 @@
}
frames[2].location = targetUrl;
}
function step() {
frames[0].document.designMode = "on";
if (firstPass)
return;
var body = frames[0].document.body;
body.addEventListener("DOMNodeInserted", function() {
body.removeEventListener("DOMNodeInserted", arguments.callee, true);
xss();
}, true);
}
function xss() {
var documentInjected = false;
document.getElementsByTagName("iframe")[1].onload =
function() { documentInjected = true; };
frames[1].location = targetUrl;
for (var c = 0; !documentInjected && c < 20; c++) {
var r = new XMLHttpRequest();
r.open("GET", location.href, false);
@ -47,7 +47,7 @@
r.send(null);
}
document.getElementById("state").textContent = "done";
var event = document.createEvent("MessageEvent");
event.initMessageEvent("464620_b", true, false, "done", location.href, "", window);
document.dispatchEvent(event);

Просмотреть файл

@ -4,12 +4,12 @@
function test() {
/** Test for Bug 464620 (injection on DOM node insertion) **/
waitForExplicitFinish();
let testURL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_464620_b.html";
var frameCount = 0;
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
@ -17,14 +17,14 @@ function test() {
if (frameCount++ < 6)
return;
this.removeEventListener("load", arguments.callee, true);
executeSoon(function() {
frameCount = 0;
let tab2 = gBrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("464620_b", function(aEvent) {
tab2.linkedBrowser.removeEventListener("464620_b", arguments.callee, true);
is(aEvent.data, "done", "XSS injection was attempted");
// let form restoration complete and take into account the
// setTimeout(..., 0) in sss_restoreDocument_proxy
executeSoon(function() {
@ -34,11 +34,11 @@ function test() {
"cross domain document was loaded");
ok(!/XXX/.test(win.frames[1].document.body.innerHTML),
"no content was injected");
// clean up
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab);
finish();
}, 0);
});

Просмотреть файл

@ -4,32 +4,32 @@
function test() {
/** Test for Bug 465215 **/
waitForExplicitFinish();
let uniqueName = "bug 465215";
let uniqueValue1 = "as good as unique: " + Date.now();
let uniqueValue2 = "as good as unique: " + Math.random();
// set a unique value on a new, blank tab
let tab1 = gBrowser.addTab();
tab1.linkedBrowser.addEventListener("load", function() {
tab1.linkedBrowser.removeEventListener("load", arguments.callee, true);
ss.setTabValue(tab1, uniqueName, uniqueValue1);
// duplicate the tab with that value
let tab2 = ss.duplicateTab(window, tab1);
is(ss.getTabValue(tab2, uniqueName), uniqueValue1, "tab value was duplicated");
ss.setTabValue(tab2, uniqueName, uniqueValue2);
isnot(ss.getTabValue(tab1, uniqueName), uniqueValue2, "tab values aren't sync'd");
// overwrite the tab with the value which should remove it
ss.setTabState(tab1, JSON.stringify({ entries: [] }));
tab1.linkedBrowser.addEventListener("load", function() {
tab1.linkedBrowser.removeEventListener("load", arguments.callee, true);
is(ss.getTabValue(tab1, uniqueName), "", "tab value was cleared");
// clean up
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab1);

Просмотреть файл

@ -4,7 +4,7 @@
function test() {
/** Test for Bug 466937 **/
waitForExplicitFinish();
var file = Components.classes["@mozilla.org/file/directory_service;1"]
@ -13,17 +13,17 @@ function test() {
file.append("466937_test.file");
file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
let testPath = file.path;
let testURL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_466937_sample.html";
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
let doc = tab.linkedBrowser.contentDocument;
doc.getElementById("reverse_thief").value = "/home/user/secret2";
doc.getElementById("bystander").value = testPath;
let tab2 = gBrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
@ -34,11 +34,11 @@ function test() {
"text field value wasn't set to full file path");
is(doc.getElementById("bystander").value, testPath,
"normal case: file path was correctly preserved");
// clean up
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab);
finish();
}, true);
}, true);

Просмотреть файл

@ -5,7 +5,7 @@
function test() {
/** Test for Bug 477657 **/
waitForExplicitFinish();
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
newWin.removeEventListener("load", arguments.callee, false);

Просмотреть файл

@ -4,11 +4,11 @@
function test() {
/** Test for Bug 485482 **/
waitForExplicitFinish();
let uniqueValue = Math.random();
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_485482_sample.html";
let tab = gBrowser.addTab(testURL);
@ -17,7 +17,7 @@ function test() {
let doc = tab.linkedBrowser.contentDocument;
doc.querySelector("input[type=text]").value = uniqueValue;
doc.querySelector("input[type=checkbox]").checked = true;
let tab2 = gBrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
@ -26,7 +26,7 @@ function test() {
"generated XPath expression was valid");
ok(doc.querySelector("input[type=checkbox]").checked,
"generated XPath expression was valid");
// clean up
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab);

Просмотреть файл

@ -4,11 +4,11 @@
function test() {
/** Test for Bug 485563 **/
waitForExplicitFinish();
let uniqueValue = Math.random() + "\u2028Second line\u2029Second paragraph\u2027";
let tab = gBrowser.addTab();
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
@ -20,7 +20,7 @@ function test() {
ss.setTabState(tab, JSON.stringify(tabState));
is(ss.getTabValue(tab, "bug485563"), uniqueValue,
"unicode line separator was correctly preserved");
gBrowser.removeTab(tab);
finish();
}, true);

Просмотреть файл

@ -4,10 +4,10 @@
function test() {
/** Test for Bug 491577 **/
// test setup
waitForExplicitFinish();
const REMEMBER = Date.now(), FORGET = Math.random();
let test_state = {
windows: [ { tabs: [{ entries: [{ url: "http://example.com/" }] }], selected: 1 } ],
@ -64,10 +64,10 @@ function test() {
]
};
let remember_count = 1;
function countByTitle(aClosedWindowList, aTitle)
aClosedWindowList.filter(function(aData) aData.title == aTitle).length;
function testForError(aFunction) {
try {
aFunction();
@ -77,7 +77,7 @@ function test() {
return ex.name == "NS_ERROR_ILLEGAL_VALUE";
}
}
// open a window and add the above closed window list
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
@ -85,7 +85,7 @@ function test() {
gPrefService.setIntPref("browser.sessionstore.max_windows_undo",
test_state._closedWindows.length);
ss.setWindowState(newWin, JSON.stringify(test_state), true);
let closedWindows = JSON.parse(ss.getClosedWindowData());
is(closedWindows.length, test_state._closedWindows.length,
"Closed window list has the expected length");
@ -94,17 +94,17 @@ function test() {
"The correct amount of windows are to be forgotten");
is(countByTitle(closedWindows, REMEMBER), remember_count,
"Everything is set up.");
// all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
ok(testForError(function() ss.forgetClosedWindow(-1)),
"Invalid window for forgetClosedWindow throws");
ok(testForError(function() ss.forgetClosedWindow(test_state._closedWindows.length + 1)),
"Invalid window for forgetClosedWindow throws");
// Remove third window, then first window
ss.forgetClosedWindow(2);
ss.forgetClosedWindow(null);
closedWindows = JSON.parse(ss.getClosedWindowData());
is(closedWindows.length, remember_count,
"The correct amount of windows were removed");

Просмотреть файл

@ -9,7 +9,7 @@ function test() {
tab.linkedBrowser.stop();
let tabState = JSON.parse(ss.getTabState(tab));
is(tabState.disallow || "", "", "Everything is allowed per default");
// collect all permissions that can be set on a docShell (i.e. all
// attributes starting with "allow" such as "allowJavascript") and
// disallow them all, as SessionStore only remembers disallowed ones
@ -21,7 +21,7 @@ function test() {
docShell[attribute] = false;
}
}
// make sure that all available permissions have been remembered
tabState = JSON.parse(ss.getTabState(tab));
let disallow = tabState.disallow.split(",");
@ -30,6 +30,6 @@ function test() {
});
// IF A TEST FAILS, please add the missing permission's name (without the
// leading "allow") to nsSessionStore.js's CAPABILITIES array. Thanks.
gBrowser.removeTab(tab);
}

Просмотреть файл

@ -4,7 +4,7 @@
function test() {
/** Test for Bug 495495 **/
waitForExplicitFinish();
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no,toolbar=yes");

Просмотреть файл

@ -25,7 +25,7 @@ function test() {
return -1;
}
// delete existing sessionstore.js, to make sure we're not reading
// delete existing sessionstore.js, to make sure we're not reading
// the mtime of an old one initialy
let (sessionStoreJS = getSessionstoreFile()) {
if (sessionStoreJS.exists())
@ -62,7 +62,7 @@ function test() {
tab.linkedBrowser.contentWindow.scrollTo(1100, 1200);
setTimeout(function step2(e) {
let mtime2 = getSessionstorejsModificationTime();
is(mtime2, mtime1,
is(mtime2, mtime1,
"tab selection and scrolling: sessionstore.js not updated");
// ok, done, cleanup and finish

Просмотреть файл

@ -4,7 +4,7 @@
function test() {
/** Test for Bug 526613 **/
// test setup
waitForExplicitFinish();

Просмотреть файл

@ -12,7 +12,7 @@ function test() {
};
let pageData = {
url: "about:sessionrestore",
formdata: { id: { "sessionData": oldState } }
formdata: { id: { "sessionData": oldState } }
};
let state = { windows: [{ tabs: [{ entries: [pageData] }] }] };

Просмотреть файл

@ -36,7 +36,7 @@ function test() {
}
function windowObserver(aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
if (aTopic == "domwindowopened") {
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);

Просмотреть файл

@ -0,0 +1,47 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// This test ensures that attempts made to save/restore ("duplicate") pages
// using designmode AND make changes to document structure (remove body)
// don't result in uncaught errors and a broken browser state.
function test() {
waitForExplicitFinish();
let testURL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_739531_sample.html";
let loadCount = 0;
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function onLoad(aEvent) {
// make sure both the page and the frame are loaded
if (++loadCount < 2)
return;
tab.linkedBrowser.removeEventListener("load", onLoad, true);
// executeSoon to allow the JS to execute on the page
executeSoon(function() {
let tab2;
let caughtError = false;
try {
tab2 = ss.duplicateTab(window, tab);
}
catch (e) {
caughtError = true;
info(e);
}
is(gBrowser.tabs.length, 3, "there should be 3 tabs")
ok(!caughtError, "duplicateTab didn't throw");
// if the test fails, we don't want to try to close a tab that doesn't exist
if (tab2)
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab);
finish();
});
}, true);
}

Просмотреть файл

@ -0,0 +1,24 @@
<!-- originally a crash test for bug 713417
https://bug713417.bugzilla.mozilla.org/attachment.cgi?id=584240 -->
<!DOCTYPE html>
<html>
<head>
<script>
function boom()
{
var w = document.getElementById("f").contentWindow;
var d = w.document;
d.designMode = 'on';
var r = d.documentElement;
d.removeChild(r);
document.adoptNode(r);
}
</script>
</head>
<body onload="boom();">
<iframe src="data:text/html,1" id="f"></iframe>
</body>
</html>

Просмотреть файл

@ -9,6 +9,7 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const FRAME_STEP_CACHE_DURATION = 100; // ms
const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
Cu.import("resource:///modules/source-editor.jsm");
@ -341,6 +342,7 @@ function StackFrames() {
this._onResume = this._onResume.bind(this);
this._onFrames = this._onFrames.bind(this);
this._onFramesCleared = this._onFramesCleared.bind(this);
this._afterFramesCleared = this._afterFramesCleared.bind(this);
}
StackFrames.prototype = {
@ -369,6 +371,8 @@ StackFrames.prototype = {
* The next function in the initialization sequence.
*/
connect: function SF_connect(aCallback) {
window.addEventListener("Debugger:FetchedVariables", this._onFetchedVars, false);
this.activeThread.addListener("paused", this._onPaused);
this.activeThread.addListener("resumed", this._onResume);
this.activeThread.addListener("framesadded", this._onFrames);
@ -383,6 +387,8 @@ StackFrames.prototype = {
* Disconnect from the client.
*/
disconnect: function SF_disconnect() {
window.removeEventListener("Debugger:FetchedVariables", this._onFetchedVars, false);
if (!this.activeThread) {
return;
}
@ -431,12 +437,24 @@ StackFrames.prototype = {
* Handler for the thread client's framescleared notification.
*/
_onFramesCleared: function SF__onFramesCleared() {
DebuggerView.StackFrames.emptyText();
// After each frame step (in, over, out), framescleared is fired, which
// forces the UI to be emptied and rebuilt on framesadded. Most of the times
// this is not necessary, and will result in a brief redraw flicker.
// To avoid it, invalidate the UI only after a short time if necessary.
window.setTimeout(this._afterFramesCleared, FRAME_STEP_CACHE_DURATION);
this.selectedFrame = null;
},
// Clear the properties as well.
DebuggerView.Properties.localScope.empty();
DebuggerView.Properties.globalScope.empty();
/**
* Called soon after the thread client's framescleared notification.
*/
_afterFramesCleared: function SF__afterFramesCleared() {
if (!this.activeThread.cachedFrames.length) {
DebuggerView.StackFrames.emptyText();
DebuggerView.Properties.localScope.empty();
DebuggerView.Properties.globalScope.empty();
DebuggerController.dispatchEvent("Debugger:AfterFramesCleared");
}
},
/**
@ -500,6 +518,9 @@ StackFrames.prototype = {
editor.setDebugLocation(-1);
}
// Start recording any added variables or properties in any scope.
DebuggerView.Properties.createHierarchyStore();
// Display the local variables.
let localScope = DebuggerView.Properties.localScope;
localScope.empty();
@ -539,6 +560,13 @@ StackFrames.prototype = {
DebuggerController.dispatchEvent("Debugger:FetchedVariables");
},
/**
* Called afters variables have been fetched after a frame was selected.
*/
_onFetchedVars: function SF__onFetchedVars() {
DebuggerView.Properties.commitHierarchy();
},
/**
* Adds an 'onexpand' callback for a variable, lazily handling the addition of
* new properties.
@ -631,6 +659,18 @@ StackFrames.prototype = {
return aFrame["calleeName"] ? aFrame["calleeName"] : "(anonymous)";
}
return "(" + aFrame.type + ")";
},
/**
* Evaluate an expression in the context of the selected frame. This is used
* for modifying the value of variables in scope.
*
* @param string aExpression
* The expression to evaluate.
*/
evaluate: function SF_evaluate(aExpression) {
let frame = this.activeThread.cachedFrames[this.selectedFrame];
this.activeThread.eval(frame.actor, aExpression);
}
};
@ -709,6 +749,11 @@ SourceScripts.prototype = {
* Handler for the debugger client's unsolicited newScript notification.
*/
_onNewScript: function SS__onNewScript(aNotification, aPacket) {
// Ignore scripts generated from 'clientEvaluate' packets.
if (aPacket.url == "debugger eval code") {
return;
}
this._addScript({ url: aPacket.url, startLine: aPacket.startLine }, true);
},

Просмотреть файл

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const PROPERTY_VIEW_FLASH_DURATION = 400; // ms
/**
* Object mediating visual changes and event listeners between the debugger and
* the html view.
@ -482,12 +484,12 @@ StackFramesView.prototype = {
// If we're paused, show a pause label and a resume label on the button.
if (aState == "paused") {
resume.label = L10N.getStr("resumeLabel");
resume.setAttribute("tooltiptext", L10N.getStr("resumeTooltip"));
resume.setAttribute("checked", true);
}
// If we're attached, do the opposite.
else if (aState == "attached") {
resume.label = L10N.getStr("pauseLabel");
resume.setAttribute("tooltiptext", L10N.getStr("pauseTooltip"));
resume.removeAttribute("checked");
}
@ -511,11 +513,11 @@ StackFramesView.prototype = {
// Make sure the container is empty first.
this.empty();
let item = document.createElement("div");
let item = document.createElement("label");
// The empty node should look grayed out to avoid confusion.
item.className = "empty list-item";
item.appendChild(document.createTextNode(L10N.getStr("emptyStackText")));
item.className = "list-item empty";
item.setAttribute("value", L10N.getStr("emptyStackText"));
this._frames.appendChild(item);
},
@ -540,21 +542,25 @@ StackFramesView.prototype = {
return null;
}
let frame = document.createElement("div");
let frameName = document.createElement("span");
let frameDetails = document.createElement("span");
let frame = document.createElement("box");
let frameName = document.createElement("label");
let frameDetails = document.createElement("label");
// Create a list item to be added to the stackframes container.
frame.id = "stackframe-" + aDepth;
frame.className = "dbg-stackframe list-item";
// This list should display the name and details for the frame.
frameName.className = "dbg-stackframe-name";
frameDetails.className = "dbg-stackframe-details";
frameName.appendChild(document.createTextNode(aFrameNameText));
frameDetails.appendChild(document.createTextNode(aFrameDetailsText));
frameName.className = "dbg-stackframe-name plain";
frameDetails.className = "dbg-stackframe-details plain";
frameName.setAttribute("value", aFrameNameText);
frameDetails.setAttribute("value", aFrameDetailsText);
let spacer = document.createElement("spacer");
spacer.setAttribute("flex", "1");
frame.appendChild(frameName);
frame.appendChild(spacer);
frame.appendChild(frameDetails);
this._frames.appendChild(frame);
@ -825,7 +831,7 @@ PropertiesView.prototype = {
// Contains generic nodes and functionality.
let element = this._createPropertyElement(aName, aId, "variable",
aScope.querySelector(".details"));
aScope.getElementsByClassName("details")[0]);
// Make sure the element was created successfully.
if (!element) {
@ -844,21 +850,41 @@ PropertiesView.prototype = {
// Setup the additional elements specific for a variable node.
element.refresh(function() {
let separator = document.createElement("span");
let info = document.createElement("span");
let title = element.querySelector(".title");
let arrow = element.querySelector(".arrow");
let separatorLabel = document.createElement("label");
let valueLabel = document.createElement("label");
let title = element.getElementsByClassName("title")[0];
// Separator shouldn't be selectable.
separator.className = "unselectable";
separator.appendChild(document.createTextNode(": "));
// Separator between the variable name and its value.
separatorLabel.className = "plain";
separatorLabel.setAttribute("value", ":");
// The variable information (type, class and/or value).
info.className = "info";
valueLabel.className = "value plain";
title.appendChild(separator);
title.appendChild(info);
// Handle the click event when pressing the element value label.
valueLabel.addEventListener("click", this._activateElementInputMode.bind({
scope: this,
element: element,
valueLabel: valueLabel
}));
// Maintain the symbolic name of the variable.
Object.defineProperty(element, "token", {
value: aName,
writable: false,
enumerable: true,
configurable: true
});
title.appendChild(separatorLabel);
title.appendChild(valueLabel);
// Remember a simple hierarchy between the parent and the element.
this._saveHierarchy({
parent: aScope,
element: element,
valueLabel: valueLabel
});
}.bind(this));
// Return the element for later use if necessary.
@ -898,19 +924,37 @@ PropertiesView.prototype = {
aGrip = { type: "null" };
}
let info = aVar.querySelector(".info") || aVar.target.info;
let valueLabel = aVar.getElementsByClassName("value")[0];
// Make sure the info node exists.
if (!info) {
// Make sure the value node exists.
if (!valueLabel) {
return null;
}
info.textContent = this._propertyString(aGrip);
info.classList.add(this._propertyColor(aGrip));
this._applyGrip(valueLabel, aGrip);
return aVar;
},
/**
* Applies the necessary text content and class name to a value node based
* on a grip.
*
* @param object aValueLabel
* The value node to apply the changes to.
* @param object aGrip
* @see DebuggerView.Properties._setGrip
*/
_applyGrip: function DVP__applyGrip(aValueLabel, aGrip) {
let prevGrip = aValueLabel.currentGrip;
if (prevGrip) {
aValueLabel.classList.remove(this._propertyColor(prevGrip));
}
aValueLabel.setAttribute("value", this._propertyString(aGrip));
aValueLabel.classList.add(this._propertyColor(aGrip));
aValueLabel.currentGrip = aGrip;
},
/**
* Adds multiple properties to a specified variable.
* This function handles two types of properties: data properties and
@ -1000,7 +1044,7 @@ PropertiesView.prototype = {
// Contains generic nodes and functionality.
let element = this._createPropertyElement(aName, aId, "property",
aVar.querySelector(".details"));
aVar.getElementsByClassName("details")[0]);
// Make sure the element was created successfully.
if (!element) {
@ -1019,40 +1063,51 @@ PropertiesView.prototype = {
// Setup the additional elements specific for a variable node.
element.refresh(function(pKey, pGrip) {
let propertyString = this._propertyString(pGrip);
let propertyColor = this._propertyColor(pGrip);
let key = document.createElement("div");
let value = document.createElement("div");
let separator = document.createElement("span");
let title = element.querySelector(".title");
let arrow = element.querySelector(".arrow");
// Use a key element to specify the property name.
key.className = "key";
key.appendChild(document.createTextNode(pKey));
// Use a value element to specify the property value.
value.className = "value";
value.appendChild(document.createTextNode(propertyString));
value.classList.add(propertyColor);
// Separator shouldn't be selected.
separator.className = "unselectable";
separator.appendChild(document.createTextNode(": "));
let title = element.getElementsByClassName("title")[0];
let nameLabel = title.getElementsByClassName("name")[0];
let separatorLabel = document.createElement("label");
let valueLabel = document.createElement("label");
if ("undefined" !== typeof pKey) {
title.appendChild(key);
// Use a key element to specify the property name.
nameLabel.className = "key plain";
nameLabel.setAttribute("value", pKey.trim());
title.appendChild(nameLabel);
}
if ("undefined" !== typeof pGrip) {
title.appendChild(separator);
title.appendChild(value);
// Separator between the variable name and its value.
separatorLabel.className = "plain";
separatorLabel.setAttribute("value", ":");
// Use a value element to specify the property value.
valueLabel.className = "value plain";
this._applyGrip(valueLabel, pGrip);
title.appendChild(separatorLabel);
title.appendChild(valueLabel);
}
// Make the property also behave as a variable, to allow
// recursively adding properties to properties.
element.target = {
info: value
};
// Handle the click event when pressing the element value label.
valueLabel.addEventListener("click", this._activateElementInputMode.bind({
scope: this,
element: element,
valueLabel: valueLabel
}));
// Maintain the symbolic name of the property.
Object.defineProperty(element, "token", {
value: aVar.token + "['" + pKey + "']",
writable: false,
enumerable: true,
configurable: true
});
// Remember a simple hierarchy between the parent and the element.
this._saveHierarchy({
parent: aVar,
element: element,
valueLabel: valueLabel
});
// Save the property to the variable for easier access.
Object.defineProperty(aVar, pKey, { value: element,
@ -1065,6 +1120,109 @@ PropertiesView.prototype = {
return element;
},
/**
* Makes an element's (variable or priperty) value editable.
* Make sure 'this' is bound to an object containing the properties:
* {
* "scope": the original scope to be used, probably DebuggerView.Properties,
* "element": the element whose value should be made editable,
* "valueLabel": the label displaying the value
* }
*
* @param event aEvent [optional]
* The event requesting this action.
*/
_activateElementInputMode: function DVP__activateElementInputMode(aEvent) {
if (aEvent) {
aEvent.stopPropagation();
}
let self = this.scope;
let element = this.element;
let valueLabel = this.valueLabel;
let titleNode = valueLabel.parentNode;
let initialValue = valueLabel.getAttribute("value");
// When editing an object we need to collapse it first, in order to avoid
// displaying an inconsistent state while the user is editing.
element._previouslyExpanded = element.expanded;
element._preventExpand = true;
element.collapse();
element.forceHideArrow();
// Create a texbox input element which will be shown in the current
// element's value location.
let textbox = document.createElement("textbox");
textbox.setAttribute("value", initialValue);
textbox.className = "element-input";
textbox.width = valueLabel.clientWidth + 1;
// Save the new value when the texbox looses focus or ENTER is pressed.
function DVP_element_textbox_blur(aTextboxEvent) {
DVP_element_textbox_save();
}
function DVP_element_textbox_keyup(aTextboxEvent) {
if (aTextboxEvent.keyCode === aTextboxEvent.DOM_VK_LEFT ||
aTextboxEvent.keyCode === aTextboxEvent.DOM_VK_RIGHT ||
aTextboxEvent.keyCode === aTextboxEvent.DOM_VK_UP ||
aTextboxEvent.keyCode === aTextboxEvent.DOM_VK_DOWN) {
return;
}
if (aTextboxEvent.keyCode === aTextboxEvent.DOM_VK_RETURN ||
aTextboxEvent.keyCode === aTextboxEvent.DOM_VK_ENTER) {
DVP_element_textbox_save();
return;
}
if (aTextboxEvent.keyCode === aTextboxEvent.DOM_VK_ESCAPE) {
valueLabel.setAttribute("value", initialValue);
DVP_element_textbox_clear();
return;
}
}
// The actual save mechanism for the new variable/property value.
function DVP_element_textbox_save() {
if (textbox.value !== valueLabel.getAttribute("value")) {
valueLabel.setAttribute("value", textbox.value);
let expr = "(" + element.token + "=" + textbox.value + ")";
DebuggerController.StackFrames.evaluate(expr);
}
DVP_element_textbox_clear();
}
// Removes the event listeners and appends the value node again.
function DVP_element_textbox_clear() {
element._preventExpand = false;
if (element._previouslyExpanded) {
element._previouslyExpanded = false;
element.expand();
}
element.showArrow();
textbox.removeEventListener("blur", DVP_element_textbox_blur, false);
textbox.removeEventListener("keyup", DVP_element_textbox_keyup, false);
titleNode.removeChild(textbox);
titleNode.appendChild(valueLabel);
}
textbox.addEventListener("blur", DVP_element_textbox_blur, false);
textbox.addEventListener("keyup", DVP_element_textbox_keyup, false);
titleNode.removeChild(valueLabel);
titleNode.appendChild(textbox);
textbox.select();
// When the value is a string (displayed as "value"), then we probably want
// to change it to another string in the textbox, so to avoid typing the ""
// again, tackle with the selection bounds just a bit.
if (valueLabel.getAttribute("value").match(/^"[^"]*"$/)) {
textbox.selectionEnd--;
textbox.selectionStart++;
}
},
/**
* Returns a custom formatted property string for a type and a value.
*
@ -1129,6 +1287,8 @@ PropertiesView.prototype = {
/**
* Creates an element which contains generic nodes and functionality used by
* any scope, variable or property added to the tree.
* If the variable or property already exists, null is returned.
* Otherwise, the newly created element is returned.
*
* @param string aName
* A generic name used in a title strip.
@ -1150,11 +1310,12 @@ PropertiesView.prototype = {
return null;
}
let element = document.createElement("div");
let arrow = document.createElement("span");
let name = document.createElement("span");
let title = document.createElement("div");
let details = document.createElement("div");
let element = document.createElement("vbox");
let arrow = document.createElement("box");
let name = document.createElement("label");
let title = document.createElement("box");
let details = document.createElement("vbox");
// Create a scope node to contain all the elements.
element.id = aId;
@ -1165,16 +1326,24 @@ PropertiesView.prototype = {
arrow.style.visibility = "hidden";
// The name element.
name.className = "name unselectable";
name.appendChild(document.createTextNode(aName || ""));
name.className = "name plain";
name.setAttribute("value", aName || "");
// The title element, containing the arrow and the name.
title.className = "title";
title.addEventListener("click", function() { element.toggle(); }, true);
title.setAttribute("align", "center")
// The node element which will contain any added scope variables.
details.className = "details";
// Add the click event handler for the title, or arrow and name.
if (aClass === "scope") {
title.addEventListener("click", function() { element.toggle(); }, false);
} else {
arrow.addEventListener("click", function() { element.toggle(); }, false);
name.addEventListener("click", function() { element.toggle(); }, false);
}
title.appendChild(arrow);
title.appendChild(name);
@ -1217,6 +1386,9 @@ PropertiesView.prototype = {
* The same element.
*/
element.expand = function DVP_element_expand() {
if (element._preventExpand) {
return;
}
arrow.setAttribute("open", "");
details.setAttribute("open", "");
@ -1232,6 +1404,9 @@ PropertiesView.prototype = {
* The same element.
*/
element.collapse = function DVP_element_collapse() {
if (element._preventCollapse) {
return;
}
arrow.removeAttribute("open");
details.removeAttribute("open");
@ -1261,39 +1436,49 @@ PropertiesView.prototype = {
* The same element.
*/
element.showArrow = function DVP_element_showArrow() {
if (details.childNodes.length) {
if (element._forceShowArrow || details.childNodes.length) {
arrow.style.visibility = "visible";
}
return element;
};
/**
* Forces the element expand/collapse arrow to be visible, even if there
* are no child elements.
*
* @param boolean aPreventHideFlag
* Prevents the arrow to be hidden when requested.
* @return object
* The same element.
*/
element.forceShowArrow = function DVP_element_forceShowArrow(aPreventHideFlag) {
element._preventHide = aPreventHideFlag;
arrow.style.visibility = "visible";
return element;
};
/**
* Hides the element expand/collapse arrow.
* @return object
* The same element.
*/
element.hideArrow = function DVP_element_hideArrow() {
if (!element._preventHide) {
if (!element._forceShowArrow) {
arrow.style.visibility = "hidden";
}
return element;
};
/**
* Forces the element expand/collapse arrow to be visible, even if there
* are no child elements.
*
* @return object
* The same element.
*/
element.forceShowArrow = function DVP_element_forceShowArrow() {
element._forceShowArrow = true;
arrow.style.visibility = "visible";
return element;
};
/**
* Forces the element expand/collapse arrow to be hidden, even if there
* are some child elements.
*
* @return object
* The same element.
*/
element.forceHideArrow = function DVP_element_forceHideArrow() {
arrow.style.visibility = "hidden";
return element;
};
/**
* Returns if the element is visible.
* @return boolean
@ -1336,7 +1521,7 @@ PropertiesView.prototype = {
* The same element.
*/
element.empty = function DVP_element_empty() {
// this details node won't have any elements, so hide the arrow
// This details node won't have any elements, so hide the arrow.
arrow.style.visibility = "hidden";
while (details.firstChild) {
details.removeChild(details.firstChild);
@ -1362,6 +1547,24 @@ PropertiesView.prototype = {
return element;
};
/**
* Returns if the element expander (arrow) is visible.
* @return boolean
* True if the arrow is visible.
*/
Object.defineProperty(element, "arrowVisible", {
get: function DVP_element_getArrowVisible() {
return arrow.style.visibility !== "hidden";
},
set: function DVP_element_setExpanded(value) {
if (value) {
element.showArrow();
} else {
element.hideArrow();
}
}
});
/**
* Generic function refreshing the internal state of the element when
* it's modified (e.g. a child detail, variable, property is added).
@ -1377,8 +1580,8 @@ PropertiesView.prototype = {
}
let node = aParent.parentNode;
let arrow = node.querySelector(".arrow");
let children = node.querySelector(".details").childNodes.length;
let arrow = node.getElementsByClassName("arrow")[0];
let children = node.getElementsByClassName("details")[0].childNodes.length;
// If the parent details node has at least one element, set the
// expand/collapse arrow visible.
@ -1393,6 +1596,95 @@ PropertiesView.prototype = {
return element;
},
/**
* Remember a simple hierarchy of parent->element->children.
*
* @param object aProperties
* Container for the parent, element and the associated value node.
*/
_saveHierarchy: function DVP__saveHierarchy(aProperties) {
let parent = aProperties.parent;
let element = aProperties.element;
let valueLabel = aProperties.valueLabel;
let store = aProperties.store || parent._children;
// Make sure we have a valid element and a children storage object.
if (!element || !store) {
return;
}
let relation = {
root: parent ? (parent._root || parent) : null,
parent: parent || null,
element: element,
valueLabel: valueLabel,
children: {}
};
store[element.id] = relation;
element._root = relation.root;
element._children = relation.children;
},
/**
* Creates an object to store a hierarchy of scopes, variables and properties
* and saves the previous store.
*/
createHierarchyStore: function DVP_createHierarchyStore() {
this._prevHierarchy = this._currHierarchy;
this._currHierarchy = {};
this._saveHierarchy({ element: this._globalScope, store: this._currHierarchy });
this._saveHierarchy({ element: this._localScope, store: this._currHierarchy });
this._saveHierarchy({ element: this._withScope, store: this._currHierarchy });
this._saveHierarchy({ element: this._closureScope, store: this._currHierarchy });
},
/**
* Briefly flash the variables that changed between pauses.
*/
commitHierarchy: function DVS_commitHierarchy() {
for (let i in this._currHierarchy) {
let currScope = this._currHierarchy[i];
let prevScope = this._prevHierarchy[i];
for (let v in currScope.children) {
let currVar = currScope.children[v];
let prevVar = prevScope.children[v];
let action = "";
if (prevVar) {
let prevValue = prevVar.valueLabel.getAttribute("value");
let currValue = currVar.valueLabel.getAttribute("value");
if (currValue != prevValue) {
action = "changed";
} else {
action = "unchanged";
}
} else {
action = "added";
}
if (action) {
currVar.element.setAttribute(action, "");
window.setTimeout(function() {
currVar.element.removeAttribute(action);
}, PROPERTY_VIEW_FLASH_DURATION);
}
}
}
},
/**
* A simple model representation of all the scopes, variables and properties,
* with parent-child relations.
*/
_currHierarchy: null,
_prevHierarchy: null,
/**
* Returns the global scope container.
*/
@ -1494,6 +1786,8 @@ PropertiesView.prototype = {
* Initialization function, called when the debugger is initialized.
*/
initialize: function DVP_initialize() {
this.createHierarchyStore();
this._vars = document.getElementById("variables");
this._localScope = this._addScope(L10N.getStr("localScope")).expand();
this._withScope = this._addScope(L10N.getStr("withScope")).hide();
@ -1505,6 +1799,8 @@ PropertiesView.prototype = {
* Destruction function, called when the debugger is shut down.
*/
destroy: function DVP_destroy() {
this._currHierarchy = null;
this._prevHierarchy = null;
this._vars = null;
this._globalScope = null;
this._localScope = null;

Просмотреть файл

@ -4,133 +4,38 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Debugger content
*/
.dbg-default {
cursor: default;
}
/**
* Stack frames
*/
#stack {
width: 200px;
}
#stackframes {
overflow: auto;
}
.dbg-stackframe {
display: block;
}
.dbg-stackframe-name {
float: left;
}
.dbg-stackframe-details {
float: right;
}
.dbg-stackframe-name:-moz-locale-dir(rtl) {
float: right;
}
.dbg-stackframe-details:-moz-locale-dir(rtl) {
float: left;
}
/**
* Properties elements
*/
#properties {
width: 250px;
}
#variables {
overflow: auto;
}
/**
* Scope element
* Scope, variable and property elements
*/
.scope {
display: -moz-box;
-moz-box-orient: vertical;
}
.scope > .details {
.details {
display: none;
}
.scope > .details[open] {
.details[open] {
display: -moz-box;
-moz-box-orient: vertical;
}
/**
* Variable element
* Toolbar
*/
.variable {
display: -moz-box;
-moz-box-orient: vertical;
}
.variable > .title {
white-space: nowrap;
}
.variable > .details {
.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
display: none;
}
.variable > .details[open] {
display: -moz-box;
-moz-box-orient: vertical;
}
/**
* Property element
*/
.property {
display: -moz-box;
-moz-box-orient: vertical;
}
.property > .title {
white-space: nowrap;
}
.property > .title > .key {
display: inline-block;
}
.property > .title > .value {
display: inline-block;
}
.property > .details {
display: none;
}
.property > .details[open] {
display: -moz-box;
-moz-box-orient: vertical;
}
/**
* Display helpers
*/
.unselectable {
-moz-user-select: -moz-none;
cursor: default;
}

Просмотреть файл

@ -41,24 +41,29 @@
<vbox id="body" flex="1">
<toolbar id="dbg-toolbar" class="devtools-toolbar">
#ifdef XP_MACOSX
<toolbarbutton id="close" class="devtools-closebutton"/>
<toolbarbutton id="close"
tooltiptext="&debuggerUI.closeButton.tooltip;"
class="devtools-closebutton"/>
#endif
<toolbarbutton id="resume"
class="devtools-toolbarbutton"
type="checkbox"
tabindex="0"/>
<toolbarbutton id="step-over"
class="devtools-toolbarbutton"
label="&debuggerUI.stepOverButton;"
tabindex="0"/>
<toolbarbutton id="step-in"
class="devtools-toolbarbutton"
label="&debuggerUI.stepInButton;"
tabindex="0"/>
<toolbarbutton id="step-out"
class="devtools-toolbarbutton"
label="&debuggerUI.stepOutButton;"
tabindex="0"/>
<hbox id="debugger-controls">
<toolbarbutton id="resume"
class="devtools-toolbarbutton"
type="checkbox"
tabindex="0"/>
<toolbarbutton id="step-over"
class="devtools-toolbarbutton"
tooltiptext="&debuggerUI.stepOverButton.tooltip;"
tabindex="0"/>
<toolbarbutton id="step-in"
class="devtools-toolbarbutton"
tooltiptext="&debuggerUI.stepInButton.tooltip;"
tabindex="0"/>
<toolbarbutton id="step-out"
class="devtools-toolbarbutton"
tooltiptext="&debuggerUI.stepOutButton.tooltip;"
tabindex="0"/>
</hbox>
<menulist id="scripts" class="devtools-menulist"
label="&debuggerUI.emptyScriptText;"/>
<textbox id="scripts-search" type="search"
@ -66,7 +71,9 @@
emptytext="&debuggerUI.emptyFilterText;"/>
<spacer flex="1"/>
#ifndef XP_MACOSX
<toolbarbutton id="close" class="devtools-closebutton"/>
<toolbarbutton id="close"
tooltiptext="&debuggerUI.closeButton.tooltip;"
class="devtools-closebutton"/>
#endif
</toolbar>
<hbox id="dbg-content" flex="1">

Просмотреть файл

@ -30,6 +30,7 @@ _BROWSER_TEST_FILES = \
browser_dbg_propertyview-06.js \
browser_dbg_propertyview-07.js \
browser_dbg_propertyview-08.js \
browser_dbg_propertyview-edit.js \
browser_dbg_panesize.js \
browser_dbg_stack-01.js \
browser_dbg_stack-02.js \

Просмотреть файл

@ -33,7 +33,7 @@ function testAnonCall() {
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
"Should have three frames.");
is(frames.querySelector("#stackframe-0 .dbg-stackframe-name").textContent,
is(frames.querySelector("#stackframe-0 .dbg-stackframe-name").getAttribute("value"),
"anonFunc", "Frame name should be anonFunc");
resumeAndFinish();

Просмотреть файл

@ -23,8 +23,8 @@ function testPause() {
"Should be running after debug_tab_pane.");
let button = gDebugger.document.getElementById("resume");
is(button.label, gDebugger.L10N.getStr("pauseLabel"),
"Button label should be pause when running.");
is(button.getAttribute("tooltiptext"), gDebugger.L10N.getStr("pauseTooltip"),
"Button tooltip should be pause when running.");
gDebugger.DebuggerController.activeThread.addOneTimeListener("paused", function() {
Services.tm.currentThread.dispatch({ run: function() {
@ -35,8 +35,8 @@ function testPause() {
is(gDebugger.DebuggerController.activeThread.paused, true,
"Should be paused after an interrupt request.");
is(button.label, gDebugger.L10N.getStr("resumeLabel"),
"Button label should be resume when paused.");
is(button.getAttribute("tooltiptext"), gDebugger.L10N.getStr("resumeTooltip"),
"Button tooltip should be resume when paused.");
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
"Should have no frames when paused in the main loop.");
@ -58,8 +58,8 @@ function testResume() {
"Should be paused after an interrupt request.");
let button = gDebugger.document.getElementById("resume");
is(button.label, gDebugger.L10N.getStr("pauseLabel"),
"Button label should be pause when running.");
is(button.getAttribute("tooltiptext"), gDebugger.L10N.getStr("pauseTooltip"),
"Button tooltip should be pause when running.");
closeDebuggerAndFinish(gTab);
}}, 0);

Просмотреть файл

@ -31,7 +31,7 @@ function testSimpleCall() {
is(testScope.id, "test-scope",
"The newly created scope should have the default id set.");
is(testScope.querySelector(".name").textContent, "test",
is(testScope.querySelector(".name").getAttribute("value"), "test",
"Any new scope should have the designated title.");
is(testScope.querySelector(".details").childNodes.length, 0,

Просмотреть файл

@ -36,13 +36,17 @@ function testSimpleCall() {
is(testVar.id, "test-scope->something-variable",
"The newly created scope should have the default id set.");
is(testVar.querySelector(".name").textContent, "something",
is(testVar.querySelector(".name").getAttribute("value"), "something",
"Any new variable should have the designated title.");
is(testVar.querySelector(".details").childNodes.length, 0,
"Any new variable should have a details container with no child nodes.");
let properties = testVar.addProperties({ "child": { "value": { "type": "object",
"class": "Object" } } });
ok(!testVar.expanded,
"Any new created variable should be initially collapsed.");
@ -84,18 +88,87 @@ function testSimpleCall() {
"The testVar should remember it is collapsed even if it is hidden.");
EventUtils.sendMouseEvent({ type: "click" },
testVar.querySelector(".title"),
testVar.querySelector(".name"),
gDebugger);
ok(testVar.expanded,
"Clicking the testScope tilte should expand it.");
"Clicking the testVar name should expand it.");
EventUtils.sendMouseEvent({ type: "click" },
testVar.querySelector(".name"),
gDebugger);
ok(!testVar.expanded,
"Clicking again the testVar name should collapse it.");
EventUtils.sendMouseEvent({ type: "click" },
testVar.querySelector(".arrow"),
gDebugger);
ok(testVar.expanded,
"Clicking the testVar arrow should expand it.");
EventUtils.sendMouseEvent({ type: "click" },
testVar.querySelector(".arrow"),
gDebugger);
ok(!testVar.expanded,
"Clicking again the testVar arrow should collapse it.");
EventUtils.sendMouseEvent({ type: "click" },
testVar.querySelector(".title"),
gDebugger);
ok(!testVar.expanded,
"Clicking again the testScope tilte should collapse it.");
"Clicking the testVar title div shouldn't expand it.");
testScope.show();
testScope.expand();
testVar.show();
testVar.expand();
ok(!testVar.child.expanded,
"The testVar child property should remember it is collapsed even if it is hidden.");
EventUtils.sendMouseEvent({ type: "click" },
testVar.child.querySelector(".key"),
gDebugger);
ok(testVar.child.expanded,
"Clicking the testVar child property name should expand it.");
EventUtils.sendMouseEvent({ type: "click" },
testVar.child.querySelector(".key"),
gDebugger);
ok(!testVar.child.expanded,
"Clicking again the testVar child property name should collapse it.");
EventUtils.sendMouseEvent({ type: "click" },
testVar.child.querySelector(".arrow"),
gDebugger);
ok(testVar.child.expanded,
"Clicking the testVar child property arrow should expand it.");
EventUtils.sendMouseEvent({ type: "click" },
testVar.child.querySelector(".arrow"),
gDebugger);
ok(!testVar.child.expanded,
"Clicking again the testVar child property arrow should collapse it.");
EventUtils.sendMouseEvent({ type: "click" },
testVar.child.querySelector(".title"),
gDebugger);
ok(!testVar.child.expanded,
"Clicking the testVar child property title div shouldn't expand it.");
let emptyCallbackSender = null;

Просмотреть файл

@ -28,7 +28,7 @@ function testSimpleCall() {
testVar.setGrip(1.618);
is(testVar.querySelector(".info").textContent, "1.618",
is(testVar.querySelector(".value").getAttribute("value"), "1.618",
"The grip information for the variable wasn't set correctly.");
is(testVar.querySelector(".details").childNodes.length, 0,
@ -40,7 +40,7 @@ function testSimpleCall() {
is(testVar.querySelector(".details").childNodes.length, 0,
"Adding type and class properties shouldn't add any new tree nodes.");
is(testVar.querySelector(".info").textContent, "[object Window]",
is(testVar.querySelector(".value").getAttribute("value"), "[object Window]",
"The information for the variable wasn't set correctly.");

Просмотреть файл

@ -104,28 +104,28 @@ function testSimpleCall() {
"The localVar5.someProp5 doesn't contain all the created properties.");
is(windowVar.querySelector(".info").textContent, "[object Window]",
is(windowVar.querySelector(".value").getAttribute("value"), "[object Window]",
"The grip information for the windowVar wasn't set correctly.");
is(documentVar.querySelector(".info").textContent, "[object HTMLDocument]",
is(documentVar.querySelector(".value").getAttribute("value"), "[object HTMLDocument]",
"The grip information for the documentVar wasn't set correctly.");
is(localVar0.querySelector(".info").textContent, "42",
is(localVar0.querySelector(".value").getAttribute("value"), "42",
"The grip information for the localVar0 wasn't set correctly.");
is(localVar1.querySelector(".info").textContent, "true",
is(localVar1.querySelector(".value").getAttribute("value"), "true",
"The grip information for the localVar1 wasn't set correctly.");
is(localVar2.querySelector(".info").textContent, "\"nasu\"",
is(localVar2.querySelector(".value").getAttribute("value"), "\"nasu\"",
"The grip information for the localVar2 wasn't set correctly.");
is(localVar3.querySelector(".info").textContent, "undefined",
is(localVar3.querySelector(".value").getAttribute("value"), "undefined",
"The grip information for the localVar3 wasn't set correctly.");
is(localVar4.querySelector(".info").textContent, "null",
is(localVar4.querySelector(".value").getAttribute("value"), "null",
"The grip information for the localVar4 wasn't set correctly.");
is(localVar5.querySelector(".info").textContent, "[object Object]",
is(localVar5.querySelector(".value").getAttribute("value"), "[object Object]",
"The grip information for the localVar5 wasn't set correctly.");
gDebugger.DebuggerController.activeThread.resume(function() {

Просмотреть файл

@ -55,37 +55,37 @@ function testFrameParameters()
is(localNodes.length, 11,
"The localScope should contain all the created variable elements.");
is(localNodes[0].querySelector(".info").textContent, "[object Proxy]",
is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Proxy]",
"Should have the right property value for 'this'.");
is(localNodes[1].querySelector(".info").textContent, "[object Object]",
is(localNodes[1].querySelector(".value").getAttribute("value"), "[object Object]",
"Should have the right property value for 'aArg'.");
is(localNodes[2].querySelector(".info").textContent, '"beta"',
is(localNodes[2].querySelector(".value").getAttribute("value"), '"beta"',
"Should have the right property value for 'bArg'.");
is(localNodes[3].querySelector(".info").textContent, "3",
is(localNodes[3].querySelector(".value").getAttribute("value"), "3",
"Should have the right property value for 'cArg'.");
is(localNodes[4].querySelector(".info").textContent, "false",
is(localNodes[4].querySelector(".value").getAttribute("value"), "false",
"Should have the right property value for 'dArg'.");
is(localNodes[5].querySelector(".info").textContent, "null",
is(localNodes[5].querySelector(".value").getAttribute("value"), "null",
"Should have the right property value for 'eArg'.");
is(localNodes[6].querySelector(".info").textContent, "undefined",
is(localNodes[6].querySelector(".value").getAttribute("value"), "undefined",
"Should have the right property value for 'fArg'.");
is(localNodes[7].querySelector(".info").textContent, "1",
is(localNodes[7].querySelector(".value").getAttribute("value"), "1",
"Should have the right property value for 'a'.");
is(localNodes[8].querySelector(".info").textContent, "[object Object]",
is(localNodes[8].querySelector(".value").getAttribute("value"), "[object Object]",
"Should have the right property value for 'b'.");
is(localNodes[9].querySelector(".info").textContent, "[object Object]",
is(localNodes[9].querySelector(".value").getAttribute("value"), "[object Object]",
"Should have the right property value for 'c'.");
is(localNodes[10].querySelector(".info").textContent, "[object Arguments]",
is(localNodes[10].querySelector(".value").getAttribute("value"), "[object Arguments]",
"Should have the right property value for 'arguments'.");
resumeAndFinish();
@ -98,16 +98,15 @@ function testFrameParameters()
}
function resumeAndFinish() {
gDebugger.DebuggerController.activeThread.addOneTimeListener("framescleared", function() {
Services.tm.currentThread.dispatch({ run: function() {
var frames = gDebugger.DebuggerView.StackFrames._frames;
gDebugger.addEventListener("Debugger:AfterFramesCleared", function listener() {
gDebugger.removeEventListener("Debugger:AfterFramesCleared", listener, true);
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
"Should have no frames.");
var frames = gDebugger.DebuggerView.StackFrames._frames;
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
"Should have no frames.");
closeDebuggerAndFinish(gTab);
}}, 0);
});
closeDebuggerAndFinish(gTab);
}, true);
gDebugger.DebuggerController.activeThread.resume();
}

Просмотреть файл

@ -53,7 +53,7 @@ function testFrameParameters()
is(localNodes.length, 11,
"The localScope should contain all the created variable elements.");
is(localNodes[0].querySelector(".info").textContent, "[object Proxy]",
is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Proxy]",
"Should have the right property value for 'this'.");
// Expand the 'this', 'arguments' and 'c' tree nodes. This causes
@ -79,34 +79,34 @@ function testFrameParameters()
}
window.clearInterval(intervalID);
is(localNodes[0].querySelector(".property > .title > .key")
.textContent, "__proto__ ",
.getAttribute("value"), "__proto__",
"Should have the right property name for __proto__.");
ok(localNodes[0].querySelector(".property > .title > .value")
.textContent.search(/object/) != -1,
.getAttribute("value").search(/object/) != -1,
"__proto__ should be an object.");
is(localNodes[9].querySelector(".info").textContent, "[object Object]",
is(localNodes[9].querySelector(".value").getAttribute("value"), "[object Object]",
"Should have the right property value for 'c'.");
is(localNodes[9].querySelectorAll(".property > .title > .key")[1]
.textContent, "a",
.getAttribute("value"), "a",
"Should have the right property name for 'a'.");
is(localNodes[9].querySelectorAll(".property > .title > .value")[1]
.textContent, 1,
.getAttribute("value"), 1,
"Should have the right value for 'c.a'.");
is(localNodes[10].querySelector(".info").textContent,
is(localNodes[10].querySelector(".value").getAttribute("value"),
"[object Arguments]",
"Should have the right property value for 'arguments'.");
is(localNodes[10].querySelectorAll(".property > .title > .key")[7]
.textContent, "length",
.getAttribute("value"), "length",
"Should have the right property name for 'length'.");
is(localNodes[10].querySelectorAll(".property > .title > .value")[7]
.textContent, 5,
.getAttribute("value"), 5,
"Should have the right argument length.");
resumeAndFinish();
@ -120,19 +120,17 @@ function testFrameParameters()
}
function resumeAndFinish() {
let thread = gDebugger.DebuggerController.activeThread;
thread.addOneTimeListener("framescleared", function() {
Services.tm.currentThread.dispatch({ run: function() {
var frames = gDebugger.DebuggerView.StackFrames._frames;
gDebugger.addEventListener("Debugger:AfterFramesCleared", function listener() {
gDebugger.removeEventListener("Debugger:AfterFramesCleared", listener, true);
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
"Should have no frames.");
var frames = gDebugger.DebuggerView.StackFrames._frames;
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
"Should have no frames.");
closeDebuggerAndFinish(gTab);
}}, 0);
});
closeDebuggerAndFinish(gTab);
}, true);
thread.resume();
gDebugger.DebuggerController.activeThread.resume();
}
registerCleanupFunction(function() {

Просмотреть файл

@ -0,0 +1,103 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var gPane = null;
var gTab = null;
var gDebuggee = null;
var gDebugger = null;
const TAB_URL = EXAMPLE_URL + "browser_dbg_frame-parameters.html";
function test() {
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.contentWindow;
testFrameEval();
});
}
function testFrameEval() {
gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
Services.tm.currentThread.dispatch({ run: function() {
is(gDebugger.DebuggerController.activeThread.state, "paused",
"Should only be getting stack frames while paused.");
var localScope = gDebugger.DebuggerView.Properties.localScope,
localNodes = localScope.querySelector(".details").childNodes,
varA = localNodes[7];
is(varA.querySelector(".name").getAttribute("value"), "a",
"Should have the right name for 'a'.");
is(varA.querySelector(".value").getAttribute("value"), 1,
"Should have the right initial value for 'a'.");
testModification(varA, function(aVar) {
testModification(aVar, function(aVar) {
testModification(aVar, function(aVar) {
resumeAndFinish();
}, "document.title", '"Debugger Function Call Parameter Test"');
}, "b", "[object Object]");
}, "{ a: 1 }", "[object Object]");
}}, 0);
}, false);
EventUtils.sendMouseEvent({ type: "click" },
content.document.querySelector("button"),
content.window);
}
function testModification(aVar, aCallback, aNewValue, aNewResult) {
function makeChangesAndExitInputMode() {
EventUtils.sendString(aNewValue);
EventUtils.sendKey("RETURN");
}
EventUtils.sendMouseEvent({ type: "click" },
aVar.querySelector(".value"),
gDebugger);
executeSoon(function() {
ok(aVar.querySelector(".element-input"),
"There should be an input element created.");
gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
// Get the variable reference anew, since the old ones were discarded when
// we resumed.
var localScope = gDebugger.DebuggerView.Properties.localScope,
localNodes = localScope.querySelector(".details").childNodes,
varA = localNodes[7];
is(varA.querySelector(".value").getAttribute("value"), aNewResult,
"Should have the right value for 'a'.");
executeSoon(function() {
aCallback(varA);
});
}, false);
makeChangesAndExitInputMode();
});
}
function resumeAndFinish() {
gDebugger.DebuggerController.activeThread.resume(function() {
closeDebuggerAndFinish(gTab);
});
}
registerCleanupFunction(function() {
removeTab(gTab);
gPane = null;
gTab = null;
gDebuggee = null;
gDebugger = null;
});

Просмотреть файл

@ -36,7 +36,7 @@ function testEvalCall() {
is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
"All children should be frames.");
is(frames.querySelector("#stackframe-0 .dbg-stackframe-name").textContent,
is(frames.querySelector("#stackframe-0 .dbg-stackframe-name").getAttribute("value"),
"(eval)", "Frame name should be (eval)");
ok(frames.querySelector("#stackframe-0").classList.contains("selected"),

Просмотреть файл

@ -37,7 +37,8 @@ function testEvalCallResume() {
"All children should be frames.");
gDebugger.DebuggerController.activeThread.addOneTimeListener("framescleared", function() {
gDebugger.addEventListener("Debugger:AfterFramesCleared", function listener() {
gDebugger.removeEventListener("Debugger:AfterFramesCleared", listener, true);
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
"Should have no frames after resume");
@ -49,7 +50,7 @@ function testEvalCallResume() {
"Should have the empty list explanation.");
closeDebuggerAndFinish(gTab);
});
}, true);
gDebugger.DebuggerController.activeThread.resume();
}}, 0);

Просмотреть файл

@ -448,15 +448,16 @@ InspectorUI.prototype = {
this.win = this.browser.contentWindow;
this.winID = this.getWindowID(this.win);
this.toolbar = this.chromeDoc.getElementById("inspector-toolbar");
this.inspectMenuitem = this.chromeDoc.getElementById("Tools:Inspect");
this.inspectToolbutton =
this.chromeDoc.getElementById("inspector-inspect-toolbutton");
this.inspectCommand = this.chromeDoc.getElementById("Inspector:Inspect");
// Update menus:
this.inspectorUICommand = this.chromeDoc.getElementById("Tools:Inspect");
this.inspectorUICommand.setAttribute("checked", "true");
this.chromeWin.Tilt.setup();
this.treePanel = new TreePanel(this.chromeWin, this);
this.toolbar.hidden = false;
this.inspectMenuitem.setAttribute("checked", true);
// initialize the HTML Breadcrumbs
this.breadcrumbs = new HTMLBreadcrumbs(this);
@ -649,7 +650,8 @@ InspectorUI.prototype = {
if (!aKeepInspector)
this.store.deleteInspector(this.winID);
this.inspectMenuitem.removeAttribute("checked");
this.inspectorUICommand.setAttribute("checked", "false");
this.browser = this.win = null; // null out references to browser and window
this.winID = null;
this.selection = null;
@ -658,6 +660,8 @@ InspectorUI.prototype = {
delete this.treePanel;
delete this.stylePanel;
delete this.inspectorUICommand;
delete this.inspectCommand;
delete this.toolbar;
Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.CLOSED, null);
@ -677,7 +681,7 @@ InspectorUI.prototype = {
if (this.treePanel && this.treePanel.editingContext)
this.treePanel.closeEditor();
this.inspectToolbutton.checked = true;
this.inspectCommand.setAttribute("checked", "true");
this.inspecting = true;
this.highlighter.unlock();
@ -702,7 +706,7 @@ InspectorUI.prototype = {
return;
}
this.inspectToolbutton.checked = false;
this.inspectCommand.setAttribute("checked", "false");
this.inspecting = false;
if (this.highlighter.getNode()) {

Просмотреть файл

@ -68,6 +68,8 @@ function runSelectionTests(subject)
is(subject.wrappedJSObject, InspectorUI,
"InspectorUI accessible in the observer");
InspectorUI.highlighter.veilContainer.setAttribute("disable-transitions", "true");
executeSoon(function() {
InspectorUI.highlighter.addListener("nodeselected", performTestComparisons);
EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);

Просмотреть файл

@ -10,6 +10,7 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/inspector.jsm");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource:///modules/devtools/CssLogic.jsm");
var EXPORTED_SYMBOLS = ["LayoutView"];
@ -24,6 +25,7 @@ function LayoutView(aOptions)
LayoutView.prototype = {
init: function LV_init() {
this.cssLogic = new CssLogic();
this.update = this.update.bind(this);
this.onMessage = this.onMessage.bind(this);
@ -40,13 +42,16 @@ LayoutView.prototype = {
// We update the values when:
// a node is locked
// we get the MozAfterPaint event and the node is locked
function onLock() {
this.undim();
this.update();
// We make sure we never add 2 listeners.
if (!this.trackingPaint) {
this.browser.addEventListener("MozAfterPaint", this.update, true);
this.trackingPaint = true;
function onSelect() {
if (this.inspector.locked) {
this.cssLogic.highlight(this.inspector.selection);
this.undim();
this.update();
// We make sure we never add 2 listeners.
if (!this.trackingPaint) {
this.browser.addEventListener("MozAfterPaint", this.update, true);
this.trackingPaint = true;
}
}
}
@ -56,9 +61,9 @@ LayoutView.prototype = {
this.dim();
}
this.onLock = onLock.bind(this);
this.onSelect= onSelect.bind(this);
this.onUnlock = onUnlock.bind(this);
this.inspector.on("locked", this.onLock);
this.inspector.on("select", this.onSelect);
this.inspector.on("unlocked", this.onUnlock);
// Build the layout view in the sidebar.
@ -118,7 +123,7 @@ LayoutView.prototype = {
* Destroy the nodes. Remove listeners.
*/
destroy: function LV_destroy() {
this.inspector.removeListener("locked", this.onLock);
this.inspector.removeListener("select", this.onSelect);
this.inspector.removeListener("unlocked", this.onUnlock);
this.browser.removeEventListener("MozAfterPaint", this.update, true);
this.iframe.removeEventListener("keypress", this.bound_handleKeypress, true);
@ -158,7 +163,7 @@ LayoutView.prototype = {
// inside the iframe.
if (this.inspector.locked)
this.onLock();
this.onSelect();
else
this.onUnlock();
@ -301,6 +306,16 @@ LayoutView.prototype = {
let selector = this.map[i].selector;
let property = this.map[i].property;
this.map[i].value = parseInt(style.getPropertyValue(property));
}
let margins = this.processMargins(node);
if ("top" in margins) this.map.marginTop.value = "auto";
if ("right" in margins) this.map.marginRight.value = "auto";
if ("bottom" in margins) this.map.marginBottom.value = "auto";
if ("left" in margins) this.map.marginLeft.value = "auto";
for (let i in this.map) {
let selector = this.map[i].selector;
let span = this.doc.querySelector(selector);
span.textContent = this.map[i].value;
}
@ -313,4 +328,21 @@ LayoutView.prototype = {
this.doc.querySelector(".size > span").textContent = width + "x" + height;
},
/**
* Find margins declared 'auto'
*/
processMargins: function LV_processMargins(node) {
let margins = {};
for each (let prop in ["top", "bottom", "left", "right"]) {
let info = this.cssLogic.getPropertyInfo("margin-" + prop);
let selectors = info.matchedSelectors;
if (selectors && selectors.length > 0 && selectors[0].value == "auto") {
margins[prop] = "auto";
}
}
return margins;
},
}

Просмотреть файл

@ -16,9 +16,9 @@ function test() {
{selector: "#element-size", value: "160x160"},
{selector: ".size > span", value: "100x100"},
{selector: ".margin.top > span", value: 30},
{selector: ".margin.left > span", value: 30},
{selector: ".margin.left > span", value: "auto"},
{selector: ".margin.bottom > span", value: 30},
{selector: ".margin.right > span", value: 30},
{selector: ".margin.right > span", value: "auto"},
{selector: ".padding.top > span", value: 20},
{selector: ".padding.left > span", value: 20},
{selector: ".padding.bottom > span", value: 20},
@ -30,16 +30,16 @@ function test() {
];
let res2 = [
{selector: "#element-size", value: "160x210"},
{selector: "#element-size", value: "190x210"},
{selector: ".size > span", value: "100x150"},
{selector: ".margin.top > span", value: 30},
{selector: ".margin.left > span", value: 30},
{selector: ".margin.left > span", value: "auto"},
{selector: ".margin.bottom > span", value: 30},
{selector: ".margin.right > span", value: 50},
{selector: ".margin.right > span", value: "auto"},
{selector: ".padding.top > span", value: 20},
{selector: ".padding.left > span", value: 20},
{selector: ".padding.bottom > span", value: 20},
{selector: ".padding.right > span", value: 20},
{selector: ".padding.right > span", value: 50},
{selector: ".border.top > span", value: 10},
{selector: ".border.left > span", value: 10},
{selector: ".border.bottom > span", value: 10},
@ -53,7 +53,7 @@ function test() {
waitForFocus(setupTest, content);
}, true);
let style = "div { position: absolute; top: 42px; left: 42px; height: 100px; width: 100px; border: 10px solid black; padding: 20px; margin: 30px; }";
let style = "div { position: absolute; top: 42px; left: 42px; height: 100px; width: 100px; border: 10px solid black; padding: 20px; margin: 30px auto;}";
let html = "<style>" + style + "</style><div></div>"
content.location = "data:text/html," + encodeURIComponent(html);
@ -106,7 +106,7 @@ function test() {
}
InspectorUI.selection.style.height = "150px";
InspectorUI.selection.style.marginRight = "50px";
InspectorUI.selection.style.paddingRight = "50px";
setTimeout(test2, 200); // Should be enough to get a MozAfterPaint event
}

Просмотреть файл

@ -487,11 +487,11 @@ var Scratchpad = {
GetStringFromName("propertyPanel.updateButton.label"),
accesskey: this.strings.
GetStringFromName("propertyPanel.updateButton.accesskey"),
oncommand: function () {
oncommand: function _SP_PP_Update_onCommand() {
let [error, result] = self.evalForContext(aEvalString);
if (!error) {
propPanel.treeView.data = result;
propPanel.treeView.data = { object: result };
}
}
});
@ -499,8 +499,9 @@ var Scratchpad = {
let doc = this.browserWindow.document;
let parent = doc.getElementById("mainPopupSet");
let title = aOutputObject.toString();
propPanel = new PropertyPanel(parent, doc, title, aOutputObject, buttons);
let title = String(aOutputObject);
propPanel = new PropertyPanel(parent, title, { object: aOutputObject },
buttons);
let panel = propPanel.panel;
panel.setAttribute("class", "scratchpad_propertyPanel");

Просмотреть файл

@ -25,6 +25,10 @@ function runTest() {
document.getElementById("Tools:DevToolbar").doCommand();
}
function isChecked(b) {
return b.getAttribute("checked") == "true";
}
function checkOpen() {
ok(DeveloperToolbar.visible, "DeveloperToolbar is visible in checkOpen");
@ -35,21 +39,21 @@ function checkOpen() {
ok(close, "Close button exists");
ok(!webconsole.checked, "web console button state 1");
ok(!inspector.checked, "inspector button state 1");
ok(!debuggr.checked, "debugger button state 1");
ok(!isChecked(webconsole), "web console button state 1");
ok(!isChecked(inspector), "inspector button state 1");
ok(!isChecked(debuggr), "debugger button state 1");
document.getElementById("Tools:WebConsole").doCommand();
ok(webconsole.checked, "web console button state 2");
ok(!inspector.checked, "inspector button state 2");
ok(!debuggr.checked, "debugger button state 2");
ok(isChecked(webconsole), "web console button state 2");
ok(!isChecked(inspector), "inspector button state 2");
ok(!isChecked(debuggr), "debugger button state 2");
document.getElementById("Tools:Inspect").doCommand();
ok(webconsole.checked, "web console button state 3");
ok(inspector.checked, "inspector button state 3");
ok(!debuggr.checked, "debugger button state 3");
ok(isChecked(webconsole), "web console button state 3");
ok(isChecked(inspector), "inspector button state 3");
ok(!isChecked(debuggr), "debugger button state 3");
// Christmas tree!
@ -59,24 +63,24 @@ function checkOpen() {
document.getElementById("Tools:WebConsole").doCommand();
ok(!webconsole.checked, "web console button state 6");
ok(inspector.checked, "inspector button state 6");
ok(!debuggr.checked, "debugger button state 6");
ok(!isChecked(webconsole), "web console button state 6");
ok(isChecked(inspector), "inspector button state 6");
ok(!isChecked(debuggr), "debugger button state 6");
document.getElementById("Tools:Inspect").doCommand();
ok(!webconsole.checked, "web console button state 7");
ok(!inspector.checked, "inspector button state 7");
ok(!debuggr.checked, "debugger button state 7");
ok(!isChecked(webconsole), "web console button state 7");
ok(!isChecked(inspector), "inspector button state 7");
ok(!isChecked(debuggr), "debugger button state 7");
// All closed
// Check we can open and close and retain button state
document.getElementById("Tools:Inspect").doCommand();
ok(!webconsole.checked, "web console button state 8");
ok(inspector.checked, "inspector button state 8");
ok(!debuggr.checked, "debugger button state 8");
ok(!isChecked(webconsole), "web console button state 8");
ok(isChecked(inspector), "inspector button state 8");
ok(!isChecked(debuggr), "debugger button state 8");
oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkClosed));
document.getElementById("Tools:DevToolbar").doCommand();
@ -99,9 +103,9 @@ function checkReOpen() {
let inspector = document.getElementById("developer-toolbar-inspector");
let debuggr = document.getElementById("developer-toolbar-debugger");
ok(webconsole.checked, "web console button state 9");
ok(inspector.checked, "inspector button state 9");
ok(!debuggr.checked, "debugger button state 9");
ok(isChecked(webconsole), "web console button state 9");
ok(isChecked(inspector), "inspector button state 9");
ok(!isChecked(debuggr), "debugger button state 9");
oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkReClosed));
document.getElementById("developer-toolbar-closebutton").doCommand();

Просмотреть файл

@ -23,6 +23,7 @@ let Services = tempScope.Services;
let gConsoleStorage = tempScope.ConsoleAPIStorage;
let WebConsoleUtils = tempScope.WebConsoleUtils;
let l10n = WebConsoleUtils.l10n;
let JSPropertyProvider = tempScope.JSPropertyProvider;
tempScope = null;
let _alive = true; // Track if this content script should still be alive.
@ -32,7 +33,6 @@ let _alive = true; // Track if this content script should still be alive.
*/
let Manager = {
get window() content,
get console() this.window.console,
sandbox: null,
hudId: null,
_sequence: 0,
@ -60,11 +60,7 @@ let Manager = {
// Need to track the owner XUL window to listen to the unload and TabClose
// events, to avoid memory leaks.
let xulWindow = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler.ownerDocument.defaultView;
let xulWindow = this._xulWindow();
xulWindow.addEventListener("unload", this._onXULWindowClose, false);
let tabContainer = xulWindow.gBrowser.tabContainer;
@ -357,6 +353,19 @@ let Manager = {
}
},
/**
* Find the XUL window that owns the content script.
* @private
* @return Window
* The XUL window that owns the content script.
*/
_xulWindow: function Manager__xulWindow()
{
return this.window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell)
.chromeEventHandler.ownerDocument.defaultView;
},
/**
* Destroy the Web Console content script instance.
*/
@ -366,11 +375,7 @@ let Manager = {
Services.obs.removeObserver(this, "quit-application-granted");
_alive = false;
let xulWindow = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler.ownerDocument.defaultView;
let xulWindow = this._xulWindow();
xulWindow.removeEventListener("unload", this._onXULWindowClose, false);
let tabContainer = xulWindow.gBrowser.tabContainer;
tabContainer.removeEventListener("TabClose", this._onTabClose, false);
@ -389,11 +394,238 @@ let Manager = {
},
};
/**
* JSTerm helper functions.
*
* Defines a set of functions ("helper functions") that are available from the
* Web Console but not from the web page.
*
* A list of helper functions used by Firebug can be found here:
* http://getfirebug.com/wiki/index.php/Command_Line_API
*/
function JSTermHelper(aJSTerm)
{
/**
* Find a node by ID.
*
* @param string aId
* The ID of the element you want.
* @return nsIDOMNode or null
* The result of calling document.getElementById(aId).
*/
aJSTerm.sandbox.$ = function JSTH_$(aId)
{
return aJSTerm.window.document.getElementById(aId);
};
/**
* Find the nodes matching a CSS selector.
*
* @param string aSelector
* A string that is passed to window.document.querySelectorAll.
* @return nsIDOMNodeList
* Returns the result of document.querySelectorAll(aSelector).
*/
aJSTerm.sandbox.$$ = function JSTH_$$(aSelector)
{
return aJSTerm.window.document.querySelectorAll(aSelector);
};
/**
* Runs an xPath query and returns all matched nodes.
*
* @param string aXPath
* xPath search query to execute.
* @param [optional] nsIDOMNode aContext
* Context to run the xPath query on. Uses window.document if not set.
* @returns array of nsIDOMNode
*/
aJSTerm.sandbox.$x = function JSTH_$x(aXPath, aContext)
{
let nodes = [];
let doc = aJSTerm.window.document;
let aContext = aContext || doc;
try {
let results = doc.evaluate(aXPath, aContext, null,
Ci.nsIDOMXPathResult.ANY_TYPE, null);
let node;
while (node = results.iterateNext()) {
nodes.push(node);
}
}
catch (ex) {
aJSTerm.console.error(ex.message);
}
return nodes;
};
/**
* Returns the currently selected object in the highlighter.
*
* Warning: this implementation crosses the process boundaries! This is not
* usable within a remote browser. To implement this feature correctly we need
* support for remote inspection capabilities within the Inspector as well.
*
* @return nsIDOMElement|null
* The DOM element currently selected in the highlighter.
*/
Object.defineProperty(aJSTerm.sandbox, "$0", {
get: function() {
try {
return Manager._xulWindow().InspectorUI.selection;
}
catch (ex) {
aJSTerm.console.error(ex.message);
}
},
enumerable: true,
configurable: false
});
/**
* Clears the output of the JSTerm.
*/
aJSTerm.sandbox.clear = function JSTH_clear()
{
aJSTerm.helperEvaluated = true;
Manager.sendMessage("JSTerm:ClearOutput", {});
};
/**
* Returns the result of Object.keys(aObject).
*
* @param object aObject
* Object to return the property names from.
* @returns array of string
*/
aJSTerm.sandbox.keys = function JSTH_keys(aObject)
{
return Object.keys(WebConsoleUtils.unwrap(aObject));
};
/**
* Returns the values of all properties on aObject.
*
* @param object aObject
* Object to display the values from.
* @returns array of string
*/
aJSTerm.sandbox.values = function JSTH_values(aObject)
{
let arrValues = [];
let obj = WebConsoleUtils.unwrap(aObject);
try {
for (let prop in obj) {
arrValues.push(obj[prop]);
}
}
catch (ex) {
aJSTerm.console.error(ex.message);
}
return arrValues;
};
/**
* Opens a help window in MDN.
*/
aJSTerm.sandbox.help = function JSTH_help()
{
aJSTerm.helperEvaluated = true;
aJSTerm.window.open(
"https://developer.mozilla.org/AppLinks/WebConsoleHelp?locale=" +
aJSTerm.window.navigator.language, "help", "");
};
/**
* Inspects the passed aObject. This is done by opening the PropertyPanel.
*
* @param object aObject
* Object to inspect.
*/
aJSTerm.sandbox.inspect = function JSTH_inspect(aObject)
{
if (!WebConsoleUtils.isObjectInspectable(aObject)) {
return aObject;
}
aJSTerm.helperEvaluated = true;
let message = {
input: aJSTerm._evalInput,
objectCacheId: Manager.sequenceId,
};
message.resultObject =
aJSTerm.prepareObjectForRemote(WebConsoleUtils.unwrap(aObject),
message.objectCacheId);
Manager.sendMessage("JSTerm:InspectObject", message);
};
/**
* Prints aObject to the output.
*
* @param object aObject
* Object to print to the output.
* @return string
*/
aJSTerm.sandbox.pprint = function JSTH_pprint(aObject)
{
aJSTerm.helperEvaluated = true;
if (aObject === null || aObject === undefined || aObject === true ||
aObject === false) {
aJSTerm.console.error(l10n.getStr("helperFuncUnsupportedTypeError"));
return;
}
else if (typeof aObject == "function") {
aJSTerm.helperRawOutput = true;
return aObject + "\n";
}
aJSTerm.helperRawOutput = true;
let output = [];
let pairs = WebConsoleUtils.namesAndValuesOf(WebConsoleUtils.unwrap(aObject));
pairs.forEach(function(aPair) {
output.push(aPair.name + ": " + aPair.value);
});
return " " + output.join("\n ");
};
/**
* Print a string to the output, as-is.
*
* @param string aString
* A string you want to output.
* @returns void
*/
aJSTerm.sandbox.print = function JSTH_print(aString)
{
aJSTerm.helperEvaluated = true;
aJSTerm.helperRawOutput = true;
return String(aString);
};
}
/**
* The JavaScript terminal is meant to allow remote code execution for the Web
* Console.
*/
let JSTerm = {
get window() Manager.window,
get console() this.window.console,
/**
* The Cu.Sandbox() object where code is evaluated.
*/
sandbox: null,
_messageHandlers: {},
/**
* Evaluation result objects are cached in this object. The chrome process can
* request any object based on its ID.
@ -406,16 +638,112 @@ let JSTerm = {
init: function JST_init()
{
this._objectCache = {};
this._messageHandlers = {
"JSTerm:EvalRequest": this.handleEvalRequest,
"JSTerm:GetEvalObject": this.handleGetEvalObject,
"JSTerm:Autocomplete": this.handleAutocomplete,
"JSTerm:ClearObjectCache": this.handleClearObjectCache,
};
Manager.addMessageHandler("JSTerm:GetEvalObject",
this.handleGetEvalObject.bind(this));
Manager.addMessageHandler("JSTerm:ClearObjectCache",
this.handleClearObjectCache.bind(this));
for (let name in this._messageHandlers) {
let handler = this._messageHandlers[name].bind(this);
Manager.addMessageHandler(name, handler);
}
this._createSandbox();
},
/**
* Handler for the "JSTerm:EvalRequest" remote message. This method evaluates
* user input in the JavaScript sandbox and sends the result back to the
* remote process. The "JSTerm:EvalResult" message includes the following
* data:
* - id - the same ID as the EvalRequest (for tracking purposes).
* - input - the JS string that was evaluated.
* - resultString - the evaluation result converted to a string formatted
* for display.
* - timestamp - timestamp when evaluation occurred (Date.now(),
* milliseconds since the UNIX epoch).
* - inspectable - boolean that tells if the evaluation result object can be
* inspected or not.
* - error - the evaluation exception object (if any).
* - errorMessage - the exception object converted to a string (if any error
* occurred).
* - helperResult - boolean that tells if a JSTerm helper was evaluated.
* - helperRawOutput - boolean that tells if the helper evaluation result
* should be displayed as raw output.
*
* If the result object is inspectable then two additional properties are
* included:
* - childrenCacheId - tells where child objects are cached. This is the
* same as aRequest.resultCacheId.
* - resultObject - the result object prepared for the remote process. See
* this.prepareObjectForRemote().
*
* @param object aRequest
* The code evaluation request object:
* - id - request ID.
* - str - string to evaluate.
* - resultCacheId - where to cache the evaluation child objects.
*/
handleEvalRequest: function JST_handleEvalRequest(aRequest)
{
let id = aRequest.id;
let input = aRequest.str;
let result, error = null;
let timestamp;
this.helperEvaluated = false;
this.helperRawOutput = false;
this._evalInput = input;
try {
timestamp = Date.now();
result = this.evalInSandbox(input);
}
catch (ex) {
error = ex;
}
delete this._evalInput;
let inspectable = !error && WebConsoleUtils.isObjectInspectable(result);
let resultString = undefined;
if (!error) {
resultString = this.helperRawOutput ? result :
WebConsoleUtils.formatResult(result);
}
let message = {
id: id,
input: input,
resultString: resultString,
timestamp: timestamp,
error: error,
errorMessage: error ? String(error) : null,
inspectable: inspectable,
helperResult: this.helperEvaluated,
helperRawOutput: this.helperRawOutput,
};
if (inspectable) {
message.childrenCacheId = aRequest.resultCacheId;
message.resultObject =
this.prepareObjectForRemote(result, message.childrenCacheId);
}
Manager.sendMessage("JSTerm:EvalResult", message);
},
/**
* Handler for the remote "JSTerm:GetEvalObject" message. This allows the
* remote Web Console instance to retrieve an object from the content process.
* The "JSTerm:EvalObject" message is sent back to the remote process:
* - id - the request ID, used to trace back to the initial request.
* - cacheId - the cache ID where the requested object is stored.
* - objectId - the ID of the object being sent.
* - object - the object representation prepared for remote inspection. See
* this.prepareObjectForRemote().
* - childrenCacheId - the cache ID where any child object of |object| are
* stored.
*
* @param object aRequest
* The message that requests the content object. Properties: cacheId,
@ -481,8 +809,7 @@ let JSTerm = {
* method in aObject. Each element describes the property. For details
* see WebConsoleUtils.namesAndValuesOf().
*/
prepareObjectForRemote:
function JST_prepareObjectForRemote(aObject, aCacheId)
prepareObjectForRemote: function JST_prepareObjectForRemote(aObject, aCacheId)
{
// Cache the properties that have inspectable values.
let propCache = this._objectCache[aCacheId] || {};
@ -494,14 +821,103 @@ let JSTerm = {
return result;
},
/**
* Handler for the "JSTerm:Autocomplete" remote message. This handler provides
* completion results for user input. The "JSterm:AutocompleteProperties"
* message is sent to the remote process:
* - id - the same as request ID.
* - input - the user input (same as in the request message).
* - matches - an array of matched properties (strings).
* - matchProp - the part that was used from the user input for finding the
* matches. For details see the JSPropertyProvider description and
* implementation.
*
*
* @param object aRequest
* The remote request object which holds two properties: an |id| and
* the user |input|.
*/
handleAutocomplete: function JST_handleAutocomplete(aRequest)
{
let result = JSPropertyProvider(this.window, aRequest.input) || {};
let message = {
id: aRequest.id,
input: aRequest.input,
matches: result.matches || [],
matchProp: result.matchProp,
};
Manager.sendMessage("JSTerm:AutocompleteProperties", message);
},
/**
* Create the JavaScript sandbox where user input is evaluated.
* @private
*/
_createSandbox: function JST__createSandbox()
{
this.sandbox = new Cu.Sandbox(this.window, {
sandboxPrototype: this.window,
wantXrays: false,
});
this.sandbox.console = this.console;
JSTermHelper(this);
},
/**
* Evaluates a string in the sandbox.
*
* @param string aString
* String to evaluate in the sandbox.
* @returns something
* The result of the evaluation.
*/
evalInSandbox: function JST_evalInSandbox(aString)
{
// The help function needs to be easy to guess, so we make the () optional
if (aString.trim() == "help" || aString.trim() == "?") {
aString = "help()";
}
let window = WebConsoleUtils.unwrap(this.sandbox.window);
let $ = null, $$ = null;
// We prefer to execute the page-provided implementations for the $() and
// $$() functions.
if (typeof window.$ == "function") {
$ = this.sandbox.$;
delete this.sandbox.$;
}
if (typeof window.$$ == "function") {
$$ = this.sandbox.$$;
delete this.sandbox.$$;
}
let result = Cu.evalInSandbox(aString, this.sandbox, "1.8",
"Web Console", 1);
if ($) {
this.sandbox.$ = $;
}
if ($$) {
this.sandbox.$$ = $$;
}
return result;
},
/**
* Destroy the JSTerm instance.
*/
destroy: function JST_destroy()
{
Manager.removeMessageHandler("JSTerm:GetEvalObject");
Manager.removeMessageHandler("JSTerm:ClearObjectCache");
for (let name in this._messageHandlers) {
Manager.removeMessageHandler(name);
}
delete this.sandbox;
delete this._messageHandlers;
delete this._objectCache;
},
};

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -12,7 +12,6 @@ include $(DEPTH)/config/autoconf.mk
EXTRA_JS_MODULES = \
PropertyPanel.jsm \
PropertyPanelAsync.jsm \
NetworkHelper.jsm \
AutocompletePopup.jsm \
WebConsoleUtils.jsm \

Просмотреть файл

@ -4,6 +4,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
@ -11,242 +13,18 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
var EXPORTED_SYMBOLS = ["PropertyPanel", "PropertyTreeView",
"namesAndValuesOf", "isNonNativeGetter"];
XPCOMUtils.defineLazyGetter(this, "WebConsoleUtils", function () {
let obj = {};
Cu.import("resource:///modules/WebConsoleUtils.jsm", obj);
return obj.WebConsoleUtils;
});
///////////////////////////////////////////////////////////////////////////
//// Helper for PropertyTreeView
var EXPORTED_SYMBOLS = ["PropertyPanel", "PropertyTreeView"];
const TYPE_OBJECT = 0, TYPE_FUNCTION = 1, TYPE_ARRAY = 2, TYPE_OTHER = 3;
/**
* Figures out the type of aObject and the string to display in the tree.
*
* @param object aObject
* The object to operate on.
* @returns object
* A object with the form:
* {
* type: TYPE_OBJECT || TYPE_FUNCTION || TYPE_ARRAY || TYPE_OTHER,
* display: string for displaying the object in the tree
* }
*/
function presentableValueFor(aObject)
{
if (aObject === null || aObject === undefined) {
return {
type: TYPE_OTHER,
display: aObject === undefined ? "undefined" : "null"
};
}
let presentable;
switch (aObject.constructor && aObject.constructor.name) {
case "Array":
return {
type: TYPE_ARRAY,
display: "Array"
};
case "String":
return {
type: TYPE_OTHER,
display: "\"" + aObject + "\""
};
case "Date":
case "RegExp":
case "Number":
case "Boolean":
return {
type: TYPE_OTHER,
display: aObject
};
case "Iterator":
return {
type: TYPE_OTHER,
display: "Iterator"
};
case "Function":
presentable = aObject.toString();
return {
type: TYPE_FUNCTION,
display: presentable.substring(0, presentable.indexOf(')') + 1)
};
default:
presentable = aObject.toString();
let m = /^\[object (\S+)\]/.exec(presentable);
try {
if (typeof aObject == "object" && typeof aObject.next == "function" &&
m && m[1] == "Generator") {
return {
type: TYPE_OTHER,
display: m[1]
};
}
}
catch (ex) {
// window.history.next throws in the typeof check above.
return {
type: TYPE_OBJECT,
display: m ? m[1] : "Object"
};
}
if (typeof aObject == "object" && typeof aObject.__iterator__ == "function") {
return {
type: TYPE_OTHER,
display: "Iterator"
};
}
return {
type: TYPE_OBJECT,
display: m ? m[1] : "Object"
};
}
}
/**
* Tells if the given function is native or not.
*
* @param function aFunction
* The function you want to check if it is native or not.
*
* @return boolean
* True if the given function is native, false otherwise.
*/
function isNativeFunction(aFunction)
{
return typeof aFunction == "function" && !("prototype" in aFunction);
}
/**
* Tells if the given property of the provided object is a non-native getter or
* not.
*
* @param object aObject
* The object that contains the property.
*
* @param string aProp
* The property you want to check if it is a getter or not.
*
* @return boolean
* True if the given property is a getter, false otherwise.
*/
function isNonNativeGetter(aObject, aProp) {
if (typeof aObject != "object") {
return false;
}
let desc;
while (aObject) {
try {
if (desc = Object.getOwnPropertyDescriptor(aObject, aProp)) {
break;
}
}
catch (ex) {
// Native getters throw here. See bug 520882.
if (ex.name == "NS_ERROR_XPC_BAD_CONVERT_JS" ||
ex.name == "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO") {
return false;
}
throw ex;
}
aObject = Object.getPrototypeOf(aObject);
}
if (desc && desc.get && !isNativeFunction(desc.get)) {
return true;
}
return false;
}
/**
* Get an array of property name value pairs for the tree.
*
* @param object aObject
* The object to get properties for.
* @returns array of object
* Objects have the name, value, display, type, children properties.
*/
function namesAndValuesOf(aObject)
{
let pairs = [];
let value, presentable;
let isDOMDocument = aObject instanceof Ci.nsIDOMDocument;
for (var propName in aObject) {
// See bug 632275: skip deprecated width and height properties.
if (isDOMDocument && (propName == "width" || propName == "height")) {
continue;
}
// Also skip non-native getters.
if (isNonNativeGetter(aObject, propName)) {
value = ""; // Value is never displayed.
presentable = {type: TYPE_OTHER, display: "Getter"};
}
else {
try {
value = aObject[propName];
presentable = presentableValueFor(value);
}
catch (ex) {
continue;
}
}
let pair = {};
pair.name = propName;
pair.display = propName + ": " + presentable.display;
pair.type = presentable.type;
pair.value = value;
// Convert the pair.name to a number for later sorting.
pair.nameNumber = parseFloat(pair.name)
if (isNaN(pair.nameNumber)) {
pair.nameNumber = false;
}
pairs.push(pair);
}
pairs.sort(function(a, b)
{
// Sort numbers.
if (a.nameNumber !== false && b.nameNumber === false) {
return -1;
}
else if (a.nameNumber === false && b.nameNumber !== false) {
return 1;
}
else if (a.nameNumber !== false && b.nameNumber !== false) {
return a.nameNumber - b.nameNumber;
}
// Sort string.
else if (a.name < b.name) {
return -1;
}
else if (a.name > b.name) {
return 1;
}
else {
return 0;
}
});
return pairs;
}
///////////////////////////////////////////////////////////////////////////
//// PropertyTreeView.
/**
* This is an implementation of the nsITreeView interface. For comments on the
* interface properties, see the documentation:
@ -254,30 +32,87 @@ function namesAndValuesOf(aObject)
*/
var PropertyTreeView = function() {
this._rows = [];
this._objectCache = {};
};
PropertyTreeView.prototype = {
/**
* Stores the visible rows of the tree.
* @private
*/
_rows: null,
/**
* Stores the nsITreeBoxObject for this tree.
* @private
*/
_treeBox: null,
/**
* Stores cached information about local objects being inspected.
* @private
*/
_objectCache: null,
/**
* Use this setter to update the content of the tree.
*
* @param object aObject
* The new object to be displayed in the tree.
* @returns void
* @param object aData
* A meta object that holds information about the object you want to
* display in the property panel. Object properties:
* - object:
* This is the raw object you want to display. You can only provide
* this object if you want the property panel to work in sync mode.
* - remoteObject:
* An array that holds information on the remote object being
* inspected. Each element in this array describes each property in the
* remote object. See WebConsoleUtils.namesAndValuesOf() for details.
* - rootCacheId:
* The cache ID where the objects referenced in remoteObject are found.
* - panelCacheId:
* The cache ID where any object retrieved by this property panel
* instance should be stored into.
* - remoteObjectProvider:
* A function that is invoked when a new object is needed. This is
* called when the user tries to expand an inspectable property. The
* callback must take four arguments:
* - fromCacheId:
* Tells from where to retrieve the object the user picked (from
* which cache ID).
* - objectId:
* The object ID the user wants.
* - panelCacheId:
* Tells in which cache ID to store the objects referenced by
* objectId so they can be retrieved later.
* - callback:
* The callback function to be invoked when the remote object is
* received. This function takes one argument: the raw message
* received from the Web Console content script.
*/
set data(aObject) {
set data(aData) {
let oldLen = this._rows.length;
this._rows = this.getChildItems(aObject, true);
this._cleanup();
if (!aData) {
return;
}
if (aData.remoteObject) {
this._rootCacheId = aData.rootCacheId;
this._panelCacheId = aData.panelCacheId;
this._remoteObjectProvider = aData.remoteObjectProvider;
this._rows = [].concat(aData.remoteObject);
this._updateRemoteObject(this._rows, 0);
}
else if (aData.object) {
this._rows = this._inspectObject(aData.object);
}
else {
throw new Error("First argument must have a .remoteObject or " +
"an .object property!");
}
if (this._treeBox) {
this._treeBox.beginUpdateBatch();
if (oldLen) {
@ -289,53 +124,66 @@ PropertyTreeView.prototype = {
},
/**
* Generates the child items for the treeView of a given aItem. If there is
* already a children property on the aItem, this cached one is returned.
* Update a remote object so it can be used with the tree view. This method
* adds properties to each array element.
*
* @param object aItem
* An item of the tree's elements to generate the children for.
* @param boolean aRootElement
* If set, aItem is handled as an JS object and not as an item
* element of the tree.
* @returns array of objects
* Child items of aItem.
* @private
* @param array aObject
* The remote object you want prepared for use with the tree view.
* @param number aLevel
* The level you want to give to each property in the remote object.
*/
getChildItems: function(aItem, aRootElement)
_updateRemoteObject: function PTV__updateRemoteObject(aObject, aLevel)
{
// If item.children is an array, then the children has already been
// computed and can get returned directly.
// Skip this checking if aRootElement is true. It could happen, that aItem
// is passed as ({children:[1,2,3]}) which would be true, although these
// "kind" of children has no value/type etc. data as needed to display in
// the tree. As the passed ({children:[1,2,3]}) are instanceof
// itsWindow.Array and not this modules's global Array
// aItem.children instanceof Array can't be true, but for saftey the
// !aRootElement is kept here.
if (!aRootElement && aItem && aItem.children instanceof Array) {
return aItem.children;
}
aObject.forEach(function(aElement) {
aElement.level = aLevel;
aElement.isOpened = false;
aElement.children = null;
});
},
let pairs;
let newPairLevel;
/**
* Inspect a local object.
*
* @private
* @param object aObject
* The object you want to inspect.
*/
_inspectObject: function PTV__inspectObject(aObject)
{
this._objectCache = {};
this._remoteObjectProvider = this._localObjectProvider.bind(this);
let children = WebConsoleUtils.namesAndValuesOf(aObject, this._objectCache);
this._updateRemoteObject(children, 0);
return children;
},
if (!aRootElement) {
newPairLevel = aItem.level + 1;
aItem = aItem.value;
}
else {
newPairLevel = 0;
}
pairs = namesAndValuesOf(aItem);
for each (var pair in pairs) {
pair.level = newPairLevel;
pair.isOpened = false;
pair.children = pair.type == TYPE_OBJECT || pair.type == TYPE_FUNCTION ||
pair.type == TYPE_ARRAY;
}
return pairs;
/**
* An object provider for when the user inspects local objects (not remote
* ones).
*
* @private
* @param string aFromCacheId
* The cache ID from where to retrieve the desired object.
* @param string aObjectId
* The ID of the object you want.
* @param string aDestCacheId
* The ID of the cache where to store any objects referenced by the
* desired object.
* @param function aCallback
* The function you want to receive the object.
*/
_localObjectProvider:
function PTV__localObjectProvider(aFromCacheId, aObjectId, aDestCacheId,
aCallback)
{
let object = WebConsoleUtils.namesAndValuesOf(this._objectCache[aObjectId],
this._objectCache);
aCallback({cacheId: aFromCacheId,
objectId: aObjectId,
object: object,
childrenCacheId: aDestCacheId || aFromCacheId,
});
},
/** nsITreeView interface implementation **/
@ -344,10 +192,19 @@ PropertyTreeView.prototype = {
get rowCount() { return this._rows.length; },
setTree: function(treeBox) { this._treeBox = treeBox; },
getCellText: function(idx, column) { return this._rows[idx].display; },
getLevel: function(idx) { return this._rows[idx].level; },
isContainer: function(idx) { return !!this._rows[idx].children; },
isContainerOpen: function(idx) { return this._rows[idx].isOpened; },
getCellText: function(idx, column) {
let row = this._rows[idx];
return row.name + ": " + row.value;
},
getLevel: function(idx) {
return this._rows[idx].level;
},
isContainer: function(idx) {
return !!this._rows[idx].inspectable;
},
isContainerOpen: function(idx) {
return this._rows[idx].isOpened;
},
isContainerEmpty: function(idx) { return false; },
isSeparator: function(idx) { return false; },
isSorted: function() { return false; },
@ -359,7 +216,7 @@ PropertyTreeView.prototype = {
if (this.getLevel(idx) == 0) {
return -1;
}
for (var t = idx - 1; t >= 0 ; t--) {
for (var t = idx - 1; t >= 0; t--) {
if (this.isContainer(t)) {
return t;
}
@ -375,13 +232,13 @@ PropertyTreeView.prototype = {
toggleOpenState: function(idx)
{
var item = this._rows[idx];
if (!item.children) {
let item = this._rows[idx];
if (!item.inspectable) {
return;
}
this._treeBox.beginUpdateBatch();
if (item.isOpened) {
this._treeBox.beginUpdateBatch();
item.isOpened = false;
var thisLevel = item.level;
@ -394,18 +251,38 @@ PropertyTreeView.prototype = {
this._rows.splice(idx + 1, deleteCount);
this._treeBox.rowCountChanged(idx + 1, -deleteCount);
}
this._treeBox.invalidateRow(idx);
this._treeBox.endUpdateBatch();
}
else {
item.isOpened = true;
let levelUpdate = true;
let callback = function _onRemoteResponse(aResponse) {
this._treeBox.beginUpdateBatch();
item.isOpened = true;
var toInsert = this.getChildItems(item);
item.children = toInsert;
this._rows.splice.apply(this._rows, [idx + 1, 0].concat(toInsert));
if (levelUpdate) {
this._updateRemoteObject(aResponse.object, item.level + 1);
item.children = aResponse.object;
}
this._treeBox.rowCountChanged(idx + 1, toInsert.length);
this._rows.splice.apply(this._rows, [idx + 1, 0].concat(item.children));
this._treeBox.rowCountChanged(idx + 1, item.children.length);
this._treeBox.invalidateRow(idx);
this._treeBox.endUpdateBatch();
}.bind(this);
if (!item.children) {
let fromCacheId = item.level > 0 ? this._panelCacheId :
this._rootCacheId;
this._remoteObjectProvider(fromCacheId, item.objectId,
this._panelCacheId, callback);
}
else {
levelUpdate = false;
callback({object: item.children});
}
}
this._treeBox.invalidateRow(idx);
this._treeBox.endUpdateBatch();
},
getImageSrc: function(idx, column) { },
@ -424,7 +301,21 @@ PropertyTreeView.prototype = {
setCellValue: function(row, col, value) { },
setCellText: function(row, col, value) { },
drop: function(index, orientation, dataTransfer) { },
canDrop: function(index, orientation, dataTransfer) { return false; }
canDrop: function(index, orientation, dataTransfer) { return false; },
_cleanup: function PTV__cleanup()
{
if (this._rows.length) {
// Reset the existing _rows children to the initial state.
this._updateRemoteObject(this._rows, 0);
this._rows = [];
}
delete this._objectCache;
delete this._rootCacheId;
delete this._panelCacheId;
delete this._remoteObjectProvider;
},
};
///////////////////////////////////////////////////////////////////////////
@ -477,21 +368,23 @@ function appendChild(aDocument, aParent, aTag, aAttributes)
/**
* Creates a new PropertyPanel.
*
* @see PropertyTreeView
* @param nsIDOMNode aParent
* Parent node to append the created panel to.
* @param nsIDOMDocument aDocument
* Document to create the new nodes on.
* @param string aTitle
* Title for the panel.
* @param string aObject
* Object to display in the tree.
* Object to display in the tree. For details about this object please
* see the PropertyTreeView constructor in this file.
* @param array of objects aButtons
* Array with buttons to display at the bottom of the panel.
*/
function PropertyPanel(aParent, aDocument, aTitle, aObject, aButtons)
function PropertyPanel(aParent, aTitle, aObject, aButtons)
{
let document = aParent.ownerDocument;
// Create the underlying panel
this.panel = createElement(aDocument, "panel", {
this.panel = createElement(document, "panel", {
label: aTitle,
titlebar: "normal",
noautofocus: "true",
@ -500,13 +393,13 @@ function PropertyPanel(aParent, aDocument, aTitle, aObject, aButtons)
});
// Create the tree.
let tree = this.tree = createElement(aDocument, "tree", {
let tree = this.tree = createElement(document, "tree", {
flex: 1,
hidecolumnpicker: "true"
});
let treecols = aDocument.createElement("treecols");
appendChild(aDocument, treecols, "treecol", {
let treecols = document.createElement("treecols");
appendChild(document, treecols, "treecol", {
primary: "true",
flex: 1,
hideheader: "true",
@ -514,18 +407,18 @@ function PropertyPanel(aParent, aDocument, aTitle, aObject, aButtons)
});
tree.appendChild(treecols);
tree.appendChild(aDocument.createElement("treechildren"));
tree.appendChild(document.createElement("treechildren"));
this.panel.appendChild(tree);
// Create the footer.
let footer = createElement(aDocument, "hbox", { align: "end" });
appendChild(aDocument, footer, "spacer", { flex: 1 });
let footer = createElement(document, "hbox", { align: "end" });
appendChild(document, footer, "spacer", { flex: 1 });
// The footer can have butttons.
let self = this;
if (aButtons) {
aButtons.forEach(function(button) {
let buttonNode = appendChild(aDocument, footer, "button", {
let buttonNode = appendChild(document, footer, "button", {
label: button.label,
accesskey: button.accesskey || "",
class: button.class || "",
@ -534,7 +427,7 @@ function PropertyPanel(aParent, aDocument, aTitle, aObject, aButtons)
});
}
appendChild(aDocument, footer, "resizer", { dir: "bottomend" });
appendChild(document, footer, "resizer", { dir: "bottomend" });
this.panel.appendChild(footer);
aParent.appendChild(this.panel);
@ -559,20 +452,15 @@ function PropertyPanel(aParent, aDocument, aTitle, aObject, aButtons)
}
/**
* Destroy the PropertyPanel. This closes the poped up panel and removes
* it from the browser DOM.
*
* @returns void
* Destroy the PropertyPanel. This closes the panel and removes it from the
* browser DOM.
*/
PropertyPanel.prototype.destroy = function PP_destroy()
{
this.treeView.data = null;
this.panel.parentNode.removeChild(this.panel);
this.treeView = null;
this.panel = null;
this.tree = null;
if (this.linkNode) {
this.linkNode._panelOpen = false;
this.linkNode = null;
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше