author | David Anderson <danderson@mozilla.com> |
Thu, 01 Mar 2012 13:45:48 -0800 | |
changeset 112209 | f977d96263c2bbf35d4308c576feb7cce22c665e |
parent 112208 | 7d6da2d655957746cf5ccbb3426b7ba65706634f (current diff) |
parent 90932 | 3a7b9e61c26342f4fa46deb7f37cc6f6b389c00a (diff) |
child 112210 | 3a1754a676440e0087bbbe9f20e3adfac0abbfeb |
push id | 1708 |
push user | akeybl@mozilla.com |
push date | Mon, 19 Nov 2012 21:10:21 +0000 |
treeherder | mozilla-beta@27b14fe50103 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 13.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/accessible/src/atk/nsMaiInterfaceText.cpp +++ b/accessible/src/atk/nsMaiInterfaceText.cpp @@ -38,16 +38,18 @@ * * ***** END LICENSE BLOCK ***** */ #include "nsMaiInterfaceText.h" #include "nsHyperTextAccessible.h" #include "nsRoleMap.h" +#include "nsIPersistentProperties2.h" + AtkAttributeSet* ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes); void textInterfaceInitCB(AtkTextIface *aIface) { NS_ASSERTION(aIface, "Invalid aIface"); if (!aIface) return;
--- a/accessible/src/base/StyleInfo.cpp +++ b/accessible/src/base/StyleInfo.cpp @@ -107,19 +107,27 @@ StyleInfo::Margin(css::Side aSide, nsASt aValue.Truncate(); nscoord coordVal = mElement->GetPrimaryFrame()->GetUsedMargin().Side(aSide); aValue.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(coordVal)); aValue.AppendLiteral("px"); } void -StyleInfo::Format(const nscolor& aValue, nsString& aFormattedValue) +StyleInfo::FormatColor(const nscolor& aValue, nsString& aFormattedValue) { // Combine the string like rgb(R, G, B) from nscolor. aFormattedValue.AppendLiteral("rgb("); aFormattedValue.AppendInt(NS_GET_R(aValue)); aFormattedValue.AppendLiteral(", "); aFormattedValue.AppendInt(NS_GET_G(aValue)); aFormattedValue.AppendLiteral(", "); aFormattedValue.AppendInt(NS_GET_B(aValue)); aFormattedValue.Append(')'); } + +void +StyleInfo::FormatFontStyle(const nscoord& aValue, nsAString& aFormattedValue) +{ + nsCSSKeyword keyword = + nsCSSProps::ValueToKeywordEnum(aValue, nsCSSProps::kFontStyleKTable); + AppendUTF8toUTF16(nsCSSKeywords::GetStringValue(keyword), aFormattedValue); +}
--- a/accessible/src/base/StyleInfo.h +++ b/accessible/src/base/StyleInfo.h @@ -55,17 +55,18 @@ public: void Display(nsAString& aValue); void TextAlign(nsAString& aValue); void TextIndent(nsAString& aValue); void MarginLeft(nsAString& aValue) { Margin(css::eSideLeft, aValue); } void MarginRight(nsAString& aValue) { Margin(css::eSideRight, aValue); } void MarginTop(nsAString& aValue) { Margin(css::eSideTop, aValue); } void MarginBottom(nsAString& aValue) { Margin(css::eSideBottom, aValue); } - static void Format(const nscolor& aValue, nsString& aFormattedValue); + static void FormatColor(const nscolor& aValue, nsString& aFormattedValue); + static void FormatFontStyle(const nscoord& aValue, nsAString& aFormattedValue); private: StyleInfo() MOZ_DELETE; StyleInfo(const StyleInfo&) MOZ_DELETE; StyleInfo& operator = (const StyleInfo&) MOZ_DELETE; void Margin(css::Side aSide, nsAString& aValue);
--- a/accessible/src/base/nsTextAttrs.cpp +++ b/accessible/src/base/nsTextAttrs.cpp @@ -69,18 +69,16 @@ struct nsCSSTextAttrMapItem * 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 - { "font-family", kAnyValue, &nsGkAtoms::font_family, kCopyValue }, - { "font-style", kAnyValue, &nsGkAtoms::font_style, kCopyValue }, { "text-decoration", "line-through", &nsGkAtoms::textLineThroughStyle, "solid" }, { "text-decoration", "underline", &nsGkAtoms::textUnderlineStyle, "solid" }, { "vertical-align", kAnyValue, &nsGkAtoms::textPosition, kCopyValue } }; //////////////////////////////////////////////////////////////////////////////// // nsTextAttrs @@ -150,53 +148,53 @@ nsTextAttrsMgr::GetAttributes(nsIPersist offsetElm = nsCoreUtils::GetDOMElementFor(offsetNode); frame = offsetElm->GetPrimaryFrame(); } nsTArray<nsITextAttr*> textAttrArray(10); // "language" text attribute nsLangTextAttr langTextAttr(mHyperTextAcc, hyperTextElm, offsetNode); - textAttrArray.AppendElement(static_cast<nsITextAttr*>(&langTextAttr)); - - // "font-family" text attribute - nsCSSTextAttr fontFamilyTextAttr(0, hyperTextElm, offsetElm); - textAttrArray.AppendElement(static_cast<nsITextAttr*>(&fontFamilyTextAttr)); - - // "font-style" text attribute - nsCSSTextAttr fontStyleTextAttr(1, hyperTextElm, offsetElm); - textAttrArray.AppendElement(static_cast<nsITextAttr*>(&fontStyleTextAttr)); + textAttrArray.AppendElement(&langTextAttr); // "text-line-through-style" text attribute - nsCSSTextAttr lineThroughTextAttr(2, hyperTextElm, offsetElm); - textAttrArray.AppendElement(static_cast<nsITextAttr*>(&lineThroughTextAttr)); + nsCSSTextAttr lineThroughTextAttr(0, hyperTextElm, offsetElm); + textAttrArray.AppendElement(&lineThroughTextAttr); // "text-underline-style" text attribute - nsCSSTextAttr underlineTextAttr(3, hyperTextElm, offsetElm); - textAttrArray.AppendElement(static_cast<nsITextAttr*>(&underlineTextAttr)); + nsCSSTextAttr underlineTextAttr(1, hyperTextElm, offsetElm); + textAttrArray.AppendElement(&underlineTextAttr); // "text-position" text attribute - nsCSSTextAttr posTextAttr(4, hyperTextElm, offsetElm); - textAttrArray.AppendElement(static_cast<nsITextAttr*>(&posTextAttr)); + nsCSSTextAttr posTextAttr(2, hyperTextElm, offsetElm); + textAttrArray.AppendElement(&posTextAttr); // "background-color" text attribute nsBGColorTextAttr bgColorTextAttr(rootFrame, frame); - textAttrArray.AppendElement(static_cast<nsITextAttr*>(&bgColorTextAttr)); + textAttrArray.AppendElement(&bgColorTextAttr); // "color" text attribute ColorTextAttr colorTextAttr(rootFrame, frame); - textAttrArray.AppendElement(static_cast<nsITextAttr*>(&colorTextAttr)); + textAttrArray.AppendElement(&colorTextAttr); + + // "font-family" text attribute + FontFamilyTextAttr fontFamilyTextAttr(rootFrame, frame); + textAttrArray.AppendElement(&fontFamilyTextAttr); // "font-size" text attribute nsFontSizeTextAttr fontSizeTextAttr(rootFrame, frame); - textAttrArray.AppendElement(static_cast<nsITextAttr*>(&fontSizeTextAttr)); + textAttrArray.AppendElement(&fontSizeTextAttr); + + // "font-style" text attribute + FontStyleTextAttr fontStyleTextAttr(rootFrame, frame); + textAttrArray.AppendElement(&fontStyleTextAttr); // "font-weight" text attribute nsFontWeightTextAttr fontWeightTextAttr(rootFrame, frame); - textAttrArray.AppendElement(static_cast<nsITextAttr*>(&fontWeightTextAttr)); + textAttrArray.AppendElement(&fontWeightTextAttr); // Expose text attributes if applicable. if (aAttributes) { PRUint32 len = textAttrArray.Length(); for (PRUint32 idx = 0; idx < len; idx++) { nsITextAttr *textAttr = textAttrArray[idx]; nsAutoString value; @@ -386,17 +384,17 @@ nsBGColorTextAttr::GetValueFor(nsIConten return GetColor(frame, aValue); } void nsBGColorTextAttr::Format(const nscolor& aValue, nsAString& aFormattedValue) { nsAutoString value; - StyleInfo::Format(aValue, value); + StyleInfo::FormatColor(aValue, value); aFormattedValue = value; } bool nsBGColorTextAttr::GetColor(nsIFrame *aFrame, nscolor *aColor) { const nsStyleBackground *styleBackground = aFrame->GetStyleBackground(); @@ -448,22 +446,66 @@ ColorTextAttr::GetValueFor(nsIContent* a return false; } void ColorTextAttr::Format(const nscolor& aValue, nsAString& aFormattedValue) { nsAutoString value; - StyleInfo::Format(aValue, value); + StyleInfo::FormatColor(aValue, value); aFormattedValue = value; } //////////////////////////////////////////////////////////////////////////////// +// FontFamilyTextAttr +//////////////////////////////////////////////////////////////////////////////// + +FontFamilyTextAttr::FontFamilyTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) : + nsTextAttr<nsAutoString>(aFrame == nsnull) +{ + mIsRootDefined = GetFontFamily(aRootFrame, mRootNativeValue); + + if (aFrame) + mIsDefined = GetFontFamily(aFrame, mNativeValue); +} + +bool +FontFamilyTextAttr::GetValueFor(nsIContent* aElm, nsAutoString* aValue) +{ + nsIFrame* frame = aElm->GetPrimaryFrame(); + if (!frame) + return false; + + return GetFontFamily(frame, *aValue); +} + +void +FontFamilyTextAttr::Format(const nsAutoString& aValue, + nsAString& aFormattedValue) +{ + aFormattedValue = aValue; +} + +bool +FontFamilyTextAttr::GetFontFamily(nsIFrame* aFrame, nsAutoString& aFamily) +{ + nsRefPtr<nsFontMetrics> fm; + nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm)); + + gfxFontGroup* fontGroup = fm->GetThebesFontGroup(); + gfxFont* font = fontGroup->GetFontAt(0); + gfxFontEntry* fontEntry = font->GetFontEntry(); + aFamily = fontEntry->FamilyName(); + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// // nsFontSizeTextAttr //////////////////////////////////////////////////////////////////////////////// nsFontSizeTextAttr::nsFontSizeTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame) : nsTextAttr<nscoord>(aFrame == nsnull) { mDC = aRootFrame->PresContext()->DeviceContext(); @@ -477,29 +519,29 @@ nsFontSizeTextAttr::nsFontSizeTextAttr(n } bool nsFontSizeTextAttr::GetValueFor(nsIContent *aContent, nscoord *aValue) { nsIFrame *frame = aContent->GetPrimaryFrame(); if (!frame) return false; - + *aValue = GetFontSize(frame); return true; } void nsFontSizeTextAttr::Format(const nscoord& aValue, nsAString& aFormattedValue) { // Convert from nscoord to pt. // // Note: according to IA2, "The conversion doesn't have to be exact. // The intent is to give the user a feel for the size of the text." - // + // // ATK does not specify a unit and will likely follow IA2 here. // // XXX todo: consider sharing this code with layout module? (bug 474621) float px = NSAppUnitsToFloatPixels(aValue, nsDeviceContext::AppUnitsPerCSSPixel()); // Each pt is 4/3 of a CSS pixel. int pts = NS_lround(px*3/4); @@ -512,16 +554,51 @@ nsFontSizeTextAttr::Format(const nscoord nscoord nsFontSizeTextAttr::GetFontSize(nsIFrame *aFrame) { return aFrame->GetStyleFont()->mSize; } //////////////////////////////////////////////////////////////////////////////// +// FontStyleTextAttr +//////////////////////////////////////////////////////////////////////////////// + +FontStyleTextAttr::FontStyleTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) : + nsTextAttr<nscoord>(!aFrame) +{ + mRootNativeValue = aRootFrame->GetStyleFont()->mFont.style; + mIsRootDefined = true; + + if (aFrame) { + mNativeValue = aFrame->GetStyleFont()->mFont.style; + mIsDefined = true; + } +} + +bool +FontStyleTextAttr::GetValueFor(nsIContent* aContent, nscoord* aValue) +{ + nsIFrame* frame = aContent->GetPrimaryFrame(); + if (frame) { + *aValue = frame->GetStyleFont()->mFont.style; + return true; + } + + return false; +} + +void +FontStyleTextAttr::Format(const nscoord& aValue, nsAString& aFormattedValue) +{ + StyleInfo::FormatFontStyle(aValue, aFormattedValue); +} + + +//////////////////////////////////////////////////////////////////////////////// // nsFontWeightTextAttr //////////////////////////////////////////////////////////////////////////////// nsFontWeightTextAttr::nsFontWeightTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame) : nsTextAttr<PRInt32>(aFrame == nsnull) { mRootNativeValue = GetFontWeight(aRootFrame);
--- a/accessible/src/base/nsTextAttrs.h +++ b/accessible/src/base/nsTextAttrs.h @@ -36,27 +36,20 @@ * * ***** END LICENSE BLOCK ***** */ #ifndef nsTextAttrs_h_ #define nsTextAttrs_h_ class nsHyperTextAccessible; - -#include "nsIDOMNode.h" -#include "nsIDOMElement.h" - #include "nsIContent.h" #include "nsIFrame.h" #include "nsIPersistentProperties2.h" -#include "nsCOMPtr.h" -#include "nsString.h" - class nsITextAttr; /** * Used to expose text attributes for the hyper text accessible (see * nsHyperTextAccessible class). It is indended for the work with 'language' and * CSS based text attributes. * * @note "invalid: spelling" text attrbiute is implemented entirerly in @@ -95,17 +88,16 @@ public: * @param aStartHTOffset [out, optional] start hyper text offset * @param aEndHTOffset [out, optional] end hyper text offset */ nsresult GetAttributes(nsIPersistentProperties *aAttributes, PRInt32 *aStartHTOffset = nsnull, PRInt32 *aEndHTOffset = nsnull); 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 aStartHTOffset [in, out] the start offset * @param aEndHTOffset [in, out] the end offset @@ -322,16 +314,40 @@ public: protected: // nsTextAttr virtual bool GetValueFor(nsIContent* aContent, nscolor* aValue); virtual void Format(const nscolor& aValue, nsAString& aFormattedValue); }; /** + * Class is used for the work with "font-family" text attribute in + * nsTextAttrsMgr class. + */ +class FontFamilyTextAttr : public nsTextAttr<nsAutoString> +{ +public: + FontFamilyTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame); + + // nsITextAttr + virtual nsIAtom* GetName() const { return nsGkAtoms::font_family; } + +protected: + + // nsTextAttr + virtual bool GetValueFor(nsIContent* aContent, nsAutoString* aValue); + virtual void Format(const nsAutoString& aValue, nsAString& aFormattedValue); + +private: + + bool GetFontFamily(nsIFrame* aFrame, nsAutoString& aFamily); +}; + + +/** * Class is used for the work with "font-size" text attribute in nsTextAttrsMgr * class. */ class nsFontSizeTextAttr : public nsTextAttr<nscoord> { public: nsFontSizeTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame); @@ -354,16 +370,36 @@ private: */ nscoord GetFontSize(nsIFrame *aFrame); nsDeviceContext *mDC; }; /** + * Class is used for the work with "font-style" text attribute in nsTextAttrsMgr + * class. + */ +class FontStyleTextAttr : public nsTextAttr<nscoord> +{ +public: + FontStyleTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame); + + // nsITextAttr + virtual nsIAtom* GetName() const { return nsGkAtoms::font_style; } + +protected: + + // nsTextAttr + virtual bool GetValueFor(nsIContent* aContent, nscoord* aValue); + virtual void Format(const nscoord &aValue, nsAString &aFormattedValue); +}; + + +/** * Class is used for the work with "font-weight" text attribute in * nsTextAttrsMgr class. */ class nsFontWeightTextAttr : public nsTextAttr<PRInt32> { public: nsFontWeightTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame);
--- a/accessible/src/html/nsHyperTextAccessible.h +++ b/accessible/src/html/nsHyperTextAccessible.h @@ -41,17 +41,16 @@ #define _nsHyperTextAccessible_H_ #include "nsIAccessibleText.h" #include "nsIAccessibleHyperText.h" #include "nsIAccessibleEditableText.h" #include "AccCollector.h" #include "nsAccessibleWrap.h" -#include "nsTextAttrs.h" #include "nsFrameSelection.h" #include "nsISelectionController.h" enum EGetTextType { eGetBefore=-1, eGetAt=0, eGetAfter=1 }; // This character marks where in the text returned via nsIAccessibleText(), // that embedded object characters exist
--- a/accessible/src/msaa/CAccessibleText.cpp +++ b/accessible/src/msaa/CAccessibleText.cpp @@ -40,16 +40,18 @@ #include "CAccessibleText.h" #include "Accessible2.h" #include "AccessibleText_i.c" #include "nsHyperTextAccessible.h" +#include "nsIPersistentProperties2.h" + // IUnknown STDMETHODIMP CAccessibleText::QueryInterface(REFIID iid, void** ppv) { *ppv = NULL; if (IID_IAccessibleText == iid) {
--- a/accessible/tests/mochitest/attributes.js +++ b/accessible/tests/mochitest/attributes.js @@ -203,38 +203,71 @@ const kNormalFontWeight = const kBoldFontWeight = function equalsToBold(aWeight) { return aWeight > 400; } // The pt font size of the input element can vary by Linux distro. const kInputFontSize = WIN ? "10pt" : (MAC ? "8pt" : function() { return true; }); +const kAbsentFontFamily = + function(aFontFamily) { return aFontFamily != "sans-serif"; } +const kInputFontFamily = + function(aFontFamily) { return aFontFamily != "sans-serif"; } + +const kMonospaceFontFamily = + function(aFontFamily) { return aFontFamily != "monospace"; } +const kSansSerifFontFamily = + function(aFontFamily) { return aFontFamily != "sans-serif"; } +const kSerifFontFamily = + function(aFontFamily) { return aFontFamily != "serif"; } + +const kCursiveFontFamily = WIN ? "Comic Sans MS" : + (LINUX ? "DejaVu Serif" : "MacFont"); + +/** + * Return used font from the given computed style. + */ +function fontFamily(aComputedStyle) +{ + var name = aComputedStyle.fontFamily; + switch (name) { + case "monospace": + return kMonospaceFontFamily; + case "sans-serif": + return kSansSerifFontFamily; + case "serif": + return kSerifFontFamily; + default: + return name; + } +} + /** * Build an object of default text attributes expected for the given accessible. * * @param aID [in] identifier of accessible * @param aFontSize [in] font size * @param aFontWeight [in, optional] kBoldFontWeight or kNormalFontWeight, * default value is kNormalFontWeight */ -function buildDefaultTextAttrs(aID, aFontSize, aFontWeight) +function buildDefaultTextAttrs(aID, aFontSize, aFontWeight, aFontFamily) { var elm = getNode(aID); var computedStyle = document.defaultView.getComputedStyle(elm, ""); var bgColor = computedStyle.backgroundColor == "transparent" ? "rgb(255, 255, 255)" : computedStyle.backgroundColor; var defAttrs = { "font-style": computedStyle.fontStyle, "font-size": aFontSize, "background-color": bgColor, "font-weight": aFontWeight ? aFontWeight : kNormalFontWeight, "color": computedStyle.color, - "font-family": computedStyle.fontFamily, + "font-family": aFontFamily ? aFontFamily : fontFamily(computedStyle), "text-position": computedStyle.verticalAlign }; return defAttrs; } //////////////////////////////////////////////////////////////////////////////// // Private.
--- a/accessible/tests/mochitest/attributes/test_text.html +++ b/accessible/tests/mochitest/attributes/test_text.html @@ -258,17 +258,17 @@ testTextAttrs(ID, 45, attrs, defAttrs, 44, 61); attrs = {}; testTextAttrs(ID, 62, attrs, defAttrs, 61, 69); // Walk from span with font-style to the one with font-family. tempElem = tempElem.nextSibling.nextSibling; gComputedStyle = document.defaultView.getComputedStyle(tempElem, ""); - attrs = { "font-family": gComputedStyle.fontFamily }; + attrs = { "font-family": kMonospaceFontFamily }; testTextAttrs(ID, 70, attrs, defAttrs, 69, 83); attrs = {}; testTextAttrs(ID, 84, attrs, defAttrs, 83, 91); attrs = { "text-underline-style": "solid" }; testTextAttrs(ID, 92, attrs, defAttrs, 91, 101); @@ -276,16 +276,17 @@ testTextAttrs(ID, 102, attrs, defAttrs, 101, 109); attrs = { "text-line-through-style": "solid" }; testTextAttrs(ID, 110, attrs, defAttrs, 109, 122); attrs = {}; testTextAttrs(ID, 123, attrs, defAttrs, 122, 130); + ////////////////////////////////////////////////////////////////////////// // area10, different single style spans in non-styled paragraph ID = "area10"; defAttrs = buildDefaultTextAttrs(ID, "12pt"); testDefaultTextAttrs(ID, defAttrs); attrs = {}; testTextAttrs(ID, 0, attrs, defAttrs, 0, 7); @@ -311,17 +312,17 @@ testTextAttrs(ID, 46, attrs, defAttrs, 45, 62); attrs = {}; testTextAttrs(ID, 63, attrs, defAttrs, 62, 70); // Walk from span with font-style to the one with font-family. tempElem = tempElem.nextSibling.nextSibling; gComputedStyle = document.defaultView.getComputedStyle(tempElem, ""); - attrs = {"font-family": gComputedStyle.fontFamily}; + attrs = { "font-family": kMonospaceFontFamily }; testTextAttrs(ID, 71, attrs, defAttrs, 70, 84); attrs = {}; testTextAttrs(ID, 85, attrs, defAttrs, 84, 92); attrs = { "text-underline-style": "solid" }; testTextAttrs(ID, 93, attrs, defAttrs, 92, 102); @@ -329,16 +330,17 @@ testTextAttrs(ID, 103, attrs, defAttrs, 102, 110); attrs = { "text-line-through-style": "solid" }; testTextAttrs(ID, 111, attrs, defAttrs, 110, 123); attrs = {}; testTextAttrs(ID, 124, attrs, defAttrs, 123, 131); + ////////////////////////////////////////////////////////////////////////// // area11, "font-weight" tests ID = "area11"; defAttrs = buildDefaultTextAttrs(ID, "12pt", kBoldFontWeight); testDefaultTextAttrs(ID, defAttrs); attrs = { }; testTextAttrs(ID, 0, attrs, defAttrs, 0, 13); @@ -368,17 +370,18 @@ ////////////////////////////////////////////////////////////////////////// // test zero offset on empty hypertext accessibles ID = "area13"; defAttrs = buildDefaultTextAttrs(ID, "12pt"); attrs = { }; testTextAttrs(ID, 0, attrs, defAttrs, 0, 0); ID = "area14"; - defAttrs = buildDefaultTextAttrs(ID, kInputFontSize); + defAttrs = buildDefaultTextAttrs(ID, kInputFontSize, + kNormalFontWeight, kInputFontFamily); attrs = { }; testTextAttrs(ID, 0, attrs, defAttrs, 0, 0); ////////////////////////////////////////////////////////////////////////// // area15, embed char tests, "*plain*plain**bold*bold*" ID = "area15"; defAttrs = buildDefaultTextAttrs(ID, "12pt"); @@ -399,29 +402,64 @@ // p testTextAttrs(ID, 18, { }, { }, 18, 19); // bold attrs = { "font-weight": kBoldFontWeight }; testTextAttrs(ID, 19, attrs, defAttrs, 19, 23); // p testTextAttrs(ID, 23, { }, { }, 23, 24); + ////////////////////////////////////////////////////////////////////////// + // area16, "font-family" tests + ID = "area16"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + testDefaultTextAttrs(ID, defAttrs); + + attrs = { "font-family": kMonospaceFontFamily }; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 4); + + attrs = { }; + testTextAttrs(ID, 4, attrs, defAttrs, 4, 9); + + attrs = { "font-family": kSerifFontFamily }; + testTextAttrs(ID, 9, attrs, defAttrs, 9, 13); + + attrs = { }; + testTextAttrs(ID, 13, attrs, defAttrs, 13, 18); + + attrs = { "font-family": kAbsentFontFamily }; + testTextAttrs(ID, 18, attrs, defAttrs, 18, 22); + + attrs = { }; + testTextAttrs(ID, 22, attrs, defAttrs, 22, 27); + + attrs = { "font-family": kCursiveFontFamily }; + testTextAttrs(ID, 27, attrs, defAttrs, 27, 31); + + attrs = { }; + testTextAttrs(ID, 31, attrs, defAttrs, 31, 45); + SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); addA11yLoadEvent(doTest); </script> </head> <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><br> + <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> <p id="display"></p> <div id="content" style="display: none"></div> <pre id="test"> </pre> <p id="area1" style="font-size: smaller">Normal <b>Bold</b> Normal</p> <p id="area2" style="font-size: 120%">Normal <b>Bold <i>Italic </i>Bold</b> Normal</p> @@ -464,26 +502,26 @@ <span style="color: magenta">Magenta<b>Bold</b>Magenta</span> </span> </p> <p id="area9" style="font-size: smaller">Small <span style="font-size: 120%">bigger</span> smaller <span style="background-color: blue;">background blue</span> normal <span style="font-style: italic;">Different styling</span> normal - <span style="font-family: tahoma;">Different font</span> normal + <span style="font-family: monospace;">Different font</span> normal <span style="text-decoration: underline;">underlined</span> normal <span style="text-decoration: line-through;">strikethrough</span> normal </p> <p id="area10">Normal <span style="font-size: 120%">bigger</span> smaller <span style="background-color: blue;">background blue</span> normal <span style="font-style: italic;">Different styling</span> normal - <span style="font-family: tahoma;">Different font</span> normal + <span style="font-family: monospace;">Different font</span> normal <span style="text-decoration: underline;">underlined</span> normal <span style="text-decoration: line-through;">strikethrough</span> normal </p> <p id="area11" style="font-weight: bolder;"> <span style="font-weight: bolder;">bolder</span>bolder <span style="font-weight: lighter;">lighter</span>bolder <span style="font-weight: normal;">normal</span>bolder @@ -495,10 +533,18 @@ </p> <p id="area12">hello</p> <p id="area13"></p> <input id="area14"> <!-- *plain*plain**bold*bold*--> <div id="area15"><p>embed</p>plain<p>embed</p>plain<p>embed</p><img src="../moz.png" alt="image"/><b>bold</b><p>embed</p><b>bold</b><p>embed</p></div> + + <p id="area16" style="font-family: sans-serif;"> + <span style="font-family: monospace;">text</span>text + <span style="font-family: serif;">text</span>text + <span style="font-family: BodoniThatDoesntExist;">text</span>text + <span style="font-family: Comic Sans MS, cursive;">text</span>text + <span style="font-family: sans-serif, fantasy;">text</span>text + </p> </body> </html>
--- a/accessible/tests/mochitest/events/Makefile.in +++ b/accessible/tests/mochitest/events/Makefile.in @@ -60,16 +60,17 @@ include $(topsrcdir)/config/rules.mk test_contextmenu.html \ test_docload.html \ test_docload.xul \ test_dragndrop.html \ test_flush.html \ test_focus_aria_activedescendant.html \ test_focus_autocomplete.xul \ test_focus_browserui.xul \ + test_focus_canvas.html \ test_focus_contextmenu.xul \ test_focus_controls.html \ test_focus_dialog.html \ test_focus_doc.html \ test_focus_general.html \ test_focus_general.xul \ test_focus_listcontrols.xul \ test_focus_menu.xul \
new file mode 100644 --- /dev/null +++ b/accessible/tests/mochitest/events/test_focus_canvas.html @@ -0,0 +1,59 @@ +<html> + +<head> + <title>Accessible focus testing in canvas subdom</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="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../events.js"></script> + <script type="application/javascript" + src="../states.js"></script> + + <script type="application/javascript"> + //gA11yEventDumpToConsole = true; + + var gQueue = null; + function doTests() + { + gQueue = new eventQueue(); + + gQueue.push(new synthFocus("button")); + gQueue.push(new synthTab("button", new focusChecker("textbox"))); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + <a target="_blank" + title="Expose content in Canvas element" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=495912"> + Mozilla Bug 495912 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <canvas> + <input id="button" type="button"> + <input id="textbox"> + </canvas> + + <div id="eventdump"></div> +</body> +</html>
--- a/accessible/tests/mochitest/events/test_textattrchange.html +++ b/accessible/tests/mochitest/events/test_textattrchange.html @@ -44,17 +44,19 @@ //var spellchecker = editor.getInlineSpellChecker(true); //spellchecker.enableRealTimeSpell = true; this.DOMNode.value = "valid text inalid tixt"; } this.finalCheck = function spelledTextInvoker_finalCheck() { - var defAttrs = buildDefaultTextAttrs(this.DOMNode, kInputFontSize); + var defAttrs = buildDefaultTextAttrs(this.DOMNode, kInputFontSize, + kNormalFontWeight, + kInputFontFamily); testDefaultTextAttrs(aID, defAttrs); var attrs = { }; var misspelledAttrs = { "invalid": "spelling" }; testTextAttrs(aID, 0, attrs, defAttrs, 0, 11);
--- a/accessible/tests/mochitest/tree/test_canvas.html +++ b/accessible/tests/mochitest/tree/test_canvas.html @@ -13,23 +13,23 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="application/javascript" src="../common.js"></script> <script type="application/javascript" src="../role.js"></script> <script type="application/javascript"> function doTest() { - var accTree = { - role: ROLE_CANVAS, - children: [ - ] - }; + var accTree = + { CANVAS: [ + { CHECKBUTTON: [] }, + { ENTRY: [] } + ] }; + testAccessibleTree("canvas", accTree); - SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); addA11yLoadEvent(doTest); </script> </head> <body> @@ -37,19 +37,17 @@ https://bugzilla.mozilla.org/show_bug.cg <a target="_blank" title="Expose alternative content in Canvas element to ATs" href="https://bugzilla.mozilla.org/show_bug.cgi?id=495912">Mozilla Bug 495912</a> <p id="display"></p> <div id="content" style="display: none"></div> <pre id="test"> </pre> - <canvas id="canvas" tabindex="0"> - fallback content. - </canvas> + <canvas id="canvas" tabindex="0"><input type="checkbox"><input></canvas> <script type="text/javascript"> var c=document.getElementById("canvas"); var cxt=c.getContext("2d"); cxt.fillStyle="#005500"; cxt.fillRect(0,0,150,75); </script>
--- a/accessible/tests/mochitest/treeupdate/Makefile.in +++ b/accessible/tests/mochitest/treeupdate/Makefile.in @@ -42,16 +42,17 @@ srcdir = @srcdir@ VPATH = @srcdir@ relativesrcdir = accessible/treeupdate include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _TEST_FILES =\ test_ariadialog.html \ + test_canvas.html \ test_colorpicker.xul \ test_cssoverflow.html \ test_contextmenu.xul \ test_doc.html \ test_gencontent.html \ test_list_editabledoc.html \ test_list.html \ test_menu.xul \
new file mode 100644 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_canvas.html @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Canvas subdom mutation</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" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function addSubtree(aID) + { + this.node = getNode(aID); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, this.node) + ]; + + this.invoke = function addSubtree_invoke() + { + // ensure we start with no subtree + testAccessibleTree("canvas", { CANVAS: [] }); + getNode("dialog").style.display = "block"; + } + + this.finalCheck = function addSubtree_finalCheck() { + testAccessibleTree("dialog", { DIALOG: [] }); + } + + this.getID = function addSubtree_getID() + { + return "show canvas subdom"; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + // make the subdom come alive! + gQueue.push(new addSubtree("dialog")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Expose content in Canvas element" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=495912"> + Mozilla Bug 495912 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <canvas id="canvas"> + <div id="dialog" role="dialog" style="display: none;"> + </div> + </canvas> + + <div id="eventdump"></div> +</body> +</html>
--- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -405,16 +405,20 @@ pref("browser.link.open_newwindow.restri // Enable browser frame pref("dom.mozBrowserFramesEnabled", true); pref("dom.mozBrowserFramesWhitelist", "http://localhost:7777"); // Temporary permission hack for WebSMS pref("dom.sms.enabled", true); pref("dom.sms.whitelist", "file://,http://localhost:7777"); +// Temporary permission hack for WebContacts +pref("dom.mozContacts.enabled", true); +pref("dom.mozContacts.whitelist", "http://localhost:7777"); + // Ignore X-Frame-Options headers. pref("b2g.ignoreXFrameOptions", true); // controls if we want camera support pref("device.camera.enabled", true); pref("media.realtime_decoder.enabled", true); // "Preview" landing of bug 710563, which is bogged down in analysis
--- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -11,16 +11,17 @@ const CC = Components.Constructor; const Cr = Components.results; const LocalFile = CC('@mozilla.org/file/local;1', 'nsILocalFile', 'initWithPath'); Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); +Cu.import('resource://gre/modules/ContactService.jsm'); XPCOMUtils.defineLazyGetter(Services, 'env', function() { return Cc['@mozilla.org/process/environment;1'] .getService(Ci.nsIEnvironment); }); XPCOMUtils.defineLazyGetter(Services, 'ss', function() { return Cc['@mozilla.org/content/style-sheet-service;1'] @@ -55,17 +56,17 @@ function startupHttpd(baseDir, port) { #endif // FIXME Bug 707625 // until we have a proper security model, add some rights to // the pre-installed web applications // XXX never grant 'content-camera' to non-gaia apps function addPermissions(urls) { let permissions = [ - 'indexedDB', 'indexedDB-unlimited', 'webapps-manage', 'offline-app', 'content-camera' + 'indexedDB', 'indexedDB-unlimited', 'webapps-manage', 'offline-app', 'content-camera', 'webcontacts-manage' ]; urls.forEach(function(url) { let uri = Services.io.newURI(url, null, null); let allow = Ci.nsIPermissionManager.ALLOW_ACTION; permissions.forEach(function(permission) { Services.perms.add(uri, permission, allow); });
--- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -150,16 +150,17 @@ @BINPATH@/components/dom_wifi.xpt @BINPATH@/components/dom_system_b2g.xpt #endif @BINPATH@/components/dom_battery.xpt #ifdef MOZ_B2G_BT @BINPATH@/components/dom_bluetooth.xpt #endif @BINPATH@/components/dom_canvas.xpt +@BINPATH@/components/dom_contacts.xpt @BINPATH@/components/dom_core.xpt @BINPATH@/components/dom_css.xpt @BINPATH@/components/dom_events.xpt @BINPATH@/components/dom_geolocation.xpt @BINPATH@/components/dom_network.xpt @BINPATH@/components/dom_notification.xpt @BINPATH@/components/dom_html.xpt @BINPATH@/components/dom_indexeddb.xpt @@ -289,16 +290,18 @@ @BINPATH@/components/xuldoc.xpt @BINPATH@/components/xultmpl.xpt @BINPATH@/components/zipwriter.xpt @BINPATH@/components/webapps.xpt ; JavaScript components @BINPATH@/components/ConsoleAPI.manifest @BINPATH@/components/ConsoleAPI.js +@BINPATH@/components/ContactManager.js +@BINPATH@/components/ContactManager.manifest @BINPATH@/components/FeedProcessor.manifest @BINPATH@/components/FeedProcessor.js @BINPATH@/components/BrowserFeeds.manifest @BINPATH@/components/FeedConverter.js @BINPATH@/components/FeedWriter.js @BINPATH@/components/fuelApplication.manifest @BINPATH@/components/fuelApplication.js @BINPATH@/components/WebContentConverter.js
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -953,16 +953,17 @@ pref("services.sync.prefs.sync.addons.ig // uncompromised Sync-connected devices. pref("services.sync.prefs.sync.app.update.mode", true); pref("services.sync.prefs.sync.browser.download.manager.closeWhenDone", true); pref("services.sync.prefs.sync.browser.download.manager.retention", true); pref("services.sync.prefs.sync.browser.download.manager.scanWhenDone", true); pref("services.sync.prefs.sync.browser.download.manager.showWhenStarting", true); pref("services.sync.prefs.sync.browser.formfill.enable", true); pref("services.sync.prefs.sync.browser.link.open_newwindow", true); +pref("services.sync.prefs.sync.browser.newtabpage.enabled", true); pref("services.sync.prefs.sync.browser.offline-apps.notify", true); pref("services.sync.prefs.sync.browser.safebrowsing.enabled", true); pref("services.sync.prefs.sync.browser.safebrowsing.malware.enabled", true); pref("services.sync.prefs.sync.browser.search.selectedEngine", true); pref("services.sync.prefs.sync.browser.search.update", true); pref("services.sync.prefs.sync.browser.sessionstore.restore_on_demand", true); pref("services.sync.prefs.sync.browser.startup.homepage", true); pref("services.sync.prefs.sync.browser.startup.page", true);
--- a/browser/base/content/browser-places.js +++ b/browser/base/content/browser-places.js @@ -96,35 +96,35 @@ var StarUI = { case "popuphidden": if (aEvent.originalTarget == this.panel) { if (!this._element("editBookmarkPanelContent").hidden) this.quitEditMode(); this._restoreCommandsState(); this._itemId = -1; if (this._batching) { - PlacesUIUtils.ptm.endBatch(); + PlacesUtils.transactionManager.endBatch(); this._batching = false; } switch (this._actionOnHide) { case "cancel": { - PlacesUIUtils.ptm.undoTransaction(); + PlacesUtils.transactionManager.undoTransaction(); break; } case "remove": { // Remove all bookmarks for the bookmark's url, this also removes // the tags for the url. - PlacesUIUtils.ptm.beginBatch(); + PlacesUtils.transactionManager.beginBatch(); let itemIds = PlacesUtils.getBookmarksForURI(this._uriForRemoval); for (let i = 0; i < itemIds.length; i++) { - let txn = PlacesUIUtils.ptm.removeItem(itemIds[i]); - PlacesUIUtils.ptm.doTransaction(txn); + let txn = new PlacesRemoveItemTransaction(itemIds[i]); + PlacesUtils.transactionManager.doTransaction(txn); } - PlacesUIUtils.ptm.endBatch(); + PlacesUtils.transactionManager.endBatch(); break; } } this._actionOnHide = ""; } break; case "keypress": if (aEvent.defaultPrevented) { @@ -270,17 +270,17 @@ var StarUI = { removeBookmarkButtonCommand: function SU_removeBookmarkButtonCommand() { this._uriForRemoval = PlacesUtils.bookmarks.getBookmarkURI(this._itemId); this._actionOnHide = "remove"; this.panel.hidePopup(); }, beginBatch: function SU_beginBatch() { if (!this._batching) { - PlacesUIUtils.ptm.beginBatch(); + PlacesUtils.transactionManager.beginBatch(); this._batching = true; } } } var PlacesCommandHook = { /** * Adds a bookmark to the page loaded in the given browser. @@ -320,19 +320,20 @@ var PlacesCommandHook = { // but open right into the "edit" state, start batching here, so // "Cancel" in that state removes the bookmark. StarUI.beginBatch(); } var parent = aParent != undefined ? aParent : PlacesUtils.unfiledBookmarksFolderId; var descAnno = { name: PlacesUIUtils.DESCRIPTION_ANNO, value: description }; - var txn = PlacesUIUtils.ptm.createItem(uri, parent, -1, - title, null, [descAnno]); - PlacesUIUtils.ptm.doTransaction(txn); + var txn = new PlacesCreateBookmarkTransaction(uri, parent, + PlacesUtils.bookmarks.DEFAULT_INDEX, + title, null, [descAnno]); + PlacesUtils.transactionManager.doTransaction(txn); // Set the character-set if (charset) PlacesUtils.history.setCharsetForURI(uri, charset); itemId = PlacesUtils.getMostRecentBookmarkForURI(uri); } // Revert the contents of the location bar if (gURLBar)
--- a/browser/base/content/browser-syncui.js +++ b/browser/base/content/browser-syncui.js @@ -286,41 +286,41 @@ let gSyncUI = { * "reset" -- reset sync */ openSetup: function SUI_openSetup(wizardType) { let win = Services.wm.getMostRecentWindow("Weave:AccountSetup"); if (win) win.focus(); else { - window.openDialog("chrome://browser/content/syncSetup.xul", + window.openDialog("chrome://browser/content/sync/setup.xul", "weaveSetup", "centerscreen,chrome,resizable=no", wizardType); } }, openAddDevice: function () { if (!Weave.Utils.ensureMPUnlocked()) return; let win = Services.wm.getMostRecentWindow("Sync:AddDevice"); if (win) win.focus(); else - window.openDialog("chrome://browser/content/syncAddDevice.xul", + window.openDialog("chrome://browser/content/sync/addDevice.xul", "syncAddDevice", "centerscreen,chrome,resizable=no"); }, openQuotaDialog: function SUI_openQuotaDialog() { let win = Services.wm.getMostRecentWindow("Sync:ViewQuota"); if (win) win.focus(); else Services.ww.activeWindow.openDialog( - "chrome://browser/content/syncQuota.xul", "", + "chrome://browser/content/sync/quota.xul", "", "centerscreen,chrome,dialog,modal"); }, openPrefs: function SUI_openPrefs() { openPreferences("paneSync"); },
--- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -324,22 +324,22 @@ window[chromehidden~="toolbar"] toolbar: #status-bar , #mainPopupSet { min-width: 1px; } %ifdef MOZ_SERVICES_SYNC /* Sync notification UI */ #sync-notifications { - -moz-binding: url("chrome://browser/content/syncNotification.xml#notificationbox"); + -moz-binding: url("chrome://browser/content/sync/notification.xml#notificationbox"); overflow-y: visible !important; } #sync-notifications notification { - -moz-binding: url("chrome://browser/content/syncNotification.xml#notification"); + -moz-binding: url("chrome://browser/content/sync/notification.xml#notification"); } %endif /* Identity UI */ #identity-popup-content-box.unknownIdentity > #identity-popup-connectedToLabel , #identity-popup-content-box.unknownIdentity > #identity-popup-runByLabel , #identity-popup-content-box.unknownIdentity > #identity-popup-content-host , #identity-popup-content-box.unknownIdentity > #identity-popup-content-owner ,
--- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -284,19 +284,24 @@ nsContextMenu.prototype = { // (or is in a frame), or a canvas. this.showItem("context-viewimage", (this.onImage && (!this.inSyntheticDoc || this.inFrame)) || this.onCanvas); // View video depends on not having a standalone video. this.showItem("context-viewvideo", this.onVideo && (!this.inSyntheticDoc || this.inFrame)); this.setItemAttr("context-viewvideo", "disabled", !this.mediaURL); - // View background image depends on whether there is one. - this.showItem("context-viewbgimage", shouldShow && !this._hasMultipleBGImages); - this.showItem("context-sep-viewbgimage", shouldShow && !this._hasMultipleBGImages); + // View background image depends on whether there is one, but don't make + // background images of a stand-alone media document available. + this.showItem("context-viewbgimage", shouldShow && + !this._hasMultipleBGImages && + !this.inSyntheticDoc); + this.showItem("context-sep-viewbgimage", shouldShow && + !this._hasMultipleBGImages && + !this.inSyntheticDoc); document.getElementById("context-viewbgimage") .disabled = !this.hasBGImage; this.showItem("context-viewimageinfo", this.onImage); }, initMiscItems: function CM_initMiscItems() { var isTextSelected = this.isTextSelected; @@ -382,17 +387,19 @@ nsContextMenu.prototype = { this.showItem("context-sep-undo", this.onTextInput); this.showItem("context-cut", this.onTextInput); this.showItem("context-copy", this.isContentSelected || this.onTextInput); this.showItem("context-paste", this.onTextInput); this.showItem("context-delete", this.onTextInput); this.showItem("context-sep-paste", this.onTextInput); this.showItem("context-selectall", !(this.onLink || this.onImage || - this.onVideo || this.onAudio) || this.isDesignMode); + this.onVideo || this.onAudio || + this.inSyntheticDoc) || + this.isDesignMode); this.showItem("context-sep-selectall", this.isContentSelected ); // XXX dr // ------ // nsDocumentViewer.cpp has code to determine whether we're // on a link or an image. we really ought to be using that... // Copy email link depends on whether we're on an email link.
--- a/browser/base/content/overrides/app-license.html +++ b/browser/base/content/overrides/app-license.html @@ -1,3 +1,3 @@ <p><b>Binaries</b> of this product have been made available to you by the <a href="http://www.mozilla.org/">Mozilla Project</a> under the Mozilla - Public License. <a href="about:rights">Know your rights</a>.</p> + Public License 2.0 (MPL). <a href="about:rights">Know your rights</a>.</p>
rename from browser/base/content/aboutSyncTabs-bindings.xml rename to browser/base/content/sync/aboutSyncTabs-bindings.xml
rename from browser/base/content/aboutSyncTabs.css rename to browser/base/content/sync/aboutSyncTabs.css --- a/browser/base/content/aboutSyncTabs.css +++ b/browser/base/content/sync/aboutSyncTabs.css @@ -1,7 +1,7 @@ richlistitem[type="tab"] { - -moz-binding: url(chrome://browser/content/aboutSyncTabs-bindings.xml#tab-listing); + -moz-binding: url(chrome://browser/content/sync/aboutSyncTabs-bindings.xml#tab-listing); } richlistitem[type="client"] { - -moz-binding: url(chrome://browser/content/aboutSyncTabs-bindings.xml#client-listing); + -moz-binding: url(chrome://browser/content/sync/aboutSyncTabs-bindings.xml#client-listing); }
rename from browser/base/content/aboutSyncTabs.js rename to browser/base/content/sync/aboutSyncTabs.js
rename from browser/base/content/aboutSyncTabs.xul rename to browser/base/content/sync/aboutSyncTabs.xul --- a/browser/base/content/aboutSyncTabs.xul +++ b/browser/base/content/sync/aboutSyncTabs.xul @@ -35,30 +35,30 @@ # 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 ***** <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?> <?xml-stylesheet href="chrome://browser/skin/aboutSyncTabs.css" type="text/css"?> -<?xml-stylesheet href="chrome://browser/content/aboutSyncTabs.css" type="text/css"?> +<?xml-stylesheet href="chrome://browser/content/sync/aboutSyncTabs.css" type="text/css"?> <!DOCTYPE window [ <!ENTITY % aboutSyncTabsDTD SYSTEM "chrome://browser/locale/aboutSyncTabs.dtd"> %aboutSyncTabsDTD; ]> <window id="tabs-display" onload="RemoteTabViewer.init()" onunload="RemoteTabViewer.uninit()" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" title="&tabs.otherComputers.label;"> - <script type="application/javascript;version=1.8" src="chrome://browser/content/aboutSyncTabs.js"/> + <script type="application/javascript;version=1.8" src="chrome://browser/content/sync/aboutSyncTabs.js"/> <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> <html:head> <html:link rel="icon" href="chrome://browser/skin/sync-16.png"/> </html:head> <popupset id="contextmenus"> <menupopup id="tabListContext"> <menuitem label="&tabs.context.openTab.label;"
rename from browser/base/content/syncAddDevice.xul rename to browser/base/content/sync/addDevice.xul --- a/browser/base/content/syncAddDevice.xul +++ b/browser/base/content/sync/addDevice.xul @@ -56,19 +56,19 @@ windowtype="Sync:AddDevice" persist="screenX screenY" onwizardnext="return gSyncAddDevice.onWizardAdvance();" onwizardback="return gSyncAddDevice.onWizardBack();" onwizardcancel="gSyncAddDevice.onWizardCancel();" onload="gSyncAddDevice.init();"> <script type="application/javascript" - src="chrome://browser/content/syncAddDevice.js"/> + src="chrome://browser/content/sync/addDevice.js"/> <script type="application/javascript" - src="chrome://browser/content/syncUtils.js"/> + src="chrome://browser/content/sync/utils.js"/> <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> <script type="application/javascript" src="chrome://global/content/printUtils.js"/> <wizardpage id="addDevicePage" label="&pairDevice.title.label;" onpageshow="gSyncAddDevice.onPageShow();">
rename from browser/base/content/syncGenericChange.js rename to browser/base/content/sync/genericChange.js
rename from browser/base/content/syncGenericChange.xul rename to browser/base/content/sync/genericChange.xul --- a/browser/base/content/syncGenericChange.xul +++ b/browser/base/content/sync/genericChange.xul @@ -55,19 +55,19 @@ xmlns:html="http://www.w3.org/1999/xhtml" id="change-dialog" windowtype="Weave:ChangeSomething" persist="screenX screenY" onwizardnext="Change.onLoad()" onwizardfinish="return Change.onDialogAccept();"> <script type="application/javascript" - src="chrome://browser/content/syncGenericChange.js"/> + src="chrome://browser/content/sync/genericChange.js"/> <script type="application/javascript" - src="chrome://browser/content/syncUtils.js"/> + src="chrome://browser/content/sync/utils.js"/> <script type="application/javascript" src="chrome://global/content/printUtils.js"/> <wizardpage id="change-page" label=""> <description id="introText"> </description>
rename from browser/base/content/syncNotification.xml rename to browser/base/content/sync/notification.xml
rename from browser/base/content/syncProgress.xhtml rename to browser/base/content/sync/progress.xhtml --- a/browser/base/content/syncProgress.xhtml +++ b/browser/base/content/sync/progress.xhtml @@ -56,17 +56,17 @@ <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/syncProgress.css"/> <link rel="icon" type="image/png" id="favicon" href="chrome://browser/skin/sync-16.png"/> <script type="text/javascript;version=1.8" - src="chrome://browser/content/syncProgress.js"/> + src="chrome://browser/content/sync/progress.js"/> </head> <body onload="onLoad(event)" onunload="onUnload(event)"> <title>&setup.successPage.title;</title> <div id="floatingBox" class="main-content"> <div id="title"> <h1>&setup.successPage.title;</h1> </div> <div id="successLogo">
rename from browser/base/content/syncQuota.xul rename to browser/base/content/sync/quota.xul --- a/browser/base/content/syncQuota.xul +++ b/browser/base/content/sync/quota.xul @@ -55,17 +55,17 @@ xmlns:html="http://www.w3.org/1999/xhtml" onload="gSyncQuota.init()" buttons="accept,cancel" title=""a.dialogTitle.label;" ondialogcancel="return gSyncQuota.onCancel();" ondialogaccept="return gSyncQuota.onAccept();"> <script type="application/javascript" - src="chrome://browser/content/syncQuota.js"/> + src="chrome://browser/content/sync/quota.js"/> <stringbundleset id="stringbundleset"> <stringbundle id="quotaStrings" src="chrome://browser/locale/syncQuota.properties"/> </stringbundleset> <vbox flex="1"> <label id="usageLabel"
rename from browser/base/content/syncSetup.xul rename to browser/base/content/sync/setup.xul --- a/browser/base/content/syncSetup.xul +++ b/browser/base/content/sync/setup.xul @@ -60,19 +60,19 @@ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" onwizardnext="return gSyncSetup.onWizardAdvance()" onwizardback="return gSyncSetup.onWizardBack()" onwizardcancel="gSyncSetup.onWizardCancel()" onload="gSyncSetup.init()"> <script type="application/javascript" - src="chrome://browser/content/syncSetup.js"/> + src="chrome://browser/content/sync/setup.js"/> <script type="application/javascript" - src="chrome://browser/content/syncUtils.js"/> + src="chrome://browser/content/sync/utils.js"/> <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> <script type="application/javascript" src="chrome://global/content/printUtils.js"/> <wizardpage id="addDevicePage" label="&pairDevice.title.label;" onpageshow="gSyncSetup.onPageShow()">
rename from browser/base/content/syncUtils.js rename to browser/base/content/sync/utils.js --- a/browser/base/content/syncUtils.js +++ b/browser/base/content/sync/utils.js @@ -71,17 +71,17 @@ let gSyncUtils = { // Just re-show the dialog if it's already open let openedDialog = Services.wm.getMostRecentWindow("Sync:" + type); if (openedDialog != null) { openedDialog.focus(); return; } // Open up the change dialog - let changeXUL = "chrome://browser/content/syncGenericChange.xul"; + let changeXUL = "chrome://browser/content/sync/genericChange.xul"; let changeOpt = "centerscreen,chrome,resizable=no"; Services.ww.activeWindow.openDialog(changeXUL, "", changeOpt, type, duringSetup); }, changePassword: function () { if (Weave.Utils.ensureMPUnlocked()) this.openChange("ChangePassword"); @@ -120,17 +120,17 @@ let gSyncUtils = { * @param elid : ID of the form element containing the passphrase. * @param callback : Function called once the iframe has loaded. */ _preparePPiframe: function(elid, callback) { let pp = document.getElementById(elid).value; // Create an invisible iframe whose contents we can print. let iframe = document.createElement("iframe"); - iframe.setAttribute("src", "chrome://browser/content/syncKey.xhtml"); + iframe.setAttribute("src", "chrome://browser/content/sync/key.xhtml"); iframe.collapsed = true; document.documentElement.appendChild(iframe); iframe.contentWindow.addEventListener("load", function() { iframe.contentWindow.removeEventListener("load", arguments.callee, false); // Insert the Sync Key into the page. let el = iframe.contentDocument.getElementById("synckey"); el.firstChild.nodeValue = pp;
--- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1413,17 +1413,19 @@ ]]> </body> </method> <method name="warnAboutClosingTabs"> <parameter name="aAll"/> <body> <![CDATA[ - var tabsToClose = (aAll ? this.tabs.length : this.visibleTabs.length - 1) + var tabsToClose = (aAll ? + this.tabs.length - this._removingTabs.length : + this.visibleTabs.length - 1) - gBrowser._numPinnedTabs; if (tabsToClose <= 1) return true; const pref = "browser.tabs.warnOnClose"; var shouldPrompt = Services.prefs.getBoolPref(pref); if (!shouldPrompt) @@ -1521,16 +1523,17 @@ if (!aTab.pinned && !aTab.hidden && aTab._fullyOpen && byMouse) this.tabContainer._lockTabSizing(aTab); else this.tabContainer._unlockTabSizing(); if (!animate /* the caller didn't opt in */ || isLastTab || aTab.pinned || + aTab.hidden || this._removingTabs.length > 3 /* don't want lots of concurrent animations */ || aTab.getAttribute("fadein") != "true" /* fade-in transition hasn't been triggered yet */ || window.getComputedStyle(aTab).maxWidth == "0.1px" /* fade-in transition hasn't moved yet */ || !Services.prefs.getBoolPref("browser.tabs.animate")) { this._endRemoveTab(aTab); return; }
--- a/browser/base/content/test/browser_homeDrop.js +++ b/browser/base/content/test/browser_homeDrop.js @@ -23,48 +23,33 @@ function test() { ok(true, "dialog appeared in response to home button drop"); domwindow.document.documentElement.cancelDialog(); Services.wm.removeListener(dialogListener); // Now trigger the invalid URI test executeSoon(function () { let consoleListener = { observe: function (m) { - info("m.message: " + m.message + "\n"); if (m.message.indexOf("NS_ERROR_DOM_BAD_URI") > -1) { ok(true, "drop was blocked"); executeSoon(finish); } } } Services.console.registerListener(consoleListener); registerCleanupFunction(function () { Services.console.unregisterListener(consoleListener); }); executeSoon(function () { info("Attempting second drop, of a javascript: URI"); // The drop handler throws an exception when dragging URIs that inherit // principal, e.g. javascript: expectUncaughtException(); - let originalHandler = homeButtonObserver.onDrop; - homeButtonObserver.onDrop = function (aEvent) { - info("homeButtonObserver.onDrop called"); - try { - originalHandler(aEvent); - } catch (ex) { - info("originalHandler threw an exception: " + ex); - throw ex; - } - }; - registerCleanupFunction(function () { - homeButtonObserver.onDrop = originalHandler; - }); chromeUtils.synthesizeDrop(homeButton, homeButton, [[{type: "text/plain", data: "javascript:8888"}]], "copy", window, EventUtils); - info("Triggered the second drop of a javascript: URI"); }); }) }); Services.wm.addListener(dialogListener); chromeUtils.synthesizeDrop(homeButton, homeButton, [[{type: "text/plain", data: "http://mochi.test:8888/"}]], "copy", window, EventUtils); }
--- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -36,51 +36,51 @@ browser.jar: * content/browser/pageinfo/pageInfo.xul (content/pageinfo/pageInfo.xul) * content/browser/pageinfo/pageInfo.js (content/pageinfo/pageInfo.js) * content/browser/pageinfo/pageInfo.css (content/pageinfo/pageInfo.css) * content/browser/pageinfo/pageInfo.xml (content/pageinfo/pageInfo.xml) * content/browser/pageinfo/feeds.js (content/pageinfo/feeds.js) * content/browser/pageinfo/feeds.xml (content/pageinfo/feeds.xml) * content/browser/pageinfo/permissions.js (content/pageinfo/permissions.js) * content/browser/pageinfo/security.js (content/pageinfo/security.js) +#ifdef MOZ_SERVICES_SYNC +* content/browser/sync/aboutSyncTabs.xul (content/sync/aboutSyncTabs.xul) + content/browser/sync/aboutSyncTabs.js (content/sync/aboutSyncTabs.js) + content/browser/sync/aboutSyncTabs.css (content/sync/aboutSyncTabs.css) +* content/browser/sync/aboutSyncTabs-bindings.xml (content/sync/aboutSyncTabs-bindings.xml) +* content/browser/sync/setup.xul (content/sync/setup.xul) + content/browser/sync/addDevice.js (content/sync/addDevice.js) +* content/browser/sync/addDevice.xul (content/sync/addDevice.xul) + content/browser/sync/setup.js (content/sync/setup.js) +* content/browser/sync/genericChange.xul (content/sync/genericChange.xul) + content/browser/sync/genericChange.js (content/sync/genericChange.js) +* content/browser/sync/key.xhtml (content/sync/key.xhtml) +* content/browser/sync/notification.xml (content/sync/notification.xml) +* content/browser/sync/quota.xul (content/sync/quota.xul) + content/browser/sync/quota.js (content/sync/quota.js) + content/browser/sync/utils.js (content/sync/utils.js) + content/browser/sync/progress.js (content/sync/progress.js) +* content/browser/sync/progress.xhtml (content/sync/progress.xhtml) +#endif * content/browser/openLocation.js (content/openLocation.js) * content/browser/openLocation.xul (content/openLocation.xul) * content/browser/safeMode.js (content/safeMode.js) * content/browser/safeMode.xul (content/safeMode.xul) * content/browser/sanitize.js (content/sanitize.js) * content/browser/sanitize.xul (content/sanitize.xul) * content/browser/sanitizeDialog.js (content/sanitizeDialog.js) content/browser/sanitizeDialog.css (content/sanitizeDialog.css) * content/browser/tabbrowser.css (content/tabbrowser.css) * content/browser/tabbrowser.xml (content/tabbrowser.xml) * content/browser/urlbarBindings.xml (content/urlbarBindings.xml) * content/browser/utilityOverlay.js (content/utilityOverlay.js) * content/browser/web-panels.js (content/web-panels.js) * content/browser/web-panels.xul (content/web-panels.xul) * content/browser/baseMenuOverlay.xul (content/baseMenuOverlay.xul) * content/browser/nsContextMenu.js (content/nsContextMenu.js) -#ifdef MOZ_SERVICES_SYNC -* content/browser/aboutSyncTabs.xul (content/aboutSyncTabs.xul) - content/browser/aboutSyncTabs.js (content/aboutSyncTabs.js) - content/browser/aboutSyncTabs.css (content/aboutSyncTabs.css) -* content/browser/aboutSyncTabs-bindings.xml (content/aboutSyncTabs-bindings.xml) -* content/browser/syncSetup.xul (content/syncSetup.xul) - content/browser/syncAddDevice.js (content/syncAddDevice.js) -* content/browser/syncAddDevice.xul (content/syncAddDevice.xul) - content/browser/syncSetup.js (content/syncSetup.js) -* content/browser/syncGenericChange.xul (content/syncGenericChange.xul) - content/browser/syncGenericChange.js (content/syncGenericChange.js) -* content/browser/syncKey.xhtml (content/syncKey.xhtml) -* content/browser/syncNotification.xml (content/syncNotification.xml) -* content/browser/syncQuota.xul (content/syncQuota.xul) - content/browser/syncQuota.js (content/syncQuota.js) - content/browser/syncUtils.js (content/syncUtils.js) - content/browser/syncProgress.js (content/syncProgress.js) -* content/browser/syncProgress.xhtml (content/syncProgress.xhtml) -#endif # XXX: We should exclude this one as well (bug 71895) * content/browser/hiddenWindow.xul (content/hiddenWindow.xul) #ifdef XP_MACOSX * content/browser/macBrowserOverlay.xul (content/macBrowserOverlay.xul) * content/browser/downloadManagerOverlay.xul (content/downloadManagerOverlay.xul) * content/browser/jsConsoleOverlay.xul (content/jsConsoleOverlay.xul) * content/browser/softwareUpdateOverlay.xul (content/softwareUpdateOverlay.xul) #endif
--- a/browser/components/about/AboutRedirector.cpp +++ b/browser/components/about/AboutRedirector.cpp @@ -92,19 +92,19 @@ static RedirEntry kRedirMap[] = { nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::ALLOW_SCRIPT }, { "robots", "chrome://browser/content/aboutRobots.xhtml", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::ALLOW_SCRIPT }, { "sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml", nsIAboutModule::ALLOW_SCRIPT }, #ifdef MOZ_SERVICES_SYNC - { "sync-progress", "chrome://browser/content/syncProgress.xhtml", + { "sync-progress", "chrome://browser/content/sync/progress.xhtml", nsIAboutModule::ALLOW_SCRIPT }, - { "sync-tabs", "chrome://browser/content/aboutSyncTabs.xul", + { "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul", nsIAboutModule::ALLOW_SCRIPT }, #endif { "home", "chrome://browser/content/aboutHome.xhtml", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::ALLOW_SCRIPT }, { "newtab", "chrome://browser/content/newtab/newTab.xul", nsIAboutModule::ALLOW_SCRIPT }, { "permissions", "chrome://browser/content/preferences/aboutPermissions.xul",
--- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -1,8 +1,9 @@ +# -*- indent-tabs-mode: nil -*- # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # @@ -769,51 +770,94 @@ BrowserGlue.prototype = { #ifdef MOZ_TELEMETRY_REPORTING _showTelemetryNotification: function BG__showTelemetryNotification() { const PREF_TELEMETRY_PROMPTED = "toolkit.telemetry.prompted"; const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled"; const PREF_TELEMETRY_REJECTED = "toolkit.telemetry.rejected"; const PREF_TELEMETRY_INFOURL = "toolkit.telemetry.infoURL"; const PREF_TELEMETRY_SERVER_OWNER = "toolkit.telemetry.server_owner"; + const PREF_TELEMETRY_ENABLED_BY_DEFAULT = "toolkit.telemetry.enabledByDefault"; + const PREF_TELEMETRY_NOTIFIED_OPTOUT = "toolkit.telemetry.notifiedOptOut"; // This is used to reprompt users when privacy message changes const TELEMETRY_PROMPT_REV = 2; - function appendTelemetryNotification(notifyBox, message, buttons, hideclose) { + // Stick notifications onto the selected tab of the active browser window. + var win = this.getMostRecentBrowserWindow(); + var tabbrowser = win.gBrowser; + var notifyBox = tabbrowser.getNotificationBox(); + + var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); + var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); + var productName = brandBundle.GetStringFromName("brandFullName"); + var serverOwner = Services.prefs.getCharPref(PREF_TELEMETRY_SERVER_OWNER); + + function appendTelemetryNotification(message, buttons, hideclose) { let notification = notifyBox.appendNotification(message, "telemetry", null, - notifyBox.PRIORITY_INFO_LOW, - buttons); - notification.setAttribute("hideclose", hideclose); + notifyBox.PRIORITY_INFO_LOW, + buttons); + if (hideclose) + notification.setAttribute("hideclose", hideclose); notification.persistence = -1; // Until user closes it return notification; } + function appendLearnMoreLink(notification) { + let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + let link = notification.ownerDocument.createElementNS(XULNS, "label"); + link.className = "text-link telemetry-text-link"; + link.setAttribute("value", browserBundle.GetStringFromName("telemetryLinkLabel")); + let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText"); + description.appendChild(link); + return link; + } + + var telemetryEnabledByDefault = false; + try { + telemetryEnabledByDefault = Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED_BY_DEFAULT); + } catch(e) {} + if (telemetryEnabledByDefault) { + var telemetryNotifiedOptOut = false; + try { + telemetryNotifiedOptOut = Services.prefs.getBoolPref(PREF_TELEMETRY_NOTIFIED_OPTOUT); + } catch(e) {} + if (telemetryNotifiedOptOut) + return; + + var telemetryPrompt = browserBundle.formatStringFromName("telemetryOptOutPrompt", + [productName, serverOwner, productName], 3); + + Services.prefs.setBoolPref(PREF_TELEMETRY_NOTIFIED_OPTOUT, true); + + let notification = appendTelemetryNotification(telemetryPrompt, null, false); + let link = appendLearnMoreLink(notification); + link.addEventListener('click', function() { + // Open the learn more url in a new tab + let url = Services.urlFormatter.formatURLPref("app.support.baseURL"); + url += "how-can-i-help-submitting-performance-data"; + tabbrowser.selectedTab = tabbrowser.addTab(url); + // Remove the notification on which the user clicked + notification.parentNode.removeNotification(notification, true); + }, false); + return; + } + var telemetryPrompted = null; try { telemetryPrompted = Services.prefs.getIntPref(PREF_TELEMETRY_PROMPTED); } catch(e) {} // If the user has seen the latest telemetry prompt, do not prompt again // else clear old prefs and reprompt if (telemetryPrompted === TELEMETRY_PROMPT_REV) return; Services.prefs.clearUserPref(PREF_TELEMETRY_PROMPTED); Services.prefs.clearUserPref(PREF_TELEMETRY_ENABLED); - // Stick the notification onto the selected tab of the active browser window. - var win = this.getMostRecentBrowserWindow(); - var browser = win.gBrowser; // for closure in notification bar callback - var notifyBox = browser.getNotificationBox(); - - var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); - var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); - - var productName = brandBundle.GetStringFromName("brandFullName"); - var serverOwner = Services.prefs.getCharPref(PREF_TELEMETRY_SERVER_OWNER); - var telemetryPrompt = browserBundle.formatStringFromName("telemetryPrompt", [productName, serverOwner], 2); + var telemetryPrompt = browserBundle.formatStringFromName("telemetryPrompt", [productName, serverOwner], 2); var buttons = [ { label: browserBundle.GetStringFromName("telemetryYesButtonLabel2"), accessKey: browserBundle.GetStringFromName("telemetryYesButtonAccessKey"), popup: null, callback: function(aNotificationBar, aButton) { Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); @@ -827,33 +871,27 @@ BrowserGlue.prototype = { Services.prefs.setBoolPref(PREF_TELEMETRY_REJECTED, true); } } ]; // Set pref to indicate we've shown the notification. Services.prefs.setIntPref(PREF_TELEMETRY_PROMPTED, TELEMETRY_PROMPT_REV); - let notification = appendTelemetryNotification(notifyBox, telemetryPrompt, - buttons, true); - let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - let link = notification.ownerDocument.createElementNS(XULNS, "label"); - link.className = "text-link telemetry-text-link"; - link.setAttribute("value", browserBundle.GetStringFromName("telemetryLinkLabel")); + let notification = appendTelemetryNotification(telemetryPrompt, buttons, true); + let link = appendLearnMoreLink(notification); link.addEventListener('click', function() { // Open the learn more url in a new tab - browser.selectedTab = browser.addTab(Services.prefs.getCharPref(PREF_TELEMETRY_INFOURL)); + tabbrowser.selectedTab = tabbrowser.addTab(Services.prefs.getCharPref(PREF_TELEMETRY_INFOURL)); // Remove the notification on which the user clicked notification.parentNode.removeNotification(notification, true); // Add a new notification to that tab, with no "Learn more" link - notifyBox = browser.getNotificationBox(); - appendTelemetryNotification(notifyBox, telemetryPrompt, buttons, true); + notifyBox = tabbrowser.getNotificationBox(); + appendTelemetryNotification(telemetryPrompt, buttons, true); }, false); - let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText"); - description.appendChild(link); }, #endif _showPluginUpdatePage: function BG__showPluginUpdatePage() { Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false); var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. getService(Ci.nsIURLFormatter);
--- a/browser/components/places/content/bookmarkProperties.js +++ b/browser/components/places/content/bookmarkProperties.js @@ -431,25 +431,25 @@ var BookmarkPropertiesPanel = { break; } }, _beginBatch: function BPP__beginBatch() { if (this._batching) return; - PlacesUIUtils.ptm.beginBatch(); + PlacesUtils.transactionManager.beginBatch(); this._batching = true; }, _endBatch: function BPP__endBatch() { if (!this._batching) return; - PlacesUIUtils.ptm.endBatch(); + PlacesUtils.transactionManager.endBatch(); this._batching = false; }, _fillEditProperties: function BPP__fillEditProperties() { gEditItemOverlay.initPanel(this._itemId, { hiddenRows: this._hiddenRows, forceReadOnly: this._readOnly }); }, @@ -509,17 +509,17 @@ var BookmarkPropertiesPanel = { onDialogCancel: function BPP_onDialogCancel() { // The order here is important! We have to uninit the panel first, otherwise // changes done as part of Undo may change the panel contents and by // that force it to commit more transactions. gEditItemOverlay.uninitPanel(true); gEditItemOverlay = null; this._endBatch(); - PlacesUIUtils.ptm.undoTransaction(); + PlacesUtils.transactionManager.undoTransaction(); window.arguments[0].performed = false; }, /** * This method checks to see if the input fields are in a valid state. * * @returns true if the input is valid, false otherwise */ @@ -572,54 +572,69 @@ var BookmarkPropertiesPanel = { * various fields and opening arguments of the dialog. */ _getCreateNewBookmarkTransaction: function BPP__getCreateNewBookmarkTransaction(aContainer, aIndex) { var annotations = []; var childTransactions = []; if (this._description) { - childTransactions.push( - PlacesUIUtils.ptm.editItemDescription(-1, this._description)); + let annoObj = { name : PlacesUIUtils.DESCRIPTION_ANNO, + type : Ci.nsIAnnotationService.TYPE_STRING, + flags : 0, + value : this._description, + expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; + let editItemTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj); + childTransactions.push(editItemTxn); } if (this._loadInSidebar) { - childTransactions.push( - PlacesUIUtils.ptm.setLoadInSidebar(-1, this._loadInSidebar)); + let annoObj = { name : PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO, + type : Ci.nsIAnnotationService.TYPE_INT32, + flags : 0, + value : this._loadInSidebar, + expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; + let setLoadTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj); + childTransactions.push(setLoadTxn); } if (this._postData) { - childTransactions.push( - PlacesUIUtils.ptm.editBookmarkPostData(-1, this._postData)); + let postDataTxn = new PlacesEditBookmarkPostDataTransaction(-1, this._postData); + childTransactions.push(postDataTxn); } //XXX TODO: this should be in a transaction! if (this._charSet) PlacesUtils.history.setCharsetForURI(this._uri, this._charSet); - var transactions = [PlacesUIUtils.ptm.createItem(this._uri, - aContainer, aIndex, - this._title, this._keyword, - annotations, - childTransactions)]; + let createTxn = new PlacesCreateBookmarkTransaction(this._uri, + aContainer, + aIndex, + this._title, + this._keyword, + annotations, + childTransactions); - return PlacesUIUtils.ptm.aggregateTransactions(this._getDialogTitle(), - transactions); + return new PlacesAggregatedTransaction(this._getDialogTitle(), + [createTxn]); }, /** * Returns a childItems-transactions array representing the URIList with * which the dialog has been opened. */ _getTransactionsForURIList: function BPP__getTransactionsForURIList() { var transactions = []; for (var i = 0; i < this._URIs.length; ++i) { var uri = this._URIs[i]; var title = this._getURITitleFromHistory(uri); - transactions.push(PlacesUIUtils.ptm.createItem(uri, -1, -1, title)); + var createTxn = new PlacesCreateBookmarkTransaction(uri, -1, + PlacesUtils.bookmarks.DEFAULT_INDEX, + title); + transactions.push(createTxn); } return transactions; }, /** * Returns a transaction for creating a new folder item representing the * various fields and opening arguments of the dialog. */ @@ -628,29 +643,30 @@ var BookmarkPropertiesPanel = { var annotations = []; var childItemsTransactions; if (this._URIs.length) childItemsTransactions = this._getTransactionsForURIList(); if (this._description) annotations.push(this._getDescriptionAnnotation(this._description)); - return PlacesUIUtils.ptm.createFolder(this._title, aContainer, aIndex, - annotations, childItemsTransactions); + return new PlacesCreateFolderTransaction(this._title, aContainer, + aIndex, annotations, + childItemsTransactions); }, /** * Returns a transaction for creating a new live-bookmark item representing * the various fields and opening arguments of the dialog. */ _getCreateNewLivemarkTransaction: function BPP__getCreateNewLivemarkTransaction(aContainer, aIndex) { - return PlacesUIUtils.ptm.createLivemark(this._feedURI, this._siteURI, - this._title, - aContainer, aIndex); + return new PlacesCreateLivemarkTransaction(this._feedURI, this._siteURI, + this._title, + aContainer, aIndex); }, /** * Dialog-accept code-path for creating a new item (any type) */ _createNewItem: function BPP__getCreateItemTransaction() { var [container, index] = this._getInsertionPointDetails(); var txn; @@ -661,12 +677,12 @@ var BookmarkPropertiesPanel = { break; case LIVEMARK_CONTAINER: txn = this._getCreateNewLivemarkTransaction(container, index); break; default: // BOOKMARK_ITEM txn = this._getCreateNewBookmarkTransaction(container, index); } - PlacesUIUtils.ptm.doTransaction(txn); + PlacesUtils.transactionManager.doTransaction(txn); this._itemId = PlacesUtils.bookmarks.getIdForItemAt(container, index); } };
--- a/browser/components/places/content/browserPlacesViews.js +++ b/browser/components/places/content/browserPlacesViews.js @@ -1853,23 +1853,26 @@ PlacesMenu.prototype = { this._onPopupHidden(aEvent); break; } }, _onPopupHidden: function PM__onPopupHidden(aEvent) { // Avoid handling popuphidden of inner views. let popup = aEvent.originalTarget; - if (!popup._placesNode || PlacesUIUtils.getViewForNode(popup) != this) + let placesNode = popup._placesNode; + if (!placesNode || PlacesUIUtils.getViewForNode(popup) != this) return; // UI performance: folder queries are cheap, keep the resultnode open // so we don't rebuild its contents whenever the popup is reopened. - if (!PlacesUtils.nodeIsFolder(popup._placesNode)) - popup._placesNode.containerOpen = false; + // Though, we want to always close feed containers so their expiration + // status will be checked at next opening. + if (!PlacesUtils.nodeIsFolder(placesNode) || placesNode._feedURI) + placesNode.containerOpen = false; // The autoopened attribute is set for folders which have been // automatically opened when dragged over. Turn off this attribute // when the folder closes because it is no longer applicable. popup.removeAttribute("autoopened"); popup.removeAttribute("dragstart"); } };
--- a/browser/components/places/content/controller.js +++ b/browser/components/places/content/controller.js @@ -152,19 +152,19 @@ PlacesController.prototype = { // filters out other commands that we do _not_ support (see 329587). const CMD_PREFIX = "placesCmd_"; return (aCommand.substr(0, CMD_PREFIX.length) == CMD_PREFIX); }, isCommandEnabled: function PC_isCommandEnabled(aCommand) { switch (aCommand) { case "cmd_undo": - return PlacesUIUtils.ptm.numberOfUndoItems > 0; + return PlacesUtils.transactionManager.numberOfUndoItems > 0; case "cmd_redo": - return PlacesUIUtils.ptm.numberOfRedoItems > 0; + return PlacesUtils.transactionManager.numberOfRedoItems > 0; case "cmd_cut": case "placesCmd_cut": var nodes = this._view.selectedNodes; // If selection includes history nodes there's no reason to allow cut. for (var i = 0; i < nodes.length; i++) { if (nodes[i].itemId == -1) return false; } @@ -225,20 +225,20 @@ PlacesController.prototype = { default: return false; } }, doCommand: function PC_doCommand(aCommand) { switch (aCommand) { case "cmd_undo": - PlacesUIUtils.ptm.undoTransaction(); + PlacesUtils.transactionManager.undoTransaction(); break; case "cmd_redo": - PlacesUIUtils.ptm.redoTransaction(); + PlacesUtils.transactionManager.redoTransaction(); break; case "cmd_cut": case "placesCmd_cut": this.cut(); break; case "cmd_copy": case "placesCmd_copy": this.copy(); @@ -780,18 +780,18 @@ PlacesController.prototype = { /** * Create a new Bookmark separator somewhere. */ newSeparator: function PC_newSeparator() { var ip = this._view.insertionPoint; if (!ip) throw Cr.NS_ERROR_NOT_AVAILABLE; - var txn = PlacesUIUtils.ptm.createSeparator(ip.itemId, ip.index); - PlacesUIUtils.ptm.doTransaction(txn); + var txn = new PlacesCreateSeparatorTransaction(ip.itemId, ip.index); + PlacesUtils.transactionManager.doTransaction(txn); // select the new item var insertedNodeId = PlacesUtils.bookmarks .getIdForItemAt(ip.itemId, ip.index); this._view.selectItems([insertedNodeId], false); }, /** * Opens a dialog for moving the selected nodes. @@ -802,18 +802,18 @@ PlacesController.prototype = { this._view.selectedNodes); }, /** * Sort the selected folder by name */ sortFolderByName: function PC_sortFolderByName() { var itemId = PlacesUtils.getConcreteItemId(this._view.selectedNode); - var txn = PlacesUIUtils.ptm.sortFolderByName(itemId); - PlacesUIUtils.ptm.doTransaction(txn); + var txn = new PlacesSortFolderByNameTransaction(itemId); + PlacesUtils.transactionManager.doTransaction(txn); }, /** * Walk the list of folders we're removing in this delete operation, and * see if the selected node specified is already implicitly being removed * because it is a child of that folder. * @param node * Node to check for containment. @@ -867,30 +867,33 @@ PlacesController.prototype = { if (this._shouldSkipNode(node, removedFolders)) continue; if (PlacesUtils.nodeIsTagQuery(node.parent)) { // This is a uri node inside a tag container. It needs a special // untag transaction. var tagItemId = PlacesUtils.getConcreteItemId(node.parent); var uri = NetUtil.newURI(node.uri); - transactions.push(PlacesUIUtils.ptm.untagURI(uri, [tagItemId])); + let txn = new PlacesUntagURITransaction(uri, [tagItemId]); + transactions.push(txn); } else if (PlacesUtils.nodeIsTagQuery(node) && node.parent && PlacesUtils.nodeIsQuery(node.parent) && PlacesUtils.asQuery(node.parent).queryOptions.resultType == Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY) { // This is a tag container. // Untag all URIs tagged with this tag only if the tag container is // child of the "Tags" query in the library, in all other places we // must only remove the query node. var tag = node.title; var URIs = PlacesUtils.tagging.getURIsForTag(tag); - for (var j = 0; j < URIs.length; j++) - transactions.push(PlacesUIUtils.ptm.untagURI(URIs[j], [tag])); + for (var j = 0; j < URIs.length; j++) { + let txn = new PlacesUntagURITransaction(URIs[j], [tag]); + transactions.push(txn); + } } else if (PlacesUtils.nodeIsURI(node) && PlacesUtils.nodeIsQuery(node.parent) && PlacesUtils.asQuery(node.parent).queryOptions.queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) { // This is a uri node inside an history query. PlacesUtils.bhistory.removePage(NetUtil.newURI(node.uri)); // History deletes are not undoable, so we don't have a transaction. @@ -907,17 +910,18 @@ PlacesController.prototype = { } else { // This is a common bookmark item. if (PlacesUtils.nodeIsFolder(node)) { // If this is a folder we add it to our array of folders, used // to skip nodes that are children of an already removed folder. removedFolders.push(node); } - transactions.push(PlacesUIUtils.ptm.removeItem(node.itemId)); + let txn = new PlacesRemoveItemTransaction(node.itemId); + transactions.push(txn); } } }, /** * Removes the set of selected ranges from bookmarks. * @param txnName * See |remove|. @@ -926,18 +930,18 @@ PlacesController.prototype = { var ranges = this._view.removableSelectionRanges; var transactions = []; var removedFolders = []; for (var i = 0; i < ranges.length; i++) this._removeRange(ranges[i], transactions, removedFolders); if (transactions.length > 0) { - var txn = PlacesUIUtils.ptm.aggregateTransactions(txnName, transactions); - PlacesUIUtils.ptm.doTransaction(txn); + var txn = new PlacesAggregatedTransaction(txnName, transactions); + PlacesUtils.transactionManager.doTransaction(txn); } }, /** * Removes the set of selected ranges from history. * * @note history deletes are not undoable. */ @@ -1273,37 +1277,35 @@ PlacesController.prototype = { } let transactions = []; let insertionIndex = ip.index; for (let i = 0; i < items.length; ++i) { if (ip.isTag) { // Pasting into a tag container means tagging the item, regardless of // the requested action. - transactions.push( - new PlacesTagURITransaction(NetUtil.newURI(items[i].uri), - [ip.itemId]) - ); + let tagTxn = new PlacesTagURITransaction(NetUtil.newURI(items[i].uri), + [ip.itemId]); + transactions.push(tagTxn); continue; } // Adjust index to make sure items are pasted in the correct position. // If index is DEFAULT_INDEX, items are just appended. if (ip.index != PlacesUtils.bookmarks.DEFAULT_INDEX) insertionIndex = ip.index + i; transactions.push( PlacesUIUtils.makeTransaction(items[i], type, ip.itemId, insertionIndex, action == "copy") ); } - PlacesUtils.transactionManager.doTransaction( - new PlacesAggregatedTransaction("Paste", transactions) - ); + let aggregatedTxn = new PlacesAggregatedTransaction("Paste", transactions); + PlacesUtils.transactionManager.doTransaction(aggregatedTxn); // Cut/past operations are not repeatable, so clear the clipboard. if (action == "cut") { this._clearClipboard(); } // Select the pasted items, they should be consecutive. let insertedNodeIds = []; @@ -1543,27 +1545,28 @@ let PlacesControllerDragHelper = { if (index != -1 && dragginUp) index+= movedCount++; // If dragging over a tag container we should tag the item. if (insertionPoint.isTag && insertionPoint.orientation == Ci.nsITreeView.DROP_ON) { let uri = NetUtil.newURI(unwrapped.uri); let tagItemId = insertionPoint.itemId; - transactions.push(PlacesUIUtils.ptm.tagURI(uri,[tagItemId])); + let tagTxn = new PlacesTagURITransaction(uri, [tagItemId]); + transactions.push(tagTxn); } else { transactions.push(PlacesUIUtils.makeTransaction(unwrapped, flavor, insertionPoint.itemId, index, doCopy)); } } - let txn = PlacesUIUtils.ptm.aggregateTransactions("DropItems", transactions); - PlacesUIUtils.ptm.doTransaction(txn); + let txn = new PlacesAggregatedTransaction("DropItems", transactions); + PlacesUtils.transactionManager.doTransaction(txn); }, /** * Checks if we can insert into a container. * @param aContainer * The container were we are want to drop */ disallowInsertion: function(aContainer) {
--- a/browser/components/places/content/editBookmarkOverlay.js +++ b/browser/components/places/content/editBookmarkOverlay.js @@ -430,25 +430,28 @@ var gEditItemOverlay = { if (tags.indexOf(currentTags[i]) == -1) tagsToRemove.push(currentTags[i]); } for (var i = 0; i < tags.length; i++) { if (currentTags.indexOf(tags[i]) == -1) tagsToAdd.push(tags[i]); } - if (tagsToRemove.length > 0) - txns.push(PlacesUIUtils.ptm.untagURI(this._uri, tagsToRemove)); - if (tagsToAdd.length > 0) - txns.push(PlacesUIUtils.ptm.tagURI(this._uri, tagsToAdd)); + if (tagsToRemove.length > 0) { + let untagTxn = new PlacesUntagURITransaction(this._uri, tagsToRemove); + txns.push(untagTxn); + } + if (tagsToAdd.length > 0) { + let tagTxn = new PlacesTagURITransaction(this._uri, tagsToAdd); + txns.push(tagTxn); + } if (txns.length > 0) { - var aggregate = PlacesUIUtils.ptm.aggregateTransactions("Update tags", - txns); - PlacesUIUtils.ptm.doTransaction(aggregate); + let aggregate = new PlacesAggregatedTransaction("Update tags", txns); + PlacesUtils.transactionManager.doTransaction(aggregate); // Ensure the tagsField is in sync, clean it up from empty tags var tags = PlacesUtils.tagging.getTagsForURI(this._uri).join(", "); this._initTextField("tagsField", tags, false); return true; } } return false; @@ -490,105 +493,116 @@ var gEditItemOverlay = { tagsToAdd[i] = []; for (var j = 0; j < tags.length; j++) { if (this._tags[i].indexOf(tags[j]) == -1) tagsToAdd[i].push(tags[j]); } } if (tagsToAdd.length > 0) { - for (i = 0; i < this._uris.length; i++) { - if (tagsToAdd[i].length > 0) - txns.push(PlacesUIUtils.ptm.tagURI(this._uris[i], tagsToAdd[i])); + for (let i = 0; i < this._uris.length; i++) { + if (tagsToAdd[i].length > 0) { + let tagTxn = new PlacesTagURITransaction(this._uris[i], + tagsToAdd[i]); + txns.push(tagTxn); + } } } if (tagsToRemove.length > 0) { - for (var i = 0; i < this._uris.length; i++) - txns.push(PlacesUIUtils.ptm.untagURI(this._uris[i], tagsToRemove)); + for (let i = 0; i < this._uris.length; i++) { + let untagTxn = new PlacesUntagURITransaction(this._uris[i], + tagsToRemove); + txns.push(untagTxn); + } } if (txns.length > 0) { - var aggregate = PlacesUIUtils.ptm.aggregateTransactions("Update tags", - txns); - PlacesUIUtils.ptm.doTransaction(aggregate); + let aggregate = new PlacesAggregatedTransaction("Update tags", txns); + PlacesUtils.transactionManager.doTransaction(aggregate); this._allTags = tags; this._tags = []; - for (i = 0; i < this._uris.length; i++) + for (let i = 0; i < this._uris.length; i++) { this._tags[i] = PlacesUtils.tagging.getTagsForURI(this._uris[i]); + } // Ensure the tagsField is in sync, clean it up from empty tags this._initTextField("tagsField", tags, false); return true; } } return false; }, onNamePickerChange: function EIO_onNamePickerChange() { if (this._itemId == -1) return; var namePicker = this._element("namePicker") - var txns = []; - const ptm = PlacesUIUtils.ptm; // Here we update either the item title or its cached static title var newTitle = namePicker.value; if (!newTitle && PlacesUtils.bookmarks.getFolderIdForItem(this._itemId) == PlacesUtils.tagsFolderId) { // We don't allow setting an empty title for a tag, restore the old one. this._initNamePicker(); } else if (this._getItemStaticTitle() != newTitle) { this._mayUpdateFirstEditField("namePicker"); - txns.push(ptm.editItemTitle(this._itemId, newTitle)); + let txn = new PlacesEditItemTitleTransaction(this._itemId, newTitle); + PlacesUtils.transactionManager.doTransaction(txn); } - - var aggregate = ptm.aggregateTransactions("Edit Item Title", txns); - ptm.doTransaction(aggregate); }, onDescriptionFieldBlur: function EIO_onDescriptionFieldBlur() { var description = this._element("descriptionField").value; if (description != PlacesUIUtils.getItemDescription(this._itemId)) { - var txn = PlacesUIUtils.ptm - .editItemDescription(this._itemId, description); - PlacesUIUtils.ptm.doTransaction(txn); + var annoObj = { name : PlacesUIUtils.DESCRIPTION_ANNO, + type : Ci.nsIAnnotationService.TYPE_STRING, + flags : 0, + value : description, + expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; + var txn = new PlacesSetItemAnnotationTransaction(this._itemId, annoObj); + PlacesUtils.transactionManager.doTransaction(txn); } }, onLocationFieldBlur: function EIO_onLocationFieldBlur() { var uri; try { uri = PlacesUIUtils.createFixedURI(this._element("locationField").value); } catch(ex) { return; } if (!this._uri.equals(uri)) { - var txn = PlacesUIUtils.ptm.editBookmarkURI(this._itemId, uri); - PlacesUIUtils.ptm.doTransaction(txn); + var txn = new PlacesEditBookmarkURITransaction(this._itemId, uri); + PlacesUtils.transactionManager.doTransaction(txn); this._uri = uri; } }, onKeywordFieldBlur: function EIO_onKeywordFieldBlur() { var keyword = this._element("keywordField").value; if (keyword != PlacesUtils.bookmarks.getKeywordForBookmark(this._itemId)) { - var txn = PlacesUIUtils.ptm.editBookmarkKeyword(this._itemId, keyword); - PlacesUIUtils.ptm.doTransaction(txn); + var txn = new PlacesEditBookmarkKeywordTransaction(this._itemId, keyword); + PlacesUtils.transactionManager.doTransaction(txn); } }, onLoadInSidebarCheckboxCommand: function EIO_onLoadInSidebarCheckboxCommand() { var loadInSidebarChecked = this._element("loadInSidebarCheckbox").checked; - var txn = PlacesUIUtils.ptm.setLoadInSidebar(this._itemId, - loadInSidebarChecked); - PlacesUIUtils.ptm.doTransaction(txn); + var annoObj = { name : PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO, + type : Ci.nsIAnnotationService.TYPE_INT32, + flags : 0, + value : loadInSidebarChecked, + expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; + var txn = new PlacesSetItemAnnotationTransaction(this._itemId, + annoObj); + PlacesUtils.transactionManager.doTransaction(txn); }, toggleFolderTreeVisibility: function EIO_toggleFolderTreeVisibility() { var expander = this._element("foldersExpander"); var folderTreeRow = this._element("folderTreeRow"); if (!folderTreeRow.collapsed) { expander.className = "expander-down"; expander.setAttribute("tooltiptext", @@ -667,18 +681,20 @@ var gEditItemOverlay = { // menulist right away setTimeout(function(self) self.toggleFolderTreeVisibility(), 100, this); return; } // Move the item var container = this._getFolderIdFromMenuList(); if (PlacesUtils.bookmarks.getFolderIdForItem(this._itemId) != container) { - var txn = PlacesUIUtils.ptm.moveItem(this._itemId, container, -1); - PlacesUIUtils.ptm.doTransaction(txn); + var txn = new PlacesMoveItemTransaction(this._itemId, + container, + PlacesUtils.bookmarks.DEFAULT_INDEX); + PlacesUtils.transactionManager.doTransaction(txn); // Mark the containing folder as recently-used if it isn't in the // static list if (container != PlacesUtils.unfiledBookmarksFolderId && container != PlacesUtils.toolbarFolderId && container != PlacesUtils.bookmarksMenuFolderId) this._markFolderAsRecentlyUsed(container); } @@ -715,25 +731,27 @@ var gEditItemOverlay = { _markFolderAsRecentlyUsed: function EIO__markFolderAsRecentlyUsed(aFolderId) { var txns = []; // Expire old unused recent folders var anno = this._getLastUsedAnnotationObject(false); while (this._recentFolders.length > MAX_FOLDER_ITEM_IN_MENU_LIST) { var folderId = this._recentFolders.pop().folderId; - txns.push(PlacesUIUtils.ptm.setItemAnnotation(folderId, anno)); + let annoTxn = new PlacesSetItemAnnotationTransaction(folderId, anno); + txns.push(annoTxn); } // Mark folder as recently used anno = this._getLastUsedAnnotationObject(true); - txns.push(PlacesUIUtils.ptm.setItemAnnotation(aFolderId, anno)); + let annoTxn = new PlacesSetItemAnnotationTransaction(aFolderId, anno); + txns.push(annoTxn); - var aggregate = PlacesUIUtils.ptm.aggregateTransactions("Update last used folders", txns); - PlacesUIUtils.ptm.doTransaction(aggregate); + let aggregate = new PlacesAggregatedTransaction("Update last used folders", txns); + PlacesUtils.transactionManager.doTransaction(aggregate); }, /** * Returns an object which could then be used to set/unset the * LAST_USED_ANNO annotation for a folder. * * @param aLastUsed * Whether to set or unset the LAST_USED_ANNO annotation. @@ -835,18 +853,18 @@ var gEditItemOverlay = { if (!ip || ip.itemId == PlacesUIUtils.allBookmarksFolderId) { ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId, PlacesUtils.bookmarks.DEFAULT_INDEX, Ci.nsITreeView.DROP_ON); } // XXXmano: add a separate "New Folder" string at some point... var defaultLabel = this._element("newFolderButton").label; - var txn = PlacesUIUtils.ptm.createFolder(defaultLabel, ip.itemId, ip.index); - PlacesUIUtils.ptm.doTransaction(txn); + var txn = new PlacesCreateFolderTransaction(defaultLabel, ip.itemId, ip.index); + PlacesUtils.transactionManager.doTransaction(txn); this._folderTree.focus(); this._folderTree.selectItems([this._lastNewItem]); this._folderTree.startEditing(this._folderTree.view.selection.currentIndex, this._folderTree.columns.getFirstColumn()); }, // nsIDOMEventListener handleEvent: function EIO_nsIDOMEventListener(aEvent) {
--- a/browser/components/places/content/moveBookmarks.js +++ b/browser/components/places/content/moveBookmarks.js @@ -62,23 +62,25 @@ var gMoveBookmarksDialog = { var selectedFolderID = PlacesUtils.getConcreteItemId(selectedNode); var transactions = []; for (var i=0; i < this._nodes.length; i++) { // Nothing to do if the node is already under the selected folder if (this._nodes[i].parent.itemId == selectedFolderID) continue; - transactions.push(new - PlacesUIUtils.ptm.moveItem(this._nodes[i].itemId, selectedFolderID, -1)); + let txn = new PlacesMoveItemTransaction(this._nodes[i].itemId, + selectedFolderID, + PlacesUtils.bookmarks.DEFAULT_INDEX); + transactions.push(txn); } if (transactions.length != 0) { - var txn = PlacesUIUtils.ptm.aggregateTransactions("Move Items", transactions); - PlacesUIUtils.ptm.doTransaction(txn); + let txn = new PlacesAggregatedTransaction("Move Items", transactions); + PlacesUtils.transactionManager.doTransaction(txn); } }, newFolder: function MBD_newFolder() { // The command is disabled when the tree is not focused this.foldersTree.focus(); goDoCommand("placesCmd_new:folder"); }
--- a/browser/components/places/content/places.js +++ b/browser/components/places/content/places.js @@ -823,21 +823,21 @@ var PlacesOrganizer = { var input = {value: defaultText}; var save = prompts.prompt(null, title, inputLabel, input, null, check); // Don't add the query if the user cancels or clears the seach name. if (!save || input.value == "") return; // Add the place: uri as a bookmark under the bookmarks root. - var txn = PlacesUIUtils.ptm.createItem(placeURI, - PlacesUtils.bookmarksMenuFolderId, - PlacesUtils.bookmarks.DEFAULT_INDEX, - input.value); - PlacesUIUtils.ptm.doTransaction(txn); + var txn = new PlacesCreateBookmarkTransaction(placeURI, + PlacesUtils.bookmarksMenuFolderId, + PlacesUtils.bookmarks.DEFAULT_INDEX, + input.value); + PlacesUtils.transactionManager.doTransaction(txn); // select and load the new query this._places.selectPlaceURI(placeSpec); } }; /** * A set of utilities relating to search within Bookmarks and History.
--- a/browser/components/places/content/treeView.js +++ b/browser/components/places/content/treeView.js @@ -1655,18 +1655,18 @@ PlacesTreeView.prototype = { return true; }, setCellText: function PTV_setCellText(aRow, aColumn, aText) { // We may only get here if the cell is editable. let node = this._rows[aRow]; if (node.title != aText) { - let txn = PlacesUIUtils.ptm.editItemTitle(node.itemId, aText); - PlacesUIUtils.ptm.doTransaction(txn); + let txn = new PlacesEditItemTitleTransaction(node.itemId, aText); + PlacesUtils.transactionManager.doTransaction(txn); } }, selectionChanged: function() { }, cycleCell: function(aRow, aColumn) { }, isSelectable: function(aRow, aColumn) { return false; }, performAction: function(aAction) { }, performActionOnRow: function(aAction, aRow) { },
--- a/browser/components/places/tests/browser/Makefile.in +++ b/browser/components/places/tests/browser/Makefile.in @@ -59,17 +59,17 @@ include $(topsrcdir)/config/rules.mk browser_history_sidebar_search.js \ browser_bookmarksProperties.js \ $(warning browser_forgetthissite_single.js temporarily disabled because of very frequent oranges, see bug 551540) \ browser_library_left_pane_commands.js \ browser_drag_bookmarks_on_toolbar.js \ browser_library_middleclick.js \ browser_library_views_liveupdate.js \ browser_views_liveupdate.js \ - browser_sidebarpanels_click.js \ + $(warning browser_sidebarpanels_click.js temporarily disabled cause it breaks the treeview, see bug 658744) \ sidebarpanels_click_test_page.html \ browser_library_infoBox.js \ browser_markPageAsFollowedLink.js \ framedPage.html \ frameLeft.html \ frameRight.html \ browser_toolbar_migration.js \ browser_library_batch_delete.js \
--- a/browser/components/places/tests/browser/browser_425884.js +++ b/browser/components/places/tests/browser/browser_425884.js @@ -81,40 +81,40 @@ function test() { folderANode.containerOpen = false; var transaction = PlacesUIUtils.makeTransaction(rawNode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER, testRootId, -1, true); ok(transaction, "create transaction"); - PlacesUIUtils.ptm.doTransaction(transaction); + PlacesUtils.transactionManager.doTransaction(transaction); // confirm copy is(testRootNode.childCount, 2, "create test folder via copy"); // validate the copy var folderBNode = testRootNode.getChild(1); validate(folderBNode); // undo the transaction, confirm the removal - PlacesUIUtils.ptm.undoTransaction(); + PlacesUtils.transactionManager.undoTransaction(); is(testRootNode.childCount, 1, "confirm undo removed the copied folder"); // redo the transaction - PlacesUIUtils.ptm.redoTransaction(); + PlacesUtils.transactionManager.redoTransaction(); is(testRootNode.childCount, 2, "confirm redo re-copied the folder"); folderBNode = testRootNode.getChild(1); validate(folderBNode); // Close containers, cleaning up their observers. testRootNode.containerOpen = false; toolbarNode.containerOpen = false; // clean up - PlacesUIUtils.ptm.undoTransaction(); + PlacesUtils.transactionManager.undoTransaction(); PlacesUtils.bookmarks.removeItem(folderAId); } function populate(aFolderId) { var folderId = PlacesUtils.bookmarks.createFolder(aFolderId, "test folder", -1); PlacesUtils.bookmarks.insertBookmark(folderId, PlacesUtils._uri("http://foo"), -1, "test bookmark"); PlacesUtils.bookmarks.insertSeparator(folderId, -1); }
--- a/browser/components/places/tests/browser/browser_457473_no_copy_guid.js +++ b/browser/components/places/tests/browser/browser_457473_no_copy_guid.js @@ -80,43 +80,43 @@ function test() { // Create a copy transaction from the serialization. // this exercises the guid-filtering var transaction = PlacesUIUtils.makeTransaction(rawNode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER, testRootId, -1, true); ok(transaction, "create transaction"); // execute it, copying to the test root folder - PlacesUIUtils.ptm.doTransaction(transaction); + PlacesUtils.transactionManager.doTransaction(transaction); is(testRootNode.childCount, 2, "create test folder via copy"); // check GUIDs are different var folderBNode = testRootNode.getChild(1); ok(checkGUIDs(folderBNode, folderAGUIDs, false), "confirm folder A GUIDs don't match folder B GUIDs"); var folderBGUIDs = getGUIDs(folderBNode); ok(checkGUIDs(folderBNode, folderBGUIDs, true), "confirm test of new GUIDs"); // undo the transaction, confirm the removal - PlacesUIUtils.ptm.undoTransaction(); + PlacesUtils.transactionManager.undoTransaction(); is(testRootNode.childCount, 1, "confirm undo removed the copied folder"); // redo the transaction // confirming GUIDs persist through undo/redo - PlacesUIUtils.ptm.redoTransaction(); + PlacesUtils.transactionManager.redoTransaction(); is(testRootNode.childCount, 2, "confirm redo re-copied the folder"); folderBNode = testRootNode.getChild(1); ok(checkGUIDs(folderBNode, folderAGUIDs, false), "folder B GUIDs after undo/redo don't match folder A GUIDs"); // sanity check ok(checkGUIDs(folderBNode, folderBGUIDs, true), "folder B GUIDs after under/redo should match pre-undo/redo folder B GUIDs"); // Close containers, cleaning up their observers. testRootNode.containerOpen = false; toolbarNode.containerOpen = false; // clean up - PlacesUIUtils.ptm.undoTransaction(); + PlacesUtils.transactionManager.undoTransaction(); PlacesUtils.bookmarks.removeItem(testRootId); } function getGUIDs(aNode) { PlacesUtils.asContainer(aNode); aNode.containerOpen = true; var GUIDs = { folder: PlacesUtils.bookmarks.getItemGUID(aNode.itemId),
--- a/browser/components/places/tests/unit/xpcshell.ini +++ b/browser/components/places/tests/unit/xpcshell.ini @@ -14,11 +14,9 @@ tail = [test_browserGlue_distribution.js] [test_browserGlue_migrate.js] [test_browserGlue_prefs.js] [test_browserGlue_restore.js] [test_browserGlue_shutdown.js] [test_browserGlue_smartBookmarks.js] [test_clearHistory_shutdown.js] [test_leftpane_corruption_handling.js] -[test_placesTxn.js] [test_PUIU_makeTransaction.js] -[test_txnGUIDs.js]
--- a/browser/components/preferences/sync.js +++ b/browser/components/preferences/sync.js @@ -152,40 +152,40 @@ let gSyncPane = { * "pair" -- pair a device first * "reset" -- reset sync */ openSetup: function (wizardType) { var win = Services.wm.getMostRecentWindow("Weave:AccountSetup"); if (win) win.focus(); else { - window.openDialog("chrome://browser/content/syncSetup.xul", + window.openDialog("chrome://browser/content/sync/setup.xul", "weaveSetup", "centerscreen,chrome,resizable=no", wizardType); } }, openQuotaDialog: function () { let win = Services.wm.getMostRecentWindow("Sync:ViewQuota"); if (win) win.focus(); else - window.openDialog("chrome://browser/content/syncQuota.xul", "", + window.openDialog("chrome://browser/content/sync/quota.xul", "", "centerscreen,chrome,dialog,modal"); }, openAddDevice: function () { if (!Weave.Utils.ensureMPUnlocked()) return; let win = Services.wm.getMostRecentWindow("Sync:AddDevice"); if (win) win.focus(); else - window.openDialog("chrome://browser/content/syncAddDevice.xul", + window.openDialog("chrome://browser/content/sync/addDevice.xul", "syncAddDevice", "centerscreen,chrome,resizable=no"); }, resetSync: function () { this.openSetup("reset"); } }
--- a/browser/components/preferences/sync.xul +++ b/browser/components/preferences/sync.xul @@ -64,17 +64,17 @@ <preference id="engine.prefs" name="services.sync.engine.prefs" type="bool"/> <preference id="engine.passwords" name="services.sync.engine.passwords" type="bool"/> </preferences> <script type="application/javascript" src="chrome://browser/content/preferences/sync.js"/> <script type="application/javascript" - src="chrome://browser/content/syncUtils.js"/> + src="chrome://browser/content/sync/utils.js"/> <deck id="weavePrefsDeck"> <vbox id="noAccount" align="center"> <spacer flex="1"/> <description id="syncDesc"> &weaveDesc.label; </description>
--- a/browser/confvars.sh +++ b/browser/confvars.sh @@ -37,30 +37,35 @@ # ***** END LICENSE BLOCK ***** MOZ_APP_BASENAME=Firefox MOZ_APP_VENDOR=Mozilla MOZ_UPDATER=1 MOZ_PHOENIX=1 if test "$OS_ARCH" = "WINNT"; then - MOZ_VERIFY_MAR_SIGNATURE=1 if ! test "$HAVE_64BIT_OS"; then + MOZ_VERIFY_MAR_SIGNATURE=1 MOZ_MAINTENANCE_SERVICE=1 fi fi MOZ_CHROME_FILE_FORMAT=omni MOZ_SAFE_BROWSING=1 MOZ_SERVICES_SYNC=1 MOZ_APP_VERSION=$FIREFOX_VERSION MOZ_EXTENSIONS_DEFAULT=" gnomevfs" # MOZ_APP_DISPLAYNAME will be set by branding/configure.sh -# Changing either of these values requires a clobber to ensure correct results, +# Changing MOZ_*BRANDING_DIRECTORY requires a clobber to ensure correct results, # because branding dependencies are broken. +# MOZ_BRANDING_DIRECTORY is the default branding directory used when none is +# specified. It should never point to the "official" branding directory. +# For mozilla-beta, mozilla-release, or mozilla-central repositories, use +# "nightly" branding (until bug 659568 is fixed). +# For the mozilla-aurora repository, use "aurora". MOZ_BRANDING_DIRECTORY=browser/branding/nightly MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384} # This should usually be the same as the value MAR_CHANNEL_ID. # If more than one ID is needed, then you should use a comma separated list # of values. ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central # The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
--- a/browser/devtools/sourceeditor/orion/orion.js +++ b/browser/devtools/sourceeditor/orion/orion.js @@ -6652,17 +6652,16 @@ define("orion/textview/textView", ['orio this._viewDiv = viewDiv; viewDiv.id = "viewDiv"; viewDiv.tabIndex = -1; viewDiv.style.overflow = "auto"; viewDiv.style.position = "absolute"; viewDiv.style.top = "0px"; viewDiv.style.borderWidth = "0px"; viewDiv.style.margin = "0px"; - viewDiv.style.MozOutline = "none"; viewDiv.style.outline = "none"; body.appendChild(viewDiv); var scrollDiv = frameDocument.createElement("DIV"); this._scrollDiv = scrollDiv; scrollDiv.id = "scrollDiv"; scrollDiv.style.margin = "0px"; scrollDiv.style.borderWidth = "0px"; @@ -6695,17 +6694,16 @@ define("orion/textview/textView", ['orio clientDiv.className = "viewContent"; this._clientDiv = clientDiv; clientDiv.id = "clientDiv"; clientDiv.style.whiteSpace = "pre"; clientDiv.style.position = this._clipDiv ? "absolute" : "fixed"; clientDiv.style.borderWidth = "0px"; clientDiv.style.margin = "0px"; clientDiv.style.padding = "0px"; - clientDiv.style.MozOutline = "none"; clientDiv.style.outline = "none"; clientDiv.style.zIndex = "1"; if (isPad) { clientDiv.style.WebkitTapHighlightColor = "transparent"; } (this._clipDiv || scrollDiv).appendChild(clientDiv); if (isFirefox && !clientDiv.setCapture) { @@ -8375,45 +8373,42 @@ define("orion/textview/textView", ['orio this._hightlightRGB = "Highlight"; var selDiv1 = frameDocument.createElement("DIV"); this._selDiv1 = selDiv1; selDiv1.id = "selDiv1"; selDiv1.style.position = this._clipDiv ? "absolute" : "fixed"; selDiv1.style.borderWidth = "0px"; selDiv1.style.margin = "0px"; selDiv1.style.padding = "0px"; - selDiv1.style.MozOutline = "none"; selDiv1.style.outline = "none"; selDiv1.style.background = this._hightlightRGB; selDiv1.style.width = "0px"; selDiv1.style.height = "0px"; selDiv1.style.zIndex = "0"; parent.appendChild(selDiv1); var selDiv2 = frameDocument.createElement("DIV"); this._selDiv2 = selDiv2; selDiv2.id = "selDiv2"; selDiv2.style.position = this._clipDiv ? "absolute" : "fixed"; selDiv2.style.borderWidth = "0px"; selDiv2.style.margin = "0px"; selDiv2.style.padding = "0px"; - selDiv2.style.MozOutline = "none"; selDiv2.style.outline = "none"; selDiv2.style.background = this._hightlightRGB; selDiv2.style.width = "0px"; selDiv2.style.height = "0px"; selDiv2.style.zIndex = "0"; parent.appendChild(selDiv2); var selDiv3 = frameDocument.createElement("DIV"); this._selDiv3 = selDiv3; selDiv3.id = "selDiv3"; selDiv3.style.position = this._clipDiv ? "absolute" : "fixed"; selDiv3.style.borderWidth = "0px"; selDiv3.style.margin = "0px"; selDiv3.style.padding = "0px"; - selDiv3.style.MozOutline = "none"; selDiv3.style.outline = "none"; selDiv3.style.background = this._hightlightRGB; selDiv3.style.width = "0px"; selDiv3.style.height = "0px"; selDiv3.style.zIndex = "0"; parent.appendChild(selDiv3); /*
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -150,16 +150,17 @@ @BINPATH@/components/dom_wifi.xpt @BINPATH@/components/dom_system_b2g.xpt #endif @BINPATH@/components/dom_battery.xpt #ifdef MOZ_B2G_BT @BINPATH@/components/dom_bluetooth.xpt #endif @BINPATH@/components/dom_canvas.xpt +@BINPATH@/components/dom_contacts.xpt @BINPATH@/components/dom_core.xpt @BINPATH@/components/dom_css.xpt @BINPATH@/components/dom_events.xpt @BINPATH@/components/dom_geolocation.xpt @BINPATH@/components/dom_network.xpt @BINPATH@/components/dom_notification.xpt @BINPATH@/components/dom_html.xpt @BINPATH@/components/dom_indexeddb.xpt @@ -404,16 +405,19 @@ @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/ContactManager.js +@BINPATH@/components/ContactManager.manifest + ; Modules @BINPATH@/modules/* ; Safe Browsing @BINPATH@/components/nsSafebrowsingApplication.manifest @BINPATH@/components/nsSafebrowsingApplication.js @BINPATH@/components/nsURLClassifier.manifest @BINPATH@/components/nsUrlClassifierHashCompleter.js
--- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -328,8 +328,12 @@ syncPromoNotification.learnMoreLinkText= # LOCALIZATION NOTE (telemetryPrompt): %1$S will be replaced by brandFullName, # 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 +# Telemetry opt-out prompt for Aurora and Nightly +# LOCALIZATION NOTE (telemetryOptOutPrompt): %1$S and %3$S will be replaced by +# brandFullName, and %2$S by the value of the toolkit.telemetry.server_owner preference. +telemetryOptOutPrompt = %1$S sends information about performance, hardware, usage and customizations back to %2$S to help improve %3$S.
--- a/browser/themes/gnomestripe/syncCommon.css +++ b/browser/themes/gnomestripe/syncCommon.css @@ -1,9 +1,9 @@ -/* The following are used by both syncSetup.xul and syncGenericChange.xul */ +/* The following are used by both sync/setup.xul and sync/genericChange.xul */ .status { color: -moz-dialogtext; } .statusIcon { -moz-margin-start: 4px; max-height: 16px; max-width: 16px; @@ -16,17 +16,17 @@ .statusIcon[status="error"] { list-style-image: url("moz-icon://stock/gtk-dialog-error?size=menu"); } .statusIcon[status="success"] { list-style-image: url("moz-icon://stock/gtk-dialog-info?size=menu"); } -/* .data is only used by syncGenericChange.xul, but it seems unnecessary to have +/* .data is only used by sync/genericChange.xul, but it seems unnecessary to have a separate stylesheet for it. */ .data { font-size: 90%; font-weight: bold; } dialog#change-dialog { width: 40em;
--- a/browser/themes/pinstripe/syncCommon.css +++ b/browser/themes/pinstripe/syncCommon.css @@ -1,9 +1,9 @@ -/* The following are used by both syncSetup.xul and syncGenericChange.xul */ +/* The following are used by both sync/setup.xul and sync/genericChange.xul */ .status { color: -moz-dialogtext; } .statusIcon { -moz-margin-start: 4px; max-height: 16px; max-width: 16px; @@ -16,17 +16,17 @@ .statusIcon[status="error"] { list-style-image: url("chrome://global/skin/icons/error-16.png"); } .statusIcon[status="success"] { list-style-image: url("chrome://global/skin/icons/information-16.png"); } -/* .data is only used by syncGenericChange.xul, but it seems unnecessary to have +/* .data is only used by sync/genericChange.xul, but it seems unnecessary to have a separate stylesheet for it. */ .data { font-size: 90%; font-weight: bold; } dialog#change-dialog { width: 40em;
--- a/browser/themes/winstripe/syncCommon.css +++ b/browser/themes/winstripe/syncCommon.css @@ -1,9 +1,9 @@ -/* The following are used by both syncSetup.xul and syncGenericChange.xul */ +/* The following are used by both sync/setup.xul and sync/genericChange.xul */ .status { color: -moz-dialogtext; } .statusIcon { -moz-margin-start: 4px; max-height: 16px; max-width: 16px; @@ -16,17 +16,17 @@ .statusIcon[status="error"] { list-style-image: url("chrome://global/skin/icons/error-16.png"); } .statusIcon[status="success"] { list-style-image: url("chrome://global/skin/icons/information-16.png"); } -/* .data is only used by syncGenericChange.xul, but it seems unnecessary to have +/* .data is only used by sync/genericChange.xul, but it seems unnecessary to have a separate stylesheet for it. */ .data { font-size: 90%; font-weight: bold; } dialog#change-dialog { width: 40em;
--- a/build/autoconf/compiler-opts.m4 +++ b/build/autoconf/compiler-opts.m4 @@ -2,12 +2,12 @@ dnl Add compiler specific options AC_DEFUN([MOZ_COMPILER_OPTS], [ if test "$CLANG_CXX"; then ## We disable return-type-c-linkage because jsval is defined as a C++ type but is ## returned by C functions. This is possible because we use knowledge about the ABI ## to typedef it to a C type with the same layout when the headers are included ## from C. - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-return-type-c-linkage" + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage" fi ])
deleted file mode 100644 --- a/build/mobile/devicemanager-run-test.py +++ /dev/null @@ -1,89 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is remote test framework. -# -# The Initial Developer of the Original Code is -# the Mozilla Corporation. -# Portions created by the Initial Developer are Copyright (C) 2010 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -import devicemanager -import devicemanagerUtils -import sys -import os - -def main(): - ip_addr = os.environ.get("DEVICE_IP") - port = os.environ.get("DEVICE_PORT") - gre_path = os.environ.get("REMOTE_GRE_PATH").replace('\\','/') - - if ip_addr == None: - print "Error: please define the environment variable DEVICE_IP before running this test" - sys.exit(1) - - if port == None: - print "Error: please define the environment variable DEVICE_PORT before running this test" - sys.exit(1) - - if gre_path == None: - print "Error: please define the environment variable REMOTE_GRE_PATH before running this test" - sys.exit(1) - - dm = devicemanagerUtils.getDeviceManager(ip_addr, int(port)) - if len(sys.argv) < 2: - print "usage python devicemanager-run-test.py <test program> [args1 [arg2..]]" - sys.exit(1) - - cmd = sys.argv[1] - args = ' ' - if len(sys.argv) > 2: - args = ' ' + ' '.join(sys.argv[2:]) - - dm.debug = 0 - lastslash = cmd.rfind('/'); - if lastslash == -1: - lastslash = 0 - dm.pushFile(cmd, gre_path + cmd[lastslash:]) - process = dm.launchProcess([gre_path + cmd[lastslash:] + args]) - output_list = dm.communicate(process) - ret = -1 - if (output_list != None and output_list[0] != None): - try: - output = output_list[0] - index = output.find('exited with return code') - print output[0:index] - retstr = (output[index + 24 : output.find('\n', index)]) - ret = int(retstr) - except ValueError: - ret = -1 - sys.exit(ret) - -if __name__ == '__main__': - main()
--- a/build/mobile/devicemanager.py +++ b/build/mobile/devicemanager.py @@ -192,41 +192,16 @@ class DeviceManager: """ external function DEPRECATED: Use shell() or launchApplication() for new code returns: success: output filename failure: None """ - def communicate(self, process, timeout = 600, interval = 5): - """ - loops until 'process' has exited or 'timeout' seconds is reached - loop sleeps for 'interval' seconds between iterations - external function - returns: - success: [file contents, None] - failure: [None, None] - """ - - timed_out = True - if (timeout > 0): - total_time = 0 - while total_time < timeout: - time.sleep(interval) - if self.processExist(process) == None: - timed_out = False - break - total_time += interval - - if (timed_out == True): - return [None, None] - - return [self.getFile(process, "temp.txt"), None] - def processExist(self, appname): """ iterates process list and returns pid if exists, otherwise None external function returns: success: pid failure: None """ @@ -596,24 +571,24 @@ def _pop_last_line(file): ''' Utility function to get the last line from a file (shared between ADB and SUT device managers). Function also removes it from the file. Intended to strip off the return code from a shell command. ''' bytes_from_end = 1 file.seek(0, 2) length = file.tell() + 1 - while bytes_from_end <= length: + while bytes_from_end < length: file.seek((-1)*bytes_from_end, 2) data = file.read() - if bytes_from_end == length and len(data) == 0: # no data, return None + if bytes_from_end == length-1 and len(data) == 0: # no data, return None return None - if data[0] == '\n' or bytes_from_end == length: + if data[0] == '\n' or bytes_from_end == length-1: # found the last line, which should have the return value if data[0] == '\n': data = data[1:] # truncate off the return code line file.truncate(length - bytes_from_end) file.seek(0,2) file.write('\0')
--- a/build/mobile/devicemanagerADB.py +++ b/build/mobile/devicemanagerADB.py @@ -20,21 +20,27 @@ class DeviceManagerADB(DeviceManager): self.tempDir = None if packageName == None: if os.getenv('USER'): packageName = 'org.mozilla.fennec_' + os.getenv('USER') else: packageName = 'org.mozilla.fennec_' self.Init(packageName) + def __del__(self): + if self.host: + self.disconnectRemoteADB() + def Init(self, packageName): # Initialization code that may fail: Catch exceptions here to allow # successful initialization even if, for example, adb is not installed. try: self.verifyADB() + if self.host: + self.connectRemoteADB() self.verifyRunAs(packageName) except: self.useRunAs = False self.packageName = packageName try: self.verifyZip() except: self.useZip = False @@ -73,17 +79,17 @@ class DeviceManagerADB(DeviceManager): if arg.find(" ") or arg.find("(") or arg.find(")") or arg.find("\""): cmd[index] = '\'%s\'' % arg # This is more complex than you'd think because adb doesn't actually # return the return code from a process, so we have to capture the output # to get it # FIXME: this function buffers all output of the command into memory, # always. :( - cmdline = subprocess.list2cmdline(cmd) + "; echo $?" + cmdline = " ".join(cmd) + "; echo $?" # prepend cwd and env to command if necessary if cwd: cmdline = "cd %s; %s" % (cwd, cmdline) if env: envstr = '; '.join(map(lambda x: 'export %s=%s' % (x[0], x[1]), env.iteritems())) cmdline = envstr + "; " + cmdline @@ -99,16 +105,22 @@ class DeviceManagerADB(DeviceManager): if m: return_code = m.group(1) outputfile.seek(-2, 2) outputfile.truncate() # truncate off the return code return return_code return None + def connectRemoteADB(self): + self.checkCmd(["connect", self.host + ":" + str(self.port)]) + + def disconnectRemoteADB(self): + self.checkCmd(["disconnect", self.host + ":" + str(self.port)]) + # external function # returns: # success: True # failure: False def pushFile(self, localname, destname): try: if (os.name == "nt"): destname = destname.replace('\\', '/') @@ -432,31 +444,31 @@ class DeviceManagerADB(DeviceManager): # remote directory doesn't exist but also so that we don't call isDir # twice when recursing. # returns: # success: list of files, string # failure: None def getDirectory(self, remoteDir, localDir, checkDir=True): ret = [] p = self.runCmd(["pull", remoteDir, localDir]) - p.stderr.readline() - line = p.stderr.readline() + p.stdout.readline() + line = p.stdout.readline() while (line): els = line.split() f = els[len(els) - 1] i = f.find(localDir) if (i != -1): if (localDir[len(localDir) - 1] != '/'): i = i + 1 f = f[i + len(localDir):] i = f.find("/") if (i > 0): f = f[0:i] ret.append(f) - line = p.stderr.readline() + line = p.stdout.readline() #the last line is a summary if (len(ret) > 0): ret.pop() return ret # true/false check if the two files have the same md5 sum
--- a/build/mobile/droid.py +++ b/build/mobile/droid.py @@ -34,16 +34,17 @@ # 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 ***** from devicemanagerADB import DeviceManagerADB from devicemanagerSUT import DeviceManagerSUT +import StringIO class DroidMixin(object): """Mixin to extend DeviceManager with Android-specific functionality""" def launchApplication(self, app, activity="App", intent="android.intent.action.VIEW", env=None, url=None, extra_args=None): """ @@ -54,27 +55,27 @@ class DroidMixin(object): """ # only one instance of an application may be running at once if self.processExist(app): return False acmd = [ "am", "start", "-a", intent, "-W", "-n", "%s/.%s" % (app, activity)] if extra_args: - acmd.extend(["--es", "args", " ".join(args)]) + acmd.extend(["--es", "args", " ".join(extra_args)]) if env: envCnt = 0 # env is expected to be a dict of environment variables for envkey, envval in env.iteritems(): acmd.extend(["--es", "env" + str(envCnt), envkey + "=" + envval]) envCnt += 1 if url: - acmd.extend(["-d", ''.join(['"', url, '"'])]) + acmd.extend(["-d", ''.join(["'", url, "'"])]) # shell output not that interesting and debugging logs should already # show what's going on here... so just create an empty memory buffer # and ignore shellOutput = StringIO.StringIO() if self.shell(acmd, shellOutput) == 0: return True
--- a/build/mobile/remoteautomation.py +++ b/build/mobile/remoteautomation.py @@ -136,27 +136,27 @@ class RemoteAutomation(Automation): #TODO: figure out which platform require NO_EM_RESTART # return app, ['--environ:NO_EM_RESTART=1'] + args return app, args def getLanIp(self): nettools = NetworkTools() return nettools.getLanIp() - def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = '.'): + def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = None): if stdout == None or stdout == -1 or stdout == subprocess.PIPE: stdout = self._remoteLog return self.RProcess(self._devicemanager, cmd, stdout, stderr, env, cwd) # be careful here as this inner class doesn't have access to outer class members class RProcess(object): # device manager process dm = None - def __init__(self, dm, cmd, stdout = None, stderr = None, env = None, cwd = '.'): + def __init__(self, dm, cmd, stdout = None, stderr = None, env = None, cwd = None): self.dm = dm self.stdoutlen = 0 self.proc = dm.launchProcess(cmd, stdout, cwd, env, True) if (self.proc is None): if cmd[0] == 'am': self.proc = stdout else: raise Exception("unable to launch process")
--- a/build/mobile/sutagent/android/DoCommand.java +++ b/build/mobile/sutagent/android/DoCommand.java @@ -62,16 +62,17 @@ import java.security.NoSuchAlgorithmExce import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.StringTokenizer; import java.util.TimeZone; import java.util.zip.Adler32; import java.util.zip.CheckedInputStream; import java.util.zip.CheckedOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; @@ -131,17 +132,17 @@ public class DoCommand { String currentDir = "/"; String sErrorPrefix = "##AGENT-WARNING## "; boolean bTraceOn = false; String ffxProvider = "org.mozilla.ffxcp"; String fenProvider = "org.mozilla.fencp"; - private final String prgVersion = "SUTAgentAndroid Version 1.06"; + private final String prgVersion = "SUTAgentAndroid Version 1.07"; public enum Command { RUN ("run"), EXEC ("exec"), EXECCWD ("execcwd"), ENVRUN ("envrun"), KILL ("kill"), @@ -2444,16 +2445,58 @@ private void CancelNotification() { if (lProcesses.get(lcv).processName.contains(sProcName)) { sRet = sErrorPrefix + "Unable to kill " + nPID + " " + strProcName + "\n"; break; } } } + else + { + // To kill processes other than Java applications - processes + // like xpcshell - a different strategy is necessary: use ps + // to find the process' PID. + try + { + pProc = Runtime.getRuntime().exec("ps"); + RedirOutputThread outThrd = new RedirOutputThread(pProc, null); + outThrd.start(); + outThrd.join(10000); + sTmp = outThrd.strOutput; + StringTokenizer stokLines = new StringTokenizer(sTmp, "\n"); + while(stokLines.hasMoreTokens()) + { + String sLine = stokLines.nextToken(); + StringTokenizer stokColumns = new StringTokenizer(sLine, " \t\n"); + stokColumns.nextToken(); + String sPid = stokColumns.nextToken(); + stokColumns.nextToken(); + stokColumns.nextToken(); + stokColumns.nextToken(); + stokColumns.nextToken(); + stokColumns.nextToken(); + stokColumns.nextToken(); + String sName = null; + if (stokColumns.hasMoreTokens()) + { + sName = stokColumns.nextToken(); + if (sName.contains(sProcName)) + { + NewKillProc(sPid, out); + sRet = "Successfully killed " + sPid + " " + sName + "\n"; + } + } + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } return (sRet); } public boolean IsProcessDead(String sProcName) { boolean bRet = false; ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE); @@ -2867,25 +2910,20 @@ private void CancelNotification() } return (sRet); } public String NewKillProc(String sProcId, OutputStream out) { String sRet = ""; - String [] theArgs = new String [3]; - - theArgs[0] = "su"; - theArgs[1] = "-c"; - theArgs[2] = "kill " + sProcId; try { - pProc = Runtime.getRuntime().exec(theArgs); + pProc = Runtime.getRuntime().exec("kill "+sProcId); RedirOutputThread outThrd = new RedirOutputThread(pProc, out); outThrd.start(); outThrd.join(5000); } catch (IOException e) { sRet = e.getMessage(); e.printStackTrace();
--- a/build/unix/build-toolchain/build-gcc.py +++ b/build/unix/build-toolchain/build-gcc.py @@ -245,16 +245,18 @@ if not os.path.exists(source_dir): extract(unifdef_source_tar, source_dir) extract(mpc_source_tar, source_dir) extract(mpfr_source_tar, source_dir) extract(gmp_source_tar, source_dir) extract(gcc_source_tar, source_dir) patch('plugin_finish_decl.diff', 0, gcc_source_dir) patch('pr49911.diff', 1, gcc_source_dir) patch('r159628-r163231-r171807.patch', 1, gcc_source_dir) + patch('gcc-fixinc.patch', 1, gcc_source_dir) + patch('gcc-include.patch', 1, gcc_source_dir) if os.path.exists(build_dir): shutil.rmtree(build_dir) os.makedirs(build_dir) build_aux_tools(build_dir) stage1_dir = build_dir + '/stage1'
new file mode 100644 --- /dev/null +++ b/build/unix/build-toolchain/gcc-fixinc.patch @@ -0,0 +1,12 @@ +diff -ru a/fixincludes/Makefile.in b/fixincludes/Makefile.in +--- a/fixincludes/Makefile.in 2009-07-30 18:33:49.000000000 -0400 ++++ b/fixincludes/Makefile.in 2012-02-27 14:59:09.371875951 -0500 +@@ -126,7 +126,7 @@ + fixlib.o : fixlib.c + + fixinc.sh : fixinc.in mkfixinc.sh Makefile +- srcdir="$(srcdir)" $(SHELL) $(srcdir)/mkfixinc.sh $(target) ++ echo "#!/bin/sh" > $@ + + $(srcdir)/fixincl.x: @MAINT@ fixincl.tpl inclhack.def + cd $(srcdir) ; $(SHELL) ./genfixes
new file mode 100644 --- /dev/null +++ b/build/unix/build-toolchain/gcc-include.patch @@ -0,0 +1,24 @@ +diff -ru a/configure b/configure +--- a/configure 2010-10-06 06:29:55.000000000 -0400 ++++ b/configure 2012-02-27 20:46:26.303460301 -0500 +@@ -8047,7 +8047,7 @@ + # being built; programs in there won't even run. + if test "${build}" = "${host}" && test -d ${srcdir}/gcc; then + # Search for pre-installed headers if nothing else fits. +- FLAGS_FOR_TARGET=$FLAGS_FOR_TARGET' -B$(build_tooldir)/bin/ -B$(build_tooldir)/lib/ -isystem $(build_tooldir)/include -isystem $(build_tooldir)/sys-include' ++ FLAGS_FOR_TARGET=$FLAGS_FOR_TARGET' -B$(exec_prefix)/bin/ -B$(exec_prefix)/lib/ -isystem $(exec_prefix)/include -isystem $(exec_prefix)/sys-include' + fi + + if test "x${use_gnu_ld}" = x && +diff -ru a/configure.ac b/configure.ac +--- a/configure.ac 2010-10-06 06:29:55.000000000 -0400 ++++ b/configure.ac 2012-02-27 20:46:22.587442745 -0500 +@@ -3100,7 +3100,7 @@ + # being built; programs in there won't even run. + if test "${build}" = "${host}" && test -d ${srcdir}/gcc; then + # Search for pre-installed headers if nothing else fits. +- FLAGS_FOR_TARGET=$FLAGS_FOR_TARGET' -B$(build_tooldir)/bin/ -B$(build_tooldir)/lib/ -isystem $(build_tooldir)/include -isystem $(build_tooldir)/sys-include' ++ FLAGS_FOR_TARGET=$FLAGS_FOR_TARGET' -B$(exec_prefix)/bin/ -B$(exec_prefix)/lib/ -isystem $(exec_prefix)/include -isystem $(exec_prefix)/sys-include' + fi + + if test "x${use_gnu_ld}" = x &&
--- a/caps/src/nsSecurityManagerFactory.cpp +++ b/caps/src/nsSecurityManagerFactory.cpp @@ -68,41 +68,41 @@ nsSecurityNameSet::nsSecurityNameSet() nsSecurityNameSet::~nsSecurityNameSet() { } NS_IMPL_ISUPPORTS1(nsSecurityNameSet, nsIScriptExternalNameSet) static JSString * -getStringArgument(JSContext *cx, JSObject *obj, PRUint16 argNum, uintN argc, jsval *argv) +getStringArgument(JSContext *cx, JSObject *obj, PRUint16 argNum, unsigned argc, jsval *argv) { if (argc <= argNum || !JSVAL_IS_STRING(argv[argNum])) { JS_ReportError(cx, "String argument expected"); return nsnull; } /* * We don't want to use JS_ValueToString because we want to be able * to have an object to represent a target in subsequent versions. */ return JSVAL_TO_STRING(argv[argNum]); } static bool -getBytesArgument(JSContext *cx, JSObject *obj, PRUint16 argNum, uintN argc, jsval *argv, +getBytesArgument(JSContext *cx, JSObject *obj, PRUint16 argNum, unsigned argc, jsval *argv, JSAutoByteString *bytes) { JSString *str = getStringArgument(cx, obj, argNum, argc, argv); return str && bytes->encode(cx, str); } static void getUTF8StringArgument(JSContext *cx, JSObject *obj, PRUint16 argNum, - uintN argc, jsval *argv, nsCString& aRetval) + unsigned argc, jsval *argv, nsCString& aRetval) { aRetval.Truncate(); if (argc <= argNum || !JSVAL_IS_STRING(argv[argNum])) { JS_ReportError(cx, "String argument expected"); return; } @@ -117,17 +117,17 @@ getUTF8StringArgument(JSContext *cx, JSO const PRUnichar *data = JS_GetStringCharsZ(cx, str); if (!data) return; CopyUTF16toUTF8(data, aRetval); } static JSBool -netscape_security_isPrivilegeEnabled(JSContext *cx, uintN argc, jsval *vp) +netscape_security_isPrivilegeEnabled(JSContext *cx, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; bool result = false; if (JSString *str = getStringArgument(cx, obj, 0, argc, JS_ARGV(cx, vp))) { JSAutoByteString cap(cx, str); @@ -148,17 +148,17 @@ netscape_security_isPrivilegeEnabled(JSC return JS_FALSE; } JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(result)); return JS_TRUE; } static JSBool -netscape_security_enablePrivilege(JSContext *cx, uintN argc, jsval *vp) +netscape_security_enablePrivilege(JSContext *cx, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; JSAutoByteString cap; if (!getBytesArgument(cx, obj, 0, argc, JS_ARGV(cx, vp), &cap)) return JS_FALSE; @@ -191,17 +191,17 @@ netscape_security_enablePrivilege(JSCont rv = securityManager->EnableCapability(cap.ptr()); if (NS_FAILED(rv)) return JS_FALSE; JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } static JSBool -netscape_security_disablePrivilege(JSContext *cx, uintN argc, jsval *vp) +netscape_security_disablePrivilege(JSContext *cx, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; JSAutoByteString cap; if (!getBytesArgument(cx, obj, 0, argc, JS_ARGV(cx, vp), &cap)) return JS_FALSE; @@ -217,17 +217,17 @@ netscape_security_disablePrivilege(JSCon rv = securityManager->DisableCapability(cap.ptr()); if (NS_FAILED(rv)) return JS_FALSE; JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } static JSBool -netscape_security_revertPrivilege(JSContext *cx, uintN argc, jsval *vp) +netscape_security_revertPrivilege(JSContext *cx, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; JSAutoByteString cap; if (!getBytesArgument(cx, obj, 0, argc, JS_ARGV(cx, vp), &cap)) return JS_FALSE; @@ -243,17 +243,17 @@ netscape_security_revertPrivilege(JSCont rv = securityManager->RevertCapability(cap.ptr()); if (NS_FAILED(rv)) return JS_FALSE; JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } static JSBool -netscape_security_setCanEnablePrivilege(JSContext *cx, uintN argc, jsval *vp) +netscape_security_setCanEnablePrivilege(JSContext *cx, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; if (argc < 2) return JS_FALSE; nsCAutoString principalFingerprint; getUTF8StringArgument(cx, obj, 0, argc, JS_ARGV(cx, vp), principalFingerprint); @@ -275,17 +275,17 @@ netscape_security_setCanEnablePrivilege( nsIPrincipal::ENABLE_GRANTED); if (NS_FAILED(rv)) return JS_FALSE; JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } static JSBool -netscape_security_invalidate(JSContext *cx, uintN argc, jsval *vp) +netscape_security_invalidate(JSContext *cx, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsCAutoString principalFingerprint; getUTF8StringArgument(cx, obj, 0, argc, JS_ARGV(cx, vp), principalFingerprint); if (principalFingerprint.IsEmpty())
--- a/content/base/public/nsDOMFile.h +++ b/content/base/public/nsDOMFile.h @@ -53,17 +53,17 @@ #include "nsIXMLHttpRequest.h" #include "prmem.h" #include "nsAutoPtr.h" #include "mozilla/dom/indexedDB/FileInfo.h" #include "mozilla/dom/indexedDB/FileManager.h" #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" #include "mozilla/GuardObjects.h" -#include "mozilla/StdInt.h" +#include "mozilla/StandardInteger.h" class nsIFile; class nsIInputStream; class nsIClassInfo; class nsIBlobBuilder; nsresult NS_NewBlobBuilder(nsISupports* *aSupports);
--- a/content/base/public/nsIObjectLoadingContent.idl +++ b/content/base/public/nsIObjectLoadingContent.idl @@ -37,26 +37,27 @@ #include "nsISupports.idl" interface nsIFrame; interface nsIObjectFrame; interface nsIPluginTag; interface nsIDOMElement; interface nsIDOMClientRect; +interface nsIURI; %{C++ #include "nsNPAPIPluginInstance.h" %} [ptr] native nsNPAPIPluginInstancePtr(nsNPAPIPluginInstance); /** * This interface represents a content node that loads objects. */ -[scriptable, uuid(6D8914C7-0E22-4452-8962-11B69BBE84D7)] +[scriptable, uuid(3FF07AB3-5BAC-4D98-9549-5BD15CCEBCD3)] interface nsIObjectLoadingContent : nsISupports { const unsigned long TYPE_LOADING = 0; const unsigned long TYPE_IMAGE = 1; const unsigned long TYPE_PLUGIN = 2; const unsigned long TYPE_DOCUMENT = 3; const unsigned long TYPE_NULL = 4; @@ -75,16 +76,24 @@ interface nsIObjectLoadingContent : nsIS /** * Gets the content type that corresponds to the give MIME type. See the * constants above for the list of possible values. If nothing else fits, * TYPE_NULL will be returned. */ unsigned long getContentTypeForMIMEType(in AUTF8String aMimeType); /** + * Gets the base URI to be used for this object. This differs from + * nsIContent::GetBaseURI in that it takes codebase attributes into + * account. The MIME type is required as some plugins (java) calculate + * this differently. + */ + nsIURI GetObjectBaseURI(in ACString aMimeType); + + /** * Returns the plugin instance if it has already been instantiated. This * will never instantiate the plugin and so is safe to call even when * content script must not execute. */ [noscript] readonly attribute nsNPAPIPluginInstancePtr pluginInstance; /** * Tells the content about an associated object frame.
--- a/content/base/src/nsContentAreaDragDrop.cpp +++ b/content/base/src/nsContentAreaDragDrop.cpp @@ -403,19 +403,20 @@ DragDataProducer::Produce(nsDOMDataTrans nsCOMPtr<nsISelection> selection; nsIContent* editingElement = mSelectionTargetNode->IsEditable() ? mSelectionTargetNode->GetEditingHost() : nsnull; nsCOMPtr<nsITextControlElement> textControl(do_QueryInterface(editingElement)); if (textControl) { nsISelectionController* selcon = textControl->GetSelectionController(); if (selcon) { selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); - if (!selection) - return NS_OK; } + + if (!selection) + return NS_OK; } else { mWindow->GetSelection(getter_AddRefs(selection)); if (!selection) return NS_OK; // Check if the node is inside a form control. Don't set aCanDrag to false //however, as we still want to allow the drag. @@ -444,21 +445,24 @@ DragDataProducer::Produce(nsDOMDataTrans } } // In chrome shells, only allow dragging inside editable areas. if (isChromeShell && !editingElement) return NS_OK; if (isChromeShell && textControl) { - // Only use the selection if it isn't collapsed. - bool isCollapsed = false; - selection->GetIsCollapsed(&isCollapsed); - if (!isCollapsed) - selection.swap(*aSelection); + // Only use the selection if the target node is in the selection. + bool selectionContainsTarget = false; + nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(mSelectionTargetNode); + selection->ContainsNode(targetNode, false, &selectionContainsTarget); + if (!selectionContainsTarget) + return NS_OK; + + selection.swap(*aSelection); } else { // In content shells, a number of checks are made below to determine // whether an image or a link is being dragged. If so, add additional // data to the data transfer. This is also done for chrome shells, but // only when in a non-textbox editor. bool haveSelectedContent = false;
--- a/content/base/src/nsDOMBlobBuilder.h +++ b/content/base/src/nsDOMBlobBuilder.h @@ -36,17 +36,17 @@ * ***** END LICENSE BLOCK ***** */ #ifndef nsDOMBlobBuilder_h #define nsDOMBlobBuilder_h #include "nsDOMFile.h" #include "CheckedInt.h" -#include "mozilla/StdInt.h" +#include "mozilla/StandardInteger.h" using namespace mozilla; class nsDOMMultipartFile : public nsDOMFileBase { public: // Create as a file nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
--- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -152,16 +152,18 @@ #include "prprf.h" #include "nsSVGFeatures.h" #include "nsDOMMemoryReporter.h" #include "nsWrapperCacheInlines.h" #include "nsCycleCollector.h" #include "xpcpublic.h" #include "xpcprivate.h" +#include "nsLayoutStatics.h" +#include "mozilla/Telemetry.h" using namespace mozilla; using namespace mozilla::dom; NS_DEFINE_IID(kThisPtrOffsetsSID, NS_THISPTROFFSETS_SID); PRInt32 nsIContent::sTabFocusModel = eTabFocus_any; bool nsIContent::sTabFocusModelAppliesToXUL = false; @@ -171,17 +173,17 @@ nsresult NS_NewContentIterator(nsIConten void nsWrapperCache::RemoveExpandoObject() { JSObject *expando = GetExpandoObjectPreserveColor(); if (expando) { JSCompartment *compartment = js::GetObjectCompartment(expando); xpc::CompartmentPrivate *priv = - static_cast<xpc::CompartmentPrivate *>(js_GetCompartmentPrivate(compartment)); + static_cast<xpc::CompartmentPrivate *>(JS_GetCompartmentPrivate(compartment)); priv->RemoveDOMExpandoObject(expando); } } //---------------------------------------------------------------------- nsINode::nsSlots::~nsSlots() { @@ -4369,48 +4371,145 @@ nsINode::IsEqualNode(nsIDOMNode* aOther, } //---------------------------------------------------------------------- // nsISupports implementation NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericElement) +#define SUBTREE_UNBINDINGS_PER_RUNNABLE 500 + +class ContentUnbinder : public nsRunnable +{ +public: + ContentUnbinder() + { + nsLayoutStatics::AddRef(); + mLast = this; + } + + ~ContentUnbinder() + { + Run(); + nsLayoutStatics::Release(); + } + + void UnbindSubtree(nsIContent* aNode) + { + if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE && + aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) { + return; + } + nsGenericElement* container = static_cast<nsGenericElement*>(aNode); + PRUint32 childCount = container->mAttrsAndChildren.ChildCount(); + if (childCount) { + while (childCount-- > 0) { + // Hold a strong ref to the node when we remove it, because we may be + // the last reference to it. We need to call TakeChildAt() and + // update mFirstChild before calling UnbindFromTree, since this last + // can notify various observers and they should really see consistent + // tree state. + nsCOMPtr<nsIContent> child = + container->mAttrsAndChildren.TakeChildAt(childCount); + if (childCount == 0) { + container->mFirstChild = nsnull; + } + UnbindSubtree(child); + child->UnbindFromTree(); + } + } + } + + NS_IMETHOD Run() + { + nsAutoScriptBlocker scriptBlocker; + PRUint32 len = mSubtreeRoots.Length(); + if (len) { + PRTime start = PR_Now(); + for (PRUint32 i = 0; i < len; ++i) { + UnbindSubtree(mSubtreeRoots[i]); + } + mSubtreeRoots.Clear(); + Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND, + PRUint32(PR_Now() - start) / PR_USEC_PER_MSEC); + } + if (this == sContentUnbinder) { + sContentUnbinder = nsnull; + if (mNext) { + nsRefPtr<ContentUnbinder> next; + next.swap(mNext); + sContentUnbinder = next; + next->mLast = mLast; + mLast = nsnull; + NS_DispatchToMainThread(next); + } + } + return NS_OK; + } + + static void Append(nsIContent* aSubtreeRoot) + { + if (!sContentUnbinder) { + sContentUnbinder = new ContentUnbinder(); + nsCOMPtr<nsIRunnable> e = sContentUnbinder; + NS_DispatchToMainThread(e); + } + + if (sContentUnbinder->mLast->mSubtreeRoots.Length() >= + SUBTREE_UNBINDINGS_PER_RUNNABLE) { + sContentUnbinder->mLast->mNext = new ContentUnbinder(); + sContentUnbinder->mLast = sContentUnbinder->mLast->mNext; + } + sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot); + } + +private: + nsAutoTArray<nsCOMPtr<nsIContent>, + SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots; + nsRefPtr<ContentUnbinder> mNext; + ContentUnbinder* mLast; + static ContentUnbinder* sContentUnbinder; +}; + +ContentUnbinder* ContentUnbinder::sContentUnbinder = nsnull; + NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement) nsINode::Unlink(tmp); if (tmp->HasProperties() && tmp->IsXUL()) { tmp->DeleteProperty(nsGkAtoms::contextmenulistener); tmp->DeleteProperty(nsGkAtoms::popuplistener); } // Unlink child content (and unbind our subtree). - { + if (UnoptimizableCCNode(tmp) || !nsCCUncollectableMarker::sGeneration) { PRUint32 childCount = tmp->mAttrsAndChildren.ChildCount(); if (childCount) { // Don't allow script to run while we're unbinding everything. nsAutoScriptBlocker scriptBlocker; while (childCount-- > 0) { - // Once we have XPCOMGC we shouldn't need to call UnbindFromTree. - // We could probably do a non-deep unbind here when IsInDoc is false - // for better performance. - // Hold a strong ref to the node when we remove it, because we may be // the last reference to it. We need to call TakeChildAt() and // update mFirstChild before calling UnbindFromTree, since this last // can notify various observers and they should really see consistent // tree state. nsCOMPtr<nsIContent> child = tmp->mAttrsAndChildren.TakeChildAt(childCount); if (childCount == 0) { tmp->mFirstChild = nsnull; } child->UnbindFromTree(); } } - } + } else if (!tmp->GetParent() && tmp->mAttrsAndChildren.ChildCount()) { + ContentUnbinder::Append(tmp); + } /* else { + The subtree root will end up to a ContentUnbinder, and that will + unbind the child nodes. + } */ // Unlink any DOM slots of interest. { nsDOMSlots *slots = tmp->GetExistingDOMSlots(); if (slots) { slots->Unlink(tmp->IsXUL()); } }
--- a/content/base/src/nsGenericElement.h +++ b/content/base/src/nsGenericElement.h @@ -77,16 +77,17 @@ class nsIDOMCSSStyleDeclaration; class nsIURI; class nsINodeInfo; class nsIControllers; class nsEventListenerManager; class nsIScrollableFrame; class nsAttrValueOrString; class nsContentList; class nsDOMTokenList; +class ContentUnbinder; struct nsRect; typedef PRUptrdiff PtrBits; /** * Class that implements the nsIDOMNodeList interface (a list of children of * the content), by holding a reference to the content and delegating GetLength * and Item to its existing child list. @@ -951,16 +952,17 @@ protected: * * Note: for HTML this gets the value of the 'target' attribute; for XLink * this gets the value of the xlink:_moz_target attribute, or failing that, * the value of xlink:show, converted to a suitably equivalent named target * (e.g. _blank). */ virtual void GetLinkTarget(nsAString& aTarget); + friend class ContentUnbinder; /** * Array containing all attributes and children for this element */ nsAttrAndChildArray mAttrsAndChildren; private: /** * Get this element's client area rect in app units.
--- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -593,17 +593,17 @@ nsObjectLoadingContent::InstantiatePlugi // of this method. nsCOMPtr<nsIObjectLoadingContent> kungFuDeathGrip = this; nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); nsCOMPtr<nsIURI> baseURI; if (!aURI) { // We need some URI. If we have nothing else, use the base URI. // XXX(biesi): The code used to do this. Not sure why this is correct... - GetObjectBaseURI(thisContent, getter_AddRefs(baseURI)); + GetObjectBaseURI(nsCString(aMimeType), getter_AddRefs(baseURI)); aURI = baseURI; } // Flush layout so that the plugin is initialized with the latest information. nsIDocument* doc = thisContent->GetCurrentDoc(); if (!doc) { return NS_ERROR_FAILURE; } @@ -1158,17 +1158,17 @@ nsObjectLoadingContent::LoadObject(const // Avoid StringToURI in order to use the codebase attribute as base URI nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); NS_ASSERTION(thisContent, "must be a content"); nsIDocument* doc = thisContent->OwnerDoc(); nsCOMPtr<nsIURI> baseURI; - GetObjectBaseURI(thisContent, getter_AddRefs(baseURI)); + GetObjectBaseURI(aTypeHint, getter_AddRefs(baseURI)); nsCOMPtr<nsIURI> uri; nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), aURI, doc, baseURI); // If URI creation failed, fallback immediately - this only happens for // malformed URIs if (!uri) { @@ -1189,16 +1189,61 @@ nsObjectLoadingContent::UpdateFallbackSt // Notify the UI and update the fallback state PluginSupportState state = GetPluginSupportState(aContent, aTypeHint); if (state != ePluginOtherState) { fallback.SetPluginState(state); FirePluginError(aContent, state); } } +bool +nsObjectLoadingContent::IsFileCodebaseAllowable(nsIURI* aBaseURI, nsIURI* aOriginURI) +{ + nsCOMPtr<nsIFileURL> baseFileURL(do_QueryInterface(aBaseURI)); + nsCOMPtr<nsIFileURL> originFileURL(do_QueryInterface(aOriginURI)); + + // get IFile handles and normalize + nsCOMPtr<nsIFile> originFile; + nsCOMPtr<nsIFile> baseFile; + if (!originFileURL || !baseFileURL || + NS_FAILED(originFileURL->GetFile(getter_AddRefs(originFile))) || + NS_FAILED(baseFileURL->GetFile(getter_AddRefs(baseFile))) || + NS_FAILED(baseFile->Normalize()) || + NS_FAILED(originFile->Normalize())) { + return false; + } + + // If the origin is a directory, it should contain/equal baseURI + // Otherwise, its parent directory should contain/equal baseURI + bool origin_is_dir; + bool contained = false; + nsresult rv = originFile->IsDirectory(&origin_is_dir); + NS_ENSURE_SUCCESS(rv, false); + + if (origin_is_dir) { + // originURI is a directory, ensure it contains the baseURI + rv = originFile->Contains(baseFile, true, &contained); + if (NS_SUCCEEDED(rv) && !contained) { + rv = originFile->Equals(baseFile, &contained); + } + } else { + // originURI is a file, ensure its parent contains the baseURI + nsCOMPtr<nsIFile> originParent; + rv = originFile->GetParent(getter_AddRefs(originParent)); + if (NS_SUCCEEDED(rv) && originParent) { + rv = originParent->Contains(baseFile, true, &contained); + if (NS_SUCCEEDED(rv) && !contained) { + rv = originParent->Equals(baseFile, &contained); + } + } + } + + return NS_SUCCEEDED(rv) && contained; +} + nsresult nsObjectLoadingContent::LoadObject(nsIURI* aURI, bool aNotify, const nsCString& aTypeHint, bool aForceLoad) { // Only do a URI equality check for things that aren't stopped plugins. // This is because we still need to load again if the plugin has been stopped. @@ -1286,16 +1331,38 @@ nsObjectLoadingContent::LoadObject(nsIUR nsnull, //extra &shouldLoad, nsContentUtils::GetContentPolicy(), secMan); if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) { HandleBeingBlockedByContentPolicy(rv, shouldLoad); return NS_OK; } + + // If this is a file:// URI, require that the codebase (baseURI) + // is contained within the same folder as the document origin (originURI) + // or within the document origin, if it is a folder. + // No originURI implies chrome, which bypasses the check + // -- bug 406541 + nsCOMPtr<nsIURI> originURI; + nsCOMPtr<nsIURI> baseURI; + GetObjectBaseURI(aTypeHint, getter_AddRefs(baseURI)); + rv = thisContent->NodePrincipal()->GetURI(getter_AddRefs(originURI)); + if (NS_FAILED(rv)) { + Fallback(aNotify); + return NS_OK; + } + if (originURI) { + bool isfile; + if (NS_FAILED(originURI->SchemeIs("file", &isfile)) || + (isfile && !IsFileCodebaseAllowable(baseURI, originURI))) { + Fallback(aNotify); + return NS_OK; + } + } } nsresult rv = NS_ERROR_UNEXPECTED; // This fallback variable MUST be declared after the notifier variable. Do NOT // change the order of the declarations! AutoFallback fallback(this, &rv); PRUint32 caps = GetCapabilities(); @@ -1403,17 +1470,17 @@ nsObjectLoadingContent::LoadObject(nsIUR if (isSupportedClassID) { // Use the classid's type NS_ASSERTION(!typeForID.IsEmpty(), "Must have a real type!"); mContentType = typeForID; // XXX(biesi). The plugin instantiation code used to pass the base URI // here instead of the plugin URI for instantiation via class ID, so I // continue to do so. Why that is, no idea... - GetObjectBaseURI(thisContent, getter_AddRefs(mURI)); + GetObjectBaseURI(mContentType, getter_AddRefs(mURI)); if (!mURI) { mURI = aURI; } } // rv is references by a stack-based object, need to assign here rv = AsyncStartPluginInstance(); @@ -1780,35 +1847,39 @@ nsObjectLoadingContent::TypeForClassID(c aType.AssignLiteral("application/oleobject"); return NS_OK; } } return NS_ERROR_NOT_AVAILABLE; } -void -nsObjectLoadingContent::GetObjectBaseURI(nsIContent* thisContent, nsIURI** aURI) +NS_IMETHODIMP +nsObjectLoadingContent::GetObjectBaseURI(const nsACString & aMimeType, nsIURI** aURI) { - // We want to use swap(); since this is just called from this file, - // we can assert this (callers use comptrs) - NS_PRECONDITION(*aURI == nsnull, "URI must be inited to zero"); + nsCOMPtr<nsIContent> thisContent = + do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); // For plugins, the codebase attribute is the base URI nsCOMPtr<nsIURI> baseURI = thisContent->GetBaseURI(); nsAutoString codebase; thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::codebase, codebase); - if (!codebase.IsEmpty()) { - nsContentUtils::NewURIWithDocumentCharset(aURI, codebase, - thisContent->OwnerDoc(), - baseURI); - } else { - baseURI.swap(*aURI); + + if (codebase.IsEmpty() && aMimeType.Equals("application/x-java-vm")) { + // bug 406541 + // Java resolves codebase="" as "/" -- so we replicate that quirk, to ensure + // we run security checks against the same path. + codebase.AssignLiteral("/"); } + + nsContentUtils::NewURIWithDocumentCharset(aURI, codebase, + thisContent->OwnerDoc(), + baseURI); + return NS_OK; } nsObjectFrame* nsObjectLoadingContent::GetExistingFrame() { nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); nsIFrame* frame = thisContent->GetPrimaryFrame(); nsIObjectFrame* objFrame = do_QueryFrame(frame); @@ -2057,20 +2128,23 @@ nsObjectLoadingContent::StopPluginInstan if (NS_SUCCEEDED(inst->GetMIMEType(&mime)) && mime) { if (strcmp(mime, "audio/x-pn-realaudio-plugin") == 0) { delayedStop = true; } } } #endif - DoStopPlugin(mInstanceOwner, delayedStop); - + // DoStopPlugin can process events and there may be pending InDocCheckEvent + // events which can drop in underneath us and destroy the instance we are + // about to destroy. Make sure this doesn't happen via this temp ref ptr and + // the !mInstanceOwner check above. + nsRefPtr<nsPluginInstanceOwner> instOwner = mInstanceOwner; mInstanceOwner = nsnull; - + DoStopPlugin(instOwner, delayedStop); return NS_OK; } void nsObjectLoadingContent::NotifyContentObjectWrapper() { nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
--- a/content/base/src/nsObjectLoadingContent.h +++ b/content/base/src/nsObjectLoadingContent.h @@ -249,16 +249,22 @@ class nsObjectLoadingContent : public ns void NotifyContentObjectWrapper(); /** * Check whether the given request represents a successful load. */ static bool IsSuccessfulRequest(nsIRequest* aRequest); /** + * Check if the given baseURI is contained in the same directory as the + * aOriginURI (or a child thereof) + */ + static bool IsFileCodebaseAllowable(nsIURI* aBaseURI, nsIURI* aOriginURI); + + /** * Check whether the URI can be handled internally. */ static bool CanHandleURI(nsIURI* aURI); /** * Checks whether the given type is a supported document type. */ bool IsSupportedDocument(const nsCString& aType); @@ -295,24 +301,16 @@ class nsObjectLoadingContent : public ns * For a classid, returns the MIME type that can be used to instantiate * a plugin for this ID. * * @return NS_ERROR_NOT_AVAILABLE Unsupported class ID. */ nsresult TypeForClassID(const nsAString& aClassID, nsACString& aType); /** - * Gets the base URI to be used for this object. This differs from - * nsIContent::GetBaseURI in that it takes codebase attributes into - * account. - */ - void GetObjectBaseURI(nsIContent* thisContent, nsIURI** aURI); - - - /** * Gets the frame that's associated with this content node. * Does not flush. */ nsObjectFrame* GetExistingFrame(); /** * Handle being blocked by a content policy. aStatus is the nsresult * return value of the Should* call, while aRetval is what it returned in
--- a/content/canvas/crashtests/crashtests.list +++ b/content/canvas/crashtests/crashtests.list @@ -1,6 +1,6 @@ load 360293-1.html load 421715-1.html load 553938-1.html load 647480.html load 0px-size-font-667225.html -skip-if(cocoaWidget&&layersGPUAccelerated) load 729116.html # bug 731117 +load 729116.html
--- a/content/canvas/src/CustomQS_Canvas2D.h +++ b/content/canvas/src/CustomQS_Canvas2D.h @@ -241,17 +241,17 @@ GetImageDataDimensions(JSContext *cx, JS return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR); *width = uint32_t(wi); *height = uint32_t(hi); return true; } static JSBool -nsIDOMCanvasRenderingContext2D_CreateImageData(JSContext *cx, uintN argc, jsval *vp) +nsIDOMCanvasRenderingContext2D_CreateImageData(JSContext *cx, unsigned argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); /* Note: this doesn't need JS_THIS_OBJECT */ if (argc < 1) return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); @@ -287,17 +287,17 @@ nsIDOMCanvasRenderingContext2D_CreateIma int32_t hi = JS_DoubleToInt32(height); uint32_t w = NS_ABS(wi); uint32_t h = NS_ABS(hi); return CreateImageData(cx, w, h, NULL, 0, 0, vp); } static JSBool -nsIDOMCanvasRenderingContext2D_GetImageData(JSContext *cx, uintN argc, jsval *vp) +nsIDOMCanvasRenderingContext2D_GetImageData(JSContext *cx, unsigned argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsIDOMCanvasRenderingContext2D *self; @@ -344,17 +344,17 @@ nsIDOMCanvasRenderingContext2D_GetImageD y -= h; } else { h = hi; } return CreateImageData(cx, w, h, self, x, y, vp); } static JSBool -nsIDOMCanvasRenderingContext2D_PutImageData(JSContext *cx, uintN argc, jsval *vp) +nsIDOMCanvasRenderingContext2D_PutImageData(JSContext *cx, unsigned argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsresult rv;
--- a/content/canvas/src/CustomQS_WebGL.h +++ b/content/canvas/src/CustomQS_WebGL.h @@ -78,17 +78,17 @@ helper_isFloat32Array(JSObject *obj) { /* * BufferData takes: * BufferData (int, int, int) * BufferData_buf (int, js::ArrayBuffer *, int) * BufferData_array (int, js::TypedArray *, int) */ static JSBool -nsIDOMWebGLRenderingContext_BufferData(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_BufferData(JSContext *cx, unsigned argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; @@ -151,17 +151,17 @@ nsIDOMWebGLRenderingContext_BufferData(J } /* * BufferSubData takes: * BufferSubData (int, int, js::ArrayBuffer *) * BufferSubData_array (int, int, js::TypedArray *) */ static JSBool -nsIDOMWebGLRenderingContext_BufferSubData(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_BufferSubData(JSContext *cx, unsigned argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; @@ -223,17 +223,17 @@ nsIDOMWebGLRenderingContext_BufferSubDat return JS_TRUE; } /* * CompressedTexImage2D takes: * CompressedTexImage2D(uint, int, uint, int, int, int, ArrayBufferView) */ static JSBool -nsIDOMWebGLRenderingContext_CompressedTexImage2D(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_CompressedTexImage2D(JSContext *cx, unsigned argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsresult rv; @@ -273,17 +273,17 @@ nsIDOMWebGLRenderingContext_CompressedTe return JS_TRUE; } /* * CompressedTexSubImage2D takes: * CompressedTexSubImage2D(uint, int, int, int, int, int, uint, ArrayBufferView) */ static JSBool -nsIDOMWebGLRenderingContext_CompressedTexSubImage2D(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_CompressedTexSubImage2D(JSContext *cx, unsigned argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsresult rv; @@ -324,17 +324,17 @@ nsIDOMWebGLRenderingContext_CompressedTe return JS_TRUE; } /* * ReadPixels takes: * ReadPixels(int, int, int, int, uint, uint, ArrayBufferView) */ static JSBool -nsIDOMWebGLRenderingContext_ReadPixels(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_ReadPixels(JSContext *cx, unsigned argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsresult rv; @@ -384,17 +384,17 @@ nsIDOMWebGLRenderingContext_ReadPixels(J /* * TexImage2D takes: * TexImage2D(uint, int, uint, int, int, int, uint, uint, ArrayBufferView) * TexImage2D(uint, int, uint, uint, uint, nsIDOMElement) * TexImage2D(uint, int, uint, uint, uint, ImageData) */ static JSBool -nsIDOMWebGLRenderingContext_TexImage2D(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_TexImage2D(JSContext *cx, unsigned argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsresult rv; @@ -505,17 +505,17 @@ nsIDOMWebGLRenderingContext_TexImage2D(J } /* TexSubImage2D takes: * TexSubImage2D(uint, int, int, int, int, int, uint, uint, ArrayBufferView) * TexSubImage2D(uint, int, int, int, uint, uint, nsIDOMElement) * TexSubImage2D(uint, int, int, int, uint, uint, ImageData) */ static JSBool -nsIDOMWebGLRenderingContext_TexSubImage2D(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_TexSubImage2D(JSContext *cx, unsigned argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsresult rv; @@ -616,17 +616,17 @@ nsIDOMWebGLRenderingContext_TexSubImage2 return xpc_qsThrowMethodFailed(cx, rv, vp); *vp = JSVAL_VOID; return JS_TRUE; } /* NOTE: There is a TN version of this below, update it as well */ static inline JSBool -helper_nsIDOMWebGLRenderingContext_Uniform_x_iv(JSContext *cx, uintN argc, jsval *vp, int nElements) +helper_nsIDOMWebGLRenderingContext_Uniform_x_iv(JSContext *cx, unsigned argc, jsval *vp, int nElements) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsresult rv; @@ -690,17 +690,17 @@ helper_nsIDOMWebGLRenderingContext_Unifo return xpc_qsThrowMethodFailed(cx, rv, vp); *vp = JSVAL_VOID; return JS_TRUE; } /* NOTE: There is a TN version of this below, update it as well */ static inline JSBool -helper_nsIDOMWebGLRenderingContext_Uniform_x_fv(JSContext *cx, uintN argc, jsval *vp, int nElements) +helper_nsIDOMWebGLRenderingContext_Uniform_x_fv(JSContext *cx, unsigned argc, jsval *vp, int nElements) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsresult rv; @@ -764,17 +764,17 @@ helper_nsIDOMWebGLRenderingContext_Unifo return xpc_qsThrowMethodFailed(cx, rv, vp); *vp = JSVAL_VOID; return JS_TRUE; } /* NOTE: There is a TN version of this below, update it as well */ static inline JSBool -helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv(JSContext *cx, uintN argc, jsval *vp, int nElements) +helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv(JSContext *cx, unsigned argc, jsval *vp, int nElements) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; @@ -837,17 +837,17 @@ helper_nsIDOMWebGLRenderingContext_Unifo if (NS_FAILED(rv)) return xpc_qsThrowMethodFailed(cx, rv, vp); *vp = JSVAL_VOID; return JS_TRUE; } static inline JSBool -helper_nsIDOMWebGLRenderingContext_VertexAttrib_x_fv(JSContext *cx, uintN argc, jsval *vp, int nElements) +helper_nsIDOMWebGLRenderingContext_VertexAttrib_x_fv(JSContext *cx, unsigned argc, jsval *vp, int nElements) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; @@ -905,96 +905,96 @@ helper_nsIDOMWebGLRenderingContext_Verte if (NS_FAILED(rv)) return xpc_qsThrowMethodFailed(cx, rv, vp); *vp = JSVAL_VOID; return JS_TRUE; } static JSBool -nsIDOMWebGLRenderingContext_Uniform1iv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform1iv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_Uniform_x_iv(cx, argc, vp, 1); } static JSBool -nsIDOMWebGLRenderingContext_Uniform2iv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform2iv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_Uniform_x_iv(cx, argc, vp, 2); } static JSBool -nsIDOMWebGLRenderingContext_Uniform3iv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform3iv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_Uniform_x_iv(cx, argc, vp, 3); } static JSBool -nsIDOMWebGLRenderingContext_Uniform4iv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform4iv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_Uniform_x_iv(cx, argc, vp, 4); } static JSBool -nsIDOMWebGLRenderingContext_Uniform1fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform1fv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_Uniform_x_fv(cx, argc, vp, 1); } static JSBool -nsIDOMWebGLRenderingContext_Uniform2fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform2fv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_Uniform_x_fv(cx, argc, vp, 2); } static JSBool -nsIDOMWebGLRenderingContext_Uniform3fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform3fv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_Uniform_x_fv(cx, argc, vp, 3); } static JSBool -nsIDOMWebGLRenderingContext_Uniform4fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform4fv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_Uniform_x_fv(cx, argc, vp, 4); } static JSBool -nsIDOMWebGLRenderingContext_UniformMatrix2fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_UniformMatrix2fv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv(cx, argc, vp, 2); } static JSBool -nsIDOMWebGLRenderingContext_UniformMatrix3fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_UniformMatrix3fv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv(cx, argc, vp, 3); } static JSBool -nsIDOMWebGLRenderingContext_UniformMatrix4fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_UniformMatrix4fv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv(cx, argc, vp, 4); } static JSBool -nsIDOMWebGLRenderingContext_VertexAttrib1fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_VertexAttrib1fv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_VertexAttrib_x_fv(cx, argc, vp, 1); } static JSBool -nsIDOMWebGLRenderingContext_VertexAttrib2fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_VertexAttrib2fv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_VertexAttrib_x_fv(cx, argc, vp, 2); } static JSBool -nsIDOMWebGLRenderingContext_VertexAttrib3fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_VertexAttrib3fv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_VertexAttrib_x_fv(cx, argc, vp, 3); } static JSBool -nsIDOMWebGLRenderingContext_VertexAttrib4fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_VertexAttrib4fv(JSContext *cx, unsigned argc, jsval *vp) { return helper_nsIDOMWebGLRenderingContext_VertexAttrib_x_fv(cx, argc, vp, 4); }
--- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -59,16 +59,20 @@ #include "nsIJSNativeInitializer.h" #include "nsContentUtils.h" #include "GLContextProvider.h" #include "Layers.h" #include "CheckedInt.h" +#ifdef XP_MACOSX +#include "ForceDiscreteGPUHelperCGL.h" +#endif + /* * Minimum value constants defined in 6.2 State Tables of OpenGL ES - 2.0.25 * https://bugzilla.mozilla.org/show_bug.cgi?id=686732 * * Exceptions: some of the following values are set to higher values than in the spec because * the values in the spec are ridiculously low. They are explicitly marked below */ #define MINVALUE_GL_MAX_TEXTURE_SIZE 1024 // Different from the spec, which sets it to 64 on page 162 @@ -938,16 +942,26 @@ protected: nsCOMPtr<nsITimer> mContextRestorer; bool mAllowRestore; bool mRobustnessTimerRunning; bool mDrawSinceRobustnessTimerSet; ContextStatus mContextStatus; bool mContextLostErrorSet; bool mContextLostDueToTest; +#ifdef XP_MACOSX + // see bug 713305. This RAII helper guarantees that we're on the discrete GPU, during its lifetime + // Debouncing note: we don't want to switch GPUs too frequently, so try to not create and destroy + // these objects at high frequency. Having WebGLContext's hold one such object seems fine, + // because WebGLContext objects only go away during GC, which shouldn't happen too frequently. + // If in the future GC becomes much more frequent, we may have to revisit then (maybe use a timer). + ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper; +#endif + + public: // console logging helpers static void LogMessage(const char *fmt, ...); static void LogMessage(const char *fmt, va_list ap); void LogMessageIfVerbose(const char *fmt, ...); void LogMessageIfVerbose(const char *fmt, va_list ap); friend class WebGLTexture;
--- a/content/canvas/test/webgl/test_webgl_conformance_test_suite.html +++ b/content/canvas/test/webgl/test_webgl_conformance_test_suite.html @@ -89,16 +89,36 @@ function start() { is106orHigher = (kDarwinVersion >= 10.0); if (!is106orHigher) { dump("WebGL mochitest disabled on Mac OSX versions older than 10.6\n"); SimpleTest.finish(); return; } } + // we currently disable this test on version of Mac OSX older than 10.6, + // due to various weird failures, including one making getRenderbufferParameter tests + // on DEPTH_STENCIL fail + var kDarwinVersion = 0; + if (kIsMac) { + // code borrowed from browser/modules/test/browser_taskbar_preview.js + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + kDarwinVersion = parseFloat(Components.classes["@mozilla.org/system-info;1"] + .getService(Components.interfaces.nsIPropertyBag2) + .getProperty("version")); + // the next line is correct: Mac OSX 10.6 corresponds to Darwin version 10 ! + // Mac OSX 10.5 would be Darwin version 9. the |version| string we've got here + // is the Darwin version. + if (kDarwinVersion < 10.0) { + todo(false, "Test disabled on Mac OSX versions older than 10.6."); + SimpleTest.finish(); + return; + } + } + function getEnv(env) { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); var envsvc = Components.classes["@mozilla.org/process/environment;1"].getService(Components.interfaces.nsIEnvironment); var val = envsvc.get(env); if (val == "") return null; return val; } @@ -125,45 +145,72 @@ function start() { var node = reporter.localDoc.createTextNode(''); li.appendChild(ul); div.appendChild(node); this.totalsElem = node; this.resultElem = ul; this.elem = li; }; - Page.prototype.isExpectedToFullyPass = function() { - return testsExpectedToFail.indexOf(this.url) == -1 && testsToIgnore.indexOf(this.url) == -1; + /** + * Indicates whether this test page results are not to be ignored. + */ + Page.prototype.shouldBeAccountedFor = function() { + return testsToIgnore.indexOf(this.url) == -1; } - Page.prototype.errormsg = function(msg) { - return msg + ' (URL: ' + this.url + ')'; + /** + * Indicates whether all this test page results are expected not to fail, + * if not ignored. + */ + Page.prototype.isExpectedToFullyPass = function() { + return this.shouldBeAccountedFor() && + testsExpectedToFail.indexOf(this.url) == -1; } + /** + * Returns log message with added test page url. + */ + Page.prototype.logMsg = function(msg) { + return '[' + this.url + '] ' + msg; + } + + /** + * Reports an individual test result of test page. + */ Page.prototype.addResult = function(msg, success) { ++this.totalTests; if (success === undefined) { ++this.totalTimeouts; var result = "timeout"; var css = "timeout"; // only few timeouts are actually caught here --- most are caught in finishPage(). if (this.isExpectedToFullyPass()) { - ok(false, this.errormsg('Test timed out, "' + msg + '"')); + ok(false, this.logMsg('Test timed out'), msg); + } else { + todo(false, this.logMsg('Test timed out'), msg); } } else if (success) { ++this.totalSuccessful; var result = "success"; var css = "success"; - // don't report success. + if (this.shouldBeAccountedFor()) { + ok(true, this.logMsg('Test passed'), msg); + } else { + todo(false, this.logMsg('Test passed, but is ignored'), msg); + } + // Don't report individual success to UI, to keep it light. return; } else { var result = "failed"; var css = "fail"; if (this.isExpectedToFullyPass()) { - ok(false, this.errormsg('Test failed, "' + msg + '"')); + ok(false, this.logMsg('Test failed'), msg); + } else { + todo(false, this.logMsg('Test failed'), msg); } } var node = this.reporter.localDoc.createTextNode(result + ': ' + msg); var li = this.reporter.localDoc.createElement('li'); li.appendChild(node); li.setAttribute('class', css); this.resultElem.appendChild(li); @@ -176,41 +223,52 @@ function start() { // remove previous results. while (this.resultElem.hasChildNodes()) { this.resultElem.removeChild(this.resultElem.childNodes[0]); } this.totalsElem.textContent = ''; return true; }; + /** + * Reports test page result summary. + */ Page.prototype.finishPage = function(success) { var msg = ' (' + this.totalSuccessful + ' of ' + this.totalTests + ' passed)'; if (success === undefined) { var css = 'testpagetimeout'; msg = '(*timeout*)'; ++this.totalTests; ++this.totalTimeouts; + // Most timeouts are only caught here --- though a few are (already) caught in addResult(). if (this.isExpectedToFullyPass()) { - ok(false, this.errormsg('Unexpected timeout in this test page')); - window.dump('WebGL test error: test page timeout: ' + this.url + '\n'); + ok(false, this.logMsg('Timeout in this test page')); + } else { + todo(false, this.logMsg('Timeout in this test page')); } } else if (this.totalSuccessful != this.totalTests) { var css = 'testpagefail'; + var totalFailed = this.totalTests - this.totalTimeouts - this.totalSuccessful; if (this.isExpectedToFullyPass()) { - window.dump('WebGL test error: test page failure: ' + this.url + '\n'); + ok(false, this.logMsg("(WebGL test error) " + totalFailed + ' failure(s) and ' + this.totalTimeouts + ' timeout(s)')); + } else { + todo(false, this.logMsg("(WebGL test error) " + totalFailed + ' failure(s) and ' + this.totalTimeouts + ' timeout(s)')); } - // failures have already been reported for the sub-tests } else { var css = 'testpagesuccess'; if (this.isExpectedToFullyPass()) { - ok(true, this.errormsg('Successful test page')); + ok(true, this.logMsg('All ' + this.totalSuccessful + ' test(s) passed')); + } else { + if (this.shouldBeAccountedFor()) { + todo(true, this.logMsg('Test page expected to fail, but all ' + this.totalSuccessful + ' tests passed')); + } else { + todo(false, this.logMsg('All ' + this.totalSuccessful + ' test(s) passed, but test page is ignored')); + } } - window.dump('WebGL test page successful: ' + this.url + '\n'); - testsSuccessful.push(this.url); } this.elem.setAttribute('class', css); this.totalsElem.textContent = msg; }; var Reporter = function() { this.localDoc = document; @@ -241,17 +299,17 @@ function start() { Reporter.prototype.addPage = function(url) { this.currentPage = new Page(this, url, this.resultElem); this.resultElem.appendChild(this.currentPage.elem); ++this.totalPages; this.pagesByURL[url] = this.currentPage; }; Reporter.prototype.startPage = function(url) { - dump('WebGL mochitest: starting page ' + url + '\n'); + info("[" + url + "] (WebGL mochitest) Starting test page"); // Calling garbageCollect before each test page fixes intermittent failures with // out-of-memory errors, often failing to create a WebGL context. // The explanation is that the JS engine keeps unreferenced WebGL contexts around // for too long before GCing (bug 617453), so that during this mochitest dozens of unreferenced // WebGL contexts can accumulate at a given time. SpecialPowers.DOMWindowUtils.garbageCollect(); @@ -261,24 +319,21 @@ function start() { expectedtofailTextNode.textContent = testsExpectedToFail.length + ' test pages are expected to fail out of ' + this.totalPages; ignoredtestsTextNode.textContent = testsToIgnore.length + ' test pages have their results ignored'; return page.startPage(); }; - Reporter.prototype.totalFailed = function() { - return this.totalTests - this.totalSuccessful; - }; - Reporter.prototype.displayStats = function() { + var totalFailed = this.totalTests - this.totalTimeouts - this.totalSuccessful; this.fullResultsNode.textContent = this.totalSuccessful + ' passed, ' + - this.totalFailed() + ' failed, ' + + totalFailed + ' failed, ' + this.totalTimeouts + ' timed out'; }; Reporter.prototype.addResult = function(msg, success) { if (this.currentPage != null) { this.currentPage.addResult(msg, success); } }; @@ -290,19 +345,16 @@ function start() { this.totalSuccessful += this.currentPage.totalSuccessful; this.totalTimeouts += this.currentPage.totalTimeouts; this.currentPage = null; this.displayStats(); } }; Reporter.prototype.finishedTestSuite = function() { - for (var i = 0; i < testsExpectedToFail.length; ++i) - if (testsSuccessful.indexOf(testsExpectedToFail[i]) != -1) - todo(true, 'Test expected to fail, but passed: ' + testsExpectedToFail[i]); statusTextNode.textContent = 'Finished'; SimpleTest.finish(); } Reporter.prototype.ready = function() { statusTextNode.textContent = 'Loaded test lists. Starting tests...'; window.webglTestHarness.runTests(); } @@ -348,20 +400,22 @@ function start() { getURLOptions(OPTIONS); function runTestSuite() { var reporter = new Reporter(); // try to create a dummy WebGL context, just to catch context creation failures once here, // rather than having them result in 100's of failures (one in each test page) var canvas = document.getElementById("webglcheck-default"); - var ctx = null; + var ctx; try { ctx = canvas.getContext("experimental-webgl"); - } catch(e) {} + } catch(e) { + ok(false, "canvas.getContext() failed", e); + } if (ctx) { statusTextNode.textContent = 'Loading test lists...'; var iframe = document.getElementById("testframe"); var testHarness = new WebGLTestHarnessModule.TestHarness( iframe, '00_test_list.txt', function(type, msg, success) { @@ -430,18 +484,16 @@ function start() { if (kIsMac && kDarwinVersion >= 11.0) { testsExpectedToFail.push('conformance/textures/texture-mips.html'); testsExpectedToFail.push('conformance/textures/texture-npot.html'); } var testsToIgnore = []; - var testsSuccessful = []; - runTestSuite(); } </script> </head> <body onload="start();"> <p id="display"></p> <div id="content" style="display: none">
--- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -116,16 +116,18 @@ #include "mozAutoDocUpdate.h" #include "nsContentCreatorFunctions.h" #include "nsContentUtils.h" #include "nsRadioVisitor.h" #include "mozilla/LookAndFeel.h" #include "mozilla/Util.h" // DebugOnly +#include "nsIIDNService.h" + using namespace mozilla; using namespace mozilla::dom; // XXX align=left, hspace, vspace, border? other nav4 attrs static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID); // First bits are needed for the control type. @@ -3913,28 +3915,44 @@ nsHTMLInputElement::IsValidEmailAddressL return !tokenizer.lastTokenEndedWithSeparator(); } //static bool nsHTMLInputElement::IsValidEmailAddress(const nsAString& aValue) { + nsCAutoString value = NS_ConvertUTF16toUTF8(aValue); PRUint32 i = 0; - PRUint32 length = aValue.Length(); + PRUint32 length = value.Length(); + + // Puny-encode the string if needed before running the validation algorithm. + nsCOMPtr<nsIIDNService> idnSrv = do_GetService(NS_IDNSERVICE_CONTRACTID); + if (idnSrv) { + bool ace; + if (NS_SUCCEEDED(idnSrv->IsACE(value, &ace)) && !ace) { + nsCAutoString punyCodedValue; + if (NS_SUCCEEDED(idnSrv->ConvertUTF8toACE(value, punyCodedValue))) { + value = punyCodedValue; + length = value.Length(); + } + } + } else { + NS_ERROR("nsIIDNService isn't present!"); + } // If the email address is empty, begins with a '@' or ends with a '.', // we know it's invalid. - if (length == 0 || aValue[0] == '@' || aValue[length-1] == '.') { + if (length == 0 || value[0] == '@' || value[length-1] == '.') { return false; } // Parsing the username. - for (; i < length && aValue[i] != '@'; ++i) { - PRUnichar c = aValue[i]; + for (; i < length && value[i] != '@'; ++i) { + PRUnichar c = value[i]; // The username characters have to be in this list to be valid. if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) || c == '.' || c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\''|| c == '*' || c == '+' || c == '-' || c == '/' || c == '=' || c == '?' || c == '^' || c == '_' || c == '`' || c == '{' || c == '|' || c == '}' || c == '~' )) { return false; @@ -3943,27 +3961,27 @@ nsHTMLInputElement::IsValidEmailAddress( // There is no domain name (or it's one-character long), // that's not a valid email address. if (++i >= length) { return false; } // The domain name can't begin with a dot. - if (aValue[i] == '.') { + if (value[i] == '.') { return false; } // Parsing the domain name. for (; i < length; ++i) { - PRUnichar c = aValue[i]; + PRUnichar c = value[i]; if (c == '.') { // A dot can't follow a dot. - if (aValue[i-1] == '.') { + if (value[i-1] == '.') { return false; } } else if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) || c == '-')) { // The domain characters have to be in this list to be valid. return false; } }
--- a/content/html/content/test/form_submit_server.sjs +++ b/content/html/content/test/form_submit_server.sjs @@ -38,27 +38,28 @@ function handleRequest(request, response let headers = {}; let headerEnd = s.indexOf("\r\n\r\n"); s.substr(2, headerEnd-2).split("\r\n").forEach(function(s) { // We're assuming UTF8 for now let [name, value] = s.split(': '); headers[name] = utf8decode(value); }); - let body = s.substring(headerEnd + 4, s.length - 2); - if (!headers["Content-Type"] || headers["Content-Type"] == "text/plain") { + + let body = s.substring(headerEnd + 4, s.length - 2); + if (!headers["Content-Type"] || headers["Content-Type"] == "text/plain") { // We're assuming UTF8 for now - body = utf8decode(body); - } - result.push({ headers: headers, body: body}); + body = utf8decode(body); + } + result.push({ headers: headers, body: body}); }); } if (contentTypeParams[''] == "text/plain" && request.queryString == "plain") { - result = requestBody; + result = utf8decode(requestBody); } if (contentTypeParams[''] == "application/x-www-form-urlencoded" && request.queryString == "url") { result = requestBody; } } else if (request.method == "GET") { result = request.queryString;
--- a/content/html/content/test/forms/test_input_email.html +++ b/content/html/content/test/forms/test_input_email.html @@ -82,16 +82,17 @@ var values = [ [ '\n\n \r\rfoo@bar.com\n\n \r\r', true ], [ '\n\r \n\rfoo@bar.com\n\r \n\r', true ], [ 'tulip', false ], // Some checks on the user part of the address. [ '@bar.com', false ], [ 'f\noo@bar.com', true ], [ 'f\roo@bar.com', true ], [ 'f\r\noo@bar.com', true ], + [ 'fü@foo.com', true ], // Some checks for the domain part. [ 'foo@bar', true ], [ 'foo@b', true ], [ 'foo@', false ], [ 'foo@bar.', false ], [ 'foo@foo.bar', true ], [ 'foo@foo..bar', false ], [ 'foo@.bar', false ], @@ -99,16 +100,21 @@ var values = [ [ 'foo@tulip.foo-bar', true ], [ 'foo@1.2', true ], [ 'foo@127.0.0.1', true ], [ 'foo@1.2.3', true ], [ 'foo@b\nar.com', true ], [ 'foo@b\rar.com', true ], [ 'foo@b\r\nar.com', true ], [ 'foo@.', false ], + [ 'foo@fü.com', true ], + [ 'foo@fu.cüm', true ], + // Long strings with UTF-8. + [ 'this.is.email.should.be.longer.than.sixty.four.characters.föö@mözillä.tld', true ], + [ 'this-is-email-should-be-longer-than-sixty-four-characters-föö@mözillä.tld', true, true ], ]; // Multiple values, we don't check e-mail validity, only multiple stuff. var multipleValues = [ [ 'foo@bar.com, foo@bar.com', true ], [ 'foo@bar.com,foo@bar.com', true ], [ 'foo@bar.com,foo@bar.com,foo@bar.com', true ], [ ' foo@bar.com , foo@bar.com ', true ], @@ -165,18 +171,23 @@ for each (c in legalCharacters) { values.push(["foo@bar.com" + legalCharacters, true]); // Add domain illegal characters. illegalCharacters = "()<>[]:;@\\,!#$%&'*+/=?^_`{|}~ \t"; for each (c in illegalCharacters) { values.push(['foo@foo.ba' + c + 'r', false]); } -values.forEach(function([value, valid]) { - testEmailAddress(email, value, false, valid); +values.forEach(function([value, valid, todo]) { + if (todo === true) { + email.value = value; + todo_is(email.validity.valid, true, "value should be valid"); + } else { + testEmailAddress(email, value, false, valid); + } }); multipleValues.forEach(function([value, valid]) { testEmailAddress(email, value, true, valid); }); // Make sure setting multiple changes the value. email.multiple = false;
--- a/content/html/content/test/forms/test_input_url.html +++ b/content/html/content/test/forms/test_input_url.html @@ -1,33 +1,24 @@ <!DOCTYPE HTML> <html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=344615 ---> <head> - <title>Test for Bug 344615</title> + <title>Tests for <input type='url'> validity</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <style> - input { background-color: rgb(0,0,0) !important; } - input:valid { background-color: rgb(0,255,0) !important; } - input:invalid { background-color: rgb(255,0,0) !important; } - </style> </head> <body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=344615">Mozilla Bug 344615</a> <p id="display"></p> <div id="content" style="display: none"> <input type='url' id='i' oninvalid='invalidEventHandler(event);'> </div> <pre id="test"> <script type="application/javascript"> -/** Test for Bug 344615 **/ +/** Tests for <input type='url'> validity **/ // More checks are done in test_bug551670.html. var gInvalid = false; function invalidEventHandler(e) { is(e.type, "invalid", "Invalid event type should be invalid"); @@ -39,60 +30,60 @@ function checkValidURL(element) gInvalid = false; ok(!element.validity.typeMismatch, "Element should not suffer from type mismatch"); ok(element.validity.valid, "Element should be valid"); ok(element.checkValidity(), "Element should be valid"); ok(!gInvalid, "The invalid event should not have been thrown"); is(element.validationMessage, '', "Validation message should be the empty string"); - is(window.getComputedStyle(element, null).getPropertyValue('background-color'), - "rgb(0, 255, 0)", ":valid pseudo-class should apply"); + ok(element.mozMatchesSelector(":valid"), ":valid pseudo-class should apply"); } function checkInvalidURL(element) { gInvalid = false; ok(element.validity.typeMismatch, "Element should suffer from type mismatch"); ok(!element.validity.valid, "Element should not be valid"); ok(!element.checkValidity(), "Element should not be valid"); ok(gInvalid, "The invalid event should have been thrown"); is(element.validationMessage, "Please enter a URL.", "Validation message should be related to invalid URL"); - is(window.getComputedStyle(element, null).getPropertyValue('background-color'), - "rgb(255, 0, 0)", ":invalid pseudo-class should apply"); + ok(element.mozMatchesSelector(":invalid"), + ":invalid pseudo-class should apply"); } var url = document.getElementById('i'); -is(url.type, 'url', "url state should be recognized"); - -// The empty string should not be considered as invalid. -url.value = ''; -checkValidURL(url); - -// We are only testing obviously (in)valid URI's because the function used -// to check if an URI is valid is not specific to this functionality. -url.value = 'foo'; -checkInvalidURL(url); - -url.value = 'http://mozilla.com/'; -checkValidURL(url); -url.value = 'http://mozil\nla\r.com/'; -checkValidURL(url); - -url.value = ' http://mozilla.com/ '; -checkValidURL(url); +var values = [ + // [ value, validity ] + // The empty string should be considered as valid. + [ "", true ], + [ "foo", false ], + [ "http://mozilla.com/", true ], + [ "http://mozilla.com", true ], + [ "http://mozil\nla\r.com/", true ], + [ " http://mozilla.com/ ", true ], + [ "\r http://mozilla.com/ \n", true ], + [ "file:///usr/bin/tulip", true ], + [ "../../bar.html", false ], + [ "http://mozillá.org", true ], + [ "https://mózillä.org", true ], + [ "http://mózillä.órg", true ], + [ "ht://mózillä.órg", true ], + [ "httÅ://mózillä.órg", false ], +]; -url.value = '\r http://mozilla.com/ \n'; -checkValidURL(url); +values.forEach(function([value, valid]) { + url.value = value; -url.value = 'file:///usr/bin/tulip'; -checkValidURL(url); - -url.value = '../../bar.html'; -checkInvalidURL(url); + if (valid) { + checkValidURL(url); + } else { + checkInvalidURL(url); + } +}); </script> </pre> </body> </html>
--- a/content/html/content/test/test_formSubmission.html +++ b/content/html/content/test/test_formSubmission.html @@ -2,16 +2,17 @@ <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=523771 --> <head> <title>Test for Bug 523771</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=523771">Mozilla Bug 523771</a> <p id="display"></p> <iframe name="target_iframe" id="target_iframe"></iframe> <form action="form_submit_server.sjs" target="target_iframe" id="form" method="POST" enctype="multipart/form-data"> <table> @@ -221,16 +222,42 @@ https://bugzilla.mozilla.org/show_bug.cg <td><button type=foobar name="n18_2" value=""></button></td> <td><button type=foobar name="n18_3"></button></td> <td><button type=foobar name="" value="v18_4"></button></td> <td><button type=foobar value="v18_5"></button></td> <td><button type=foobar ></button></td> <td><button type=foobar name="n18_7_ _ _ _ _"" value="v18_7_ _ _ _ _""></button></td> </tr> + <tr> + <td><input type='url'></td> + <td><input type=url name="n19_1" value="http://v19_1.org"></td> + <td><input type=url name="n19_2" value=""></td> + <td><input type=url name="n19_3"></td> + <td><input type=url name="" value="http://v19_4.org"></td> + <td><input type=url value="http://v19_5.org"></td> + <td><input type=url ></td> + <td><input type=url name="n19_7_ _ _ __"" + value="http://v19_7_ _ _ __""> + <!-- Put UTF-8 value in the "strange" column. --> + <input type=url name="n19_8" value="http://mózillä.órg"></td> + </tr> + <tr> + <td><input type='email'></td> + <td><input type=email name="n20_1" value="v20_1@bar"></td> + <td><input type=email name="n20_2" value=""></td> + <td><input type=email name="n20_3"></td> + <td><input type=email name="" value="v20_4@bar"></td> + <td><input type=email value="v20_5@bar"></td> + <td><input type=email ></td> + <td><input type=email name="n20_7_ _ _ __"" + value="v20_7_ _ _ __"@bar"> + <!-- Put UTF-8 value is the "strange" column. --> + <input type=email name="n20_8" value="foo@mózillä.órg"></td> + </tr> </table> <p> File input: <input type=file name="file_1" class="setfile"> <input type=file name="file_2"> <input type=file name="" class="setfile"> <input type=file name=""> @@ -447,16 +474,28 @@ var expectedSub = [ // Button input // Image input // Reset input // Unknown input { name: "n13_1", value: "v13_1" }, { name: "n13_2", value: "" }, { name: "n13_3", value: "" }, { name: "n13_7_\r\n_\r\n_\r\n_ _\"", value: "v13_7____ _\"" }, + // <input type='url'> + { name: "n19_1", value: "http://v19_1.org" }, + { name: "n19_2", value: "" }, + { name: "n19_3", value: "" }, + { name: "n19_7_\r\n_\r\n_\r\n__\"", value: "http://v19_7_____\"" }, + { name: "n19_8", value: "http://m\xf3zill\xe4.\xf3rg" }, + // <input type='email'> + { name: "n20_1", value: "v20_1@bar" }, + { name: "n20_2", value: "" }, + { name: "n20_3", value: "" }, + { name: "n20_7_\r\n_\r\n_\r\n__\"", value: "v20_7_____\"@bar" }, + { name: "n20_8", value: "foo@mózillä.órg" }, // Default button // Submit button // Button button // Reset button // Unknown button // File { name: "file_1", value: myFile1 }, { name: "file_2", value: emptyFile }, @@ -580,19 +619,25 @@ function checkMPSubmission(sub, expected "Wrong number of headers in " + test); is(sub[i].body, expected[i].value, "Correct value in " + test); } } } +function utf8encode(s) { + return unescape(encodeURIComponent(s)); +} + function checkURLSubmission(sub, expected) { function urlEscape(s) { - return escape(s).replace("%20", "+", "g"); + return escape(utf8encode(s)).replace("%20", "+", "g") + .replace("/", "%2F", "g") + .replace("@", "%40", "g"); } subItems = sub.split("&"); is(subItems.length, expected.length, "Correct number of url items"); var i; for (i = 0; i < expected.length; ++i) { let expect = urlEscape(expected[i].name) + "=" + @@ -651,19 +696,20 @@ function runTest() { }; expectedSub.forEach(fileFixup); expectedAugment.forEach(fileFixup); var form = $("form"); // multipart/form-data - // Make normal submission var iframe = $("target_iframe"); iframe.onload = function() { gen.next(); }; + + // Make normal submission form.submit(); yield; // Wait for iframe to load as a result of the submission var submission = JSON.parse(iframe.contentDocument.documentElement.textContent); checkMPSubmission(submission, expectedSub, "normal submission"); // Disabled controls setDisabled(document.querySelectorAll("input, select, textarea"), true); form.submit();
--- a/content/media/VideoUtils.cpp +++ b/content/media/VideoUtils.cpp @@ -34,17 +34,17 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "VideoUtils.h" #include "nsMathUtils.h" #include "prtypes.h" -#include "mozilla/StdInt.h" +#include "mozilla/StandardInteger.h" // Converts from number of audio frames to microseconds, given the specified // audio rate. CheckedInt64 FramesToUsecs(PRInt64 aFrames, PRUint32 aRate) { return (CheckedInt64(aFrames) * USECS_PER_S) / aRate; } // Converts from microseconds to number of audio frames, given the specified
--- a/content/media/nsBuiltinDecoderReader.cpp +++ b/content/media/nsBuiltinDecoderReader.cpp @@ -41,17 +41,17 @@ #include "nsClassHashtable.h" #include "nsTArray.h" #include "nsBuiltinDecoder.h" #include "nsBuiltinDecoderReader.h" #include "nsBuiltinDecoderStateMachine.h" #include "VideoUtils.h" #include "mozilla/mozalloc.h" -#include "mozilla/StdInt.h" +#include "mozilla/StandardInteger.h" using namespace mozilla; using mozilla::layers::ImageContainer; using mozilla::layers::PlanarYCbCrImage; // Verify these values are sane. Once we've checked the frame sizes, we then // can do less integer overflow checking. PR_STATIC_ASSERT(MAX_VIDEO_WIDTH < PlanarYCbCrImage::MAX_DIMENSION);
--- a/content/media/nsBuiltinDecoderStateMachine.cpp +++ b/content/media/nsBuiltinDecoderStateMachine.cpp @@ -43,17 +43,17 @@ #include "nsBuiltinDecoderReader.h" #include "nsBuiltinDecoderStateMachine.h" #include "mozilla/mozalloc.h" #include "VideoUtils.h" #include "nsTimeRanges.h" #include "nsDeque.h" #include "mozilla/Preferences.h" -#include "mozilla/StdInt.h" +#include "mozilla/StandardInteger.h" #include "mozilla/Util.h" using namespace mozilla; using namespace mozilla::layers; #ifdef PR_LOGGING extern PRLogModuleInfo* gBuiltinDecoderLog; #define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
--- a/content/media/nsMediaCache.cpp +++ b/content/media/nsMediaCache.cpp @@ -2155,26 +2155,28 @@ nsMediaCacheStream::Read(char* aBuffer, return NS_ERROR_FAILURE; PRUint32 count = 0; // Read one block (or part of a block) at a time while (count < aCount) { PRUint32 streamBlock = PRUint32(mStreamOffset/BLOCK_SIZE); PRUint32 offsetInStreamBlock = PRUint32(mStreamOffset - streamBlock*BLOCK_SIZE); - PRInt32 size = NS_MIN(aCount - count, BLOCK_SIZE - offsetInStreamBlock); + PRInt64 size = NS_MIN(aCount - count, BLOCK_SIZE - offsetInStreamBlock); if (mStreamLength >= 0) { // Don't try to read beyond the end of the stream PRInt64 bytesRemaining = mStreamLength - mStreamOffset; if (bytesRemaining <= 0) { // Get out of here and return NS_OK break; } - size = NS_MIN(size, PRInt32(bytesRemaining)); + size = NS_MIN(size, bytesRemaining); + // Clamp size until 64-bit file size issues (bug 500784) are fixed. + size = NS_MIN(size, PRInt64(PR_INT32_MAX)); } PRInt32 bytes; PRInt32 cacheBlock = streamBlock < mBlocks.Length() ? mBlocks[streamBlock] : -1; if (cacheBlock < 0) { // We don't have a complete cached block here. if (count > 0) { @@ -2219,17 +2221,18 @@ nsMediaCacheStream::Read(char* aBuffer, return NS_ERROR_FAILURE; } continue; } gMediaCache->NoteBlockUsage(this, cacheBlock, mCurrentMode, TimeStamp::Now()); PRInt64 offset = cacheBlock*BLOCK_SIZE + offsetInStreamBlock; - nsresult rv = gMediaCache->ReadCacheFile(offset, aBuffer + count, size, &bytes); + NS_ABORT_IF_FALSE(size >= 0 && size <= PR_INT32_MAX, "Size out of range."); + nsresult rv = gMediaCache->ReadCacheFile(offset, aBuffer + count, PRInt32(size), &bytes); if (NS_FAILED(rv)) { if (count == 0) return rv; // If we did successfully read some data, may as well return it break; } mStreamOffset += bytes; count += bytes; @@ -2257,25 +2260,27 @@ nsMediaCacheStream::ReadFromCache(char* // Read one block (or part of a block) at a time PRUint32 count = 0; PRInt64 streamOffset = aOffset; while (count < aCount) { PRUint32 streamBlock = PRUint32(streamOffset/BLOCK_SIZE); PRUint32 offsetInStreamBlock = PRUint32(streamOffset - streamBlock*BLOCK_SIZE); - PRInt32 size = NS_MIN<PRInt64>(aCount - count, BLOCK_SIZE - offsetInStreamBlock); + PRInt64 size = NS_MIN<PRInt64>(aCount - count, BLOCK_SIZE - offsetInStreamBlock); if (mStreamLength >= 0) { // Don't try to read beyond the end of the stream PRInt64 bytesRemaining = mStreamLength - streamOffset; if (bytesRemaining <= 0) { return NS_ERROR_FAILURE; } - size = NS_MIN(size, PRInt32(bytesRemaining)); + size = NS_MIN(size, bytesRemaining); + // Clamp size until 64-bit file size issues (bug 500784) are fixed. + size = NS_MIN(size, PRInt64(PR_INT32_MAX)); } PRInt32 bytes; PRUint32 channelBlock = PRUint32(mChannelOffset/BLOCK_SIZE); PRInt32 cacheBlock = streamBlock < mBlocks.Length() ? mBlocks[streamBlock] : -1; if (channelBlock == streamBlock && streamOffset < mChannelOffset) { // We can just use the data in mPartialBlockBuffer. In fact we should // use it rather than waiting for the block to fill and land in @@ -2284,17 +2289,18 @@ nsMediaCacheStream::ReadFromCache(char* memcpy(aBuffer + count, reinterpret_cast<char*>(mPartialBlockBuffer) + offsetInStreamBlock, bytes); } else { if (cacheBlock < 0) { // We expect all blocks to be cached! Fail! return NS_ERROR_FAILURE; } PRInt64 offset = cacheBlock*BLOCK_SIZE + offsetInStreamBlock; - nsresult rv = gMediaCache->ReadCacheFile(offset, aBuffer + count, size, &bytes); + NS_ABORT_IF_FALSE(size >= 0 && size <= PR_INT32_MAX, "Size out of range."); + nsresult rv = gMediaCache->ReadCacheFile(offset, aBuffer + count, PRInt32(size), &bytes); if (NS_FAILED(rv)) { return rv; } } streamOffset += bytes; count += bytes; }
--- a/content/media/ogg/nsOggCodecState.cpp +++ b/content/media/ogg/nsOggCodecState.cpp @@ -39,17 +39,17 @@ #include "nsDebug.h" #include "nsOggCodecState.h" #include "nsOggDecoder.h" #include <string.h> #include "nsTraceRefcnt.h" #include "VideoUtils.h" #include "nsBuiltinDecoderReader.h" -#include "mozilla/StdInt.h" +#include "mozilla/StandardInteger.h" #ifdef PR_LOGGING extern PRLogModuleInfo* gBuiltinDecoderLog; #define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg) #else #define LOG(type, msg) #endif
--- a/content/media/ogg/nsOggCodecState.h +++ b/content/media/ogg/nsOggCodecState.h @@ -46,17 +46,17 @@ #else #include <vorbis/codec.h> #endif #include <nsDeque.h> #include <nsTArray.h> #include <nsClassHashtable.h> #include "VideoUtils.h" -#include "mozilla/StdInt.h" +#include "mozilla/StandardInteger.h" // Uncomment the following to validate that we're predicting the number // of Vorbis samples in each packet correctly. #define VALIDATE_VORBIS_SAMPLE_CALCULATION #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION #include <map> #endif
--- a/content/media/wave/nsWaveReader.cpp +++ b/content/media/wave/nsWaveReader.cpp @@ -38,17 +38,17 @@ #include "nsError.h" #include "nsBuiltinDecoderStateMachine.h" #include "nsBuiltinDecoder.h" #include "MediaResource.h" #include "nsWaveReader.h" #include "nsTimeRanges.h" #include "VideoUtils.h" -#include "mozilla/StdInt.h" +#include "mozilla/StandardInteger.h" using namespace mozilla; // Un-comment to enable logging of seek bisections. //#define SEEK_LOGGING #ifdef PR_LOGGING extern PRLogModuleInfo* gBuiltinDecoderLog;
--- a/content/media/webm/nsWebMReader.h +++ b/content/media/webm/nsWebMReader.h @@ -34,17 +34,17 @@ * 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 ***** */ #if !defined(nsWebMReader_h_) #define nsWebMReader_h_ -#include "mozilla/StdInt.h" +#include "mozilla/StandardInteger.h" #include "nsDeque.h" #include "nsBuiltinDecoderReader.h" #include "nsWebMBufferedParser.h" #include "nsAutoRef.h" #include "nestegg/nestegg.h" #define VPX_DONT_DEFINE_STDINT_TYPES
--- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -111,17 +111,17 @@ XBLFinalize(JSContext *cx, JSObject *obj static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj)); NS_RELEASE(docInfo); nsXBLJSClass* c = static_cast<nsXBLJSClass*>(::JS_GetClass(obj)); c->Drop(); } static JSBool -XBLResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, +XBLResolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp) { // Note: if we get here, that means that the implementation for some binding // was installed, which means that AllowScripts() tested true. Hence no need // to do checks like that here. // Default to not resolving things. NS_ASSERTION(*objp, "Must have starting object");
--- a/content/xbl/src/nsXBLProtoImplField.h +++ b/content/xbl/src/nsXBLProtoImplField.h @@ -75,12 +75,12 @@ public: const PRUnichar* GetName() const { return mName; } protected: nsXBLProtoImplField* mNext; PRUnichar* mName; PRUnichar* mFieldText; PRUint32 mFieldTextLength; PRUint32 mLineNumber; - uintN mJSAttributes; + unsigned mJSAttributes; }; #endif // nsXBLProtoImplField_h__
--- a/content/xbl/src/nsXBLProtoImplProperty.h +++ b/content/xbl/src/nsXBLProtoImplProperty.h @@ -92,16 +92,16 @@ protected: union { // The raw text for the setter (prior to compilation). nsXBLTextWithLineNumber* mSetterText; // The JS object for the setter (after compilation) JSObject * mJSSetterObject; }; - uintN mJSAttributes; // A flag for all our JS properties (getter/setter/readonly/shared/enum) + unsigned mJSAttributes; // A flag for all our JS properties (getter/setter/readonly/shared/enum) #ifdef DEBUG bool mIsCompiled; #endif }; #endif // nsXBLProtoImplProperty_h__
--- a/dom/Makefile.in +++ b/dom/Makefile.in @@ -42,16 +42,17 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk MODULE = dom DIRS = \ interfaces/base \ interfaces/canvas \ + interfaces/contacts \ interfaces/core \ interfaces/html \ interfaces/events \ interfaces/stylesheets \ interfaces/sidebar \ interfaces/css \ interfaces/traversal \ interfaces/range \ @@ -72,16 +73,17 @@ ifeq (gonk,$(MOZ_WIDGET_TOOLKIT)) DIRS += \ interfaces/apps \ $(NULL) endif DIRS += \ base \ battery \ + contacts \ power \ sms \ src \ locales \ network \ plugins/base \ plugins/ipc \ indexedDB \
--- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -679,21 +679,16 @@ protected: virtual ~IDBEventTargetSH() { } public: NS_IMETHOD PreCreate(nsISupports *aNativeObj, JSContext *aCx, JSObject *aGlobalObj, JSObject **aParentObj); - NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *aWrapper, JSContext *aCx, - JSObject *aObj, jsid aId, jsval *aVp, bool *aRetval); - - virtual void PreserveWrapper(nsISupports *aNative); - static nsIClassInfo *doCreate(nsDOMClassInfoData *aData) { return new IDBEventTargetSH(aData); } }; } // anonymous namespace @@ -5147,16 +5142,21 @@ nsWindowSH::PreCreate(nsISupports *nativ nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(nativeObj)); NS_ASSERTION(sgo, "nativeObj not a global object!"); nsGlobalWindow *win = nsGlobalWindow::FromSupports(nativeObj); NS_ASSERTION(win->IsInnerWindow(), "Should be inner window."); JSObject *winObj = win->FastGetGlobalJSObject(); if (!winObj) { + + // See bug 691178 comment 11 for why this is necessary. + if (win->IsClosedOrClosing()) + return NS_ERROR_FAILURE; + NS_ASSERTION(win->GetOuterWindowInternal()->IsCreatingInnerWindow(), "should have a JS object by this point"); return NS_OK; } *parentObj = winObj; return NS_OK; } @@ -5235,17 +5235,17 @@ GetDocument(JSObject *obj) { return static_cast<nsHTMLDocument*>( static_cast<nsIHTMLDocument*>(::JS_GetPrivate(obj))); } // static JSBool nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj, - jsid id, uintN flags, + jsid id, unsigned flags, JSObject **objp) { if (flags & (JSRESOLVE_ASSIGNING | JSRESOLVE_DECLARING | JSRESOLVE_CLASSNAME | JSRESOLVE_QUALIFIED) || !JSID_IS_STRING(id)) { // Nothing to do here if we're either assigning or declaring, // resolving a class name, doing a qualified resolve, or // resolving a number. @@ -5541,17 +5541,17 @@ FindConstructorFunc(const nsDOMClassInfo } } return nsnull; } static nsresult BaseStubConstructor(nsIWeakReference* aWeakOwner, const nsGlobalNameStruct *name_struct, JSContext *cx, - JSObject *obj, uintN argc, jsval *argv, jsval *rval) + JSObject *obj, unsigned argc, jsval *argv, jsval *rval) { nsresult rv; nsCOMPtr<nsISupports> native; if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) { const nsDOMClassInfoData* ci_data = &sClassInfoData[name_struct->mDOMClassInfoID]; const char *contractid = FindConstructorContractID(ci_data); if (contractid) { @@ -6635,17 +6635,17 @@ nsWindowSH::GlobalResolve(nsGlobalWindow } return rv; } // Native code for window._content getter, this simply maps // window._content to window.content for backwards compatibility only. static JSBool -ContentWindowGetter(JSContext *cx, uintN argc, jsval *vp) +ContentWindowGetter(JSContext *cx, unsigned argc, jsval *vp) { JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; return ::JS_GetProperty(cx, obj, "content", vp); } @@ -7695,35 +7695,16 @@ IDBEventTargetSH::PreCreate(nsISupports JSObject *aGlobalObj, JSObject **aParentObj) { IDBWrapperCache *target = IDBWrapperCache::FromSupports(aNativeObj); JSObject *parent = target->GetParentObject(); *aParentObj = parent ? parent : aGlobalObj; return NS_OK; } -NS_IMETHODIMP -IDBEventTargetSH::AddProperty(nsIXPConnectWrappedNative *aWrapper, - JSContext *aCx, JSObject *aObj, jsid aId, - jsval *aVp, bool *aRetval) -{ - if (aId != sAddEventListener_id) { - IDBEventTargetSH::PreserveWrapper(GetNative(aWrapper, aObj)); - } - - return NS_OK; -} - -void -IDBEventTargetSH::PreserveWrapper(nsISupports *aNative) -{ - IDBWrapperCache *target = IDBWrapperCache::FromSupports(aNative); - nsContentUtils::PreserveWrapper(aNative, target); -} - // Element helper static bool GetBindingURL(Element *aElement, nsIDocument *aDocument, nsCSSValue::URL **aResult) { // If we have a frame the frame has already loaded the binding. And // otherwise, don't do anything else here unless we're dealing with @@ -8896,17 +8877,17 @@ nsHTMLDocumentSH::DocumentAllGetProperty *vp = JSVAL_VOID; } return JS_TRUE; } JSBool nsHTMLDocumentSH::DocumentAllNewResolve(JSContext *cx, JSObject *obj, jsid id, - uintN flags, JSObject **objp) + unsigned flags, JSObject **objp) { if (flags & JSRESOLVE_ASSIGNING) { // Nothing to do here if we're assigning return JS_TRUE; } jsval v = JSVAL_VOID; @@ -8966,17 +8947,17 @@ void nsHTMLDocumentSH::ReleaseDocument(JSContext *cx, JSObject *obj) { nsIHTMLDocument *doc = (nsIHTMLDocument *)::JS_GetPrivate(obj); NS_IF_RELEASE(doc); } JSBool -nsHTMLDocumentSH::CallToGetPropMapper(JSContext *cx, uintN argc, jsval *vp) +nsHTMLDocumentSH::CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp) { // Handle document.all("foo") style access to document.all. if (argc != 1) { // XXX: Should throw NS_ERROR_XPC_NOT_ENOUGH_ARGS for argc < 1, // and create a new NS_ERROR_XPC_TOO_MANY_ARGS for argc > 1? IE // accepts nothing other than one arg. nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_INVALID_ARG); @@ -9092,17 +9073,17 @@ nsHTMLDocumentSH::DocumentAllHelperGetPr } } return JS_TRUE; } JSBool nsHTMLDocumentSH::DocumentAllHelperNewResolve(JSContext *cx, JSObject *obj, - jsid id, uintN flags, + jsid id, unsigned flags, JSObject **objp) { if (id == nsDOMClassInfo::sAll_id) { // document.all is resolved for the first time. Define it. JSObject *helper = GetDocumentAllHelper(obj); if (helper) { if (!::JS_DefineProperty(cx, helper, "all", JSVAL_VOID, nsnull, nsnull, @@ -9115,17 +9096,17 @@ nsHTMLDocumentSH::DocumentAllHelperNewRe } return JS_TRUE; } JSBool nsHTMLDocumentSH::DocumentAllTagsNewResolve(JSContext *cx, JSObject *obj, - jsid id, uintN flags, + jsid id, unsigned flags, JSObject **objp) { if (JSID_IS_STRING(id)) { nsDocument *doc = GetDocument(obj); JSObject *proto = ::JS_GetPrototype(obj); if (NS_UNLIKELY(!proto)) { return JS_TRUE;
--- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -420,17 +420,17 @@ public: JSObject *obj, jsid id, PRUint32 flags, JSObject **objp, bool *_retval); NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj); NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, JSObject * *_retval); static JSBool GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj, - jsid id, uintN flags, + jsid id, unsigned flags, JSObject **objp); static JSBool GlobalScopePolluterGetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); static JSBool SecurityCheckOnAddDelProp(JSContext *cx, JSObject *obj, jsid id, jsval *vp); static JSBool SecurityCheckOnSetProp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp); static void InvalidateGlobalScopePolluter(JSContext *cx, JSObject *obj); @@ -858,26 +858,26 @@ protected: static JSBool GetDocumentAllNodeList(JSContext *cx, JSObject *obj, nsDocument *doc, nsContentList **nodeList); public: static JSBool DocumentAllGetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); static JSBool DocumentAllNewResolve(JSContext *cx, JSObject *obj, jsid id, - uintN flags, JSObject **objp); + unsigned flags, JSObject **objp); static void ReleaseDocument(JSContext *cx, JSObject *obj); - static JSBool CallToGetPropMapper(JSContext *cx, uintN argc, jsval *vp); + static JSBool CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp); static JSBool DocumentAllHelperGetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); static JSBool DocumentAllHelperNewResolve(JSContext *cx, JSObject *obj, - jsid id, uintN flags, + jsid id, unsigned flags, JSObject **objp); static JSBool DocumentAllTagsNewResolve(JSContext *cx, JSObject *obj, - jsid id, uintN flags, + jsid id, unsigned flags, JSObject **objp); NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, PRUint32 flags, JSObject **objp, bool *_retval); NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, jsval *vp, bool *_retval);
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1222,58 +1222,18 @@ nsGlobalWindow::ClearControllers() if (context) context->SetCommandContext(nsnull); } mControllers = nsnull; } } -// static void -nsGlobalWindow::TryClearWindowScope(nsISupports *aWindow) -{ - nsGlobalWindow *window = - static_cast<nsGlobalWindow *>(static_cast<nsIDOMWindow*>(aWindow)); - - // This termination function might be called when any script evaluation in our - // context terminated, even if there are other scripts in the stack. Thus, we - // have to check again if a script is executing and post a new termination - // function if necessary. - window->ClearScopeWhenAllScriptsStop(); -} - -void -nsGlobalWindow::ClearScopeWhenAllScriptsStop() -{ - NS_ASSERTION(IsInnerWindow(), "Must be an inner window"); - - // We cannot clear scope safely until all the scripts in our script context - // stopped. This might be a long wait, for example if one script is busy - // because it started a nested event loop for a modal dialog. - nsIScriptContext *jsscx = GetContextInternal(); - if (jsscx && jsscx->GetExecutingScript()) { - // We ignore the return value because the only reason that we clear scope - // here is to try to prevent leaks. Failing to clear scope might mean that - // we'll leak more but if we don't have enough memory to allocate a - // termination function we probably don't have to worry about this anyway. - jsscx->SetTerminationFunction(TryClearWindowScope, - static_cast<nsIDOMWindow *>(this)); - return; - } - - NotifyWindowIDDestroyed("inner-window-destroyed"); - nsIScriptContext *scx = GetContextInternal(); - if (scx) { - scx->ClearScope(mJSObject, true); - } -} - -void -nsGlobalWindow::FreeInnerObjects(bool aClearScope) +nsGlobalWindow::FreeInnerObjects() { NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window"); // Make sure that this is called before we null out the document and // other members that the window destroyed observers could // re-create. NotifyDOMWindowDestroyed(this); @@ -1325,19 +1285,17 @@ nsGlobalWindow::FreeInnerObjects(bool aC if (mApplicationCache) { static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->Disconnect(); mApplicationCache = nsnull; } mIndexedDB = nsnull; - if (aClearScope) { - ClearScopeWhenAllScriptsStop(); - } + NotifyWindowIDDestroyed("inner-window-destroyed"); if (mDummyJavaPluginOwner) { // Tear down the dummy java plugin. // XXXjst: On a general note, should windows with java stuff in // them ever even make it into the fast-back cache? mDummyJavaPluginOwner->Destroy(); @@ -1661,17 +1619,16 @@ nsGlobalWindow::WouldReuseInnerWindow(ns } NS_ASSERTION(NS_IsAboutBlank(mDoc->GetDocumentURI()), "How'd this happen?"); // Great, we're the original document, check for one of the other // conditions. if (mDoc == aNewDocument) { - // aClearScopeHint is false. return true; } bool equal; if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(), &equal)) && equal) { // The origin is the same. @@ -1835,20 +1792,18 @@ WindowStateHolder::WindowStateHolder(nsG WindowStateHolder::~WindowStateHolder() { if (mInnerWindow) { // This window was left in the bfcache and is now going away. We need to // free it up. // Note that FreeInnerObjects may already have been called on the // inner window if its outer has already had SetDocShell(null) - // called. In this case the contexts will all be null and the - // true for aClearScope won't do anything; this is OK since - // SetDocShell(null) already did it. - mInnerWindow->FreeInnerObjects(true); + // called. + mInnerWindow->FreeInnerObjects(); } } NS_IMPL_ISUPPORTS1(WindowStateHolder, WindowStateHolder) struct ReparentWaiverClosure { @@ -2001,22 +1956,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume return NS_ERROR_FAILURE; } JSAutoRequest ar(cx); nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState); NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?"); - // Make sure to clear scope on the outer window *before* we - // initialize the new inner window. If we don't, things - // (Object.prototype etc) could leak from the old outer to the new - // inner scope. - mContext->ClearScope(mJSObject, false); - if (reUseInnerWindow) { // We're reusing the current inner window. NS_ASSERTION(!currentInner->IsFrozen(), "We should never be reusing a shared inner window"); newInnerWindow = currentInner; if (aDocument != oldDoc) { nsWindowSH::InvalidateGlobalScopePolluter(cx, currentInner->mJSObject); @@ -2076,66 +2025,31 @@ nsGlobalWindow::SetNewDocument(nsIDocume mCreatingInnerWindow = false; Thaw(); NS_ENSURE_SUCCESS(rv, rv); } if (currentInner && currentInner->mJSObject) { - bool termFuncSet = false; - if (oldDoc == aDocument) { // Move the navigator from the old inner window to the new one since // this is a document.write. This is safe from a same-origin point of // view because document.write can only be used by the same origin. newInnerWindow->mNavigator = currentInner->mNavigator; currentInner->mNavigator = nsnull; if (newInnerWindow->mNavigator) { newInnerWindow->mNavigator->SetWindow(newInnerWindow); } - - // Suspend the current context's request before Pop() resumes the old - // context's request. - JSAutoSuspendRequest asr(cx); - - // Pop our context here so that we get the correct one for the - // termination function. - cxPusher.Pop(); - - JSContext *oldCx = nsContentUtils::GetCurrentJSContext(); - - nsIScriptContext *callerScx; - if (oldCx && (callerScx = GetScriptContextFromJSContext(oldCx))) { - // We're called from document.open() (and document.open() is - // called from JS), clear the scope etc in a termination - // function on the calling context to prevent clearing the - // calling scope. - NS_ASSERTION(!currentInner->IsFrozen(), - "How does this opened window get into session history"); - - JSAutoRequest ar(oldCx); - - callerScx->SetTerminationFunction(ClearWindowScope, - static_cast<nsIDOMWindow *> - (currentInner)); - - termFuncSet = true; - } - - // Re-push our context. - cxPusher.Push(cx); } - // Don't clear scope on our current inner window if it's going to be + // Don't free objects on our current inner window if it's going to be // held in the bfcache. if (!currentInner->IsFrozen()) { - // Skip the ClearScope if we set a termination function to do - // it ourselves, later. - currentInner->FreeInnerObjects(!termFuncSet); + currentInner->FreeInnerObjects(); } } mInnerWindow = newInnerWindow; if (!mJSObject) { mContext->CreateOuterObject(this, newInnerWindow); mContext->DidInitializeContext(); @@ -2172,17 +2086,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume } JS_SetParent(cx, mJSObject, newInnerWindow->mJSObject); mContext->SetOuterObject(mJSObject); JSCompartment *compartment = js::GetObjectCompartment(mJSObject); xpc::CompartmentPrivate *priv = - static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(cx, compartment)); + static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(compartment)); if (priv && priv->waiverWrapperMap) { NS_ASSERTION(!JS_IsExceptionPending(cx), "We might overwrite a pending exception!"); ReparentWaiverClosure closure = { cx, newInnerWindow->mJSObject }; priv->waiverWrapperMap->Enumerate(ReparentWaiverWrappers, &closure); @@ -2428,17 +2342,17 @@ nsGlobalWindow::SetDocShell(nsIDocShell* // Call FreeInnerObjects on all inner windows, not just the current // one, since some could be held by WindowStateHolder objects that // are GC-owned. for (nsRefPtr<nsGlobalWindow> inner = (nsGlobalWindow *)PR_LIST_HEAD(this); inner != this; inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) { NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this, "bad outer window pointer"); - inner->FreeInnerObjects(true); + inner->FreeInnerObjects(); } // Make sure that this is called before we null out the document. NotifyDOMWindowDestroyed(this); NotifyWindowIDDestroyed("outer-window-destroyed"); nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); @@ -2450,20 +2364,16 @@ nsGlobalWindow::SetDocShell(nsIDocShell* mDocumentPrincipal = mDoc->NodePrincipal(); // Release our document reference mDocument = nsnull; mDoc = nsnull; mFocusedNode = nsnull; } - if (mContext) { - mContext->ClearScope(mJSObject, true); - } - ClearControllers(); mChromeEventHandler = nsnull; // force release now if (mArguments) { // We got no new document after someone called // SetArguments(), drop our reference to the arguments. mArguments = nsnull; @@ -8980,27 +8890,16 @@ nsGlobalWindow::CloseWindow(nsISupports (static_cast<nsPIDOMWindow*>(win)); // Need to post an event for closing, otherwise window and // presshell etc. may get destroyed while creating frames, bug 338897. nsCloseEvent::PostCloseEvent(globalWin); // else if OOM, better not to close. That might cause a crash. } -// static -void -nsGlobalWindow::ClearWindowScope(nsISupports *aWindow) -{ - nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aWindow)); - nsIScriptContext *scx = sgo->GetContext(); - if (scx) { - scx->ClearScope(sgo->GetGlobalJSObject(), true); - } -} - //***************************************************************************** // nsGlobalWindow: Timeout Functions //***************************************************************************** PRUint32 sNestingLevel; nsresult nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
--- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -231,17 +231,17 @@ private: //***************************************************************************** // nsOuterWindow: Outer Window Proxy //***************************************************************************** class nsOuterWindowProxy : public js::Wrapper { public: - nsOuterWindowProxy() : js::Wrapper((uintN)0) {} + nsOuterWindowProxy() : js::Wrapper((unsigned)0) {} virtual bool isOuterWindow() { return true; } JSString *obj_toString(JSContext *cx, JSObject *wrapper); void finalize(JSContext *cx, JSObject *proxy); static nsOuterWindowProxy singleton; @@ -590,21 +590,19 @@ private: protected: friend class HashchangeCallback; friend class nsBarProp; // Object Management virtual ~nsGlobalWindow(); void CleanUp(bool aIgnoreModalDialog); void ClearControllers(); - static void TryClearWindowScope(nsISupports* aWindow); - void ClearScopeWhenAllScriptsStop(); nsresult FinalClose(); - void FreeInnerObjects(bool aClearScope); + void FreeInnerObjects(); nsGlobalWindow *CallerInnerWindow(); nsresult InnerSetNewDocument(nsIDocument* aDocument); nsresult DefineArgumentsProperty(nsIArray *aArguments); // Get the parent, returns null if this is a toplevel window nsIDOMWindow* GetParentInternal(); @@ -672,17 +670,16 @@ protected: bool aDoJSFixups, nsIArray *argv, nsISupports *aExtraArgument, nsIPrincipal *aCalleePrincipal, JSContext *aJSCallerContext, nsIDOMWindow **aReturn); static void CloseWindow(nsISupports* aWindow); - static void ClearWindowScope(nsISupports* aWindow); // Timeout Functions // Language agnostic timeout function (all args passed). // |interval| is in milliseconds. nsresult SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler, PRInt32 interval, bool aIsInterval, PRInt32 *aReturn); nsresult ClearTimeoutOrInterval(PRInt32 aTimerID);
--- a/dom/base/nsIScriptContext.h +++ b/dom/base/nsIScriptContext.h @@ -70,18 +70,18 @@ public: virtual nsIScriptObjectPrincipal* GetObjectPrincipal() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptContextPrincipal, NS_ISCRIPTCONTEXTPRINCIPAL_IID) #define NS_ISCRIPTCONTEXT_IID \ -{ 0xf3840057, 0x4fe5, 0x4f92, \ - { 0xa3, 0xb8, 0x27, 0xd7, 0x44, 0x6f, 0x72, 0x4d } } +{ 0x6d69fbee, 0x0723, 0x48f5, \ + { 0x82, 0x48, 0xcd, 0xcf, 0x88, 0xac, 0x25, 0x74 } } /* This MUST match JSVERSION_DEFAULT. This version stuff if we don't know what language we have is a little silly... */ #define SCRIPTVERSION_DEFAULT JSVERSION_DEFAULT /** * It is used by the application to initialize a runtime and run scripts. * A script runtime would implement this interface. @@ -423,30 +423,16 @@ public: * Initialize DOM classes on aGlobalObj, always call * WillInitializeContext() before calling InitContext(), and always * call DidInitializeContext() when a context is fully * (successfully) initialized. */ virtual nsresult InitClasses(JSObject* aGlobalObj) = 0; /** - * Clear the scope object - may be called either as we are being torn down, - * or before we are attached to a different document. - * - * aClearFromProtoChain is probably somewhat JavaScript specific. It - * indicates that the global scope polluter should be removed from the - * prototype chain and that the objects in the prototype chain should - * also have their scopes cleared. We don't do this all the time - * because the prototype chain is shared between inner and outer - * windows, and needs to stay with inner windows that we're keeping - * around. - */ - virtual void ClearScope(void* aGlobalObj, bool aClearFromProtoChain) = 0; - - /** * Tell the context we're about to be reinitialize it. */ virtual void WillInitializeContext() = 0; /** * Tell the context we're done reinitializing it. */ virtual void DidInitializeContext() = 0;
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2717,39 +2717,39 @@ CheckUniversalXPConnectForTraceMalloc(JS IsCapabilityEnabled("UniversalXPConnect", &hasCap); if (NS_SUCCEEDED(rv) && hasCap) return JS_TRUE; JS_ReportError(cx, "trace-malloc functions require UniversalXPConnect"); return JS_FALSE; } static JSBool -TraceMallocDisable(JSContext *cx, uintN argc, jsval *vp) +TraceMallocDisable(JSContext *cx, unsigned argc, jsval *vp) { if (!CheckUniversalXPConnectForTraceMalloc(cx)) return JS_FALSE; NS_TraceMallocDisable(); JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } static JSBool -TraceMallocEnable(JSContext *cx, uintN argc, jsval *vp) +TraceMallocEnable(JSContext *cx, unsigned argc, jsval *vp) { if (!CheckUniversalXPConnectForTraceMalloc(cx)) return JS_FALSE; NS_TraceMallocEnable(); JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } static JSBool -TraceMallocOpenLogFile(JSContext *cx, uintN argc, jsval *vp) +TraceMallocOpenLogFile(JSContext *cx, unsigned argc, jsval *vp) { int fd; JSString *str; if (!CheckUniversalXPConnectForTraceMalloc(cx)) return JS_FALSE; if (argc == 0) { @@ -2767,17 +2767,17 @@ TraceMallocOpenLogFile(JSContext *cx, ui return JS_FALSE; } } JS_SET_RVAL(cx, vp, INT_TO_JSVAL(fd)); return JS_TRUE; } static JSBool -TraceMallocChangeLogFD(JSContext *cx, uintN argc, jsval *vp) +TraceMallocChangeLogFD(JSContext *cx, unsigned argc, jsval *vp) { if (!CheckUniversalXPConnectForTraceMalloc(cx)) return JS_FALSE; int32_t fd, oldfd; if (argc == 0) { oldfd = -1; } else { @@ -2789,50 +2789,50 @@ TraceMallocChangeLogFD(JSContext *cx, ui return JS_FALSE; } } JS_SET_RVAL(cx, vp, INT_TO_JSVAL(oldfd)); return JS_TRUE; } static JSBool -TraceMallocCloseLogFD(JSContext *cx, uintN argc, jsval *vp) +TraceMallocCloseLogFD(JSContext *cx, unsigned argc, jsval *vp) { if (!CheckUniversalXPConnectForTraceMalloc(cx)) return JS_FALSE; int32_t fd; JS_SET_RVAL(cx, vp, JSVAL_VOID); if (argc == 0) return JS_TRUE; if (!JS_ValueToECMAInt32(cx, JS_ARGV(cx, vp)[0], &fd)) return JS_FALSE; NS_TraceMallocCloseLogFD((int) fd); return JS_TRUE; } static JSBool -TraceMallocLogTimestamp(JSContext *cx, uintN argc, jsval *vp) +TraceMallocLogTimestamp(JSContext *cx, unsigned argc, jsval *vp) { if (!CheckUniversalXPConnectForTraceMalloc(cx)) return JS_FALSE; JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID); if (!str) return JS_FALSE; JSAutoByteString caption(cx, str); if (!caption) return JS_FALSE; NS_TraceMallocLogTimestamp(caption.ptr()); JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } static JSBool -TraceMallocDumpAllocations(JSContext *cx, uintN argc, jsval *vp) +TraceMallocDumpAllocations(JSContext *cx, unsigned argc, jsval *vp) { if (!CheckUniversalXPConnectForTraceMalloc(cx)) return JS_FALSE; JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID); if (!str) return JS_FALSE; JSAutoByteString pathname(cx, str); @@ -2870,17 +2870,17 @@ IsJProfAction(struct sigaction *action) (action->sa_flags & (SA_RESTART | SA_SIGINFO)) == (SA_RESTART | SA_SIGINFO)); } void NS_JProfStartProfiling(); void NS_JProfStopProfiling(); void NS_JProfClearCircular(); static JSBool -JProfStartProfilingJS(JSContext *cx, uintN argc, jsval *vp) +JProfStartProfilingJS(JSContext *cx, unsigned argc, jsval *vp) { NS_JProfStartProfiling(); return JS_TRUE; } void NS_JProfStartProfiling() { // Figure out whether we're dealing with SIGPROF, SIGALRM, or @@ -2912,45 +2912,45 @@ void NS_JProfStartProfiling() raise(SIGPOLL); return; } printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n"); } static JSBool -JProfStopProfilingJS(JSContext *cx, uintN argc, jsval *vp) +JProfStopProfilingJS(JSContext *cx, unsigned argc, jsval *vp) { NS_JProfStopProfiling(); return JS_TRUE; } void NS_JProfStopProfiling() { raise(SIGUSR1); //printf("Stopped jprof profiling.\n"); } static JSBool -JProfClearCircularJS(JSContext *cx, uintN argc, jsval *vp) +JProfClearCircularJS(JSContext *cx, unsigned argc, jsval *vp) { NS_JProfClearCircular(); return JS_TRUE; } void NS_JProfClearCircular() { raise(SIGUSR2); //printf("cleared jprof buffer\n"); } static JSBool -JProfSaveCircularJS(JSContext *cx, uintN argc, jsval *vp) +JProfSaveCircularJS(JSContext *cx, unsigned argc, jsval *vp) { // Not ideal... NS_JProfStopProfiling(); NS_JProfStartProfiling(); return JS_TRUE; } static JSFunctionSpec JProfFunctions[] = { @@ -2964,17 +2964,17 @@ static JSFunctionSpec JProfFunctions[] = #endif /* defined(MOZ_JPROF) */ #ifdef MOZ_DMD // See https://wiki.mozilla.org/Performance/MemShrink/DMD for instructions on // how to use DMD. static JSBool -DMDCheckJS(JSContext *cx, uintN argc, jsval *vp) +DMDCheckJS(JSContext *cx, unsigned argc, jsval *vp) { mozilla::DMDCheckAndDump(); return JS_TRUE; } static JSFunctionSpec DMDFunctions[] = { {"DMD", DMDCheckJS, 0, 0}, {nsnull, nsnull, 0, 0} @@ -3011,92 +3011,16 @@ nsJSContext::InitClasses(JSObject* aGlob #endif JSOptionChangedCallback(js_options_dot_str, this); return rv; } void -nsJSContext::ClearScope(void *aGlobalObj, bool aClearFromProtoChain) -{ - // Push our JSContext on our thread's context stack. - nsCOMPtr<nsIJSContextStack> stack = - do_GetService("@mozilla.org/js/xpc/ContextStack;1"); - if (stack && NS_FAILED(stack->Push(mContext))) { - stack = nsnull; - } - - if (aGlobalObj) { - JSObject *obj = (JSObject *)aGlobalObj; - JSAutoRequest ar(mContext); - - JSAutoEnterCompartment ac; - ac.enterAndIgnoreErrors(mContext, obj); - - // Grab a reference to the window property, which is the outer - // window, so that we can re-define it once we've cleared - // scope. This is what keeps the outer window alive in cases where - // nothing else does. - jsval window; - if (!JS_GetProperty(mContext, obj, "window", &window)) { - window = JSVAL_VOID; - - JS_ClearPendingException(mContext); - } - - JS_ClearScope(mContext, obj); - - NS_ABORT_IF_FALSE(!xpc::WrapperFactory::IsXrayWrapper(obj), "unexpected wrapper"); - - if (window != JSVAL_VOID) { - if (!JS_DefineProperty(mContext, obj, "window", window, - JS_PropertyStub, JS_StrictPropertyStub, - JSPROP_ENUMERATE | JSPROP_READONLY | - JSPROP_PERMANENT)) { - JS_ClearPendingException(mContext); - } - } - - if (!js::GetObjectParent(obj)) { - JS_ClearRegExpStatics(mContext, obj); - } - - // Always clear watchpoints, to deal with two cases: - // 1. The first document for this window is loading, and a miscreant has - // preset watchpoints on the window object in order to attack the new - // document's privileged information. - // 2. A document loaded and used watchpoints on its own window, leaving - // them set until the next document loads. We must clean up window - // watchpoints here. - // Watchpoints set on document and subordinate objects are all cleared - // when those sub-window objects are finalized, after JS_ClearScope and - // a GC run that finds them to be garbage. - ::JS_ClearWatchPointsForObject(mContext, obj); - - // Since the prototype chain is shared between inner and outer (and - // stays with the inner), we don't clear things from the prototype - // chain when we're clearing an outer window whose current inner we - // still want. - if (aClearFromProtoChain) { - nsWindowSH::InvalidateGlobalScopePolluter(mContext, obj); - - // Clear up obj's prototype chain, but not Object.prototype. - for (JSObject *o = ::JS_GetPrototype(obj), *next; - o && (next = ::JS_GetPrototype(o)); o = next) - ::JS_ClearScope(mContext, o); - } - } - - if (stack) { - stack->Pop(nsnull); - } -} - -void nsJSContext::WillInitializeContext() { mIsInitialized = false; } void nsJSContext::DidInitializeContext() {
--- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -156,17 +156,16 @@ public: virtual bool GetProcessingScriptTag(); virtual void SetProcessingScriptTag(bool aResult); virtual bool GetExecutingScript(); virtual void SetGCOnDestruction(bool aGCOnDestruction); virtual nsresult InitClasses(JSObject* aGlobalObj); - virtual void ClearScope(void* aGlobalObj, bool bClearPolluters); virtual void WillInitializeContext(); virtual void DidInitializeContext(); virtual nsresult Serialize(nsIObjectOutputStream* aStream, JSScript* aScriptObject); virtual nsresult Deserialize(nsIObjectInputStream* aStream, nsScriptObjectHolder<JSScript>& aResult);
new file mode 100644 --- /dev/null +++ b/dom/contacts/ContactManager.js @@ -0,0 +1,404 @@ +/* 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/. */ + +"use strict" + +/* static functions */ +let DEBUG = 0; +if (DEBUG) + debug = function (s) { dump("-*- ContactManager: " + s + "\n"); } +else + debug = function (s) {} + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +const nsIClassInfo = Ci.nsIClassInfo; +const CONTACTPROPERTIES_CID = Components.ID("{53ed7c20-ceda-11e0-9572-0800200c9a66}"); +const nsIDOMContactProperties = Ci.nsIDOMContactProperties; + +// ContactProperties is not directly instantiated. It is used as interface. + +ContactProperties.prototype = { + + classID : CONTACTPROPERTIES_CID, + classInfo : XPCOMUtils.generateCI({classID: CONTACTPROPERTIES_CID, + contractID:"@mozilla.org/contactProperties;1", + classDescription: "ContactProperties", + interfaces: [nsIDOMContactProperties], + flags: nsIClassInfo.DOM_OBJECT}), + + QueryInterface : XPCOMUtils.generateQI([nsIDOMContactProperties]) +} + +//ContactAddress + +const CONTACTADDRESS_CONTRACTID = "@mozilla.org/contactAddress;1"; +const CONTACTADDRESS_CID = Components.ID("{27a568b0-cee1-11e0-9572-0800200c9a66}"); +const nsIDOMContactAddress = Components.interfaces.nsIDOMContactAddress; + +function ContactAddress(aStreetAddress, aLocality, aRegion, aPostalCode, aCountryName) { + this.streetAddress = aStreetAddress || null; + this.locality = aLocality || null; + this.region = aRegion || null; + this.postalCode = aPostalCode || null; + this.countryName = aCountryName || null; +}; + +function ContactProperties(aProp) { debug("ContactProperties Constructor"); } + +ContactAddress.prototype = { + + classID : CONTACTADDRESS_CID, + classInfo : XPCOMUtils.generateCI({classID: CONTACTADDRESS_CID, + contractID: CONTACTADDRESS_CONTRACTID, + classDescription: "ContactAddress", + interfaces: [nsIDOMContactAddress], + flags: nsIClassInfo.DOM_OBJECT}), + + QueryInterface : XPCOMUtils.generateQI([nsIDOMContactAddress]) +} + +//ContactFindOptions + +const CONTACTFINDOPTIONS_CONTRACTID = "@mozilla.org/contactFindOptions;1"; +const CONTACTFINDOPTIONS_CID = Components.ID("{e31daea0-0cb6-11e1-be50-0800200c9a66}"); +const nsIDOMContactFindOptions = Components.interfaces.nsIDOMContactFindOptions; + +function ContactFindOptions(aFilterValue, aFilterBy, aFilterOp, aFilterLimit) { + this.filterValue = aFilterValue || ''; + + this.filterBy = new Array(); + for (let field in aFilterBy) + this.filterBy.push(field); + + this.filterOp = aFilterOp || ''; + this.filterLimit = aFilterLimit || 0; +}; + +ContactFindOptions.prototype = { + + classID : CONTACTFINDOPTIONS_CID, + classInfo : XPCOMUtils.generateCI({classID: CONTACTFINDOPTIONS_CID, + contractID: CONTACTFINDOPTIONS_CONTRACTID, + classDescription: "ContactFindOptions", + interfaces: [nsIDOMContactFindOptions], + flags: nsIClassInfo.DOM_OBJECT}), + + QueryInterface : XPCOMUtils.generateQI([nsIDOMContactFindOptions]) +} + +//Contact + +const CONTACT_CONTRACTID = "@mozilla.org/contact;1"; +const CONTACT_CID = Components.ID("{da0f7040-388b-11e1-b86c-0800200c9a66}"); +const nsIDOMContact = Components.interfaces.nsIDOMContact; + +function Contact() { debug("Contact constr: "); }; + +Contact.prototype = { + + init: function init(aProp) { + // Accept non-array strings for DOMString[] properties and convert them. + function _create(aField) { + if (typeof aField == "string") + return new Array(aField); + return aField; + }; + + this.name = _create(aProp.name) || null; + this.honorificPrefix = _create(aProp.honorificPrefix) || null; + this.givenName = _create(aProp.givenName) || null; + this.additionalName = _create(aProp.additionalName) || null; + this.familyName = _create(aProp.familyName) || null; + this.honorificSuffix = _create(aProp.honorificSuffix) || null; + this.nickname = _create(aProp.nickname) || null; + this.email = _create(aProp.email) || null; + this.photo = _create(aProp.photo) || null; + this.url = _create(aProp.url) || null; + this.category = _create(aProp.category) || null; + + if (aProp.adr) { + // Make sure adr argument is an array. Instanceof doesn't work. + aProp.adr = aProp.adr.length == undefined ? [aProp.adr] : aProp.adr; + + this.adr = new Array(); + for (let i = 0; i < aProp.adr.length; i++) + this.adr.push(new ContactAddress(aProp.adr[i].streetAddress, aProp.adr[i].locality, + aProp.adr[i].region, aProp.adr[i].postalCode, + aProp.adr[i].countryName)); + } else { + this.adr = null; + } + + this.tel = _create(aProp.tel) || null; + this.org = _create(aProp.org) || null; + this.bday = (aProp.bday == "undefined" || aProp.bday == null) ? null : new Date(aProp.bday); + this.note = _create(aProp.note) || null; + this.impp = _create(aProp.impp) || null; + this.anniversary = (aProp.anniversary == "undefined" || aProp.anniversary == null) ? null : new Date(aProp.anniversary); + this.sex = (aProp.sex != "undefined") ? aProp.sex : null; + this.genderIdentity = (aProp.genderIdentity != "undefined") ? aProp.genderIdentity : null; + }, + + get published () { + return this._published; + }, + + set published(aPublished) { + this._published = aPublished; + }, + + get updated () { + return this._updated; + }, + + set updated(aUpdated) { + this._updated = aUpdated; + }, + + classID : CONTACT_CID, + classInfo : XPCOMUtils.generateCI({classID: CONTACT_CID, + contractID: CONTACT_CONTRACTID, + classDescription: "Contact", + interfaces: [nsIDOMContact, nsIDOMContactProperties], + flags: nsIClassInfo.DOM_OBJECT}), + + QueryInterface : XPCOMUtils.generateQI([nsIDOMContact, nsIDOMContactProperties]) +} + +// ContactManager + +const CONTACTMANAGER_CONTRACTID = "@mozilla.org/contactManager;1"; +const CONTACTMANAGER_CID = Components.ID("{50a820b0-ced0-11e0-9572-0800200c9a66}"); +const nsIDOMContactManager = Components.interfaces.nsIDOMContactManager; + +function ContactManager() +{ + debug("Constructor"); +} + +ContactManager.prototype = { + + save: function save(aContact) { + let request; + if (this.hasPrivileges) { + debug("save: " + JSON.stringify(aContact) + " :" + aContact.id); + let newContact = {}; + newContact.properties = { + name: [], + honorificPrefix: [], + givenName: [], + additionalName: [], + familyName: [], + honorificSuffix: [], + nickname: [], + email: [], + photo: [], + url: [], + category: [], + adr: [], + tel: [], + org: [], + bday: null, + note: [], + impp: [], + anniversary: null, + sex: null, + genderIdentity: null + }; + for (let field in newContact.properties) + newContact.properties[field] = aContact[field]; + + if (aContact.id == "undefined") { + debug("Create id!"); + aContact.id = this._getRandomId(); + } + + this._setMetaData(newContact, aContact); + debug("send: " + JSON.stringify(newContact)); + request = this._rs.createRequest(this._window); + this._mm.sendAsyncMessage("Contact:Save", {contact: newContact, + requestID: this.getRequestId({ request: request })}); + return request; + } else { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + } + }, + + remove: function removeContact(aRecord) { + let request; + if (this.hasPrivileges) { + request = this._rs.createRequest(this._window); + this._mm.sendAsyncMessage("Contact:Remove", {id: aRecord.id, + requestID: this.getRequestId({ request: request })}); + return request; + } else { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + } + }, + + _setMetaData: function(aNewContact, aRecord) { + aNewContact.id = aRecord.id; + aNewContact.published = aRecord.published; + aNewContact.updated = aRecord.updated; + }, + + _convertContactsArray: function(aContacts) { + let contacts = new Array(); + for (let i in aContacts) { + let newContact = new Contact(); + newContact.init(aContacts[i].properties); + this._setMetaData(newContact, aContacts[i]); + contacts.push(newContact); + } + return contacts; + }, + + getRequestId: function(aRequest) { + let id = "id" + this._getRandomId(); + this._requests[id] = aRequest; + return id; + }, + + getRequest: function(aId) { + if (this._requests[aId]) + return this._requests[aId].request; + }, + + removeRequest: function(aId) { + if (this._requests[aId]) + delete this._requests[aId]; + }, + + _getRandomId: function() { + return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString(); + }, + + receiveMessage: function(aMessage) { + debug("Contactmanager::receiveMessage: " + aMessage.name); + let msg = aMessage.json; + let contacts = msg.contacts; + + switch (aMessage.name) { + case "Contacts:Find:Return:OK": + let req = this.getRequest(msg.requestID); + if (req) { + let result = this._convertContactsArray(contacts); + debug("result: " + JSON.stringify(result)); + this._rs.fireSuccess(req, result); + } else { + debug("no request stored!" + msg.requestID); + } + break; + case "Contact:Save:Return:OK": + case "Contacts:Clear:Return:OK": + case "Contact:Remove:Return:OK": + req = this.getRequest(msg.requestID); + if (req) + this._rs.fireSuccess(req, 0); + break; + case "Contacts:Find:Return:KO": + case "Contact:Save:Return:KO": + case "Contact:Remove:Return:KO": + case "Contacts:Clear:Return:KO": + req = this.getRequest(msg.requestID); + if (req) + this._rs.fireError(req, msg.errorMsg); + break; + default: + debug("Wrong message: " + aMessage.name); + } + this.removeRequest(msg.requestID); + }, + + find: function(aOptions) { + let request; + if (this.hasPrivileges) { + request = this._rs.createRequest(this._window); + this._mm.sendAsyncMessage("Contacts:Find", {findOptions: aOptions, + requestID: this.getRequestId({ request: request })}); + return request; + } else { + debug("find not allowed"); + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + } + }, + + clear: function() { + let request; + if (this.hasPrivileges) { + request = this._rs.createRequest(this._window); + this._mm.sendAsyncMessage("Contacts:Clear", {requestID: this.getRequestId({ request: request })}); + return request; + } else { + debug("clear not allowed"); + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + } + }, + + init: function(aWindow) { + // Set navigator.mozContacts to null. + if (!Services.prefs.getBoolPref("dom.mozContacts.enabled")) + return null; + + this._window = aWindow; + this._messages = ["Contacts:Find:Return:OK", "Contacts:Find:Return:KO", + "Contacts:Clear:Return:OK", "Contacts:Clear:Return:KO", + "Contact:Save:Return:OK", "Contact:Save:Return:KO", + "Contact:Remove:Return:OK", "Contact:Remove:Return:KO"]; + + this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager); + this._messages.forEach((function(msgName) { + this._mm.addMessageListener(msgName, this); + }).bind(this)); + + this._rs = Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService); + this._requests = []; + Services.obs.addObserver(this, "inner-window-destroyed", false); + let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + this._innerWindowID = util.currentInnerWindowID; + + let principal = aWindow.document.nodePrincipal; + let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager); + + let perm = principal == secMan.getSystemPrincipal() ? + Ci.nsIPermissionManager.ALLOW_ACTION : + Services.perms.testExactPermission(principal.URI, "webcontacts-manage"); + + //only pages with perm set can use the contacts + this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION; + debug("has privileges :" + this.hasPrivileges); + }, + + observe: function(aSubject, aTopic, aData) { + let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data; + if (wId == this.innerWindowID) { + Services.obs.removeObserver(this, "inner-window-destroyed"); + this._messages.forEach((function(msgName) { + this._mm.removeMessageListener(msgName, this); + }).bind(this)); + this._mm = null; + this._messages = null; + this._requests = null; + this._window = null; + this._innerWindowID = null; + } + }, + + classID : CONTACTMANAGER_CID, + QueryInterface : XPCOMUtils.generateQI([nsIDOMContactManager, Ci.nsIDOMGlobalPropertyInitializer]), + + classInfo : XPCOMUtils.generateCI({classID: CONTACTMANAGER_CID, + contractID: CONTACTMANAGER_CONTRACTID, + classDescription: "ContactManager", + interfaces: [nsIDOMContactManager], + flags: nsIClassInfo.DOM_OBJECT}) +} + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([Contact, ContactManager, ContactProperties, ContactAddress, ContactFindOptions])
new file mode 100644 --- /dev/null +++ b/dom/contacts/ContactManager.manifest @@ -0,0 +1,16 @@ +component {53ed7c20-ceda-11e0-9572-0800200c9a66} ContactManager.js +contract @mozilla.org/contactProperties;1 {53ed7c20-ceda-11e0-9572-0800200c9a66} + +component {27a568b0-cee1-11e0-9572-0800200c9a66} ContactManager.js +contract @mozilla.org/contactAddress;1 {27a568b0-cee1-11e0-9572-0800200c9a66} + +component {e31daea0-0cb6-11e1-be50-0800200c9a66} ContactManager.js +contract @mozilla.org/contactFindOptions;1 {e31daea0-0cb6-11e1-be50-0800200c9a66} + +component {da0f7040-388b-11e1-b86c-0800200c9a66} ContactManager.js +contract @mozilla.org/contact;1 {da0f7040-388b-11e1-b86c-0800200c9a66} +category JavaScript-global-constructor mozContact @mozilla.org/contact;1 + +component {50a820b0-ced0-11e0-9572-0800200c9a66} ContactManager.js +contract @mozilla.org/contactManager;1 {50a820b0-ced0-11e0-9572-0800200c9a66} +category JavaScript-navigator-property mozContacts @mozilla.org/contactManager;1
new file mode 100644 --- /dev/null +++ b/dom/contacts/Makefile.in @@ -0,0 +1,47 @@ +# 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/. + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = \ + $(srcdir) \ + $(NULL) + +include $(DEPTH)/config/autoconf.mk + +ifeq ($(MOZ_WIDGET_TOOLKIT),gonk) +VPATH += $(srcdir)/fallback +endif + +MODULE = dom +LIBRARY_NAME = jsdomcontacts_s +LIBXUL_LIBRARY = 1 + +EXTRA_COMPONENTS = \ + ContactManager.js \ + ContactManager.manifest \ + $(NULL) + +ifeq ($(MOZ_WIDGET_TOOLKIT),gonk) +EXTRA_JS_MODULES = ContactService.jsm \ + $(NULL) + +EXTRA_JS_MODULES += ContactDB.jsm \ + $(NULL) +endif + +ifdef ENABLE_TESTS +DIRS += tests +endif + +# Add VPATH to LOCAL_INCLUDES so we are going to include the correct backend +# subdirectory (and the ipc one). +LOCAL_INCLUDES += $(VPATH:%=-I%) + +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk +include $(topsrcdir)/config/rules.mk + +DEFINES += -D_IMPL_NS_LAYOUT
new file mode 100644 --- /dev/null +++ b/dom/contacts/fallback/ContactDB.jsm @@ -0,0 +1,410 @@ +/* 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/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ['ContactDB']; + +let DEBUG = 0; +/* static functions */ +if (DEBUG) + debug = function (s) { dump("-*- ContactDB component: " + s + "\n"); } +else + debug = function (s) {} + +const Cu = Components.utils; +const Cc = Components.classes; +const Ci = Components.interfaces; + +Cu.import("resource://gre/modules/Services.jsm"); + +const DB_NAME = "contacts"; +const DB_VERSION = 1; +const STORE_NAME = "contacts"; + +function ContactDB(aGlobal) { + debug("Constructor"); + this._indexedDB = aGlobal.mozIndexedDB; +} + +ContactDB.prototype = { + + // Cache the DB + db: null, + + close: function close() { + debug("close"); + if (this.db) + this.db.close(); + }, + + /** + * Prepare the database. This may include opening the database and upgrading + * it to the latest schema version. + * + * @return (via callback) a database ready for use. + */ + ensureDB: function ensureDB(callback, failureCb) { + if (this.db) { + debug("ensureDB: already have a database, returning early."); + callback(this.db); + return; + } + + let self = this; + debug("try to open database:" + DB_NAME + " " + DB_VERSION); + let request = this._indexedDB.open(DB_NAME, DB_VERSION); + request.onsuccess = function (event) { + debug("Opened database:", DB_NAME, DB_VERSION); + self.db = event.target.result; + self.db.onversionchange = function(event) { + debug("WARNING: DB modified from a different window."); + } + callback(self.db); + }; + request.onupgradeneeded = function (event) { + debug("Database needs upgrade:" + DB_NAME + event.oldVersion + event.newVersion); + debug("Correct new database version:" + event.newVersion == DB_VERSION); + + let db = event.target.result; + switch (event.oldVersion) { + case 0: + debug("New database"); + self.createSchema(db); + break; + + default: + debug("No idea what to do with old database version:" + event.oldVersion); + failureCb(event.target.errorMessage); + break; + } + }; + request.onerror = function (event) { + debug("Failed to open database:", DB_NAME); + failureCb(event.target.errorMessage); + }; + request.onblocked = function (event) { + debug("Opening database request is blocked."); + }; + }, + + /** + * Create the initial database schema. + * + * The schema of records stored is as follows: + * + * {id: "...", // UUID + * published: Date(...), // First published date. + * updated: Date(...), // Last updated date. + * properties: {...} // Object holding the ContactProperties + * } + */ + createSchema: function createSchema(db) { + let objectStore = db.createObjectStore(STORE_NAME, {keyPath: "id"}); + + // Metadata indexes + objectStore.createIndex("published", "published", { unique: false }); + objectStore.createIndex("updated", "updated", { unique: false }); + + // Properties indexes + objectStore.createIndex("nickname", "properties.nickname", { unique: false, multiEntry: true }); + objectStore.createIndex("name", "properties.name", { unique: false, multiEntry: true }); + objectStore.createIndex("familyName", "properties.familyName", { unique: false, multiEntry: true }); + objectStore.createIndex("givenName", "properties.givenName", { unique: false, multiEntry: true }); + objectStore.createIndex("tel", "properties.tel", { unique: false, multiEntry: true }); + objectStore.createIndex("email", "properties.email", { unique: false, multiEntry: true }); + objectStore.createIndex("note", "properties.note", { unique: false, multiEntry: true }); + + debug("Created object stores and indexes"); + }, + + /** + * Start a new transaction. + * + * @param txn_type + * Type of transaction (e.g. IDBTransaction.READ_WRITE) + * @param callback + * Function to call when the transaction is available. It will + * be invoked with the transaction and the 'contacts' object store. + * @param successCb [optional] + * Success callback to call on a successful transaction commit. + * @param failureCb [optional] + * Error callback to call when an error is encountered. + */ + newTxn: function newTxn(txn_type, callback, successCb, failureCb) { + this.ensureDB(function (db) { + debug("Starting new transaction" + txn_type); + let txn = db.transaction(STORE_NAME, txn_type); + debug("Retrieving object store", STORE_NAME); + let store = txn.objectStore(STORE_NAME); + + txn.oncomplete = function (event) { + debug("Transaction complete. Returning to callback."); + successCb(txn.result); + }; + + txn.onabort = function (event) { + debug("Caught error on transaction" + event.target.errorCode); + switch(event.target.errorCode) { + case Ci.nsIIDBDatabaseException.ABORT_ERR: + case Ci.nsIIDBDatabaseException.CONSTRAINT_ERR: + case Ci.nsIIDBDatabaseException.DATA_ERR: + case Ci.nsIIDBDatabaseException.TRANSIENT_ERR: + case Ci.nsIIDBDatabaseException.NOT_ALLOWED_ERR: + case Ci.nsIIDBDatabaseException.NOT_FOUND_ERR: + case Ci.nsIIDBDatabaseException.QUOTA_ERR: + case Ci.nsIIDBDatabaseException.READ_ONLY_ERR: + case Ci.nsIIDBDatabaseException.TIMEOUT_ERR: + case Ci.nsIIDBDatabaseException.TRANSACTION_INACTIVE_ERR: + case Ci.nsIIDBDatabaseException.VERSION_ERR: + case Ci.nsIIDBDatabaseException.UNKNOWN_ERR: + failureCb("UnknownError"); + break; + default: + debug("Unknown errorCode", event.target.errorCode); + failureCb("UnknownError"); + break; + } + }; + callback(txn, store); + }, failureCb); + }, + + // Todo: add searchfields. "Tom" should be a result with T, t, To, to... + makeImport: function makeImport(aContact) { + let contact = {}; + contact.properties = { + name: [], + honorificPrefix: [], + givenName: [], + additionalName: [], + familyName: [], + honorificSuffix: [], + nickname: [], + email: [], + photo: [], + url: [], + category: [], + adr: [], + tel: [], + org: [], + bday: null, + note: [], + impp: [], + anniversary: null, + sex: null, + genderIdentity: null + }; + + for (let field in aContact.properties) { + contact.properties[field] = aContact.properties[field]; + } + + contact.updated = aContact.updated; + contact.published = aContact.published; + contact.id = aContact.id; + + return contact; + }, + + // Needed to remove searchfields + makeExport: function makeExport(aRecord) { + let contact = {}; + contact.properties = aRecord.properties; + + for (let field in aRecord.properties) + contact.properties[field] = aRecord.properties[field]; + + contact.updated = aRecord.updated; + contact.published = aRecord.published; + contact.id = aRecord.id; + return contact; + }, + + updateRecordMetadata: function updateRecordMetadata(record) { + if (!record.id) { + Cu.reportError("Contact without ID"); + } + if (!record.published) { + record.published = new Date(); + } + record.updated = new Date(); + }, + + saveContact: function saveContact(aContact, successCb, errorCb) { + let contact = this.makeImport(aContact); + this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function (txn, store) { + debug("Going to update" + JSON.stringify(contact)); + + // Look up the existing record and compare the update timestamp. + // If no record exists, just add the new entry. + let newRequest = store.get(contact.id); + newRequest.onsuccess = function (event) { + if (!event.target.result) { + debug("new record!") + this.updateRecordMetadata(contact); + store.put(contact); + } else { + debug("old record!") + if (new Date(typeof contact.updated === "undefined" ? 0 : contact.updated) < new Date(event.target.result.updated)) { + debug("rev check fail!"); + txn.abort(); + return; + } else { + debug("rev check OK"); + contact.published = event.target.result.published; + contact.updated = new Date(); + store.put(contact); + } + } + }.bind(this); + }.bind(this), successCb, errorCb); + }, + + removeContact: function removeContact(aId, aSuccessCb, aErrorCb) { + this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function (txn, store) { + debug("Going to delete" + aId); + store.delete(aId); + }, aSuccessCb, aErrorCb); + }, + + clear: function clear(aSuccessCb, aErrorCb) { + this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function (txn, store) { + debug("Going to clear all!"); + store.clear(); + }, aSuccessCb, aErrorCb); + }, + + /** + * @param successCb + * Callback function to invoke with result array. + * @param failureCb [optional] + * Callback function to invoke when there was an error. + * @param options [optional] + * Object specifying search options. Possible attributes: + * - filterBy + * - filterOp + * - filterValue + * - count + * Possibly supported in the future: + * - fields + * - sortBy + * - sortOrder + * - startIndex + */ + find: function find(aSuccessCb, aFailureCb, aOptions) { + debug("ContactDB:find val:" + aOptions.filterValue + " by: " + aOptions.filterBy + " op: " + aOptions.filterOp + "\n"); + + let self = this; + this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (txn, store) { + if (aOptions && aOptions.filterOp == "equals") { + self._findWithIndex(txn, store, aOptions); + } else if (aOptions && aOptions.filterBy) { + self._findWithSearch(txn, store, aOptions); + } else { + self._findAll(txn, store, aOptions); + } + }, aSuccessCb, aFailureCb); + }, + + _findWithIndex: function _findWithIndex(txn, store, options) { + debug("_findWithIndex: " + options.filterValue +" " + options.filterOp + " " + options.filterBy + " "); + let fields = options.filterBy; + for (let key in fields) { + debug("key: " + fields[key]); + if (!store.indexNames.contains(fields[key]) && !fields[key] == "id") { + debug("Key not valid!" + fields[key] + ", " + store.indexNames); + txn.abort(); +