author | David Anderson <danderson@mozilla.com> |
Tue, 13 Mar 2012 16:10:05 -0700 | |
changeset 105978 | 7eb3fbbd50d7d1ce99527ee27f8b619c87e96fc7 |
parent 105976 | e159956eb94c48c1f6f0bffea296f2305b45ba38 (current diff) |
parent 88953 | c71845b3b2a6a1d1708e4a1ed23c0244cb0d4a8c (diff) |
child 105979 | ae003e1f04ff21c4e9d2229e795321766fadac89 |
push id | 23447 |
push user | danderson@mozilla.com |
push date | Tue, 11 Sep 2012 17:34:27 +0000 |
treeherder | mozilla-central@fdfaef738a00 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 14.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/.hgtags +++ b/.hgtags @@ -73,8 +73,9 @@ 462c726144bc1fb45b61e774f64ac5d61b4e047c 54bfd8bf682e295ffd7f22fa921ca343957b6c1c AURORA_BASE_20111108 a8506ab2c65480cf2f85f54e203ea746522c62bb AURORA_BASE_20111220 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R16 bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131 bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131 0000000000000000000000000000000000000000 AURORA_BASE_20120131 0000000000000000000000000000000000000000 AURORA_BASE_20120131 bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131 +b6627f28b7ec17e1b46a594df0f780d3a40847e4 FIREFOX_AURORA_13_BASE
--- a/accessible/public/nsIAccessible.idl +++ b/accessible/public/nsIAccessible.idl @@ -54,17 +54,17 @@ interface nsIAccessibleRelation; * accessibility APIs like MSAA and ATK. Contains the sum of what's needed * to support IAccessible as well as ATK's generic accessibility objects. * Can also be used by in-process accessibility clients to get information * about objects in the accessible tree. The accessible tree is a subset of * nodes in the DOM tree -- such as documents, focusable elements and text. * 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 { /** * Parent node in accessible tree. */ readonly attribute nsIAccessible parent; /** @@ -106,36 +106,16 @@ interface nsIAccessible : nsISupports /** * The innerHTML for the HTML element associated with this accessible if applicable. * This is a text string of all the markup inside the DOM * node, not including the start and end tag for the node. */ 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. */ readonly attribute nsIDOMNode DOMNode; /** * The document accessible that this access node resides in. */ readonly attribute nsIAccessibleDocument document;
--- a/accessible/src/base/AccGroupInfo.h +++ b/accessible/src/base/AccGroupInfo.h @@ -61,16 +61,18 @@ public: { mozilla::a11y::role role = aAccessible->Role(); if (role != mozilla::a11y::roles::ROW && role != mozilla::a11y::roles::GRID_CELL && role != mozilla::a11y::roles::OUTLINEITEM && 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 && role != mozilla::a11y::roles::PAGETAB) return nsnull; AccGroupInfo* info = new AccGroupInfo(aAccessible, BaseRole(role)); return info; @@ -78,16 +80,17 @@ public: private: AccGroupInfo(const AccGroupInfo&); AccGroupInfo& operator =(const AccGroupInfo&); 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; } /** * Return true if the given parent role is conceptual parent of the given * role.
--- a/accessible/src/base/TextAttrs.cpp +++ b/accessible/src/base/TextAttrs.cpp @@ -47,42 +47,16 @@ #include "gfxUserFontSet.h" #include "nsFontMetrics.h" #include "nsLayoutUtils.h" 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 //////////////////////////////////////////////////////////////////////////////// void TextAttrsMgr::GetAttributes(nsIPersistentProperties* aAttributes, PRInt32* aStartHTOffset, PRInt32* aEndHTOffset) { @@ -134,88 +108,87 @@ TextAttrsMgr::GetAttributes(nsIPersisten nsIContent *offsetNode = nsnull, *offsetElm = nsnull; nsIFrame *frame = nsnull; if (mOffsetAcc) { offsetNode = mOffsetAcc->GetContent(); offsetElm = nsCoreUtils::GetDOMElementFor(offsetNode); 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); // Stop on embedded accessible since embedded accessibles are combined into // own range. if (nsAccUtils::IsEmbeddedObject(currAcc)) break; nsIContent* currElm = nsCoreUtils::GetDOMElementFor(currAcc->GetContent()); if (!currElm) 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; } } if (offsetFound) break; @@ -230,18 +203,18 @@ TextAttrsMgr::GetRange(const nsTArray<Te if (nsAccUtils::IsEmbeddedObject(currAcc)) break; nsIContent* currElm = nsCoreUtils::GetDOMElementFor(currAcc->GetContent()); if (!currElm) 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. if (!textAttr->Equal(currElm)) { offsetFound = true; break; } } @@ -289,70 +262,16 @@ TextAttrsMgr::LangTextAttr:: GetLang(nsIContent* aElm, nsAString& aLang) { nsCoreUtils::GetLanguageFor(aElm, mRootContent, aLang); return !aLang.IsEmpty(); } //////////////////////////////////////////////////////////////////////////////// -// 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 //////////////////////////////////////////////////////////////////////////////// TextAttrsMgr::BGColorTextAttr:: BGColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) : TTextAttr<nscolor>(!aFrame), mRootFrame(aRootFrame) { mIsRootDefined = GetColor(mRootFrame, &mRootNativeValue); @@ -737,8 +656,109 @@ TextAttrsMgr::TextDecorTextAttr:: formattedStyle); nsAutoString formattedColor; StyleInfo::FormatColor(aValue.Color(), formattedColor); nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textLineThroughColor, 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; +}
--- a/accessible/src/base/TextAttrs.h +++ b/accessible/src/base/TextAttrs.h @@ -102,21 +102,22 @@ public: protected: /** * Calculates range (start and end offsets) of text where the text attributes * are stretched. New offsets may be smaller if one of text attributes changes * 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: nsHyperTextAccessible* mHyperTextAcc; bool mIncludeDefAttrs; nsAccessible* mOffsetAcc; @@ -152,17 +153,17 @@ protected: * Base class to work with text attributes. See derived classes below. */ template<class T> class TTextAttr : public TextAttr { public: TTextAttr(bool aGetRootValue) : mGetRootValue(aGetRootValue) {} - // ITextAttr + // TextAttr virtual void Expose(nsIPersistentProperties* aAttributes, bool aIncludeDefAttrValue) { if (mGetRootValue) { if (mIsRootDefined) ExposeValue(aAttributes, mRootNativeValue); return; } @@ -238,37 +239,16 @@ protected: private: bool GetLang(nsIContent* aElm, nsAString& aLang); nsCOMPtr<nsIContent> mRootContent; }; /** - * 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. */ class BGColorTextAttr : public TTextAttr<nscolor> { public: BGColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame); virtual ~BGColorTextAttr() { } @@ -430,14 +410,42 @@ protected: protected: // TextAttr virtual bool GetValueFor(nsIContent* aElm, TextDecorValue* aValue); virtual void ExposeValue(nsIPersistentProperties* aAttributes, 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 } // namespace mozilla #endif
--- a/accessible/src/base/nsAccUtils.cpp +++ b/accessible/src/base/nsAccUtils.cpp @@ -170,80 +170,16 @@ nsAccUtils::GetPositionAndSizeForXULSele if (index < static_cast<PRUint32>(indexOf)) (*aPosInSet)--; } } (*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) { nsCOMPtr<nsIDOMXULContainerItemElement> item(do_QueryInterface(aContent)); if (!item) return 0; nsCOMPtr<nsIDOMXULContainerElement> container;
--- a/accessible/src/base/nsAccUtils.h +++ b/accessible/src/base/nsAccUtils.h @@ -111,24 +111,16 @@ public: * Compute position in group (posinset) and group size (setsize) for * nsIDOMXULSelectControlItemElement node. */ static void GetPositionAndSizeForXULSelectControlItem(nsIContent *aContent, 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. */ static PRInt32 GetLevelForXULContainerItem(nsIContent *aContent); /** * Set container-foo live region attributes for the given node. * * @param aAttributes where to store the attributes
--- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -227,52 +227,16 @@ nsAccessible::~nsAccessible() void 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) { NS_ENSURE_ARG_POINTER(aDocument); NS_IF_ADDREF(*aDocument = Document()); return NS_OK; }
--- a/accessible/src/base/nsApplicationAccessible.cpp +++ b/accessible/src/base/nsApplicationAccessible.cpp @@ -463,32 +463,14 @@ nsApplicationAccessible::ScrollTo(PRUint NS_IMETHODIMP nsApplicationAccessible::ScrollToPoint(PRUint32 aCoordinateType, PRInt32 aX, PRInt32 aY) { 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) { aLanguage.Truncate(); return NS_OK; }
--- a/accessible/src/base/nsApplicationAccessible.h +++ b/accessible/src/base/nsApplicationAccessible.h @@ -71,22 +71,16 @@ public: // nsIAccessible NS_SCRIPTABLE NS_IMETHOD GetDOMNode(nsIDOMNode** aDOMNode); NS_SCRIPTABLE NS_IMETHOD GetDocument(nsIAccessibleDocument** aDocument); NS_SCRIPTABLE NS_IMETHOD GetRootDocument(nsIAccessibleDocument** aRootDocument); NS_SCRIPTABLE NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML); NS_SCRIPTABLE NS_IMETHOD ScrollTo(PRUint32 aScrollType); NS_SCRIPTABLE NS_IMETHOD ScrollToPoint(PRUint32 aCoordinateType, PRInt32 aX, PRInt32 aY); - NS_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); NS_IMETHOD GetPreviousSibling(nsIAccessible **aPreviousSibling); NS_IMETHOD GetName(nsAString &aName); NS_IMETHOD GetValue(nsAString &aValue); NS_IMETHOD GetAttributes(nsIPersistentProperties **aAttributes); NS_IMETHOD GroupPosition(PRInt32 *aGroupLevel, PRInt32 *aSimilarItemsInGroup,
--- a/accessible/src/base/nsCoreUtils.cpp +++ b/accessible/src/base/nsCoreUtils.cpp @@ -565,36 +565,16 @@ nsCoreUtils::GetLanguageFor(nsIContent * aLanguage.Truncate(); nsIContent *walkUp = aContent; while (walkUp && walkUp != aRootContent && !walkUp->GetAttr(kNameSpaceID_None, nsGkAtoms::lang, aLanguage)) 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) { nsCOMPtr<nsIDOMElement> tcElm; aTreeBoxObj->GetTreeBody(getter_AddRefs(tcElm)); nsCOMPtr<nsIDOMXULElement> tcXULElm(do_QueryInterface(tcElm)); if (!tcXULElm) return nsnull;
--- a/accessible/src/base/nsCoreUtils.h +++ b/accessible/src/base/nsCoreUtils.h @@ -284,23 +284,16 @@ public: * @param aContent [in] the given node * @param aRootContent [in] container of the given node * @param aLanguage [out] language */ 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. */ static already_AddRefed<nsIBoxObject> GetTreeBodyBoxObject(nsITreeBoxObject *aTreeBoxObj); /** * Return tree box object from any levels DOMNode under the XUL tree. */
--- a/accessible/src/html/nsHTMLTableAccessible.cpp +++ b/accessible/src/html/nsHTMLTableAccessible.cpp @@ -1436,19 +1436,36 @@ nsHTMLTableAccessible::IsProbablyForLayo } if (childElm->Tag() == nsGkAtoms::tbody) { for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm; rowElm = rowElm->GetNextSibling()) { if (rowElm->IsHTML() && rowElm->Tag() == nsGkAtoms::tr) { for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm; cellElm = cellElm->GetNextSibling()) { - if (cellElm->IsHTML() && cellElm->Tag() == nsGkAtoms::th) { - RETURN_LAYOUT_ANSWER(false, - "Has th -- legitimate table structures"); + 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"); + } } } } } } } if (HasDescendant(NS_LITERAL_STRING("table"))) {
--- a/accessible/src/msaa/CAccessibleComponent.cpp +++ b/accessible/src/msaa/CAccessibleComponent.cpp @@ -38,16 +38,18 @@ * * ***** END LICENSE BLOCK ***** */ #include "CAccessibleComponent.h" #include "AccessibleComponent_i.c" #include "nsAccessible.h" +#include "nsCoreUtils.h" +#include "nsWinUtils.h" #include "States.h" #include "nsString.h" #include "nsIDOMCSSPrimitiveValue.h" #include "nsIDOMNSRGBAColor.h" using namespace mozilla::a11y; @@ -151,27 +153,33 @@ CAccessibleComponent::get_background(IA2 HRESULT CAccessibleComponent::GetARGBValueFromCSSProperty(const nsAString& aPropName, IA2Color *aColorValue) { __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); nsCOMPtr<nsIDOMNSRGBAColor> rgbaColor(do_QueryInterface(rgbColor)); if (!rgbaColor) return GetHRESULT(rv); // get alpha
--- a/accessible/src/msaa/nsAccessNodeWrap.cpp +++ b/accessible/src/msaa/nsAccessNodeWrap.cpp @@ -334,17 +334,17 @@ STDMETHODIMP nsAccessNodeWrap::get_compu { __try{ *aNumStyleProperties = 0; if (IsDefunct() || IsDocumentNode()) return E_FAIL; nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl = - nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), mContent); + nsWinUtils::GetComputedStyleDeclaration(mContent); NS_ENSURE_TRUE(cssDecl, E_FAIL); PRUint32 length; cssDecl->GetLength(&length); PRUint32 index, realIndex; for (index = realIndex = 0; index < length && realIndex < aMaxStyleProperties; index ++) { nsAutoString property, value; @@ -369,17 +369,17 @@ STDMETHODIMP nsAccessNodeWrap::get_compu /* [length_is][size_is][in] */ BSTR __RPC_FAR *aStyleProperties, /* [length_is][size_is][out] */ BSTR __RPC_FAR *aStyleValues) { __try { if (IsDefunct() || IsDocumentNode()) return E_FAIL; nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl = - nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), mContent); + nsWinUtils::GetComputedStyleDeclaration(mContent); NS_ENSURE_TRUE(cssDecl, E_FAIL); PRUint32 index; for (index = 0; index < aNumStyleProperties; index ++) { nsAutoString value; if (aStyleProperties[index]) cssDecl->GetPropertyValue(nsDependentString(static_cast<PRUnichar*>(aStyleProperties[index])), value); // Get property value aStyleValues[index] = ::SysAllocString(value.get());
--- a/accessible/src/msaa/nsWinUtils.cpp +++ b/accessible/src/msaa/nsWinUtils.cpp @@ -50,16 +50,35 @@ using namespace mozilla; using namespace mozilla::a11y; // Window property used by ipc related code in identifying accessible // 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) { *aIA2Array = NULL; *aIA2ArrayLen = 0; if (!aGeckoArray)
--- a/accessible/src/msaa/nsWinUtils.h +++ b/accessible/src/msaa/nsWinUtils.h @@ -48,16 +48,25 @@ const LPCWSTR kClassNameRoot = L"MozillaUIWindowClass"; 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. */ static HRESULT ConvertToIA2Array(nsIArray *aCollection, IUnknown ***aAccessibles, long *aCount); /** * Start window emulation if presence of specific AT is detected.
--- a/accessible/src/xul/nsXULMenuAccessible.cpp +++ b/accessible/src/xul/nsXULMenuAccessible.cpp @@ -306,24 +306,16 @@ nsXULMenuitemAccessible::NativeRole() } PRInt32 nsXULMenuitemAccessible::GetLevelInternal() { return nsAccUtils::GetLevelForXULContainerItem(mContent); } -void -nsXULMenuitemAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet, - PRInt32 *aSetSize) -{ - nsAccUtils::GetPositionAndSizeForXULContainerItem(mContent, aPosInSet, - aSetSize); -} - bool nsXULMenuitemAccessible::CanHaveAnonChildren() { // That indicates we don't walk anonymous children for menuitems return false; } NS_IMETHODIMP nsXULMenuitemAccessible::DoAction(PRUint8 index)
--- a/accessible/src/xul/nsXULMenuAccessible.h +++ b/accessible/src/xul/nsXULMenuAccessible.h @@ -58,18 +58,16 @@ public: NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName); // nsAccessible virtual void Description(nsString& aDescription); virtual nsresult GetNameInternal(nsAString& aName); virtual mozilla::a11y::role NativeRole(); virtual PRUint64 NativeState(); virtual PRInt32 GetLevelInternal(); - virtual void GetPositionAndSizeInternal(PRInt32 *aPosInSet, - PRInt32 *aSetSize); virtual bool CanHaveAnonChildren(); // ActionAccessible virtual PRUint8 ActionCount(); virtual KeyBinding AccessKey() const; virtual KeyBinding KeyboardShortcut() const;
--- a/accessible/tests/mochitest/Makefile.in +++ b/accessible/tests/mochitest/Makefile.in @@ -96,17 +96,16 @@ include $(topsrcdir)/config/rules.mk test_aria_role_equation.html \ test_aria_roles.html \ test_aria_roles.xul \ test_aria_token_attrs.html \ test_bug420863.html \ test_descr.html \ test_nsIAccessibleDocument.html \ test_nsIAccessibleImage.html \ - test_nsIAccessNode_utils.html \ test_nsOuterDocAccessible.html \ test_role_nsHyperTextAcc.html \ test_textboxes.html \ test_textboxes.xul \ testTextboxes.js \ text.js \ treeview.css \ treeview.js \
--- a/accessible/tests/mochitest/attributes/test_obj_group.html +++ b/accessible/tests/mochitest/attributes/test_obj_group.html @@ -134,16 +134,23 @@ // HTML headings testGroupAttrs("h1", 0, 0, 1); testGroupAttrs("h2", 0, 0, 2); testGroupAttrs("h3", 0, 0, 3); testGroupAttrs("h4", 0, 0, 4); 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(); } SimpleTest.waitForExplicitFinish(); addA11yLoadEvent(doTest); </script> </head> <body> @@ -300,10 +307,17 @@ <h1 id="h1">heading1</h1> <h2 id="h2">heading2</h2> <h3 id="h3">heading3</h3> <h4 id="h4">heading4</h4> <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>
--- a/accessible/tests/mochitest/attributes/test_obj_group.xul +++ b/accessible/tests/mochitest/attributes/test_obj_group.xul @@ -86,16 +86,23 @@ testGroupAttrs("tab2", 2, 2); ////////////////////////////////////////////////////////////////////////// // xul:radio 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); testGroupAttrs("aria-menuitemcheckbox", 2, 3); testGroupAttrs("aria-menuitemradio", 3, 3); testGroupAttrs("aria-menuitem2", 1, 1); ////////////////////////////////////////////////////////////////////////// // xul:menu (bug 443881) @@ -170,16 +177,25 @@ </tabpanels> </tabbox> <radiogroup> <radio id="radio1" label="radio1"/> <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"/> <description role="menuitemcheckbox" id="aria-menuitemcheckbox" value="conventional checkbox menuitem"/> <description role="menuitem" hidden="true"/> <description role="menuitemradio" id="aria-menuitemradio" value="conventional radio menuitem"/>
--- a/accessible/tests/mochitest/attributes/test_text.html +++ b/accessible/tests/mochitest/attributes/test_text.html @@ -176,16 +176,40 @@ attrs = {}; testTextAttrs(ID, 50, attrs, defAttrs, 50, 55); tempElem = tempElem.nextSibling.nextSibling; gComputedStyle = document.defaultView.getComputedStyle(tempElem, ""); 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"; defAttrs = buildDefaultTextAttrs(ID, "12pt"); defAttrs["language"] = "en"; testDefaultTextAttrs(ID, defAttrs); attrs = {"language": "ru"}; @@ -501,16 +525,21 @@ <body style="font-size: 12pt"> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=345759" 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"> Mozilla Bug 473576 </a> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=523304" title="expose text-underline-color and text-line-through-color text attributes"> Mozilla Bug 523304 @@ -544,17 +573,21 @@ <span style="color: green">Green</span> <img src="../moz.png" alt="image"/> <span style="color: red">Red</span>Normal </p> <p id="area6"> 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"> <span lang="ru">Привет</span> <span style="background-color: blue">Blue BG color</span> <span lang="de">Ich bin/Du bist</span> <span lang="en"> Normal
deleted file mode 100644 --- a/accessible/tests/mochitest/test_nsIAccessNode_utils.html +++ /dev/null @@ -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>
--- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -41,17 +41,17 @@ pref("toolkit.defaultChromeURI", "chrome pref("browser.chromeURL", "chrome://browser/content/"); #ifdef MOZ_OFFICIAL_BRANDING pref("browser.homescreenURL", "file:///system/home/homescreen.html"); #else pref("browser.homescreenURL", "file:///data/local/homescreen.html,file:///system/home/homescreen.html"); #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); /* disable text selection */ pref("browser.ignoreNativeFrameTextSelection", true); /* cache prefs */ @@ -439,8 +439,17 @@ pref("b2g.keys.menu.enabled", true); pref("b2g.keys.search.enabled", false); // Screen timeout in minutes 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", "");
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -277,17 +277,17 @@ pref("browser.urlbar.clickSelectsAll", f #else pref("browser.urlbar.clickSelectsAll", true); #endif #ifdef UNIX_BUT_NOT_MAC 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 // 2: Match only on word boundaries (e.g., after / or .) // 3: Match at the beginning of the url or title pref("browser.urlbar.matchBehavior", 1); pref("browser.urlbar.filter.javascript", true); @@ -1037,19 +1037,16 @@ pref("devtools.inspector.sidebarOpen", f pref("devtools.inspector.activeSidebar", "ruleview"); // Enable the Debugger 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); // Enable the Tilt inspector pref("devtools.tilt.enabled", true); pref("devtools.tilt.intro_transition", true); pref("devtools.tilt.outro_transition", true);
--- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -435,17 +435,18 @@ window[chromehidden~="toolbar"] toolbar: } /* We use the iconBox as the notification anchor when a popup notification is created with a null anchorID, so in that case use a default anchor icon. */ #notification-popup-box[anchorid="notification-popup-box"] > #default-notification-icon, #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; } #invalid-form-popup > description { max-width: 280px; } #geolocation-notification {
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -4747,20 +4747,25 @@ var XULBrowserWindow = { } catch (e) {} URLBarSetURI(uri); // Update starring UI PlacesStarButton.updateState(); } // Show or hide browser chrome based on the whitelist - if (this.hideChromeForLocation(location)) + if (this.hideChromeForLocation(location)) { document.documentElement.setAttribute("disablechrome", "true"); - else - document.documentElement.removeAttribute("disablechrome"); + } 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; if (aLocationURI && (aLocationURI.schemeIs("about") || aLocationURI.schemeIs("chrome"))) { let docElt = content.document.documentElement; disableFind = docElt && docElt.getAttribute("disablefastfind") == "true"; } @@ -9095,17 +9100,16 @@ XPCOMUtils.defineLazyGetter(window, "gSh let sysInfo = Components.classes["@mozilla.org/system-info;1"] .getService(Components.interfaces.nsIPropertyBag2); return parseFloat(sysInfo.getProperty("version")) < 6; #else return false; #endif }); - var MousePosTracker = { _listeners: [], _x: 0, _y: 0, get _windowUtils() { delete this._windowUtils; return this._windowUtils = window.getInterface(Ci.nsIDOMWindowUtils); },
--- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -522,16 +522,17 @@ onfocus="document.getElementById('identity-box').style.MozUserFocus= 'normal'" onblur="setTimeout(function() document.getElementById('identity-box').style.MozUserFocus = '', 0);"> <box id="notification-popup-box" hidden="true" align="center"> <image id="default-notification-icon" class="notification-anchor-icon" role="button"/> <image id="geo-notification-icon" class="notification-anchor-icon" role="button"/> <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. We only add the identity-box button to the tab order when the location bar has focus, otherwise pressing F6 focuses it instead of the location bar --> <box id="identity-box" role="button" onclick="gIdentityHandler.handleIdentityButtonEvent(event);" onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);" @@ -998,18 +999,16 @@ hidden="true"> #ifdef XP_MACOSX <toolbarbutton id="highlighter-closebutton" oncommand="InspectorUI.closeInspectorUI(false);" tooltiptext="&inspectCloseButton.tooltiptext;"/> #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" label="&htmlPanel.label;" accesskey="&htmlPanel.accesskey;" tooltiptext="&htmlPanel.tooltiptext;" command="Inspector:HTMLPanel"/> <arrowscrollbox id="inspector-breadcrumbs"
--- a/browser/base/content/highlighter.css +++ b/browser/base/content/highlighter.css @@ -82,8 +82,12 @@ html|*#highlighter-nodeinfobar-tagname { #highlighter-nodeinfobar-container[disabled] { visibility: hidden; } html|*#highlighter-nodeinfobar-tagname { text-transform: lowercase; } + +.devtools-toolbarbutton:not([label]) > .toolbarbutton-text { + display: none; +}
--- a/browser/base/content/newtab/cells.js +++ b/browser/base/content/newtab/cells.js @@ -10,26 +10,24 @@ * aren't handled here. */ function Cell(aGrid, aNode) { this._grid = aGrid; this._node = 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, /** * The cell's DOM node. */ get node() this._node, @@ -92,46 +90,32 @@ Cell.prototype = { * Checks whether the cell contains a site (is empty). * @return Whether the cell is empty. */ isEmpty: function Cell_isEmpty() { return !this.site; }, /** - * Event handler for the 'dragenter' event. - * @param aEvent The dragenter event. - */ - onDragEnter: function Cell_onDragEnter(aEvent) { - if (gDrag.isValid(aEvent)) { - aEvent.preventDefault(); - gDrop.enter(this, aEvent); - } - }, - - /** - * Event handler for the 'dragover' event. - * @param aEvent The dragover event. + * Handles all cell events. */ - onDragOver: function Cell_onDragOver(aEvent) { - if (gDrag.isValid(aEvent)) - aEvent.preventDefault(); - }, + handleEvent: function Cell_handleEvent(aEvent) { + if (aEvent.type != "dragexit" && !gDrag.isValid(aEvent)) + return; - /** - * Event handler for the 'dragexit' event. - * @param aEvent The dragexit event. - */ - onDragExit: function Cell_onDragExit(aEvent) { - gDrop.exit(this, aEvent); - }, - - /** - * Event handler for the 'drop' event. - * @param aEvent The drop event. - */ - onDrop: function Cell_onDrop(aEvent) { - if (gDrag.isValid(aEvent)) { - aEvent.preventDefault(); - gDrop.drop(this, aEvent); + switch (aEvent.type) { + case "dragenter": + aEvent.preventDefault(); + gDrop.enter(this, aEvent); + break; + case "dragover": + aEvent.preventDefault(); + break; + case "dragexit": + gDrop.exit(this, aEvent); + break; + case "drop": + aEvent.preventDefault(); + gDrop.drop(this, aEvent); + break; } } };
--- a/browser/base/content/newtab/drag.js +++ b/browser/base/content/newtab/drag.js @@ -31,21 +31,21 @@ let gDrag = { /** * Start a new drag operation. * @param aSite The site that's being dragged. * @param aEvent The 'dragstart' event. */ 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); // Store the cursor offset. let node = aSite.node; let rect = node.getBoundingClientRect(); this._offsetX = aEvent.clientX - rect.left; this._offsetY = aEvent.clientY - rect.top; @@ -83,35 +83,38 @@ let gDrag = { }, /** * Ends the current drag operation. * @param aSite The site that's being dragged. * @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; }, /** * Checks whether we're responsible for a given drag event. * @param aEvent The drag event to check. * @return Whether we should handle this drag and drop operation. */ 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); }, /** * Initializes the drag data for the current drag operation. * @param aSite The site that's being dragged. * @param aEvent The 'dragstart' event. */ _setDragData: function Drag_setDragData(aSite, aEvent) { @@ -123,18 +126,18 @@ let gDrag = { dt.setData("text/plain", url); dt.setData("text/uri-list", url); dt.setData("text/x-moz-url", url + "\n" + title); dt.setData("text/html", "<a href=\"" + url + "\">" + url + "</a>"); // 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); } };
--- a/browser/base/content/newtab/dropTargetShim.js +++ b/browser/base/content/newtab/dropTargetShim.js @@ -21,52 +21,60 @@ let gDropTargetShim = { _lastDropTarget: null, /** * Initializes the drop target shim. */ init: function DropTargetShim_init() { let node = gGrid.node; - this._dragover = this._dragover.bind(this); + // Add drag event handlers. + node.addEventListener("dragstart", this, true); + node.addEventListener("dragend", this, true); + }, - // 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); + /** + * 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; + } }, /** * Handles the 'dragstart' event. * @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); } }, /** * Handles the 'drag' event and determines the current drop target. * @param aEvent The 'drag' event. */ _drag: function DropTargetShim_drag(aEvent) { // 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); if (target) // We're now hovering a (new) drop target. this._dispatchEvent(aEvent, "dragenter", target); @@ -79,17 +87,17 @@ let gDropTargetShim = { }, /** * Handles the 'dragover' event as long as bug 505521 isn't fixed to get * current mouse cursor coordinates while dragging. * @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); }, /** * Handles the 'dragend' event. * @param aEvent The 'dragend' event. @@ -112,17 +120,17 @@ let gDropTargetShim = { // Clean up. this._lastDropTarget = null; this._cellPositions = null; } gGrid.unlock(); // XXX bug 505521 - Remove the document's dragover listener. - document.documentElement.removeEventListener("dragover", this._dragover, false); + document.documentElement.removeEventListener("dragover", this, false); }, /** * Determines the current drop target by matching the dragged site's position * against all cells in the grid. * @return The currently hovered drop target or null. */ _findDropTarget: function DropTargetShim_findDropTarget() {
--- a/browser/base/content/newtab/grid.js +++ b/browser/base/content/newtab/grid.js @@ -19,17 +19,17 @@ let gGrid = { */ _siteFragment: null, /** * All cells contained in the grid. */ 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])); // Replace the getter with our cached value. Object.defineProperty(this, "cells", {value: cells, enumerable: true}); return cells; }, @@ -38,18 +38,18 @@ let gGrid = { * All sites contained in the grid's cells. Sites may be empty. */ get sites() [cell.site for each (cell in this.cells)], /** * 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(); }, /** * Creates a new site in the grid. * @param aLink The new site's link. * @param aCell The cell that will contain the new site. @@ -91,31 +91,30 @@ let gGrid = { unlock: function Grid_unlock() { this.node.removeAttribute("locked"); }, /** * 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); }, /** * Draws the grid, creates all sites and puts them into their cells. */
--- a/browser/base/content/newtab/newTab.css +++ b/browser/base/content/newtab/newTab.css @@ -1,173 +1,186 @@ :root { - -moz-appearance: none; -} - -#scrollbox:not([page-disabled]) { - overflow: auto; + -moz-user-focus: normal; } -#body { - position: relative; - margin: 0; - min-width: 675px; - -moz-user-select: none; -} - -.button { +input[type=button] { cursor: pointer; } -/* TOOLBAR */ -#toolbar { - position: absolute; +/* SCROLLBOX */ +#newtab-scrollbox { + display: -moz-box; + position: relative; + -moz-box-flex: 1; } -#toolbar[page-disabled] { - position: fixed; +#newtab-scrollbox:not([page-disabled]) { + overflow: auto; } -#toolbar:-moz-locale-dir(rtl) { - left: 8px; +/* TOGGLE */ +#newtab-toggle { + position: absolute; + top: 12px; + right: 12px; +} + +#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; +} + +#newtab-margin-top { + min-height: 50px; + max-height: 80px; + -moz-box-flex: 1; +} + +#newtab-margin-bottom { + min-height: 40px; + max-height: 100px; + -moz-box-flex: 1; } -#toolbar-button-show, -#toolbar-button-reset { +#newtab-horizontal-margin { + display: -moz-box; + -moz-box-flex: 5; +} + +.newtab-side-margin { + min-width: 40px; + max-width: 300px; + -moz-box-flex: 1; +} + +/* GRID */ +#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; +} + +#newtab-grid[page-disabled] { opacity: 0; +} + +#newtab-grid[locked], +#newtab-grid[page-disabled] { pointer-events: none; } -#toolbar-button-reset[modified], -#toolbar-button-show[page-disabled] { - opacity: 1; - pointer-events: auto; -} - -#toolbar-button-hide[page-disabled], -#toolbar-button-reset[page-disabled] { - opacity: 0; - pointer-events: none; -} - -/* GRID */ -#grid { - width: 637px; - height: 411px; - overflow: hidden; - list-style-type: none; - -moz-transition: opacity 200ms ease-out; -} - -#grid[page-disabled] { - opacity: 0; -} - -#grid[page-disabled], -#grid[locked] { - 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; +/* LINK + THUMBNAILS */ +.newtab-link, +.newtab-thumbnail { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; } -.site:hover > .site-img, -.site[ontop] > .site-img, -.site:-moz-focusring > .site-img { +.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; } -.site-img[loading] { - display: none; -} - -/* SITE TITLE */ -.site-title { +/* TITLES */ +.newtab-title { position: absolute; left: 0; + right: 0; bottom: 0; + white-space: nowrap; overflow: hidden; + text-overflow: ellipsis; } -/* SITE STRIP */ -.site-strip { +/* CONTROLS */ +.newtab-control { position: absolute; - left: 0; - top: 0; - width: 195px; - height: 17px; - overflow: hidden; + top: 4px; opacity: 0; - -moz-transition: opacity 200ms ease-out; + -moz-transition: opacity 100ms ease-out; } -.site:hover:not([frozen]) > .site-strip { +.newtab-control:-moz-focusring, +.newtab-site:hover > .newtab-control { opacity: 1; } -.strip-button-pin, -.strip-button-block:-moz-locale-dir(rtl) { - float: left; +.newtab-control[dragged] { + opacity: 0 !important; } -.strip-button-block, -.strip-button-pin:-moz-locale-dir(rtl) { - float: right; +.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 */ /* * This is just a temporary drag element used for dataTransfer.setDragImage() * 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; opacity: 0.01; }
--- a/browser/base/content/newtab/newTab.js +++ b/browser/base/content/newtab/newTab.js @@ -25,26 +25,23 @@ let { XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() { return Services.strings. createBundle("chrome://browser/locale/newTab.properties"); }); 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 #include drag.js #include drop.js #include dropTargetShim.js #include dropPreview.js #include updater.js // Everything is loaded. Initialize the New Tab Page. -gPage.init("#toolbar", "#grid"); +gPage.init();
--- a/browser/base/content/newtab/newTab.xul +++ b/browser/base/content/newtab/newTab.xul @@ -1,40 +1,56 @@ <?xml version="1.0" encoding="UTF-8"?> # 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/. -<?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"?> <!DOCTYPE window [ <!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd"> %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> + + <div class="newtab-side-margin"/> </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 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:script type="text/javascript;version=1.8" + src="chrome://browser/content/newtab/newTab.js"/> </xul:window>
--- a/browser/base/content/newtab/page.js +++ b/browser/base/content/newtab/page.js @@ -6,35 +6,34 @@ /** * This singleton represents the whole 'New Tab Page' and takes care of * initializing all its components. */ 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); }, /** * Listens for notifications specific to this page. */ observe: function Page_observe() { let enabled = gAllPages.enabled; this._updateAttributes(enabled); @@ -43,132 +42,82 @@ let gPage = { if (enabled) this._init(); }, /** * 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. */ _init: function Page_init() { if (this._initialized) return; 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++) { let node = nodes[i]; if (aValue) node.removeAttribute("page-disabled"); else node.setAttribute("page-disabled", "true"); } - this._updateTabIndices(aValue, this._isModified()); - }, - - /** - * Checks whether the page is modified. - * @return Whether the page is modified or not. - */ - _isModified: function Page_isModified() { - // The page is considered modified only if sites have been removed. - return !gBlockedLinks.isEmpty(); + // Update the toggle button's title. + let toggle = document.getElementById("newtab-toggle"); + toggle.setAttribute("title", newTabString(aValue ? "hide" : "show")); }, /** - * Updates the tab indices of focusable elements. - * @param aEnabled Whether the page is currently enabled. - * @param aModified Whether the page is currently modified. + * Handles all page events. */ - _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) { - 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) { - if (gDrag.isValid(aEvent) && gDrag.draggedSite) { - aEvent.preventDefault(); - aEvent.stopPropagation(); + 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(); + break; + case "drop": + if (gDrag.isValid(aEvent) && gDrag.draggedSite) { + aEvent.preventDefault(); + aEvent.stopPropagation(); + } + break; } } };
--- a/browser/base/content/newtab/sites.js +++ b/browser/base/content/newtab/sites.js @@ -77,22 +77,26 @@ Site.prototype = { */ isPinned: function Site_isPinned() { return gPinnedLinks.isPinned(this._link); }, /** * 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) { - gBlockedLinks.block(this._link); - gUpdater.updateGrid(aCallback); - gPage.updateModifiedFlag(); + if (gBlockedLinks.isBlocked(this._link)) { + if (aCallback) + aCallback(); + } else { + gBlockedLinks.block(this._link); + gUpdater.updateGrid(aCallback); + } }, /** * Gets the DOM node specified by the given query selector. * @param aSelector The query selector. * @return The DOM node we found. */ _querySelector: function Site_querySelector(aSelector) { @@ -100,110 +104,76 @@ Site.prototype = { }, /** * Updates attributes for all nodes which status depends on this site being * pinned or unpinned. * @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")); } }, /** * Renders the site's data (fills the HTML fragment). */ _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 + ")"; }, /** * Adds event handlers for the site and its buttons. */ _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); - - let self = this; - - function pin(aEvent) { - if (aEvent) - aEvent.preventDefault(); + this._node.addEventListener("dragstart", this, false); + this._node.addEventListener("dragend", this, false); - if (self.isPinned()) - self.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); + let controls = this.node.querySelectorAll(".newtab-control"); + for (let i = 0; i < controls.length; i++) + controls[i].addEventListener("click", this, false); }, /** - * Event handler for the 'dragstart' event. - * @param aEvent The drag event. + * Handles all site events. */ - _onDragStart: function Site_onDragStart(aEvent) { - gDrag.start(this, aEvent); - }, - - /** - * Event handler for the 'drag' event. - * @param aEvent The drag event. - */ - _onDrag: function Site_onDrag(aEvent) { - gDrag.drag(this, aEvent); - }, - - /** - * Event handler for the 'dragend' event. - * @param aEvent The drag event. - */ - _onDragEnd: function Site_onDragEnd(aEvent) { - gDrag.end(this, aEvent); + handleEvent: function Site_handleEvent(aEvent) { + switch (aEvent.type) { + case "click": + aEvent.preventDefault(); + if (aEvent.target.classList.contains("newtab-control-block")) + this.block(); + else if (this.isPinned()) + this.unpin(); + else + this.pin(); + break; + case "dragstart": + gDrag.start(this, aEvent); + break; + case "drag": + gDrag.drag(this, aEvent); + break; + case "dragend": + gDrag.end(this, aEvent); + break; + } } };
deleted file mode 100644 --- a/browser/base/content/newtab/toolbar.js +++ /dev/null @@ -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(); - } -}; -
--- a/browser/base/content/newtab/transformations.js +++ b/browser/base/content/newtab/transformations.js @@ -6,16 +6,34 @@ /** * This singleton allows to transform the grid by repositioning a site's node * in the DOM and by showing or hiding the node. It additionally provides * 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. * @return A Rect instance with the position. */ getNodePosition: function Transformation_getNodePosition(aNode) { let {left, top, width, height} = aNode.getBoundingClientRect(); return new Rect(left + scrollX, top + scrollY, width, height); }, @@ -75,27 +93,38 @@ let gTransformation = { style.left = left + "px"; }, /** * Freezes a site in its current position by positioning it absolute. * @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)); }, /** * Unfreezes a site by removing its absolute positioning. * @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"); }, /** * Slides the given site to the target node's position. * @param aSite The site to move. * @param aTarget The slide target. * @param aOptions Set of options (see below). @@ -112,18 +141,23 @@ let gTransformation = { function finish() { if (aOptions && aOptions.unfreeze) self.unfreezeSitePosition(aSite); if (callback) 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); this._whenTransitionEnded(aSite.node, finish); } }, /** @@ -217,10 +251,19 @@ let gTransformation = { * Moves a site to the cell with the given index. * @param aSite The site to move. * @param aIndex The target cell's index. * @param aOptions Options that are directly passed to slideSiteTo(). */ _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"); } };
--- a/browser/base/content/test/browser_pageInfo.js +++ b/browser/base/content/test/browser_pageInfo.js @@ -1,46 +1,38 @@ function test() { waitForExplicitFinish(); var pageInfo; + gBrowser.selectedTab = gBrowser.addTab(); - gBrowser.selectedBrowser.addEventListener("load", function () { - gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + gBrowser.selectedBrowser.addEventListener("load", function loadListener() { + gBrowser.selectedBrowser.removeEventListener("load", loadListener, true); + + Services.obs.addObserver(observer, "page-info-dialog-loaded", false); pageInfo = BrowserPageInfo(); - Services.obs.addObserver(observer, "page-info-dialog-loaded", false); }, 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(); gBrowser.removeCurrentTab(); finish(); } }
--- a/browser/base/content/test/newtab/Makefile.in +++ b/browser/base/content/test/newtab/Makefile.in @@ -18,13 +18,15 @@ include $(topsrcdir)/config/rules.mk browser_newtab_drop_preview.js \ browser_newtab_private_browsing.js \ browser_newtab_reset.js \ browser_newtab_tabsync.js \ browser_newtab_unpin.js \ browser_newtab_bug722273.js \ browser_newtab_bug723102.js \ browser_newtab_bug723121.js \ + browser_newtab_bug725996.js \ + browser_newtab_bug734043.js \ head.js \ $(NULL) libs:: $(_BROWSER_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/base/content/test/newtab/browser_newtab_bug723102.js +++ b/browser/base/content/test/newtab/browser_newtab_bug723102.js @@ -7,11 +7,12 @@ function runTests() { setPinnedLinks(""); yield addNewTabPageTab(); let firstTab = gBrowser.selectedTab; 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"); }
--- a/browser/base/content/test/newtab/browser_newtab_bug723121.js +++ b/browser/base/content/test/newtab/browser_newtab_bug723121.js @@ -5,25 +5,29 @@ function runTests() { setLinks("0,1,2,3,4,5,6,7,8"); setPinnedLinks(""); yield addNewTabPageTab(); checkGridLocked(false, "grid is unlocked"); 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) { is(cw.gGrid.node.hasAttribute("locked"), aLocked, aMessage); } function sendDragEvent(aNode, aType) { let ifaceReq = cw.QueryInterface(Ci.nsIInterfaceRequestor);
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/newtab/browser_newtab_bug725996.js @@ -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); +}
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/newtab/browser_newtab_bug734043.js @@ -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); +}
--- a/browser/base/content/test/newtab/browser_newtab_disable.js +++ b/browser/base/content/test/newtab/browser_newtab_disable.js @@ -10,25 +10,25 @@ function runTests() { setLinks("0,1,2,3,4,5,6,7,8"); setPinnedLinks(""); yield addNewTabPageTab(); let gridNode = cw.gGrid.node; 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; // create a second new tage page and make sure it's disabled. enable it // again and check if the former page gets enabled as well. yield addNewTabPageTab(); ok(gridNode.hasAttribute("page-disabled"), "page is disabled"); // 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"); }
--- a/browser/base/content/test/newtab/browser_newtab_reset.js +++ b/browser/base/content/test/newtab/browser_newtab_reset.js @@ -1,15 +1,18 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /* * 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(""); yield addNewTabPageTab(); let resetButton = cw.document.getElementById("toolbar-button-reset"); checkGrid("0,1,2,3,4,5,6,7,8");
--- a/browser/base/content/test/newtab/browser_newtab_tabsync.js +++ b/browser/base/content/test/newtab/browser_newtab_tabsync.js @@ -3,16 +3,19 @@ /* * These tests make sure that all changes that are made to a specific * 'New Tab Page' are synchronized with all other open 'New Tab Pages' * automatically. All about:newtab pages should always be in the same * state. */ function runTests() { + // Disabled until bug 716543 is fixed. + return; + setLinks("0,1,2,3,4,5,6,7,8,9"); setPinnedLinks(",1"); yield addNewTabPageTab(); checkGrid("0,1p,2,3,4,5,6,7,8"); let resetButton = cw.document.getElementById("toolbar-button-reset"); ok(!resetButton.hasAttribute("modified"), "page is not modified");
--- a/browser/base/content/test/newtab/head.js +++ b/browser/base/content/test/newtab/head.js @@ -183,17 +183,17 @@ function checkGrid(aSitesPattern, aSites // Check the site's url. if (site.url != "about:blank#" + num) { valid = false; is(site.url, "about:blank#" + num, "cell#" + index + " has the wrong url"); } 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) { if (!cellContainsPinned) { valid = false; ok(false, "expected cell#" + index + " to be pinned"); } else if (!cssClassPinned) { valid = false; @@ -260,8 +260,25 @@ function simulateDrop(aDropTarget, aDrag if (aDragSource) cw.gDrag.start(aDragSource.site, event); cw.gDrop.drop(aDropTarget, event, function () executeSoon(TestRunner.next)); 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); + }); +}
--- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -39,17 +39,33 @@ # # ***** END LICENSE BLOCK ***** // Services = object with smart getters for common XPCOM services 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"; var gBidiUI = false; /** * Determines whether the given url is considered a special URL for new tabs.
--- a/browser/build.mk +++ b/browser/build.mk @@ -93,16 +93,22 @@ distclean:: @$(MAKE) -C browser/installer distclean source-package:: @$(MAKE) -C browser/installer 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 ifdef ENABLE_TESTS # Implemented in testing/testsuite-targets.mk mochitest-browser-chrome: $(RUN_MOCHITEST) --browser-chrome
--- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -62,16 +62,19 @@ XPCOMUtils.defineLazyGetter(this, "NetUt XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() { Cu.import("resource://gre/modules/PlacesUtils.jsm"); return PlacesUtils; }); 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"; // We try to backup bookmarks at idle times, to avoid doing that at shutdown. // Number of idle seconds before trying to backup bookmarks. 15 minutes. const BOOKMARKS_BACKUP_IDLE_TIME = 15 * 60; // Minimum interval in milliseconds between backups. const BOOKMARKS_BACKUP_INTERVAL = 86400 * 1000; @@ -341,16 +344,17 @@ BrowserGlue.prototype = { this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME); if (this._isPlacesInitObserver) os.removeObserver(this, "places-init-complete"); if (this._isPlacesLockedObserver) os.removeObserver(this, "places-database-locked"); if (this._isPlacesShutdownObserver) os.removeObserver(this, "places-shutdown"); os.removeObserver(this, "defaultURIFixup-using-keyword-pref"); + webappsUI.uninit(); }, _onAppDefaults: function BG__onAppDefaults() { // apply distribution customizations (prefs) // other customizations are applied in _onProfileStartup() this._distributionCustomizer.applyPrefDefaults(); }, @@ -365,16 +369,19 @@ BrowserGlue.prototype = { // apply distribution customizations // prefs are applied in _onAppDefaults() this._distributionCustomizer.applyCustomizations(); // handle any UI migration this._migrateUI(); + // Initialize webapps UI + webappsUI.init(); + Services.obs.notifyObservers(null, "browser-ui-startup-complete", ""); }, // the first browser window has finished initializing _onFirstWindowLoaded: function BG__onFirstWindowLoaded() { #ifdef XP_WIN // For windows seven, initialize the jump list module. const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1"; @@ -400,17 +407,16 @@ BrowserGlue.prototype = { this._showRightsNotification(); #ifdef MOZ_TELEMETRY_REPORTING } else { // Only show telemetry notification when about:rights notification is not shown. this._showTelemetryNotification(); #endif } - // Show update notification, if needed. if (Services.prefs.prefHasUserValue("app.update.postupdate")) this._showUpdateNotification(); // Load the "more info" page for a locked places.sqlite // This property is set earlier by places-database-locked topic. if (this._isPlacesDatabaseLocked) { this._showPlacesLockedNotificationBox();
--- a/browser/components/places/content/browserPlacesViews.js +++ b/browser/components/places/content/browserPlacesViews.js @@ -371,41 +371,36 @@ PlacesViewBase.prototype = { * @param aPopup * The livemark container popup * @param aStatus * The livemark status */ _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; } }, toggleCutNode: function PVB_toggleCutNode(aNode, aValue) { let elt = aNode._DOMElement; if (elt) { // We may get the popup for menus, but we need the menu itself. if (elt.localName == "menupopup") @@ -887,16 +882,24 @@ function PlacesToolbar(aPlace) { this._viewElt._placesView = this; this._addEventListeners(this._viewElt, this._cbEvents, false); this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true); 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") .add(Date.now() - startTime); } PlacesToolbar.prototype = { __proto__: PlacesViewBase.prototype, @@ -913,16 +916,17 @@ PlacesToolbar.prototype = { }, uninit: function PT_uninit() { this._removeEventListeners(this._viewElt, this._cbEvents, false); this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"], 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); }, _openedMenuButton: null, _allowPopupShowing: true, _rebuild: function PT__rebuild() { @@ -1012,19 +1016,20 @@ PlacesToolbar.prototype = { if (aBefore) this._rootElt.insertBefore(button, aBefore); else this._rootElt.appendChild(button); }, _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"; } }, _onChevronPopupShowing: function PT__onChevronPopupShowing(aEvent) { // Handle popupshowing only for the chevron popup, not for nested ones. if (aEvent.target != this._chevronPopup) return; @@ -1066,17 +1071,21 @@ PlacesToolbar.prototype = { case "underflow": if (aEvent.target != aEvent.currentTarget) return; // Ignore purely vertical underflows. if (aEvent.detail == 0) return; + this.updateChevron(); this._chevron.collapsed = true; + break; + case "TabOpen": + case "TabClose": this.updateChevron(); break; case "dragstart": this._onDragStart(aEvent); break; case "dragover": this._onDragOver(aEvent); break;
--- a/browser/components/thumbnails/PageThumbs.jsm +++ b/browser/components/thumbnails/PageThumbs.jsm @@ -13,25 +13,25 @@ const Ci = Components.interfaces; const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml"; /** * The default width for page thumbnails. * * 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. * * 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. */ const THUMBNAIL_BG_COLOR = "#fff"; Cu.import("resource://gre/modules/XPCOMUtils.jsm");
--- a/browser/config/mozconfigs/linux32/debug +++ b/browser/config/mozconfigs/linux32/debug @@ -13,8 +13,11 @@ export MOZILLA_OFFICIAL=1 # Enable parallel compiling mk_add_options MOZ_MAKE_FLAGS="-j4" #Use ccache 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
--- a/browser/config/mozconfigs/linux32/nightly +++ b/browser/config/mozconfigs/linux32/nightly @@ -22,8 +22,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT # Enable parallel compiling mk_add_options MOZ_MAKE_FLAGS="-j4" #Use ccache 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
--- a/browser/config/mozconfigs/linux32/release +++ b/browser/config/mozconfigs/linux32/release @@ -16,8 +16,11 @@ export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. 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
--- a/browser/config/mozconfigs/linux64/debug +++ b/browser/config/mozconfigs/linux64/debug @@ -10,8 +10,11 @@ ac_add_options --enable-stdcxx-compat # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 # Enable parallel compiling 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
--- a/browser/config/mozconfigs/linux64/nightly +++ b/browser/config/mozconfigs/linux64/nightly @@ -22,8 +22,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT # Enable parallel compiling mk_add_options MOZ_MAKE_FLAGS="-j4" #Use ccache 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
--- a/browser/config/mozconfigs/linux64/release +++ b/browser/config/mozconfigs/linux64/release @@ -16,8 +16,11 @@ export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. 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
--- a/browser/config/mozconfigs/macosx-universal/nightly +++ b/browser/config/mozconfigs/macosx-universal/nightly @@ -17,8 +17,11 @@ export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 mk_add_options MOZ_MAKE_FLAGS="-j4" 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
--- a/browser/config/mozconfigs/macosx-universal/release +++ b/browser/config/mozconfigs/macosx-universal/release @@ -12,8 +12,11 @@ export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. 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
--- a/browser/config/mozconfigs/macosx32/debug +++ b/browser/config/mozconfigs/macosx32/debug @@ -5,8 +5,11 @@ ac_add_options --enable-signmar # Enable parallel compiling mk_add_options MOZ_MAKE_FLAGS="-j4" # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 ac_add_options --with-macbundlename-prefix=Firefox + +# Package js shell. +export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/macosx64/debug +++ b/browser/config/mozconfigs/macosx64/debug @@ -10,8 +10,11 @@ mk_add_options MOZ_MAKE_FLAGS="-j4" # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 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
--- a/browser/config/mozconfigs/win32/debug +++ b/browser/config/mozconfigs/win32/debug @@ -3,8 +3,11 @@ ac_add_options --enable-trace-malloc ac_add_options --enable-signmar # Needed to enable breakpad in application.ini 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
--- a/browser/config/mozconfigs/win32/nightly +++ b/browser/config/mozconfigs/win32/nightly @@ -12,8 +12,11 @@ ac_add_options --enable-js-diagnostics # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 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
--- a/browser/config/mozconfigs/win32/release +++ b/browser/config/mozconfigs/win32/release @@ -8,8 +8,11 @@ ac_add_options --enable-jemalloc ac_add_options --enable-official-branding # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 . $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig + +# Package js shell. +export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/win64/debug +++ b/browser/config/mozconfigs/win64/debug @@ -4,8 +4,13 @@ ac_add_options --host=x86_64-pc-mingw32 ac_add_options --enable-debug ac_add_options --enable-trace-malloc ac_add_options --enable-signmar # Needed to enable breakpad in application.ini 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
--- a/browser/config/mozconfigs/win64/nightly +++ b/browser/config/mozconfigs/win64/nightly @@ -13,8 +13,13 @@ ac_add_options --enable-signmar ac_add_options --enable-js-diagnostics # Needed to enable breakpad in application.ini 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
new file mode 100644 --- /dev/null +++ b/browser/config/mozconfigs/win64/vs2010-mozconfig @@ -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"
--- a/browser/devtools/debugger/test/Makefile.in +++ b/browser/devtools/debugger/test/Makefile.in @@ -66,33 +66,35 @@ include $(topsrcdir)/config/rules.mk browser_dbg_stack-01.js \ browser_dbg_stack-02.js \ browser_dbg_stack-03.js \ browser_dbg_stack-04.js \ browser_dbg_location-changes.js \ 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) _BROWSER_TEST_PAGES = \ browser_dbg_tab1.html \ browser_dbg_tab2.html \ browser_dbg_debuggerstatement.html \ browser_dbg_stack.html \ browser_dbg_script-switching.html \ test-script-switching-01.js \ test-script-switching-02.js \ browser_dbg_frame-parameters.html \ browser_dbg_update-editor-mode.html \ test-editor-mode \ + browser_dbg_displayName.html \ $(NULL) libs:: $(_BROWSER_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir) libs:: $(_BROWSER_TEST_PAGES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_displayName.html @@ -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>
new file mode 100644 --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_displayName.js @@ -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(); + }); +}
--- a/browser/devtools/highlighter/TreePanel.jsm +++ b/browser/devtools/highlighter/TreePanel.jsm @@ -304,92 +304,107 @@ TreePanel.prototype = { this.IUI.highlighter.highlight(node); } } } }, /** * 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. */ onTreeDblClick: function TP_onTreeDblClick(aEvent) { // if already editing an attribute value, double-clicking elsewhere // in the tree is the same as a click, which dismisses the editor if (this.editingContext) this.closeEditor(); let target = aEvent.target; + if (!this.hasClass(target, "editable")) { + return; + } + + let repObj = this.getRepObject(target); + if (this.hasClass(target, "nodeValue")) { - let repObj = this.getRepObject(target); 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 * The name of the attribute being edited * @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 = this.treeBrowserDocument.getElementById("attribute-editor-input"); let attrDims = aAttrObj.getBoundingClientRect(); // figure out actual viewable viewport dimensions (sans scrollbars) let viewportWidth = this.treeBrowserDocument.documentElement.clientWidth; let viewportHeight = this.treeBrowserDocument.documentElement.clientHeight; // saves the editing context for use when the editor is saved/closed this.editingContext = { attrObj: aAttrObj, repObj: aRepObj, - attrName: aAttrName + attrName: aAttrName, + attrValue: aAttrVal }; // highlight attribute-value node in tree while editing this.addClass(aAttrObj, "editingAttributeValue"); // show the editor 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; // outer editor is sized based on the <input> box inside it editorInput.style.width = Math.min(attrDims.width, viewportWidth - editorViewportBoundary) + "px"; editorInput.style.height = Math.min(attrDims.height, viewportHeight - editorViewportBoundary) + "px"; let editorDims = editor.getBoundingClientRect(); // calculate position for the editor according to the attribute node let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX - // 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( (this.treeIFrame.contentWindow.scrollX + viewportWidth - editorDims.width), editorLeft) ); editorTop = Math.max(0, Math.min( @@ -398,18 +413,23 @@ TreePanel.prototype = { editorTop) ); // position the editor editor.style.left = editorLeft + "px"; editor.style.top = editorTop + "px"; // set and select the text - editorInput.value = aAttrVal; - editorInput.select(); + 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) { aEvent.stopPropagation(); }); this.bindEditorEvent(editor, "dblclick", function(aEvent) { aEvent.stopPropagation(); }); @@ -505,25 +525,42 @@ TreePanel.prototype = { /** * Commit the edits made in the editor, then close it. */ saveEditor: function TP_saveEditor() { let editorInput = this.treeBrowserDocument.getElementById("attribute-editor-input"); + let dirty = false; - // set the new attribute value on the original target DOM element - this.editingContext.repObj.setAttribute(this.editingContext.attrName, - editorInput.value); + 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; + } - // update the HTML tree attribute value - this.editingContext.attrObj.innerHTML = editorInput.value; + if (this.hasClass(this.editingContext.attrObj, "nodeName")) { + // remove the original attribute from the original target DOM element + this.editingContext.repObj.removeAttribute(this.editingContext.attrName); - this.IUI.isDirty = true; + // 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 Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, null); this.closeEditor(); },
--- a/browser/devtools/highlighter/inspector.jsm +++ b/browser/devtools/highlighter/inspector.jsm @@ -22,16 +22,17 @@ * * Contributor(s): * Rob Campbell <rcampbell@mozilla.com> (original author) * Mihai Șucan <mihai.sucan@gmail.com> * Julian Viereck <jviereck@mozilla.com> * 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 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -99,16 +100,25 @@ function InspectorUI(aWindow) { this.chromeWin = aWindow; this.chromeDoc = aWindow.document; this.tabbrowser = aWindow.gBrowser; this.tools = {}; 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 = { browser: null, tools: null, toolEvents: null, inspecting: false, ruleViewEnabled: true, @@ -840,30 +850,26 @@ InspectorUI.prototype = { }, /** * Copy the innerHTML of the selected Node to the clipboard. Called via the * Inspector:CopyInner command. */ 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); }, /** * Copy the outerHTML of the selected Node to the clipboard. Called via the * Inspector:CopyOuter command. */ 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); }, /** * Delete the selected node. Called via the Inspector:DeleteNode command. */ deleteNode: function IUI_deleteNode() { let selection = this.selection; @@ -921,22 +927,44 @@ InspectorUI.prototype = { let ruleViewStore = this.store.getValue(winID, "ruleView"); if (!ruleViewStore) { ruleViewStore = {}; this.store.setValue(winID, "ruleView", ruleViewStore); } 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); Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, null); }.bind(this); iframe.addEventListener("load", boundLoadListener, true); @@ -998,28 +1026,365 @@ InspectorUI.prototype = { this.chromeWin.StyleEditor.openChrome(styleSheet, rule.ruleLine); } else { let href = rule.elementStyle.element.ownerDocument.location.href; this.chromeWin.openUILinkIn("view-source:" + href, "window"); } }, /** + * 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; } }, ///////////////////////////////////////////////////////////////////////// //// Utility Methods @@ -1231,16 +1596,17 @@ InspectorUI.prototype = { btn.setAttribute("group", "sidebar-tools"); this.sidebarToolbar.appendChild(btn); // create tool iframe let iframe = this.chromeDoc.createElement("iframe"); 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 this.bindToolEvent(btn, "click", function showIframe() { this.toolShow(aRegObj); }.bind(this)); }, @@ -1342,16 +1708,20 @@ InspectorUI.prototype = { */ unregisterSidebarTool: function IUI_unregisterSidebarTool(aRegObj) { // unbind tool button click event let buttonId = this.getToolbarButtonId(aRegObj.id); 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); // call unregister callback and remove from collection, this also removes // the iframe. if (aRegObj.unregister) aRegObj.unregister.call(aRegObj.context); @@ -2227,8 +2597,22 @@ XPCOMUtils.defineLazyGetter(this, "Style var obj = {}; Cu.import("resource:///modules/devtools/StyleInspector.jsm", obj); return obj.StyleInspector; }); 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; +});
--- a/browser/devtools/highlighter/test/Makefile.in +++ b/browser/devtools/highlighter/test/Makefile.in @@ -53,16 +53,17 @@ include $(topsrcdir)/config/rules.mk browser_inspector_tab_switch.js \ browser_inspector_treePanel_output.js \ browser_inspector_treePanel_input.html \ browser_inspector_treePanel_result.html \ browser_inspector_registertools.js \ 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 \ browser_inspector_bug_672902_keyboard_shortcuts.js \ browser_inspector_keybindings.js \ browser_inspector_breadcrumbs.html \ browser_inspector_breadcrumbs.js \ browser_inspector_bug_699308_iframe_navigation.js \
--- a/browser/devtools/highlighter/test/browser_inspector_editor.js +++ b/browser/devtools/highlighter/test/browser_inspector_editor.js @@ -1,20 +1,15 @@ /* -*- 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/ - * - * 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; let editorTestSteps; function doNextStep() { editorTestSteps.next(); }
new file mode 100644 --- /dev/null +++ b/browser/devtools/highlighter/test/browser_inspector_editor_name.js @@ -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"; +} +
--- a/browser/devtools/scratchpad/scratchpad.xul +++ b/browser/devtools/scratchpad/scratchpad.xul @@ -19,16 +19,17 @@ - The Mozilla Foundation. - Portions created by the Initial Developer are Copyright (C) 2011 - the Initial Developer. All Rights Reserved. - - Contributor(s): - 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 - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - in which case the provisions of the GPL or the LGPL are applicable instead - of those above. If you wish to allow use of your version of this file only - under the terms of either the GPL or the LGPL, and not to allow others to - use your version of this file under the terms of the MPL, indicate your
--- a/browser/devtools/sourceeditor/orion/Makefile.dryice.js +++ b/browser/devtools/sourceeditor/orion/Makefile.dryice.js @@ -48,16 +48,17 @@ copy({ ORION_EDITOR + "/orion/textview/global.js", ORION_EDITOR + "/orion/textview/eventTarget.js", ORION_EDITOR + "/orion/editor/regex.js", ORION_EDITOR + "/orion/textview/keyBinding.js", ORION_EDITOR + "/orion/textview/annotations.js", 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", ORION_EDITOR + "/orion/editor/htmlGrammar.js", ORION_EDITOR + "/orion/editor/textMateStyler.js", ORION_EDITOR + "/examples/textview/textStyler.js", ], dest: js_src,
--- a/browser/devtools/sourceeditor/orion/README +++ b/browser/devtools/sourceeditor/orion/README @@ -17,16 +17,20 @@ Orion version: git clone from 2012-01-26 + patches for Eclipse Bug 370606 - Problems with UndoStack and deletions at the beginning of the document http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=cec71bddaf32251c34d3728df5da13c130d14f33 http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=3ce24b94f1d8103b16b9cf16f2f50a6302d43b18 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 file: orion.js orion.css # Theming
--- a/browser/devtools/sourceeditor/orion/orion.js +++ b/browser/devtools/sourceeditor/orion/orion.js @@ -2565,16 +2565,600 @@ define("orion/textview/textModel", ['ori });/******************************************************************************* * @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 + * 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: IBM Corporation - initial API and implementation ******************************************************************************/ /*global define setTimeout clearTimeout setInterval clearInterval Node */ define("orion/textview/tooltip", ['orion/textview/textView', 'orion/textview/textModel', 'orion/textview/projectionTextModel'], function(mTextView, mTextModel, mProjectionTextModel) { /** @private */
--- a/browser/devtools/sourceeditor/source-editor-orion.jsm +++ b/browser/devtools/sourceeditor/source-editor-orion.jsm @@ -903,17 +903,18 @@ SourceEditor.prototype = { } }, /** * Retrieve the list of Orion Annotations filtered by type for the given text range. * * @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 * End offset for retrieving the annotations. * @return array * The array of annotations, filtered by type, within the given text * range. */
--- a/browser/devtools/sourceeditor/source-editor.jsm +++ b/browser/devtools/sourceeditor/source-editor.jsm @@ -263,34 +263,34 @@ SourceEditor.EVENTS = { FOCUS: "Focus", /** * The blur event is fired when the editor goes out of focus. */ 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. */ MOUSE_OUT: "MouseOut", /** * The BreakpointChange event is fired when a new breakpoint is added or when * a breakpoint is removed - either through API use or through the editor UI.
--- a/browser/devtools/styleinspector/CssHtmlTree.jsm +++ b/browser/devtools/styleinspector/CssHtmlTree.jsm @@ -36,16 +36,17 @@ * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ const Ci = Components.interfaces; +const Cc = Components.classes; const Cu = Components.utils; const FILTER_CHANGED_TIMEOUT = 300; const HTML_NS = "http://www.w3.org/1999/xhtml"; const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PluralForm.jsm"); @@ -156,31 +157,41 @@ function CssHtmlTree(aStyleInspector) this.styleWin = aStyleInspector.iframe; this.styleInspector = aStyleInspector; this.cssLogic = aStyleInspector.cssLogic; this.doc = aStyleInspector.document; this.win = aStyleInspector.window; 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"); this.propertyContainer = this.styleDocument.getElementById("propertyContainer"); this.panel = aStyleInspector.panel; // No results text. this.noResults = this.styleDocument.getElementById("noResults"); // The element that we're inspecting, and the document that it comes from. this.viewedElement = null; this.createStyleViews(); + this.createContextMenu(); } /** * Memoized lookup of a l10n string from a string bundle. * @param {string} aName The key to lookup. * @returns A localized version of the given key. */ CssHtmlTree.l10n = function CssHtmlTree_l10n(aName) @@ -226,16 +237,21 @@ CssHtmlTree.processTemplate = function C XPCOMUtils.defineLazyGetter(CssHtmlTree, "_strings", function() Services.strings .createBundle("chrome://browser/locale/devtools/styleinspector.properties")); 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, _unmatchedProperties: null, htmlComplete: false, // Used for cancelling timeouts in the style filter. @@ -465,32 +481,229 @@ CssHtmlTree.prototype = { let result = this.cssLogic.hasUnmatchedSelectors([aProperty]); this._unmatchedProperties[aProperty] = result[aProperty]; } 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. */ destroy: function CssHtmlTree_destroy() { delete this.viewedElement; // Remove event listeners this.onlyUserStylesCheckbox.removeEventListener("command", this.onlyUserStylesChanged); this.searchField.removeEventListener("command", this.filterChanged); // Cancel tree construction if (this._refreshProcess) { 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; delete this.panel; // The document in which we display the results (csshtmltree.xul). delete this.styleDocument; @@ -653,42 +866,45 @@ PropertyView.prototype = { return "property-content-hidden"; }, buildMain: function PropertyView_buildMain() { 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); + aEvent.keyCode == keyEvent.DOM_VK_SPACE) { + 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); helpcontainer.setAttribute("class", "helplink-container"); let helplink = doc.createElementNS(HTML_NS, "a"); helpcontainer.appendChild(helplink); helplink.setAttribute("class", "helplink"); @@ -749,19 +965,19 @@ PropertyView.prototype = { * Refresh the panel matched rules. */ refreshMatchedSelectors: function PropertyView_refreshMatchedSelectors() { let hasMatchedSelectors = this.hasMatchedSelectors; 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) { CssHtmlTree.processTemplate(this.templateMatchedSelectors, this.matchedSelectorsContainer, this); this.matchedExpander.setAttribute("open", ""); } else { this.matchedSelectorsContainer.innerHTML = ""; @@ -847,27 +1063,23 @@ PropertyView.prototype = { return this._unmatchedSelectorViews; }, /** * 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(); - } + this.matchedExpanded = !this.matchedExpanded; + this.refreshAllSelectors(); + aEvent.preventDefault(); }, /** * The action when a user expands unmatched selectors. */ unmatchedSelectorsClick: function PropertyView_unmatchedSelectorsClick(aEvent) { this.unmatchedExpanded = !this.unmatchedExpanded; @@ -981,16 +1193,24 @@ SelectorView.prototype = { aEvent.preventDefault(); }.bind(this), false); result += ".style"; } 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). * 2. Open the link in the style editor. * * @param aEvent The click event */ openStyleEditor: function(aEvent)
--- a/browser/devtools/styleinspector/CssLogic.jsm +++ b/browser/devtools/styleinspector/CssLogic.jsm @@ -922,17 +922,18 @@ CssLogic.shortSource = function CssLogic if (url.filePath) { return url.filePath; } if (url.query) { return url.query; } - return aSheet.href; + let dataUrl = aSheet.href.match(/^(data:[^,]*),/); + return dataUrl ? dataUrl[1] : aSheet.href; } /** * A safe way to access cached bits of information about a stylesheet. * * @constructor * @param {CssLogic} aCssLogic pointer to the CssLogic instance working with * this CssSheet object.
--- a/browser/devtools/styleinspector/CssRuleView.jsm +++ b/browser/devtools/styleinspector/CssRuleView.jsm @@ -18,16 +18,17 @@ * The Initial Developer of the Original Code is * The Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * 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 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -802,34 +803,36 @@ CssRuleView.prototype = { /** * Creates editor UI for each of the rules in _elementStyle. */ _createEditors: function CssRuleView_createEditors() { 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); } }, }; /** * 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); this._create(); } RuleEditor.prototype = { @@ -888,18 +891,26 @@ RuleEditor.prototype = { this.closeBrace = createChild(code, "div", { class: "ruleview-ruleclose", tabindex: "0", textContent: "}" }); // 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.newProperty(); + 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); }, /** * Create a text input for a property name. If a non-empty property * name is given, we'll create a real TextProperty and add it to the * rule. */ @@ -1256,16 +1267,31 @@ TextPropertyEditor.prototype = { * If any characters in advanceChars are typed, focus will advance * to the next element. */ 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; function InplaceEditor(aOptions) { this.elt = aOptions.element; this.elt.inplaceEditor = this;
--- a/browser/devtools/styleinspector/StyleInspector.jsm +++ b/browser/devtools/styleinspector/StyleInspector.jsm @@ -50,17 +50,17 @@ var EXPORTED_SYMBOLS = ["StyleInspector" * StyleInspector Constructor Function. * @param {window} aContext, the chrome window context we're calling from. * @param {InspectorUI} aIUI (optional) An InspectorUI instance if called from the * Highlighter. */ function StyleInspector(aContext, aIUI) { this._init(aContext, aIUI); -}; +} StyleInspector.prototype = { /** * Initialization method called from constructor. * @param {window} aContext, the chrome window context we're calling from. * @param {InspectorUI} aIUI (optional) An InspectorUI instance if called from * the Highlighter.
--- a/browser/devtools/styleinspector/csshtmltree.xul +++ b/browser/devtools/styleinspector/csshtmltree.xul @@ -34,16 +34,17 @@ - decision by deleting the provisions above and replace them with the notice - and other provisions required by the LGPL or the GPL. If you do not delete - the provisions above, a recipient may use your version of this file under - the terms of any one of the MPL, the GPL or the LGPL. - - ***** END LICENSE BLOCK ***** --> <?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 [ <!ENTITY % inspectorDTD SYSTEM "chrome://browser/locale/devtools/styleinspector.dtd"> %inspectorDTD; <!ELEMENT loop ANY> <!ATTLIST li foreach CDATA #IMPLIED> <!ATTLIST div foreach CDATA #IMPLIED> @@ -109,18 +110,21 @@ To visually debug the templates without <div id="templateMatchedSelectors"> <table> <loop foreach="selector in ${matchedSelectorViews}"> <tr> <td dir="ltr" class="rule-text ${selector.statusClass}"> ${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> </table> </div> </div> </xul:window>
--- a/browser/devtools/styleinspector/styleinspector.css +++ b/browser/devtools/styleinspector/styleinspector.css @@ -30,21 +30,49 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ +#root { + display: -moz-box; +} + +.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; } -.ruleview-computedlist:not(.styleinspector-open) { +.property-view-hidden, +.property-content-hidden, +.ruleview-computedlist:not(.styleinspector-open), +.ruleview-warning[hidden] { display: none; } .ruleview-code { direction: ltr; } .ruleview-property:not(:hover) > .ruleview-enableproperty {
--- a/browser/devtools/styleinspector/test/Makefile.in +++ b/browser/devtools/styleinspector/test/Makefile.in @@ -60,16 +60,18 @@ include $(topsrcdir)/config/rules.mk browser_ruleview_manipulation.js \ browser_ruleview_override.js \ browser_ruleview_ui.js \ browser_ruleview_focus.js \ browser_bug705707_is_content_stylesheet.js \ 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) _BROWSER_TEST_PAGES = \ browser_bug683672.html \ browser_bug705707_is_content_stylesheet.html \ browser_bug705707_is_content_stylesheet_imported.css \ browser_bug705707_is_content_stylesheet_imported2.css \
--- a/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js +++ b/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js @@ -50,36 +50,36 @@ function SI_test() Services.obs.removeObserver(SI_test, "StyleInspector-populated", false); info("checking keybindings"); let iframe = stylePanel.iframe; 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); checkHelpLinkKeybinding(); Services.obs.addObserver(finishUp, "StyleInspector-closed", false); stylePanel.close(); }); info("Adding focus event handler to search filter"); 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); }); info("Making sure that the style inspector panel is focused"); SimpleTest.waitForFocus(function windowFocused() { info("window is focused"); info("focusing search filter"); searchbar.focus();
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_computedview_bug_703643_context_menu_copy.js @@ -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"; +}
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js @@ -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>"; +}
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -139,16 +139,17 @@ @BINPATH@/components/content_htmldoc.xpt @BINPATH@/components/content_html.xpt @BINPATH@/components/content_xslt.xpt @BINPATH@/components/content_xtf.xpt @BINPATH@/components/cookie.xpt @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 @BINPATH@/components/dom_wifi.xpt @BINPATH@/components/dom_system_b2g.xpt #endif @BINPATH@/components/dom_battery.xpt #ifdef MOZ_B2G_BT @@ -405,16 +406,18 @@ #ifdef MOZ_SERVICES_SYNC @BINPATH@/components/SyncComponents.manifest @BINPATH@/components/Weave.js #endif @BINPATH@/components/TelemetryPing.js @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 ; Modules @BINPATH@/modules/* ; Safe Browsing
--- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -208,18 +208,16 @@ can reach it easily. --> - "Scratchpad" in your locale. You should feel free to find a close - approximation to it or choose a word (or words) that means - "simple discardable text editor". --> <!ENTITY scratchpad.label "Scratchpad"> <!ENTITY scratchpad.accesskey "s"> <!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"> <!ENTITY inspectorHTMLCopyInner.accesskey "I"> <!ENTITY inspectorHTMLCopyOuter.label "Copy Outer HTML"> <!ENTITY inspectorHTMLCopyOuter.accesskey "O">
--- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -329,16 +329,22 @@ syncPromoNotification.learnMoreLinkText= # and %2$S by the value of the toolkit.telemetry.server_owner preference. telemetryPrompt = Will you help improve %1$S by sending anonymous information about performance, hardware characteristics, feature usage, and browser customizations to %2$S? telemetryLinkLabel = Learn More telemetryYesButtonLabel2 = Yes, I want to help 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 # - %2$S is a host name (e.g. "somewebsearch.com") from the current value of keyword.URL # - %3$S is the name of the default search engine (e.g. "Google") keywordPrompt.message = %1$S is using '%2$S' for searches from the location bar. Would you like to restore the default search (%3$S)? # LOCALIZATION NOTE (keywordPrompt.yesButton): %1$S is the name of the default search engine
--- a/browser/locales/en-US/chrome/browser/devtools/inspector.properties +++ b/browser/locales/en-US/chrome/browser/devtools/inspector.properties @@ -19,9 +19,16 @@ confirmNavigationAway.buttonStayAccesske breadcrumbs.siblings=Siblings # LOCALIZATION NOTE (htmlPanel): Used in the Inspector tool's openInspectorUI # method when registering the HTML panel. # LOCALIZATION NOTE (ruleView.*): Button label, accesskey and tooltip text # associated with the Highlighter's CSS Rule View in the Style Sidebar. ruleView.label=Rules ruleView.accesskey=R -ruleView.tooltiptext=View and Edit CSS \ No newline at end of file +ruleView.tooltiptext=View and Edit CSS + +# LOCALIZATION NOTE (inspectButton.tooltiptext): +# This button appears in the Inspector Toolbar. inspectButton is stateful, +# if it's pressed users can select an element with the mouse. Pressing the +# "Return" key # changes that state. %S is the keyboard shortcut (VK_RETURN in +# chrome://global/locale/keys.properties). +inspectButton.tooltiptext=Select element with mouse (%S)
--- a/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties +++ b/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties @@ -44,8 +44,80 @@ style.highlighter.button.tooltip2=Inspec # quickly jump to the documentation from the Mozilla Developer Network site. # This is the link title shown in the hover tooltip. helpLinkTitle=Read the documentation for this property # LOCALIZATION NOTE (rule.warning.title): When an invalid property value is # entered into the rule view a warning icon is displayed. This text is used for # the title attribute of the warning icon. rule.warning.title=Invalid property value + +# LOCALIZATION NOTE (style.contextmenu.copyselection): The computed view's +# context menu copy entry. +style.contextmenu.copyselection=Copy selection + +# LOCALIZATION NOTE (style.contextmenu.copyselection.accesskey): The computed +# view's context menu copy entry access key. +style.contextmenu.copyselection.accesskey=C + +# LOCALIZATION NOTE (style.contextmenu.copydeclaration): The style inspector's +# context menu copy property entry allows a complete CSS property to be copied. +style.contextmenu.copydeclaration=Copy declaration line + +# LOCALIZATION NOTE (style.contextmenu.copydeclaration.accesskey): The style +# inspector's context menu copy property access key. +style.contextmenu.copydeclaration.accesskey=D + +# LOCALIZATION NOTE (style.contextmenu.copyproperty): The style inspector's +# context menu copy property name entry allows a CSS property name to be copied. +style.contextmenu.copyproperty=Copy property + +# LOCALIZATION NOTE (style.contextmenu.copyproperty.accesskey): The style +# inspector's context menu copy property name access key. +style.contextmenu.copyproperty.accesskey=P + +# LOCALIZATION NOTE (style.contextmenu.copypropertyvalue): The style inspector's +# context menu copy property value entry allows a CSS property name to be copied. +style.contextmenu.copypropertyvalue=Copy property value + +# LOCALIZATION NOTE (style.contextmenu.copypropertyvalue.accesskey): The style +# inspector's context menu copy property value access key. +style.contextmenu.copypropertyvalue.accesskey=U + +# LOCALIZATION NOTE (rule.contextmenu.copyselection): The rule view's context +# menu copy entry. +rule.contextmenu.copyselection=Copy selection + +# LOCALIZATION NOTE (rule.contextmenu.copyselection.accesskey): The rule view's +# context menu copy entry access key. +rule.contextmenu.copyselection.accesskey=C + +# LOCALIZATION NOTE (rule.contextmenu.copyrule): The rule view's context menu +# copy rule entry allows a complete CSS rule to be copied. +rule.contextmenu.copyrule=Copy rule + +# LOCALIZATION NOTE (rule.contextmenu.copyrule.accesskey): The rule view's +# context menu copy rule access key. +rule.contextmenu.copyrule.accesskey=R + +# LOCALIZATION NOTE (rule.contextmenu.copydeclaration): The rule view's context +# menu copy property entry allows a complete CSS property to be copied. +rule.contextmenu.copydeclaration=Copy declaration line + +# LOCALIZATION NOTE (rule.contextmenu.copydeclaration.accesskey): The rule view's +# context menu copy property access key. +rule.contextmenu.copydeclaration.accesskey=D + +# LOCALIZATION NOTE (rule.contextmenu.copyproperty): The rule view's context +# menu copy property entry allows a CSS property name to be copied. +rule.contextmenu.copyproperty=Copy property + +# LOCALIZATION NOTE (rule.contextmenu.copyproperty.accesskey): The rule +# view's context menu copy property name access key. +rule.contextmenu.copyproperty.accesskey=P + +# LOCALIZATION NOTE (rule.contextmenu.copypropertyvalue): The rule view's +# context menu copy property entry allows a CSS property value to be copied. +rule.contextmenu.copypropertyvalue=Copy property value + +# LOCALIZATION NOTE (rule.contextmenu.copypropertyvalue.accesskey): The rule +# view's context menu copy property value access key. +rule.contextmenu.copypropertyvalue.accesskey=U
--- a/browser/locales/en-US/chrome/browser/newTab.dtd +++ b/browser/locales/en-US/chrome/browser/newTab.dtd @@ -1,6 +1,2 @@ <!-- These strings are used in the about:newtab page --> <!ENTITY newtab.pageTitle "New Tab"> - -<!ENTITY newtab.show "Show the New Tab Page"> -<!ENTITY newtab.hide "Hide the New Tab Page"> -<!ENTITY newtab.reset "Reset the New Tab Page">
--- a/browser/locales/en-US/chrome/browser/newTab.properties +++ b/browser/locales/en-US/chrome/browser/newTab.properties @@ -1,3 +1,5 @@ newtab.pin=Pin this site at its current position newtab.unpin=Unpin this site newtab.block=Remove this site +newtab.show=Show the new tab page +newtab.hide=Hide the new tab page
--- a/browser/modules/Makefile.in +++ b/browser/modules/Makefile.in @@ -48,16 +48,17 @@ TEST_DIRS += test EXTRA_JS_MODULES = \ openLocationLastURL.jsm \ NetworkPrioritizer.jsm \ NewTabUtils.jsm \ offlineAppCache.jsm \ TelemetryTimestamps.jsm \ KeywordURLResetPrompter.jsm \ + webappsUI.jsm \ $(NULL) ifeq ($(MOZ_WIDGET_TOOLKIT),windows) EXTRA_JS_MODULES += \ WindowsPreviewPerTab.jsm \ WindowsJumpLists.jsm \ $(NULL) endif
new file mode 100644 --- /dev/null +++ b/browser/modules/webappsUI.jsm @@ -0,0 +1,133 @@ +/* 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/. */ + +let EXPORTED_SYMBOLS = ["webappsUI"]; + +let Ci = Components.interfaces; +let Cc = Components.classes; +let Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Webapps.jsm"); + +let webappsUI = { + init: function webappsUI_init() { + Services.obs.addObserver(this, "webapps-ask-install", false); + Services.obs.addObserver(this, "webapps-launch", false); + }, + + uninit: function webappsUI_uninit() { + Services.obs.removeObserver(this, "webapps-ask-install"); + Services.obs.removeObserver(this, "webapps-launch"); + }, + + observe: function webappsUI_observe(aSubject, aTopic, aData) { + let data = JSON.parse(aData); + + switch(aTopic) { + case "webapps-ask-install": + let [chromeWin, browser] = this._getBrowserForId(data.oid); + if (chromeWin) + this.doInstall(data, browser, chromeWin); + break; + case "webapps-launch": + DOMApplicationRegistry.getManifestFor(data.origin, (function(aManifest) { + if (!aManifest) + return; + let manifest = new DOMApplicationManifest(aManifest, data.origin); + this.openURL(manifest.fullLaunchPath(), data.origin); + }).bind(this)); + break; + } + }, + + openURL: function(aUrl, aOrigin) { + let browserEnumerator = Services.wm.getEnumerator("navigator:browser"); + let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); + + // Check each browser instance for our URL + let found = false; + while (!found && browserEnumerator.hasMoreElements()) { + let browserWin = browserEnumerator.getNext(); + let tabbrowser = browserWin.gBrowser; + + // Check each tab of this browser instance + let numTabs = tabbrowser.tabs.length; + for (let index = 0; index < numTabs; index++) { + let tab = tabbrowser.tabs[index]; + let appURL = ss.getTabValue(tab, "appOrigin"); + if (appURL == aOrigin) { + // The URL is already opened. Select this tab. + tabbrowser.selectedTab = tab; + browserWin.focus(); + found = true; + break; + } + } + } + + // Our URL isn't open. Open it now. + if (!found) { + let recentWindow = Services.wm.getMostRecentWindow("navigator:browser"); + if (recentWindow) { + // Use an existing browser window + let browser = recentWindow.gBrowser; + let tab = browser.addTab(aUrl); + browser.pinTab(tab); + browser.selectedTab = tab; + ss.setTabValue(tab, "appOrigin", aOrigin); + } + } + }, + + _getBrowserForId: function(aId) { + let someWindow = Services.wm.getMostRecentWindow(null); + + if (someWindow) { + let windowUtils = someWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + let content = windowUtils.getOuterWindowWithId(aId); + if (content) { + let browser = content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell).chromeEventHandler; + let win = browser.ownerDocument.defaultView; + return [win, browser]; + } + } + + return [null, null]; + }, + + doInstall: function(aData, aBrowser, aWindow) { + let bundle = aWindow.gNavigatorBundle; + + let mainAction = { + label: bundle.getString("webapps.install"), + accessKey: bundle.getString("webapps.install.accesskey"), + callback: function(notification) { + installDone = true; + DOMApplicationRegistry.confirmInstall(aData); + } + }; + + let requestingURI = aWindow.makeURI(aData.from); + let manifest = new DOMApplicationManifest(aData.app.manifest, aData.app.origin); + + let host; + try { + host = requestingURI.host; + } catch(e) { + host = requestingURI.spec; + } + + let message = bundle.getFormattedString("webapps.requestInstall", + [manifest.name, host], 2); + + aWindow.PopupNotifications.show(aBrowser, "webapps-install", message, "webapps-notification-icon", + mainAction, null, { popupIconURL: manifest.iconURLForSize(64) }); + + } +}
--- a/browser/themes/gnomestripe/browser.css +++ b/browser/themes/gnomestripe/browser.css @@ -1225,16 +1225,20 @@ toolbar[iconsize="small"] #feed-button { list-style-image: url(chrome://global/skin/icons/question-64.png); } .popup-notification-icon[popupid="password-save"], .popup-notification-icon[popupid="password-change"] { list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png); } +.popup-notification-icon[popupid="webapps-install"] { + list-style-image: url(chrome://browser/skin/webapps-64.png); +} + /* Notification icon box */ #notification-popup-box { position: relative; background-color: #fff; background-clip: padding-box; padding-left: 4px; border-radius: 2.5px 0 0 2.5px; border-width: 0 8px 0 0; @@ -1279,16 +1283,20 @@ toolbar[iconsize="small"] #feed-button { #indexedDB-notification-icon { list-style-image: url(chrome://global/skin/icons/question-16.png); } #password-notification-icon { list-style-image: url(chrome://mozapps/skin/passwordmgr/key-16.png); } +#webapps-notification-icon { + list-style-image: url(chrome://browser/skin/webapps-16.png); +} + #treecolAutoCompleteImage { max-width : 36px; } .ac-result-type-bookmark, .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) { list-style-image: url("chrome://browser/skin/places/pageStarred.png"); width: 16px; @@ -1983,16 +1991,25 @@ panel[dimmed="true"] { #highlighter-veil-container[locked] > #highlighter-veil-middlebox > #highlighter-veil-transparentbox { box-shadow: 0 0 0 1px black; outline-color: white; } /* Highlighter toolbar */ +#inspector-inspect-toolbutton { + list-style-image: url("chrome://browser/skin/devtools/inspect-button.png"); + -moz-image-region: rect(0px 16px 16px 0px); +} + +#inspector-inspect-toolbutton[checked] { + -moz-image-region: rect(0px 32px 16px 16px); +} + #inspector-toolbar { border-top: 1px solid hsla(210, 8%, 5%, .65); } #devtools-side-splitter { -moz-appearance: none; border: 0; -moz-border-start: 1px solid #242b33;
--- a/browser/themes/gnomestripe/devtools/common.css +++ b/browser/themes/gnomestripe/devtools/common.css @@ -52,16 +52,20 @@ text-shadow: 0 -1px 0 hsla(210,8%,5%,.45); border: 1px solid hsla(210,8%,5%,.45); border-radius: 3px; background: -moz-linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box; box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15); margin: 0 3px; } +.devtools-toolbarbutton:not([label]) { + min-width: 32px; +} + .devtools-toolbarbutton:not([checked]):hover:active { border-color: hsla(210,8%,5%,.6); background: -moz-linear-gradient(hsla(220,6%,10%,.3), hsla(212,7%,57%,.15) 65%, hsla(212,7%,57%,.3)); box-shadow: 0 0 3px hsla(210,8%,5%,.25) inset, 0 1px 3px hsla(210,8%,5%,.25) inset, 0 1px 0 hsla(210,16%,76%,.15); } .devtools-toolbarbutton[checked] { color: hsl(208,100%,60%) !important;
--- a/browser/themes/gnomestripe/devtools/csshtmltree.css +++ b/browser/themes/gnomestripe/devtools/csshtmltree.css @@ -40,21 +40,16 @@ * ***** END LICENSE BLOCK ***** */ :root { -moz-appearance: none; background: -moz-Field; color: -moz-FieldText; } -#root { - display: -moz-box; -} - - .property-header { padding: 5px 0; white-space: nowrap; vertical-align: text-top; } /* Take away these two :visited rules to get a core dumper */ /* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */ @@ -68,17 +63,16 @@ .helplink:visited { text-decoration: none; } .link:hover { text-decoration: underline; } .helplink { - display: block; height: 14px; width: 0; overflow: hidden; -moz-padding-start: 14px; background-image: url("chrome://browser/skin/devtools/goto-mdn.png"); -moz-margin-end: 2px; cursor: pointer; } @@ -92,54 +86,44 @@ padding: 0; } .expander { -moz-appearance: treetwisty; padding-top: 12px; -moz-margin-start: 5px; -moz-margin-end: 5px; - display: inline-block; vertical-align: middle; } .expander[open] { -moz-appearance: treetwistyopen; } -.expandable { - cursor: pointer; -} - .match { visibility: hidden; } -.expandable > .property-header > .match { +.expandable { + cursor: pointer; visibility: visible; } .property-name { font-size: 12px; color: -moz-FieldText; - display: inline-block; } .property-value { padding: 0; font-size: 10px; color: grey; vertical-align: text-top; width: 100%; } -.property-view-hidden, -.property-content-hidden { - display: none; -} - .rule-link { text-align: end; -moz-padding-start: 10px; cursor: pointer; } /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */ .rule-text { @@ -155,20 +139,16 @@ .matched { text-decoration: line-through; } .parentmatch { color: #666; } #propertyContainer { - display: -moz-box; - -moz-box-orient: vertical; - -moz-box-flex: 1; - overflow-y: auto; border-collapse: collapse; } .darkrow { background-color: rgba(0,0,0,.022); } #noResults { @@ -215,46 +195,45 @@ } .ruleview-code { padding: 2px 5px; } .ruleview-warning { background: url("chrome://browser/skin/devtools/alerticon-warning.png"); - display: inline-block; -moz-margin-start: 5px; vertical-align: middle; width: 13px; height: 12px; } -.ruleview-warning[hidden] { - display: none; +.ruleview-ruleopen { + -moz-padding-end: 5px; } -.ruleview-ruleopen { - -moz-padding-end: 5px; +.ruleview-ruleclose { + width: -moz-min-content; + padding-right: 20px; } .ruleview-propertylist { list-style: none; padding: 0; margin: 0; } .ruleview-enableproperty { height: 10px; width: 10px; -moz-margin-start: 2px; -moz-margin-end: 0; } .ruleview-expander { - display: inline-block; width: 8px; height: 8px; background: url("chrome://browser/skin/devtools/arrows.png") 24px 0; cursor: pointer; -moz-margin-start: 2px; -moz-margin-end: 5px; } @@ -263,17 +242,16 @@ } .ruleview-newproperty { /* (enable checkbox width: 12px) + (expander width: 15px) */ -moz-margin-start: 27px; } .ruleview-propertyname { - display: inline-block; padding: 1px 0; cursor: text; color: #0060C0; text-decoration: inherit; } .ruleview-propertyvalue { cursor: text;
new file mode 100644 index 0000000000000000000000000000000000000000..ebfd9586d201416665b64e4fe57af05c1a967edb GIT binary patch literal 1321 zc$@(#1=jkBP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyS_ z05usk)smk800gN?L_t(Y$K{l1Y*a-6$A9zQUOjeu&=$I-(1MhZQV@|K5n=%`MvM?t zA|`@dO7J!qFc>BJ$;23=h#Yc<s2CDN3J3&>fIzuKkaEc(bPMg$?RMGSw{PBi;|CV9 z7z})OKEBCs<~RTOzk&bz5CHDHpa&=b@&Wff7zwlk5g84c0IXFykPT!3`aO6XNCFAQ z3)_|cSh#q>m(xc-^<>rkSl4aZa^{tpZ!Q6@0?>LXcW71c^IWfc%Kh*+QgYdzGog2Z z=q-WbQKLqdTecIsp9MyZ8d(k$-x3g!=76GX;?;zGU$N)tVxovN&r=x!Ihv-Y0A8Lp zBeG*>ePcT6%;6p}ytdDZZx<G5nr;I*w`Za)SuxXbDd5_lhd&MPUSE$itUGC+t7zER z+RoJjXUA+@L|d`|Tp3-k0Cw!GZ%~Q@0yG4Ez*35MdM*Zx%<Rw2Ta`juH}`8jao{KI z*tWB(xI$3T&qFHdh%@_5jF@Q6>)hU{cmLV9m`njOP8<QkLQuZ9e%q7{b(@=(EM2~@ ztu1mzK}#Z$l-b!?sI>5HSf=s3l6Km!U3X@!EgW1?vN3L{_2=WPN=Q2_4sK{OP16uU z_(&xk0PSA_Qh;N3gcMNNsi>GYX1>~E_l~VEe*EFwm&!^@ONS1B{OsyA-(5|M-XS2R z1h?0*J3>l?FZ;uZR4<{dd3|!NhHAh1#NTT`$(8-vC&j~8*AQ)q|5pNDcV18jyk4)z zv>j#o{eCfJ^2DMEFO2W}bKRznufJJ*3NYPyK}|+O4uCYx+jq@crLljVH)YDKWD0aa zQMOCGRasUt%_z$IC3Nw^QgZSoYn8}Mzy!=O&y4N|Q~?1^(@elrkg^>W^LV|{=bszr zTC-|d^_*Gnbj>_~DJ7<q1bg;=npC0$pWkbkQZuC_X-cjqOqy?)Ifl~4l~wlqs$$U4 z!Q%8j{bmvJf`iUA{!#1kcyQ@jwh+Qq^U&b)M;d-B%8Yr(&U$UuoVm$x`1)bsFCc71 zLzZ}Lb+16*M@|B+LZ6OD7yRAaI@%K`sLSM?RPuQ8V&i8{^SPrK@er|a`>g~b%T}&7 z0cqJzR9g0R%d)+Iv@FY-zhIGOS(Y`t_Az_dqeCnpgd(C8cKSDro6)unQcAAH3@*1P zabgi)x))9=(y~=b>A2kP>in)1-IUwc%ISSEacHA+Gl4)b0Q5l674QM=MV&hNk2LHp zdvDHM&ksMYYo0oJQrSlzyl)srGHd#b*N^?Wd+#0@4J81yAro@&<(A{}l%VS#gdTHO zJseQoYqmPk@N(zGuJ85!Bc_Vng7JkuJ)UyJvk^<*$=kh69YA>fGTrJczy+YGw6u## z8b)B@;-x2096$Q|F{698@-8(seNzH*wrt&g#!1Bt0Ij3V7R_g4qUlttxO}o%v^BN5 z9~kJ7?ku4WZk$ik=?f|zJ*GNW4ltCc%oettjD)waYlReddn#RdK@CL!$OUdFxFMjR zpfJDxxUpkOmo8t~2sB#JQ2cJZ9taBc;1jtdV>w78A<iF7sOs8)sHo}dR1O#`&K+7r fEPTn?{Dtuko9Z^s$RVN?00000NkvXXu0mjf0U~aI
--- a/browser/themes/gnomestripe/jar.mn +++ b/browser/themes/gnomestripe/jar.mn @@ -39,18 +39,18 @@ browser.jar: skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png) skin/classic/browser/feeds/videoFeedIcon.png (feeds/feedIcon.png) skin/classic/browser/feeds/videoFeedIcon16.png (feeds/feedIcon16.png) skin/classic/browser/feeds/audioFeedIcon.png (feeds/feedIcon.png) skin/classic/browser/feeds/audioFeedIcon16.png (feeds/feedIcon16.png) skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css) skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css) skin/classic/browser/newtab/newTab.css (newtab/newTab.css) - skin/classic/browser/newtab/strip.png (newtab/strip.png) - skin/classic/browser/newtab/toolbar.png (newtab/toolbar.png) + skin/classic/browser/newtab/controls.png (newtab/controls.png) + skin/classic/browser/newtab/noise.png (newtab/noise.png) skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png) skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png) skin/classic/browser/places/calendar.png (places/calendar.png) * skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css) skin/classic/browser/places/livemark-item.png (places/livemark-item.png) skin/classic/browser/places/pageStarred.png (places/pageStarred.png) skin/classic/browser/places/starred48.png (places/starred48.png) skin/classic/browser/places/unstarred48.png (places/unstarred48.png) @@ -124,23 +124,26 @@ browser.jar: skin/classic/browser/devtools/breadcrumbs/rtl-start-selected.png (devtools/breadcrumbs/rtl-start-selected.png) skin/classic/browser/devtools/splitview.css (devtools/splitview.css) skin/classic/browser/devtools/styleeditor.css (devtools/styleeditor.css) skin/classic/browser/devtools/debugger.css (devtools/debugger.css) skin/classic/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png) skin/classic/browser/devtools/itemToggle.png (devtools/itemToggle.png) skin/classic/browser/devtools/itemArrow-rtl.png (devtools/itemArrow-rtl.png) skin/classic/browser/devtools/itemArrow-ltr.png (devtools/itemArrow-ltr.png) + skin/classic/browser/devtools/inspect-button.png (devtools/inspect-button.png) #ifdef MOZ_SERVICES_SYNC skin/classic/browser/sync-16-throbber.png skin/classic/browser/sync-16.png skin/classic/browser/sync-24-throbber.png skin/classic/browser/sync-32.png skin/classic/browser/sync-bg.png skin/classic/browser/sync-128.png skin/classic/browser/sync-desktopIcon.png skin/classic/browser/sync-mobileIcon.png skin/classic/browser/sync-notification-24.png skin/classic/browser/syncSetup.css skin/classic/browser/syncCommon.css skin/classic/browser/syncQuota.css skin/classic/browser/syncProgress.css #endif + skin/classic/browser/webapps-16.png + skin/classic/browser/webapps-64.png
new file mode 100644 index 0000000000000000000000000000000000000000..14f382fbdd18a1209f3dcd63831014b5ad2fc428 GIT binary patch literal 4180 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
--- a/browser/themes/gnomestripe/newtab/newTab.css +++ b/browser/themes/gnomestripe/newtab/newTab.css @@ -1,148 +1,131 @@ -#scrollbox { - padding-bottom: 18px; - background-color: #fff; -} - -#body { - padding-top: 106px; - font-family: sans-serif; -} - -.button { - padding: 0; - border: 0 none; +:root { + -moz-appearance: none; + background-color: transparent; } -/* TOOLBAR */ -#toolbar { - top: 8px; - right: 8px; - width: 13px; - height: 30px; - padding: 0; - margin: 0; -} - -.toolbar-button { - background: transparent url(chrome://browser/skin/newtab/toolbar.png); -} - -#toolbar-button-show { - width: 11px; - height: 11px; - background-position: -10px 0; -} - -#toolbar-button-show:hover { - background-position: -10px -12px; +/* SCROLLBOX */ +#newtab-scrollbox:not([page-disabled]) { + background-color: rgb(229,229,229); + background-image: url(chrome://browser/skin/newtab/noise.png), + -moz-linear-gradient(rgba(255,255,255,.5), rgba(255,255,255,.2)); + background-attachment: fixed; } -#toolbar-button-show:active { - background-position: -10px -24px; -} - -#toolbar-button-hide { - width: 10px; - height: 10px; -} - -#toolbar-button-hide:hover { - background-position: 0 -12px; -} - -#toolbar-button-hide:active { - background-position: 0 -24px; +/* TOGGLE */ +#newtab-toggle { + width: 16px; + height: 16px; + padding: 0; + border: none; + background: -216px 0 transparent url(chrome://browser/skin/newtab/controls.png); } -#toolbar-button-reset { - top: 17px; - width: 11px; - height: 12px; -} - -#toolbar-button-reset { - background-position: -21px 0; +#newtab-toggle[page-disabled] { + background-position: -232px 0; } -#toolbar-button-reset:hover { - background-position: -21px -12px; +/* ROWS */ +.newtab-row { + margin-bottom: 20px; } -#toolbar-button-reset:active { - background-position: -21px -24px; -} - -/* GRID */ -#grid { - padding: 1px; - margin: 0 auto; +.newtab-row:last-child { + margin-bottom: 0; } /* CELLS */ -.cell { - outline: 1px dashed #ccc; - outline-offset: -1px; +.newtab-cell { + -moz-margin-end: 20px; + background-color: rgba(255,255,255,.2); + border: 1px solid; + border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16); + border-radius: 1px; + -moz-transition: border-color 100ms ease-out; +} + +.newtab-cell:empty { + border: 1px dashed; + border-color: rgba(8,22,37,.15) rgba(8,22,37,.17) rgba(8,22,37,.19); +} + +.newtab-cell:last-child { + -moz-margin-end: 0; +} + +.newtab-cell:hover:not(:empty) { + border-color: rgba(8,22,37,.25) rgba(8,22,37,.27) rgba(8,22,37,.3); } /* SITES */ -.site { - background-color: #ececec; - -moz-transition: 200ms ease-out; - -moz-transition-property: top, left, box-shadow, opacity; +.newtab-site { + text-decoration: none; + -moz-transition-property: top, left, opacity, box-shadow, background-color; } -.site[dragged] { - -moz-transition-property: box-shadow; +.newtab-site:hover, +.newtab-site[dragged] { + box-shadow: 0 0 10px rgba(8,22,37,.3); +} + +.newtab-site[dragged] { + -moz-transition-property: box-shadow, background-color; + background-color: rgb(242,242,242); } -.site[ontop] { - box-shadow: 0 1px 4px #000; - outline: none; +/* THUMBNAILS */ +.newtab-thumbnail { + background-origin: padding-box; + background-clip: padding-box; + background-repeat: no-repeat; + background-size: cover; } -/* SITE TITLE */ -.site-title { - height: 2.4em; - width: 189px; - padding: 0 6px; - background-color: rgba(0,0,0,0.5); - border: solid transparent; - border-width: 6px 0; - color: #fff; - text-decoration: none; - line-height: 1.2em; - font-weight: 700; +/* TITLES */ +.newtab-title { + padding: 0 8px; + background-color: rgba(248,249,251,.95); + color: #1f364c; + font-size: 12px; + line-height: 24px; } -/* SITE STRIP */ -.site-strip { - padding: 4px 3px; - background-color: rgba(0,0,0,0.5); +/* CONTROLS */ +.newtab-control { + width: 24px; + height: 24px; + padding: 1px 2px 3px; + border: none; + background: transparent url(chrome://browser/skin/newtab/controls.png); } -.strip-button { - width: 17px; - height: 17px; - background: transparent url(chrome://browser/skin/newtab/strip.png); +.newtab-control-pin:hover { + background-position: -24px 0; } -.strip-button-pin:hover { - background-position: 0 -17px; +.newtab-control-pin:active { + background-position: -48px 0; } -.strip-button-pin:active, -.site[pinned] .strip-button-pin { - background-position: 0 -34px; +.newtab-control-pin[pinned] { + background-position: -72px 0; +} + +.newtab-control-pin[pinned]:hover { + background-position: -96px 0; +} + +.newtab-control-pin[pinned]:active { + background-position: -120px 0; } -.strip-button-block { - background-position: -17px 0; +.newtab-control-block { + background-position: -144px 0; } -.strip-button-block:hover { - background-position: -17px -17px; +.newtab-control-block:hover { + background-position: -168px 0; } -.strip-button-block:active { - background-position: -17px -34px; +.newtab-control-block:active { + background-position: -192px 0; }
new file mode 100644 index 0000000000000000000000000000000000000000..01d340aaa9095a54d19071516cf9e0ca75b1f72e GIT binary patch literal 2118 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
deleted file mode 100644 index 2527df6e72b5c80b91e180af9644ba96a5c148d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
deleted file mode 100644 index 389f689aa2a7611f53977594c048f79c4991affd..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
new file mode 100644 index 0000000000000000000000000000000000000000..cf59fa3ca1e903968f793d940b2dc651f6ac4e1f GIT binary patch literal 366 zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgg)Zhm0~jSb$HZUBYedAc};Se!n4 zX}y<Apa9#4`Sk&U%?dhM@$w0glU-SErd(CXGt9sEK>CGrL^$uhrbNRVc_tAd0UIN9 z!UW4s$5_Oi6ZN$+n3K3Ke_!#whaavRPJEf0EdRcuJC3_O;y~=&2yT4|w|U8ImtUy= z4d1f*aCZgQA=76@;%AZs=A`R9kK6c>`S$D51AZ&&6Lg!`AB$_dv9_uANb=0>oN*~1 zmaaOXvQEf}o!#P4+9mk}Em^I{+~yWXCC(KHepTJpbpJ^DT{*c&(|%j@7Ydbj+Ad>$ zEZUf-Q>dzZk0<B7btBW~7ph|3v-ck8x?l6v?}>e%pyf;J<ieGucY!`<@O1TaS?83{ F1OTL^jTis`
new file mode 100644 index 0000000000000000000000000000000000000000..442132d987b04e37e7cd24b45785e7b605be2e32 GIT binary patch literal 1096 zc$@)91h@N%P)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipq| z6AUK%MzGTW00YQLL_t(|+U=T2XcR#dhQGKVLPl|mOI(vk3}`@&UKA9gf(pUq;sV}; zfPxnxdXV695l<e>K@V<-1{4K7=tV(66csnz1w{l`P-Db}nD&rrcotnV-96pa6R97X z>8|B%UG?g{*8>g?4h{|u4h{~9U?y+`s7!(O2;cxPzZ4bE0p|fvT%EuPpb6*`lkQUB zDDXw2eHj>5D$4H{f_00_)2_$BE?@@GKY&g@pdM%i?gz~8)_y(UP?m~s3sBt4Be;Kk zRYuTJ2Ye_n*Tiq9O~uy%T_J?j`~Co{L(p#me#W5h0UOd(d@k@Vf?%?LM~W*<m*q3C zI7!7P0rx^GuJ@l-d%GAMi^=<m+E%eKLG$cUnO7zF5-NeaF=e>~)Wl)B9C#jL8OpXF zz_Jv#;R5AE^BF>y^EI%x7^d~WHN#OS8MRn+#G6CNa~o(#lIb|$On}7?XLuC11;}#& zs7^cTJEZpH=n@FTh5|Q$c6IMy1LeTp5|U?&&DGdXMRgwVTSabJVFvJC?Ejoi<-4^+ zXT0SVc*$@a=4>l3?~hwhevg57szxv=ZwfxPq@ehkh(TG-mhvV6Iufeh)B-PKhQT?T z%A16^2@H?HyDxAyg5rrAVN6WsCPCUF@ZKIF6%NBYRx(r#JW9w2a_;OrJST>%2t`r; z6)?4!ZLl`Mw{a>$e4m8!%}ILU<3;Yri-J}&JtcG>v(&QJdBxrL=h8ZZ>wu3jJo0rx zqfxcVf=P{PlQqNh5qS7xI+Za~M@z`ctp_|5^BP3IOj#$3_9deIN=P0aJtcRm7yw)? z5S6_W<ELoC2N}ShH%Ii{EfDqH0|whvyuViHT%^SczHpz!n6UxEn+)LhPZRww3{H4e zK-^*l9<W|(+sD73#e57gygAbrkYgr^F>f({5o%KH*ff9#JOwJW>g@xdOg#_G<10)C z(8o&p_tjXL72XnKtsB6<_8LOje2y}o0rWZL0rXn~=2$m?+>^h7d1B130v^1O0ra^8 z)Y$2mOXHcX0R3ywn-FJs7z0QepXvEjo^MrUW`IHkied~9MAt++R=S%c$_)3VN;mdD zH@<1BAuCAU9X1Ux2-qavi0wV-^Yd53M%&S##KD+ZViX@>@&^Yf5sU)vL^MQH14x>8 zfRU6S76RWx8Y1d3=!e*i^a5-RacqT&1he}5o*3E_$OTT)%TWb9i;xcc<DO~RV6VqY zQ8&;_VsZs^0W0{Av0EuB)7sjs%34{Wf}TG=%jXIl92^`R92^`R?BOqH7L8M0{IIqF O0000<MNUMnLSTaKyWXDw
--- a/browser/themes/pinstripe/browser.css +++ b/browser/themes/pinstripe/browser.css @@ -2354,16 +2354,20 @@ toolbarbutton.chevron > .toolbarbutton-m #addons-notification-icon { list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric-16.png); } #password-notification-icon { list-style-image: url(chrome://mozapps/skin/passwordmgr/key-16.png); } +#webapps-notification-icon { + list-style-image: url(chrome://browser/skin/webapps-16.png); +} + .popup-notification-icon { width: 64px; height: 64px; -moz-margin-end: 10px; } .popup-notification-icon[popupid="geolocation"] { list-style-image: url(chrome://browser/skin/Geolocation-64.png); @@ -2420,16 +2424,20 @@ toolbarbutton.chevron > .toolbarbutton-m list-style-image: url(chrome://global/skin/icons/question-64.png); } .popup-notification-icon[popupid="password-save"], .popup-notification-icon[popupid="password-change"] { list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png); } +.popup-notification-icon[popupid="webapps-install"] { + list-style-image: url(chrome://browser/skin/webapps-64.png); +} + /* Popup Buttons */ #identity-popup-more-info-button { @hudButton@ margin: 10px 0 0; min-height: 0px; } #identity-popup-more-info-button:focus { @@ -2716,16 +2724,25 @@ panel[dimmed="true"] { #highlighter-veil-container[locked] > #highlighter-veil-middlebox > #highlighter-veil-transparentbox { box-shadow: 0 0 0 1px black; outline-color: white; } /* Highlighter toolbar */ +#inspector-inspect-toolbutton { + list-style-image: url("chrome://browser/skin/devtools/inspect-button.png"); + -moz-image-region: rect(0px 16px 16px 0px); +} + +#inspector-inspect-toolbutton[checked] { + -moz-image-region: rect(0px 32px 16px 16px); +} + #inspector-toolbar { border-top: 1px solid hsla(210, 8%, 5%, .65); padding-top: 4px; padding-bottom: 4px; } #inspector-toolbar:-moz-locale-dir(ltr) { padding-left: 2px;
--- a/browser/themes/pinstripe/devtools/common.css +++ b/browser/themes/pinstripe/devtools/common.css @@ -57,16 +57,20 @@ background: -moz-linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box; box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15); } .devtools-toolbarbutton > .toolbarbutton-text { margin: 1px 6px; } +.devtools-toolbarbutton:not([label]) { + min-width: 32px; +} + .devtools-toolbarbutton:not([checked]):hover:active { border-color: hsla(210,8%,5%,.6); background: -moz-linear-gradient(hsla(220,6%,10%,.3), hsla(212,7%,57%,.15) 65%, hsla(212,7%,57%,.3)); box-shadow: 0 0 3px hsla(210,8%,5%,.25) inset, 0 1px 3px hsla(210,8%,5%,.25) inset, 0 1px 0 hsla(210,16%,76%,.15); } .devtools-toolbarbutton[checked] { color: hsl(208,100%,60%) !important;
--- a/browser/themes/pinstripe/devtools/csshtmltree.css +++ b/browser/themes/pinstripe/devtools/csshtmltree.css @@ -40,21 +40,16 @@ * ***** END LICENSE BLOCK ***** */ :root { -moz-appearance: none; background: -moz-Field; color: -moz-FieldText; } -#root { - display: -moz-box; -} - - .property-header { padding: 5px 0; white-space: nowrap; vertical-align: text-top; } /* Take away these two :visited rules to get a core dumper */ /* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */ @@ -68,17 +63,16 @@ .helplink:visited { text-decoration: none; } .link:hover { text-decoration: underline; } .helplink { - display: block; height: 14px; width: 0; overflow: hidden; -moz-padding-start: 14px; background-image: url("chrome://browser/skin/devtools/goto-mdn.png"); -moz-margin-end: 2px; cursor: pointer; } @@ -94,54 +88,44 @@ .expander { -moz-appearance: treetwisty; width: 12px; height: 12px; padding-top: 12px; -moz-margin-start: 5px; -moz-margin-end: 5px; - display: inline-block; vertical-align: middle; } .expander[open] { -moz-appearance: treetwistyopen; } -.expandable { - cursor: pointer; -} - .match { visibility: hidden; } -.expandable > .property-header > .match { +.expandable { + cursor: pointer; visibility: visible; } .property-name { font-size: 12px; color: -moz-FieldText; - display: inline-block; } .property-value { padding: 0; font-size: 10px; color: grey; vertical-align: text-top; width: 100%; } -.property-view-hidden, -.property-content-hidden { - display: none; -} - .rule-link { text-align: end; -moz-padding-start: 10px; cursor: pointer; } /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */ .rule-text { @@ -157,20 +141,16 @@ .matched { text-decoration: line-through; } .parentmatch { color: #666; } #propertyContainer { - display: -moz-box; - -moz-box-orient: vertical; - -moz-box-flex: 1; - overflow-y: auto; border-collapse: collapse; } .darkrow { background-color: rgba(0,0,0,.022); } #noResults { @@ -217,46 +197,45 @@ } .ruleview-code { padding: 2px 5px; } .ruleview-warning { background: url("chrome://browser/skin/devtools/alerticon-warning.png"); - display: inline-block; -moz-margin-start: 5px; vertical-align: middle; width: 13px; height: 12px; } -.ruleview-warning[hidden] { - display: none; +.ruleview-ruleopen { + -moz-padding-end: 5px; } -.ruleview-ruleopen { - -moz-padding-end: 5px; +.ruleview-ruleclose { + width: -moz-min-content; + padding-right: 20px; } .ruleview-propertylist { list-style: none; padding: 0; margin: 0; } .ruleview-enableproperty { height: 10px; width: 10px; -moz-margin-start: 2px; -moz-margin-end: 0; } .ruleview-expander { - display: inline-block; width: 8px; height: 8px; background: url("chrome://browser/skin/devtools/arrows.png") 24px 0; cursor: pointer; -moz-margin-start: 2px; -moz-margin-end: 5px; } @@ -265,17 +244,16 @@ } .ruleview-newproperty { /* (enable checkbox width: 12px) + (expander width: 15px) */ -moz-margin-start: 27px; } .ruleview-propertyname { - display: inline-block; padding: 1px 0; cursor: text; color: #0060C0; text-decoration: inherit; } .ruleview-propertyvalue { cursor: text;
new file mode 100644 index 0000000000000000000000000000000000000000..ebfd9586d201416665b64e4fe57af05c1a967edb GIT binary patch literal 1321 zc$@(#1=jkBP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyS_ z05usk)smk800gN?L_t(Y$K{l1Y*a-6$A9zQUOjeu&=$I-(1MhZQV@|K5n=%`MvM?t zA|`@dO7J!qFc>BJ$;23=h#Yc<s2CDN3J3&>fIzuKkaEc(bPMg$?RMGSw{PBi;|CV9 z7z})OKEBCs<~RTOzk&bz5CHDHpa&=b@&Wff7zwlk5g84c0IXFykPT!3`aO6XNCFAQ z3)_|cSh#q>m(xc-^<>rkSl4aZa^{tpZ!Q6@0?>LXcW71c^IWfc%Kh*+QgYdzGog2Z z=q-WbQKLqdTecIsp9MyZ8d(k$-x3g!=76GX;?;zGU$N)tVxovN&r=x!Ihv-Y0A8Lp zBeG*>ePcT6%;6p}ytdDZZx<G5nr;I*w`Za)SuxXbDd5_lhd&MPUSE$itUGC+t7zER z+RoJjXUA+@L|d`|Tp3-k0Cw!GZ%~Q@0yG4Ez*35MdM*Zx%<Rw2Ta`juH}`8jao{KI z*tWB(xI$3T&qFHdh%@_5jF@Q6>)hU{cmLV9m`njOP8<QkLQuZ9e%q7{b(@=(EM2~@ ztu1mzK}#Z$l-b!?sI>5HSf=s3l6Km!U3X@!EgW1?vN3L{_2=WPN=Q2_4sK{OP16uU z_(&xk0PSA_Qh;N3gcMNNsi>GYX1>~E_l~VEe*EFwm&!^@ONS1B{OsyA-(5|M-XS2R z1h?0*J3>l?FZ;uZR4<{dd3|!NhHAh1#NTT`$(8-vC&j~8*AQ)q|5pNDcV18jyk4)z zv>j#o{eCfJ^2DMEFO2W}bKRznufJJ*3NYPyK}|+O4uCYx+jq@crLljVH)YDKWD0aa zQMOCGRasUt%_z$IC3Nw^QgZSoYn8}Mzy!=O&y4N|Q~?1^(@elrkg^>W^LV|{=bszr zTC-|d^_*Gnbj>_~DJ7<q1bg;=npC0$pWkbkQZuC_X-cjqOqy?)Ifl~4l~wlqs$$U4 z!Q%8j{bmvJf`iUA{!#1kcyQ@jwh+Qq^U&b)M;d-B%8Yr(&U$UuoVm$x`1)bsFCc71 zLzZ}Lb+16*M@|B+LZ6OD7yRAaI@%K`sLSM?RPuQ8V&i8{^SPrK@er|a`>g~b%T}&7 z0cqJzR9g0R%d)+Iv@FY-zhIGOS(Y`t_Az_dqeCnpgd(C8cKSDro6)unQcAAH3@*1P zabgi)x))9=(y~=b>A2kP>in)1-IUwc%ISSEacHA+Gl4)b0Q5l674QM=MV&hNk2LHp zdvDHM&ksMYYo0oJQrSlzyl)srGHd#b*N^?Wd+#0@4J81yAro@&<(A{}l%VS#gdTHO zJseQoYqmPk@N(zGuJ85!Bc_Vng7JkuJ)UyJvk^<*$=kh69YA>fGTrJczy+YGw6u## z8b)B@;-x2096$Q|F{698@-8(seNzH*wrt&g#!1Bt0Ij3V7R_g4qUlttxO}o%v^BN5 z9~kJ7?ku4WZk$ik=?f|zJ*GNW4ltCc%oettjD)waYlReddn#RdK@CL!$OUdFxFMjR zpfJDxxUpkOmo8t~2sB#JQ2cJZ9taBc;1jtdV>w78A<iF7sOs8)sHo}dR1O#`&K+7r fEPTn?{Dtuko9Z^s$RVN?00000NkvXXu0mjf0U~aI
--- a/browser/themes/pinstripe/jar.mn +++ b/browser/themes/pinstripe/jar.mn @@ -49,18 +49,18 @@ browser.jar: skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css) skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png) skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png) skin/classic/browser/feeds/videoFeedIcon.png (feeds/feedIcon.png) skin/classic/browser/feeds/videoFeedIcon16.png (feeds/feedIcon16.png) skin/classic/browser/feeds/audioFeedIcon.png (feeds/feedIcon.png) skin/classic/browser/feeds/audioFeedIcon16.png (feeds/feedIcon16.png) skin/classic/browser/newtab/newTab.css (newtab/newTab.css) - skin/classic/browser/newtab/strip.png (newtab/strip.png) - skin/classic/browser/newtab/toolbar.png (newtab/toolbar.png) + skin/classic/browser/newtab/controls.png (newtab/controls.png) + skin/classic/browser/newtab/noise.png (newtab/noise.png) skin/classic/browser/setDesktopBackground.css skin/classic/browser/monitor.png skin/classic/browser/monitor_16-10.png skin/classic/browser/places/allBookmarks.png (places/allBookmarks.png) * skin/classic/browser/places/places.css (places/places.css) * skin/classic/browser/places/organizer.css (places/organizer.css) skin/classic/browser/places/query.png (places/query.png) skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png) @@ -165,16 +165,17 @@ browser.jar: skin/classic/browser/devtools/splitview.css (devtools/splitview.css) skin/classic/browser/devtools/styleeditor.css (devtools/styleeditor.css) skin/classic/browser/devtools/debugger.css (devtools/debugger.css) skin/classic/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png) skin/classic/browser/devtools/itemToggle.png (devtools/itemToggle.png) skin/classic/browser/devtools/itemArrow-rtl.png (devtools/itemArrow-rtl.png) skin/classic/browser/devtools/itemArrow-ltr.png (devtools/itemArrow-ltr.png) skin/classic/browser/devtools/background-noise-toolbar.png (devtools/background-noise-toolbar.png) + skin/classic/browser/devtools/inspect-button.png (devtools/inspect-button.png) #ifdef MOZ_SERVICES_SYNC skin/classic/browser/sync-throbber.png skin/classic/browser/sync-16.png skin/classic/browser/sync-32.png skin/classic/browser/sync-bg.png skin/classic/browser/sync-128.png skin/classic/browser/sync-desktopIcon.png skin/classic/browser/sync-mobileIcon.png @@ -185,15 +186,17 @@ browser.jar: skin/classic/browser/syncProgress.css #endif skin/classic/browser/lion/keyhole-circle.png (keyhole-circle-lion.png) skin/classic/browser/lion/Toolbar.png (Toolbar-lion.png) skin/classic/browser/lion/toolbarbutton-dropmarker.png (toolbarbutton-dropmarker-lion.png) skin/classic/browser/lion/tabbrowser/alltabs-box-bkgnd-icon.png (tabbrowser/alltabs-box-bkgnd-icon-lion.png) skin/classic/browser/lion/tabview/tabview.png (tabview/tabview-lion.png) skin/classic/browser/lion/places/toolbar.png (places/toolbar-lion.png) + skin/classic/browser/webapps-16.png + skin/classic/browser/webapps-64.png % override chrome://browser/skin/keyhole-circle.png chrome://browser/skin/lion/keyhole-circle.png os=Darwin osversion>=10.7 % override chrome://browser/skin/Toolbar.png chrome://browser/skin/lion/Toolbar.png os=Darwin osversion>=10.7 % override chrome://browser/skin/toolbarbutton-dropmarker.png chrome://browser/skin/lion/toolbarbutton-dropmarker.png os=Darwin osversion>=10.7 % override chrome://browser/skin/tabbrowser/alltabs-box-bkgnd-icon.png chrome://browser/skin/lion/tabbrowser/alltabs-box-bkgnd-icon.png os=Darwin osversion>=10.7 % override chrome://browser/skin/tabview/tabview.png chrome://browser/skin/lion/tabview/tabview.png os=Darwin osversion>=10.7 % override chrome://browser/skin/places/toolbar.png chrome://browser/skin/lion/places/toolbar.png os=Darwin osversion>=10.7
new file mode 100644 index 0000000000000000000000000000000000000000..14f382fbdd18a1209f3dcd63831014b5ad2fc428 GIT binary patch literal 4180 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
--- a/browser/themes/pinstripe/newtab/newTab.css +++ b/browser/themes/pinstripe/newtab/newTab.css @@ -1,147 +1,131 @@ -#scrollbox { - padding-bottom: 18px; - background-color: #fff; -} - -#body { - padding-top: 106px; - font-family: sans-serif; -} - -.button { - padding: 0; - border: 0 none; +:root { + -moz-appearance: none; + background-color: transparent; } -/* TOOLBAR */ -#toolbar { - top: 8px; - right: 8px; - width: 13px; - height: 30px; - padding: 0; - margin: 0; -} - -.toolbar-button { - background: transparent url(chrome://browser/skin/newtab/toolbar.png); -} - -#toolbar-button-show { - width: 11px; - height: 11px; - background-position: -10px 0; -} - -#toolbar-button-show:hover { - background-position: -10px -12px; +/* SCROLLBOX */ +#newtab-scrollbox:not([page-disabled]) { + background-color: rgb(229,229,229); + background-image: url(chrome://browser/skin/newtab/noise.png), + -moz-linear-gradient(rgba(255,255,255,.5), rgba(255,255,255,.2)); + background-attachment: fixed; } -#toolbar-button-show:active { - background-position: -10px -24px; -} - -#toolbar-button-hide { - width: 10px; - height: 10px; -} - -#toolbar-button-hide:hover { - background-position: 0 -12px; -} - -#toolbar-button-hide:active { - background-position: 0 -24px; +/* TOGGLE */ +#newtab-toggle { + width: 16px; + height: 16px; + padding: 0; + border: none; + background: -216px 0 transparent url(chrome://browser/skin/newtab/controls.png); } -#toolbar-button-reset { - top: 17px; - width: 11px; - height: 12px; -} - -#toolbar-button-reset { - background-position: -21px 0; +#newtab-toggle[page-disabled] { + background-position: -232px 0; } -#toolbar-button-reset:hover { - background-position: -21px -12px; +/* ROWS */ +.newtab-row { + margin-bottom: 20px; } -#toolbar-button-reset:active { - background-position: -21px -24px; -} - -/* GRID */ -#grid { - padding: 1px; - margin: 0 auto; +.newtab-row:last-child { + margin-bottom: 0; } /* CELLS */ -.cell { - outline: 1px dashed #ccc; - outline-offset: -1px; +.newtab-cell { + -moz-margin-end: 20px; + background-color: rgba(255,255,255,.2); + border: 1px solid; + border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16); + border-radius: 1px; + -moz-transition: border-color 100ms ease-out; +} + +.newtab-cell:empty { + border: 1px dashed; + border-color: rgba(8,22,37,.15) rgba(8,22,37,.17) rgba(8,22,37,.19); +} + +.newtab-cell:last-child { + -moz-margin-end: 0; +} + +.newtab-cell:hover:not(:empty) { + border-color: rgba(8,22,37,.25) rgba(8,22,37,.27) rgba(8,22,37,.3); } /* SITES */ -.site { - background-color: #ececec; - -moz-transition: 200ms ease-out; - -moz-transition-property: top, left, box-shadow, opacity; +.newtab-site { + text-decoration: none; + -moz-transition-property: top, left, opacity, box-shadow, background-color; } -.site[dragged] { - -moz-transition-property: box-shadow; +.newtab-site:hover, +.newtab-site[dragged] { + box-shadow: 0 0 10px rgba(8,22,37,.3); +} + +.newtab-site[dragged] { + -moz-transition-property: box-shadow, background-color; + background-color: rgb(242,242,242); } -.site[ontop] { - box-shadow: 0 1px 4px #000; +/* THUMBNAILS */ +.newtab-thumbnail { + background-origin: padding-box; + background-clip: padding-box; + background-repeat: no-repeat; + background-size: cover; } -/* SITE TITLE */ -.site-title { - height: 2.4em; - width: 189px; - padding: 0 6px; - background-color: rgba(0,0,0,0.5); - border: solid transparent; - border-width: 6px 0; - color: #fff; - text-decoration: none; - line-height: 1.2em; - font-weight: 700; +/* TITLES */ +.newtab-title { + padding: 0 8px; + background-color: rgba(248,249,251,.95); + color: #1f364c; + font-size: 12px; + line-height: 24px; } -/* SITE STRIP */ -.site-strip { - padding: 4px 3px; - background-color: rgba(0,0,0,0.5); +/* CONTROLS */ +.newtab-control { + width: 24px; + height: 24px; + padding: 1px 2px 3px; + border: none; + background: transparent url(chrome://browser/skin/newtab/controls.png); } -.strip-button { - width: 17px; - height: 17px; - background: transparent url(chrome://browser/skin/newtab/strip.png); +.newtab-control-pin:hover { + background-position: -24px 0; } -.strip-button-pin:hover { - background-position: 0 -17px; +.newtab-control-pin:active { + background-position: -48px 0; } -.strip-button-pin:active, -.site[pinned] .strip-button-pin { - background-position: 0 -34px; +.newtab-control-pin[pinned] { + background-position: -72px 0; +} + +.newtab-control-pin[pinned]:hover { + background-position: -96px 0; +} + +.newtab-control-pin[pinned]:active { + background-position: -120px 0; } -.strip-button-block { - background-position: -17px 0; +.newtab-control-block { + background-position: -144px 0; } -.strip-button-block:hover { - background-position: -17px -17px; +.newtab-control-block:hover { + background-position: -168px 0; } -.strip-button-block: