mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 20:35:50 +00:00
Merge from mozilla-central.
This commit is contained in:
commit
fc580d8237
1
.hgtags
1
.hgtags
@ -78,3 +78,4 @@ bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
|
||||
0000000000000000000000000000000000000000 AURORA_BASE_20120131
|
||||
0000000000000000000000000000000000000000 AURORA_BASE_20120131
|
||||
bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
|
||||
b6627f28b7ec17e1b46a594df0f780d3a40847e4 FIREFOX_AURORA_13_BASE
|
||||
|
@ -59,7 +59,7 @@ interface nsIAccessibleRelation;
|
||||
* Mozilla creates the implementations of nsIAccessible on demand.
|
||||
* See http://www.mozilla.org/projects/ui/accessibility for more information.
|
||||
*/
|
||||
[scriptable, uuid(3126544c-826c-4694-a2ed-67bfe56a1f37)]
|
||||
[scriptable, uuid(e7c44e0d-736e-4ead-afee-b51f4b574020)]
|
||||
interface nsIAccessible : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -110,26 +110,6 @@ interface nsIAccessible : nsISupports
|
||||
*/
|
||||
readonly attribute DOMString innerHTML;
|
||||
|
||||
/**
|
||||
* Retrieve the computed style value for this DOM node, if it is a DOM element.
|
||||
* Note: the meanings of width, height and other size measurements depend
|
||||
* on the version of CSS being used. Therefore, for bounds information,
|
||||
* it is better to use nsIAccessible::accGetBounds.
|
||||
*
|
||||
* @param pseudoElt [in] The pseudo element to retrieve style for, or NULL
|
||||
* for general computed style information for this node.
|
||||
* @param propertyName [in] Retrieve the computed style value for this property name,
|
||||
* for example "border-bottom".
|
||||
*/
|
||||
DOMString getComputedStyleValue(in DOMString pseudoElt, in DOMString propertyName);
|
||||
|
||||
/**
|
||||
* The method is similar to getComputedStyleValue() excepting that this one
|
||||
* returns nsIDOMCSSPrimitiveValue.
|
||||
*/
|
||||
nsIDOMCSSPrimitiveValue getComputedStyleCSSValue(in DOMString pseudoElt,
|
||||
in DOMString propertyName);
|
||||
|
||||
/**
|
||||
* The DOM node this nsIAccessible is associated with.
|
||||
*/
|
||||
|
@ -66,6 +66,8 @@ public:
|
||||
role != mozilla::a11y::roles::OPTION &&
|
||||
role != mozilla::a11y::roles::LISTITEM &&
|
||||
role != mozilla::a11y::roles::MENUITEM &&
|
||||
role != mozilla::a11y::roles::COMBOBOX_OPTION &&
|
||||
role != mozilla::a11y::roles::PARENT_MENUITEM &&
|
||||
role != mozilla::a11y::roles::CHECK_MENU_ITEM &&
|
||||
role != mozilla::a11y::roles::RADIO_MENU_ITEM &&
|
||||
role != mozilla::a11y::roles::RADIOBUTTON &&
|
||||
@ -83,6 +85,7 @@ private:
|
||||
static mozilla::a11y::role BaseRole(mozilla::a11y::role aRole)
|
||||
{
|
||||
if (aRole == mozilla::a11y::roles::CHECK_MENU_ITEM ||
|
||||
aRole == mozilla::a11y::roles::PARENT_MENUITEM ||
|
||||
aRole == mozilla::a11y::roles::RADIO_MENU_ITEM)
|
||||
return mozilla::a11y::roles::MENUITEM;
|
||||
return aRole;
|
||||
|
@ -51,32 +51,6 @@
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Constants and structures
|
||||
|
||||
/**
|
||||
* Item of the gCSSTextAttrsMap map.
|
||||
*/
|
||||
struct nsCSSTextAttrMapItem
|
||||
{
|
||||
const char* mCSSName;
|
||||
const char* mCSSValue;
|
||||
nsIAtom** mAttrName;
|
||||
const char* mAttrValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* The map of CSS properties to text attributes.
|
||||
*/
|
||||
const char* const kAnyValue = nsnull;
|
||||
const char* const kCopyValue = nsnull;
|
||||
|
||||
static nsCSSTextAttrMapItem gCSSTextAttrsMap[] =
|
||||
{
|
||||
// CSS name CSS value Attribute name Attribute value
|
||||
{ "vertical-align", kAnyValue, &nsGkAtoms::textPosition, kCopyValue }
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TextAttrsMgr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -139,62 +113,61 @@ TextAttrsMgr::GetAttributes(nsIPersistentProperties* aAttributes,
|
||||
frame = offsetElm->GetPrimaryFrame();
|
||||
}
|
||||
|
||||
nsTArray<TextAttr*> textAttrArray(9);
|
||||
|
||||
// "language" text attribute
|
||||
LangTextAttr langTextAttr(mHyperTextAcc, hyperTextElm, offsetNode);
|
||||
textAttrArray.AppendElement(&langTextAttr);
|
||||
|
||||
// "text-position" text attribute
|
||||
CSSTextAttr posTextAttr(0, hyperTextElm, offsetElm);
|
||||
textAttrArray.AppendElement(&posTextAttr);
|
||||
|
||||
// "background-color" text attribute
|
||||
BGColorTextAttr bgColorTextAttr(rootFrame, frame);
|
||||
textAttrArray.AppendElement(&bgColorTextAttr);
|
||||
|
||||
// "color" text attribute
|
||||
ColorTextAttr colorTextAttr(rootFrame, frame);
|
||||
textAttrArray.AppendElement(&colorTextAttr);
|
||||
|
||||
// "font-family" text attribute
|
||||
FontFamilyTextAttr fontFamilyTextAttr(rootFrame, frame);
|
||||
textAttrArray.AppendElement(&fontFamilyTextAttr);
|
||||
|
||||
// "font-size" text attribute
|
||||
FontSizeTextAttr fontSizeTextAttr(rootFrame, frame);
|
||||
textAttrArray.AppendElement(&fontSizeTextAttr);
|
||||
|
||||
// "font-style" text attribute
|
||||
FontStyleTextAttr fontStyleTextAttr(rootFrame, frame);
|
||||
textAttrArray.AppendElement(&fontStyleTextAttr);
|
||||
|
||||
// "font-weight" text attribute
|
||||
FontWeightTextAttr fontWeightTextAttr(rootFrame, frame);
|
||||
textAttrArray.AppendElement(&fontWeightTextAttr);
|
||||
|
||||
// "text-underline(line-through)-style(color)" text attributes
|
||||
TextDecorTextAttr textDecorTextAttr(rootFrame, frame);
|
||||
textAttrArray.AppendElement(&textDecorTextAttr);
|
||||
|
||||
// "text-position" text attribute
|
||||
TextPosTextAttr textPosTextAttr(rootFrame, frame);
|
||||
|
||||
TextAttr* attrArray[] =
|
||||
{
|
||||
&langTextAttr,
|
||||
&bgColorTextAttr,
|
||||
&colorTextAttr,
|
||||
&fontFamilyTextAttr,
|
||||
&fontSizeTextAttr,
|
||||
&fontStyleTextAttr,
|
||||
&fontWeightTextAttr,
|
||||
&textDecorTextAttr,
|
||||
&textPosTextAttr
|
||||
};
|
||||
|
||||
// Expose text attributes if applicable.
|
||||
if (aAttributes) {
|
||||
PRUint32 len = textAttrArray.Length();
|
||||
for (PRUint32 idx = 0; idx < len; idx++)
|
||||
textAttrArray[idx]->Expose(aAttributes, mIncludeDefAttrs);
|
||||
for (PRUint32 idx = 0; idx < ArrayLength(attrArray); idx++)
|
||||
attrArray[idx]->Expose(aAttributes, mIncludeDefAttrs);
|
||||
}
|
||||
|
||||
// Expose text attributes range where they are applied if applicable.
|
||||
if (mOffsetAcc)
|
||||
GetRange(textAttrArray, aStartHTOffset, aEndHTOffset);
|
||||
GetRange(attrArray, ArrayLength(attrArray), aStartHTOffset, aEndHTOffset);
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::GetRange(const nsTArray<TextAttr*>& aTextAttrArray,
|
||||
TextAttrsMgr::GetRange(TextAttr* aAttrArray[], PRUint32 aAttrArrayLen,
|
||||
PRInt32* aStartHTOffset, PRInt32* aEndHTOffset)
|
||||
{
|
||||
PRUint32 attrLen = aTextAttrArray.Length();
|
||||
|
||||
// Navigate backward from anchor accessible to find start offset.
|
||||
for (PRInt32 childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
|
||||
nsAccessible *currAcc = mHyperTextAcc->GetChildAt(childIdx);
|
||||
@ -209,8 +182,8 @@ TextAttrsMgr::GetRange(const nsTArray<TextAttr*>& aTextAttrArray,
|
||||
return;
|
||||
|
||||
bool offsetFound = false;
|
||||
for (PRUint32 attrIdx = 0; attrIdx < attrLen; attrIdx++) {
|
||||
TextAttr* textAttr = aTextAttrArray[attrIdx];
|
||||
for (PRUint32 attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
|
||||
TextAttr* textAttr = aAttrArray[attrIdx];
|
||||
if (!textAttr->Equal(currElm)) {
|
||||
offsetFound = true;
|
||||
break;
|
||||
@ -235,8 +208,8 @@ TextAttrsMgr::GetRange(const nsTArray<TextAttr*>& aTextAttrArray,
|
||||
return;
|
||||
|
||||
bool offsetFound = false;
|
||||
for (PRUint32 attrIdx = 0; attrIdx < attrLen; attrIdx++) {
|
||||
TextAttr* textAttr = aTextAttrArray[attrIdx];
|
||||
for (PRUint32 attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
|
||||
TextAttr* textAttr = aAttrArray[attrIdx];
|
||||
|
||||
// Alter the end offset when text attribute changes its value and stop
|
||||
// the search.
|
||||
@ -293,60 +266,6 @@ TextAttrsMgr::LangTextAttr::
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// CSSTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::CSSTextAttr::
|
||||
CSSTextAttr(PRUint32 aIndex, nsIContent* aRootElm, nsIContent* aElm) :
|
||||
TTextAttr<nsString>(!aElm), mIndex(aIndex)
|
||||
{
|
||||
mIsRootDefined = GetValueFor(aRootElm, &mRootNativeValue);
|
||||
|
||||
if (aElm)
|
||||
mIsDefined = GetValueFor(aElm, &mNativeValue);
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::CSSTextAttr::
|
||||
GetValueFor(nsIContent* aElm, nsString* aValue)
|
||||
{
|
||||
nsCOMPtr<nsIDOMCSSStyleDeclaration> currStyleDecl =
|
||||
nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), aElm);
|
||||
if (!currStyleDecl)
|
||||
return false;
|
||||
|
||||
NS_ConvertASCIItoUTF16 cssName(gCSSTextAttrsMap[mIndex].mCSSName);
|
||||
|
||||
nsresult rv = currStyleDecl->GetPropertyValue(cssName, *aValue);
|
||||
if (NS_FAILED(rv))
|
||||
return true;
|
||||
|
||||
const char *cssValue = gCSSTextAttrsMap[mIndex].mCSSValue;
|
||||
if (cssValue != kAnyValue && !aValue->EqualsASCII(cssValue))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::CSSTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const nsString& aValue)
|
||||
{
|
||||
const char* attrValue = gCSSTextAttrsMap[mIndex].mAttrValue;
|
||||
if (attrValue != kCopyValue) {
|
||||
nsAutoString formattedValue;
|
||||
AppendASCIItoUTF16(attrValue, formattedValue);
|
||||
nsAccUtils::SetAccAttr(aAttributes, *gCSSTextAttrsMap[mIndex].mAttrName,
|
||||
formattedValue);
|
||||
return;
|
||||
}
|
||||
|
||||
nsAccUtils::SetAccAttr(aAttributes, *gCSSTextAttrsMap[mIndex].mAttrName,
|
||||
aValue);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// BGColorTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -742,3 +661,104 @@ TextAttrsMgr::TextDecorTextAttr::
|
||||
formattedColor);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TextPosTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::TextPosTextAttr::
|
||||
TextPosTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
|
||||
TTextAttr<TextPosValue>(!aFrame)
|
||||
{
|
||||
mRootNativeValue = GetTextPosValue(aRootFrame);
|
||||
mIsRootDefined = mRootNativeValue != eTextPosNone;
|
||||
|
||||
if (aFrame) {
|
||||
mNativeValue = GetTextPosValue(aFrame);
|
||||
mIsDefined = mNativeValue != eTextPosNone;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::TextPosTextAttr::
|
||||
GetValueFor(nsIContent* aContent, TextPosValue* aValue)
|
||||
{
|
||||
nsIFrame* frame = aContent->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
*aValue = GetTextPosValue(frame);
|
||||
return *aValue != eTextPosNone;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::TextPosTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const TextPosValue& aValue)
|
||||
{
|
||||
switch (aValue) {
|
||||
case eTextPosBaseline:
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
|
||||
NS_LITERAL_STRING("baseline"));
|
||||
break;
|
||||
|
||||
case eTextPosSub:
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
|
||||
NS_LITERAL_STRING("sub"));
|
||||
break;
|
||||
|
||||
case eTextPosSuper:
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
|
||||
NS_LITERAL_STRING("super"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TextAttrsMgr::TextPosValue
|
||||
TextAttrsMgr::TextPosTextAttr::
|
||||
GetTextPosValue(nsIFrame* aFrame) const
|
||||
{
|
||||
const nsStyleCoord& styleCoord = aFrame->GetStyleTextReset()->mVerticalAlign;
|
||||
switch (styleCoord.GetUnit()) {
|
||||
case eStyleUnit_Enumerated:
|
||||
switch (styleCoord.GetIntValue()) {
|
||||
case NS_STYLE_VERTICAL_ALIGN_BASELINE:
|
||||
return eTextPosBaseline;
|
||||
case NS_STYLE_VERTICAL_ALIGN_SUB:
|
||||
return eTextPosSub;
|
||||
case NS_STYLE_VERTICAL_ALIGN_SUPER:
|
||||
return eTextPosSuper;
|
||||
|
||||
// No good guess for these:
|
||||
// NS_STYLE_VERTICAL_ALIGN_TOP
|
||||
// NS_STYLE_VERTICAL_ALIGN_TEXT_TOP
|
||||
// NS_STYLE_VERTICAL_ALIGN_MIDDLE
|
||||
// NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM
|
||||
// NS_STYLE_VERTICAL_ALIGN_BOTTOM
|
||||
// NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE
|
||||
// Do not expose value of text-position attribute.
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return eTextPosNone;
|
||||
|
||||
case eStyleUnit_Percent:
|
||||
{
|
||||
float percentValue = styleCoord.GetPercentValue();
|
||||
return percentValue > 0 ?
|
||||
eTextPosSuper :
|
||||
(percentValue < 0 ? eTextPosSub : eTextPosBaseline);
|
||||
}
|
||||
|
||||
case eStyleUnit_Coord:
|
||||
{
|
||||
nscoord coordValue = styleCoord.GetCoordValue();
|
||||
return coordValue > 0 ?
|
||||
eTextPosSuper :
|
||||
(coordValue < 0 ? eTextPosSub : eTextPosBaseline);
|
||||
}
|
||||
}
|
||||
|
||||
return eTextPosNone;
|
||||
}
|
||||
|
@ -107,11 +107,12 @@ protected:
|
||||
* its value before or after the given offsets.
|
||||
*
|
||||
* @param aTextAttrArray [in] text attributes array
|
||||
* @param aAttrArrayLen [in] text attributes array length
|
||||
* @param aStartHTOffset [in, out] the start offset
|
||||
* @param aEndHTOffset [in, out] the end offset
|
||||
*/
|
||||
class TextAttr;
|
||||
void GetRange(const nsTArray<TextAttr*>& aTextAttrArray,
|
||||
void GetRange(TextAttr* aAttrArray[], PRUint32 aAttrArrayLen,
|
||||
PRInt32* aStartHTOffset, PRInt32* aEndHTOffset);
|
||||
|
||||
private:
|
||||
@ -157,7 +158,7 @@ protected:
|
||||
public:
|
||||
TTextAttr(bool aGetRootValue) : mGetRootValue(aGetRootValue) {}
|
||||
|
||||
// ITextAttr
|
||||
// TextAttr
|
||||
virtual void Expose(nsIPersistentProperties* aAttributes,
|
||||
bool aIncludeDefAttrValue)
|
||||
{
|
||||
@ -242,27 +243,6 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class is used for the work with CSS based text attributes.
|
||||
*/
|
||||
class CSSTextAttr : public TTextAttr<nsString>
|
||||
{
|
||||
public:
|
||||
CSSTextAttr(PRUint32 aIndex, nsIContent* aRootElm, nsIContent* aElm);
|
||||
virtual ~CSSTextAttr() { }
|
||||
|
||||
protected:
|
||||
|
||||
// TextAttr
|
||||
virtual bool GetValueFor(nsIContent* aElm, nsString* aValue);
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const nsString& aValue);
|
||||
|
||||
private:
|
||||
PRInt32 mIndex;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class is used for the work with 'background-color' text attribute.
|
||||
*/
|
||||
@ -435,6 +415,34 @@ protected:
|
||||
const TextDecorValue& aValue);
|
||||
};
|
||||
|
||||
/**
|
||||
* Class is used for the work with "text-position" text attribute.
|
||||
*/
|
||||
|
||||
enum TextPosValue {
|
||||
eTextPosNone = 0,
|
||||
eTextPosBaseline,
|
||||
eTextPosSub,
|
||||
eTextPosSuper
|
||||
};
|
||||
|
||||
class TextPosTextAttr : public TTextAttr<TextPosValue>
|
||||
{
|
||||
public:
|
||||
TextPosTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
|
||||
virtual ~TextPosTextAttr() { }
|
||||
|
||||
protected:
|
||||
|
||||
// TextAttr
|
||||
virtual bool GetValueFor(nsIContent* aElm, TextPosValue* aValue);
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const TextPosValue& aValue);
|
||||
|
||||
private:
|
||||
TextPosValue GetTextPosValue(nsIFrame* aFrame) const;
|
||||
};
|
||||
|
||||
}; // TextAttrMgr
|
||||
|
||||
} // namespace a11y
|
||||
|
@ -175,70 +175,6 @@ nsAccUtils::GetPositionAndSizeForXULSelectControlItem(nsIContent *aContent,
|
||||
(*aPosInSet)++; // group position is 1-index based.
|
||||
}
|
||||
|
||||
void
|
||||
nsAccUtils::GetPositionAndSizeForXULContainerItem(nsIContent *aContent,
|
||||
PRInt32 *aPosInSet,
|
||||
PRInt32 *aSetSize)
|
||||
{
|
||||
nsCOMPtr<nsIDOMXULContainerItemElement> item(do_QueryInterface(aContent));
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIDOMXULContainerElement> container;
|
||||
item->GetParentContainer(getter_AddRefs(container));
|
||||
if (!container)
|
||||
return;
|
||||
|
||||
// Get item count.
|
||||
PRUint32 itemsCount = 0;
|
||||
container->GetItemCount(&itemsCount);
|
||||
|
||||
// Get item index.
|
||||
PRInt32 indexOf = 0;
|
||||
container->GetIndexOfItem(item, &indexOf);
|
||||
|
||||
// Calculate set size and position in the set.
|
||||
*aSetSize = 0, *aPosInSet = 0;
|
||||
for (PRInt32 index = indexOf; index >= 0; index--) {
|
||||
nsCOMPtr<nsIDOMXULElement> item;
|
||||
container->GetItemAtIndex(index, getter_AddRefs(item));
|
||||
nsCOMPtr<nsINode> itemNode(do_QueryInterface(item));
|
||||
|
||||
nsAccessible* itemAcc = itemNode ?
|
||||
GetAccService()->GetAccessible(itemNode, nsnull) : nsnull;
|
||||
|
||||
if (itemAcc) {
|
||||
PRUint32 itemRole = Role(itemAcc);
|
||||
if (itemRole == nsIAccessibleRole::ROLE_SEPARATOR)
|
||||
break; // We reached the beginning of our group.
|
||||
|
||||
if (!(itemAcc->State() & states::INVISIBLE)) {
|
||||
(*aSetSize)++;
|
||||
(*aPosInSet)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (PRInt32 index = indexOf + 1; index < static_cast<PRInt32>(itemsCount);
|
||||
index++) {
|
||||
nsCOMPtr<nsIDOMXULElement> item;
|
||||
container->GetItemAtIndex(index, getter_AddRefs(item));
|
||||
nsCOMPtr<nsINode> itemNode(do_QueryInterface(item));
|
||||
|
||||
nsAccessible* itemAcc =
|
||||
itemNode ? GetAccService()->GetAccessible(itemNode, nsnull) : nsnull;
|
||||
|
||||
if (itemAcc) {
|
||||
PRUint32 itemRole = Role(itemAcc);
|
||||
if (itemRole == nsIAccessibleRole::ROLE_SEPARATOR)
|
||||
break; // We reached the end of our group.
|
||||
|
||||
if (!(itemAcc->State() & states::INVISIBLE))
|
||||
(*aSetSize)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsAccUtils::GetLevelForXULContainerItem(nsIContent *aContent)
|
||||
{
|
||||
|
@ -115,14 +115,6 @@ public:
|
||||
PRInt32 *aPosInSet,
|
||||
PRInt32 *aSetSize);
|
||||
|
||||
/**
|
||||
* Compute group position and group size (posinset and setsize) for
|
||||
* nsIDOMXULContainerItemElement node.
|
||||
*/
|
||||
static void GetPositionAndSizeForXULContainerItem(nsIContent *aContent,
|
||||
PRInt32 *aPosInSet,
|
||||
PRInt32 *aSetSize);
|
||||
|
||||
/**
|
||||
* Compute group level for nsIDOMXULContainerItemElement node.
|
||||
*/
|
||||
|
@ -231,42 +231,6 @@ nsAccessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
|
||||
mRoleMapEntry = aRoleMapEntry;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessible::GetComputedStyleValue(const nsAString& aPseudoElt,
|
||||
const nsAString& aPropertyName,
|
||||
nsAString& aValue)
|
||||
{
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl =
|
||||
nsCoreUtils::GetComputedStyleDeclaration(aPseudoElt, mContent);
|
||||
NS_ENSURE_TRUE(styleDecl, NS_ERROR_FAILURE);
|
||||
|
||||
return styleDecl->GetPropertyValue(aPropertyName, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessible::GetComputedStyleCSSValue(const nsAString& aPseudoElt,
|
||||
const nsAString& aPropertyName,
|
||||
nsIDOMCSSPrimitiveValue **aCSSValue) {
|
||||
NS_ENSURE_ARG_POINTER(aCSSValue);
|
||||
*aCSSValue = nsnull;
|
||||
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl =
|
||||
nsCoreUtils::GetComputedStyleDeclaration(aPseudoElt, mContent);
|
||||
NS_ENSURE_STATE(styleDecl);
|
||||
|
||||
nsCOMPtr<nsIDOMCSSValue> cssValue;
|
||||
styleDecl->GetPropertyCSSValue(aPropertyName, getter_AddRefs(cssValue));
|
||||
NS_ENSURE_TRUE(cssValue, NS_ERROR_FAILURE);
|
||||
|
||||
return CallQueryInterface(cssValue, aCSSValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessible::GetDocument(nsIAccessibleDocument **aDocument)
|
||||
{
|
||||
|
@ -467,24 +467,6 @@ nsApplicationAccessible::ScrollToPoint(PRUint32 aCoordinateType,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationAccessible::GetComputedStyleValue(const nsAString &aPseudoElt,
|
||||
const nsAString &aPropertyName,
|
||||
nsAString &aValue)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationAccessible::GetComputedStyleCSSValue(const nsAString &aPseudoElt,
|
||||
const nsAString &aPropertyName,
|
||||
nsIDOMCSSPrimitiveValue **aCSSPrimitiveValue)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCSSPrimitiveValue);
|
||||
*aCSSPrimitiveValue = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationAccessible::GetLanguage(nsAString &aLanguage)
|
||||
{
|
||||
|
@ -76,12 +76,6 @@ public:
|
||||
NS_SCRIPTABLE NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML);
|
||||
NS_SCRIPTABLE NS_IMETHOD ScrollTo(PRUint32 aScrollType);
|
||||
NS_SCRIPTABLE NS_IMETHOD ScrollToPoint(PRUint32 aCoordinateType, PRInt32 aX, PRInt32 aY);
|
||||
NS_SCRIPTABLE NS_IMETHOD GetComputedStyleValue(const nsAString& aPseudoElt,
|
||||
const nsAString& aPropertyName,
|
||||
nsAString& aValue NS_OUTPARAM);
|
||||
NS_SCRIPTABLE NS_IMETHOD GetComputedStyleCSSValue(const nsAString& aPseudoElt,
|
||||
const nsAString& aPropertyName,
|
||||
nsIDOMCSSPrimitiveValue** aValue NS_OUTPARAM);
|
||||
NS_SCRIPTABLE NS_IMETHOD GetLanguage(nsAString& aLanguage);
|
||||
NS_IMETHOD GetParent(nsIAccessible **aParent);
|
||||
NS_IMETHOD GetNextSibling(nsIAccessible **aNextSibling);
|
||||
|
@ -570,26 +570,6 @@ nsCoreUtils::GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent,
|
||||
walkUp = walkUp->GetParent();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDOMCSSStyleDeclaration>
|
||||
nsCoreUtils::GetComputedStyleDeclaration(const nsAString& aPseudoElt,
|
||||
nsIContent *aContent)
|
||||
{
|
||||
nsIContent* content = GetDOMElementFor(aContent);
|
||||
if (!content)
|
||||
return nsnull;
|
||||
|
||||
// Returns number of items in style declaration
|
||||
nsCOMPtr<nsIDOMWindow> window =
|
||||
do_QueryInterface(content->OwnerDoc()->GetWindow());
|
||||
if (!window)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
|
||||
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(content));
|
||||
window->GetComputedStyle(domElement, aPseudoElt, getter_AddRefs(cssDecl));
|
||||
return cssDecl.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIBoxObject>
|
||||
nsCoreUtils::GetTreeBodyBoxObject(nsITreeBoxObject *aTreeBoxObj)
|
||||
{
|
||||
|
@ -288,13 +288,6 @@ public:
|
||||
static void GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent,
|
||||
nsAString& aLanguage);
|
||||
|
||||
/**
|
||||
* Return computed styles declaration for the given node.
|
||||
*/
|
||||
static already_AddRefed<nsIDOMCSSStyleDeclaration>
|
||||
GetComputedStyleDeclaration(const nsAString& aPseudoElt,
|
||||
nsIContent *aContent);
|
||||
|
||||
/**
|
||||
* Return box object for XUL treechildren element by tree box object.
|
||||
*/
|
||||
|
@ -1441,10 +1441,27 @@ nsHTMLTableAccessible::IsProbablyForLayout(bool *aIsProbablyForLayout)
|
||||
if (rowElm->IsHTML() && rowElm->Tag() == nsGkAtoms::tr) {
|
||||
for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm;
|
||||
cellElm = cellElm->GetNextSibling()) {
|
||||
if (cellElm->IsHTML() && cellElm->Tag() == nsGkAtoms::th) {
|
||||
if (cellElm->IsHTML()) {
|
||||
|
||||
if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) {
|
||||
RETURN_LAYOUT_ANSWER(false,
|
||||
"Has th -- legitimate table structures");
|
||||
}
|
||||
|
||||
if (cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) ||
|
||||
cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) ||
|
||||
cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) {
|
||||
RETURN_LAYOUT_ANSWER(false,
|
||||
"Has headers, scope, or abbr attribute -- legitimate table structures");
|
||||
}
|
||||
|
||||
nsAccessible* cell = mDoc->GetAccessible(cellElm);
|
||||
if (cell && cell->GetChildCount() == 1 &&
|
||||
cell->FirstChild()->IsAbbreviation()) {
|
||||
RETURN_LAYOUT_ANSWER(false,
|
||||
"has abbr -- legitimate table structures");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,8 @@
|
||||
#include "AccessibleComponent_i.c"
|
||||
|
||||
#include "nsAccessible.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "nsWinUtils.h"
|
||||
#include "States.h"
|
||||
|
||||
#include "nsString.h"
|
||||
@ -156,17 +158,23 @@ __try {
|
||||
*aColorValue = 0;
|
||||
|
||||
nsRefPtr<nsAccessible> acc(do_QueryObject(this));
|
||||
if (!acc)
|
||||
if (acc->IsDefunct())
|
||||
return E_FAIL;
|
||||
|
||||
nsCOMPtr<nsIDOMCSSPrimitiveValue> cssValue;
|
||||
nsresult rv = acc->GetComputedStyleCSSValue(EmptyString(), aPropName,
|
||||
getter_AddRefs(cssValue));
|
||||
if (NS_FAILED(rv) || !cssValue)
|
||||
return GetHRESULT(rv);
|
||||
nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl =
|
||||
nsWinUtils::GetComputedStyleDeclaration(acc->GetContent());
|
||||
NS_ENSURE_STATE(styleDecl);
|
||||
|
||||
nsCOMPtr<nsIDOMCSSValue> cssGenericValue;
|
||||
styleDecl->GetPropertyCSSValue(aPropName, getter_AddRefs(cssGenericValue));
|
||||
|
||||
nsCOMPtr<nsIDOMCSSPrimitiveValue> cssValue =
|
||||
do_QueryInterface(cssGenericValue);
|
||||
if (!cssValue)
|
||||
return E_FAIL;
|
||||
|
||||
nsCOMPtr<nsIDOMRGBColor> rgbColor;
|
||||
rv = cssValue->GetRGBColorValue(getter_AddRefs(rgbColor));
|
||||
nsresult rv = cssValue->GetRGBColorValue(getter_AddRefs(rgbColor));
|
||||
if (NS_FAILED(rv) || !rgbColor)
|
||||
return GetHRESULT(rv);
|
||||
|
||||
|
@ -339,7 +339,7 @@ __try{
|
||||
return E_FAIL;
|
||||
|
||||
nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl =
|
||||
nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), mContent);
|
||||
nsWinUtils::GetComputedStyleDeclaration(mContent);
|
||||
NS_ENSURE_TRUE(cssDecl, E_FAIL);
|
||||
|
||||
PRUint32 length;
|
||||
@ -374,7 +374,7 @@ __try {
|
||||
return E_FAIL;
|
||||
|
||||
nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl =
|
||||
nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), mContent);
|
||||
nsWinUtils::GetComputedStyleDeclaration(mContent);
|
||||
NS_ENSURE_TRUE(cssDecl, E_FAIL);
|
||||
|
||||
PRUint32 index;
|
||||
|
@ -55,6 +55,25 @@ using namespace mozilla::a11y;
|
||||
// tab windows.
|
||||
const PRUnichar* kPropNameTabContent = L"AccessibleTabWindow";
|
||||
|
||||
already_AddRefed<nsIDOMCSSStyleDeclaration>
|
||||
nsWinUtils::GetComputedStyleDeclaration(nsIContent* aContent)
|
||||
{
|
||||
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aContent);
|
||||
if (!elm)
|
||||
return nsnull;
|
||||
|
||||
// Returns number of items in style declaration
|
||||
nsCOMPtr<nsIDOMWindow> window =
|
||||
do_QueryInterface(elm->OwnerDoc()->GetWindow());
|
||||
if (!window)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
|
||||
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(elm));
|
||||
window->GetComputedStyle(domElement, EmptyString(), getter_AddRefs(cssDecl));
|
||||
return cssDecl.forget();
|
||||
}
|
||||
|
||||
HRESULT
|
||||
nsWinUtils::ConvertToIA2Array(nsIArray *aGeckoArray, IUnknown ***aIA2Array,
|
||||
long *aIA2ArrayLen)
|
||||
|
@ -52,6 +52,15 @@ const LPCWSTR kClassNameTabContent = L"MozillaContentWindowClass";
|
||||
class nsWinUtils
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Return computed styles declaration for the given node.
|
||||
*
|
||||
* @note Please use it carefully since it can shutdown the accessible tree
|
||||
* you operate on.
|
||||
*/
|
||||
static already_AddRefed<nsIDOMCSSStyleDeclaration>
|
||||
GetComputedStyleDeclaration(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Convert nsIArray array of accessible objects to an array of IUnknown*
|
||||
* objects used in IA2 methods.
|
||||
|
@ -311,14 +311,6 @@ nsXULMenuitemAccessible::GetLevelInternal()
|
||||
return nsAccUtils::GetLevelForXULContainerItem(mContent);
|
||||
}
|
||||
|
||||
void
|
||||
nsXULMenuitemAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet,
|
||||
PRInt32 *aSetSize)
|
||||
{
|
||||
nsAccUtils::GetPositionAndSizeForXULContainerItem(mContent, aPosInSet,
|
||||
aSetSize);
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULMenuitemAccessible::CanHaveAnonChildren()
|
||||
{
|
||||
|
@ -63,8 +63,6 @@ public:
|
||||
virtual mozilla::a11y::role NativeRole();
|
||||
virtual PRUint64 NativeState();
|
||||
virtual PRInt32 GetLevelInternal();
|
||||
virtual void GetPositionAndSizeInternal(PRInt32 *aPosInSet,
|
||||
PRInt32 *aSetSize);
|
||||
|
||||
virtual bool CanHaveAnonChildren();
|
||||
|
||||
|
@ -101,7 +101,6 @@ _TEST_FILES =\
|
||||
test_descr.html \
|
||||
test_nsIAccessibleDocument.html \
|
||||
test_nsIAccessibleImage.html \
|
||||
test_nsIAccessNode_utils.html \
|
||||
test_nsOuterDocAccessible.html \
|
||||
test_role_nsHyperTextAcc.html \
|
||||
test_textboxes.html \
|
||||
|
@ -139,6 +139,13 @@
|
||||
testGroupAttrs("h5", 0, 0, 5);
|
||||
testGroupAttrs("h6", 0, 0, 6);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ARIA combobox
|
||||
testGroupAttrs("combo1_opt1", 1, 4);
|
||||
testGroupAttrs("combo1_opt2", 2, 4);
|
||||
testGroupAttrs("combo1_opt3", 3, 4);
|
||||
testGroupAttrs("combo1_opt4", 4, 4);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
@ -305,5 +312,12 @@
|
||||
<h5 id="h5">heading5</h5>
|
||||
<h6 id="h6">heading6</h6>
|
||||
|
||||
<ul id="combo1" role="combobox">Password
|
||||
<li id="combo1_opt1" role="option">Xyzzy</li>
|
||||
<li id="combo1_opt2" role="option">Plughs</li>
|
||||
<li id="combo1_opt3" role="option">Shazaam</li>
|
||||
<li id="combo1_opt4" role="option">JoeSentMe</li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -90,6 +90,13 @@
|
||||
testGroupAttrs("radio1", 1, 2);
|
||||
testGroupAttrs("radio2", 2, 2);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// xul:menulist
|
||||
testGroupAttrs("menulist1.1", 1);
|
||||
testGroupAttrs("menulist1.2", 2);
|
||||
testGroupAttrs("menulist1.3", 3);
|
||||
testGroupAttrs("menulist1.4", 4);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ARIA menu (bug 441888)
|
||||
testGroupAttrs("aria-menuitem", 1, 3);
|
||||
@ -175,6 +182,15 @@
|
||||
<radio id="radio2" label="radio2"/>
|
||||
</radiogroup>
|
||||
|
||||
<menulist id="menulist1" label="Vehicle">
|
||||
<menupopup>
|
||||
<menuitem id="menulist1.1" label="Car"/>
|
||||
<menuitem id="menulist1.2" label="Taxi"/>
|
||||
<menuitem id="menulist1.3" label="Bus" selected="true"/>
|
||||
<menuitem id="menulist1.4" label="Train"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
|
||||
<vbox>
|
||||
<description role="menuitem" id="aria-menuitem"
|
||||
value="conventional menuitem"/>
|
||||
|
@ -181,6 +181,30 @@
|
||||
attrs = {"text-position": gComputedStyle.verticalAlign};
|
||||
testTextAttrs(ID, 55, attrs, defAttrs, 55, 64);
|
||||
|
||||
attrs = {};
|
||||
testTextAttrs(ID, 64, attrs, defAttrs, 64, 69);
|
||||
|
||||
attrs = { "text-position": "super" };
|
||||
testTextAttrs(ID, 69, attrs, defAttrs, 69, 84);
|
||||
|
||||
attrs = {};
|
||||
testTextAttrs(ID, 84, attrs, defAttrs, 84, 89);
|
||||
|
||||
attrs = { "text-position": "sub" };
|
||||
testTextAttrs(ID, 89, attrs, defAttrs, 89, 102);
|
||||
|
||||
attrs = {};
|
||||
testTextAttrs(ID, 102, attrs, defAttrs, 102, 107);
|
||||
|
||||
attrs = { "text-position": "super" };
|
||||
testTextAttrs(ID, 107, attrs, defAttrs, 107, 123);
|
||||
|
||||
attrs = {};
|
||||
testTextAttrs(ID, 123, attrs, defAttrs, 123, 128);
|
||||
|
||||
attrs = { "text-position": "sub" };
|
||||
testTextAttrs(ID, 128, attrs, defAttrs, 128, 142);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// area7
|
||||
ID = "area7";
|
||||
@ -505,6 +529,11 @@
|
||||
title="Implement text attributes">
|
||||
Mozilla Bug 345759
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=473569"
|
||||
title="Restrict text-position to allowed values">
|
||||
Mozilla Bug 473569
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=473576"
|
||||
title="font-family text attribute should expose actual font used">
|
||||
@ -549,7 +578,11 @@
|
||||
This <sup>sentence</sup> has the word
|
||||
<span style="vertical-align:super;">sentence</span> in
|
||||
<sub>superscript</sub> and
|
||||
<span style="vertical-align:sub;">subscript</span>
|
||||
<span style="vertical-align:sub;">subscript</span> and
|
||||
<span style="vertical-align:20%;">superscript 20%</span> and
|
||||
<span style="vertical-align:-20%;">subscript 20%</span> and
|
||||
<span style="vertical-align:20px;">superscript 20px</span> and
|
||||
<span style="vertical-align:-20px;">subscript 20px</span>
|
||||
</p>
|
||||
|
||||
<p lang="en" id="area7">
|
||||
|
@ -1,53 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>nsIAccessNode util methods testing</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="common.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
function doTest()
|
||||
{
|
||||
var elmObj = {};
|
||||
var acc = getAccessible("span", null, elmObj);
|
||||
computedStyle = document.defaultView.getComputedStyle(elmObj.value, "");
|
||||
|
||||
// html:span element
|
||||
is(acc.getComputedStyleValue("", "color"), computedStyle.color,
|
||||
"Wrong color for element with ID 'span'");
|
||||
|
||||
// text child of html:span element
|
||||
acc = getAccessible(acc.firstChild);
|
||||
is(acc.getComputedStyleValue("", "color"), computedStyle.color,
|
||||
"Wrong color for text child of element with ID 'span'");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=454211"
|
||||
title="nsIAccessNode util methods testing">
|
||||
Mozilla Bug 454211
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<span role="note" style="color: red" id="span">text</span>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -46,7 +46,7 @@ pref("browser.homescreenURL", "file:///data/local/homescreen.html,file:///system
|
||||
#endif
|
||||
|
||||
// URL for the dialer application.
|
||||
pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html http://localhost:7777/data/local/apps/homescreen/homescreen.html http://localhost:7777/apps/dialer/dialer.html http://localhost:7777/apps/homescreen/homescreen.html");
|
||||
pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html,http://localhost:7777/data/local/apps/homescreen/homescreen.html,http://localhost:7777/apps/dialer/dialer.html,http://localhost:7777/apps/homescreen/homescreen.html");
|
||||
|
||||
// Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
|
||||
pref("browser.viewport.scaleRatio", -1);
|
||||
@ -444,3 +444,12 @@ pref("power.screen.timeout", 60);
|
||||
pref("full-screen-api.enabled", true);
|
||||
|
||||
pref("media.volume.steps", 10);
|
||||
|
||||
// Data connection settings. These will eventually live in the
|
||||
// navigator.settings API, or even in a database where we can look
|
||||
// it up automatically (bug 729440), but for this will have to do.
|
||||
pref("ril.data.enabled", false);
|
||||
pref("ril.data.roaming.enabled", false);
|
||||
pref("ril.data.apn", "");
|
||||
pref("ril.data.user", "");
|
||||
pref("ril.data.passwd", "");
|
||||
|
@ -282,7 +282,7 @@ pref("browser.urlbar.doubleClickSelectsAll", true);
|
||||
#else
|
||||
pref("browser.urlbar.doubleClickSelectsAll", false);
|
||||
#endif
|
||||
pref("browser.urlbar.autoFill", false);
|
||||
pref("browser.urlbar.autoFill", true);
|
||||
pref("browser.urlbar.autoFill.typed", true);
|
||||
// 0: Match anywhere (e.g., middle of words)
|
||||
// 1: Match on word boundaries and then try matching anywhere
|
||||
@ -1042,9 +1042,6 @@ pref("devtools.debugger.enabled", false);
|
||||
// The default Debugger UI height
|
||||
pref("devtools.debugger.ui.height", 250);
|
||||
|
||||
// Disable remote debugging protocol logging
|
||||
pref("devtools.debugger.log", false);
|
||||
|
||||
// Enable the style inspector
|
||||
pref("devtools.styleinspector.enabled", true);
|
||||
|
||||
|
@ -440,7 +440,8 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
|
||||
#notification-popup-box[anchorid="geo-notification-icon"] > #geo-notification-icon,
|
||||
#notification-popup-box[anchorid="indexedDB-notification-icon"] > #indexedDB-notification-icon,
|
||||
#notification-popup-box[anchorid="addons-notification-icon"] > #addons-notification-icon,
|
||||
#notification-popup-box[anchorid="password-notification-icon"] > #password-notification-icon {
|
||||
#notification-popup-box[anchorid="password-notification-icon"] > #password-notification-icon,
|
||||
#notification-popup-box[anchorid="webapps-notification-icon"] > #webapps-notification-icon {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
|
@ -4752,10 +4752,15 @@ var XULBrowserWindow = {
|
||||
}
|
||||
|
||||
// Show or hide browser chrome based on the whitelist
|
||||
if (this.hideChromeForLocation(location))
|
||||
if (this.hideChromeForLocation(location)) {
|
||||
document.documentElement.setAttribute("disablechrome", "true");
|
||||
} else {
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
if (ss.getTabValue(gBrowser.selectedTab, "appOrigin"))
|
||||
document.documentElement.setAttribute("disablechrome", "true");
|
||||
else
|
||||
document.documentElement.removeAttribute("disablechrome");
|
||||
}
|
||||
|
||||
// Disable find commands in documents that ask for them to be disabled.
|
||||
let disableFind = false;
|
||||
@ -9100,7 +9105,6 @@ XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
|
||||
#endif
|
||||
});
|
||||
|
||||
|
||||
var MousePosTracker = {
|
||||
_listeners: [],
|
||||
_x: 0,
|
||||
|
@ -527,6 +527,7 @@
|
||||
<image id="addons-notification-icon" class="notification-anchor-icon" role="button"/>
|
||||
<image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"/>
|
||||
<image id="password-notification-icon" class="notification-anchor-icon" role="button"/>
|
||||
<image id="webapps-notification-icon" class="webapps-anchor-icon" role="button"/>
|
||||
</box>
|
||||
<!-- Use onclick instead of normal popup= syntax since the popup
|
||||
code fires onmousedown, and hence eats our favicon drag events.
|
||||
@ -1003,8 +1004,6 @@
|
||||
#endif
|
||||
<toolbarbutton id="inspector-inspect-toolbutton"
|
||||
class="devtools-toolbarbutton"
|
||||
label="&inspectButton.label;"
|
||||
accesskey="&inspectButton.accesskey;"
|
||||
command="Inspector:Inspect"/>
|
||||
<toolbarbutton id="inspector-treepanel-toolbutton"
|
||||
class="devtools-toolbarbutton"
|
||||
|
@ -87,3 +87,7 @@ html|*#highlighter-nodeinfobar-tagname {
|
||||
html|*#highlighter-nodeinfobar-tagname {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
|
@ -15,16 +15,14 @@ function Cell(aGrid, aNode) {
|
||||
this._node._newtabCell = this;
|
||||
|
||||
// Register drag-and-drop event handlers.
|
||||
["DragEnter", "DragOver", "DragExit", "Drop"].forEach(function (aType) {
|
||||
let method = "on" + aType;
|
||||
this[method] = this[method].bind(this);
|
||||
this._node.addEventListener(aType.toLowerCase(), this[method], false);
|
||||
["dragenter", "dragover", "dragexit", "drop"].forEach(function (aType) {
|
||||
this._node.addEventListener(aType, this, false);
|
||||
}, this);
|
||||
}
|
||||
|
||||
Cell.prototype = {
|
||||
/**
|
||||
*
|
||||
* The grid.
|
||||
*/
|
||||
_grid: null,
|
||||
|
||||
@ -97,41 +95,27 @@ Cell.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'dragenter' event.
|
||||
* @param aEvent The dragenter event.
|
||||
* Handles all cell events.
|
||||
*/
|
||||
onDragEnter: function Cell_onDragEnter(aEvent) {
|
||||
if (gDrag.isValid(aEvent)) {
|
||||
handleEvent: function Cell_handleEvent(aEvent) {
|
||||
if (aEvent.type != "dragexit" && !gDrag.isValid(aEvent))
|
||||
return;
|
||||
|
||||
switch (aEvent.type) {
|
||||
case "dragenter":
|
||||
aEvent.preventDefault();
|
||||
gDrop.enter(this, aEvent);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'dragover' event.
|
||||
* @param aEvent The dragover event.
|
||||
*/
|
||||
onDragOver: function Cell_onDragOver(aEvent) {
|
||||
if (gDrag.isValid(aEvent))
|
||||
break;
|
||||
case "dragover":
|
||||
aEvent.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'dragexit' event.
|
||||
* @param aEvent The dragexit event.
|
||||
*/
|
||||
onDragExit: function Cell_onDragExit(aEvent) {
|
||||
break;
|
||||
case "dragexit":
|
||||
gDrop.exit(this, aEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'drop' event.
|
||||
* @param aEvent The drop event.
|
||||
*/
|
||||
onDrop: function Cell_onDrop(aEvent) {
|
||||
if (gDrag.isValid(aEvent)) {
|
||||
break;
|
||||
case "drop":
|
||||
aEvent.preventDefault();
|
||||
gDrop.drop(this, aEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -36,11 +36,11 @@ let gDrag = {
|
||||
start: function Drag_start(aSite, aEvent) {
|
||||
this._draggedSite = aSite;
|
||||
|
||||
// Prevent moz-transform for left, top.
|
||||
aSite.node.setAttribute("dragged", "true");
|
||||
|
||||
// Make sure the dragged site is floating above the grid.
|
||||
aSite.node.setAttribute("ontop", "true");
|
||||
// Mark nodes as being dragged.
|
||||
let selector = ".newtab-site, .newtab-control, .newtab-thumbnail";
|
||||
let nodes = aSite.node.parentNode.querySelectorAll(selector);
|
||||
for (let i = 0; i < nodes.length; i++)
|
||||
nodes[i].setAttribute("dragged", "true");
|
||||
|
||||
this._setDragData(aSite, aEvent);
|
||||
|
||||
@ -88,13 +88,12 @@ let gDrag = {
|
||||
* @param aEvent The 'dragend' event.
|
||||
*/
|
||||
end: function Drag_end(aSite, aEvent) {
|
||||
aSite.node.removeAttribute("dragged");
|
||||
let nodes = aSite.node.parentNode.querySelectorAll("[dragged]");
|
||||
for (let i = 0; i < nodes.length; i++)
|
||||
nodes[i].removeAttribute("dragged");
|
||||
|
||||
// Slide the dragged site back into its cell (may be the old or the new cell).
|
||||
gTransformation.slideSiteTo(aSite, aSite.cell, {
|
||||
unfreeze: true,
|
||||
callback: function () aSite.node.removeAttribute("ontop")
|
||||
});
|
||||
gTransformation.slideSiteTo(aSite, aSite.cell, {unfreeze: true});
|
||||
|
||||
this._draggedSite = null;
|
||||
},
|
||||
@ -106,7 +105,11 @@ let gDrag = {
|
||||
*/
|
||||
isValid: function Drag_isValid(aEvent) {
|
||||
let dt = aEvent.dataTransfer;
|
||||
return dt && dt.types.contains("text/x-moz-url");
|
||||
let mimeType = "text/x-moz-url";
|
||||
|
||||
// Check that the drag data is non-empty.
|
||||
// Can happen when dragging places folders.
|
||||
return dt && dt.types.contains(mimeType) && dt.getData(mimeType);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -128,13 +131,13 @@ let gDrag = {
|
||||
// Create and use an empty drag element. We don't want to use the default
|
||||
// drag image with its default opacity.
|
||||
let dragElement = document.createElementNS(HTML_NAMESPACE, "div");
|
||||
dragElement.classList.add("drag-element");
|
||||
let body = document.getElementById("body");
|
||||
body.appendChild(dragElement);
|
||||
dragElement.classList.add("newtab-drag");
|
||||
let scrollbox = document.getElementById("newtab-scrollbox");
|
||||
scrollbox.appendChild(dragElement);
|
||||
dt.setDragImage(dragElement, 0, 0);
|
||||
|
||||
// After the 'dragstart' event has been processed we can remove the
|
||||
// temporary drag element from the DOM.
|
||||
setTimeout(function () body.removeChild(dragElement), 0);
|
||||
setTimeout(function () scrollbox.removeChild(dragElement), 0);
|
||||
}
|
||||
};
|
||||
|
@ -26,13 +26,26 @@ let gDropTargetShim = {
|
||||
init: function DropTargetShim_init() {
|
||||
let node = gGrid.node;
|
||||
|
||||
this._dragover = this._dragover.bind(this);
|
||||
|
||||
// Add drag event handlers.
|
||||
node.addEventListener("dragstart", this._start.bind(this), true);
|
||||
// XXX bug 505521 - Don't listen for drag, it's useless at the moment.
|
||||
//node.addEventListener("drag", this._drag.bind(this), false);
|
||||
node.addEventListener("dragend", this._end.bind(this), true);
|
||||
node.addEventListener("dragstart", this, true);
|
||||
node.addEventListener("dragend", this, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles all shim events.
|
||||
*/
|
||||
handleEvent: function DropTargetShim_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "dragstart":
|
||||
this._start(aEvent);
|
||||
break;
|
||||
case "dragover":
|
||||
this._dragover(aEvent);
|
||||
break;
|
||||
case "dragend":
|
||||
this._end(aEvent);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -40,11 +53,11 @@ let gDropTargetShim = {
|
||||
* @param aEvent The 'dragstart' event.
|
||||
*/
|
||||
_start: function DropTargetShim_start(aEvent) {
|
||||
if (aEvent.target.classList.contains("site")) {
|
||||
if (aEvent.target.classList.contains("newtab-link")) {
|
||||
gGrid.lock();
|
||||
|
||||
// XXX bug 505521 - Listen for dragover on the document.
|
||||
document.documentElement.addEventListener("dragover", this._dragover, false);
|
||||
document.documentElement.addEventListener("dragover", this, false);
|
||||
}
|
||||
},
|
||||
|
||||
@ -56,12 +69,7 @@ let gDropTargetShim = {
|
||||
// Let's see if we find a drop target.
|
||||
let target = this._findDropTarget(aEvent);
|
||||
|
||||
if (target == this._lastDropTarget) {
|
||||
// XXX bug 505521 - Don't fire dragover for now (causes recursion).
|
||||
/*if (target)
|
||||
// The last drop target is valid and didn't change.
|
||||
this._dispatchEvent(aEvent, "dragover", target);*/
|
||||
} else {
|
||||
if (target != this._lastDropTarget) {
|
||||
if (this._lastDropTarget)
|
||||
// We left the last drop target.
|
||||
this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget);
|
||||
@ -84,7 +92,7 @@ let gDropTargetShim = {
|
||||
* @param aEvent The 'dragover' event.
|
||||
*/
|
||||
_dragover: function DropTargetShim_dragover(aEvent) {
|
||||
let sourceNode = aEvent.dataTransfer.mozSourceNode;
|
||||
let sourceNode = aEvent.dataTransfer.mozSourceNode.parentNode;
|
||||
gDrag.drag(sourceNode._newtabSite, aEvent);
|
||||
|
||||
this._drag(aEvent);
|
||||
@ -117,7 +125,7 @@ let gDropTargetShim = {
|
||||
gGrid.unlock();
|
||||
|
||||
// XXX bug 505521 - Remove the document's dragover listener.
|
||||
document.documentElement.removeEventListener("dragover", this._dragover, false);
|
||||
document.documentElement.removeEventListener("dragover", this, false);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -24,7 +24,7 @@ let gGrid = {
|
||||
*/
|
||||
get cells() {
|
||||
let cells = [];
|
||||
let children = this.node.querySelectorAll("li");
|
||||
let children = this.node.querySelectorAll(".newtab-cell");
|
||||
for (let i = 0; i < children.length; i++)
|
||||
cells.push(new Cell(this, children[i]));
|
||||
|
||||
@ -43,8 +43,8 @@ let gGrid = {
|
||||
* Initializes the grid.
|
||||
* @param aSelector The query selector of the grid.
|
||||
*/
|
||||
init: function Grid_init(aSelector) {
|
||||
this._node = document.querySelector(aSelector);
|
||||
init: function Grid_init() {
|
||||
this._node = document.getElementById("newtab-grid");
|
||||
this._createSiteFragment();
|
||||
this._draw();
|
||||
},
|
||||
@ -96,21 +96,20 @@ let gGrid = {
|
||||
* Creates the DOM fragment that is re-used when creating sites.
|
||||
*/
|
||||
_createSiteFragment: function Grid_createSiteFragment() {
|
||||
let site = document.createElementNS(HTML_NAMESPACE, "a");
|
||||
site.classList.add("site");
|
||||
let site = document.createElementNS(HTML_NAMESPACE, "div");
|
||||
site.classList.add("newtab-site");
|
||||
site.setAttribute("draggable", "true");
|
||||
|
||||
// Create the site's inner HTML code.
|
||||
site.innerHTML =
|
||||
'<img class="site-img" width="' + THUMB_WIDTH +'" ' +
|
||||
' height="' + THUMB_HEIGHT + '" alt=""/>' +
|
||||
'<span class="site-title"/>' +
|
||||
'<span class="site-strip">' +
|
||||
' <input class="button strip-button strip-button-pin" type="button"' +
|
||||
' tabindex="-1" title="' + newTabString("pin") + '"/>' +
|
||||
' <input class="button strip-button strip-button-block" type="button"' +
|
||||
' tabindex="-1" title="' + newTabString("block") + '"/>' +
|
||||
'</span>';
|
||||
'<a class="newtab-link">' +
|
||||
' <span class="newtab-thumbnail"/>' +
|
||||
' <span class="newtab-title"/>' +
|
||||
'</a>' +
|
||||
'<input type="button" title="' + newTabString("pin") + '"' +
|
||||
' class="newtab-control newtab-control-pin"/>' +
|
||||
'<input type="button" title="' + newTabString("block") + '"' +
|
||||
' class="newtab-control newtab-control-block"/>';
|
||||
|
||||
this._siteFragment = document.createDocumentFragment();
|
||||
this._siteFragment.appendChild(site);
|
||||
|
@ -1,161 +1,174 @@
|
||||
:root {
|
||||
-moz-appearance: none;
|
||||
-moz-user-focus: normal;
|
||||
}
|
||||
|
||||
#scrollbox:not([page-disabled]) {
|
||||
input[type=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* SCROLLBOX */
|
||||
#newtab-scrollbox {
|
||||
display: -moz-box;
|
||||
position: relative;
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
#newtab-scrollbox:not([page-disabled]) {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#body {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
min-width: 675px;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* TOOLBAR */
|
||||
#toolbar {
|
||||
/* TOGGLE */
|
||||
#newtab-toggle {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
#toolbar[page-disabled] {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#toolbar:-moz-locale-dir(rtl) {
|
||||
left: 8px;
|
||||
#newtab-toggle:-moz-locale-dir(rtl) {
|
||||
left: 12px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.toolbar-button {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
-moz-transition: opacity 200ms ease-out;
|
||||
/* MARGINS */
|
||||
#newtab-vertical-margin {
|
||||
display: -moz-box;
|
||||
position: relative;
|
||||
-moz-box-flex: 1;
|
||||
-moz-box-orient: vertical;
|
||||
}
|
||||
|
||||
#toolbar-button-show,
|
||||
#toolbar-button-reset {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
#newtab-margin-top {
|
||||
min-height: 50px;
|
||||
max-height: 80px;
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
#toolbar-button-reset[modified],
|
||||
#toolbar-button-show[page-disabled] {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
#newtab-margin-bottom {
|
||||
min-height: 40px;
|
||||
max-height: 100px;
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
#toolbar-button-hide[page-disabled],
|
||||
#toolbar-button-reset[page-disabled] {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
#newtab-horizontal-margin {
|
||||
display: -moz-box;
|
||||
-moz-box-flex: 5;
|
||||
}
|
||||
|
||||
.newtab-side-margin {
|
||||
min-width: 40px;
|
||||
max-width: 300px;
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
/* GRID */
|
||||
#grid {
|
||||
width: 637px;
|
||||
height: 411px;
|
||||
overflow: hidden;
|
||||
list-style-type: none;
|
||||
-moz-transition: opacity 200ms ease-out;
|
||||
#newtab-grid {
|
||||
display: -moz-box;
|
||||
-moz-box-flex: 5;
|
||||
-moz-box-orient: vertical;
|
||||
min-width: 600px;
|
||||
min-height: 400px;
|
||||
-moz-transition: 100ms ease-out;
|
||||
-moz-transition-property: opacity;
|
||||
}
|
||||
|
||||
#grid[page-disabled] {
|
||||
#newtab-grid[page-disabled] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#grid[page-disabled],
|
||||
#grid[locked] {
|
||||
#newtab-grid[locked],
|
||||
#newtab-grid[page-disabled] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ROWS */
|
||||
.newtab-row {
|
||||
display: -moz-box;
|
||||
-moz-box-orient: horizontal;
|
||||
-moz-box-direction: normal;
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
/* CELLS */
|
||||
.cell {
|
||||
float: left;
|
||||
width: 201px;
|
||||
height: 127px;
|
||||
margin-bottom: 15px;
|
||||
-moz-margin-end: 16px;
|
||||
}
|
||||
|
||||
.cell:-moz-locale-dir(rtl) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.cell:nth-child(3n+3) {
|
||||
-moz-margin-end: 0;
|
||||
.newtab-cell {
|
||||
display: -moz-box;
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
/* SITES */
|
||||
.site {
|
||||
display: block;
|
||||
.newtab-site {
|
||||
position: relative;
|
||||
width: 201px;
|
||||
height: 127px;
|
||||
-moz-box-flex: 1;
|
||||
-moz-transition: 100ms ease-out;
|
||||
-moz-transition-property: top, left, opacity;
|
||||
}
|
||||
|
||||
.site[frozen] {
|
||||
.newtab-site[frozen] {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.site[ontop] {
|
||||
.newtab-site[dragged] {
|
||||
-moz-transition-property: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* SITE IMAGE */
|
||||
.site-img {
|
||||
display: block;
|
||||
opacity: 0.75;
|
||||
-moz-transition: opacity 200ms ease-out;
|
||||
}
|
||||
|
||||
.site:hover > .site-img,
|
||||
.site[ontop] > .site-img,
|
||||
.site:-moz-focusring > .site-img {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.site-img[loading] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* SITE TITLE */
|
||||
.site-title {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* SITE STRIP */
|
||||
.site-strip {
|
||||
/* LINK + THUMBNAILS */
|
||||
.newtab-link,
|
||||
.newtab-thumbnail {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 195px;
|
||||
height: 17px;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
-moz-transition: opacity 200ms ease-out;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.site:hover:not([frozen]) > .site-strip {
|
||||
.newtab-thumbnail {
|
||||
opacity: .8;
|
||||
-moz-transition: opacity 100ms ease-out;
|
||||
}
|
||||
|
||||
.newtab-thumbnail[dragged],
|
||||
.newtab-link:-moz-focusring > .newtab-thumbnail,
|
||||
.newtab-site:hover > .newtab-link > .newtab-thumbnail {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.strip-button-pin,
|
||||
.strip-button-block:-moz-locale-dir(rtl) {
|
||||
float: left;
|
||||
/* TITLES */
|
||||
.newtab-title {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.strip-button-block,
|
||||
.strip-button-pin:-moz-locale-dir(rtl) {
|
||||
float: right;
|
||||
/* CONTROLS */
|
||||
.newtab-control {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
opacity: 0;
|
||||
-moz-transition: opacity 100ms ease-out;
|
||||
}
|
||||
|
||||
.newtab-control:-moz-focusring,
|
||||
.newtab-site:hover > .newtab-control {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.newtab-control[dragged] {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
.newtab-control-pin:-moz-locale-dir(ltr),
|
||||
.newtab-control-block:-moz-locale-dir(rtl) {
|
||||
left: 4px;
|
||||
}
|
||||
|
||||
.newtab-control-block:-moz-locale-dir(ltr),
|
||||
.newtab-control-pin:-moz-locale-dir(rtl) {
|
||||
right: 4px;
|
||||
}
|
||||
|
||||
/* DRAG & DROP */
|
||||
@ -165,7 +178,7 @@
|
||||
* so that we can use custom drag images and elements. It needs an opacity of
|
||||
* 0.01 so that the core code detects that it's in fact a visible element.
|
||||
*/
|
||||
.drag-element {
|
||||
.newtab-drag {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
background-color: #fff;
|
||||
|
@ -30,13 +30,10 @@ XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() {
|
||||
function newTabString(name) gStringBundle.GetStringFromName('newtab.' + name);
|
||||
|
||||
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
||||
const THUMB_WIDTH = 201;
|
||||
const THUMB_HEIGHT = 127;
|
||||
|
||||
#include batch.js
|
||||
#include transformations.js
|
||||
#include page.js
|
||||
#include toolbar.js
|
||||
#include grid.js
|
||||
#include cells.js
|
||||
#include sites.js
|
||||
@ -47,4 +44,4 @@ const THUMB_HEIGHT = 127;
|
||||
#include updater.js
|
||||
|
||||
// Everything is loaded. Initialize the New Tab Page.
|
||||
gPage.init("#toolbar", "#grid");
|
||||
gPage.init();
|
||||
|
@ -4,7 +4,7 @@
|
||||
# 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/.
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/newtab/newTab.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/newtab/newTab.css" type="text/css"?>
|
||||
|
||||
@ -13,28 +13,44 @@
|
||||
%newTabDTD;
|
||||
]>
|
||||
|
||||
<xul:window xmlns="http://www.w3.org/1999/xhtml"
|
||||
<xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
disablefastfind="true" title="&newtab.pageTitle;">
|
||||
<xul:vbox id="scrollbox" flex="1" title=" ">
|
||||
<body id="body">
|
||||
<div id="toolbar">
|
||||
<input class="button toolbar-button" id="toolbar-button-show"
|
||||
type="button" title="&newtab.show;"/>
|
||||
<input class="button toolbar-button" id="toolbar-button-hide"
|
||||
type="button" title="&newtab.hide;"/>
|
||||
<input class="button toolbar-button" id="toolbar-button-reset"
|
||||
type="button" title="&newtab.reset;"/>
|
||||
xul:disablefastfind="true" xul:title="&newtab.pageTitle;">
|
||||
|
||||
<div id="newtab-scrollbox">
|
||||
|
||||
<div id="newtab-vertical-margin">
|
||||
<div id="newtab-margin-top"/>
|
||||
|
||||
<div id="newtab-horizontal-margin">
|
||||
<div class="newtab-side-margin"/>
|
||||
|
||||
<div id="newtab-grid">
|
||||
<div class="newtab-row">
|
||||
<div class="newtab-cell"/>
|
||||
<div class="newtab-cell"/>
|
||||
<div class="newtab-cell"/>
|
||||
</div>
|
||||
<div class="newtab-row">
|
||||
<div class="newtab-cell"/>
|
||||
<div class="newtab-cell"/>
|
||||
<div class="newtab-cell"/>
|
||||
</div>
|
||||
<div class="newtab-row">
|
||||
<div class="newtab-cell"/>
|
||||
<div class="newtab-cell"/>
|
||||
<div class="newtab-cell"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul id="grid">
|
||||
<li class="cell"/><li class="cell"/><li class="cell"/>
|
||||
<li class="cell"/><li class="cell"/><li class="cell"/>
|
||||
<li class="cell"/><li class="cell"/><li class="cell"/>
|
||||
</ul>
|
||||
<div class="newtab-side-margin"/>
|
||||
</div>
|
||||
|
||||
<div id="newtab-margin-bottom"/>
|
||||
</div>
|
||||
<input id="newtab-toggle" type="button"/>
|
||||
</div>
|
||||
|
||||
<xul:script type="text/javascript;version=1.8"
|
||||
src="chrome://browser/content/newtab/newTab.js"/>
|
||||
</body>
|
||||
</xul:vbox>
|
||||
</xul:window>
|
||||
|
@ -11,25 +11,24 @@
|
||||
let gPage = {
|
||||
/**
|
||||
* Initializes the page.
|
||||
* @param aToolbarSelector The query selector for the page toolbar.
|
||||
* @param aGridSelector The query selector for the grid.
|
||||
*/
|
||||
init: function Page_init(aToolbarSelector, aGridSelector) {
|
||||
gToolbar.init(aToolbarSelector);
|
||||
this._gridSelector = aGridSelector;
|
||||
|
||||
init: function Page_init() {
|
||||
// Add ourselves to the list of pages to receive notifications.
|
||||
gAllPages.register(this);
|
||||
|
||||
// Listen for 'unload' to unregister this page.
|
||||
function unload() { gAllPages.unregister(this); }
|
||||
addEventListener("unload", unload.bind(this), false);
|
||||
addEventListener("unload", this, false);
|
||||
|
||||
// Listen for toggle button clicks.
|
||||
let button = document.getElementById("newtab-toggle");
|
||||
button.addEventListener("click", this, false);
|
||||
|
||||
// Check if the new tab feature is enabled.
|
||||
if (gAllPages.enabled)
|
||||
let enabled = gAllPages.enabled;
|
||||
if (enabled)
|
||||
this._init();
|
||||
else
|
||||
this._updateAttributes(false);
|
||||
|
||||
this._updateAttributes(enabled);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -48,25 +47,9 @@ let gPage = {
|
||||
* Updates the whole page and the grid when the storage has changed.
|
||||
*/
|
||||
update: function Page_update() {
|
||||
this.updateModifiedFlag();
|
||||
gGrid.refresh();
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the page is modified and sets the CSS class accordingly
|
||||
*/
|
||||
updateModifiedFlag: function Page_updateModifiedFlag() {
|
||||
let node = document.getElementById("toolbar-button-reset");
|
||||
let modified = this._isModified();
|
||||
|
||||
if (modified)
|
||||
node.setAttribute("modified", "true");
|
||||
else
|
||||
node.removeAttribute("modified");
|
||||
|
||||
this._updateTabIndices(gAllPages.enabled, modified);
|
||||
},
|
||||
|
||||
/**
|
||||
* Internally initializes the page. This runs only when/if the feature
|
||||
* is/gets enabled.
|
||||
@ -78,29 +61,27 @@ let gPage = {
|
||||
this._initialized = true;
|
||||
|
||||
gLinks.populateCache(function () {
|
||||
// Check if the grid is modified.
|
||||
this.updateModifiedFlag();
|
||||
|
||||
// Initialize and render the grid.
|
||||
gGrid.init(this._gridSelector);
|
||||
gGrid.init();
|
||||
|
||||
// Initialize the drop target shim.
|
||||
gDropTargetShim.init();
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// Workaround to prevent a delay on MacOSX due to a slow drop animation.
|
||||
document.addEventListener("dragover", this.onDragOver, false);
|
||||
document.addEventListener("drop", this.onDrop, false);
|
||||
document.addEventListener("dragover", this, false);
|
||||
document.addEventListener("drop", this, false);
|
||||
#endif
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the 'page-disabled' attributes of the respective DOM nodes.
|
||||
* @param aValue Whether to set or remove attributes.
|
||||
* @param aValue Whether the New Tab Page is enabled or not.
|
||||
*/
|
||||
_updateAttributes: function Page_updateAttributes(aValue) {
|
||||
let nodes = document.querySelectorAll("#grid, #scrollbox, #toolbar, .toolbar-button");
|
||||
let selector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid";
|
||||
let nodes = document.querySelectorAll(selector);
|
||||
|
||||
// Set the nodes' states.
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
@ -111,64 +92,32 @@ let gPage = {
|
||||
node.setAttribute("page-disabled", "true");
|
||||
}
|
||||
|
||||
this._updateTabIndices(aValue, this._isModified());
|
||||
// Update the toggle button's title.
|
||||
let toggle = document.getElementById("newtab-toggle");
|
||||
toggle.setAttribute("title", newTabString(aValue ? "hide" : "show"));
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether the page is modified.
|
||||
* @return Whether the page is modified or not.
|
||||
* Handles all page events.
|
||||
*/
|
||||
_isModified: function Page_isModified() {
|
||||
// The page is considered modified only if sites have been removed.
|
||||
return !gBlockedLinks.isEmpty();
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the tab indices of focusable elements.
|
||||
* @param aEnabled Whether the page is currently enabled.
|
||||
* @param aModified Whether the page is currently modified.
|
||||
*/
|
||||
_updateTabIndices: function Page_updateTabIndices(aEnabled, aModified) {
|
||||
function setFocusable(aNode, aFocusable) {
|
||||
if (aFocusable)
|
||||
aNode.removeAttribute("tabindex");
|
||||
else
|
||||
aNode.setAttribute("tabindex", "-1");
|
||||
}
|
||||
|
||||
// Sites and the 'hide' button are always focusable when the grid is shown.
|
||||
let nodes = document.querySelectorAll(".site, #toolbar-button-hide");
|
||||
for (let i = 0; i < nodes.length; i++)
|
||||
setFocusable(nodes[i], aEnabled);
|
||||
|
||||
// The 'show' button is focusable when the grid is hidden.
|
||||
let btnShow = document.getElementById("toolbar-button-show");
|
||||
setFocusable(btnShow, !aEnabled);
|
||||
|
||||
// The 'reset' button is focusable when the grid is shown and modified.
|
||||
let btnReset = document.getElementById("toolbar-button-reset");
|
||||
setFocusable(btnReset, aEnabled && aModified);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'dragover' event. Workaround to prevent a delay on MacOSX
|
||||
* due to a slow drop animation.
|
||||
* @param aEvent The 'dragover' event.
|
||||
*/
|
||||
onDragOver: function Page_onDragOver(aEvent) {
|
||||
handleEvent: function Page_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "unload":
|
||||
gAllPages.unregister(this);
|
||||
break;
|
||||
case "click":
|
||||
gAllPages.enabled = !gAllPages.enabled;
|
||||
break;
|
||||
case "dragover":
|
||||
if (gDrag.isValid(aEvent) && gDrag.draggedSite)
|
||||
aEvent.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'drop' event. Workaround to prevent a delay on MacOSX due to
|
||||
* a slow drop animation.
|
||||
* @param aEvent The 'drop' event.
|
||||
*/
|
||||
onDrop: function Page_onDrop(aEvent) {
|
||||
break;
|
||||
case "drop":
|
||||
if (gDrag.isValid(aEvent) && gDrag.draggedSite) {
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -82,12 +82,16 @@ Site.prototype = {
|
||||
/**
|
||||
* Blocks the site (removes it from the grid) and calls the given callback
|
||||
* when done.
|
||||
* @param aCallback The callback to be called when finished.
|
||||
* @param aCallback The function to be called when finished.
|
||||
*/
|
||||
block: function Site_block(aCallback) {
|
||||
if (gBlockedLinks.isBlocked(this._link)) {
|
||||
if (aCallback)
|
||||
aCallback();
|
||||
} else {
|
||||
gBlockedLinks.block(this._link);
|
||||
gUpdater.updateGrid(aCallback);
|
||||
gPage.updateModifiedFlag();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -105,14 +109,14 @@ Site.prototype = {
|
||||
* @param aPinned Whether this site is now pinned or unpinned.
|
||||
*/
|
||||
_updateAttributes: function (aPinned) {
|
||||
let buttonPin = this._querySelector(".strip-button-pin");
|
||||
let control = this._querySelector(".newtab-control-pin");
|
||||
|
||||
if (aPinned) {
|
||||
this.node.setAttribute("pinned", true);
|
||||
buttonPin.setAttribute("title", newTabString("unpin"));
|
||||
control.setAttribute("pinned", true);
|
||||
control.setAttribute("title", newTabString("unpin"));
|
||||
} else {
|
||||
this.node.removeAttribute("pinned");
|
||||
buttonPin.setAttribute("title", newTabString("pin"));
|
||||
control.removeAttribute("pinned");
|
||||
control.setAttribute("title", newTabString("pin"));
|
||||
}
|
||||
},
|
||||
|
||||
@ -121,32 +125,17 @@ Site.prototype = {
|
||||
*/
|
||||
_render: function Site_render() {
|
||||
let title = this.title || this.url;
|
||||
this.node.setAttribute("title", title);
|
||||
this.node.setAttribute("href", this.url);
|
||||
this._querySelector(".site-title").textContent = title;
|
||||
let link = this._querySelector(".newtab-link");
|
||||
link.setAttribute("title", title);
|
||||
link.setAttribute("href", this.url);
|
||||
this._querySelector(".newtab-title").textContent = title;
|
||||
|
||||
if (this.isPinned())
|
||||
this._updateAttributes(true);
|
||||
|
||||
this._renderThumbnail();
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the site's thumbnail.
|
||||
*/
|
||||
_renderThumbnail: function Site_renderThumbnail() {
|
||||
let img = this._querySelector(".site-img")
|
||||
img.setAttribute("alt", this.title || this.url);
|
||||
img.setAttribute("loading", "true");
|
||||
|
||||
// Wait until the image has loaded.
|
||||
img.addEventListener("load", function onLoad() {
|
||||
img.removeEventListener("load", onLoad, false);
|
||||
img.removeAttribute("loading");
|
||||
}, false);
|
||||
|
||||
// Set the thumbnail url.
|
||||
img.setAttribute("src", PageThumbs.getThumbnailURL(this.url));
|
||||
let thumbnailURL = PageThumbs.getThumbnailURL(this.url);
|
||||
let thumbnail = this._querySelector(".newtab-thumbnail");
|
||||
thumbnail.style.backgroundImage = "url(" + thumbnailURL + ")";
|
||||
},
|
||||
|
||||
/**
|
||||
@ -154,56 +143,37 @@ Site.prototype = {
|
||||
*/
|
||||
_addEventHandlers: function Site_addEventHandlers() {
|
||||
// Register drag-and-drop event handlers.
|
||||
["DragStart", /*"Drag",*/ "DragEnd"].forEach(function (aType) {
|
||||
let method = "_on" + aType;
|
||||
this[method] = this[method].bind(this);
|
||||
this._node.addEventListener(aType.toLowerCase(), this[method], false);
|
||||
}, this);
|
||||
this._node.addEventListener("dragstart", this, false);
|
||||
this._node.addEventListener("dragend", this, false);
|
||||
|
||||
let self = this;
|
||||
let controls = this.node.querySelectorAll(".newtab-control");
|
||||
for (let i = 0; i < controls.length; i++)
|
||||
controls[i].addEventListener("click", this, false);
|
||||
},
|
||||
|
||||
function pin(aEvent) {
|
||||
if (aEvent)
|
||||
/**
|
||||
* Handles all site events.
|
||||
*/
|
||||
handleEvent: function Site_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "click":
|
||||
aEvent.preventDefault();
|
||||
|
||||
if (self.isPinned())
|
||||
self.unpin();
|
||||
if (aEvent.target.classList.contains("newtab-control-block"))
|
||||
this.block();
|
||||
else if (this.isPinned())
|
||||
this.unpin();
|
||||
else
|
||||
self.pin();
|
||||
}
|
||||
|
||||
function block(aEvent) {
|
||||
if (aEvent)
|
||||
aEvent.preventDefault();
|
||||
|
||||
self.block();
|
||||
}
|
||||
|
||||
this._querySelector(".strip-button-pin").addEventListener("click", pin, false);
|
||||
this._querySelector(".strip-button-block").addEventListener("click", block, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'dragstart' event.
|
||||
* @param aEvent The drag event.
|
||||
*/
|
||||
_onDragStart: function Site_onDragStart(aEvent) {
|
||||
this.pin();
|
||||
break;
|
||||
case "dragstart":
|
||||
gDrag.start(this, aEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'drag' event.
|
||||
* @param aEvent The drag event.
|
||||
*/
|
||||
_onDrag: function Site_onDrag(aEvent) {
|
||||
break;
|
||||
case "drag":
|
||||
gDrag.drag(this, aEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'dragend' event.
|
||||
* @param aEvent The drag event.
|
||||
*/
|
||||
_onDragEnd: function Site_onDragEnd(aEvent) {
|
||||
break;
|
||||
case "dragend":
|
||||
gDrag.end(this, aEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,87 +0,0 @@
|
||||
#ifdef 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This singleton represents the page's toolbar that allows to enable/disable
|
||||
* the 'New Tab Page' feature and to reset the whole page.
|
||||
*/
|
||||
let gToolbar = {
|
||||
/**
|
||||
* Initializes the toolbar.
|
||||
* @param aSelector The query selector of the toolbar.
|
||||
*/
|
||||
init: function Toolbar_init(aSelector) {
|
||||
this._node = document.querySelector(aSelector);
|
||||
let buttons = this._node.querySelectorAll("input");
|
||||
|
||||
// Listen for 'click' events on the toolbar buttons.
|
||||
["show", "hide", "reset"].forEach(function (aType, aIndex) {
|
||||
let self = this;
|
||||
let button = buttons[aIndex];
|
||||
let handler = function () self[aType]();
|
||||
|
||||
button.addEventListener("click", handler, false);
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// Per default buttons lose focus after being clicked on Mac OS X.
|
||||
// So when the URL bar has focus and a toolbar button is clicked the
|
||||
// URL bar regains focus and the history pops up. We need to prevent
|
||||
// that by explicitly removing its focus.
|
||||
button.addEventListener("mousedown", function () {
|
||||
window.focus();
|
||||
}, false);
|
||||
#endif
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables the 'New Tab Page' feature.
|
||||
*/
|
||||
show: function Toolbar_show() {
|
||||
this._passButtonFocus("show", "hide");
|
||||
gAllPages.enabled = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disables the 'New Tab Page' feature.
|
||||
*/
|
||||
hide: function Toolbar_hide() {
|
||||
this._passButtonFocus("hide", "show");
|
||||
gAllPages.enabled = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the whole page and forces it to re-render its content.
|
||||
* @param aCallback The callback to call when the page has been reset.
|
||||
*/
|
||||
reset: function Toolbar_reset(aCallback) {
|
||||
this._passButtonFocus("reset", "hide");
|
||||
let node = gGrid.node;
|
||||
|
||||
// animate the page reset
|
||||
gTransformation.fadeNodeOut(node, function () {
|
||||
NewTabUtils.reset();
|
||||
|
||||
gLinks.populateCache(function () {
|
||||
gAllPages.update();
|
||||
|
||||
// Without the setTimeout() we have a strange flicker.
|
||||
setTimeout(function () gTransformation.fadeNodeIn(node, aCallback));
|
||||
}, true);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Passes the focus from the current button to the next.
|
||||
* @param aCurrent The button that currently has focus.
|
||||
* @param aNext The button that is focused next.
|
||||
*/
|
||||
_passButtonFocus: function Toolbar_passButtonFocus(aCurrent, aNext) {
|
||||
if (document.querySelector("#toolbar-button-" + aCurrent + ":-moz-focusring"))
|
||||
document.getElementById("toolbar-button-" + aNext).focus();
|
||||
}
|
||||
};
|
||||
|
@ -10,6 +10,24 @@
|
||||
* convenience methods to work with a site's DOM node.
|
||||
*/
|
||||
let gTransformation = {
|
||||
/**
|
||||
* Returns the width of the left and top border of a cell. We need to take it
|
||||
* into account when measuring and comparing site and cell positions.
|
||||
*/
|
||||
get _cellBorderWidths() {
|
||||
let cstyle = window.getComputedStyle(gGrid.cells[0].node, null);
|
||||
let widths = {
|
||||
left: parseInt(cstyle.getPropertyValue("border-left-width")),
|
||||
top: parseInt(cstyle.getPropertyValue("border-top-width"))
|
||||
};
|
||||
|
||||
// Cache this value, overwrite the getter.
|
||||
Object.defineProperty(this, "_cellBorderWidths",
|
||||
{value: widths, enumerable: true});
|
||||
|
||||
return widths;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a DOM node's position.
|
||||
* @param aNode The DOM node.
|
||||
@ -80,6 +98,14 @@ let gTransformation = {
|
||||
* @param aSite The site to freeze.
|
||||
*/
|
||||
freezeSitePosition: function Transformation_freezeSitePosition(aSite) {
|
||||
if (this._isFrozen(aSite))
|
||||
return;
|
||||
|
||||
let style = aSite.node.style;
|
||||
let comp = getComputedStyle(aSite.node, null);
|
||||
style.width = comp.getPropertyValue("width")
|
||||
style.height = comp.getPropertyValue("height");
|
||||
|
||||
aSite.node.setAttribute("frozen", "true");
|
||||
this.setSitePosition(aSite, this.getNodePosition(aSite.node));
|
||||
},
|
||||
@ -89,8 +115,11 @@ let gTransformation = {
|
||||
* @param aSite The site to unfreeze.
|
||||
*/
|
||||
unfreezeSitePosition: function Transformation_unfreezeSitePosition(aSite) {
|
||||
if (!this._isFrozen(aSite))
|
||||
return;
|
||||
|
||||
let style = aSite.node.style;
|
||||
style.left = style.top = "";
|
||||
style.left = style.top = style.width = style.height = "";
|
||||
aSite.node.removeAttribute("frozen");
|
||||
},
|
||||
|
||||
@ -117,8 +146,13 @@ let gTransformation = {
|
||||
callback();
|
||||
}
|
||||
|
||||
// We need to take the width of a cell's border into account.
|
||||
targetPosition.left += this._cellBorderWidths.left;
|
||||
targetPosition.top += this._cellBorderWidths.top;
|
||||
|
||||
// Nothing to do here if the positions already match.
|
||||
if (currentPosition.equals(targetPosition)) {
|
||||
if (currentPosition.left == targetPosition.left &&
|
||||
currentPosition.top == targetPosition.top) {
|
||||
finish();
|
||||
} else {
|
||||
this.setSitePosition(aSite, targetPosition);
|
||||
@ -222,5 +256,14 @@ let gTransformation = {
|
||||
_moveSite: function Transformation_moveSite(aSite, aIndex, aOptions) {
|
||||
this.freezeSitePosition(aSite);
|
||||
this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether a site is currently frozen.
|
||||
* @param aSite The site to check.
|
||||
* @return Whether the given site is frozen.
|
||||
*/
|
||||
_isFrozen: function Transformation_isFrozen(aSite) {
|
||||
return aSite.node.hasAttribute("frozen");
|
||||
}
|
||||
};
|
||||
|
@ -2,41 +2,33 @@ function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var pageInfo;
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function () {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
pageInfo = BrowserPageInfo();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function loadListener() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
|
||||
|
||||
Services.obs.addObserver(observer, "page-info-dialog-loaded", false);
|
||||
pageInfo = BrowserPageInfo();
|
||||
}, true);
|
||||
content.location =
|
||||
"https://example.com/browser/browser/base/content/test/feed_tab.html";
|
||||
|
||||
function observer(win, topic, data) {
|
||||
if (topic != "page-info-dialog-loaded")
|
||||
return;
|
||||
|
||||
Services.obs.removeObserver(observer, topic);
|
||||
Services.obs.removeObserver(observer, "page-info-dialog-loaded");
|
||||
handlePageInfo();
|
||||
}
|
||||
|
||||
function $(aId) { return pageInfo.document.getElementById(aId) };
|
||||
|
||||
function handlePageInfo() {
|
||||
var feedTab = $("feedTab");
|
||||
var feedListbox = $("feedListbox");
|
||||
|
||||
ok(feedListbox, "Feed list is null (feeds tab is broken)");
|
||||
ok(pageInfo.document.getElementById("feedTab"), "Feed tab");
|
||||
let feedListbox = pageInfo.document.getElementById("feedListbox");
|
||||
ok(feedListbox, "Feed list");
|
||||
|
||||
var feedRowsNum = feedListbox.getRowCount();
|
||||
|
||||
ok(feedRowsNum == 3, "Number of feeds listed: " +
|
||||
feedRowsNum + ", should be 3");
|
||||
|
||||
is(feedRowsNum, 3, "Number of feeds listed");
|
||||
|
||||
for (var i = 0; i < feedRowsNum; i++) {
|
||||
let feedItem = feedListbox.getItemAtIndex(i);
|
||||
ok(feedItem.getAttribute("name") == (i+1),
|
||||
"Name given: " + feedItem.getAttribute("name") + ", should be " + (i+1));
|
||||
is(feedItem.getAttribute("name"), i + 1, "Feed name");
|
||||
}
|
||||
|
||||
pageInfo.close();
|
||||
|
@ -23,6 +23,8 @@ _BROWSER_FILES = \
|
||||
browser_newtab_bug722273.js \
|
||||
browser_newtab_bug723102.js \
|
||||
browser_newtab_bug723121.js \
|
||||
browser_newtab_bug725996.js \
|
||||
browser_newtab_bug734043.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
@ -12,6 +12,7 @@ function runTests() {
|
||||
yield addNewTabPageTab();
|
||||
gBrowser.removeTab(firstTab);
|
||||
|
||||
cw.gToolbar.hide();
|
||||
ok(NewTabUtils.allPages.enabled, true, "page is enabled");
|
||||
NewTabUtils.allPages.enabled = false;
|
||||
ok(cw.gGrid.node.hasAttribute("page-disabled"), "page is disabled");
|
||||
}
|
||||
|
@ -10,15 +10,19 @@ function runTests() {
|
||||
|
||||
let cell = cells[0].node;
|
||||
let site = cells[0].site.node;
|
||||
let link = site.querySelector(".newtab-link");
|
||||
|
||||
sendDragEvent(site, "dragstart");
|
||||
sendDragEvent(link, "dragstart");
|
||||
checkGridLocked(true, "grid is now locked");
|
||||
|
||||
sendDragEvent(site, "dragend");
|
||||
sendDragEvent(link, "dragend");
|
||||
checkGridLocked(false, "grid isn't locked anymore");
|
||||
|
||||
sendDragEvent(cell, "dragstart");
|
||||
checkGridLocked(false, "grid isn't locked - dragstart was ignored");
|
||||
|
||||
sendDragEvent(site, "dragstart");
|
||||
checkGridLocked(false, "grid isn't locked - dragstart was ignored");
|
||||
}
|
||||
|
||||
function checkGridLocked(aLocked, aMessage) {
|
||||
|
52
browser/base/content/test/newtab/browser_newtab_bug725996.js
Normal file
52
browser/base/content/test/newtab/browser_newtab_bug725996.js
Normal file
@ -0,0 +1,52 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function runTests() {
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
|
||||
let cell = cells[0].node;
|
||||
|
||||
sendDropEvent(cell, "about:blank#99\nblank");
|
||||
is(NewTabUtils.pinnedLinks.links[0].url, "about:blank#99",
|
||||
"first cell is pinned and contains the dropped site");
|
||||
|
||||
yield whenPagesUpdated();
|
||||
checkGrid("99p,0,1,2,3,4,5,6,7");
|
||||
|
||||
sendDropEvent(cell, "");
|
||||
is(NewTabUtils.pinnedLinks.links[0].url, "about:blank#99",
|
||||
"first cell is still pinned with the site we dropped before");
|
||||
}
|
||||
|
||||
function sendDropEvent(aNode, aData) {
|
||||
let ifaceReq = cw.QueryInterface(Ci.nsIInterfaceRequestor);
|
||||
let windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
let dataTransfer = {
|
||||
mozUserCancelled: false,
|
||||
setData: function () null,
|
||||
setDragImage: function () null,
|
||||
getData: function () aData,
|
||||
|
||||
types: {
|
||||
contains: function (aType) aType == "text/x-moz-url"
|
||||
},
|
||||
|
||||
mozGetDataAt: function (aType, aIndex) {
|
||||
if (aIndex || aType != "text/x-moz-url")
|
||||
return null;
|
||||
|
||||
return aData;
|
||||
},
|
||||
};
|
||||
|
||||
let event = cw.document.createEvent("DragEvents");
|
||||
event.initDragEvent("drop", true, true, cw, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, null, dataTransfer);
|
||||
|
||||
windowUtils.dispatchDOMEventViaPresShell(aNode, event, true);
|
||||
}
|
30
browser/base/content/test/newtab/browser_newtab_bug734043.js
Normal file
30
browser/base/content/test/newtab/browser_newtab_bug734043.js
Normal file
@ -0,0 +1,30 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function runTests() {
|
||||
// TODO Bug 735166 - Intermittent timeout in browser_newtab_bug734043.js
|
||||
return;
|
||||
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
|
||||
let receivedError = false;
|
||||
let block = cw.document.querySelector(".newtab-control-block");
|
||||
|
||||
function onError() {
|
||||
receivedError = true;
|
||||
}
|
||||
|
||||
cw.addEventListener("error", onError);
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
EventUtils.synthesizeMouseAtCenter(block, {}, cw);
|
||||
yield executeSoon(TestRunner.next);
|
||||
}
|
||||
|
||||
yield whenPagesUpdated();
|
||||
ok(!receivedError, "we got here without any errors");
|
||||
cw.removeEventListener("error", onError);
|
||||
}
|
@ -15,7 +15,7 @@ function runTests() {
|
||||
|
||||
ok(!gridNode.hasAttribute("page-disabled"), "page is not disabled");
|
||||
|
||||
cw.gToolbar.hide();
|
||||
NewTabUtils.allPages.enabled = false;
|
||||
ok(gridNode.hasAttribute("page-disabled"), "page is disabled");
|
||||
|
||||
let oldGridNode = cw.gGrid.node;
|
||||
@ -28,7 +28,7 @@ function runTests() {
|
||||
// check that no sites have been rendered
|
||||
is(0, cw.document.querySelectorAll(".site").length, "no sites have been rendered");
|
||||
|
||||
cw.gToolbar.show();
|
||||
NewTabUtils.allPages.enabled = true;
|
||||
ok(!gridNode.hasAttribute("page-disabled"), "page is not disabled");
|
||||
ok(!oldGridNode.hasAttribute("page-disabled"), "old page is not disabled");
|
||||
}
|
||||
|
@ -5,6 +5,9 @@
|
||||
* These tests make sure that resetting the 'New Tage Page' works as expected.
|
||||
*/
|
||||
function runTests() {
|
||||
// Disabled until bug 716543 is fixed.
|
||||
return;
|
||||
|
||||
// create a new tab page and check its modified state after blocking a site
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("");
|
||||
|
@ -8,6 +8,9 @@
|
||||
* state.
|
||||
*/
|
||||
function runTests() {
|
||||
// Disabled until bug 716543 is fixed.
|
||||
return;
|
||||
|
||||
setLinks("0,1,2,3,4,5,6,7,8,9");
|
||||
setPinnedLinks(",1");
|
||||
|
||||
|
@ -188,7 +188,7 @@ function checkGrid(aSitesPattern, aSites) {
|
||||
|
||||
let shouldBePinned = /p$/.test(id);
|
||||
let cellContainsPinned = site.isPinned();
|
||||
let cssClassPinned = site.node && site.node.hasAttribute("pinned");
|
||||
let cssClassPinned = site.node && site.node.querySelector(".newtab-control-pin").hasAttribute("pinned");
|
||||
|
||||
// Check if the site should be and is pinned.
|
||||
if (shouldBePinned) {
|
||||
@ -265,3 +265,20 @@ function simulateDrop(aDropTarget, aDragSource) {
|
||||
if (aDragSource)
|
||||
cw.gDrag.end(aDragSource.site);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes testing when all pages have been updated.
|
||||
*/
|
||||
function whenPagesUpdated() {
|
||||
let page = {
|
||||
update: function () {
|
||||
NewTabUtils.allPages.unregister(this);
|
||||
executeSoon(TestRunner.next);
|
||||
}
|
||||
};
|
||||
|
||||
NewTabUtils.allPages.register(page);
|
||||
registerCleanupFunction(function () {
|
||||
NewTabUtils.allPages.unregister(page);
|
||||
});
|
||||
}
|
||||
|
@ -44,7 +44,23 @@ Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "BROWSER_NEW_TAB_URL", function () {
|
||||
return Services.prefs.getCharPref("browser.newtab.url") || "about:blank";
|
||||
const PREF = "browser.newtab.url";
|
||||
|
||||
function getNewTabPageURL() {
|
||||
return Services.prefs.getCharPref(PREF) || "about:blank";
|
||||
}
|
||||
|
||||
function update() {
|
||||
BROWSER_NEW_TAB_URL = getNewTabPageURL();
|
||||
}
|
||||
|
||||
Services.prefs.addObserver(PREF, update, false);
|
||||
addEventListener("unload", function onUnload() {
|
||||
removeEventListener("unload", onUnload);
|
||||
Services.prefs.removeObserver(PREF, update);
|
||||
});
|
||||
|
||||
return getNewTabPageURL();
|
||||
});
|
||||
|
||||
var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
|
||||
|
@ -98,6 +98,12 @@ source-package::
|
||||
upload::
|
||||
@$(MAKE) -C browser/installer upload
|
||||
|
||||
source-upload::
|
||||
@$(MAKE) -C browser/installer source-upload
|
||||
|
||||
hg-bundle::
|
||||
@$(MAKE) -C browser/installer hg-bundle
|
||||
|
||||
l10n-check::
|
||||
@$(MAKE) -C browser/locales l10n-check
|
||||
|
||||
|
@ -67,6 +67,9 @@ XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "KeywordURLResetPrompter",
|
||||
"resource:///modules/KeywordURLResetPrompter.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "webappsUI",
|
||||
"resource://gre/modules/webappsUI.jsm");
|
||||
|
||||
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
|
||||
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
|
||||
|
||||
@ -346,6 +349,7 @@ BrowserGlue.prototype = {
|
||||
if (this._isPlacesShutdownObserver)
|
||||
os.removeObserver(this, "places-shutdown");
|
||||
os.removeObserver(this, "defaultURIFixup-using-keyword-pref");
|
||||
webappsUI.uninit();
|
||||
},
|
||||
|
||||
_onAppDefaults: function BG__onAppDefaults() {
|
||||
@ -370,6 +374,9 @@ BrowserGlue.prototype = {
|
||||
// handle any UI migration
|
||||
this._migrateUI();
|
||||
|
||||
// Initialize webapps UI
|
||||
webappsUI.init();
|
||||
|
||||
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
|
||||
},
|
||||
|
||||
@ -405,7 +412,6 @@ BrowserGlue.prototype = {
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Show update notification, if needed.
|
||||
if (Services.prefs.prefHasUserValue("app.update.postupdate"))
|
||||
this._showUpdateNotification();
|
||||
|
@ -376,31 +376,26 @@ PlacesViewBase.prototype = {
|
||||
_setLivemarkStatusMenuItem:
|
||||
function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) {
|
||||
let statusMenuitem = aPopup._statusMenuitem;
|
||||
let stringId = "";
|
||||
if (aStatus == Ci.mozILivemark.STATUS_LOADING)
|
||||
stringId = "bookmarksLivemarkLoading";
|
||||
else if (aStatus == Ci.mozILivemark.STATUS_FAILED)
|
||||
stringId = "bookmarksLivemarkFailed";
|
||||
|
||||
if (stringId && !statusMenuitem) {
|
||||
if (!statusMenuitem) {
|
||||
// Create the status menuitem and cache it in the popup object.
|
||||
statusMenuitem = document.createElement("menuitem");
|
||||
statusMenuitem.setAttribute("livemarkStatus", stringId);
|
||||
statusMenuitem.className = "livemarkstatus-menuitem";
|
||||
statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
|
||||
statusMenuitem.setAttribute("disabled", true);
|
||||
aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
|
||||
aPopup._statusMenuitem = statusMenuitem;
|
||||
}
|
||||
else if (stringId &&
|
||||
statusMenuitem.getAttribute("livemarkStatus") != stringId) {
|
||||
|
||||
if (aStatus == Ci.mozILivemark.STATUS_LOADING ||
|
||||
aStatus == Ci.mozILivemark.STATUS_FAILED) {
|
||||
// Status has changed, update the cached status menuitem.
|
||||
let stringId = aStatus == Ci.mozILivemark.STATUS_LOADING ?
|
||||
"bookmarksLivemarkLoading" : "bookmarksLivemarkFailed";
|
||||
statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
|
||||
if (aPopup._startMarker.nextSibling != statusMenuitem)
|
||||
aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
|
||||
}
|
||||
else if (!stringId && statusMenuitem) {
|
||||
else {
|
||||
// The livemark has finished loading.
|
||||
aPopup.removeChild(aPopup._statusMenuitem);
|
||||
aPopup._statusMenuitem = null;
|
||||
}
|
||||
},
|
||||
|
||||
@ -892,6 +887,14 @@ function PlacesToolbar(aPlace) {
|
||||
this._addEventListeners(this._rootElt, ["overflow", "underflow"], true);
|
||||
this._addEventListeners(window, ["resize", "unload"], false);
|
||||
|
||||
// If personal-bookmarks has been dragged to the tabs toolbar,
|
||||
// we have to track addition and removals of tabs, to properly
|
||||
// recalculate the available space for bookmarks.
|
||||
// TODO (bug 734730): Use a performant mutation listener when available.
|
||||
if (this._viewElt.parentNode.parentNode == document.getElementById("TabsToolbar")) {
|
||||
this._addEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
|
||||
}
|
||||
|
||||
PlacesViewBase.call(this, aPlace);
|
||||
|
||||
Services.telemetry.getHistogramById("FX_BOOKMARKS_TOOLBAR_INIT_MS")
|
||||
@ -918,6 +921,7 @@ PlacesToolbar.prototype = {
|
||||
true);
|
||||
this._removeEventListeners(this._rootElt, ["overflow", "underflow"], true);
|
||||
this._removeEventListeners(window, ["resize", "unload"], false);
|
||||
this._removeEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
|
||||
|
||||
PlacesViewBase.prototype.uninit.apply(this, arguments);
|
||||
},
|
||||
@ -1017,9 +1021,10 @@ PlacesToolbar.prototype = {
|
||||
|
||||
_updateChevronPopupNodesVisibility:
|
||||
function PT__updateChevronPopupNodesVisibility() {
|
||||
for (let i = 0; i < this._chevronPopup.childNodes.length; i++) {
|
||||
this._chevronPopup.childNodes[i].hidden =
|
||||
this._rootElt.childNodes[i].style.visibility != "hidden";
|
||||
for (let i = 0, node = this._chevronPopup._startMarker.nextSibling;
|
||||
node != this._chevronPopup._endMarker;
|
||||
i++, node = node.nextSibling) {
|
||||
node.hidden = this._rootElt.childNodes[i].style.visibility != "hidden";
|
||||
}
|
||||
},
|
||||
|
||||
@ -1071,7 +1076,11 @@ PlacesToolbar.prototype = {
|
||||
if (aEvent.detail == 0)
|
||||
return;
|
||||
|
||||
this.updateChevron();
|
||||
this._chevron.collapsed = true;
|
||||
break;
|
||||
case "TabOpen":
|
||||
case "TabClose":
|
||||
this.updateChevron();
|
||||
break;
|
||||
case "dragstart":
|
||||
|
@ -18,7 +18,7 @@ const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
||||
* Hint: This is the default value because the 'New Tab Page' is the only
|
||||
* client for now.
|
||||
*/
|
||||
const THUMBNAIL_WIDTH = 201;
|
||||
const THUMBNAIL_WIDTH = 400;
|
||||
|
||||
/**
|
||||
* The default height for page thumbnails.
|
||||
@ -26,7 +26,7 @@ const THUMBNAIL_WIDTH = 201;
|
||||
* Hint: This is the default value because the 'New Tab Page' is the only
|
||||
* client for now.
|
||||
*/
|
||||
const THUMBNAIL_HEIGHT = 127;
|
||||
const THUMBNAIL_HEIGHT = 225;
|
||||
|
||||
/**
|
||||
* The default background color for page thumbnails.
|
||||
|
@ -18,3 +18,6 @@ ac_add_options --with-ccache=/usr/bin/ccache
|
||||
|
||||
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
|
||||
ac_add_options --enable-warnings-as-errors
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -27,3 +27,6 @@ ac_add_options --with-ccache=/usr/bin/ccache
|
||||
|
||||
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
|
||||
ac_add_options --enable-warnings-as-errors
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -21,3 +21,6 @@ ac_add_options --enable-warnings-as-errors
|
||||
|
||||
# Enable parallel compiling
|
||||
mk_add_options MOZ_MAKE_FLAGS="-j4"
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -15,3 +15,6 @@ mk_add_options MOZ_MAKE_FLAGS="-j4"
|
||||
|
||||
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
|
||||
ac_add_options --enable-warnings-as-errors
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -27,3 +27,6 @@ ac_add_options --with-ccache=/usr/bin/ccache
|
||||
|
||||
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
|
||||
ac_add_options --enable-warnings-as-errors
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -21,3 +21,6 @@ ac_add_options --enable-warnings-as-errors
|
||||
|
||||
# Enable parallel compiling
|
||||
mk_add_options MOZ_MAKE_FLAGS="-j4"
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -22,3 +22,6 @@ ac_add_options --with-macbundlename-prefix=Firefox
|
||||
|
||||
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
|
||||
ac_add_options --enable-warnings-as-errors
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -17,3 +17,6 @@ ac_add_options --enable-warnings-as-errors
|
||||
|
||||
# Enable parallel compiling
|
||||
mk_add_options MOZ_MAKE_FLAGS="-j4"
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -10,3 +10,6 @@ mk_add_options MOZ_MAKE_FLAGS="-j4"
|
||||
export MOZILLA_OFFICIAL=1
|
||||
|
||||
ac_add_options --with-macbundlename-prefix=Firefox
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -15,3 +15,6 @@ ac_add_options --with-macbundlename-prefix=Firefox
|
||||
|
||||
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
|
||||
ac_add_options --enable-warnings-as-errors
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -8,3 +8,6 @@ export MOZILLA_OFFICIAL=1
|
||||
mk_add_options MOZ_MAKE_FLAGS=-j1
|
||||
|
||||
. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -17,3 +17,6 @@ export MOZ_TELEMETRY_REPORTING=1
|
||||
mk_add_options MOZ_MAKE_FLAGS=-j1
|
||||
|
||||
. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -13,3 +13,6 @@ export MOZILLA_OFFICIAL=1
|
||||
export MOZ_TELEMETRY_REPORTING=1
|
||||
|
||||
. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
@ -9,3 +9,8 @@ ac_add_options --enable-signmar
|
||||
export MOZILLA_OFFICIAL=1
|
||||
|
||||
mk_add_options MOZ_MAKE_FLAGS=-j1
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
||||
. $topsrcdir/browser/config/mozconfigs/win64/vs2010-mozconfig
|
||||
|
@ -18,3 +18,8 @@ export MOZILLA_OFFICIAL=1
|
||||
export MOZ_TELEMETRY_REPORTING=1
|
||||
|
||||
mk_add_options MOZ_MAKE_FLAGS=-j1
|
||||
|
||||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
||||
. $topsrcdir/browser/config/mozconfigs/win64/vs2010-mozconfig
|
||||
|
16
browser/config/mozconfigs/win64/vs2010-mozconfig
Normal file
16
browser/config/mozconfigs/win64/vs2010-mozconfig
Normal file
@ -0,0 +1,16 @@
|
||||
export INCLUDE=/c/tools/msvs10/vc/include:/c/tools/msvs10/vc/atlmfc/include:/c/tools/sdks/v7.0/include:/c/tools/sdks/v7.0/include/atl:/c/tools/sdks/dx10/include
|
||||
export LIBPATH=/c/tools/msvs10/vc/lib/amd64:/c/tools/msvs10/vc/atlmfc/lib/amd64
|
||||
export LIB=/c/tools/msvs10/vc/lib/amd64:/c/tools/msvs10/vc/atlmfc/lib/amd64:/c/tools/sdks/v7.0/lib/x64:/c/tools/sdks/dx10/lib/x64
|
||||
export PATH="/c/tools/msvs10/Common7/IDE:/c/tools/msvs10/VC/BIN/amd64:/c/tools/msvs10/VC/BIN/x86_amd64:/c/tools/msvs10/VC/BIN:/c/tools/msvs10/Common7/Tools:/c/tools/msvs10/VC/VCPackages:${PATH}"
|
||||
export WIN32_REDIST_DIR=/c/tools/msvs10/VC/redist/x64/Microsoft.VC100.CRT
|
||||
|
||||
# Use 32bit linker for PGO crash bug.
|
||||
# https://connect.microsoft.com/VisualStudio/feedback/details/686117/
|
||||
export LD=/c/tools/msvs10/VC/BIN/x86_amd64/link.exe
|
||||
|
||||
|
||||
mk_add_options "export LIB=$LIB"
|
||||
mk_add_options "export LIBPATH=$LIBPATH"
|
||||
mk_add_options "export PATH=$PATH"
|
||||
mk_add_options "export INCLUDE=$INCLUDE"
|
||||
mk_add_options "export WIN32_REDIST_DIR=$WIN32_REDIST_DIR"
|
@ -1 +1 @@
|
||||
13.0a1
|
||||
14.0a1
|
||||
|
@ -71,10 +71,11 @@ _BROWSER_TEST_FILES = \
|
||||
browser_dbg_script-switching.js \
|
||||
browser_dbg_pause-resume.js \
|
||||
browser_dbg_update-editor-mode.js \
|
||||
browser_dbg_select-line.js \
|
||||
$(warning browser_dbg_select-line.js temporarily disabled due to oranges, see bug 726609) \
|
||||
browser_dbg_clean-exit.js \
|
||||
browser_dbg_bug723069_editor-breakpoints.js \
|
||||
browser_dbg_bug731394_editor-contextmenu.js \
|
||||
browser_dbg_displayName.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
@ -89,6 +90,7 @@ _BROWSER_TEST_PAGES = \
|
||||
browser_dbg_frame-parameters.html \
|
||||
browser_dbg_update-editor-mode.html \
|
||||
test-editor-mode \
|
||||
browser_dbg_displayName.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_TEST_FILES)
|
||||
|
26
browser/devtools/debugger/test/browser_dbg_displayName.html
Normal file
26
browser/devtools/debugger/test/browser_dbg_displayName.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head><title>Browser Debugger Test Tab</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript">
|
||||
|
||||
var a = function() {
|
||||
return function() {
|
||||
debugger;
|
||||
}
|
||||
}
|
||||
|
||||
var anon = a();
|
||||
anon.displayName = "anonFunc";
|
||||
|
||||
function evalCall() {
|
||||
eval("anon();");
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body></body>
|
||||
|
||||
</html>
|
53
browser/devtools/debugger/test/browser_dbg_displayName.js
Normal file
53
browser/devtools/debugger/test/browser_dbg_displayName.js
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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_displayName.html";
|
||||
|
||||
function test() {
|
||||
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.debuggerWindow;
|
||||
|
||||
testAnonCall();
|
||||
});
|
||||
}
|
||||
|
||||
function testAnonCall() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let frames = gDebugger.DebuggerView.Stackframes._frames;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
|
||||
"Should have three frames.");
|
||||
|
||||
is(frames.querySelector("#stackframe-0 .dbg-stackframe-name").textContent,
|
||||
"anonFunc", "Frame name should be anonFunc");
|
||||
|
||||
resumeAndFinish();
|
||||
}}, 0);
|
||||
});
|
||||
|
||||
gDebuggee.evalCall();
|
||||
}
|
||||
|
||||
function resumeAndFinish() {
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
removeTab(gTab);
|
||||
gPane = null;
|
||||
gDebuggee = null;
|
||||
finish();
|
||||
});
|
||||
}
|
@ -309,7 +309,7 @@ TreePanel.prototype = {
|
||||
|
||||
/**
|
||||
* Handle double-click events in the html tree panel.
|
||||
* (double-clicking an attribute value allows it to be edited)
|
||||
* Double-clicking an attribute name or value allows it to be edited.
|
||||
* @param aEvent
|
||||
* The mouse event.
|
||||
*/
|
||||
@ -322,19 +322,33 @@ TreePanel.prototype = {
|
||||
|
||||
let target = aEvent.target;
|
||||
|
||||
if (this.hasClass(target, "nodeValue")) {
|
||||
if (!this.hasClass(target, "editable")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let repObj = this.getRepObject(target);
|
||||
|
||||
if (this.hasClass(target, "nodeValue")) {
|
||||
let attrName = target.getAttribute("data-attributeName");
|
||||
let attrVal = target.innerHTML;
|
||||
|
||||
this.editAttributeValue(target, repObj, attrName, attrVal);
|
||||
this.editAttribute(target, repObj, attrName, attrVal);
|
||||
}
|
||||
|
||||
if (this.hasClass(target, "nodeName")) {
|
||||
let attrName = target.innerHTML;
|
||||
let attrValNode = target.nextSibling.nextSibling; // skip 2 (=)
|
||||
|
||||
if (attrValNode)
|
||||
this.editAttribute(target, repObj, attrName, attrValNode.innerHTML);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts the editor for an attribute value.
|
||||
* Starts the editor for an attribute name or value.
|
||||
* @param aAttrObj
|
||||
* The DOM object representing the attribute value in the HTML Tree
|
||||
* The DOM object representing the attribute name or value in the HTML
|
||||
* Tree.
|
||||
* @param aRepObj
|
||||
* The original DOM (target) object being inspected/edited
|
||||
* @param aAttrName
|
||||
@ -342,8 +356,8 @@ TreePanel.prototype = {
|
||||
* @param aAttrVal
|
||||
* The current value of the attribute being edited
|
||||
*/
|
||||
editAttributeValue:
|
||||
function TP_editAttributeValue(aAttrObj, aRepObj, aAttrName, aAttrVal)
|
||||
editAttribute:
|
||||
function TP_editAttribute(aAttrObj, aRepObj, aAttrName, aAttrVal)
|
||||
{
|
||||
let editor = this.treeBrowserDocument.getElementById("attribute-editor");
|
||||
let editorInput =
|
||||
@ -357,7 +371,8 @@ TreePanel.prototype = {
|
||||
this.editingContext = {
|
||||
attrObj: aAttrObj,
|
||||
repObj: aRepObj,
|
||||
attrName: aAttrName
|
||||
attrName: aAttrName,
|
||||
attrValue: aAttrVal
|
||||
};
|
||||
|
||||
// highlight attribute-value node in tree while editing
|
||||
@ -367,7 +382,7 @@ TreePanel.prototype = {
|
||||
this.addClass(editor, "editing");
|
||||
|
||||
// offset the editor below the attribute-value node being edited
|
||||
let editorVeritcalOffset = 2;
|
||||
let editorVerticalOffset = 2;
|
||||
|
||||
// keep the editor comfortably within the bounds of the viewport
|
||||
let editorViewportBoundary = 5;
|
||||
@ -384,7 +399,7 @@ TreePanel.prototype = {
|
||||
// center the editor against the attribute value
|
||||
((editorDims.width - attrDims.width) / 2);
|
||||
let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY +
|
||||
attrDims.height + editorVeritcalOffset;
|
||||
attrDims.height + editorVerticalOffset;
|
||||
|
||||
// but, make sure the editor stays within the visible viewport
|
||||
editorLeft = Math.max(0, Math.min(
|
||||
@ -403,8 +418,13 @@ TreePanel.prototype = {
|
||||
editor.style.top = editorTop + "px";
|
||||
|
||||
// set and select the text
|
||||
if (this.hasClass(aAttrObj, "nodeValue")) {
|
||||
editorInput.value = aAttrVal;
|
||||
editorInput.select();
|
||||
} else {
|
||||
editorInput.value = aAttrName;
|
||||
editorInput.select();
|
||||
}
|
||||
|
||||
// listen for editor specific events
|
||||
this.bindEditorEvent(editor, "click", function(aEvent) {
|
||||
@ -510,15 +530,32 @@ TreePanel.prototype = {
|
||||
{
|
||||
let editorInput =
|
||||
this.treeBrowserDocument.getElementById("attribute-editor-input");
|
||||
let dirty = false;
|
||||
|
||||
if (this.hasClass(this.editingContext.attrObj, "nodeValue")) {
|
||||
// set the new attribute value on the original target DOM element
|
||||
this.editingContext.repObj.setAttribute(this.editingContext.attrName,
|
||||
editorInput.value);
|
||||
|
||||
// update the HTML tree attribute value
|
||||
this.editingContext.attrObj.innerHTML = editorInput.value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
this.IUI.isDirty = true;
|
||||
if (this.hasClass(this.editingContext.attrObj, "nodeName")) {
|
||||
// remove the original attribute from the original target DOM element
|
||||
this.editingContext.repObj.removeAttribute(this.editingContext.attrName);
|
||||
|
||||
// set the new attribute value on the original target DOM element
|
||||
this.editingContext.repObj.setAttribute(editorInput.value,
|
||||
this.editingContext.attrValue);
|
||||
|
||||
// update the HTML tree attribute value
|
||||
this.editingContext.attrObj.innerHTML = editorInput.value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
this.IUI.isDirty = dirty;
|
||||
this.IUI.nodeChanged(this.registrationObject);
|
||||
|
||||
// event notification
|
||||
|
@ -27,6 +27,7 @@
|
||||
* Paul Rouget <paul@mozilla.com>
|
||||
* Kyle Simpson <ksimpson@mozilla.com>
|
||||
* Johan Charlez <johan.charlez@gmail.com>
|
||||
* Mike Ratcliffe <mratcliffe@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -104,6 +105,15 @@ function InspectorUI(aWindow)
|
||||
this.toolEvents = {};
|
||||
this.store = new InspectorStore();
|
||||
this.INSPECTOR_NOTIFICATIONS = INSPECTOR_NOTIFICATIONS;
|
||||
|
||||
// Set the tooltip of the inspect button.
|
||||
let keysbundle = Services.strings.createBundle(
|
||||
"chrome://global/locale/keys.properties");
|
||||
let returnString = keysbundle.GetStringFromName("VK_RETURN");
|
||||
let tooltip = this.strings.formatStringFromName("inspectButton.tooltiptext",
|
||||
[returnString], 1);
|
||||
let button = this.chromeDoc.getElementById("inspector-inspect-toolbutton");
|
||||
button.setAttribute("tooltiptext", tooltip);
|
||||
}
|
||||
|
||||
InspectorUI.prototype = {
|
||||
@ -845,9 +855,7 @@ InspectorUI.prototype = {
|
||||
*/
|
||||
copyInnerHTML: function IUI_copyInnerHTML()
|
||||
{
|
||||
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
|
||||
getService(Ci.nsIClipboardHelper);
|
||||
clipboard.copyString(this.selection.innerHTML);
|
||||
clipboardHelper.copyString(this.selection.innerHTML);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -856,9 +864,7 @@ InspectorUI.prototype = {
|
||||
*/
|
||||
copyOuterHTML: function IUI_copyOuterHTML()
|
||||
{
|
||||
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
|
||||
getService(Ci.nsIClipboardHelper);
|
||||
clipboard.copyString(this.selection.outerHTML);
|
||||
clipboardHelper.copyString(this.selection.outerHTML);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -926,12 +932,34 @@ InspectorUI.prototype = {
|
||||
|
||||
this.ruleView = new CssRuleView(doc, ruleViewStore);
|
||||
|
||||
// Add event handlers bound to this.
|
||||
this.boundRuleViewChanged = this.ruleViewChanged.bind(this);
|
||||
this.ruleView.element.addEventListener("CssRuleViewChanged",
|
||||
this.boundRuleViewChanged);
|
||||
this.cssRuleViewBoundCSSLinkClicked = this.ruleViewCSSLinkClicked.bind(this);
|
||||
this.ruleView.element.addEventListener("CssRuleViewCSSLinkClicked",
|
||||
this.cssRuleViewBoundCSSLinkClicked);
|
||||
this.cssRuleViewBoundMouseDown = this.ruleViewMouseDown.bind(this);
|
||||
this.ruleView.element.addEventListener("mousedown",
|
||||
this.cssRuleViewBoundMouseDown);
|
||||
this.cssRuleViewBoundMouseUp = this.ruleViewMouseUp.bind(this);
|
||||
this.ruleView.element.addEventListener("mouseup",
|
||||
this.cssRuleViewBoundMouseUp);
|
||||
this.cssRuleViewBoundMouseMove = this.ruleViewMouseMove.bind(this);
|
||||
this.cssRuleViewBoundMenuUpdate = this.ruleViewMenuUpdate.bind(this);
|
||||
|
||||
this.cssRuleViewBoundCopy = this.ruleViewCopy.bind(this);
|
||||
iframe.addEventListener("copy", this.cssRuleViewBoundCopy);
|
||||
|
||||
this.cssRuleViewBoundCopyRule = this.ruleViewCopyRule.bind(this);
|
||||
this.cssRuleViewBoundCopyDeclaration =
|
||||
this.ruleViewCopyDeclaration.bind(this);
|
||||
this.cssRuleViewBoundCopyProperty = this.ruleViewCopyProperty.bind(this);
|
||||
this.cssRuleViewBoundCopyPropertyValue =
|
||||
this.ruleViewCopyPropertyValue.bind(this);
|
||||
|
||||
// Add the rule view's context menu.
|
||||
this.ruleViewAddContextMenu();
|
||||
|
||||
doc.documentElement.appendChild(this.ruleView.element);
|
||||
this.ruleView.highlight(this.selection);
|
||||
@ -1002,19 +1030,356 @@ InspectorUI.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This is the mousedown handler for the rule view. We use it to track whether
|
||||
* text is currently getting selected.
|
||||
* .
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
ruleViewMouseDown: function IUI_ruleViewMouseDown(aEvent)
|
||||
{
|
||||
this.ruleView.element.addEventListener("mousemove",
|
||||
this.cssRuleViewBoundMouseMove);
|
||||
},
|
||||
|
||||
/**
|
||||
* This is the mouseup handler for the rule view. We use it to track whether
|
||||
* text is currently getting selected.
|
||||
* .
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
ruleViewMouseUp: function IUI_ruleViewMouseUp(aEvent)
|
||||
{
|
||||
this.ruleView.element.removeEventListener("mousemove",
|
||||
this.cssRuleViewBoundMouseMove);
|
||||
this.ruleView._selectionMode = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* This is the mousemove handler for the rule view. We use it to track whether
|
||||
* text is currently getting selected.
|
||||
* .
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
ruleViewMouseMove: function IUI_ruleViewMouseMove(aEvent)
|
||||
{
|
||||
this.ruleView._selectionMode = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a context menu to the rule view.
|
||||
*/
|
||||
ruleViewAddContextMenu: function IUI_ruleViewAddContextMenu()
|
||||
{
|
||||
let iframe = this.getToolIframe(this.ruleViewObject);
|
||||
let popupSet = this.chromeDoc.getElementById("mainPopupSet");
|
||||
let menu = this.chromeDoc.createElement("menupopup");
|
||||
menu.addEventListener("popupshowing", this.cssRuleViewBoundMenuUpdate);
|
||||
menu.id = "rule-view-context-menu";
|
||||
|
||||
// Copy selection
|
||||
let label = styleInspectorStrings
|
||||
.GetStringFromName("rule.contextmenu.copyselection");
|
||||
let accessKey = styleInspectorStrings
|
||||
.GetStringFromName("rule.contextmenu.copyselection.accesskey");
|
||||
let item = this.chromeDoc.createElement("menuitem");
|
||||
item.id = "rule-view-copy";
|
||||
item.setAttribute("label", label);
|
||||
item.setAttribute("accesskey", accessKey);
|
||||
item.addEventListener("command", this.cssRuleViewBoundCopy);
|
||||
menu.appendChild(item);
|
||||
|
||||
// Copy rule
|
||||
label = styleInspectorStrings.
|
||||
GetStringFromName("rule.contextmenu.copyrule");
|
||||
accessKey = styleInspectorStrings.
|
||||
GetStringFromName("rule.contextmenu.copyrule.accesskey");
|
||||
item = this.chromeDoc.createElement("menuitem");
|
||||
item.id = "rule-view-copy-rule";
|
||||
item.setAttribute("label", label);
|
||||
item.setAttribute("accesskey", accessKey);
|
||||
item.addEventListener("command", this.cssRuleViewBoundCopyRule);
|
||||
menu.appendChild(item);
|
||||
|
||||
// Copy declaration
|
||||
label = styleInspectorStrings.
|
||||
GetStringFromName("rule.contextmenu.copydeclaration");
|
||||
accessKey = styleInspectorStrings.
|
||||
GetStringFromName("rule.contextmenu.copydeclaration.accesskey");
|
||||
item = this.chromeDoc.createElement("menuitem");
|
||||
item.id = "rule-view-copy-declaration";
|
||||
item.setAttribute("label", label);
|
||||
item.setAttribute("accesskey", accessKey);
|
||||
item.addEventListener("command", this.cssRuleViewBoundCopyDeclaration);
|
||||
menu.appendChild(item);
|
||||
|
||||
// Copy property name
|
||||
label = styleInspectorStrings.
|
||||
GetStringFromName("rule.contextmenu.copyproperty");
|
||||
accessKey = styleInspectorStrings.
|
||||
GetStringFromName("rule.contextmenu.copyproperty.accesskey");
|
||||
item = this.chromeDoc.createElement("menuitem");
|
||||
item.id = "rule-view-copy-property";
|
||||
item.setAttribute("label", label);
|
||||
item.setAttribute("accesskey", accessKey);
|
||||
item.addEventListener("command", this.cssRuleViewBoundCopyProperty);
|
||||
menu.appendChild(item);
|
||||
|
||||
// Copy property value
|
||||
label = styleInspectorStrings.
|
||||
GetStringFromName("rule.contextmenu.copypropertyvalue");
|
||||
accessKey = styleInspectorStrings.
|
||||
GetStringFromName("rule.contextmenu.copypropertyvalue.accesskey");
|
||||
item = this.chromeDoc.createElement("menuitem");
|
||||
item.id = "rule-view-copy-property-value";
|
||||
item.setAttribute("label", label);
|
||||
item.setAttribute("accesskey", accessKey);
|
||||
item.addEventListener("command", this.cssRuleViewBoundCopyPropertyValue);
|
||||
menu.appendChild(item);
|
||||
|
||||
popupSet.appendChild(menu);
|
||||
|
||||
iframe.setAttribute("context", menu.id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the rule view's context menu by disabling irrelevant menuitems and
|
||||
* enabling relevant ones.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
ruleViewMenuUpdate: function IUI_ruleViewMenuUpdate(aEvent)
|
||||
{
|
||||
let iframe = this.getToolIframe(this.ruleViewObject);
|
||||
let win = iframe.contentWindow;
|
||||
|
||||
// Copy selection.
|
||||
let disable = win.getSelection().isCollapsed;
|
||||
let menuitem = this.chromeDoc.getElementById("rule-view-copy");
|
||||
menuitem.disabled = disable;
|
||||
|
||||
// Copy property, copy property name & copy property value.
|
||||
let node = this.chromeDoc.popupNode;
|
||||
if (!node.classList.contains("ruleview-property") &&
|
||||
!node.classList.contains("ruleview-computed")) {
|
||||
while (node = node.parentElement) {
|
||||
if (node.classList.contains("ruleview-property") ||
|
||||
node.classList.contains("ruleview-computed")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let disablePropertyItems = !node || (node &&
|
||||
!node.classList.contains("ruleview-property") &&
|
||||
!node.classList.contains("ruleview-computed"));
|
||||
|
||||
menuitem = this.chromeDoc.querySelector("#rule-view-copy-declaration");
|
||||
menuitem.disabled = disablePropertyItems;
|
||||
menuitem = this.chromeDoc.querySelector("#rule-view-copy-property");
|
||||
menuitem.disabled = disablePropertyItems;
|
||||
menuitem = this.chromeDoc.querySelector("#rule-view-copy-property-value");
|
||||
menuitem.disabled = disablePropertyItems;
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy selected text from the rule view.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
ruleViewCopy: function IUI_ruleViewCopy(aEvent)
|
||||
{
|
||||
let iframe = this.getToolIframe(this.ruleViewObject);
|
||||
let win = iframe.contentWindow;
|
||||
let text = win.getSelection().toString();
|
||||
|
||||
// Remove any double newlines.
|
||||
text = text.replace(/(\r?\n)\r?\n/g, "$1");
|
||||
|
||||
// Remove "inline"
|
||||
let inline = styleInspectorStrings.GetStringFromName("rule.sourceInline");
|
||||
let rx = new RegExp("^" + inline + "\\r?\\n?", "g");
|
||||
text = text.replace(rx, "");
|
||||
|
||||
// Remove file:line
|
||||
text = text.replace(/[\w\.]+:\d+(\r?\n)/g, "$1");
|
||||
|
||||
// Remove inherited from: line
|
||||
let inheritedFrom = styleInspectorStrings
|
||||
.GetStringFromName("rule.inheritedSource");
|
||||
inheritedFrom = inheritedFrom.replace(/\s%S\s\(%S\)/g, "");
|
||||
rx = new RegExp("(\r?\n)" + inheritedFrom + ".*", "g");
|
||||
text = text.replace(rx, "$1");
|
||||
|
||||
clipboardHelper.copyString(text);
|
||||
|
||||
if (aEvent) {
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy a rule from the rule view.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
ruleViewCopyRule: function IUI_ruleViewCopyRule(aEvent)
|
||||
{
|
||||
let node = this.chromeDoc.popupNode;
|
||||
if (node.className != "ruleview-code") {
|
||||
if (node.className == "ruleview-rule-source") {
|
||||
node = node.nextElementSibling;
|
||||
} else {
|
||||
while (node = node.parentElement) {
|
||||
if (node.className == "ruleview-code") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node.className == "ruleview-code") {
|
||||
// We need to strip expanded properties from the node because we use
|
||||
// node.textContent below, which also gets text from hidden nodes. The
|
||||
// simplest way to do this is to clone the node and remove them from the
|
||||
// clone.
|
||||
node = node.cloneNode();
|
||||
let computed = node.querySelector(".ruleview-computedlist");
|
||||
if (computed) {
|
||||
computed.parentNode.removeChild(computed);
|
||||
}
|
||||
}
|
||||
|
||||
let text = node.textContent;
|
||||
|
||||
// Format the rule
|
||||
if (osString == "WINNT") {
|
||||
text = text.replace(/{/g, "{\r\n ");
|
||||
text = text.replace(/;/g, ";\r\n ");
|
||||
text = text.replace(/\s*}/g, "\r\n}");
|
||||
} else {
|
||||
text = text.replace(/{/g, "{\n ");
|
||||
text = text.replace(/;/g, ";\n ");
|
||||
text = text.replace(/\s*}/g, "\n}");
|
||||
}
|
||||
|
||||
clipboardHelper.copyString(text);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy a declaration from the rule view.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
ruleViewCopyDeclaration: function IUI_ruleViewCopyDeclaration(aEvent)
|
||||
{
|
||||
let node = this.chromeDoc.popupNode;
|
||||
if (!node.classList.contains("ruleview-property") &&
|
||||
!node.classList.contains("ruleview-computed")) {
|
||||
while (node = node.parentElement) {
|
||||
if (node.classList.contains("ruleview-property") ||
|
||||
node.classList.contains("ruleview-computed")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need to strip expanded properties from the node because we use
|
||||
// node.textContent below, which also gets text from hidden nodes. The
|
||||
// simplest way to do this is to clone the node and remove them from the
|
||||
// clone.
|
||||
node = node.cloneNode();
|
||||
let computed = node.querySelector(".ruleview-computedlist");
|
||||
if (computed) {
|
||||
computed.parentNode.removeChild(computed);
|
||||
}
|
||||
clipboardHelper.copyString(node.textContent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy a property name from the rule view.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
ruleViewCopyProperty: function IUI_ruleViewCopyProperty(aEvent)
|
||||
{
|
||||
let node = this.chromeDoc.popupNode;
|
||||
|
||||
if (!node.classList.contains("ruleview-propertyname")) {
|
||||
node = node.querySelector(".ruleview-propertyname");
|
||||
}
|
||||
|
||||
if (node) {
|
||||
clipboardHelper.copyString(node.textContent);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy a property value from the rule view.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
ruleViewCopyPropertyValue: function IUI_ruleViewCopyPropertyValue(aEvent)
|
||||
{
|
||||
let node = this.chromeDoc.popupNode;
|
||||
|
||||
if (!node.classList.contains("ruleview-propertyvalue")) {
|
||||
node = node.querySelector(".ruleview-propertyvalue");
|
||||
}
|
||||
|
||||
if (node) {
|
||||
clipboardHelper.copyString(node.textContent);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the rule view.
|
||||
*/
|
||||
destroyRuleView: function IUI_destroyRuleView()
|
||||
{
|
||||
let iframe = this.getToolIframe(this.ruleViewObject);
|
||||
iframe.removeEventListener("copy", this.cssRuleViewBoundCopy);
|
||||
iframe.parentNode.removeChild(iframe);
|
||||
|
||||
if (this.ruleView) {
|
||||
let menu = this.chromeDoc.querySelector("#rule-view-context-menu");
|
||||
if (menu) {
|
||||
// Copy
|
||||
let menuitem = this.chromeDoc.querySelector("#rule-view-copy");
|
||||
menuitem.removeEventListener("command", this.cssRuleViewBoundCopy);
|
||||
|
||||
// Copy rule
|
||||
menuitem = this.chromeDoc.querySelector("#rule-view-copy-rule");
|
||||
menuitem.removeEventListener("command", this.cssRuleViewBoundCopyRule);
|
||||
|
||||
// Copy property
|
||||
menuitem = this.chromeDoc.querySelector("#rule-view-copy-declaration");
|
||||
menuitem.removeEventListener("command",
|
||||
this.cssRuleViewBoundCopyDeclaration);
|
||||
|
||||
// Copy property name
|
||||
menuitem = this.chromeDoc.querySelector("#rule-view-copy-property");
|
||||
menuitem.removeEventListener("command",
|
||||
this.cssRuleViewBoundCopyProperty);
|
||||
|
||||
// Copy property value
|
||||
menuitem = this.chromeDoc.querySelector("#rule-view-copy-property-value");
|
||||
menuitem.removeEventListener("command",
|
||||
this.cssRuleViewBoundCopyPropertyValue);
|
||||
|
||||
menu.removeEventListener("popupshowing", this.cssRuleViewBoundMenuUpdate);
|
||||
menu.parentNode.removeChild(menu);
|
||||
}
|
||||
|
||||
this.ruleView.element.removeEventListener("CssRuleViewChanged",
|
||||
this.boundRuleViewChanged);
|
||||
this.ruleView.element.removeEventListener("CssRuleViewCSSLinkClicked",
|
||||
this.cssRuleViewBoundCSSLinkClicked);
|
||||
this.ruleView.element.removeEventListener("mousedown",
|
||||
this.cssRuleViewBoundMouseDown);
|
||||
this.ruleView.element.removeEventListener("mouseup",
|
||||
this.cssRuleViewBoundMouseUp);
|
||||
this.ruleView.element.removeEventListener("mousemove",
|
||||
this.cssRuleViewBoundMouseMove);
|
||||
delete boundRuleViewChanged;
|
||||
this.ruleView.clear();
|
||||
delete this.ruleView;
|
||||
@ -1236,6 +1601,7 @@ InspectorUI.prototype = {
|
||||
iframe.id = "devtools-sidebar-iframe-" + aRegObj.id;
|
||||
iframe.setAttribute("flex", "1");
|
||||
iframe.setAttribute("tooltip", "aHTMLTooltip");
|
||||
iframe.addEventListener("mousedown", iframe.focus);
|
||||
this.sidebarDeck.appendChild(iframe);
|
||||
|
||||
// wire up button to show the iframe
|
||||
@ -1347,6 +1713,10 @@ InspectorUI.prototype = {
|
||||
let btn = this.chromeDoc.getElementById(buttonId);
|
||||
this.unbindToolEvent(btn, "click");
|
||||
|
||||
// Remove focus listener
|
||||
let iframe = this.getToolIframe(aRegObj);
|
||||
iframe.removeEventListener("mousedown", iframe.focus);
|
||||
|
||||
// remove sidebar buttons and tools
|
||||
this.sidebarToolbar.removeChild(btn);
|
||||
|
||||
@ -2232,3 +2602,17 @@ XPCOMUtils.defineLazyGetter(this, "StyleInspector", function () {
|
||||
XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
|
||||
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
|
||||
return Cc["@mozilla.org/widget/clipboardhelper;1"].
|
||||
getService(Ci.nsIClipboardHelper);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "styleInspectorStrings", function() {
|
||||
return Services.strings.createBundle(
|
||||
"chrome://browser/locale/devtools/styleinspector.properties");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "osString", function() {
|
||||
return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
|
||||
});
|
||||
|
@ -58,6 +58,7 @@ _BROWSER_FILES = \
|
||||
browser_inspector_bug_665880.js \
|
||||
browser_inspector_bug_674871.js \
|
||||
browser_inspector_editor.js \
|
||||
browser_inspector_editor_name.js \
|
||||
browser_inspector_bug_566084_location_changed.js \
|
||||
browser_inspector_infobar.js \
|
||||
browser_inspector_bug_690361.js \
|
||||
|
@ -3,13 +3,8 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* Contributor(s):
|
||||
* Rob Campbell <rcampbell@mozilla.com>
|
||||
* Mihai Sucan <mihai.sucan@gmail.com>
|
||||
* Kyle Simpson <ksimpson@mozilla.com>
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
let doc;
|
||||
let div;
|
||||
|
@ -0,0 +1,253 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
let doc;
|
||||
let div;
|
||||
let editorTestSteps;
|
||||
|
||||
function doNextStep() {
|
||||
editorTestSteps.next();
|
||||
}
|
||||
|
||||
function setupEditorTests()
|
||||
{
|
||||
div = doc.createElement("div");
|
||||
div.setAttribute("id", "foobar");
|
||||
div.setAttribute("class", "barbaz");
|
||||
doc.body.appendChild(div);
|
||||
|
||||
Services.obs.addObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
|
||||
InspectorUI.toggleInspectorUI();
|
||||
}
|
||||
|
||||
function setupHTMLPanel()
|
||||
{
|
||||
Services.obs.removeObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
|
||||
Services.obs.addObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
|
||||
InspectorUI.toggleHTMLPanel();
|
||||
}
|
||||
|
||||
function runEditorTests()
|
||||
{
|
||||
Services.obs.removeObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
|
||||
InspectorUI.stopInspecting();
|
||||
InspectorUI.inspectNode(doc.body, true);
|
||||
|
||||
// setup generator for async test steps
|
||||
editorTestSteps = doEditorTestSteps();
|
||||
|
||||
// add step listeners
|
||||
Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
|
||||
Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
|
||||
Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
|
||||
|
||||
// start the tests
|
||||
doNextStep();
|
||||
}
|
||||
|
||||
function highlighterTrap()
|
||||
{
|
||||
// bug 696107
|
||||
InspectorUI.highlighter.removeListener("nodeselected", highlighterTrap);
|
||||
ok(false, "Highlighter moved. Shouldn't be here!");
|
||||
finishUp();
|
||||
}
|
||||
|
||||
function doEditorTestSteps()
|
||||
{
|
||||
let treePanel = InspectorUI.treePanel;
|
||||
let editor = treePanel.treeBrowserDocument.getElementById("attribute-editor");
|
||||
let editorInput = treePanel.treeBrowserDocument.getElementById("attribute-editor-input");
|
||||
|
||||
// Step 1: grab and test the attribute-name nodes in the HTML panel, then open editor
|
||||
let nodes = treePanel.treeBrowserDocument.querySelectorAll(".nodeName.editable");
|
||||
let attrNameNode_id = nodes[0]
|
||||
let attrNameNode_class = nodes[1];
|
||||
|
||||
is(attrNameNode_id.innerHTML, "id", "Step 1: we have the correct `id` attribute-name node in the HTML panel");
|
||||
is(attrNameNode_class.innerHTML, "class", "we have the correct `class` attribute-name node in the HTML panel");
|
||||
|
||||
// double-click the `id` attribute-name node to open the editor
|
||||
executeSoon(function() {
|
||||
// firing 2 clicks right in a row to simulate a double-click
|
||||
EventUtils.synthesizeMouse(attrNameNode_id, 2, 2, {clickCount: 2}, attrNameNode_id.ownerDocument.defaultView);
|
||||
});
|
||||
|
||||
yield; // End of Step 1
|
||||
|
||||
|
||||
// Step 2: validate editing session, enter new attribute value into editor, and save input
|
||||
ok(InspectorUI.treePanel.editingContext, "Step 2: editor session started");
|
||||
let selection = InspectorUI.selection;
|
||||
|
||||
ok(selection, "Selection is: " + selection);
|
||||
|
||||
let editorVisible = editor.classList.contains("editing");
|
||||
ok(editorVisible, "editor popup visible");
|
||||
|
||||
// check if the editor popup is "near" the correct position
|
||||
let editorDims = editor.getBoundingClientRect();
|
||||
let attrNameNodeDims = attrNameNode_id.getBoundingClientRect();
|
||||
let editorPositionOK = (editorDims.left >= (attrNameNodeDims.left - editorDims.width - 5)) &&
|
||||
(editorDims.right <= (attrNameNodeDims.right + editorDims.width + 5)) &&
|
||||
(editorDims.top >= (attrNameNodeDims.top - editorDims.height - 5)) &&
|
||||
(editorDims.bottom <= (attrNameNodeDims.bottom + editorDims.height + 5));
|
||||
|
||||
ok(editorPositionOK, "editor position acceptable");
|
||||
|
||||
// check to make sure the attribute-value node being edited is properly highlighted
|
||||
let attrNameNodeHighlighted = attrNameNode_id.classList.contains("editingAttributeValue");
|
||||
ok(attrNameNodeHighlighted, "`id` attribute-name node is editor-highlighted");
|
||||
|
||||
is(treePanel.editingContext.repObj, div, "editor session has correct reference to div");
|
||||
is(treePanel.editingContext.attrObj, attrNameNode_id, "editor session has correct reference to `id` attribute-name node in HTML panel");
|
||||
is(treePanel.editingContext.attrName, "id", "editor session knows correct attribute-name");
|
||||
|
||||
editorInput.value = "burp";
|
||||
editorInput.focus();
|
||||
|
||||
InspectorUI.highlighter.addListener("nodeselected", highlighterTrap);
|
||||
|
||||
// hit <enter> to save the textbox value
|
||||
executeSoon(function() {
|
||||
// Extra key to test that keyboard handlers have been removed. bug 696107.
|
||||
EventUtils.synthesizeKey("VK_LEFT", {}, attrNameNode_id.ownerDocument.defaultView);
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, attrNameNode_id.ownerDocument.defaultView);
|
||||
});
|
||||
|
||||
// two `yield` statements, to trap both the "SAVED" and "CLOSED" events that will be triggered
|
||||
yield;
|
||||
yield; // End of Step 2
|
||||
|
||||
// remove this from previous step
|
||||
InspectorUI.highlighter.removeListener("nodeselected", highlighterTrap);
|
||||
|
||||
// Step 3: validate that the previous editing session saved correctly, then open editor on `class` attribute value
|
||||
ok(!treePanel.editingContext, "Step 3: editor session ended");
|
||||
editorVisible = editor.classList.contains("editing");
|
||||
ok(!editorVisible, "editor popup hidden");
|
||||
attrNameNodeHighlighted = attrNameNode_id.classList.contains("editingAttributeValue");
|
||||
ok(!attrNameNodeHighlighted, "`id` attribute-value node is no longer editor-highlighted");
|
||||
is(div.getAttribute("burp"), "foobar", "`id` attribute-name successfully updated");
|
||||
is(attrNameNode_id.innerHTML, "burp", "attribute-name node in HTML panel successfully updated");
|
||||
|
||||
// double-click the `class` attribute-value node to open the editor
|
||||
executeSoon(function() {
|
||||
// firing 2 clicks right in a row to simulate a double-click
|
||||
EventUtils.synthesizeMouse(attrNameNode_class, 2, 2, {clickCount: 2}, attrNameNode_class.ownerDocument.defaultView);
|
||||
});
|
||||
|
||||
yield; // End of Step 3
|
||||
|
||||
|
||||
// Step 4: enter value into editor, then hit <escape> to discard it
|
||||
ok(treePanel.editingContext, "Step 4: editor session started");
|
||||
editorVisible = editor.classList.contains("editing");
|
||||
ok(editorVisible, "editor popup visible");
|
||||
|
||||
is(treePanel.editingContext.attrObj, attrNameNode_class, "editor session has correct reference to `class` attribute-name node in HTML panel");
|
||||
is(treePanel.editingContext.attrName, "class", "editor session knows correct attribute-name");
|
||||
|
||||
editorInput.value = "Hello World";
|
||||
editorInput.focus();
|
||||
|
||||
// hit <escape> to discard the inputted value
|
||||
executeSoon(function() {
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {}, attrNameNode_class.ownerDocument.defaultView);
|
||||
});
|
||||
|
||||
yield; // End of Step 4
|
||||
|
||||
|
||||
// Step 5: validate that the previous editing session discarded correctly, then open editor on `id` attribute value again
|
||||
ok(!treePanel.editingContext, "Step 5: editor session ended");
|
||||
editorVisible = editor.classList.contains("editing");
|
||||
ok(!editorVisible, "editor popup hidden");
|
||||
is(div.getAttribute("class"), "barbaz", "`class` attribute-name *not* updated");
|
||||
is(attrNameNode_class.innerHTML, "class", "attribute-name node in HTML panel *not* updated");
|
||||
|
||||
// double-click the `id` attribute-name node to open the editor
|
||||
executeSoon(function() {
|
||||
// firing 2 clicks right in a row to simulate a double-click
|
||||
EventUtils.synthesizeMouse(attrNameNode_id, 2, 2, {clickCount: 2}, attrNameNode_id.ownerDocument.defaultView);
|
||||
});
|
||||
|
||||
yield; // End of Step 5
|
||||
|
||||
|
||||
// Step 6: validate that editor opened again, then test double-click inside of editor (should do nothing)
|
||||
ok(treePanel.editingContext, "Step 6: editor session started");
|
||||
editorVisible = editor.classList.contains("editing");
|
||||
ok(editorVisible, "editor popup visible");
|
||||
|
||||
// double-click on the editor input box
|
||||
executeSoon(function() {
|
||||
// firing 2 clicks right in a row to simulate a double-click
|
||||
EventUtils.synthesizeMouse(editorInput, 2, 2, {clickCount: 2}, editorInput.ownerDocument.defaultView);
|
||||
|
||||
// since the previous double-click is supposed to do nothing,
|
||||
// wait a brief moment, then move on to the next step
|
||||
executeSoon(function() {
|
||||
doNextStep();
|
||||
});
|
||||
});
|
||||
|
||||
yield; // End of Step 6
|
||||
|
||||
|
||||
// Step 7: validate that editing session is still correct, then enter a value and try a click
|
||||
// outside of editor (should cancel the editing session)
|
||||
ok(treePanel.editingContext, "Step 7: editor session still going");
|
||||
editorVisible = editor.classList.contains("editing");
|
||||
ok(editorVisible, "editor popup still visible");
|
||||
|
||||
editorInput.value = "all your base are belong to us";
|
||||
|
||||
// single-click the `class` attribute-value node
|
||||
executeSoon(function() {
|
||||
EventUtils.synthesizeMouse(attrNameNode_class, 2, 2, {}, attrNameNode_class.ownerDocument.defaultView);
|
||||
});
|
||||
|
||||
yield; // End of Step 7
|
||||
|
||||
|
||||
// Step 8: validate that the editor was closed and that the editing was not saved
|
||||
ok(!treePanel.editingContext, "Step 8: editor session ended");
|
||||
editorVisible = editor.classList.contains("editing");
|
||||
ok(!editorVisible, "editor popup hidden");
|
||||
is(div.getAttribute("burp"), "foobar", "`id` attribute-name *not* updated");
|
||||
is(attrNameNode_id.innerHTML, "burp", "attribute-value node in HTML panel *not* updated");
|
||||
|
||||
// End of Step 8
|
||||
executeSoon(finishUp);
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
// end of all steps, so clean up
|
||||
Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
|
||||
Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
|
||||
Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
|
||||
doc = div = null;
|
||||
InspectorUI.closeInspectorUI();
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
doc = content.document;
|
||||
waitForFocus(setupEditorTests, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,basic tests for html panel attribute-value editor";
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
- Rob Campbell <robcee@mozilla.com> (original author)
|
||||
- Mihai Sucan <mihai.sucan@gmail.com>
|
||||
- Erik Vold <erikvvold@gmail.com>
|
||||
- Mark Capella <markcapella@twcny.rr.com>
|
||||
-
|
||||
- Alternatively, the contents of this file may be used under the terms of
|
||||
- either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -53,6 +53,7 @@ copy({
|
||||
ORION_EDITOR + "/orion/textview/rulers.js",
|
||||
ORION_EDITOR + "/orion/textview/undoStack.js",
|
||||
ORION_EDITOR + "/orion/textview/textModel.js",
|
||||
ORION_EDITOR + "/orion/textview/projectionTextModel.js",
|
||||
ORION_EDITOR + "/orion/textview/tooltip.js",
|
||||
ORION_EDITOR + "/orion/textview/textView.js",
|
||||
ORION_EDITOR + "/orion/textview/textDND.js",
|
||||
|
@ -22,6 +22,10 @@ Orion version: git clone from 2012-01-26
|
||||
http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=27177e9a3dc70c20b4877e3eab3adfff1d56e342
|
||||
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=370606
|
||||
|
||||
+ patch for Mozilla Bug 730532 - remove CSS2Properties aliases for MozOpacity
|
||||
and MozOutline*
|
||||
see https://bugzilla.mozilla.org/show_bug.cgi?id=730532#c3
|
||||
|
||||
# License
|
||||
|
||||
The following files are licensed according to the contents in the LICENSE
|
||||
|
@ -2563,6 +2563,590 @@ define("orion/textview/textModel", ['orion/textview/eventTarget'], function(mEve
|
||||
|
||||
return {TextModel: TextModel};
|
||||
});/*******************************************************************************
|
||||
* @license
|
||||
* Copyright (c) 2010, 2011 IBM Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License v1.0
|
||||
* (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
|
||||
* License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
|
||||
*
|
||||
* Contributors:
|
||||
* Felipe Heidrich (IBM Corporation) - initial API and implementation
|
||||
* Silenio Quarti (IBM Corporation) - initial API and implementation
|
||||
******************************************************************************/
|
||||
|
||||
/*global define */
|
||||
|
||||
define("orion/textview/projectionTextModel", ['orion/textview/textModel', 'orion/textview/eventTarget'], function(mTextModel, mEventTarget) {
|
||||
|
||||
/**
|
||||
* @class This object represents a projection range. A projection specifies a
|
||||
* range of text and the replacement text. The range of text is relative to the
|
||||
* base text model associated to a projection model.
|
||||
* <p>
|
||||
* <b>See:</b><br/>
|
||||
* {@link orion.textview.ProjectionTextModel}<br/>
|
||||
* {@link orion.textview.ProjectionTextModel#addProjection}<br/>
|
||||
* </p>
|
||||
* @name orion.textview.Projection
|
||||
*
|
||||
* @property {Number} start The start offset of the projection range.
|
||||
* @property {Number} end The end offset of the projection range. This offset is exclusive.
|
||||
* @property {String|orion.textview.TextModel} [text=""] The projection text to be inserted
|
||||
*/
|
||||
/**
|
||||
* Constructs a new <code>ProjectionTextModel</code> based on the specified <code>TextModel</code>.
|
||||
*
|
||||
* @param {orion.textview.TextModel} baseModel The base text model.
|
||||
*
|
||||
* @name orion.textview.ProjectionTextModel
|
||||
* @class The <code>ProjectionTextModel</code> represents a projection of its base text
|
||||
* model. Projection ranges can be added to the projection text model to hide and/or insert
|
||||
* ranges to the base text model.
|
||||
* <p>
|
||||
* The contents of the projection text model is modified when changes occur in the base model,
|
||||
* projection model or by calls to {@link #addProjection} and {@link #removeProjection}.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>See:</b><br/>
|
||||
* {@link orion.textview.TextView}<br/>
|
||||
* {@link orion.textview.TextModel}
|
||||
* {@link orion.textview.TextView#setModel}
|
||||
* </p>
|
||||
* @borrows orion.textview.EventTarget#addEventListener as #addEventListener
|
||||
* @borrows orion.textview.EventTarget#removeEventListener as #removeEventListener
|
||||
* @borrows orion.textview.EventTarget#dispatchEvent as #dispatchEvent
|
||||
*/
|
||||
function ProjectionTextModel(baseModel) {
|
||||
this._model = baseModel; /* Base Model */
|
||||
this._projections = [];
|
||||
}
|
||||
|
||||
ProjectionTextModel.prototype = /** @lends orion.textview.ProjectionTextModel.prototype */ {
|
||||
/**
|
||||
* Adds a projection range to the model.
|
||||
* <p>
|
||||
* The model must notify the listeners before and after the the text is
|
||||
* changed by calling {@link #onChanging} and {@link #onChanged} respectively.
|
||||
* </p>
|
||||
* @param {orion.textview.Projection} projection The projection range to be added.
|
||||
*
|
||||
* @see #removeProjection
|
||||
*/
|
||||
addProjection: function(projection) {
|
||||
if (!projection) {return;}
|
||||
//start and end can't overlap any exist projection
|
||||
var model = this._model, projections = this._projections;
|
||||
projection._lineIndex = model.getLineAtOffset(projection.start);
|
||||
projection._lineCount = model.getLineAtOffset(projection.end) - projection._lineIndex;
|
||||
var text = projection.text;
|
||||
if (!text) { text = ""; }
|
||||
if (typeof text === "string") {
|
||||
projection._model = new mTextModel.TextModel(text, model.getLineDelimiter());
|
||||
} else {
|
||||
projection._model = text;
|
||||
}
|
||||
var eventStart = this.mapOffset(projection.start, true);
|
||||
var removedCharCount = projection.end - projection.start;
|
||||
var removedLineCount = projection._lineCount;
|
||||
var addedCharCount = projection._model.getCharCount();
|
||||
var addedLineCount = projection._model.getLineCount() - 1;
|
||||
var modelChangingEvent = {
|
||||
type: "Changing",
|
||||
text: projection._model.getText(),
|
||||
start: eventStart,
|
||||
removedCharCount: removedCharCount,
|
||||
addedCharCount: addedCharCount,
|
||||
removedLineCount: removedLineCount,
|
||||
addedLineCount: addedLineCount
|
||||
};
|
||||
this.onChanging(modelChangingEvent);
|
||||
var index = this._binarySearch(projections, projection.start);
|
||||
projections.splice(index, 0, projection);
|
||||
var modelChangedEvent = {
|
||||
type: "Changed",
|
||||
start: eventStart,
|
||||
removedCharCount: removedCharCount,
|
||||
addedCharCount: addedCharCount,
|
||||
removedLineCount: removedLineCount,
|
||||
addedLineCount: addedLineCount
|
||||
};
|
||||
this.onChanged(modelChangedEvent);
|
||||
},
|
||||
/**
|
||||
* Returns all projection ranges of this model.
|
||||
*
|
||||
* @return {orion.textview.Projection[]} The projection ranges.
|
||||
*
|
||||
* @see #addProjection
|
||||
*/
|
||||
getProjections: function() {
|
||||
return this._projections.slice(0);
|
||||
},
|
||||
/**
|
||||
* Gets the base text model.
|
||||
*
|
||||
* @return {orion.textview.TextModel} The base text model.
|
||||
*/
|
||||
getBaseModel: function() {
|
||||
return this._model;
|
||||
},
|
||||
/**
|
||||
* Maps offsets between the projection model and its base model.
|
||||
*
|
||||
* @param {Number} offset The offset to be mapped.
|
||||
* @param {Boolean} [baseOffset=false] <code>true</code> if <code>offset</code> is in base model and
|
||||
* should be mapped to the projection model.
|
||||
* @return {Number} The mapped offset
|
||||
*/
|
||||
mapOffset: function(offset, baseOffset) {
|
||||
var projections = this._projections, delta = 0, i, projection;
|
||||
if (baseOffset) {
|
||||
for (i = 0; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
if (projection.start > offset) { break; }
|
||||
if (projection.end > offset) { return -1; }
|
||||
delta += projection._model.getCharCount() - (projection.end - projection.start);
|
||||
}
|
||||
return offset + delta;
|
||||
}
|
||||
for (i = 0; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
if (projection.start > offset - delta) { break; }
|
||||
var charCount = projection._model.getCharCount();
|
||||
if (projection.start + charCount > offset - delta) {
|
||||
return -1;
|
||||
}
|
||||
delta += charCount - (projection.end - projection.start);
|
||||
}
|
||||
return offset - delta;
|
||||
},
|
||||
/**
|
||||
* Removes a projection range from the model.
|
||||
* <p>
|
||||
* The model must notify the listeners before and after the the text is
|
||||
* changed by calling {@link #onChanging} and {@link #onChanged} respectively.
|
||||
* </p>
|
||||
*
|
||||
* @param {orion.textview.Projection} projection The projection range to be removed.
|
||||
*
|
||||
* @see #addProjection
|
||||
*/
|
||||
removeProjection: function(projection) {
|
||||
//TODO remove listeners from model
|
||||
var i, delta = 0;
|
||||
for (i = 0; i < this._projections.length; i++) {
|
||||
var p = this._projections[i];
|
||||
if (p === projection) {
|
||||
projection = p;
|
||||
break;
|
||||
}
|
||||
delta += p._model.getCharCount() - (p.end - p.start);
|
||||
}
|
||||
if (i < this._projections.length) {
|
||||
var model = this._model;
|
||||
var eventStart = projection.start + delta;
|
||||
var addedCharCount = projection.end - projection.start;
|
||||
var addedLineCount = projection._lineCount;
|
||||
var removedCharCount = projection._model.getCharCount();
|
||||
var removedLineCount = projection._model.getLineCount() - 1;
|
||||
var modelChangingEvent = {
|
||||
type: "Changing",
|
||||
text: model.getText(projection.start, projection.end),
|
||||
start: eventStart,
|
||||
removedCharCount: removedCharCount,
|
||||
addedCharCount: addedCharCount,
|
||||
removedLineCount: removedLineCount,
|
||||
addedLineCount: addedLineCount
|
||||
};
|
||||
this.onChanging(modelChangingEvent);
|
||||
this._projections.splice(i, 1);
|
||||
var modelChangedEvent = {
|
||||
type: "Changed",
|
||||
start: eventStart,
|
||||
removedCharCount: removedCharCount,
|
||||
addedCharCount: addedCharCount,
|
||||
removedLineCount: removedLineCount,
|
||||
addedLineCount: addedLineCount
|
||||
};
|
||||
this.onChanged(modelChangedEvent);
|
||||
}
|
||||
},
|
||||
/** @ignore */
|
||||
_binarySearch: function (array, offset) {
|
||||
var high = array.length, low = -1, index;
|
||||
while (high - low > 1) {
|
||||
index = Math.floor((high + low) / 2);
|
||||
if (offset <= array[index].start) {
|
||||
high = index;
|
||||
} else {
|
||||
low = index;
|
||||
}
|
||||
}
|
||||
return high;
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#getCharCount
|
||||
*/
|
||||
getCharCount: function() {
|
||||
var count = this._model.getCharCount(), projections = this._projections;
|
||||
for (var i = 0; i < projections.length; i++) {
|
||||
var projection = projections[i];
|
||||
count += projection._model.getCharCount() - (projection.end - projection.start);
|
||||
}
|
||||
return count;
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#getLine
|
||||
*/
|
||||
getLine: function(lineIndex, includeDelimiter) {
|
||||
if (lineIndex < 0) { return null; }
|
||||
var model = this._model, projections = this._projections;
|
||||
var delta = 0, result = [], offset = 0, i, lineCount, projection;
|
||||
for (i = 0; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
if (projection._lineIndex >= lineIndex - delta) { break; }
|
||||
lineCount = projection._model.getLineCount() - 1;
|
||||
if (projection._lineIndex + lineCount >= lineIndex - delta) {
|
||||
var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
|
||||
if (projectionLineIndex < lineCount) {
|
||||
return projection._model.getLine(projectionLineIndex, includeDelimiter);
|
||||
} else {
|
||||
result.push(projection._model.getLine(lineCount));
|
||||
}
|
||||
}
|
||||
offset = projection.end;
|
||||
delta += lineCount - projection._lineCount;
|
||||
}
|
||||
offset = Math.max(offset, model.getLineStart(lineIndex - delta));
|
||||
for (; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
if (projection._lineIndex > lineIndex - delta) { break; }
|
||||
result.push(model.getText(offset, projection.start));
|
||||
lineCount = projection._model.getLineCount() - 1;
|
||||
if (projection._lineIndex + lineCount > lineIndex - delta) {
|
||||
result.push(projection._model.getLine(0, includeDelimiter));
|
||||
return result.join("");
|
||||
}
|
||||
result.push(projection._model.getText());
|
||||
offset = projection.end;
|
||||
delta += lineCount - projection._lineCount;
|
||||
}
|
||||
var end = model.getLineEnd(lineIndex - delta, includeDelimiter);
|
||||
if (offset < end) {
|
||||
result.push(model.getText(offset, end));
|
||||
}
|
||||
return result.join("");
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#getLineAtOffset
|
||||
*/
|
||||
getLineAtOffset: function(offset) {
|
||||
var model = this._model, projections = this._projections;
|
||||
var delta = 0, lineDelta = 0;
|
||||
for (var i = 0; i < projections.length; i++) {
|
||||
var projection = projections[i];
|
||||
if (projection.start > offset - delta) { break; }
|
||||
var charCount = projection._model.getCharCount();
|
||||
if (projection.start + charCount > offset - delta) {
|
||||
var projectionOffset = offset - (projection.start + delta);
|
||||
lineDelta += projection._model.getLineAtOffset(projectionOffset);
|
||||
delta += projectionOffset;
|
||||
break;
|
||||
}
|
||||
lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
|
||||
delta += charCount - (projection.end - projection.start);
|
||||
}
|
||||
return model.getLineAtOffset(offset - delta) + lineDelta;
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#getLineCount
|
||||
*/
|
||||
getLineCount: function() {
|
||||
var model = this._model, projections = this._projections;
|
||||
var count = model.getLineCount();
|
||||
for (var i = 0; i < projections.length; i++) {
|
||||
var projection = projections[i];
|
||||
count += projection._model.getLineCount() - 1 - projection._lineCount;
|
||||
}
|
||||
return count;
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#getLineDelimiter
|
||||
*/
|
||||
getLineDelimiter: function() {
|
||||
return this._model.getLineDelimiter();
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#getLineEnd
|
||||
*/
|
||||
getLineEnd: function(lineIndex, includeDelimiter) {
|
||||
if (lineIndex < 0) { return -1; }
|
||||
var model = this._model, projections = this._projections;
|
||||
var delta = 0, offsetDelta = 0;
|
||||
for (var i = 0; i < projections.length; i++) {
|
||||
var projection = projections[i];
|
||||
if (projection._lineIndex > lineIndex - delta) { break; }
|
||||
var lineCount = projection._model.getLineCount() - 1;
|
||||
if (projection._lineIndex + lineCount > lineIndex - delta) {
|
||||
var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
|
||||
return projection._model.getLineEnd (projectionLineIndex, includeDelimiter) + projection.start + offsetDelta;
|
||||
}
|
||||
offsetDelta += projection._model.getCharCount() - (projection.end - projection.start);
|
||||
delta += lineCount - projection._lineCount;
|
||||
}
|
||||
return model.getLineEnd(lineIndex - delta, includeDelimiter) + offsetDelta;
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#getLineStart
|
||||
*/
|
||||
getLineStart: function(lineIndex) {
|
||||
if (lineIndex < 0) { return -1; }
|
||||
var model = this._model, projections = this._projections;
|
||||
var delta = 0, offsetDelta = 0;
|
||||
for (var i = 0; i < projections.length; i++) {
|
||||
var projection = projections[i];
|
||||
if (projection._lineIndex >= lineIndex - delta) { break; }
|
||||
var lineCount = projection._model.getLineCount() - 1;
|
||||
if (projection._lineIndex + lineCount >= lineIndex - delta) {
|
||||
var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
|
||||
return projection._model.getLineStart (projectionLineIndex) + projection.start + offsetDelta;
|
||||
}
|
||||
offsetDelta += projection._model.getCharCount() - (projection.end - projection.start);
|
||||
delta += lineCount - projection._lineCount;
|
||||
}
|
||||
return model.getLineStart(lineIndex - delta) + offsetDelta;
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#getText
|
||||
*/
|
||||
getText: function(start, end) {
|
||||
if (start === undefined) { start = 0; }
|
||||
var model = this._model, projections = this._projections;
|
||||
var delta = 0, result = [], i, projection, charCount;
|
||||
for (i = 0; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
if (projection.start > start - delta) { break; }
|
||||
charCount = projection._model.getCharCount();
|
||||
if (projection.start + charCount > start - delta) {
|
||||
if (end !== undefined && projection.start + charCount > end - delta) {
|
||||
return projection._model.getText(start - (projection.start + delta), end - (projection.start + delta));
|
||||
} else {
|
||||
result.push(projection._model.getText(start - (projection.start + delta)));
|
||||
start = projection.end + delta + charCount - (projection.end - projection.start);
|
||||
}
|
||||
}
|
||||
delta += charCount - (projection.end - projection.start);
|
||||
}
|
||||
var offset = start - delta;
|
||||
if (end !== undefined) {
|
||||
for (; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
if (projection.start > end - delta) { break; }
|
||||
result.push(model.getText(offset, projection.start));
|
||||
charCount = projection._model.getCharCount();
|
||||
if (projection.start + charCount > end - delta) {
|
||||
result.push(projection._model.getText(0, end - (projection.start + delta)));
|
||||
return result.join("");
|
||||
}
|
||||
result.push(projection._model.getText());
|
||||
offset = projection.end;
|
||||
delta += charCount - (projection.end - projection.start);
|
||||
}
|
||||
result.push(model.getText(offset, end - delta));
|
||||
} else {
|
||||
for (; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
result.push(model.getText(offset, projection.start));
|
||||
result.push(projection._model.getText());
|
||||
offset = projection.end;
|
||||
}
|
||||
result.push(model.getText(offset));
|
||||
}
|
||||
return result.join("");
|
||||
},
|
||||
/** @ignore */
|
||||
_onChanging: function(text, start, removedCharCount, addedCharCount, removedLineCount, addedLineCount) {
|
||||
var model = this._model, projections = this._projections, i, projection, delta = 0, lineDelta;
|
||||
var end = start + removedCharCount;
|
||||
for (; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
if (projection.start > start) { break; }
|
||||
delta += projection._model.getCharCount() - (projection.end - projection.start);
|
||||
}
|
||||
/*TODO add stuff saved by setText*/
|
||||
var mapStart = start + delta, rangeStart = i;
|
||||
for (; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
if (projection.start > end) { break; }
|
||||
delta += projection._model.getCharCount() - (projection.end - projection.start);
|
||||
lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
|
||||
}
|
||||
/*TODO add stuff saved by setText*/
|
||||
var mapEnd = end + delta, rangeEnd = i;
|
||||
this.onChanging(mapStart, mapEnd - mapStart, addedCharCount/*TODO add stuff saved by setText*/, removedLineCount + lineDelta/*TODO add stuff saved by setText*/, addedLineCount/*TODO add stuff saved by setText*/);
|
||||
projections.splice(projections, rangeEnd - rangeStart);
|
||||
var count = text.length - (mapEnd - mapStart);
|
||||
for (; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
projection.start += count;
|
||||
projection.end += count;
|
||||
projection._lineIndex = model.getLineAtOffset(projection.start);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#onChanging
|
||||
*/
|
||||
onChanging: function(modelChangingEvent) {
|
||||
return this.dispatchEvent(modelChangingEvent);
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#onChanged
|
||||
*/
|
||||
onChanged: function(modelChangedEvent) {
|
||||
return this.dispatchEvent(modelChangedEvent);
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#setLineDelimiter
|
||||
*/
|
||||
setLineDelimiter: function(lineDelimiter) {
|
||||
this._model.setLineDelimiter(lineDelimiter);
|
||||
},
|
||||
/**
|
||||
* @see orion.textview.TextModel#setText
|
||||
*/
|
||||
setText: function(text, start, end) {
|
||||
if (text === undefined) { text = ""; }
|
||||
if (start === undefined) { start = 0; }
|
||||
var eventStart = start, eventEnd = end;
|
||||
var model = this._model, projections = this._projections;
|
||||
var delta = 0, lineDelta = 0, i, projection, charCount, startProjection, endProjection, startLineDelta = 0;
|
||||
for (i = 0; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
if (projection.start > start - delta) { break; }
|
||||
charCount = projection._model.getCharCount();
|
||||
if (projection.start + charCount > start - delta) {
|
||||
if (end !== undefined && projection.start + charCount > end - delta) {
|
||||
projection._model.setText(text, start - (projection.start + delta), end - (projection.start + delta));
|
||||
//TODO events - special case
|
||||
return;
|
||||
} else {
|
||||
startLineDelta = projection._model.getLineCount() - 1 - projection._model.getLineAtOffset(start - (projection.start + delta));
|
||||
startProjection = {
|
||||
projection: projection,
|
||||
start: start - (projection.start + delta)
|
||||
};
|
||||
start = projection.end + delta + charCount - (projection.end - projection.start);
|
||||
}
|
||||
}
|
||||
lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
|
||||
delta += charCount - (projection.end - projection.start);
|
||||
}
|
||||
var mapStart = start - delta, rangeStart = i, startLine = model.getLineAtOffset(mapStart) + lineDelta - startLineDelta;
|
||||
if (end !== undefined) {
|
||||
for (; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
if (projection.start > end - delta) { break; }
|
||||
charCount = projection._model.getCharCount();
|
||||
if (projection.start + charCount > end - delta) {
|
||||
lineDelta += projection._model.getLineAtOffset(end - (projection.start + delta));
|
||||
charCount = end - (projection.start + delta);
|
||||
end = projection.end + delta;
|
||||
endProjection = {
|
||||
projection: projection,
|
||||
end: charCount
|
||||
};
|
||||
break;
|
||||
}
|
||||
lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
|
||||
delta += charCount - (projection.end - projection.start);
|
||||
}
|
||||
} else {
|
||||
for (; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
|
||||
delta += projection._model.getCharCount() - (projection.end - projection.start);
|
||||
}
|
||||
end = eventEnd = model.getCharCount() + delta;
|
||||
}
|
||||
var mapEnd = end - delta, rangeEnd = i, endLine = model.getLineAtOffset(mapEnd) + lineDelta;
|
||||
|
||||
//events
|
||||
var removedCharCount = eventEnd - eventStart;
|
||||
var removedLineCount = endLine - startLine;
|
||||
var addedCharCount = text.length;
|
||||
var addedLineCount = 0;
|
||||
var cr = 0, lf = 0, index = 0;
|
||||
while (true) {
|
||||
if (cr !== -1 && cr <= index) { cr = text.indexOf("\r", index); }
|
||||
if (lf !== -1 && lf <= index) { lf = text.indexOf("\n", index); }
|
||||
if (lf === -1 && cr === -1) { break; }
|
||||
if (cr !== -1 && lf !== -1) {
|
||||
if (cr + 1 === lf) {
|
||||
index = lf + 1;
|
||||
} else {
|
||||
index = (cr < lf ? cr : lf) + 1;
|
||||
}
|
||||
} else if (cr !== -1) {
|
||||
index = cr + 1;
|
||||
} else {
|
||||
index = lf + 1;
|
||||
}
|
||||
addedLineCount++;
|
||||
}
|
||||
|
||||
var modelChangingEvent = {
|
||||
type: "Changing",
|
||||
text: text,
|
||||
start: eventStart,
|
||||
removedCharCount: removedCharCount,
|
||||
addedCharCount: addedCharCount,
|
||||
removedLineCount: removedLineCount,
|
||||
addedLineCount: addedLineCount
|
||||
};
|
||||
this.onChanging(modelChangingEvent);
|
||||
|
||||
// var changeLineCount = model.getLineAtOffset(mapEnd) - model.getLineAtOffset(mapStart) + addedLineCount;
|
||||
model.setText(text, mapStart, mapEnd);
|
||||
if (startProjection) {
|
||||
projection = startProjection.projection;
|
||||
projection._model.setText("", startProjection.start);
|
||||
}
|
||||
if (endProjection) {
|
||||
projection = endProjection.projection;
|
||||
projection._model.setText("", 0, endProjection.end);
|
||||
projection.start = projection.end;
|
||||
projection._lineCount = 0;
|
||||
}
|
||||
projections.splice(rangeStart, rangeEnd - rangeStart);
|
||||
var changeCount = text.length - (mapEnd - mapStart);
|
||||
for (i = rangeEnd; i < projections.length; i++) {
|
||||
projection = projections[i];
|
||||
projection.start += changeCount;
|
||||
projection.end += changeCount;
|
||||
// if (projection._lineIndex + changeLineCount !== model.getLineAtOffset(projection.start)) {
|
||||
// log("here");
|
||||
// }
|
||||
projection._lineIndex = model.getLineAtOffset(projection.start);
|
||||
// projection._lineIndex += changeLineCount;
|
||||
}
|
||||
|
||||
var modelChangedEvent = {
|
||||
type: "Changed",
|
||||
start: eventStart,
|
||||
removedCharCount: removedCharCount,
|
||||
addedCharCount: addedCharCount,
|
||||
removedLineCount: removedLineCount,
|
||||
addedLineCount: addedLineCount
|
||||
};
|
||||
this.onChanged(modelChangedEvent);
|
||||
}
|
||||
};
|
||||
mEventTarget.EventTarget.addMixin(ProjectionTextModel.prototype);
|
||||
|
||||
return {ProjectionTextModel: ProjectionTextModel};
|
||||
});
|
||||
/*******************************************************************************
|
||||
* @license
|
||||
* Copyright (c) 2010, 2011 IBM Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials are made
|
||||
|
@ -908,7 +908,8 @@ SourceEditor.prototype = {
|
||||
*
|
||||
* @private
|
||||
* @param string aType
|
||||
* The annotation type to filter annotations for.
|
||||
* The annotation type to filter annotations for. Use one of the keys
|
||||
* in ORION_ANNOTATION_TYPES.
|
||||
* @param number aStart
|
||||
* Offset from where to start finding the annotations.
|
||||
* @param number aEnd
|
||||
|
@ -268,24 +268,24 @@ SourceEditor.EVENTS = {
|
||||
BLUR: "Blur",
|
||||
|
||||
/**
|
||||
* The MouseMove event is sent when the user moves the mouse over a line
|
||||
* annotation. The event object properties:
|
||||
* The MouseMove event is sent when the user moves the mouse over a line.
|
||||
* The event object properties:
|
||||
* - event - the DOM mousemove event object.
|
||||
* - x and y - the mouse coordinates relative to the document being edited.
|
||||
*/
|
||||
MOUSE_MOVE: "MouseMove",
|
||||
|
||||
/**
|
||||
* The MouseOver event is sent when the mouse pointer enters a line
|
||||
* annotation. The event object properties:
|
||||
* The MouseOver event is sent when the mouse pointer enters a line.
|
||||
* The event object properties:
|
||||
* - event - the DOM mouseover event object.
|
||||
* - x and y - the mouse coordinates relative to the document being edited.
|
||||
*/
|
||||
MOUSE_OVER: "MouseOver",
|
||||
|
||||
/**
|
||||
* This MouseOut event is sent when the mouse pointer exits a line
|
||||
* annotation. The event object properties:
|
||||
* This MouseOut event is sent when the mouse pointer exits a line.
|
||||
* The event object properties:
|
||||
* - event - the DOM mouseout event object.
|
||||
* - x and y - the mouse coordinates relative to the document being edited.
|
||||
*/
|
||||
|
@ -41,6 +41,7 @@
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
const FILTER_CHANGED_TIMEOUT = 300;
|
||||
|
||||
@ -161,9 +162,18 @@ function CssHtmlTree(aStyleInspector)
|
||||
this.getRTLAttr = this.win.getComputedStyle(this.win.gBrowser).direction;
|
||||
this.propertyViews = [];
|
||||
|
||||
// Create bound methods.
|
||||
this.siBoundMenuUpdate = this.computedViewMenuUpdate.bind(this);
|
||||
this.siBoundCopy = this.computedViewCopy.bind(this);
|
||||
this.siBoundCopyDeclaration = this.computedViewCopyDeclaration.bind(this);
|
||||
this.siBoundCopyProperty = this.computedViewCopyProperty.bind(this);
|
||||
this.siBoundCopyPropertyValue = this.computedViewCopyPropertyValue.bind(this);
|
||||
|
||||
// The document in which we display the results (csshtmltree.xul).
|
||||
this.styleDocument = this.styleWin.contentWindow.document;
|
||||
|
||||
this.styleDocument.addEventListener("copy", this.siBoundCopy);
|
||||
|
||||
// Nodes used in templating
|
||||
this.root = this.styleDocument.getElementById("root");
|
||||
this.templateRoot = this.styleDocument.getElementById("templateRoot");
|
||||
@ -176,6 +186,7 @@ function CssHtmlTree(aStyleInspector)
|
||||
// The element that we're inspecting, and the document that it comes from.
|
||||
this.viewedElement = null;
|
||||
this.createStyleViews();
|
||||
this.createContextMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -231,6 +242,11 @@ XPCOMUtils.defineLazyGetter(CssHtmlTree, "HELP_LINK_TITLE", function() {
|
||||
return CssHtmlTree.HELP_LINK_TITLE = CssHtmlTree.l10n("helpLinkTitle");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
|
||||
return Cc["@mozilla.org/widget/clipboardhelper;1"].
|
||||
getService(Ci.nsIClipboardHelper);
|
||||
});
|
||||
|
||||
CssHtmlTree.prototype = {
|
||||
// Cache the list of properties that have matched and unmatched properties.
|
||||
_matchedProperties: null,
|
||||
@ -469,6 +485,177 @@ CssHtmlTree.prototype = {
|
||||
return this._unmatchedProperties[aProperty];
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a context menu.
|
||||
*/
|
||||
createContextMenu: function SI_createContextMenu()
|
||||
{
|
||||
let popupSet = this.doc.getElementById("mainPopupSet");
|
||||
|
||||
let menu = this.doc.createElement("menupopup");
|
||||
menu.addEventListener("popupshowing", this.siBoundMenuUpdate);
|
||||
menu.id = "computed-view-context-menu";
|
||||
popupSet.appendChild(menu);
|
||||
|
||||
// Copy selection
|
||||
let label = CssHtmlTree.l10n("style.contextmenu.copyselection");
|
||||
let accessKey = CssHtmlTree.l10n("style.contextmenu.copyselection.accesskey");
|
||||
let item = this.doc.createElement("menuitem");
|
||||
item.id = "computed-view-copy";
|
||||
item.setAttribute("label", label);
|
||||
item.setAttribute("accesskey", accessKey);
|
||||
item.addEventListener("command", this.siBoundCopy);
|
||||
menu.appendChild(item);
|
||||
|
||||
// Copy declaration
|
||||
label = CssHtmlTree.l10n("style.contextmenu.copydeclaration");
|
||||
accessKey = CssHtmlTree.l10n("style.contextmenu.copydeclaration.accesskey");
|
||||
item = this.doc.createElement("menuitem");
|
||||
item.id = "computed-view-copy-declaration";
|
||||
item.setAttribute("label", label);
|
||||
item.setAttribute("accesskey", accessKey);
|
||||
item.addEventListener("command", this.siBoundCopyDeclaration);
|
||||
menu.appendChild(item);
|
||||
|
||||
// Copy property name
|
||||
label = CssHtmlTree.l10n("style.contextmenu.copyproperty");
|
||||
accessKey = CssHtmlTree.l10n("style.contextmenu.copyproperty.accesskey");
|
||||
item = this.doc.createElement("menuitem");
|
||||
item.id = "computed-view-copy-property";
|
||||
item.setAttribute("label", label);
|
||||
item.setAttribute("accesskey", accessKey);
|
||||
item.addEventListener("command", this.siBoundCopyProperty);
|
||||
menu.appendChild(item);
|
||||
|
||||
// Copy property value
|
||||
label = CssHtmlTree.l10n("style.contextmenu.copypropertyvalue");
|
||||
accessKey = CssHtmlTree.l10n("style.contextmenu.copypropertyvalue.accesskey");
|
||||
item = this.doc.createElement("menuitem");
|
||||
item.id = "computed-view-copy-property-value";
|
||||
item.setAttribute("label", label);
|
||||
item.setAttribute("accesskey", accessKey);
|
||||
item.addEventListener("command", this.siBoundCopyPropertyValue);
|
||||
menu.appendChild(item);
|
||||
|
||||
this.styleWin.setAttribute("context", menu.id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the context menu by disabling irrelevant menuitems and enabling
|
||||
* relevant ones.
|
||||
*/
|
||||
computedViewMenuUpdate: function si_computedViewMenuUpdate()
|
||||
{
|
||||
let win = this.styleDocument.defaultView;
|
||||
let disable = win.getSelection().isCollapsed;
|
||||
let menuitem = this.doc.querySelector("#computed-view-copy");
|
||||
menuitem.disabled = disable;
|
||||
|
||||
let node = this.doc.popupNode;
|
||||
if (!node.classList.contains("property-view")) {
|
||||
while (node = node.parentElement) {
|
||||
if (node.classList.contains("property-view")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let disablePropertyItems = !node;
|
||||
menuitem = this.doc.querySelector("#computed-view-copy-declaration");
|
||||
menuitem.disabled = disablePropertyItems;
|
||||
menuitem = this.doc.querySelector("#computed-view-copy-property");
|
||||
menuitem.disabled = disablePropertyItems;
|
||||
menuitem = this.doc.querySelector("#computed-view-copy-property-value");
|
||||
menuitem.disabled = disablePropertyItems;
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy selected text.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
computedViewCopy: function si_computedViewCopy(aEvent)
|
||||
{
|
||||
let win = this.styleDocument.defaultView;
|
||||
let text = win.getSelection().toString();
|
||||
|
||||
// Tidy up block headings by moving CSS property names and their values onto
|
||||
// the same line and inserting a colon between them.
|
||||
text = text.replace(/(.+)\r?\n\s+/g, "$1: ");
|
||||
|
||||
// Remove any MDN link titles
|
||||
text = text.replace(CssHtmlTree.HELP_LINK_TITLE, "");
|
||||
clipboardHelper.copyString(text);
|
||||
|
||||
if (aEvent) {
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy declaration.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
computedViewCopyDeclaration: function si_computedViewCopyDeclaration(aEvent)
|
||||
{
|
||||
let node = this.doc.popupNode;
|
||||
if (!node.classList.contains("property-view")) {
|
||||
while (node = node.parentElement) {
|
||||
if (node.classList.contains("property-view")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node) {
|
||||
let name = node.querySelector(".property-name").textContent;
|
||||
let value = node.querySelector(".property-value").textContent;
|
||||
|
||||
clipboardHelper.copyString(name + ": " + value + ";");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy property name.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
computedViewCopyProperty: function si_computedViewCopyProperty(aEvent)
|
||||
{
|
||||
let node = this.doc.popupNode;
|
||||
if (!node.classList.contains("property-view")) {
|
||||
while (node = node.parentElement) {
|
||||
if (node.classList.contains("property-view")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node) {
|
||||
node = node.querySelector(".property-name");
|
||||
clipboardHelper.copyString(node.textContent);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy property value.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
*/
|
||||
computedViewCopyPropertyValue: function si_computedViewCopyPropertyValue(aEvent)
|
||||
{
|
||||
let node = this.doc.popupNode;
|
||||
if (!node.classList.contains("property-view")) {
|
||||
while (node = node.parentElement) {
|
||||
if (node.classList.contains("property-view")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node) {
|
||||
node = node.querySelector(".property-value");
|
||||
clipboardHelper.copyString(node.textContent);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Destructor for CssHtmlTree.
|
||||
*/
|
||||
@ -486,6 +673,32 @@ CssHtmlTree.prototype = {
|
||||
this._refreshProcess.cancel();
|
||||
}
|
||||
|
||||
// Remove context menu
|
||||
let menu = this.doc.querySelector("#computed-view-context-menu");
|
||||
if (menu) {
|
||||
// Copy selected
|
||||
let menuitem = this.doc.querySelector("#computed-view-copy");
|
||||
menuitem.removeEventListener("command", this.siBoundCopy);
|
||||
|
||||
// Copy property
|
||||
menuitem = this.doc.querySelector("#computed-view-copy-declaration");
|
||||
menuitem.removeEventListener("command", this.siBoundCopyDeclaration);
|
||||
|
||||
// Copy property name
|
||||
menuitem = this.doc.querySelector("#computed-view-copy-property");
|
||||
menuitem.removeEventListener("command", this.siBoundCopyProperty);
|
||||
|
||||
// Copy property value
|
||||
menuitem = this.doc.querySelector("#computed-view-copy-property-value");
|
||||
menuitem.removeEventListener("command", this.siBoundCopyPropertyValue);
|
||||
|
||||
menu.removeEventListener("popupshowing", this.siBoundMenuUpdate);
|
||||
menu.parentNode.removeChild(menu);
|
||||
}
|
||||
|
||||
// Remove bound listeners
|
||||
this.styleDocument.removeEventListener("copy", this.siBoundCopy);
|
||||
|
||||
// Nodes used in templating
|
||||
delete this.root;
|
||||
delete this.propertyContainer;
|
||||
@ -658,32 +871,35 @@ PropertyView.prototype = {
|
||||
let doc = this.tree.doc;
|
||||
this.element = doc.createElementNS(HTML_NS, "tr");
|
||||
this.element.setAttribute("class", this.propertyHeaderClassName);
|
||||
this.element.addEventListener("click", this.propertyRowClick.bind(this), false);
|
||||
|
||||
this.propertyHeader = doc.createElementNS(HTML_NS, "td");
|
||||
this.element.appendChild(this.propertyHeader);
|
||||
this.propertyHeader.setAttribute("class", "property-header");
|
||||
|
||||
this.matchedExpander = doc.createElementNS(HTML_NS, "div");
|
||||
this.propertyHeader.appendChild(this.matchedExpander);
|
||||
this.matchedExpander.setAttribute("class", "match expander");
|
||||
|
||||
this.nameNode = doc.createElementNS(HTML_NS, "div");
|
||||
this.propertyHeader.appendChild(this.nameNode);
|
||||
this.nameNode.setAttribute("tabindex", "0");
|
||||
this.nameNode.addEventListener("keydown", function(aEvent) {
|
||||
this.matchedExpander.setAttribute("tabindex", "0");
|
||||
this.matchedExpander.addEventListener("click",
|
||||
this.matchedExpanderClick.bind(this), false);
|
||||
this.matchedExpander.addEventListener("keydown", function(aEvent) {
|
||||
let keyEvent = Ci.nsIDOMKeyEvent;
|
||||
if (aEvent.keyCode == keyEvent.DOM_VK_F1) {
|
||||
this.mdnLinkClick();
|
||||
}
|
||||
if (aEvent.keyCode == keyEvent.DOM_VK_RETURN ||
|
||||
aEvent.keyCode == keyEvent.DOM_VK_SPACE) {
|
||||
this.propertyRowClick(aEvent);
|
||||
this.matchedExpanderClick(aEvent);
|
||||
}
|
||||
}.bind(this), false);
|
||||
this.propertyHeader.appendChild(this.matchedExpander);
|
||||
|
||||
this.nameNode = doc.createElementNS(HTML_NS, "div");
|
||||
this.propertyHeader.appendChild(this.nameNode);
|
||||
this.nameNode.setAttribute("class", "property-name");
|
||||
this.nameNode.textContent = this.name;
|
||||
this.nameNode.addEventListener("click", function(aEvent) {
|
||||
this.matchedExpander.focus();
|
||||
}.bind(this), false);
|
||||
|
||||
let helpcontainer = doc.createElementNS(HTML_NS, "td");
|
||||
this.element.appendChild(helpcontainer);
|
||||
@ -754,9 +970,9 @@ PropertyView.prototype = {
|
||||
this.matchedSelectorsContainer.parentNode.hidden = !hasMatchedSelectors;
|
||||
|
||||
if (hasMatchedSelectors) {
|
||||
this.propertyHeader.parentNode.classList.add("expandable");
|
||||
this.matchedExpander.classList.add("expandable");
|
||||
} else {
|
||||
this.propertyHeader.parentNode.classList.remove("expandable");
|
||||
this.matchedExpander.classList.remove("expandable");
|
||||
}
|
||||
|
||||
if (this.matchedExpanded && hasMatchedSelectors) {
|
||||
@ -852,17 +1068,13 @@ PropertyView.prototype = {
|
||||
* The action when a user expands matched selectors.
|
||||
*
|
||||
* @param {Event} aEvent Used to determine the class name of the targets click
|
||||
* event. If the class name is "helplink" then the event is allowed to bubble
|
||||
* to the mdn link icon.
|
||||
* event.
|
||||
*/
|
||||
propertyRowClick: function PropertyView_propertyRowClick(aEvent)
|
||||
matchedExpanderClick: function PropertyView_matchedExpanderClick(aEvent)
|
||||
{
|
||||
if (aEvent.target.className != "helplink") {
|
||||
this.matchedExpanded = !this.matchedExpanded;
|
||||
this.refreshAllSelectors();
|
||||
this.nameNode.focus();
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -986,6 +1198,14 @@ SelectorView.prototype = {
|
||||
return result;
|
||||
},
|
||||
|
||||
maybeOpenStyleEditor: function(aEvent)
|
||||
{
|
||||
let keyEvent = Ci.nsIDOMKeyEvent;
|
||||
if (aEvent.keyCode == keyEvent.DOM_VK_RETURN) {
|
||||
this.openStyleEditor();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When a css link is clicked this method is called in order to either:
|
||||
* 1. Open the link in view source (for element style attributes).
|
||||
|
@ -927,7 +927,8 @@ CssLogic.shortSource = function CssLogic_shortSource(aSheet)
|
||||
return url.query;
|
||||
}
|
||||
|
||||
return aSheet.href;
|
||||
let dataUrl = aSheet.href.match(/^(data:[^,]*),/);
|
||||
return dataUrl ? dataUrl[1] : aSheet.href;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,6 +23,7 @@
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com> (Original Author)
|
||||
* Rob Campbell <rcampbell@mozilla.com>
|
||||
* Mike Ratcliffe <mratcliffe@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -807,7 +808,7 @@ CssRuleView.prototype = {
|
||||
for each (let rule in this._elementStyle.rules) {
|
||||
// Don't hold a reference to this editor beyond the one held
|
||||
// by the node.
|
||||
let editor = new RuleEditor(this.doc, rule);
|
||||
let editor = new RuleEditor(this, rule);
|
||||
this.element.appendChild(editor.element);
|
||||
}
|
||||
},
|
||||
@ -816,15 +817,17 @@ CssRuleView.prototype = {
|
||||
/**
|
||||
* Create a RuleEditor.
|
||||
*
|
||||
* @param object aDoc
|
||||
* The document holding this rule editor.
|
||||
* @param CssRuleView aRuleView
|
||||
* The CssRuleView containg the document holding this rule editor and the
|
||||
* _selectionMode flag.
|
||||
* @param Rule aRule
|
||||
* The Rule object we're editing.
|
||||
* @constructor
|
||||
*/
|
||||
function RuleEditor(aDoc, aRule)
|
||||
function RuleEditor(aRuleView, aRule)
|
||||
{
|
||||
this.doc = aDoc;
|
||||
this.ruleView = aRuleView;
|
||||
this.doc = this.ruleView.doc;
|
||||
this.rule = aRule;
|
||||
|
||||
this._onNewProperty = this._onNewProperty.bind(this);
|
||||
@ -893,8 +896,16 @@ RuleEditor.prototype = {
|
||||
|
||||
// We made the close brace focusable, tabbing to it
|
||||
// or clicking on it should start the new property editor.
|
||||
this.closeBrace.addEventListener("focus", function() {
|
||||
this.closeBrace.addEventListener("focus", function(aEvent) {
|
||||
if (!this.ruleView._selectionMode) {
|
||||
this.newProperty();
|
||||
}
|
||||
}.bind(this), true);
|
||||
this.closeBrace.addEventListener("mousedown", function(aEvent) {
|
||||
aEvent.preventDefault();
|
||||
}.bind(this), true);
|
||||
this.closeBrace.addEventListener("click", function(aEvent) {
|
||||
this.closeBrace.focus();
|
||||
}.bind(this), true);
|
||||
},
|
||||
|
||||
@ -1261,6 +1272,21 @@ function editableField(aOptions)
|
||||
aOptions.element.addEventListener("focus", function() {
|
||||
new InplaceEditor(aOptions);
|
||||
}, false);
|
||||
|
||||
// In order to allow selection on the element, prevent focus on
|
||||
// mousedown. Focus on click instead.
|
||||
aOptions.element.addEventListener("mousedown", function(evt) {
|
||||
evt.preventDefault();
|
||||
}, false);
|
||||
aOptions.element.addEventListener("click", function(evt) {
|
||||
let win = this.ownerDocument.defaultView;
|
||||
let selection = win.getSelection();
|
||||
if (selection.isCollapsed) {
|
||||
aOptions.element.focus();
|
||||
} else {
|
||||
selection.removeAllRanges();
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
var _editableField = editableField;
|
||||
|
||||
|
@ -55,7 +55,7 @@ var EXPORTED_SYMBOLS = ["StyleInspector"];
|
||||
function StyleInspector(aContext, aIUI)
|
||||
{
|
||||
this._init(aContext, aIUI);
|
||||
};
|
||||
}
|
||||
|
||||
StyleInspector.prototype = {
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
- ***** END LICENSE BLOCK ***** -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/devtools/styleinspector.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/csshtmltree.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
@ -114,8 +115,11 @@ To visually debug the templates without running firefox, alter the display:none
|
||||
${selector.humanReadableText(__element)}
|
||||
</td>
|
||||
<td class="rule-link">
|
||||
<a target="_blank" onclick="${selector.openStyleEditor}" class="link"
|
||||
title="${selector.selectorInfo.href}">${selector.selectorInfo.source}</a>
|
||||
<a target="_blank" class="link"
|
||||
onclick="${selector.openStyleEditor}"
|
||||
onkeydown="${selector.maybeOpenStyleEditor}"
|
||||
title="${selector.selectorInfo.href}"
|
||||
tabindex="0">${selector.selectorInfo.source}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</loop>
|
||||
|
@ -35,11 +35,39 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
.ruleview {
|
||||
overflow: auto;
|
||||
#root {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
.ruleview-computedlist:not(.styleinspector-open) {
|
||||
.helplink {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.expander,
|
||||
.property-name,
|
||||
.ruleview-propertyname,
|
||||
.ruleview-warning,
|
||||
.ruleview-expander {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#propertyContainer {
|
||||
display: -moz-box;
|
||||
-moz-box-orient: vertical;
|
||||
-moz-box-flex: 1;
|
||||
overflow-y: auto;
|
||||
-moz-user-select: text;
|
||||
}
|
||||
|
||||
.ruleview {
|
||||
overflow: auto;
|
||||
-moz-user-select: text;
|
||||
}
|
||||
|
||||
.property-view-hidden,
|
||||
.property-content-hidden,
|
||||
.ruleview-computedlist:not(.styleinspector-open),
|
||||
.ruleview-warning[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,8 @@ _BROWSER_TEST_FILES = \
|
||||
browser_bug722196_property_view_media_queries.js \
|
||||
browser_bug722196_rule_view_media_queries.js \
|
||||
browser_bug_592743_specificity.js \
|
||||
browser_ruleview_bug_703643_context_menu_copy.js \
|
||||
browser_computedview_bug_703643_context_menu_copy.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
@ -55,12 +55,12 @@ function SI_test()
|
||||
let searchbar = stylePanel.cssHtmlTree.searchField;
|
||||
let propView = getFirstVisiblePropertyView();
|
||||
let rulesTable = propView.matchedSelectorsContainer;
|
||||
let nameNode = propView.nameNode;
|
||||
let matchedExpander = propView.matchedExpander;
|
||||
|
||||
info("Adding focus event handler to property name node");
|
||||
nameNode.addEventListener("focus", function nameFocused() {
|
||||
this.removeEventListener("focus", nameFocused);
|
||||
info("property name is focused");
|
||||
info("Adding focus event handler to property expander");
|
||||
matchedExpander.addEventListener("focus", function expanderFocused() {
|
||||
this.removeEventListener("focus", expanderFocused);
|
||||
info("property expander is focused");
|
||||
info("checking expand / collapse");
|
||||
testKey(iframe.contentWindow, "VK_SPACE", rulesTable);
|
||||
testKey(iframe.contentWindow, "VK_RETURN", rulesTable);
|
||||
@ -74,7 +74,7 @@ function SI_test()
|
||||
searchbar.addEventListener("focus", function searchbarFocused() {
|
||||
this.removeEventListener("focus", searchbarFocused);
|
||||
info("search filter is focused");
|
||||
info("tabbing to property name node");
|
||||
info("tabbing to property expander node");
|
||||
EventUtils.synthesizeKey("VK_TAB", {}, iframe.contentWindow);
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,163 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the style inspector works properly
|
||||
|
||||
let doc;
|
||||
let stylePanel;
|
||||
let cssHtmlTree;
|
||||
|
||||
function createDocument()
|
||||
{
|
||||
doc.body.innerHTML = '<style type="text/css"> ' +
|
||||
'span { font-variant: small-caps; color: #000000; } ' +
|
||||
'.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ' +
|
||||
'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">\n' +
|
||||
'<h1>Some header text</h1>\n' +
|
||||
'<p id="salutation" style="font-size: 12pt">hi.</p>\n' +
|
||||
'<p id="body" style="font-size: 12pt">I am a test-case. This text exists ' +
|
||||
'solely to provide some things to <span style="color: yellow">' +
|
||||
'highlight</span> and <span style="font-weight: bold">count</span> ' +
|
||||
'style list-items in the box at right. If you are reading this, ' +
|
||||
'you should go do something else instead. Maybe read a book. Or better ' +
|
||||
'yet, write some test-cases for another bit of code. ' +
|
||||
'<span style="font-style: italic">some text</span></p>\n' +
|
||||
'<p id="closing">more text</p>\n' +
|
||||
'<p>even more text</p>' +
|
||||
'</div>';
|
||||
doc.title = "Computed view context menu test";
|
||||
|
||||
let span = doc.querySelector("span");
|
||||
ok(span, "captain, we have the span");
|
||||
|
||||
stylePanel = new StyleInspector(window);
|
||||
Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-populated", false);
|
||||
stylePanel.createPanel(false, function() {
|
||||
stylePanel.open(span);
|
||||
});
|
||||
}
|
||||
|
||||
function runStyleInspectorTests()
|
||||
{
|
||||
Services.obs.removeObserver(runStyleInspectorTests, "StyleInspector-populated", false);
|
||||
|
||||
ok(stylePanel.isOpen(), "style inspector is open");
|
||||
|
||||
cssHtmlTree = stylePanel.cssHtmlTree;
|
||||
|
||||
let contentDocument = stylePanel.iframe.contentDocument;
|
||||
let prop = contentDocument.querySelector(".property-view");
|
||||
ok(prop, "captain, we have the property-view node");
|
||||
|
||||
// We need the context menu to open in the correct place in order for
|
||||
// popupNode to be propertly set.
|
||||
EventUtils.synthesizeMouse(prop, 1, 1, { type: "contextmenu", button: 2 },
|
||||
stylePanel.iframe.contentWindow);
|
||||
|
||||
checkCopyProperty()
|
||||
}
|
||||
|
||||
function checkCopyProperty()
|
||||
{
|
||||
info("Checking that cssHtmlTree.siBoundCopyDeclaration() returns the " +
|
||||
"correct clipboard value");
|
||||
let expectedPattern = "color: rgb\\(255, 255, 0\\);";
|
||||
info("Expected pattern: " + expectedPattern);
|
||||
|
||||
SimpleTest.waitForClipboard(function CS_boundCopyPropCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},
|
||||
cssHtmlTree.siBoundCopyDeclaration,
|
||||
checkCopyPropertyName, checkCopyPropertyName);
|
||||
}
|
||||
|
||||
function checkCopyPropertyName()
|
||||
{
|
||||
info("Checking that cssHtmlTree.siBoundCopyProperty() returns the " +
|
||||
"correct clipboard value");
|
||||
let expectedPattern = "color";
|
||||
info("Expected pattern: " + expectedPattern);
|
||||
|
||||
SimpleTest.waitForClipboard(function CS_boundCopyPropNameCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},
|
||||
cssHtmlTree.siBoundCopyProperty,
|
||||
checkCopyPropertyValue, checkCopyPropertyValue);
|
||||
}
|
||||
|
||||
function checkCopyPropertyValue()
|
||||
{
|
||||
info("Checking that cssHtmlTree.siBoundCopyPropertyValue() returns the " +
|
||||
"correct clipboard value");
|
||||
let expectedPattern = "rgb\\(255, 255, 0\\)";
|
||||
info("Expected pattern: " + expectedPattern);
|
||||
|
||||
SimpleTest.waitForClipboard(function CS_boundCopyPropValueCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},
|
||||
cssHtmlTree.siBoundCopyPropertyValue,
|
||||
checkCopySelection, checkCopySelection);
|
||||
}
|
||||
|
||||
function checkCopySelection()
|
||||
{
|
||||
let contentDocument = stylePanel.iframe.contentDocument;
|
||||
let contentWindow = stylePanel.iframe.contentWindow;
|
||||
let props = contentDocument.querySelectorAll(".property-view");
|
||||
ok(props, "captain, we have the property-view nodes");
|
||||
|
||||
let range = document.createRange();
|
||||
range.setStart(props[0], 0);
|
||||
range.setEnd(props[3], 3);
|
||||
contentWindow.getSelection().addRange(range);
|
||||
|
||||
info("Checking that cssHtmlTree.siBoundCopyPropertyValue() " +
|
||||
" returns the correct clipboard value");
|
||||
|
||||
let expectedPattern = "color: rgb\\(255, 255, 0\\)[\\r\\n]+" +
|
||||
"font-family: helvetica,sans-serif[\\r\\n]+" +
|
||||
"font-size: 16px[\\r\\n]+" +
|
||||
"font-variant: small-caps[\\r\\n]*";
|
||||
info("Expected pattern: " + expectedPattern);
|
||||
|
||||
SimpleTest.waitForClipboard(function CS_boundCopyCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},
|
||||
cssHtmlTree.siBoundCopy, closeStyleInspector, closeStyleInspector);
|
||||
}
|
||||
|
||||
function checkClipboardData(aExpectedPattern)
|
||||
{
|
||||
let actual = SpecialPowers.getClipboardData("text/unicode");
|
||||
let expectedRegExp = new RegExp(aExpectedPattern, "g");
|
||||
return expectedRegExp.test(actual);
|
||||
}
|
||||
|
||||
function closeStyleInspector()
|
||||
{
|
||||
Services.obs.addObserver(finishUp, "StyleInspector-closed", false);
|
||||
stylePanel.close();
|
||||
}
|
||||
|
||||
function finishUp()
|
||||
{
|
||||
Services.obs.removeObserver(finishUp, "StyleInspector-closed", false);
|
||||
ok(!stylePanel.isOpen(), "style inspector is closed");
|
||||
doc = stylePanel = cssHtmlTree = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||
doc = content.document;
|
||||
waitForFocus(createDocument, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,computed view context menu test";
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let doc;
|
||||
|
||||
function createDocument()
|
||||
{
|
||||
doc.body.innerHTML = '<style type="text/css"> ' +
|
||||
'html { color: #000000; } ' +
|
||||
'span { font-variant: small-caps; color: #000000; } ' +
|
||||
'.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ' +
|
||||
'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">\n' +
|
||||
'<h1>Some header text</h1>\n' +
|
||||
'<p id="salutation" style="font-size: 12pt">hi.</p>\n' +
|
||||
'<p id="body" style="font-size: 12pt">I am a test-case. This text exists ' +
|
||||
'solely to provide some things to <span style="color: yellow">' +
|
||||
'highlight</span> and <span style="font-weight: bold">count</span> ' +
|
||||
'style list-items in the box at right. If you are reading this, ' +
|
||||
'you should go do something else instead. Maybe read a book. Or better ' +
|
||||
'yet, write some test-cases for another bit of code. ' +
|
||||
'<span style="font-style: italic">some text</span></p>\n' +
|
||||
'<p id="closing">more text</p>\n' +
|
||||
'<p>even more text</p>' +
|
||||
'</div>';
|
||||
doc.title = "Rule view context menu test";
|
||||
|
||||
openInspector();
|
||||
}
|
||||
|
||||
function openInspector()
|
||||
{
|
||||
ok(window.InspectorUI, "InspectorUI variable exists");
|
||||
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
|
||||
ok(InspectorUI.store.isEmpty(), "Inspector.store is empty");
|
||||
|
||||
Services.obs.addObserver(inspectorUIOpen,
|
||||
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
|
||||
InspectorUI.openInspectorUI();
|
||||
}
|
||||
|
||||
function inspectorUIOpen()
|
||||
{
|
||||
Services.obs.removeObserver(inspectorUIOpen,
|
||||
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
|
||||
|
||||
// Make sure the inspector is open.
|
||||
ok(InspectorUI.inspecting, "Inspector is highlighting");
|
||||
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
|
||||
ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open");
|
||||
ok(!InspectorUI.store.isEmpty(), "InspectorUI.store is not empty");
|
||||
is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
|
||||
|
||||
// Highlight a node.
|
||||
let div = content.document.getElementsByTagName("div")[0];
|
||||
InspectorUI.inspectNode(div);
|
||||
InspectorUI.stopInspecting();
|
||||
is(InspectorUI.selection, div, "selection matches the div element");
|
||||
|
||||
Services.obs.addObserver(testClip,
|
||||
InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
|
||||
|
||||
InspectorUI.showSidebar();
|
||||
InspectorUI.openRuleView();
|
||||
}
|
||||
|
||||
function testClip()
|
||||
{
|
||||
Services.obs.removeObserver(testClip,
|
||||
InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
|
||||
|
||||
executeSoon(function() {
|
||||
info("Checking that InspectorUI.ruleViewCopyRule() returns " +
|
||||
"the correct clipboard value");
|
||||
let expectedPattern = "element {[\\r\\n]+" +
|
||||
" margin: 10em;[\\r\\n]+" +
|
||||
" font-size: 14pt;[\\r\\n]+" +
|
||||
" font-family: helvetica,sans-serif;[\\r\\n]+" +
|
||||
" color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
|
||||
"}[\\r\\n]*";
|
||||
info("Expected pattern: " + expectedPattern);
|
||||
|
||||
SimpleTest.waitForClipboard(function IUI_boundCopyPropCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},
|
||||
checkCopyRule, checkCopyProperty, checkCopyProperty);
|
||||
});
|
||||
}
|
||||
|
||||
function checkCopyRule() {
|
||||
let ruleView = document.querySelector("#devtools-sidebar-iframe-ruleview");
|
||||
let contentDoc = ruleView.contentDocument;
|
||||
let props = contentDoc.querySelectorAll(".ruleview-property");
|
||||
|
||||
is(props.length, 5, "checking property length");
|
||||
|
||||
let prop = props[2];
|
||||
let propName = prop.querySelector(".ruleview-propertyname").textContent;
|
||||
let propValue = prop.querySelector(".ruleview-propertyvalue").textContent;
|
||||
|
||||
is(propName, "font-family", "checking property name");
|
||||
is(propValue, "helvetica,sans-serif", "checking property value");
|
||||
|
||||
// We need the context menu to open in the correct place in order for
|
||||
// popupNode to be propertly set.
|
||||
EventUtils.synthesizeMouse(prop, 1, 1, { type: "contextmenu", button: 2 },
|
||||
ruleView.contentWindow);
|
||||
|
||||
InspectorUI.ruleViewCopyRule();
|
||||
}
|
||||
|
||||
function checkCopyProperty()
|
||||
{
|
||||
info("Checking that InspectorUI.cssRuleViewBoundCopyDeclaration() returns " +
|
||||
"the correct clipboard value");
|
||||
let expectedPattern = "font-family: helvetica,sans-serif;";
|
||||
info("Expected pattern: " + expectedPattern);
|
||||
|
||||
SimpleTest.waitForClipboard(function IUI_boundCopyPropCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},
|
||||
InspectorUI.cssRuleViewBoundCopyDeclaration,
|
||||
checkCopyPropertyName, checkCopyPropertyName);
|
||||
}
|
||||
|
||||
function checkCopyPropertyName()
|
||||
{
|
||||
info("Checking that InspectorUI.cssRuleViewBoundCopyProperty() returns " +
|
||||
"the correct clipboard value");
|
||||
let expectedPattern = "font-family";
|
||||
info("Expected pattern: " + expectedPattern);
|
||||
|
||||
SimpleTest.waitForClipboard(function IUI_boundCopyPropNameCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},
|
||||
InspectorUI.cssRuleViewBoundCopyProperty,
|
||||
checkCopyPropertyValue, checkCopyPropertyValue);
|
||||
}
|
||||
|
||||
function checkCopyPropertyValue()
|
||||
{
|
||||
info("Checking that InspectorUI.cssRuleViewBoundCopyPropertyValue() " +
|
||||
" returns the correct clipboard value");
|
||||
let expectedPattern = "helvetica,sans-serif";
|
||||
info("Expected pattern: " + expectedPattern);
|
||||
|
||||
SimpleTest.waitForClipboard(function IUI_boundCopyPropValueCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},
|
||||
InspectorUI.cssRuleViewBoundCopyPropertyValue,
|
||||
checkCopySelection, checkCopySelection);
|
||||
}
|
||||
|
||||
function checkCopySelection()
|
||||
{
|
||||
let ruleView = document.querySelector("#devtools-sidebar-iframe-ruleview");
|
||||
let contentDoc = ruleView.contentDocument;
|
||||
let props = contentDoc.querySelectorAll(".ruleview-property");
|
||||
|
||||
let range = document.createRange();
|
||||
range.setStart(props[0], 0);
|
||||
range.setEnd(props[4], 8);
|
||||
ruleView.contentWindow.getSelection().addRange(range);
|
||||
|
||||
info("Checking that InspectorUI.cssRuleViewBoundCopy() returns the correct" +
|
||||
"clipboard value");
|
||||
let expectedPattern = " margin: 10em;[\\r\\n]+" +
|
||||
" font-size: 14pt;[\\r\\n]+" +
|
||||
" font-family: helvetica,sans-serif;[\\r\\n]+" +
|
||||
" color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
|
||||
"}[\\r\\n]+" +
|
||||
"html {[\\r\\n]+" +
|
||||
" color: rgb\\(0, 0, 0\\);[\\r\\n]*";
|
||||
info("Expected pattern: " + expectedPattern);
|
||||
|
||||
SimpleTest.waitForClipboard(function IUI_boundCopyCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},InspectorUI.cssRuleViewBoundCopy, finishup, finishup);
|
||||
}
|
||||
|
||||
function checkClipboardData(aExpectedPattern)
|
||||
{
|
||||
let actual = SpecialPowers.getClipboardData("text/unicode");
|
||||
let expectedRegExp = new RegExp(aExpectedPattern, "g");
|
||||
return expectedRegExp.test(actual);
|
||||
}
|
||||
|
||||
function finishup()
|
||||
{
|
||||
InspectorUI.closeInspectorUI();
|
||||
gBrowser.removeCurrentTab();
|
||||
doc = null;
|
||||
finish();
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
|
||||
true);
|
||||
doc = content.document;
|
||||
waitForFocus(createDocument, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,<p>rule view context menu test</p>";
|
||||
}
|
@ -144,6 +144,7 @@
|
||||
@BINPATH@/components/directory.xpt
|
||||
@BINPATH@/components/docshell.xpt
|
||||
@BINPATH@/components/dom.xpt
|
||||
@BINPATH@/components/dom_apps.xpt
|
||||
@BINPATH@/components/dom_base.xpt
|
||||
#ifdef MOZ_B2G_RIL
|
||||
@BINPATH@/components/dom_telephony.xpt
|
||||
@ -410,6 +411,8 @@
|
||||
@BINPATH@/components/TelemetryPing.manifest
|
||||
@BINPATH@/components/messageWakeupService.js
|
||||
@BINPATH@/components/messageWakeupService.manifest
|
||||
@BINPATH@/components/Webapps.js
|
||||
@BINPATH@/components/Webapps.manifest
|
||||
|
||||
@BINPATH@/components/ContactManager.js
|
||||
@BINPATH@/components/ContactManager.manifest
|
||||
|
@ -213,8 +213,6 @@ can reach it easily. -->
|
||||
<!ENTITY scratchpad.keycode "VK_F4">
|
||||
<!ENTITY scratchpad.keytext "F4">
|
||||
|
||||
<!ENTITY inspectButton.label "Inspect">
|
||||
<!ENTITY inspectButton.accesskey "I">
|
||||
<!ENTITY inspectCloseButton.tooltiptext "Close Inspector">
|
||||
|
||||
<!ENTITY inspectorHTMLCopyInner.label "Copy Inner HTML">
|
||||
|
@ -334,6 +334,12 @@ telemetryYesButtonAccessKey = Y
|
||||
telemetryNoButtonLabel = No
|
||||
telemetryNoButtonAccessKey = N
|
||||
|
||||
# Webapps notification popup
|
||||
webapps.install = Install
|
||||
webapps.install.accesskey = I
|
||||
#LOCALIZATION NOTE (webapps.requestInstall) %1$S is the web app name, %2$S is the site from which the web app is installed
|
||||
webapps.requestInstall = Do you want to install "%1$S" from this site (%2$S)?
|
||||
|
||||
# Keyword.URL reset prompt
|
||||
# LOCALIZATION NOTE (keywordPrompt.message):
|
||||
# - %1$S is brandShortName
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user