author | David Anderson <danderson@mozilla.com> |
Tue, 21 Feb 2012 15:08:22 -0800 | |
changeset 105861 | 61980734d3a21c29f3783ae946733af29ebcd2bd |
parent 105859 | 82c6ca0616d0561c5e68ceecf3d4d24330db0bff (current diff) |
parent 87269 | 7dcbce54a953090ae8e537f93c6c99ab8eb0dc62 (diff) |
child 105862 | 5a061abdf807b2adfacf74e0febcde810bb5ce2b |
push id | 23447 |
push user | danderson@mozilla.com |
push date | Tue, 11 Sep 2012 17:34:27 +0000 |
treeherder | mozilla-central@fdfaef738a00 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 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/base/Makefile.in +++ b/accessible/src/base/Makefile.in @@ -71,16 +71,17 @@ CPPSRCS = \ nsEventShell.cpp \ nsFormControlAccessible.cpp \ nsRootAccessible.cpp \ nsApplicationAccessible.cpp \ nsCaretAccessible.cpp \ nsTextAccessible.cpp \ nsTextEquivUtils.cpp \ nsTextAttrs.cpp \ + StyleInfo.cpp \ TextUpdater.cpp \ $(NULL) EXPORTS = \ a11yGeneric.h \ nsAccDocManager.h \ nsAccessibilityService.h \ nsAccessible.h \ @@ -102,16 +103,17 @@ FORCE_STATIC_LIB = 1 include $(topsrcdir)/config/rules.mk LOCAL_INCLUDES += \ -I$(srcdir) \ -I$(srcdir)/../xpcom \ -I$(srcdir)/../html \ -I$(srcdir)/../xul \ -I$(srcdir)/../../../layout/generic \ + -I$(srcdir)/../../../layout/style \ -I$(srcdir)/../../../layout/xul/base/src \ -I$(srcdir)/../xforms \ $(NULL) ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2) LOCAL_INCLUDES += \ -I$(srcdir)/../atk \ $(NULL)
new file mode 100644 --- /dev/null +++ b/accessible/src/base/StyleInfo.cpp @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: */ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Alexander Surkov <surkov.alexander@gmail.com> (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of 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 ***** */ + +#include "StyleInfo.h" + +#include "mozilla/dom/Element.h" +#include "nsComputedDOMStyle.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +StyleInfo::StyleInfo(dom::Element* aElement, nsIPresShell* aPresShell) : + mElement(aElement) +{ + mStyleContext = + nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, + nsnull, + aPresShell); +} + +void +StyleInfo::Display(nsAString& aValue) +{ + aValue.Truncate(); + AppendASCIItoUTF16( + nsCSSProps::ValueToKeyword(mStyleContext->GetStyleDisplay()->mDisplay, + nsCSSProps::kDisplayKTable), aValue); +} + +void +StyleInfo::TextAlign(nsAString& aValue) +{ + aValue.Truncate(); + AppendASCIItoUTF16( + nsCSSProps::ValueToKeyword(mStyleContext->GetStyleText()->mTextAlign, + nsCSSProps::kTextAlignKTable), aValue); +} + +void +StyleInfo::TextIndent(nsAString& aValue) +{ + aValue.Truncate(); + + const nsStyleCoord& styleCoord = + mStyleContext->GetStyleText()->mTextIndent; + + nscoord coordVal; + switch (styleCoord.GetUnit()) { + case eStyleUnit_Coord: + coordVal = styleCoord.GetCoordValue(); + break; + + case eStyleUnit_Percent: + { + nsIFrame* frame = mElement->GetPrimaryFrame(); + nsIFrame* containerFrame = frame->GetContainingBlock(); + nscoord percentageBase = containerFrame->GetContentRect().width; + coordVal = NSCoordSaturatingMultiply(percentageBase, + styleCoord.GetPercentValue()); + break; + } + } + + aValue.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(coordVal)); + aValue.AppendLiteral("px"); +} + +void +StyleInfo::Margin(css::Side aSide, nsAString& aValue) +{ + aValue.Truncate(); + + nscoord coordVal = mElement->GetPrimaryFrame()->GetUsedMargin().Side(aSide); + aValue.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(coordVal)); + aValue.AppendLiteral("px"); +}
new file mode 100644 --- /dev/null +++ b/accessible/src/base/StyleInfo.h @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: */ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Alexander Surkov <surkov.alexander@gmail.com> (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of 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 ***** */ + +#ifndef _mozilla_a11y_style_h_ +#define _mozilla_a11y_style_h_ + +#include "mozilla/gfx/Types.h" +#include "nsStyleContext.h" + +namespace mozilla { +namespace a11y { + +class StyleInfo +{ +public: + StyleInfo(dom::Element* aElement, nsIPresShell* aPresShell); + ~StyleInfo() { }; + + 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); } + +private: + StyleInfo() MOZ_DELETE; + StyleInfo(const StyleInfo&) MOZ_DELETE; + StyleInfo& operator = (const StyleInfo&) MOZ_DELETE; + + void Margin(css::Side aSide, nsAString& aValue); + + dom::Element* mElement; + nsRefPtr<nsStyleContext> mStyleContext; +}; + +} // namespace a11y +} // namespace mozilla + +#endif
--- a/accessible/src/base/TextUpdater.cpp +++ b/accessible/src/base/TextUpdater.cpp @@ -89,18 +89,18 @@ TextUpdater::DoUpdate(const nsAString& a // Trim coinciding substrings from the end. PRUint32 skipEnd = 0; while (minLen - skipEnd > aSkipStart && aNewText[newLen - skipEnd - 1] == aOldText[oldLen - skipEnd - 1]) { skipEnd++; } - PRInt32 strLen1 = oldLen - aSkipStart - skipEnd; - PRInt32 strLen2 = newLen - aSkipStart - skipEnd; + PRUint32 strLen1 = oldLen - aSkipStart - skipEnd; + PRUint32 strLen2 = newLen - aSkipStart - skipEnd; const nsAString& str1 = Substring(aOldText, aSkipStart, strLen1); const nsAString& str2 = Substring(aNewText, aSkipStart, strLen2); // Increase offset of the text leaf on skipped characters amount. mTextOffset += aSkipStart; // It could be single insertion or removal or the case of long strings. Do not @@ -182,17 +182,17 @@ TextUpdater::ComputeTextChangeEvents(con PRInt32 colIdx = aStr1.Length(), rowIdx = aStr2.Length(); // Point at which strings last matched. PRInt32 colEnd = colIdx; PRInt32 rowEnd = rowIdx; PRInt32 colLen = colEnd + 1; PRUint32* row = aEntries + rowIdx * colLen; - PRInt32 dist = row[colIdx]; // current Levenshtein distance + PRUint32 dist = row[colIdx]; // current Levenshtein distance while (rowIdx && colIdx) { // stop when we can't move diagonally if (aStr1[colIdx - 1] == aStr2[rowIdx - 1]) { // match if (rowIdx < rowEnd) { // deal with any pending insertion FireInsertEvent(Substring(aStr2, rowIdx, rowEnd - rowIdx), rowIdx, aEvents); } if (colIdx < colEnd) { // deal with any pending deletion FireDeleteEvent(Substring(aStr1, colIdx, colEnd - colIdx),
--- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -39,29 +39,28 @@ #include "nsAccessible.h" #include "nsIXBLAccessible.h" #include "AccGroupInfo.h" #include "AccIterator.h" #include "nsAccUtils.h" -#include "nsDocAccessible.h" -#include "nsEventShell.h" - #include "nsAccEvent.h" #include "nsAccessibleRelation.h" #include "nsAccessibilityService.h" #include "nsAccTreeWalker.h" #include "nsIAccessibleRelation.h" +#include "nsEventShell.h" #include "nsRootAccessible.h" #include "nsTextEquivUtils.h" #include "Relation.h" #include "Role.h" #include "States.h" +#include "StyleInfo.h" #include "nsIDOMCSSValue.h" #include "nsIDOMCSSPrimitiveValue.h" #include "nsIDOMElement.h" #include "nsIDOMDocument.h" #include "nsIDOMDocumentXBL.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLFormElement.h" @@ -1437,59 +1436,50 @@ nsAccessible::GetAttributesInternal(nsIP nsIDocument *parentDoc = doc->GetParentDocument(); if (!parentDoc) break; startContent = parentDoc->FindContentForSubDocument(doc); } - // Expose 'display' attribute. + if (!mContent->IsElement()) + return NS_OK; + + // CSS style based object attributes. nsAutoString value; - nsresult rv = GetComputedStyleValue(EmptyString(), - NS_LITERAL_STRING("display"), - value); - if (NS_SUCCEEDED(rv)) - nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::display, value); + StyleInfo styleInfo(mContent->AsElement(), mDoc->PresShell()); + + // Expose 'display' attribute. + styleInfo.Display(value); + nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::display, value); // Expose 'text-align' attribute. - rv = GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("text-align"), - value); - if (NS_SUCCEEDED(rv)) - nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textAlign, value); + styleInfo.TextAlign(value); + nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textAlign, value); // Expose 'text-indent' attribute. - rv = GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("text-indent"), - value); - if (NS_SUCCEEDED(rv)) - nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textIndent, value); + styleInfo.TextIndent(value); + nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textIndent, value); // Expose 'margin-left' attribute. - rv = GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("margin-left"), - value); - if (NS_SUCCEEDED(rv)) - nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginLeft, value); + styleInfo.MarginLeft(value); + nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginLeft, value); // Expose 'margin-right' attribute. - rv = GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("margin-right"), - value); - if (NS_SUCCEEDED(rv)) - nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginRight, value); + styleInfo.MarginRight(value); + nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginRight, value); // Expose 'margin-top' attribute. - rv = GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("margin-top"), - value); - if (NS_SUCCEEDED(rv)) - nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginTop, value); + styleInfo.MarginTop(value); + nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginTop, value); // Expose 'margin-bottom' attribute. - rv = GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("margin-bottom"), - value); - if (NS_SUCCEEDED(rv)) - nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginBottom, value); + styleInfo.MarginBottom(value); + nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginBottom, value); // Expose draggable object attribute? nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mContent); if (htmlElement) { bool draggable = false; htmlElement->GetDraggable(&draggable); if (draggable) { nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::draggable,
--- a/accessible/src/html/nsHTMLTableAccessible.cpp +++ b/accessible/src/html/nsHTMLTableAccessible.cpp @@ -1489,71 +1489,54 @@ nsHTMLTableAccessible::IsProbablyForLayo if (border.top && border.bottom && border.left && border.right) { RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell"); } /** * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward */ - // Check for styled background color across the row - // Alternating background color is a common way - nsCOMPtr<nsIDOMNodeList> nodeList; - nsCOMPtr<nsIDOMElement> tableElt(do_QueryInterface(mContent)); - tableElt->GetElementsByTagName(NS_LITERAL_STRING("tr"), getter_AddRefs(nodeList)); - NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); - PRUint32 length; - nodeList->GetLength(&length); - nsAutoString color, lastRowColor; - for (PRUint32 rowCount = 0; rowCount < length; rowCount ++) { - nsCOMPtr<nsIDOMNode> rowNode; - nodeList->Item(rowCount, getter_AddRefs(rowNode)); - nsCOMPtr<nsIContent> rowContent(do_QueryInterface(rowNode)); - - nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl = - nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), rowContent); - NS_ENSURE_TRUE(styleDecl, NS_ERROR_FAILURE); - - lastRowColor = color; - styleDecl->GetPropertyValue(NS_LITERAL_STRING("background-color"), color); - if (rowCount > 0 && false == lastRowColor.Equals(color)) { - RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered"); + // Check for styled background color across rows (alternating background + // color is a common feature for data tables). + PRUint32 childCount = GetChildCount(); + nsAutoString rowColor, prevRowColor; + for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) { + nsAccessible* child = GetChildAt(childIdx); + if (child->Role() == roles::ROW) { + nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl = + nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), + child->GetContent()); + if (styleDecl) { + prevRowColor = rowColor; + styleDecl->GetPropertyValue(NS_LITERAL_STRING("background-color"), + rowColor); + if (childIdx > 0 && !prevRowColor.Equals(rowColor)) { + RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered"); + } + } } } // Check for many rows const PRInt32 kMaxLayoutRows = 20; if (rows > kMaxLayoutRows) { // A ton of rows, this is probably for data RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); } - // Check for very wide table - nsAutoString styledWidth; - GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("width"), styledWidth); - if (styledWidth.EqualsLiteral("100%")) { - RETURN_LAYOUT_ANSWER(true, "<=4 columns and 100% width"); - } - if (styledWidth.Find(NS_LITERAL_STRING("px"))) { // Hardcoded in pixels - nsIFrame *tableFrame = GetFrame(); - NS_ENSURE_TRUE(tableFrame , NS_ERROR_FAILURE); - nsSize tableSize = tableFrame->GetSize(); - - nsDocAccessible* docAccessible = Document(); - NS_ENSURE_TRUE(docAccessible, NS_ERROR_FAILURE); - nsIFrame *docFrame = docAccessible->GetFrame(); - NS_ENSURE_TRUE(docFrame , NS_ERROR_FAILURE); - - nsSize docSize = docFrame->GetSize(); - if (docSize.width > 0) { - PRInt32 percentageOfDocWidth = (100 * tableSize.width) / docSize.width; - if (percentageOfDocWidth > 95) { - // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width - // Probably for layout - RETURN_LAYOUT_ANSWER(true, "<=4 columns, width hardcoded in pixels and 95% of document width"); - } + // Check for very wide table. + nsIFrame* documentFrame = Document()->GetFrame(); + nsSize documentSize = documentFrame->GetSize(); + if (documentSize.width > 0) { + nsSize tableSize = GetFrame()->GetSize(); + PRInt32 percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; + if (percentageOfDocWidth > 95) { + // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width + // Probably for layout + RETURN_LAYOUT_ANSWER(true, + "<= 4 columns, table width is 95% of document width"); } } // Two column rules if (rows * columns <= 10) { RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); }
--- a/accessible/tests/mochitest/attributes.js +++ b/accessible/tests/mochitest/attributes.js @@ -23,16 +23,36 @@ function testAttrs(aAccOrElmOrID, aAttrs * present (name/value pairs) */ function testAbsentAttrs(aAccOrElmOrID, aAbsentAttrs) { testAttrsInternal(aAccOrElmOrID, {}, true, aAbsentAttrs); } /** + * Test CSS based object attributes. + */ +function testCSSAttrs(aID) +{ + var node = document.getElementById(aID); + var computedStyle = document.defaultView.getComputedStyle(node, ""); + + var attrs = { + "display": computedStyle.display, + "text-align": computedStyle.textAlign, + "text-indent": computedStyle.textIndent, + "margin-left": computedStyle.marginLeft, + "margin-right": computedStyle.marginRight, + "margin-top": computedStyle.marginTop, + "margin-bottom": computedStyle.marginBottom + }; + testAttrs(aID, attrs, true); +} + +/** * Test group object attributes (posinset, setsize and level) and * nsIAccessible::groupPosition() method. * * @param aAccOrElmOrID [in] the ID, DOM node or accessible * @param aPosInSet [in] the value of 'posinset' attribute * @param aSetSize [in] the value of 'setsize' attribute * @param aLevel [in, optional] the value of 'level' attribute */
--- a/accessible/tests/mochitest/attributes/Makefile.in +++ b/accessible/tests/mochitest/attributes/Makefile.in @@ -43,16 +43,17 @@ VPATH = @srcdir@ relativesrcdir = accessible/attributes include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _TEST_FILES =\ test_obj.html \ test_obj_css.html \ + test_obj_css.xul \ test_obj_group.html \ test_obj_group.xul \ test_obj_group_tree.xul \ test_text.html \ $(NULL) libs:: $(_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/attributes/test_obj_css.html +++ b/accessible/tests/mochitest/attributes/test_obj_css.html @@ -13,46 +13,75 @@ https://bugzilla.mozilla.org/show_bug.cg src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="../common.js"></script> <script type="application/javascript" src="../attributes.js"></script> <script type="application/javascript"> - function testCSSAttrs(aID) - { - var node = document.getElementById(aID); - var computedStyle = document.defaultView.getComputedStyle(node, ""); - - var attrs = { - "display": computedStyle.display, - "text-align": computedStyle.textAlign, - "text-indent": computedStyle.textIndent, - "margin-left": computedStyle.marginLeft, - "margin-right": computedStyle.marginRight, - "margin-top": computedStyle.marginTop, - "margin-bottom": computedStyle.marginBottom - }; - testAttrs(aID, attrs, true); - } - function doTest() { + // CSS display + testCSSAttrs("display_block"); + testCSSAttrs("display_inline"); + testCSSAttrs("display_inline-block"); + testCSSAttrs("display_list-item"); + testCSSAttrs("display_table"); + testCSSAttrs("display_inline-table"); + testCSSAttrs("display_table-row-group"); + testCSSAttrs("display_table-column"); + testCSSAttrs("display_table-column-group"); + testCSSAttrs("display_table-header-group"); + testCSSAttrs("display_table-footer-group"); + testCSSAttrs("display_table-row"); + testCSSAttrs("display_table-cell"); + testCSSAttrs("display_table-caption"); + + // CSS text-align + testCSSAttrs("text-align_left"); + testCSSAttrs("text-align_right"); + testCSSAttrs("text-align_center"); + testCSSAttrs("text-align_justify"); + testCSSAttrs("text-align_inherit"); + + // CSS text-indent + testCSSAttrs("text-indent_em"); + testCSSAttrs("text-indent_ex"); + testCSSAttrs("text-indent_in"); + testCSSAttrs("text-indent_cm"); + testCSSAttrs("text-indent_mm"); + testCSSAttrs("text-indent_pt"); + testCSSAttrs("text-indent_pc"); + testCSSAttrs("text-indent_px"); + testCSSAttrs("text-indent_percent"); + testCSSAttrs("text-indent_inherit"); + + // CSS margin + testCSSAttrs("margin_em"); + testCSSAttrs("margin_ex"); + testCSSAttrs("margin_in"); + testCSSAttrs("margin_cm"); + testCSSAttrs("margin_mm"); + testCSSAttrs("margin_pt"); + testCSSAttrs("margin_pc"); + testCSSAttrs("margin_px"); + testCSSAttrs("margin_percent"); + testCSSAttrs("margin_auto"); + testCSSAttrs("margin_inherit"); + + testCSSAttrs("margin-left"); + testCSSAttrs("margin-right"); + testCSSAttrs("margin-top"); + testCSSAttrs("margin-bottom"); + + // Elements testCSSAttrs("span"); testCSSAttrs("div"); - testCSSAttrs("p"); - testCSSAttrs("p2"); - - testCSSAttrs("pml"); - testCSSAttrs("pmr"); - testCSSAttrs("pmt"); - testCSSAttrs("pmb"); - testCSSAttrs("input"); testCSSAttrs("table"); testCSSAttrs("tr"); testCSSAttrs("td"); SimpleTest.finish(); } @@ -72,33 +101,93 @@ https://bugzilla.mozilla.org/show_bug.cg title="text-indent and text-align should really be object attribute"> Mozilla Bug 460932 </a> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=689540" title="Expose IA2 margin- object attributes"> Mozilla Bug 689540 </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=714579" + title="Don't use GetComputedStyle for object attribute calculation"> + Mozilla Bug 714579 + </a> <p id="display"></p> <div id="content" style="display: none"></div> <pre id="test"> </pre> + <div id="display_block" role="img" + style="display: block;">display: block</div> + <div id="display_inline" role="img" + style="display: inline;">display: inline</div> + <div id="display_inline-block" role="img" + style="display: inline-block;">display: inline-block</div> + <div id="display_list-item" role="img" + style="display: list-item;">display: list-item</div> + <div id="display_table" role="img" + style="display: table;">display: table</div> + <div id="display_inline-table" role="img" + style="display: inline-table;">display: inline-table</div> + <div id="display_table-row-group" role="img" + style="display: table-row-group;">display: table-row-group</div> + <div id="display_table-column" role="img" + style="display: table-column;">display: table-column</div> + <div id="display_table-column-group" role="img" + style="display: table-column-group;">display: table-column-group</div> + <div id="display_table-header-group" role="img" + style="display: table-header-group;">display: table-header-group</div> + <div id="display_table-footer-group" role="img" + style="display: table-footer-group;">display: table-footer-group</div> + <div id="display_table-row" role="img" + style="display: table-row;">display: table-row</div> + <div id="display_table-cell" role="img" + style="display: table-cell;">display: table-cell</div> + <div id="display_table-caption" role="img" + style="display: table-caption;">display: table-caption</div> + + <p id="text-align_left" style="text-align: left;">text-align: left</p> + <p id="text-align_right" style="text-align: right;">text-align: right</p> + <p id="text-align_center" style="text-align: center;">text-align: center</p> + <p id="text-align_justify" style="text-align: justify;">text-align: justify</p> + <p id="text-align_inherit" style="text-align: inherit;">text-align: inherit</p> + + <p id="text-indent_em" style="text-indent: 0.5em;">text-indent: 0.5em</p> + <p id="text-indent_ex" style="text-indent: 1ex;">text-indent: 1ex</p> + <p id="text-indent_in" style="text-indent: 0.5in;">text-indent: 0.5in</p> + <p id="text-indent_cm" style="text-indent: 2cm;">text-indent: 2cm</p> + <p id="text-indent_mm" style="text-indent: 10mm;">text-indent: 10mm</p> + <p id="text-indent_pt" style="text-indent: 30pt;">text-indent: 30pt</p> + <p id="text-indent_pc" style="text-indent: 2pc;">text-indent: 2pc</p> + <p id="text-indent_px" style="text-indent: 5px;">text-indent: 5px</p> + <p id="text-indent_percent" style="text-indent: 10%;">text-indent: 10%</p> + <p id="text-indent_inherit" style="text-indent: inherit;">text-indent: inherit</p> + + <p id="margin_em" style="margin: 0.5em;">margin: 0.5em</p> + <p id="margin_ex" style="margin: 1ex;">margin: 1ex</p> + <p id="margin_in" style="margin: 0.5in;">margin: 0.5in</p> + <p id="margin_cm" style="margin: 2cm;">margin: 2cm</p> + <p id="margin_mm" style="margin: 10mm;">margin: 10mm</p> + <p id="margin_pt" style="margin: 30pt;">margin: 30pt</p> + <p id="margin_pc" style="margin: 2pc;">margin: 2pc</p> + <p id="margin_px" style="margin: 5px;">margin: 5px</p> + <p id="margin_percent" style="margin: 10%;">margin: 10%</p> + <p id="margin_auto" style="margin: auto;">margin: auto</p> + <p id="margin_inherit" style="margin: inherit;">margin: inherit</p> + + <p id="margin-left" style="margin-left: 11px;">margin-left: 11px</p> + <p id="margin-right" style="margin-right: 21px;">margin-right</p> + <p id="margin-top" style="margin-top: 31px;">margin-top: 31px</p> + <p id="margin-bottom" style="margin-bottom: 41px;">margin-bottom: 41px</p> + <span id="span" role="group">It's span</span> <div id="div">It's div</div> - <p id="p">It's paragraph"</p> - <p id="p2" style="text-indent: 5px">It's another paragraph</p> - - <p id="pml" style="margin-left : 11px;">It's a paragraph with left margin</p> - <p id="pmr" style="margin-right : 21px;">It's a paragraph with right margin</p> - <p id="pmt" style="margin-top : 31px;">It's a paragraph with top margin</p> - <p id="pmb" style="margin-bottom : 41px;">It's a paragraph with bottom margin</p> - <input id="input"/> - <table id="table"> + <table id="table" style="margin: 2px; text-align: center; text-indent: 10%;"> <tr id="tr" role="group"> <td id="td">td</td> </tr> </table> </body> </html>
new file mode 100644 --- /dev/null +++ b/accessible/tests/mochitest/attributes/test_obj_css.xul @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessibility CSS-based Object Attributes Test."> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../events.js" /> + <script type="application/javascript" + src="../attributes.js" /> + + <script type="application/javascript"> + <![CDATA[ + function doTest() + { + // CSS display + testCSSAttrs("display_mozbox"); + testCSSAttrs("display_mozinlinebox"); + testCSSAttrs("display_mozgrid"); + testCSSAttrs("display_mozinlinegrid"); + testCSSAttrs("display_mozgridgroup"); + testCSSAttrs("display_mozgridline"); + testCSSAttrs("display_mozstack"); + testCSSAttrs("display_mozinlinestack"); + testCSSAttrs("display_mozdeck"); + testCSSAttrs("display_mozpopup"); + testCSSAttrs("display_mozgroupbox"); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=714579" + title="Don't use GetComputedStyle for object attribute calculation"> + Mozilla Bug 714579 + </a><br/> + + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox id="display_mozbox" style="display: -moz-box;" role="img"/> + <vbox id="display_mozinlinebox" style="display: -moz-inline-box;" role="img"/> + <vbox id="display_mozgrid" style="display: -moz-grid;" role="img"/> + <vbox id="display_mozinlinegrid" style="display: -moz-inline-grid;" role="img"/> + <vbox id="display_mozgridgroup" style="display: -moz-grid-group;" role="img"/> + <vbox id="display_mozgridline" style="display: -moz-grid-line;" role="img"/> + <vbox id="display_mozstack" style="display: -moz-stack;" role="img"/> + <vbox id="display_mozinlinestack" style="display: -moz-inline-stack;" role="img"/> + <vbox id="display_mozdeck" style="display: -moz-deck;" role="img"/> + <vbox id="display_mozpopup" style="display: -moz-popup;" role="img"/> + <vbox id="display_mozgroupbox" style="display: -moz-groupbox;" role="img"/> + + </hbox> +</window> +
--- a/accessible/tests/mochitest/common.js +++ b/accessible/tests/mochitest/common.js @@ -47,20 +47,27 @@ const nsIDOMNode = Components.interfaces const nsIDOMHTMLElement = Components.interfaces.nsIDOMHTMLElement; const nsIDOMWindow = Components.interfaces.nsIDOMWindow; const nsIDOMXULElement = Components.interfaces.nsIDOMXULElement; const nsIPropertyElement = Components.interfaces.nsIPropertyElement; //////////////////////////////////////////////////////////////////////////////// // OS detect -const MAC = (navigator.platform.indexOf("Mac") != -1)? true : false; -const LINUX = (navigator.platform.indexOf("Linux") != -1)? true : false; -const SOLARIS = (navigator.platform.indexOf("SunOS") != -1)? true : false; -const WIN = (navigator.platform.indexOf("Win") != -1)? true : false; + +const MAC = (navigator.platform.indexOf("Mac") != -1); +const LINUX = (navigator.platform.indexOf("Linux") != -1); +const SOLARIS = (navigator.platform.indexOf("SunOS") != -1); +const WIN = (navigator.platform.indexOf("Win") != -1); + +//////////////////////////////////////////////////////////////////////////////// +// Application detect +// Firefox is assumed by default. + +const SEAMONKEY = navigator.userAgent.match(/ SeaMonkey\//); //////////////////////////////////////////////////////////////////////////////// // Accessible general const STATE_BUSY = nsIAccessibleStates.STATE_BUSY; const SCROLL_TYPE_ANYWHERE = nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE;
--- a/accessible/tests/mochitest/events/test_focus_autocomplete.xul +++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xul @@ -377,16 +377,22 @@ // Tests //gA11yEventDumpID = "eventdump"; // debug stuff //gA11yEventDumpToConsole = true; // debug stuff var gInitQueue = null; function initTests() { + if (SEAMONKEY) { + todo(false, "Skipping this test on SeaMonkey ftb. (Bug 718237)"); + SimpleTest.finish(); + return; + } + // register 'test-a11y-search' autocomplete search initAutoComplete([ "hello", "hi" ], [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]); gInitQueue = new eventQueue(); gInitQueue.push(new loadFormAutoComplete("iframe")); gInitQueue.push(new initFormAutoCompleteBy("iframe", "hello")); gInitQueue.push(new initFormAutoCompleteBy("iframe", "hi"));
--- a/accessible/tests/mochitest/tree/test_tabbrowser.xul +++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul @@ -47,17 +47,17 @@ // xul:tabs role: ROLE_PAGETABLIST, children: [ // Children depend on application (UI): see below. ] }; // SeaMonkey and Firefox tabbrowser UIs differ. - if ("restoreTab" in tabBrowser) { + if (SEAMONKEY) { SimpleTest.ok(true, "Testing SeaMonkey tabbrowser UI."); tabsAccTree.children.splice(0, 0, { // xul:toolbarbutton ("Open a new tab") role: ROLE_PUSHBUTTON, children: [] },
--- a/accessible/tests/mochitest/treeupdate/Makefile.in +++ b/accessible/tests/mochitest/treeupdate/Makefile.in @@ -43,16 +43,17 @@ VPATH = @srcdir@ relativesrcdir = accessible/treeupdate include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _TEST_FILES =\ test_ariadialog.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 \ test_menubutton.xul \ test_recreation.html \
new file mode 100644 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_cssoverflow.html @@ -0,0 +1,141 @@ +<html> + +<head> + <title>Testing HTML scrollable frames (css overflow style)</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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + //////////////////////////////////////////////////////////////////////////// + + /** + * Change scroll range to not empty size and inserts a child into container + * to trigger tree update of the container. Prior to bug 677154 not empty + * size resulted to accessible creation for scroll area, container tree + * update picked up that accessible unattaching scroll area accessible + * subtree. + */ + function changeScrollRange(aContainerID, aScrollAreaID) + { + this.containerNode = getNode(aContainerID); + this.container = getAccessible(this.containerNode); + this.scrollAreaNode = getNode(aScrollAreaID); + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function changeScrollRange_invoke() + { + this.scrollAreaNode.style.width = "20px"; + this.containerNode.appendChild(document.createElement("input")); + } + + this.finalCheck = function changeScrollRange_finalCheck() + { + var accTree = + { SECTION: [ // container + { SECTION: [ // scroll area + { ENTRY: [] } // child content + ] }, + { ENTRY: [] } // inserted input + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function changeScrollRange_getID() + { + return "change scroll range for " + prettyName(aScrollAreaID); + } + } + + /** + * Change scrollbar styles from hidden to auto. That makes us to create an + * accessible for scroll area. + */ + function changeScrollbarStyles(aContainerID, aScrollAreaID) + { + this.container = getAccessible(aContainerID); + this.scrollAreaNode = getNode(aScrollAreaID); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getAccessible, this.scrollAreaNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function changeScrollbarStyles_invoke() + { + var accTree = + { SECTION: [] }; + testAccessibleTree(this.container, accTree); + + this.scrollAreaNode.style.overflow = "auto"; + } + + this.finalCheck = function changeScrollbarStyles_finalCheck() + { + var accTree = + { SECTION: [ // container + { SECTION: [] } // scroll area + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function changeScrollbarStyles_getID() + { + return "change scrollbar styles " + prettyName(aScrollAreaID); + } + } + + //////////////////////////////////////////////////////////////////////////// + // Do tests + //////////////////////////////////////////////////////////////////////////// + + var gQueue = null; + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + function doTests() + { + gQueue = new eventQueue(); + + gQueue.push(new changeScrollRange("container", "scrollarea")); + gQueue.push(new changeScrollbarStyles("container2", "scrollarea2")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=677154" + title="Detached document accessibility tree"> + Mozilla Bug 677154</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + <div id="eventdump"></div> + + <div id="container"><div id="scrollarea" style="overflow:auto;"><input></div></div> + <div id="container2"><div id="scrollarea2" style="overflow:hidden;"></div></div> +</body> +</html>
--- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -41,17 +41,17 @@ pref("toolkit.defaultChromeURI", "chrome pref("browser.chromeURL", "chrome://browser/content/"); #ifdef MOZ_OFFICIAL_BRANDING pref("browser.homescreenURL", "file:///system/home/homescreen.html"); #else pref("browser.homescreenURL", "file:///data/local/homescreen.html,file:///system/home/homescreen.html"); #endif // URL for the dialer application. -pref("dom.telephony.app.phone.url", "http://localhost:6666/apps/dialer/dialer.html"); +pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html"); // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density. pref("browser.viewport.scaleRatio", -1); /* disable text selection */ pref("browser.ignoreNativeFrameTextSelection", true); /* cache prefs */ @@ -399,21 +399,21 @@ pref("browser.link.open_newwindow", 3); // 0: no restrictions - divert everything // 1: don't divert window.open at all // 2: don't divert window.open with features pref("browser.link.open_newwindow.restriction", 0); // Enable browser frame pref("dom.mozBrowserFramesEnabled", true); -pref("dom.mozBrowserFramesWhitelist", "http://localhost:6666"); +pref("dom.mozBrowserFramesWhitelist", "http://localhost:7777"); // Temporary permission hack for WebSMS pref("dom.sms.enabled", true); -pref("dom.sms.whitelist", "file://,http://localhost:6666"); +pref("dom.sms.whitelist", "file://,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); @@ -421,14 +421,18 @@ pref("media.realtime_decoder.enabled", t // of talos regression. This is a needed change for higher-framerate // CSS animations, and incidentally works around an apparent bug in // our handling of requestAnimationFrame() listeners, which are // supposed to enable this REPEATING_PRECISE_CAN_SKIP behavior. The // secondary bug isn't really worth investigating since it's obseleted // by bug 710563. pref("layout.frame_rate.precise", true); +// Temporary remote js console hack +pref("b2g.remote-js.enabled", true); +pref("b2g.remote-js.port", 9999); + // Screen timeout in minutes pref("power.screen.timeout", 60); pref("full-screen-api.enabled", true); pref("media.volume.steps", 10);
--- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -3,16 +3,17 @@ /* 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/. */ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; 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'); @@ -20,34 +21,26 @@ XPCOMUtils.defineLazyGetter(Services, 'e return Cc['@mozilla.org/process/environment;1'] .getService(Ci.nsIEnvironment); }); XPCOMUtils.defineLazyGetter(Services, 'ss', function() { return Cc['@mozilla.org/content/style-sheet-service;1'] .getService(Ci.nsIStyleSheetService); }); + XPCOMUtils.defineLazyGetter(Services, 'idle', function() { return Cc['@mozilla.org/widget/idleservice;1'] .getService(Ci.nsIIdleService); }); -// In order to use http:// scheme instead of file:// scheme -// (that is much more restricted) the following code kick-off -// a local http server listening on http://127.0.0.1:7777 and -// http://localhost:7777. -function startupHttpd(baseDir, port) { - const httpdURL = 'chrome://browser/content/httpd.js'; - let httpd = {}; - Services.scriptloader.loadSubScript(httpdURL, httpd); - let server = new httpd.nsHttpServer(); - server.registerDirectory('/', new LocalFile(baseDir)); - server.registerContentType('appcache', 'text/cache-manifest'); - server.start(port); -} +XPCOMUtils.defineLazyServiceGetter(Services, 'fm', function(){ + return Cc['@mozilla.org/focus-managr;1'] + .getService(Ci.nsFocusManager); +}); // 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' @@ -57,17 +50,16 @@ function addPermissions(urls) { let allow = Ci.nsIPermissionManager.ALLOW_ACTION; permissions.forEach(function(permission) { Services.perms.add(uri, permission, allow); }); }); } - var shell = { // FIXME/bug 678695: this should be a system setting preferredScreenBrightness: 1.0, isDebug: false, get contentBrowser() { delete this.contentBrowser; @@ -100,47 +92,38 @@ var shell = { let msg = 'Fatal error during startup: [No homescreen found]'; return alert(msg); } window.controllers.appendController(this); window.addEventListener('keypress', this); window.addEventListener('MozApplicationManifest', this); window.addEventListener("AppCommand", this); + window.addEventListener('mozfullscreenchange', this); this.contentBrowser.addEventListener('load', this, true); try { Services.io.offline = false; let fileScheme = 'file://'; if (homeURL.substring(0, fileScheme.length) == fileScheme) { - homeURL = homeURL.replace(fileScheme, ''); - - let baseDir = homeURL.split('/'); - baseDir.pop(); - baseDir = baseDir.join('/'); - - const SERVER_PORT = 6666; - startupHttpd(baseDir, SERVER_PORT); - - let baseHost = 'http://localhost'; - homeURL = homeURL.replace(baseDir, baseHost + ':' + SERVER_PORT); + homeURL = 'http://localhost:7777' + homeURL.replace(fileScheme, ''); } addPermissions([homeURL]); } catch (e) { let msg = 'Fatal error during startup: [' + e + '[' + homeURL + ']'; return alert(msg); } - // Load webapi+apps.js as a frame script + // Load webapi.js as a frame script let frameScriptUrl = 'chrome://browser/content/webapi.js'; try { messageManager.loadFrameScript(frameScriptUrl, true); } catch (e) { - dump('Error when loading ' + frameScriptUrl + ' as a frame script: ' + e + '\n'); + dump('Error loading ' + frameScriptUrl + ' as a frame script: ' + e + '\n'); } let browser = this.contentBrowser; browser.homePage = homeURL; browser.goHome(); }, stop: function shell_stop() { @@ -238,16 +221,24 @@ var shell = { case 'VolumeUp': this.changeVolume(1); break; case 'VolumeDown': this.changeVolume(-1); break; } break; + + case 'mozfullscreenchange': + // When the screen goes fullscreen make sure to set the focus to the + // main window so noboby can prevent the ESC key to get out fullscreen + // mode + if (document.mozFullScreen) + Services.fm.focusedWindow = window; + break; case 'load': this.contentBrowser.removeEventListener('load', this, true); this.turnScreenOn(); let chromeWindow = window.QueryInterface(Ci.nsIDOMChromeWindow); chromeWindow.browserDOMWindow = new nsBrowserAccess(); this.sendEvent(window, 'ContentStart'); @@ -358,8 +349,55 @@ nsBrowserAccess.prototype = { // to logcat. Services.obs.addObserver(function onConsoleAPILogEvent(subject, topic, data) { let message = subject.wrappedJSObject; let prefix = "Content JS " + message.level.toUpperCase() + " at " + message.filename + ":" + message.lineNumber + " in " + (message.functionName || "anonymous") + ": "; Services.console.logStringMessage(prefix + Array.join(message.arguments, " ")); }, "console-api-log-event", false); + +(function Repl() { + if (!Services.prefs.getBoolPref('b2g.remote-js.enabled')) { + return; + } + const prompt = 'JS> '; + let output; + let reader = { + onInputStreamReady : function repl_readInput(input) { + let sin = Cc['@mozilla.org/scriptableinputstream;1'] + .createInstance(Ci.nsIScriptableInputStream); + sin.init(input); + try { + let val = eval(sin.read(sin.available())); + let ret = (typeof val === 'undefined') ? 'undefined\n' : val + '\n'; + output.write(ret, ret.length); + // TODO: check if socket has been closed + } catch (e) { + if (e.result === Cr.NS_BASE_STREAM_CLOSED || + (typeof e === 'object' && e.result === Cr.NS_BASE_STREAM_CLOSED)) { + return; + } + let message = (typeof e === 'object') ? e.message + '\n' : e + '\n'; + output.write(message, message.length); + } + output.write(prompt, prompt.length); + input.asyncWait(reader, 0, 0, Services.tm.mainThread); + } + } + let listener = { + onSocketAccepted: function repl_acceptConnection(serverSocket, clientSocket) { + dump('Accepted connection on ' + clientSocket.host + '\n'); + let input = clientSocket.openInputStream(Ci.nsITransport.OPEN_BLOCKING, 0, 0) + .QueryInterface(Ci.nsIAsyncInputStream); + output = clientSocket.openOutputStream(Ci.nsITransport.OPEN_BLOCKING, 0, 0); + output.write(prompt, prompt.length); + input.asyncWait(reader, 0, 0, Services.tm.mainThread); + } + } + let serverPort = Services.prefs.getIntPref('b2g.remote-js.port'); + let serverSocket = Cc['@mozilla.org/network/server-socket;1'] + .createInstance(Ci.nsIServerSocket); + serverSocket.init(serverPort, true, -1); + dump('Opened socket on ' + serverSocket.port + '\n'); + serverSocket.asyncListen(listener); +})(); +
--- a/b2g/chrome/content/webapi.js +++ b/b2g/chrome/content/webapi.js @@ -172,89 +172,82 @@ const ContentPanning = { case 'click': evt.stopPropagation(); evt.preventDefault(); evt.target.removeEventListener('click', this, true); break; } }, - position: { - origin: new Point(0, 0), - current: new Point(0 , 0) - }, + position: new Point(0 , 0), onTouchStart: function cp_onTouchStart(evt) { this.dragging = true; - KineticPanning.stop(); + + // If there is a pan animation running (from a previous pan gesture) and + // the user touch back the screen, stop this animation immediatly and + // prevent the possible click action. + if (KineticPanning.active) { + KineticPanning.stop(); + this.preventNextClick = true; + } this.scrollCallback = this.getPannable(evt.originalTarget); - this.position.origin.set(evt.screenX, evt.screenY); - this.position.current.set(evt.screenX, evt.screenY); - KineticPanning.record(new Point(0, 0)); + this.position.set(evt.screenX, evt.screenY); + KineticPanning.record(new Point(0, 0), evt.timeStamp); }, onTouchEnd: function cp_onTouchEnd(evt) { if (!this.dragging) return; this.dragging = false; - if (this.isPan()) { - if (evt.detail) // The event will generate a click - evt.target.addEventListener('click', this, true); + this.onTouchMove(evt); + let pan = KineticPanning.isPan(); + let click = evt.detail; + if (click && (pan || this.preventNextClick)) + evt.target.addEventListener('click', this, true); + + this.preventNextClick = false; + + if (pan) KineticPanning.start(this); - } }, onTouchMove: function cp_onTouchMove(evt) { if (!this.dragging || !this.scrollCallback) return; - let current = this.position.current; + let current = this.position; let delta = new Point(evt.screenX - current.x, evt.screenY - current.y); current.set(evt.screenX, evt.screenY); - if (this.isPan()) { - KineticPanning.record(delta); - this.scrollCallback(delta.scale(-1)); - } + KineticPanning.record(delta, evt.timeStamp); + this.scrollCallback(delta.scale(-1)); }, onKineticBegin: function cp_onKineticBegin(evt) { }, onKineticPan: function cp_onKineticPan(delta) { return !this.scrollCallback(delta); }, onKineticEnd: function cp_onKineticEnd() { if (!this.dragging) this.scrollCallback = null; }, - isPan: function cp_isPan() { - let dpi = content.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .displayDPI; - - let threshold = Services.prefs.getIntPref('ui.dragThresholdX') / 240 * dpi; - - let deltaX = this.position.origin.x - this.position.current.x; - let deltaY = this.position.origin.y - this.position.current.y; - return (Math.abs(deltaX) > threshold || Math.abs(deltaY) > threshold); - }, - getPannable: function cp_getPannable(node) { if (!(node instanceof Ci.nsIDOMHTMLElement) || node.tagName == 'HTML') return null; let content = node.ownerDocument.defaultView; - while (!(node instanceof Ci.nsIDOMHTMLBodyElement)) { let style = content.getComputedStyle(node, null); let overflow = [style.getPropertyValue('overflow'), style.getPropertyValue('overflow-x'), style.getPropertyValue('overflow-y')]; let rect = node.getBoundingClientRect(); @@ -294,35 +287,43 @@ const ContentPanning = { ContentPanning.init(); // Min/max velocity of kinetic panning. This is in pixels/millisecond. const kMinVelocity = 0.4; const kMaxVelocity = 6; // Constants that affect the "friction" of the scroll pane. -const kExponentialC = 1400; +const kExponentialC = 1000; const kPolynomialC = 100 / 1000000; // How often do we change the position of the scroll pane? // Too often and panning may jerk near the end. // Too little and panning will be choppy. In milliseconds. const kUpdateInterval = 16; +// The numbers of momentums to use for calculating the velocity of the pan. +// Those are taken from the end of the action +const kSamples = 5; + const KineticPanning = { _position: new Point(0, 0), _velocity: new Point(0, 0), _acceleration: new Point(0, 0), + get active() { + return this.target !== null; + }, + _target: null, start: function kp_start(target) { this.target = target; // Calculate the initial velocity of the movement based on user input - let momentums = this.momentums; + let momentums = this.momentums.slice(-kSamples); let distance = new Point(0, 0); momentums.forEach(function(momentum) { distance.add(momentum.dx, momentum.dy); }); let elapsed = momentums[momentums.length - 1].time - momentums[0].time; @@ -333,16 +334,17 @@ const KineticPanning = { } let velocityX = clampFromZero(distance.x / elapsed, 0, kMaxVelocity); let velocityY = clampFromZero(distance.y / elapsed, 0, kMaxVelocity); let velocity = this._velocity; velocity.set(Math.abs(velocityX) < kMinVelocity ? 0 : velocityX, Math.abs(velocityY) < kMinVelocity ? 0 : velocityY); + this.momentums = []; // Set acceleration vector to opposite signs of velocity function sign(x) { return x ? (x > 0 ? 1 : -1) : 0; } this._acceleration.set(velocity.clone().map(sign).scale(-kPolynomialC)); @@ -353,30 +355,42 @@ const KineticPanning = { this.target.onKineticBegin(); }, stop: function kp_stop() { if (!this.target) return; - this.momentums.splice(0); + this.momentums = []; this.target.onKineticEnd(); this.target = null; }, momentums: [], - record: function kp_record(delta) { - // If the panning direction has changed, stop the current activity. - if (this.target && ((delta.x * this._velocity.x < 0) || - (delta.y * this._velocity.y < 0))) - this.stop(); + record: function kp_record(delta, timestamp) { + this.momentums.push({ 'time': timestamp, 'dx' : delta.x, 'dy' : delta.y }); + }, + + isPan: function cp_isPan() { + let dpi = content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .displayDPI; - this.momentums.push({ 'time': Date.now(), 'dx' : delta.x, 'dy' : delta.y }); + let threshold = Services.prefs.getIntPref('ui.dragThresholdX') / 240 * dpi; + + let deltaX = 0; + let deltaY = 0; + let start = this.momentums[0].time; + return this.momentums.slice(1).some(function(momentum) { + deltaX += momentum.dx; + deltaY += momentum.dy; + return (Math.abs(deltaX) > threshold) || (Math.abs(deltaY) > threshold); + }); }, _startAnimation: function kp_startAnimation() { let c = kExponentialC; function getNextPosition(position, v, a, t) { // Important traits for this function: // p(t=0) is 0 // p'(t=0) is v0
--- a/browser/app/blocklist.xml +++ b/browser/app/blocklist.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> -<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1328822681000"> +<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1329176667000"> <emItems> <emItem blockID="i58" id="webmaster@buzzzzvideos.info"> <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> <emItem blockID="i41" id="{99079a25-328f-4bd4-be04-00955acaa0a7}"> <versionRange minVersion="0.1" maxVersion="4.3.1.00" severity="1"> </versionRange> @@ -22,18 +22,22 @@ </targetApplication> </versionRange> <versionRange minVersion="3.3.1" maxVersion="*"> <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> <versionRange minVersion="5.0a1" maxVersion="*" /> </targetApplication> </versionRange> </emItem> - <emItem blockID="i19" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}"> - <versionRange minVersion="1.1b1" maxVersion="1.1b1"> + <emItem blockID="i65" id="activity@facebook.com"> + <versionRange minVersion="0" maxVersion="*"> + </versionRange> + </emItem> + <emItem blockID="i66" id="youtubeer@youtuber.com"> + <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> <emItem blockID="i54" id="applebeegifts@mozilla.doslash.org"> <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> <emItem blockID="i16" id="{27182e60-b5f3-411c-b545-b44205977502}"> <versionRange minVersion="1.0" maxVersion="1.0"> @@ -117,18 +121,21 @@ <versionRange minVersion="9.0a1" maxVersion="9.0" /> </targetApplication> </versionRange> </emItem> <emItem blockID="i60" id="youtb3@youtb3.com"> <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> - <emItem blockID="i56" id="flash@adobe.com"> - <versionRange minVersion="0" maxVersion="*"> + <emItem blockID="i23" id="firefox@bandoo.com"> + <versionRange minVersion="5.0" maxVersion="5.0" severity="1"> + <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> + <versionRange minVersion="3.7a1pre" maxVersion="*" /> + </targetApplication> </versionRange> </emItem> <emItem blockID="i55" id="youtube@youtube7.com"> <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> <emItem blockID="i11" id="yslow@yahoo-inc.com"> <versionRange minVersion="2.0.5" maxVersion="2.0.5"> @@ -173,30 +180,31 @@ <emItem blockID="i62" id="jid0-EcdqvFOgWLKHNJPuqAnawlykCGZ@jetpack"> <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> <emItem blockID="i17" id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}"> <versionRange minVersion="2.2" maxVersion="2.2"> </versionRange> </emItem> - <emItem blockID="i23" id="firefox@bandoo.com"> - <versionRange minVersion="5.0" maxVersion="5.0" severity="1"> - <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> - <versionRange minVersion="3.7a1pre" maxVersion="*" /> - </targetApplication> + <emItem blockID="i56" id="flash@adobe.com"> + <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> <emItem blockID="i45" id="{22119944-ED35-4ab1-910B-E619EA06A115}"> <versionRange minVersion="0.1" maxVersion="7.6.1"> <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> <versionRange minVersion="8.0a1" maxVersion="*" /> </targetApplication> </versionRange> </emItem> + <emItem blockID="i19" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}"> + <versionRange minVersion="1.1b1" maxVersion="1.1b1"> + </versionRange> + </emItem> <emItem blockID="i3" id="langpack-vi-VN@firefox.mozilla.org"> <versionRange minVersion="2.0" maxVersion="2.0"> </versionRange> </emItem> <emItem blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}"> </emItem> <emItem blockID="i52" id="ff-ext@youtube"> <versionRange minVersion="0" maxVersion="*">
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -277,16 +277,17 @@ pref("browser.urlbar.clickSelectsAll", f pref("browser.urlbar.clickSelectsAll", true); #endif #ifdef UNIX_BUT_NOT_MAC pref("browser.urlbar.doubleClickSelectsAll", true); #else pref("browser.urlbar.doubleClickSelectsAll", false); #endif pref("browser.urlbar.autoFill", false); +pref("browser.urlbar.autoFill.typed", true); // 0: Match anywhere (e.g., middle of words) // 1: Match on word boundaries and then try matching anywhere // 2: Match only on word boundaries (e.g., after / or .) // 3: Match at the beginning of the url or title pref("browser.urlbar.matchBehavior", 1); pref("browser.urlbar.filter.javascript", true); // the maximum number of results to show in autocomplete when doing richResults
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -9048,38 +9048,54 @@ var Scratchpad = { XPCOMUtils.defineLazyGetter(Scratchpad, "ScratchpadManager", function() { let tmp = {}; Cu.import("resource:///modules/devtools/scratchpad-manager.jsm", tmp); return tmp.ScratchpadManager; }); var StyleEditor = { prefEnabledName: "devtools.styleeditor.enabled", - openChrome: function SE_openChrome() + /** + * Opens the style editor. If the UI is already open, it will be focused. + * + * @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet. + * @param {Number} [aLine] Line to which the caret should be moved (one-indexed). + * @param {Number} [aCol] Column to which the caret should be moved (one-indexed). + */ + openChrome: function SE_openChrome(aSelectedStyleSheet, aLine, aCol) { const CHROME_URL = "chrome://browser/content/styleeditor.xul"; const CHROME_WINDOW_TYPE = "Tools:StyleEditor"; const CHROME_WINDOW_FLAGS = "chrome,centerscreen,resizable,dialog=no"; // focus currently open Style Editor window for this document, if any let contentWindow = gBrowser.selectedBrowser.contentWindow; let contentWindowID = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor). getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID; let enumerator = Services.wm.getEnumerator(CHROME_WINDOW_TYPE); while (enumerator.hasMoreElements()) { var win = enumerator.getNext(); if (win.styleEditorChrome.contentWindowID == contentWindowID) { + if (aSelectedStyleSheet) { + win.styleEditorChrome.selectStyleSheet(aSelectedStyleSheet, aLine, aCol); + } win.focus(); return win; } } + let args = { + contentWindow: contentWindow, + selectedStyleSheet: aSelectedStyleSheet, + line: aLine, + col: aCol + }; + args.wrappedJSObject = args; let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank", - CHROME_WINDOW_FLAGS, - contentWindow); + CHROME_WINDOW_FLAGS, args); chromeWindow.focus(); return chromeWindow; } }; function onWebDeveloperMenuShowing() { document.getElementById("Tools:WebConsole").setAttribute("checked", HUDConsoleUI.getOpenHUD() != null); }
--- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1366,17 +1366,18 @@ if (!blank) { // Stop the existing about:blank load. Otherwise, if aURI // doesn't stop in-progress loads on its own, we'll get into // trouble with multiple parallel loads running at once. b.stop(); // pretend the user typed this so it'll be available till // the document successfully loads - b.userTypedValue = aURI; + if (!isBlankPageURL(aURI)) + b.userTypedValue = aURI; let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE; if (aAllowThirdPartyFixup) flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP; if (aFromExternal) flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL; if (aIsUTF8) flags |= Ci.nsIWebNavigation.LOAD_FLAGS_URI_IS_UTF8; @@ -1558,25 +1559,36 @@ <method name="_beginRemoveTab"> <parameter name="aTab"/> <parameter name="aTabWillBeMoved"/> <parameter name="aCloseWindowWithLastTab"/> <parameter name="aCloseWindowFastpath"/> <body> <![CDATA[ - if (aTab.closing || this._windowIsClosing) + if (aTab.closing || + aTab._pendingPermitUnload || + this._windowIsClosing) return false; var browser = this.getBrowserForTab(aTab); if (!aTabWillBeMoved) { let ds = browser.docShell; - if (ds && ds.contentViewer && !ds.contentViewer.permitUnload()) - return false; + if (ds && ds.contentViewer) { + // We need to block while calling permitUnload() because it + // processes the event queue and may lead to another removeTab() + // call before permitUnload() even returned. + aTab._pendingPermitUnload = true; + let permitUnload = ds.contentViewer.permitUnload(); + delete aTab._pendingPermitUnload; + + if (!permitUnload) + return false; + } } var closeWindow = false; var newTab = false; if (this.tabs.length - this._removingTabs.length == 1) { closeWindow = aCloseWindowWithLastTab != null ? aCloseWindowWithLastTab : !window.toolbar.visible || this.tabContainer._closeWindowWithLastTab; @@ -3534,22 +3546,24 @@ this.tabbrowser.swapBrowsersAndCloseOther(newTab, draggedTab); // We need to select the tab after we've done // swapBrowsersAndCloseOther, so that the updateCurrentBrowser // it triggers will correctly update our URL bar. this.tabbrowser.selectedTab = newTab; } else { - let url = browserDragAndDrop.drop(event, { }); + // Pass true to disallow dropping javascript: or data: urls + let url; + try { + url = browserDragAndDrop.drop(event, { }, true); + } catch (ex) {} // valid urls don't contain spaces ' '; if we have a space it isn't a valid url. - // Also disallow dropping javascript: or data: urls--bail out - if (!url || !url.length || url.indexOf(" ", 0) != -1 || - /^\s*(javascript|data):/.test(url)) + if (!url || url.indexOf(" ") != -1) return; let bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground"); if (event.shiftKey) bgLoad = !bgLoad; let tab = this._getDragTargetTab(event);
--- a/browser/base/content/test/Makefile.in +++ b/browser/base/content/test/Makefile.in @@ -263,16 +263,17 @@ endif test_bug628179.html \ browser_wyciwyg_urlbarCopying.js \ test_wyciwyg_copying.html \ authenticate.sjs \ browser_minimize.js \ browser_aboutSyncProgress.js \ browser_middleMouse_inherit.js \ redirect_bug623155.sjs \ + browser_tabDrop.js \ $(NULL) ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT)) _BROWSER_FILES += \ browser_bug462289.js \ $(NULL) else _BROWSER_FILES += \
--- a/browser/base/content/test/browser_homeDrop.js +++ b/browser/base/content/test/browser_homeDrop.js @@ -21,16 +21,17 @@ function test() { let dialogListener = new WindowListener("chrome://global/content/commonDialog.xul", function (domwindow) { 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 () { + info("Dialog closed? " + domwindow.closed + "\n"); let consoleListener = { observe: function (m) { info("m: " + m + "\n"); info("m.message: " + m.message + "\n"); if (m.message.indexOf("NS_ERROR_DOM_BAD_URI") > -1) { ok(true, "drop was blocked"); executeSoon(finish); } @@ -39,16 +40,24 @@ function test() { Services.console.registerListener(consoleListener); registerCleanupFunction(function () { Services.console.unregisterListener(consoleListener); }); // 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"); + originalHandler(aEvent); + }; + registerCleanupFunction(function () { + homeButtonObserver.onDrop = originalHandler; + }); chromeUtils.synthesizeDrop(homeButton, homeButton, [[{type: "text/plain", data: "javascript:8888"}]], "copy", window, EventUtils); }) }); Services.wm.addListener(dialogListener); chromeUtils.synthesizeDrop(homeButton, homeButton, [[{type: "text/plain", data: "http://mochi.test:8888/"}]], "copy", window, EventUtils); }
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/browser_tabDrop.js @@ -0,0 +1,71 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function test() { + waitForExplicitFinish(); + + let newTab = gBrowser.selectedTab = gBrowser.addTab("about:blank", {skipAnimation: true}); + registerCleanupFunction(function () { + gBrowser.removeTab(newTab); + }); + + let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. + getService(Ci.mozIJSSubScriptLoader); + let chromeUtils = {}; + scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", chromeUtils); + + let tabContainer = gBrowser.tabContainer; + var receivedDropCount = 0; + function dropListener() { + receivedDropCount++; + if (receivedDropCount == triggeredDropCount) { + is(openedTabs, validDropCount, "correct number of tabs were opened"); + executeSoon(finish); + } + } + tabContainer.addEventListener("drop", dropListener, false); + registerCleanupFunction(function () { + tabContainer.removeEventListener("drop", dropListener, false); + }); + + var openedTabs = 0; + function tabOpenListener(e) { + openedTabs++; + let tab = e.target; + executeSoon(function () { + gBrowser.removeTab(tab); + }); + } + + tabContainer.addEventListener("TabOpen", tabOpenListener, false); + registerCleanupFunction(function () { + tabContainer.removeEventListener("TabOpen", tabOpenListener, false); + }); + + var triggeredDropCount = 0; + var validDropCount = 0; + function drop(text, valid) { + triggeredDropCount++; + if (valid) + validDropCount++; + executeSoon(function () { + // A drop type of "link" onto an existing tab would normally trigger a + // load in that same tab, but tabbrowser code in _getDragTargetTab treats + // drops on the outer edges of a tab differently (loading a new tab + // instead). The events created by synthesizeDrop have all of their + // coordinates set to 0 (screenX/screenY), so they're treated as drops + // on the outer edge of the tab, thus they open new tabs. + chromeUtils.synthesizeDrop(newTab, newTab, [[{type: "text/plain", data: text}]], "link", window, EventUtils); + }); + } + + // Begin and end with valid drops to make sure we wait for all drops before + // ending the test + drop("mochi.test/first", true); + drop("javascript:'bad'"); + drop("jAvascript:'bad'"); + drop("space bad"); + drop("mochi.test/second", true); + drop("data:text/html,bad"); + drop("mochi.test/third", true); +}
--- a/browser/components/nsBrowserContentHandler.js +++ b/browser/components/nsBrowserContentHandler.js @@ -47,36 +47,34 @@ const nsIBrowserHistory = Component const nsIChannel = Components.interfaces.nsIChannel; const nsICommandLine = Components.interfaces.nsICommandLine; const nsICommandLineHandler = Components.interfaces.nsICommandLineHandler; const nsIContentHandler = Components.interfaces.nsIContentHandler; const nsIDocShellTreeItem = Components.interfaces.nsIDocShellTreeItem; const nsIDOMChromeWindow = Components.interfaces.nsIDOMChromeWindow; const nsIDOMWindow = Components.interfaces.nsIDOMWindow; const nsIFileURL = Components.interfaces.nsIFileURL; -const nsIHttpProtocolHandler = Components.interfaces.nsIHttpProtocolHandler; const nsIInterfaceRequestor = Components.interfaces.nsIInterfaceRequestor; const nsINetUtil = Components.interfaces.nsINetUtil; const nsIPrefBranch = Components.interfaces.nsIPrefBranch; const nsIPrefLocalizedString = Components.interfaces.nsIPrefLocalizedString; const nsISupportsString = Components.interfaces.nsISupportsString; const nsIURIFixup = Components.interfaces.nsIURIFixup; const nsIWebNavigation = Components.interfaces.nsIWebNavigation; const nsIWindowMediator = Components.interfaces.nsIWindowMediator; const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher; const nsIWebNavigationInfo = Components.interfaces.nsIWebNavigationInfo; const nsIBrowserSearchService = Components.interfaces.nsIBrowserSearchService; const nsICommandLineValidator = Components.interfaces.nsICommandLineValidator; -const nsIXULAppInfo = Components.interfaces.nsIXULAppInfo; const NS_BINDING_ABORTED = Components.results.NS_BINDING_ABORTED; const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001; const NS_ERROR_ABORT = Components.results.NS_ERROR_ABORT; -const URI_INHERITS_SECURITY_CONTEXT = nsIHttpProtocolHandler +const URI_INHERITS_SECURITY_CONTEXT = Components.interfaces.nsIHttpProtocolHandler .URI_INHERITS_SECURITY_CONTEXT; function shouldLoadURI(aURI) { if (aURI && !aURI.schemeIs("chrome")) return true; dump("*** Preventing external load of chrome: URI into browser window\n"); dump(" Use -chrome <uri> instead\n"); @@ -132,26 +130,24 @@ function needHomepageOverride(prefb) { var savedmstone = null; try { savedmstone = prefb.getCharPref("browser.startup.homepage_override.mstone"); } catch (e) {} if (savedmstone == "ignore") return OVERRIDE_NONE; - var mstone = Components.classes["@mozilla.org/network/protocol;1?name=http"] - .getService(nsIHttpProtocolHandler).misc; + var mstone = Services.appinfo.platformVersion; var savedBuildID = null; try { savedBuildID = prefb.getCharPref("browser.startup.homepage_override.buildID"); } catch (e) {} - var buildID = Components.classes["@mozilla.org/xre/app-info;1"] - .getService(nsIXULAppInfo).platformBuildID; + var buildID = Services.appinfo.platformBuildID; if (mstone != savedmstone) { // Bug 462254. Previous releases had a default pref to suppress the EULA // agreement if the platform's installer had already shown one. Now with // about:rights we've removed the EULA stuff and default pref, but we need // a way to make existing profiles retain the default that we removed. if (savedmstone) prefb.setBoolPref("browser.rights.3.shown", true);
--- a/browser/devtools/debugger/test/Makefile.in +++ b/browser/devtools/debugger/test/Makefile.in @@ -67,16 +67,17 @@ include $(topsrcdir)/config/rules.mk browser_dbg_stack-02.js \ browser_dbg_stack-03.js \ browser_dbg_stack-04.js \ browser_dbg_location-changes.js \ browser_dbg_script-switching.js \ browser_dbg_pause-resume.js \ browser_dbg_update-editor-mode.js \ browser_dbg_select-line.js \ + browser_dbg_clean-exit.js \ head.js \ $(NULL) _BROWSER_TEST_PAGES = \ browser_dbg_tab1.html \ browser_dbg_tab2.html \ browser_dbg_debuggerstatement.html \ browser_dbg_stack.html \
new file mode 100644 --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_clean-exit.js @@ -0,0 +1,42 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Test that closing a tab with the debugger in a paused state exits cleanly. + +var gPane = null; +var gTab = null; +var gDebuggee = null; +var gDebugger = null; + +const DEBUGGER_TAB_URL = "http://example.com/browser/browser/devtools/" + + "debugger/test/" + + "browser_dbg_debuggerstatement.html"; + +function test() { + debug_tab_pane(DEBUGGER_TAB_URL, function(aTab, aDebuggee, aPane) { + gTab = aTab; + gDebuggee = aDebuggee; + gPane = aPane; + gDebugger = gPane.debuggerWindow; + + testCleanExit(); + }); +} + +function testCleanExit() { + gPane.activeThread.addOneTimeListener("framesadded", function() { + Services.tm.currentThread.dispatch({ run: function() { + is(gDebugger.StackFrames.activeThread.paused, true, + "Should be paused after the debugger statement."); + + gPane._client.addOneTimeListener("tabDetached", function () { + finish(); + }); + removeTab(gTab); + }}, 0); + }); + + gTab.linkedBrowser.contentWindow.wrappedJSObject.runDebuggerStatement(); +}
--- a/browser/devtools/highlighter/inspector.jsm +++ b/browser/devtools/highlighter/inspector.jsm @@ -758,16 +758,19 @@ InspectorUI.prototype = { this.store.setValue(winID, "ruleView", ruleViewStore); } this.ruleView = new CssRuleView(doc, ruleViewStore); this.boundRuleViewChanged = this.ruleViewChanged.bind(this); this.ruleView.element.addEventListener("CssRuleViewChanged", this.boundRuleViewChanged); + this.cssRuleViewBoundCSSLinkClicked = this.ruleViewCSSLinkClicked.bind(this); + this.ruleView.element.addEventListener("CssRuleViewCSSLinkClicked", + this.cssRuleViewBoundCSSLinkClicked); doc.documentElement.appendChild(this.ruleView.element); this.ruleView.highlight(this.selection); Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, null); }.bind(this); iframe.addEventListener("load", boundLoadListener, true); @@ -796,26 +799,52 @@ InspectorUI.prototype = { ruleViewChanged: function IUI_ruleViewChanged() { this.isDirty = true; this.nodeChanged(this.ruleViewObject); }, /** + * When a css link is clicked this method is called in order to either: + * 1. Open the link in view source (for element style attributes) + * 2. Open the link in the style editor + * + * @param aEvent The event containing the style rule to act on + */ + ruleViewCSSLinkClicked: function(aEvent) + { + if (!this.chromeWin) { + return; + } + + let rule = aEvent.detail.rule; + let styleSheet = rule.sheet; + + if (styleSheet) { + this.chromeWin.StyleEditor.openChrome(styleSheet, rule.ruleLine); + } else { + let href = rule.elementStyle.element.ownerDocument.location.href; + this.chromeWin.openUILinkIn("view-source:" + href, "window"); + } + }, + + /** * Destroy the rule view. */ destroyRuleView: function IUI_destroyRuleView() { let iframe = this.getToolIframe(this.ruleViewObject); iframe.parentNode.removeChild(iframe); if (this.ruleView) { this.ruleView.element.removeEventListener("CssRuleViewChanged", this.boundRuleViewChanged); + this.ruleView.element.removeEventListener("CssRuleViewCSSLinkClicked", + this.cssRuleViewBoundCSSLinkClicked); delete boundRuleViewChanged; this.ruleView.clear(); delete this.ruleView; } }, ///////////////////////////////////////////////////////////////////////// //// Utility Methods
--- a/browser/devtools/sourceeditor/source-editor-orion.jsm +++ b/browser/devtools/sourceeditor/source-editor-orion.jsm @@ -73,16 +73,19 @@ const ORION_THEMES = { * to Orion events. */ const ORION_EVENTS = { ContextMenu: "ContextMenu", TextChanged: "ModelChanged", Selection: "Selection", Focus: "Focus", Blur: "Blur", + MouseOver: "MouseOver", + MouseOut: "MouseOut", + MouseMove: "MouseMove", }; /** * Known Orion annotation types. */ const ORION_ANNOTATION_TYPES = { currentBracket: "orion.annotation.currentBracket", matchingBracket: "orion.annotation.matchingBracket",
--- a/browser/devtools/sourceeditor/source-editor.jsm +++ b/browser/devtools/sourceeditor/source-editor.jsm @@ -156,16 +156,40 @@ SourceEditor.EVENTS = { * The focus event is fired when the editor is focused. */ FOCUS: "Focus", /** * The blur event is fired when the editor goes out of focus. */ BLUR: "Blur", + + /** + * The MouseMove event is sent when the user moves the mouse over a line + * annotation. The event object properties: + * - event - the DOM mousemove event object. + * - x and y - the mouse coordinates relative to the document being edited. + */ + MOUSE_MOVE: "MouseMove", + + /** + * The MouseOver event is sent when the mouse pointer enters a line + * annotation. The event object properties: + * - event - the DOM mouseover event object. + * - x and y - the mouse coordinates relative to the document being edited. + */ + MOUSE_OVER: "MouseOver", + + /** + * This MouseOut event is sent when the mouse pointer exits a line + * annotation. The event object properties: + * - event - the DOM mouseout event object. + * - x and y - the mouse coordinates relative to the document being edited. + */ + MOUSE_OUT: "MouseOut", }; /** * Extend a destination object with properties from a source object. * * @param object aDestination * @param object aSource */
--- a/browser/devtools/sourceeditor/test/Makefile.in +++ b/browser/devtools/sourceeditor/test/Makefile.in @@ -50,12 +50,13 @@ include $(topsrcdir)/config/rules.mk browser_bug687573_vscroll.js \ browser_bug687568_pagescroll.js \ browser_bug687580_drag_and_drop.js \ browser_bug684546_reset_undo.js \ browser_bug695035_middle_click_paste.js \ browser_bug687160_line_api.js \ browser_bug650345_find.js \ browser_bug703692_focus_blur.js \ + browser_bug725388_mouse_events.js \ head.js \ libs:: $(_BROWSER_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/browser/devtools/sourceeditor/test/browser_bug725388_mouse_events.js @@ -0,0 +1,97 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; + +let testWin; +let editor; + +function test() +{ + waitForExplicitFinish(); + + const windowUrl = "data:text/xml,<?xml version='1.0'?>" + + "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" + + " title='Test for bug 725388' width='600' height='500'><hbox flex='1'/></window>"; + const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no"; + + testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null); + testWin.addEventListener("load", function onWindowLoad() { + testWin.removeEventListener("load", onWindowLoad, false); + waitForFocus(initEditor, testWin); + }, false); +} + +function initEditor() +{ + let hbox = testWin.document.querySelector("hbox"); + + editor = new SourceEditor(); + editor.init(hbox, {}, editorLoaded); +} + +function editorLoaded() +{ + let text = "BrowserBug - 725388"; + editor.setText(text); + + let target = editor.editorElement; + let targetWin = target.ownerDocument.defaultView; + + let mMoveHandler = function(aEvent) { + editor.removeEventListener(SourceEditor.EVENTS.MOUSE_MOVE, mMoveHandler); + + is(aEvent.event.type, "mousemove", "MouseMove event fired."); + + editor.addEventListener(SourceEditor.EVENTS.MOUSE_OVER, mOverHandler); + waitForFocus(function() { + EventUtils.synthesizeMouse(target, 10, 10, {type: "mouseover"}, + targetWin); + }); + }; + + let mOverHandler = function(aEvent) { + editor.removeEventListener(SourceEditor.EVENTS.MOUSE_OVER, mOverHandler); + + is(aEvent.event.type, "mouseover", "MouseOver event fired."); + + editor.addEventListener(SourceEditor.EVENTS.MOUSE_OUT, mOutHandler); + waitForFocus(function() { + EventUtils.synthesizeMouse(target, -10, -10, {type: "mouseout"}, + targetWin); + }, targetWin); + }; + + let mOutHandler = function(aEvent) { + editor.removeEventListener(SourceEditor.EVENTS.MOUSE_OUT, mOutHandler); + + is(aEvent.event.type, "mouseout", "MouseOut event fired."); + executeSoon(testEnd); + }; + + editor.addEventListener(SourceEditor.EVENTS.MOUSE_MOVE, mMoveHandler); + + editor.focus(); + waitForFocus(function() { + EventUtils.synthesizeMouse(target, 1, 1, {type: "mousemove"}, + targetWin); + }, targetWin); +} + +function testEnd() +{ + if (editor) { + editor.destroy(); + } + if (testWin) { + testWin.close(); + } + testWin = editor = null; + + waitForFocus(finish, window); +}
--- a/browser/devtools/styleeditor/StyleEditor.jsm +++ b/browser/devtools/styleeditor/StyleEditor.jsm @@ -141,17 +141,17 @@ StyleEditor.prototype = { /** * Retrieve the stylesheet this editor is attached to. * * @return DOMStyleSheet */ get styleSheet() { - assert(this._styleSheet, "StyleSheet must be loaded first.") + assert(this._styleSheet, "StyleSheet must be loaded first."); return this._styleSheet; }, /** * Retrieve the index (order) of stylesheet in the document. * * @return number */ @@ -916,19 +916,21 @@ StyleEditor.prototype = { { // insert the origin editor instance as first argument if (!aArgs) { aArgs = [this]; } else { aArgs.unshift(this); } + // copy the list of listeners to allow adding/removing listeners in handlers + let listeners = this._actionListeners.concat(); // trigger all listeners that have this action handler - for (let i = 0; i < this._actionListeners.length; ++i) { - let listener = this._actionListeners[i]; + for (let i = 0; i < listeners.length; ++i) { + let listener = listeners[i]; let actionHandler = listener["on" + aName]; if (actionHandler) { actionHandler.apply(listener, aArgs); } } // when a flag got changed, user-facing state need to be persisted if (aName == "FlagChange") {
--- a/browser/devtools/styleeditor/StyleEditorChrome.jsm +++ b/browser/devtools/styleeditor/StyleEditorChrome.jsm @@ -265,19 +265,21 @@ StyleEditorChrome.prototype = { { // insert the origin Chrome instance as first argument if (!aArgs) { aArgs = [this]; } else { aArgs.unshift(this); } - // trigger all listeners that have this named handler - for (let i = 0; i < this._listeners.length; ++i) { - let listener = this._listeners[i]; + // copy the list of listeners to allow adding/removing listeners in handlers + let listeners = this._listeners.concat(); + // trigger all listeners that have this named handler. + for (let i = 0; i < listeners.length; i++) { + let listener = listeners[i]; let handler = listener["on" + aName]; if (handler) { handler.apply(listener, aArgs); } } }, /** @@ -324,20 +326,20 @@ StyleEditorChrome.prototype = { * Populate the chrome UI according to the content document. * * @see StyleEditor._setupShadowStyleSheet */ _populateChrome: function SEC__populateChrome() { this._resetChrome(); + let document = this.contentDocument; this._document.title = _("chromeWindowTitle", - this.contentDocument.title || this.contentDocument.location.href); + document.title || document.location.href); - let document = this.contentDocument; for (let i = 0; i < document.styleSheets.length; ++i) { let styleSheet = document.styleSheets[i]; let editor = new StyleEditor(document, styleSheet); editor.addActionListener(this); this._editors.push(editor); } @@ -348,16 +350,89 @@ StyleEditorChrome.prototype = { // NOT loaded/ready yet. This also helps responsivity during loading when // there are many heavy stylesheets. this._editors.forEach(function (aEditor) { this._window.setTimeout(aEditor.load.bind(aEditor), 0); }, this); }, /** + * selects a stylesheet and optionally moves the cursor to a selected line + * + * @param {CSSStyleSheet} [aSheet] + * Stylesheet that should be selected. If a stylesheet is not passed + * and the editor is not initialized we focus the first stylesheet. If + * a stylesheet is not passed and the editor is initialized we ignore + * the call. + * @param {Number} [aLine] + * Line to which the caret should be moved (one-indexed). + * @param {Number} [aCol] + * Column to which the caret should be moved (one-indexed). + */ + selectStyleSheet: function SEC_selectSheet(aSheet, aLine, aCol) + { + let select = function DEC_select(aEditor) { + let summary = aSheet ? this.getSummaryElementForEditor(aEditor) + : this._view.getSummaryElementByOrdinal(0); + let setCaret = false; + + if (aLine || aCol) { + aLine = aLine || 1; + aCol = aCol || 1; + setCaret = true; + } + if (!aEditor.sourceEditor) { + // If a line or column was specified we move the caret appropriately. + if (setCaret) { + aEditor.addActionListener({ + onAttach: function SEC_selectSheet_onAttach() + { + aEditor.removeActionListener(this); + aEditor.sourceEditor.setCaretPosition(aLine - 1, aCol - 1); + } + }); + } + this._view.activeSummary = summary; + } else { + this._view.activeSummary = summary; + + // If a line or column was specified we move the caret appropriately. + if (setCaret) { + aEditor.sourceEditor.setCaretPosition(aLine - 1, aCol - 1); + } + } + }.bind(this); + + if (!this.editors.length) { + // We are in the main initialization phase so we wait for the editor + // containing the target stylesheet to be added and select the target + // stylesheet, optionally moving the cursor to a selected line. + this.addChromeListener({ + onEditorAdded: function SEC_selectSheet_onEditorAdded(aChrome, aEditor) { + if ((!aSheet && aEditor.styleSheetIndex == 0) || + aEditor.styleSheet == aSheet) { + aChrome.removeChromeListener(this); + select(aEditor); + } + } + }); + } else if (aSheet) { + // We are already initialized and a stylesheet has been specified. Here + // we iterate through the editors and select the one containing the target + // stylesheet, optionally moving the cursor to a selected line. + for each (let editor in this.editors) { + if (editor.styleSheet == aSheet) { + select(editor); + break; + } + } + } + }, + + /** * Disable all UI, effectively making editors read-only. * This is automatically called when no content window is attached. * * @see contentWindow */ _disableChrome: function SEC__disableChrome() { let matches = this._root.querySelectorAll("button,toolbarbutton,textbox"); @@ -450,19 +525,18 @@ StyleEditorChrome.prototype = { aSummary.addEventListener("focus", function onSummaryFocus(aEvent) { if (aEvent.target == aSummary) { // autofocus the stylesheet name aSummary.querySelector(".stylesheet-name").focus(); } }, false); - // autofocus the first or new stylesheet - if (editor.styleSheetIndex == 0 || - editor.hasFlag(StyleEditorFlags.NEW)) { + // autofocus new stylesheets + if (editor.hasFlag(StyleEditorFlags.NEW)) { this._view.activeSummary = aSummary; } this._triggerChromeListeners("EditorAdded", [editor]); }.bind(this), onHide: function ASV_onItemShow(aSummary, aDetails, aData) { aData.editor.onHide(); },
--- a/browser/devtools/styleeditor/styleeditor.xul +++ b/browser/devtools/styleeditor/styleeditor.xul @@ -127,13 +127,15 @@ data-placeholder="&editorTextbox.placeholder;"/> </xul:box> </div> <!-- #splitview-templates --> </xul:box> <!-- .splitview-root --> <xul:script type="application/javascript"><![CDATA[ Components.utils.import("resource:///modules/devtools/StyleEditorChrome.jsm"); let chromeRoot = document.getElementById("style-editor-chrome"); -let contentWindow = window.arguments[0]; +let args = window.arguments[0].wrappedJSObject; +let contentWindow = args.contentWindow; let chrome = new StyleEditorChrome(chromeRoot, contentWindow); +chrome.selectStyleSheet(args.selectedStyleSheet, args.line, args.col); window.styleEditorChrome = chrome; ]]></xul:script> </xul:window>
--- a/browser/devtools/styleeditor/test/Makefile.in +++ b/browser/devtools/styleeditor/test/Makefile.in @@ -46,16 +46,17 @@ include $(topsrcdir)/config/rules.mk _BROWSER_TEST_FILES = \ browser_styleeditor_enabled.js \ browser_styleeditor_filesave.js \ browser_styleeditor_import.js \ browser_styleeditor_init.js \ browser_styleeditor_loading.js \ browser_styleeditor_new.js \ + browser_styleeditor_passedinsheet.js \ browser_styleeditor_pretty.js \ browser_styleeditor_readonly.js \ browser_styleeditor_reopen.js \ browser_styleeditor_sv_keynav.js \ browser_styleeditor_sv_resize.js \ four.html \ head.js \ media.html \
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_passedinsheet.js @@ -0,0 +1,61 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TESTCASE_URI = TEST_BASE + "simple.html"; +const LINE = 6; +const COL = 2; + +let editor = null; +let sheet = null; + +function test() +{ + waitForExplicitFinish(); + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + run(); + }, true); + content.location = TESTCASE_URI; +} + +function run() +{ + sheet = content.document.styleSheets[1]; + launchStyleEditorChrome(function attachListeners(aChrome) { + aChrome.addChromeListener({ + onEditorAdded: checkSourceEditor + }); + }, sheet, LINE, COL); +} + +function checkSourceEditor(aChrome, aEditor) +{ + if (!aEditor.sourceEditor) { + aEditor.addActionListener({ + onAttach: function (aEditor) { + aEditor.removeActionListener(this); + validate(aEditor); + } + }); + } else { + validate(aEditor); + } +} + +function validate(aEditor) +{ + info("validating style editor"); + let sourceEditor = aEditor.sourceEditor; + let caretPosition = sourceEditor.getCaretPosition(); + is(caretPosition.line, LINE - 1, "caret row is correct"); // index based + is(caretPosition.col, COL - 1, "caret column is correct"); + is(aEditor.styleSheet, sheet, "loaded stylesheet matches document stylesheet"); + finishUp(); +} + +function finishUp() +{ + editor = sheet = null; + finish(); +}
--- a/browser/devtools/styleeditor/test/head.js +++ b/browser/devtools/styleeditor/test/head.js @@ -14,33 +14,33 @@ function cleanup() gChromeWindow.close(); gChromeWindow = null; } while (gBrowser.tabs.length > 1) { gBrowser.removeCurrentTab(); } } -function launchStyleEditorChrome(aCallback) +function launchStyleEditorChrome(aCallback, aSheet, aLine, aCol) { - gChromeWindow = StyleEditor.openChrome(); + gChromeWindow = StyleEditor.openChrome(aSheet, aLine, aCol); if (gChromeWindow.document.readyState != "complete") { gChromeWindow.addEventListener("load", function onChromeLoad() { gChromeWindow.removeEventListener("load", onChromeLoad, true); gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true; aCallback(gChromeWindow.styleEditorChrome); }, true); } else { gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true; aCallback(gChromeWindow.styleEditorChrome); } } -function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback) +function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback, aSheet, aLine, aCol) { gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedBrowser.addEventListener("load", function onLoad() { gBrowser.selectedBrowser.removeEventListener("load", onLoad, true); - launchStyleEditorChrome(aCallback); + launchStyleEditorChrome(aCallback, aSheet, aLine, aCol); }, true); } registerCleanupFunction(cleanup);
--- a/browser/devtools/styleinspector/CssHtmlTree.jsm +++ b/browser/devtools/styleinspector/CssHtmlTree.jsm @@ -268,24 +268,28 @@ CssHtmlTree.prototype = { */ highlight: function CssHtmlTree_highlight(aElement) { this.viewedElement = aElement; this._unmatchedProperties = null; this._matchedProperties = null; if (this.htmlComplete) { + this.refreshSourceFilter(); this.refreshPanel(); } else { if (this._refreshProcess) { this._refreshProcess.cancel(); } CssHtmlTree.processTemplate(this.templateRoot, this.root, this); + // Refresh source filter ... this must be done after templateRoot has been + // processed. + this.refreshSourceFilter(); this.numVisibleProperties = 0; let fragment = this.doc.createDocumentFragment(); this._refreshProcess = new UpdateProcess(this.win, CssHtmlTree.propertyNames, { onItem: function(aPropertyName) { // Per-item callback. if (this.viewedElement != aElement || !this.styleInspector.isOpen()) { return false; } @@ -357,31 +361,38 @@ CssHtmlTree.prototype = { this._filterChangedTimeout = win.setTimeout(function() { this.refreshPanel(); this._filterChangeTimeout = null; }.bind(this), FILTER_CHANGED_TIMEOUT); }, /** - * The change event handler for the onlyUserStyles checkbox. When - * onlyUserStyles.checked is true we do not display properties that have no - * matched selectors, and we do not display UA styles. If .checked is false we - * do display even properties with no matched selectors, and we include the UA - * styles. + * The change event handler for the onlyUserStyles checkbox. * * @param {Event} aEvent the DOM Event object. */ onlyUserStylesChanged: function CssHtmltree_onlyUserStylesChanged(aEvent) { + this.refreshSourceFilter(); + this.refreshPanel(); + }, + + /** + * When onlyUserStyles.checked is true we only display properties that have + * matched selectors and have been included by the document or one of the + * document's stylesheets. If .checked is false we display all properties + * including those that come from UA stylesheets. + */ + refreshSourceFilter: function CssHtmlTree_setSourceFilter() + { this._matchedProperties = null; this.cssLogic.sourceFilter = this.showOnlyUserStyles ? CssLogic.FILTER.ALL : CssLogic.FILTER.UA; - this.refreshPanel(); }, /** * The CSS as displayed by the UI. */ createStyleViews: function CssHtmlTree_createStyleViews() { if (CssHtmlTree.propertyNames) { @@ -969,9 +980,29 @@ SelectorView.prototype = { this.tree.styleInspector.selectFromPath(source); aEvent.preventDefault(); }.bind(this), false); result += ".style"; } return result; }, + + /** + * When a css link is clicked this method is called in order to either: + * 1. Open the link in view source (for element style attributes). + * 2. Open the link in the style editor. + * + * @param aEvent The click event + */ + openStyleEditor: function(aEvent) + { + if (this.selectorInfo.selector._cssRule._cssSheet) { + let styleSheet = this.selectorInfo.selector._cssRule._cssSheet.domSheet; + let line = this.selectorInfo.ruleLine; + + this.tree.win.StyleEditor.openChrome(styleSheet, line); + } else { + let href = this.selectorInfo.sourceElement.ownerDocument.location.href; + this.tree.win.openUILinkIn("view-source:" + href, "window"); + } + }, };
--- a/browser/devtools/styleinspector/CssLogic.jsm +++ b/browser/devtools/styleinspector/CssLogic.jsm @@ -227,17 +227,17 @@ CssLogic.prototype = { let oldValue = this._sourceFilter; this._sourceFilter = aValue; let ruleCount = 0; // Update the CssSheet objects. this.forEachSheet(function(aSheet) { aSheet._sheetAllowed = -1; - if (!aSheet.systemSheet && aSheet.sheetAllowed) { + if (aSheet.contentSheet && aSheet.sheetAllowed) { ruleCount += aSheet.ruleCount; } }, this); this._ruleCount = ruleCount; // Full update is needed because the this.processMatchedSelectors() method // skips UA stylesheets if the filter does not allow such sheets. @@ -340,17 +340,17 @@ CssLogic.prototype = { get sheets() { if (!this._sheetsCached) { this._cacheSheets(); } let sheets = []; this.forEachSheet(function (aSheet) { - if (!aSheet.systemSheet) { + if (aSheet.contentSheet) { sheets.push(aSheet); } }, this); return sheets; }, /** @@ -390,17 +390,17 @@ CssLogic.prototype = { } if (!sheetFound) { if (!(cacheId in this._sheets)) { this._sheets[cacheId] = []; } sheet = new CssSheet(this, aDomSheet, aIndex); - if (sheet.sheetAllowed && !sheet.systemSheet) { + if (sheet.sheetAllowed && sheet.contentSheet) { this._ruleCount += sheet.ruleCount; } this._sheets[cacheId].push(sheet); } return sheet; }, @@ -564,17 +564,17 @@ CssLogic.prototype = { if (!this._matchedSelectors) { this.processMatchedSelectors(); } this._unmatchedSelectors = []; this.forEachSheet(function (aSheet) { // We do not show unmatched selectors from system stylesheets - if (aSheet.systemSheet || aSheet.disabled || !aSheet.mediaMatches) { + if (!aSheet.contentSheet || aSheet.disabled || !aSheet.mediaMatches) { return; } aSheet.forEachRule(function (aRule) { aRule.selectors.forEach(function (aSelector) { if (aSelector._matchId !== this._matchId) { this._unmatchedSelectors.push(aSelector); if (aCallback) { @@ -659,17 +659,17 @@ CssLogic.prototype = { } let sheet = this.getSheet(domRule.parentStyleSheet, -1); if (sheet._passId !== this._passId) { sheet.index = sheetIndex++; sheet._passId = this._passId; } - if (filter !== CssLogic.FILTER.UA && sheet.systemSheet) { + if (filter === CssLogic.FILTER.ALL && !sheet.contentSheet) { continue; } let rule = sheet.getRule(domRule); if (rule._passId === this._passId) { continue; } @@ -705,17 +705,17 @@ CssLogic.prototype = { { if (!this._matchedRules) { this._buildMatchedRules(); } let result = {}; this.forSomeSheets(function (aSheet) { - if (aSheet.systemSheet || aSheet.disabled || !aSheet.mediaMatches) { + if (!aSheet.contentSheet || aSheet.disabled || !aSheet.mediaMatches) { return false; } return aSheet.forSomeRules(function (aRule) { let unmatched = aRule._matchId !== this._matchId || this._ruleHasUnmatchedSelector(aRule); if (!unmatched) { return false; @@ -860,39 +860,33 @@ CssLogic.getShortNamePath = function Css * @returns A localized version of the given key. */ CssLogic.l10n = function(aName) CssLogic._strings.GetStringFromName(aName); XPCOMUtils.defineLazyGetter(CssLogic, "_strings", function() Services.strings .createBundle("chrome://browser/locale/devtools/styleinspector.properties")); /** - * Is the given property sheet a system (user agent) stylesheet? + * Is the given property sheet a content stylesheet? * * @param {CSSStyleSheet} aSheet a stylesheet - * @return {boolean} true if the given stylesheet is a system stylesheet or + * @return {boolean} true if the given stylesheet is a content stylesheet, * false otherwise. */ -CssLogic.isSystemStyleSheet = function CssLogic_isSystemStyleSheet(aSheet) +CssLogic.isContentStylesheet = function CssLogic_isContentStylesheet(aSheet) { - if (!aSheet) { + // All sheets with owner nodes have been included by content. + if (aSheet.ownerNode) { return true; } - let url = aSheet.href; - - if (!url) return false; - if (url.length === 0) return true; - - // Check for http[s] - if (url[0] === 'h') return false; - if (url.substr(0, 9) === "resource:") return true; - if (url.substr(0, 7) === "chrome:") return true; - if (url === "XPCSafeJSObjectWrapper.cpp") return true; - if (url.substr(0, 6) === "about:") return true; + // If the sheet has a CSSImportRule we need to check the parent stylesheet. + if (aSheet.ownerRule instanceof Ci.nsIDOMCSSImportRule) { + return CssLogic.isContentStylesheet(aSheet.parentStyleSheet); + } return false; }; /** * Return a shortened version of a style sheet's source. * * @param {CSSStyleSheet} aSheet the DOM object for the style sheet. @@ -937,17 +931,17 @@ CssLogic.shortSource = function CssLogic * @param {CSSStyleSheet} aDomSheet reference to a DOM CSSStyleSheet object. * @param {number} aIndex tells the index/position of the stylesheet within the * main document. */ function CssSheet(aCssLogic, aDomSheet, aIndex) { this._cssLogic = aCssLogic; this.domSheet = aDomSheet; - this.index = this.systemSheet ? -100 * aIndex : aIndex; + this.index = this.contentSheet ? aIndex : -100 * aIndex; // Cache of the sheets href. Cached by the getter. this._href = null; // Short version of href for use in select boxes etc. Cached by getter. this._shortSource = null; // null for uncached. this._sheetAllowed = null; @@ -955,31 +949,31 @@ function CssSheet(aCssLogic, aDomSheet, // Cached CssRules from the given stylesheet. this._rules = {}; this._ruleCount = -1; } CssSheet.prototype = { _passId: null, - _systemSheet: null, + _contentSheet: null, _mediaMatches: null, /** * Tells if the stylesheet is provided by the browser or not. * - * @return {boolean} true if this is a browser-provided stylesheet, or false + * @return {boolean} false if this is a browser-provided stylesheet, or true * otherwise. */ - get systemSheet() + get contentSheet() { - if (this._systemSheet === null) { - this._systemSheet = CssLogic.isSystemStyleSheet(this.domSheet); + if (this._contentSheet === null) { + this._contentSheet = CssLogic.isContentStylesheet(this.domSheet); } - return this._systemSheet; + return this._contentSheet; }, /** * Tells if the stylesheet is disabled or not. * @return {boolean} true if this stylesheet is disabled, or false otherwise. */ get disabled() { @@ -1043,17 +1037,17 @@ CssSheet.prototype = { { if (this._sheetAllowed !== null) { return this._sheetAllowed; } this._sheetAllowed = true; let filter = this._cssLogic.sourceFilter; - if (filter === CssLogic.FILTER.ALL && this.systemSheet) { + if (filter === CssLogic.FILTER.ALL && !this.contentSheet) { this._sheetAllowed = false; } if (filter !== CssLogic.FILTER.ALL && filter !== CssLogic.FILTER.UA) { this._sheetAllowed = (filter === this.href); } return this._sheetAllowed; }, @@ -1197,23 +1191,23 @@ function CssRule(aCssSheet, aDomRule, aE this._domRule = aDomRule; if (this._cssSheet) { // parse _domRule.selectorText on call to this.selectors this._selectors = null; this.line = this._cssSheet._cssLogic.domUtils.getRuleLine(this._domRule); this.source = this._cssSheet.shortSource + ":" + this.line; this.href = this._cssSheet.href; - this.systemRule = this._cssSheet.systemSheet; + this.contentRule = this._cssSheet.contentSheet; } else if (aElement) { this._selectors = [ new CssSelector(this, "@element.style") ]; this.line = -1; this.source = CssLogic.l10n("rule.sourceElement"); this.href = "#"; - this.systemRule = false; + this.contentRule = true; this.sourceElement = aElement; } } CssRule.prototype = { _passId: null, /** @@ -1391,22 +1385,22 @@ CssSelector.prototype = { get href() { return this._cssRule.href; }, /** * Check if the selector comes from a browser-provided stylesheet. * - * @return {boolean} true if the selector comes from a browser-provided + * @return {boolean} true if the selector comes from a content-provided * stylesheet, or false otherwise. */ - get systemRule() + get contentRule() { - return this._cssRule.systemRule; + return this._cssRule.contentRule; }, /** * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter. * * @return {boolean} true if the parent stylesheet is allowed by the current * sourceFilter, or false otherwise. */ @@ -1789,22 +1783,22 @@ function CssSelectorInfo(aSelector, aPro /* Score prefix: 0 UA normal property 1 UA important property 2 normal property 3 inline (element.style) 4 important 5 inline important */ - let scorePrefix = this.systemRule ? 0 : 2; + let scorePrefix = this.contentRule ? 2 : 0; if (this.elementStyle) { scorePrefix++; } if (this.important) { - scorePrefix += this.systemRule ? 1 : 2; + scorePrefix += this.contentRule ? 2 : 1; } this.specificityScore = "" + scorePrefix + this.specificity.ids + this.specificity.classes + this.specificity.tags; } CssSelectorInfo.prototype = { /** @@ -1897,32 +1891,32 @@ CssSelectorInfo.prototype = { }, /** * Check if the selector comes from a browser-provided stylesheet. * * @return {boolean} true if the selector comes from a browser-provided * stylesheet, or false otherwise. */ - get systemRule() + get contentRule() { - return this.selector.systemRule; + return this.selector.contentRule; }, /** * Compare the current CssSelectorInfo instance to another instance, based on * specificity information. * * @param {CssSelectorInfo} aThat The instance to compare ourselves against. * @return number -1, 0, 1 depending on how aThat compares with this. */ compareTo: function CssSelectorInfo_compareTo(aThat) { - if (this.systemRule && !aThat.systemRule) return 1; - if (!this.systemRule && aThat.systemRule) return -1; + if (!this.contentRule && aThat.contentRule) return 1; + if (this.contentRule && !aThat.contentRule) return -1; if (this.elementStyle && !aThat.elementStyle) { if (!this.important && aThat.important) return 1; else return -1; } if (!this.elementStyle && aThat.elementStyle) { if (this.important && !aThat.important) return -1;
--- a/browser/devtools/styleinspector/CssRuleView.jsm +++ b/browser/devtools/styleinspector/CssRuleView.jsm @@ -33,17 +33,17 @@ * 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 ***** */ -"use strict" +"use strict"; const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; const HTML_NS = "http://www.w3.org/1999/xhtml"; const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; @@ -176,18 +176,18 @@ ElementStyle.prototype = { var domRules = this.domUtils.getCSSStyleRules(aElement); // getCSStyleRules returns ordered from least-specific to // most-specific. for (let i = domRules.Count() - 1; i >= 0; i--) { let domRule = domRules.GetElementAt(i); // XXX: Optionally provide access to system sheets. - let systemSheet = CssLogic.isSystemStyleSheet(domRule.parentStyleSheet); - if (systemSheet) { + let contentSheet = CssLogic.isContentStylesheet(domRule.parentStyleSheet); + if (!contentSheet) { continue; } if (domRule.type !== Ci.nsIDOMCSSRule.STYLE_RULE) { continue; } this._maybeAddRule({ @@ -319,17 +319,17 @@ ElementStyle.prototype = { dirty = computedProp._overriddenDirty || dirty; delete computedProp._overriddenDirty; } dirty = (!!aProp.overridden != overridden) || dirty; aProp.overridden = overridden; return dirty; } -} +}; /** * A single style rule or declaration. * * @param {ElementStyle} aElementStyle * The ElementStyle to which this rule belongs. * @param {object} aOptions * The information used to construct this rule. Properties include: @@ -353,37 +353,55 @@ function Rule(aElementStyle, aOptions) } Rule.prototype = { get title() { if (this._title) { return this._title; } - let sheet = this.domRule ? this.domRule.parentStyleSheet : null; - this._title = CssLogic.shortSource(sheet); + this._title = CssLogic.shortSource(this.sheet); if (this.domRule) { - let line = this.elementStyle.domUtils.getRuleLine(this.domRule); - this._title += ":" + line; + this._title += ":" + this.ruleLine; } if (this.inherited) { let eltText = this.inherited.tagName.toLowerCase(); if (this.inherited.id) { eltText += "#" + this.inherited.id; } let args = [eltText, this._title]; this._title = CssLogic._strings.formatStringFromName("rule.inheritedSource", args, args.length); } return this._title; }, /** + * The rule's stylesheet. + */ + get sheet() + { + return this.domRule ? this.domRule.parentStyleSheet : null; + }, + + /** + * The rule's line within a stylesheet + */ + get ruleLine() + { + if (!this.sheet) { + // No stylesheet, no ruleLine + return null; + } + return this.elementStyle.domUtils.getRuleLine(this.domRule); + }, + + /** * Create a new TextProperty to include in the rule. * * @param {string} aName * The text property name (such as "background" or "border-top"). * @param {string} aValue * The property's value (not including priority). * @param {string} aPriority * The property's priority (either "important" or an empty string). @@ -525,17 +543,17 @@ Rule.prototype = { for each (let prop in disabledProps) { let textProp = new TextProperty(this, prop.name, prop.value, prop.priority); textProp.enabled = false; this.textProps.push(textProp); } }, -} +}; /** * A single property in a rule's cssText. * * @param {Rule} aRule * The rule this TextProperty came from. * @param {string} aName * The text property name (such as "background" or "border-top"). @@ -613,17 +631,17 @@ TextProperty.prototype = { this.rule.setPropertyEnabled(this, aValue); this.updateEditor(); }, remove: function TextProperty_remove() { this.rule.removeProperty(this); } -} +}; /** * View hierarchy mostly follows the model hierarchy. * * CssRuleView: * Owns an ElementStyle and creates a list of RuleEditors for its * Rules. @@ -638,29 +656,28 @@ TextProperty.prototype = { * Can mark a property disabled or enabled. */ /** * CssRuleView is a view of the style rules and declarations that * apply to a given element. After construction, the 'element' * property will be available with the user interface. * - * @param Document aDocument + * @param Document aDoc * The document that will contain the rule view. * @param object aStore * The CSS rule view can use this object to store metadata * that might outlast the rule view, particularly the current * set of disabled properties. * @constructor */ function CssRuleView(aDoc, aStore) { this.doc = aDoc; this.store = aStore; - this.element = this.doc.createElementNS(XUL_NS, "vbox"); this.element.setAttribute("tabindex", "0"); this.element.classList.add("ruleview"); this.element.flex = 1; } CssRuleView.prototype = { // The element that we're inspecting. @@ -763,16 +780,24 @@ RuleEditor.prototype = { // span to be placed absolutely against. this.element.style.position = "relative"; // Add the source link. let source = createChild(this.element, "div", { class: "ruleview-rule-source", textContent: this.rule.title }); + source.addEventListener("click", function() { + let rule = this.rule; + let evt = this.doc.createEvent("CustomEvent"); + evt.initCustomEvent("CssRuleViewCSSLinkClicked", true, false, { + rule: rule, + }); + this.element.dispatchEvent(evt); + }.bind(this)); let code = createChild(this.element, "div", { class: "ruleview-code" }); let header = createChild(code, "div", {}); let selectors = createChild(header, "span", { @@ -1089,18 +1114,16 @@ TextPropertyEditor.prototype = { * * @param {string} aValue * The value from the text editor. * @return an object with 'value' and 'priority' properties. */ _parseValue: function TextPropertyEditor_parseValue(aValue) { let pieces = aValue.split("!", 2); - let value = pieces[0]; - let priority = pieces.length > 1 ? pieces[1] : ""; return { value: pieces[0].trim(), priority: (pieces.length > 1 ? pieces[1].trim() : "") }; }, /** * Called when a value editor closes. If the user pressed escape,
--- a/browser/devtools/styleinspector/csshtmltree.xul +++ b/browser/devtools/styleinspector/csshtmltree.xul @@ -109,17 +109,17 @@ To visually debug the templates without <div id="templateMatchedSelectors"> <table> <loop foreach="selector in ${matchedSelectorViews}"> <tr> <td dir="ltr" class="rule-text ${selector.statusClass}"> ${selector.humanReadableText(__element)} </td> <td class="rule-link"> - <a target="_blank" href="view-source:${selector.selectorInfo.href}" class="link" + <a target="_blank" onclick="${selector.openStyleEditor}" class="link" title="${selector.selectorInfo.href}">${selector.selectorInfo.source}</a> </td> </tr> </loop> </table> </div> </div>
--- a/browser/devtools/styleinspector/test/Makefile.in +++ b/browser/devtools/styleinspector/test/Makefile.in @@ -54,20 +54,28 @@ include $(topsrcdir)/config/rules.mk browser_styleinspector_bug_689759_no_results_placeholder.js \ browser_bug_692400_element_style.js \ browser_csslogic_inherited.js \ browser_ruleview_editor.js \ browser_ruleview_inherit.js \ browser_ruleview_manipulation.js \ browser_ruleview_override.js \ browser_ruleview_ui.js \ + browser_bug705707_is_content_stylesheet.js \ head.js \ $(NULL) _BROWSER_TEST_PAGES = \ browser_bug683672.html \ + browser_bug705707_is_content_stylesheet.html \ + browser_bug705707_is_content_stylesheet_imported.css \ + browser_bug705707_is_content_stylesheet_imported2.css \ + browser_bug705707_is_content_stylesheet_linked.css \ + browser_bug705707_is_content_stylesheet_script.css \ + browser_bug705707_is_content_stylesheet.xul \ + browser_bug705707_is_content_stylesheet_xul.css \ $(NULL) libs:: $(_BROWSER_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir) libs:: $(_BROWSER_TEST_PAGES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.html @@ -0,0 +1,33 @@ +<html> +<head> + <title>test</title> + + <link href="./browser_bug705707_is_content_stylesheet_linked.css" rel="stylesheet" type="text/css"> + + <script> + // Load script.css + function loadCSS() { + var link = document.createElement('link'); + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = "./browser_bug705707_is_content_stylesheet_script.css"; + document.getElementsByTagName('head')[0].appendChild(link); + } + </script> + + <style> + table { + border: 1px solid #000; + } + </style> +</head> +<body onload="loadCSS();"> + <table id="target"> + <tr> + <td> + <h3>Simple test</h3> + </td> + </tr> + </table> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.js @@ -0,0 +1,100 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the correct stylesheets origins are identified in HTML & XUL +// stylesheets + +let doc; + +const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" + + "test/browser_bug705707_is_content_stylesheet.html"; +const TEST_URI2 = "http://example.com/browser/browser/devtools/styleinspector/" + + "test/browser_bug705707_is_content_stylesheet.xul"; +const XUL_URI = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService) + .newURI(TEST_URI2, null, null); + +let tempScope = {}; +Cu.import("resource:///modules/devtools/CssLogic.jsm", tempScope); +let CssLogic = tempScope.CssLogic; + +function test() +{ + waitForExplicitFinish(); + addTab(TEST_URI); + browser.addEventListener("load", htmlLoaded, true); +} + +function htmlLoaded() +{ + browser.removeEventListener("load", htmlLoaded, true); + doc = content.document; + testFromHTML() +} + +function testFromHTML() +{ + let target = doc.querySelector("#target"); + + executeSoon(function() { + checkSheets(target); + gBrowser.removeCurrentTab(); + openXUL(); + }); +} + +function openXUL() +{ + Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager) + .add(XUL_URI, 'allowXULXBL', Ci.nsIPermissionManager.ALLOW_ACTION); + addTab(TEST_URI2); + browser.addEventListener("load", xulLoaded, true); +} + +function xulLoaded() +{ + browser.removeEventListener("load", xulLoaded, true); + doc = content.document; + testFromXUL() +} + +function testFromXUL() +{ + let target = doc.querySelector("#target"); + + executeSoon(function() { + checkSheets(target); + finishUp(); + }); +} + +function checkSheets(aTarget) +{ + let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"] + .getService(Ci.inIDOMUtils); + let domRules = domUtils.getCSSStyleRules(aTarget); + + for (let i = 0, n = domRules.Count(); i < n; i++) { + let domRule = domRules.GetElementAt(i); + let sheet = domRule.parentStyleSheet; + let isContentSheet = CssLogic.isContentStylesheet(sheet); + + if (!sheet.href || + /browser_bug705707_is_content_stylesheet_/.test(sheet.href)) { + ok(isContentSheet, sheet.href + " identified as content stylesheet"); + } else { + ok(!isContentSheet, sheet.href + " identified as non-content stylesheet"); + } + } +} + +function finishUp() +{ + info("finishing up"); + Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager) + .add(XUL_URI, 'allowXULXBL', Ci.nsIPermissionManager.DENY_ACTION); + doc = null; + gBrowser.removeCurrentTab(); + finish(); +}
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.xul @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin/xul.css" type="text/css"?> +<?xml-stylesheet href="./browser_bug705707_is_content_stylesheet_xul.css" + type="text/css"?> +<!DOCTYPE window> +<window id="testwindow" xmlns:html="http://www.w3.org/1999/xhtml" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <label id="target" value="Simple XUL document" /> +</window> \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported.css @@ -0,0 +1,5 @@ +@import url("./browser_bug705707_is_content_stylesheet_imported2.css"); + +#target { + text-decoration: underline; +}
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported2.css @@ -0,0 +1,3 @@ +#target { + text-decoration: underline; +}
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_linked.css @@ -0,0 +1,3 @@ +table { + border-collapse: collapse; +}
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_script.css @@ -0,0 +1,5 @@ +@import url("./browser_bug705707_is_content_stylesheet_imported.css"); + +table { + opacity: 1; +}
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_xul.css @@ -0,0 +1,3 @@ +#target { + font-size: 200px; +}
--- a/browser/devtools/webconsole/GcliCommands.jsm +++ b/browser/devtools/webconsole/GcliCommands.jsm @@ -121,16 +121,50 @@ gcli.addCommand({ } ], exec: function Command_inspect(args, context) { let document = context.environment.chromeDocument; document.defaultView.InspectorUI.openInspectorUI(args.node); } }); +/** + * 'edit' command + */ +gcli.addCommand({ + name: "edit", + description: gcli.lookup("editDesc"), + manual: gcli.lookup("editManual"), + params: [ + { + name: 'resource', + type: { + name: 'resource', + include: 'text/css' + }, + description: gcli.lookup("editResourceDesc") + }, + { + name: "line", + defaultValue: 1, + type: { + name: "number", + min: 1, + step: 10 + }, + description: gcli.lookup("editLineToJumpToDesc") + } + ], + exec: function(args, context) { + let hud = HUDService.getHudReferenceById(context.environment.hudId); + let StyleEditor = hud.gcliterm.document.defaultView.StyleEditor; + StyleEditor.openChrome(args.resource.element, args.line); + } +}); + let breakpoints = []; /** * 'break' command */ gcli.addCommand({ name: "break", description: gcli.lookup("breakDesc"),
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties +++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties @@ -124,8 +124,28 @@ breakdelBreakidDesc=Index of breakpoint # LOCALIZATION NOTE (breakdelRemoved) Used in the output of the 'break del' # command to explain that a breakpoint was removed. breakdelRemoved=Breakpoint removed # LOCALIZATION NOTE (consolecloseDesc) A very short description of the # 'console close' command. This string is designed to be shown in a menu # alongside the command name, which is why it should be as short as possible. consolecloseDesc=Close the console + +# LOCALIZATION NOTE (editDesc) A very short description of the 'edit' +# command. See editManual for a fuller description of what it does. This +# string is designed to be shown in a menu alongside the command name, which +# is why it should be as short as possible. +editDesc=Tweak a page resource + +# LOCALIZATION NOTE (editManual) A fuller description of the 'edit' command, +# displayed when the user asks for help on what it does. +editManual=Edit one of the resources that is part of this page (or maybe any generic web resource?) + +# LOCALIZATION NOTE (editResourceDesc) A very short string to describe the +# 'resource' parameter to the 'edit' command, which is displayed in a dialog +# when the user is using this command. +editResourceDesc=URL to edit + +# LOCALIZATION NOTE (editLineToJumpToDesc) A very short string to describe the +# 'line' parameter to the 'edit' command, which is displayed in a dialog +# when the user is using this command. +editLineToJumpToDesc=Line to jump to
--- a/browser/themes/gnomestripe/devtools/csshtmltree.css +++ b/browser/themes/gnomestripe/devtools/csshtmltree.css @@ -63,16 +63,19 @@ color: #0091ff; } .link, .helplink, .link:visited, .helplink:visited { text-decoration: none; } +.link:hover { + text-decoration: underline; +} .helplink { display: block; height: 14px; width: 0; overflow: hidden; -moz-padding-start: 14px; background-image: url("chrome://browser/skin/devtools/goto-mdn.png"); @@ -130,16 +133,17 @@ .property-view-hidden, .property-content-hidden { display: none; } .rule-link { text-align: end; -moz-padding-start: 10px; + cursor: pointer; } /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */ .rule-text { direction: ltr; padding: 0; -moz-padding-start: 20px; } @@ -195,17 +199,23 @@ */ .ruleview { background-color: #FFF; } .ruleview-rule-source { background-color: -moz-dialog; + color: #0091ff; padding: 2px 5px; + cursor: pointer; +} + +.ruleview-rule-source:hover { + text-decoration: underline; } .ruleview-code { padding: 2px 5px; } .ruleview-ruleopen { -moz-padding-end: 5px;
--- a/browser/themes/pinstripe/devtools/csshtmltree.css +++ b/browser/themes/pinstripe/devtools/csshtmltree.css @@ -63,16 +63,19 @@ color: #0091ff; } .link, .helplink, .link:visited, .helplink:visited { text-decoration: none; } +.link:hover { + text-decoration: underline; +} .helplink { display: block; height: 14px; width: 0; overflow: hidden; -moz-padding-start: 14px; background-image: url("chrome://browser/skin/devtools/goto-mdn.png"); @@ -132,16 +135,17 @@ .property-view-hidden, .property-content-hidden { display: none; } .rule-link { text-align: end; -moz-padding-start: 10px; + cursor: pointer; } /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */ .rule-text { direction: ltr; padding: 0; -moz-padding-start: 20px; } @@ -197,17 +201,23 @@ */ .ruleview { background-color: #FFF; } .ruleview-rule-source { background-color: -moz-dialog; + color: #0091ff; padding: 2px 5px; + cursor: pointer; +} + +.ruleview-rule-source:hover { + text-decoration: underline; } .ruleview-code { padding: 2px 5px; } .ruleview-ruleopen { -moz-padding-end: 5px;
--- a/browser/themes/winstripe/browser-aero.css +++ b/browser/themes/winstripe/browser-aero.css @@ -176,19 +176,19 @@ #main-window[sizemode=normal][tabsontop=false] #PersonalToolbar:not(:-moz-lwtheme) { border-top-left-radius: 3.5px; border-top-right-radius: 3.5px; } /* Toolbar shadow behind tabs */ /* This code is only needed for restored windows (i.e. sizemode=normal) because of the border radius on the toolbar below the tab bar. */ - #main-window[sizemode=normal][tabsontop=true] #nav-bar:not(:-moz-lwtheme), - #main-window[sizemode=normal][tabsontop=true] > #nav-bar[collapsed=true]:not([customizing]) + toolbar:not(:-moz-lwtheme), - #main-window[sizemode=normal][tabsontop=true] > #nav-bar[collapsed=true]:not([customizing]) + #customToolbars + #PersonalToolbar:not(:-moz-lwtheme), + #main-window[sizemode=normal] #navigator-toolbox[tabsontop=true] > #nav-bar:not(:-moz-lwtheme), + #main-window[sizemode=normal] #navigator-toolbox[tabsontop=true] > #nav-bar[collapsed=true]:not([customizing]) + toolbar:not(:-moz-lwtheme), + #main-window[sizemode=normal] #navigator-toolbox[tabsontop=true] > #nav-bar[collapsed=true]:not([customizing]) + #customToolbars + #PersonalToolbar:not(:-moz-lwtheme), #main-window[sizemode=normal][tabsontop=true][disablechrome] #navigator-toolbox:not(:-moz-lwtheme)::after { border-top: 1px solid @toolbarShadowColor@; border-top-left-radius: 3.5px; border-top-right-radius: 3.5px; background-clip: padding-box; } #main-window[sizemode=normal] #TabsToolbar[tabsontop=true]:not(:-moz-lwtheme) { margin-bottom: -1px;
--- a/browser/themes/winstripe/devtools/csshtmltree.css +++ b/browser/themes/winstripe/devtools/csshtmltree.css @@ -62,16 +62,19 @@ color: #0091ff; } .link, .helplink, .link:visited, .helplink:visited { text-decoration: none; } +.link:hover { + text-decoration: underline; +} .helplink { display: block; height: 14px; width: 0; overflow: hidden; -moz-padding-start: 14px; background-image: url("chrome://browser/skin/devtools/goto-mdn.png"); @@ -130,16 +133,17 @@ .property-view-hidden, .property-content-hidden { display: none; } .rule-link { text-align: end; -moz-padding-start: 10px; + cursor: pointer; } /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */ .rule-text { direction: ltr; padding: 0; -moz-padding-start: 20px; } @@ -195,17 +199,23 @@ */ .ruleview { background-color: #FFF; } .ruleview-rule-source { background-color: -moz-dialog; + color: #0091ff; padding: 2px 5px; + cursor: pointer; +} + +.ruleview-rule-source:hover { + text-decoration: underline; } .ruleview-code { padding: 2px 5px; } .ruleview-ruleopen { -moz-padding-end: 5px;
--- a/build/Makefile.in +++ b/build/Makefile.in @@ -79,17 +79,17 @@ endif APP_BUILDID := $(shell cat $(DEPTH)/config/buildid) APP_INI_DEPS += $(DEPTH)/config/buildid DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DAPP_BUILDID=$(APP_BUILDID) DEFINES += -DMOZ_APP_VERSION="$(MOZ_APP_VERSION)" APP_INI_DEPS += $(DEPTH)/config/autoconf.mk -MOZ_SOURCE_STAMP ?= $(firstword $(shell hg -R $(topsrcdir)/$(MOZ_BUILD_APP)/.. parent --template="{node|short}\n" 2>/dev/null)) +MOZ_SOURCE_STAMP := $(firstword $(shell cd $(topsrcdir)/$(MOZ_BUILD_APP)/.. && hg parent --template="{node|short}\n" 2>/dev/null)) ifdef MOZ_SOURCE_STAMP DEFINES += -DMOZ_SOURCE_STAMP="$(MOZ_SOURCE_STAMP)" endif _dollar=$$ SOURCE_REPO := $(shell cd $(topsrcdir)/$(MOZ_BUILD_APP)/.. && hg showconfig paths.default 2>/dev/null | head -n1 | sed -e "s/^ssh:/http:/" -e "s/\/$(_dollar)//" ) ifdef SOURCE_REPO DEFINES += -DMOZ_SOURCE_REPO="$(SOURCE_REPO)"
--- a/build/unix/build-toolchain/build-gcc.py +++ b/build/unix/build-toolchain/build-gcc.py @@ -149,17 +149,17 @@ def build_tar_package(tar, name, base, d directory]) ############################################## def build_source_dir(prefix, version): return source_dir + '/' + prefix + version binutils_version = "2.21.1" -glibc_version = "2.7" #FIXME: should probably use 2.5.1 +glibc_version = "2.5.1" tar_version = "1.26" make_version = "3.81" gcc_version = "4.5.2" mpfr_version = "2.4.2" gmp_version = "5.0.1" mpc_version = "0.8.1" binutils_source_uri = "http://ftp.gnu.org/gnu/binutils/binutils-%sa.tar.bz2" % \ @@ -219,16 +219,16 @@ os.makedirs(build_dir) build_aux_tools(build_dir) stage1_dir = build_dir + '/stage1' build_one_stage({"CC": "gcc", "CXX" : "g++"}, stage1_dir, True) stage1_tool_inst_dir = stage1_dir + '/inst' stage2_dir = build_dir + '/stage2' -build_one_stage({"CC" : stage1_tool_inst_dir + "/bin/gcc", +build_one_stage({"CC" : stage1_tool_inst_dir + "/bin/gcc -fgnu89-inline", "CXX" : stage1_tool_inst_dir + "/bin/g++", "AR" : stage1_tool_inst_dir + "/bin/ar", "RANLIB" : "true" }, stage2_dir, False) build_tar_package(aux_inst_dir + "/bin/tar", "toolchain.tar", stage2_dir, "inst")
--- a/build/unix/build-toolchain/glibc-deterministic.patch +++ b/build/unix/build-toolchain/glibc-deterministic.patch @@ -35,17 +35,17 @@ diff -ru a/csu/Makefile b/csu/Makefile - "$$os" "$$version" "`date +%Y-%m-%d`";; \ + ;; \ *) ;; \ esac; \ files="$(all-Banner-files)"; \ diff -ru a/elf/Makefile b/elf/Makefile --- a/elf/Makefile 2008-10-31 16:35:11.000000000 -0400 +++ b/elf/Makefile 2012-02-16 12:20:00.038593752 -0500 -@@ -295,20 +295,13 @@ +@@ -295,18 +295,11 @@ z-now-yes = -Wl,-z,now $(objpfx)ld.so: $(objpfx)librtld.os $(ld-map) - @rm -f $@.lds - $(LINK.o) -nostdlib -nostartfiles -shared $(z-now-$(bind-now)) \ - $(LDFLAGS-rtld) -Wl,-z,defs -Wl,--verbose 2>&1 | \ - LC_ALL=C \ - sed -e '/^=========/,/^=========/!d;/^=========/d' \ @@ -53,22 +53,43 @@ diff -ru a/elf/Makefile b/elf/Makefile - > $@.lds $(LINK.o) -nostdlib -nostartfiles -shared -o $@ \ $(LDFLAGS-rtld) -Wl,-z,defs $(z-now-$(bind-now)) \ $(filter-out $(map-file),$^) $(load-map-file) \ - -Wl,-soname=$(rtld-installed-name) -T $@.lds - rm -f $@.lds + -Wl,-soname=$(rtld-installed-name) \ + -Wl,-defsym=_begin=0 - readelf -s $@ \ -- | awk '($$7 ~ /^UND(|EF)$$/ && $$1 != "0:" && $$4 != "REGISTER") { print; p=1 } END { exit p != 0 }' -+ | $(AWK) '($$7 ~ /^UND(|EF)$$/ && $$1 != "0:" && $$4 != "REGISTER") { print; p=1 } END { exit p != 0 }' # interp.c exists just to get this string into the libraries. CFLAGS-interp.c = -D'RUNTIME_LINKER="$(slibdir)/$(rtld-installed-name)"' \ +diff -ru a/localedata/Makefile b/localedata/Makefile +--- a/localedata/Makefile 2006-04-26 01:14:03.000000000 -0400 ++++ b/localedata/Makefile 2012-02-17 10:31:24.592345047 -0500 +@@ -113,7 +113,7 @@ + $(make-target-directory) + rm -f $(@:.gz=) $@ + $(INSTALL_DATA) $< $(@:.gz=) +- gzip -9 $(@:.gz=) ++ gzip -9n $(@:.gz=) + + # Install the locale source files in the appropriate directory. + $(inst_i18ndir)/locales/%: locales/% $(+force); $(do-install) +diff -ru a/Makeconfig b/Makeconfig +--- a/Makeconfig 2006-07-10 17:42:27.000000000 -0400 ++++ b/Makeconfig 2012-02-17 08:28:31.859584817 -0500 +@@ -674,7 +674,7 @@ + $(foreach lib,$(libof-$(basename $(@F))) \ + $(libof-$(<F)) $(libof-$(@F)),$(CPPFLAGS-$(lib))) \ + $(CPPFLAGS-$(<F)) $(CPPFLAGS-$(@F)) $(CPPFLAGS-$(basename $(@F))) +-override CFLAGS = -std=gnu99 \ ++override CFLAGS = -std=gnu99 -fgnu89-inline \ + $(filter-out %frame-pointer,$(+cflags)) $(+gccwarn-c) \ + $(sysdep-CFLAGS) $(CFLAGS-$(suffix $@)) $(CFLAGS-$(<F)) \ + $(CFLAGS-$(@F)) diff -ru a/Makerules b/Makerules --- a/Makerules 2011-01-17 23:34:07.000000000 -0500 +++ b/Makerules 2012-01-30 08:47:56.565068903 -0500 @@ -977,9 +977,9 @@ echo ' Use the shared library, but some functions are only in';\ echo ' the static library, so try that secondarily. */';\ cat $<; \ - echo 'GROUP ( $(slibdir)/libc.so$(libc.so-version)' \
--- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -2478,21 +2478,20 @@ nsScriptSecurityManager::doGetObjectPrin jsClass = js::GetObjectClass(aObj); } while (1); #ifdef DEBUG if (aAllowShortCircuit) { nsIPrincipal *principal = doGetObjectPrincipal(origObj, false); - // Location is always wrapped (even for same-compartment), so we can - // loosen the check to same-origin instead of same-principal. - NS_ASSERTION(strcmp(jsClass->name, "Location") == 0 ? - NS_SUCCEEDED(CheckSameOriginPrincipal(result, principal)) : - result == principal, + // Because of inner window reuse, we can have objects with one principal + // living in a scope with a different (but same-origin) principal. So + // just check same-origin here. + NS_ASSERTION(NS_SUCCEEDED(CheckSameOriginPrincipal(result, principal)), "Principal mismatch. Not good"); } #endif return result; } nsresult
--- a/client.py +++ b/client.py @@ -1,15 +1,16 @@ #!/usr/bin/python NSPR_DIRS = (('nsprpub', 'mozilla/nsprpub'),) NSS_DIRS = (('dbm', 'mozilla/dbm'), ('security/nss', 'mozilla/security/nss'), ('security/coreconf', 'mozilla/security/coreconf'), ('security/dbm', 'mozilla/security/dbm')) +NSSCKBI_DIRS = (('security/nss/lib/ckfw/builtins', 'mozilla/security/nss/lib/ckfw/builtins'),) LIBFFI_DIRS = (('js/ctypes/libffi', 'libffi'),) CVSROOT_MOZILLA = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot' CVSROOT_LIBFFI = ':pserver:anoncvs@sources.redhat.com:/cvs/libffi' import os import sys import datetime @@ -86,16 +87,23 @@ elif action in ('update_nspr'): do_cvs_export(NSPR_DIRS, tag, options.cvsroot, options.cvs) print >>file("nsprpub/TAG-INFO", "w"), tag elif action in ('update_nss'): tag, = args[1:] if not options.cvsroot: options.cvsroot = os.environ.get('CVSROOT', CVSROOT_MOZILLA) do_cvs_export(NSS_DIRS, tag, options.cvsroot, options.cvs) print >>file("security/nss/TAG-INFO", "w"), tag + print >>file("security/nss/TAG-INFO-CKBI", "w"), tag +elif action in ('update_nssckbi'): + tag, = args[1:] + if not options.cvsroot: + options.cvsroot = os.environ.get('CVSROOT', CVSROOT_MOZILLA) + do_cvs_export(NSSCKBI_DIRS, tag, options.cvsroot, options.cvs) + print >>file("security/nss/TAG-INFO-CKBI", "w"), tag elif action in ('update_libffi'): tag, = args[1:] if not options.cvsroot: options.cvsroot = CVSROOT_LIBFFI do_cvs_export(LIBFFI_DIRS, tag, options.cvsroot, options.cvs) else: o.print_help() sys.exit(2)
--- a/config/milestone.pl +++ b/config/milestone.pl @@ -46,26 +46,27 @@ use vars qw( $MILESTONE_FILE $MILESTONE $MILESTONE_NUM @MILESTONE_PARTS $MINI_VERSION $MICRO_VERSION $opt_debug $opt_template + $opt_uaversion $opt_help ); $SCRIPTDIR = $0; $SCRIPTDIR =~ s/[^\/]*$//; push(@INC,$SCRIPTDIR); require "Moz/Milestone.pm"; -&GetOptions('topsrcdir=s' => \$TOPSRCDIR, 'srcdir=s' => \$SRCDIR, 'objdir=s' => \$OBJDIR, 'debug', 'help', 'template'); +&GetOptions('topsrcdir=s' => \$TOPSRCDIR, 'srcdir=s' => \$SRCDIR, 'objdir=s' => \$OBJDIR, 'debug', 'help', 'template', 'uaversion'); if (defined($opt_help)) { &usage(); exit; } if (defined($opt_template)) { @TEMPLATE_FILE = @ARGV; @@ -95,18 +96,24 @@ if (defined(@TEMPLATE_FILE)) { if (-e $TFILE) { Moz::Milestone::build_file($TFILE,$BUILT_FILE); } else { warn("$0: No such file $TFILE!\n"); } } +} elsif(defined($opt_uaversion)) { + my $uaversion = Moz::Milestone::getMilestoneMajor($milestone) . "." . + Moz::Milestone::getMilestoneMinor($milestone); + # strip off trailing pre-release indicators + $uaversion =~ s/[a-z]+\d*$//; + print "$uaversion\n"; } else { print "$milestone\n"; } sub usage() { print <<END -`milestone.pl [--topsrcdir TOPSRCDIR] [--objdir OBJDIR] [--srcdir SRCDIR] --template [file list]` # will build file list from .tmpl files +`milestone.pl [--topsrcdir TOPSRCDIR] [--objdir OBJDIR] [--srcdir SRCDIR] --template [file list] --uaversion` # will build file list from .tmpl files END ; }
--- a/configure.in +++ b/configure.in @@ -220,24 +220,17 @@ AC_SUBST(COMPILE_ENVIRONMENT) MOZ_ARG_WITH_STRING(l10n-base, [ --with-l10n-base=DIR path to l10n repositories], L10NBASEDIR=$withval) if test -n "$L10NBASEDIR"; then if test "$L10NBASEDIR" = "yes" -o "$L10NBASEDIR" = "no"; then AC_MSG_ERROR([--with-l10n-base must specify a path]) elif test -d "$L10NBASEDIR"; then - case "$host_os" in - mingw*) - L10NBASEDIR=`cd "$L10NBASEDIR" && pwd -W` - ;; - *) - L10NBASEDIR=`cd "$L10NBASEDIR" && pwd` - ;; - esac + L10NBASEDIR=`cd "$L10NBASEDIR" && pwd` else AC_MSG_ERROR([Invalid value --with-l10n-base, $L10NBASEDIR doesn't exist]) fi fi AC_SUBST(L10NBASEDIR) dnl Check for Perl first -- needed for win32 SDK checks MOZ_PATH_PROGS(PERL, $PERL perl5 perl ) @@ -2261,26 +2254,28 @@ if test "$MOZ_CUSTOM_STDINT_H"; then AC_SUBST(MOZ_CUSTOM_STDINT_H) AC_MSG_RESULT(using $MOZ_CUSTOM_STDINT_H) else AC_MSG_RESULT(none specified) fi dnl Get mozilla version from central milestone file MOZILLA_VERSION=`$PERL $srcdir/config/milestone.pl -topsrcdir $srcdir` +MOZILLA_UAVERSION=`$PERL $srcdir/config/milestone.pl -topsrcdir $srcdir -uaversion` dnl Get version of various core apps from the version files. FIREFOX_VERSION=`cat $_topsrcdir/browser/config/version.txt` if test -z "$FIREFOX_VERSION"; then AC_MSG_ERROR([FIREFOX_VERSION is unexpectedly blank.]) fi AC_DEFINE_UNQUOTED(MOZILLA_VERSION,"$MOZILLA_VERSION") AC_DEFINE_UNQUOTED(MOZILLA_VERSION_U,$MOZILLA_VERSION) +AC_DEFINE_UNQUOTED(MOZILLA_UAVERSION,"$MOZILLA_UAVERSION") MOZ_DOING_LTO(lto_is_enabled) dnl ======================================================== dnl System overrides of the defaults for target dnl ======================================================== case "$target" in @@ -4087,16 +4082,66 @@ if test "$ac_cv_thread_keyword" = yes -a : ;; *) AC_DEFINE(HAVE_THREAD_TLS_KEYWORD) ;; esac fi +dnl Using the custom linker on ARMv6 requires 16k alignment of ELF segments. +if test -n "$MOZ_LINKER"; then + if test "$CPU_ARCH" = arm; then + dnl Determine the target ARM architecture (5 for ARMv5, v5T, v5E, etc.; 6 for ARMv6, v6K, etc.) + ARM_ARCH=`${CC-cc} ${CFLAGS} -dM -E - < /dev/null | sed -n 's/.*__ARM_ARCH_\([[0-9]]*\).*/\1/p'` + dnl When building for < ARMv7, we need to ensure 16k alignment of ELF segments + if test -n "$ARM_ARCH" && test "$ARM_ARCH" -lt 7; then + LDFLAGS="$LDFLAGS -Wl,-z,max-page-size=0x4000" + _SUBDIR_LDFLAGS="$_SUBDIR_LDFLAGS -Wl,-z,max-page-size=0x4000" + fi + fi +fi + +dnl The custom linker doesn't support text relocations, but NDK >= r6b +dnl creates some (http://code.google.com/p/android/issues/detail?id=23203) +dnl We however want to avoid these text relocations, and this can be done +dnl by making gcc not link crtbegin and crtend. In the broken NDKs, crtend +dnl doesn't contain anything at all, beside placeholders for some sections, +dnl and crtbegin only contains a finalizer function that calls +dnl __cxa_finalize. The custom linker actually takes care of calling +dnl __cxa_finalize when the library doesn't call it itself, which makes it +dnl safe not to link crtbegin. Besides, previous versions of the NDK didn't +dnl link crtbegin and crtend at all. +if test -n "$MOZ_LINKER" -a -z "$MOZ_OLD_LINKER" -a "$OS_TARGET" = "Android"; then + AC_CACHE_CHECK([whether the CRT objects have text relocations], + ac_cv_crt_has_text_relocations, + [echo 'int foo() { return 0; }' > conftest.cpp + if AC_TRY_COMMAND(${CXX-g++} -o conftest${DLL_SUFFIX} $CXXFLAGS $DSO_LDOPTS $LDFLAGS conftest.cpp $LIBS 1>&5) && + test -s conftest${DLL_SUFFIX}; then + if readelf -d conftest${DLL_SUFFIX} | grep TEXTREL > /dev/null; then + ac_cv_crt_has_text_relocations=yes + else + ac_cv_crt_has_text_relocations=no + fi + else + AC_ERROR([couldn't compile a simple C file]) + fi + rm -rf conftest*]) + if test "$ac_cv_crt_has_text_relocations" = yes; then + dnl While we want libraries to skip the CRT files, we don't want + dnl executables to be treated the same way. We thus set the flag + dnl in DSO_LDOPTS and not LDFLAGS. However, to pass it to nspr, + dnl we need to use LDFLAGS because nspr doesn't inherit DSO_LDOPTS. + dnl Using LDFLAGS in nspr is safe, since we only really build + dnl libraries there. + DSO_LDOPTS="$DSO_LDOPTS -nostartfiles" + NSPR_LDFLAGS=-nostartfiles + fi +fi + dnl Check for the existence of various allocation headers/functions MALLOC_H= MOZ_CHECK_HEADER(malloc.h, [MALLOC_H=malloc.h]) if test "$MALLOC_H" = ""; then MOZ_CHECK_HEADER(malloc/malloc.h, [MALLOC_H=malloc/malloc.h]) if test "$MALLOC_H" = ""; then MOZ_CHECK_HEADER(sys/malloc.h, [MALLOC_H=sys/malloc.h]) @@ -4924,17 +4969,16 @@ cairo-gonk) AC_DEFINE(MOZ_TOUCH) MOZ_WIDGET_TOOLKIT=gonk TK_CFLAGS='$(MOZ_CAIRO_CFLAGS)' TK_LIBS='$(MOZ_CAIRO_LIBS)' MOZ_WEBGL=1 MOZ_PDF_PRINTING=1 MOZ_B2G_RIL=1 MOZ_TOUCH=1 - MOZ_B2G_BT=1 ;; esac AC_SUBST(MOZ_OLD_LINKER) AC_SUBST(MOZ_PDF_PRINTING) if test "$MOZ_PDF_PRINTING"; then PDF_SURFACE_FEATURE="#define CAIRO_HAS_PDF_SURFACE 1" @@ -5477,16 +5521,25 @@ AC_SUBST(MOZ_DBUS_GLIB_LIBS) dnl ======================================================== dnl = Enable Android History instead of Places dnl ======================================================== if test -n "$MOZ_ANDROID_HISTORY"; then dnl Do this if defined in confvars.sh AC_DEFINE(MOZ_ANDROID_HISTORY) fi + +dnl ======================================================== +dnl = Build with the Android Java compositor +dnl ======================================================== +if test -n "$MOZ_JAVA_COMPOSITOR"; then + dnl Do this if defined in confvars.sh + AC_DEFINE(MOZ_JAVA_COMPOSITOR) +fi + dnl ======================================================== dnl = Disable WebSMS backend dnl ======================================================== MOZ_ARG_DISABLE_BOOL(websms-backend, [ --disable-websms-backend Disable WebSMS backend], MOZ_WEBSMS_BACKEND=, MOZ_WEBSMS_BACKEND=1) @@ -9101,17 +9154,21 @@ if test -z "$MOZ_NATIVE_NSPR"; then ac_configure_args="$ac_configure_args --with-arm-kuser" fi if test -n "$MOZ_LINKER" -a -z "$MOZ_OLD_LINKER" -a "$ac_cv_func_dladdr" = no ; then # dladdr is supported by the new linker, even when the system linker doesn't # support it. Trick nspr into using dladdr when it's not supported. _SAVE_CPPFLAGS="$CPPFLAGS" export CPPFLAGS="-include $_topsrcdir/mozglue/linker/dladdr.h $CPPFLAGS" fi + _SAVE_LDFLAGS="$LDFLAGS" + export LDFLAGS="$LDFLAGS $NSPR_LDFLAGS" AC_OUTPUT_SUBDIRS(nsprpub) + unset LDFLAGS + LDFLAGS="$_SAVE_LDFLAGS" if test -n "$MOZ_LINKER" -a -z "$MOZ_OLD_LINKER" -a "$ac_cv_func_dladdr" = no; then unset CPPFLAGS CPPFLAGS="$_SAVE_CFLAGS" fi ac_configure_args="$_SUBDIR_CONFIG_ARGS" fi if test -z "$MOZ_NATIVE_NSPR"; then
--- a/content/base/public/nsIContent.h +++ b/content/base/public/nsIContent.h @@ -915,27 +915,27 @@ public: * If the content is a part of HTML editor, this returns editing * host content. When the content is in designMode, this returns its body * element. Also, when the content isn't editable, this returns null. */ nsIContent* GetEditingHost(); /** * Determing language. Look at the nearest ancestor element that has a lang - * attribute in the XML namespace or is an HTML element and has a lang in + * attribute in the XML namespace or is an HTML/SVG element and has a lang in * no namespace attribute. */ void GetLang(nsAString& aResult) const { for (const nsIContent* content = this; content; content = content->GetParent()) { if (content->GetAttrCount() > 0) { // xml:lang has precedence over lang on HTML elements (see // XHTML1 section C.7). bool hasAttr = content->GetAttr(kNameSpaceID_XML, nsGkAtoms::lang, aResult); - if (!hasAttr && content->IsHTML()) { + if (!hasAttr && (content->IsHTML() || content->IsSVG())) { hasAttr = content->GetAttr(kNameSpaceID_None, nsGkAtoms::lang, aResult); } NS_ASSERTION(hasAttr || aResult.IsEmpty(), "GetAttr that returns false should not make string non-empty"); if (hasAttr) { return; }
--- a/content/base/public/nsIFrameLoader.idl +++ b/content/base/public/nsIFrameLoader.idl @@ -62,17 +62,17 @@ typedef unsigned long long nsContentView * the content document does not (yet) define. * * The view scroll values are in units of chrome-document CSS * pixels. * * These APIs are designed to be used with nsIDOMWindowUtils * setDisplayPort() and setResolution(). */ -[scriptable, uuid(fbd25468-d2cf-487b-bc58-a0e105398b47)] +[scriptable, uuid(c04c5c40-fa2a-4e9c-94f5-b362a10a86cb)] interface nsIContentView : nsISupports { /** * Scroll view to or by the given chrome-document CSS pixels. * Fails if the view is no longer valid. */ void scrollTo(in float xPx, in float yPx); void scrollBy(in float dxPx, in float dyPx); @@ -260,16 +260,23 @@ interface nsIFrameLoader : nsISupports attribute unsigned long eventMode; /** * If false, then the subdocument is not clipped to its CSS viewport, and the * subdocument's viewport scrollbar(s) are not rendered. * Defaults to true. */ attribute boolean clipSubdocument; + + /** + * If false, then the subdocument's scroll coordinates will not be clamped + * to their scroll boundaries. + * Defaults to true. + */ + attribute boolean clampScrollPosition; }; native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>); [scriptable, uuid(5879040e-83e9-40e3-b2bb-5ddf43b76e47)] interface nsIFrameLoaderOwner : nsISupports { /**
--- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -306,34 +306,28 @@ public: NS_DECL_DOM_MEMORY_REPORTER_SIZEOF friend class nsNodeUtils; friend class nsNodeWeakReference; friend class nsNodeSupportsWeakRefTearoff; friend class nsAttrAndChildArray; #ifdef MOZILLA_INTERNAL_API - static nsINode *sOrphanNodeHead; - nsINode(already_AddRefed<nsINodeInfo> aNodeInfo) : mNodeInfo(aNodeInfo), mParent(nsnull), mFlags(0), - mBoolFlags(1 << NodeIsOrphan), - mNextOrphanNode(sOrphanNodeHead->mNextOrphanNode), - mPreviousOrphanNode(sOrphanNodeHead), + mBoolFlags(0), + mNextSibling(nsnull), + mPreviousSibling(nsnull), mFirstChild(nsnull), mSlots(nsnull) { - NS_ASSERTION(GetBoolFlag(NodeIsOrphan), - "mBoolFlags not initialized correctly!"); + } - mNextOrphanNode->mPreviousOrphanNode = this; - sOrphanNodeHead->mNextOrphanNode = this; - } #endif virtual ~nsINode(); /** * Bit-flags to pass (or'ed together) to IsNodeOfType() */ enum { @@ -1104,73 +1098,18 @@ public: return NS_OK; } nsresult LookupNamespaceURI(const nsAString& aNamespacePrefix, nsAString& aNamespaceURI); nsresult IsEqualNode(nsIDOMNode* aOther, bool* aReturn); bool IsEqualTo(nsINode* aOther); - nsIContent* GetNextSibling() const - { - return NS_UNLIKELY(IsOrphan()) ? nsnull : mNextSibling; - } - - nsIContent* GetPreviousSibling() const - { - return NS_UNLIKELY(IsOrphan()) ? nsnull : mPreviousSibling; - } - - // Returns true if this node is an orphan node - bool IsOrphan() const - { -#ifdef MOZILLA_INTERNAL_API - NS_ASSERTION(this != sOrphanNodeHead, "Orphan node head orphan check?!"); -#endif - - return GetBoolFlag(NodeIsOrphan); - } - -#ifdef MOZILLA_INTERNAL_API - // Mark this node as an orphan node. This marking is only relevant - // for this node itself, not its children. Its children are not - // considered orphan until they themselves are removed from their - // parent and get marked as orphans. - void MarkAsOrphan() - { - NS_ASSERTION(!IsOrphan(), "Orphan node orphaned again?"); - NS_ASSERTION(this != sOrphanNodeHead, "Orphan node head orphaned?!"); - - mNextOrphanNode = sOrphanNodeHead->mNextOrphanNode; - mPreviousOrphanNode = sOrphanNodeHead; - mNextOrphanNode->mPreviousOrphanNode = this; - sOrphanNodeHead->mNextOrphanNode = this; - - SetBoolFlag(NodeIsOrphan); - } - - // Unmark this node as an orphan node. Do this before inserting this - // node into a parent or otherwise associating it with some other - // owner. - void MarkAsNonOrphan() - { - NS_ASSERTION(IsOrphan(), "Non-orphan node un-orphaned"); - NS_ASSERTION(this != sOrphanNodeHead, "Orphan node head unorphaned?!"); - NS_ASSERTION(!mParent, "Must not have a parent here!"); - - mPreviousOrphanNode->mNextOrphanNode = mNextOrphanNode; - mNextOrphanNode->mPreviousOrphanNode = mPreviousOrphanNode; - mPreviousOrphanNode = nsnull; - mNextOrphanNode = nsnull; - - ClearBoolFlag(NodeIsOrphan); - } -#endif - - static void Init(); + nsIContent* GetNextSibling() const { return mNextSibling; } + nsIContent* GetPreviousSibling() const { return mPreviousSibling; } /** * Get the next node in the pre-order tree traversal of the DOM. If * aRoot is non-null, then it must be an ancestor of |this| * (possibly equal to |this|) and only nodes that are descendants of * aRoot, not including aRoot itself, will be returned. Returns * null if there are no more nodes to traverse. */ @@ -1307,18 +1246,16 @@ private: NodeIsCCBlackTree, // Maybe set if the node is a root of a subtree // which needs to be kept in the purple buffer. NodeIsPurpleRoot, // Set if the node has an explicit base URI stored NodeHasExplicitBaseURI, // Set if the element has some style states locked ElementHasLockedStyleStates, - // Set if the node is an orphan node. - NodeIsOrphan, // Guard value BooleanFlagCount }; void SetBoolFlag(BooleanFlag name, bool value) { PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags)); mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name); } @@ -1524,34 +1461,18 @@ protected: PRUint32 mFlags; private: // Boolean flags. PRUint32 mBoolFlags; protected: - union { - // mNextSibling is used when this node is part of a DOM tree - nsIContent* mNextSibling; - - // mNextOrphanNode is used when this is in the linked list of - // orphan nodes. - nsINode *mNextOrphanNode; - }; - - union { - // mPreviousSibling is used when this node is part of a DOM tree - nsIContent* mPreviousSibling; - - // mPreviousOrphanNode is used when this is in the linked list of - // orphan nodes. - nsINode* mPreviousOrphanNode; - }; - + nsIContent* mNextSibling; + nsIContent* mPreviousSibling; nsIContent* mFirstChild; // Storage for more members that are usually not needed; allocated lazily. nsSlots* mSlots; }; extern const nsIID kThisPtrOffsetsSID;
--- a/content/base/public/nsIXMLHttpRequest.idl +++ b/content/base/public/nsIXMLHttpRequest.idl @@ -178,19 +178,19 @@ interface nsIXMLHttpRequest : nsISupport /** * Returns all of the response headers as a string for HTTP * requests. * * Note that this will return all the headers from the *current* * part of a multipart request, not from the original channel. * * @returns A string containing all of the response headers. - * NULL if the response has not yet been received. + * The empty string if the response has not yet been received. */ - string getAllResponseHeaders(); + DOMString getAllResponseHeaders(); /** * Returns the text of the header with the specified name for * HTTP requests. * * @param header The name of the header to retrieve * @returns A string containing the text of the header specified. * NULL if the response has not yet been received or the
--- a/content/base/src/mozSanitizingSerializer.cpp +++ b/content/base/src/mozSanitizingSerializer.cpp @@ -549,20 +549,19 @@ mozSanitizingHTMLSerializer::IsAllowedAt #endif nsresult rv; nsPRUint32Key tag_key(aTag); nsIProperties* attr_bag = (nsIProperties*)mAllowedTags.Get(&tag_key); NS_ENSURE_TRUE(attr_bag, false); bool allowed; - nsAutoString attr(anAttributeName); - ToLowerCase(attr); - rv = attr_bag->Has(NS_LossyConvertUTF16toASCII(attr).get(), - &allowed); + nsCAutoString attr; + ToLowerCase(NS_ConvertUTF16toUTF8(anAttributeName), attr); + rv = attr_bag->Has(attr.get(), &allowed); if (NS_FAILED(rv)) return false; #ifdef DEBUG_BenB printf(" Allowed: %s\n", allowed?"yes":"no"); #endif return allowed; }
--- a/content/base/src/nsAttrAndChildArray.cpp +++ b/content/base/src/nsAttrAndChildArray.cpp @@ -227,29 +227,23 @@ nsAttrAndChildArray::RemoveChildAt(PRUin already_AddRefed<nsIContent> nsAttrAndChildArray::TakeChildAt(PRUint32 aPos) { NS_ASSERTION(aPos < ChildCount(), "out-of-bounds"); PRUint32 childCount = ChildCount(); void** pos = mImpl->mBuffer + AttrSlotsSize() + aPos; nsIContent* child = static_cast<nsIContent*>(*pos); - - MOZ_ASSERT(!child->IsOrphan(), "Child should not be an orphan here"); - if (child->mPreviousSibling) { child->mPreviousSibling->mNextSibling = child->mNextSibling; } if (child->mNextSibling) { child->mNextSibling->mPreviousSibling = child->mPreviousSibling; } - - // Mark the child as an orphan now that it's no longer associated - // with its old parent. - child->MarkAsOrphan(); + child->mPreviousSibling = child->mNextSibling = nsnull; memmove(pos, pos + 1, (childCount - aPos - 1) * sizeof(nsIContent*)); SetChildCount(childCount - 1); return child; } PRInt32 @@ -658,31 +652,28 @@ nsAttrAndChildArray::Clear() nsAutoScriptBlocker scriptBlocker; PRUint32 end = slotCount * ATTRSIZE + ChildCount(); for (i = slotCount * ATTRSIZE; i < end; ++i) { nsIContent* child = static_cast<nsIContent*>(mImpl->mBuffer[i]); // making this false so tree teardown doesn't end up being // O(N*D) (number of nodes times average depth of tree). child->UnbindFromTree(false); // XXX is it better to let the owner do this? - // Mark the child as an orphan now that it's no longer a child of - // its old parent, and make sure to unlink our kids from each - // other, since someone else could stil be holding references to - // some of them. - - child->MarkAsOrphan(); + // Make sure to unlink our kids from each other, since someone + // else could stil be holding references to some of them. // XXXbz We probably can't push this assignment down into the |aNullParent| // case of UnbindFromTree because we still need the assignment in // RemoveChildAt. In particular, ContentRemoved fires between // RemoveChildAt and UnbindFromTree, and in ContentRemoved the sibling // chain needs to be correct. Though maybe we could set the prev and next // to point to each other but keep the kid being removed pointing to them // through ContentRemoved so consumers can find where it used to be in the // list? + child->mPreviousSibling = child->mNextSibling = nsnull; NS_RELEASE(child); } SetAttrSlotAndChildCount(0, 0); } PRUint32 nsAttrAndChildArray::NonMappedAttrCount() const @@ -826,26 +817,18 @@ nsAttrAndChildArray::AddAttrSlot() return true; } inline void nsAttrAndChildArray::SetChildAtPos(void** aPos, nsIContent* aChild, PRUint32 aIndex, PRUint32 aChildCount) { - MOZ_ASSERT(aChild->IsOrphan(), "aChild should be an orphan here"); - - NS_PRECONDITION(aChild->IsOrphan() || !aChild->GetNextSibling(), - "aChild should be orphan and have no next sibling!"); - NS_PRECONDITION(aChild->IsOrphan() || !aChild->GetPreviousSibling(), - "aChild should be orphan and have no prev sibling!"); - - // Unmark this child as an orphan now that it's a child of its new - // parent. - aChild->MarkAsNonOrphan(); + NS_PRECONDITION(!aChild->GetNextSibling(), "aChild with next sibling?"); + NS_PRECONDITION(!aChild->GetPreviousSibling(), "aChild with prev sibling?"); *aPos = aChild; NS_ADDREF(aChild); if (aIndex != 0) { nsIContent* previous = static_cast<nsIContent*>(*(aPos - 1)); aChild->mPreviousSibling = previous; previous->mNextSibling = aChild; }
--- a/content/base/src/nsContentAreaDragDrop.cpp +++ b/content/base/src/nsContentAreaDragDrop.cpp @@ -43,16 +43,17 @@ // Helper Classes #include "nsString.h" // Interfaces needed to be included #include "nsCopySupport.h" #include "nsIDOMUIEvent.h" #include "nsISelection.h" +#include "nsISelectionController.h" #include "nsIDOMNode.h" #include "nsIDOMNodeList.h" #include "nsIDOMEvent.h" #include "nsIDOMNSEvent.h" #include "nsIDOMDragEvent.h" #include "nsPIDOMWindow.h" #include "nsIDOMDocument.h" #include "nsIDOMRange.h" @@ -65,71 +66,41 @@ #include "nsISupportsPrimitives.h" #include "nsServiceManagerUtils.h" #include "nsNetUtil.h" #include "nsIFile.h" #include "nsIWebNavigation.h" #include "nsIDocShell.h" #include "nsIContent.h" #include "nsIImageLoadingContent.h" +#include "nsITextControlElement.h" #include "nsUnicharUtils.h" #include "nsIURL.h" #include "nsIDocument.h" #include "nsIScriptSecurityManager.h" #include "nsIPrincipal.h" #include "nsIDocShellTreeItem.h" #include "nsIWebBrowserPersist.h" #include "nsEscape.h" #include "nsContentUtils.h" #include "nsIMIMEService.h" #include "imgIContainer.h" #include "imgIRequest.h" #include "nsDOMDataTransfer.h" -// private clipboard data flavors for html copy, used by editor when pasting -#define kHTMLContext "text/_moz_htmlcontext" -#define kHTMLInfo "text/_moz_htmlinfo" - -// if aNode is null, use the selection from the window -static nsresult -GetTransferableForNodeOrSelection(nsIDOMWindow* aWindow, - nsIContent* aNode, - nsITransferable** aTransferable) -{ - NS_ENSURE_ARG_POINTER(aWindow); - - nsCOMPtr<nsIDOMDocument> domDoc; - aWindow->GetDocument(getter_AddRefs(domDoc)); - NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); - nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc); - - nsresult rv; - if (aNode) { - rv = nsCopySupport::GetTransferableForNode(aNode, doc, aTransferable); - } else { - nsCOMPtr<nsISelection> selection; - aWindow->GetSelection(getter_AddRefs(selection)); - rv = nsCopySupport::GetTransferableForSelection(selection, doc, - aTransferable); - } - - NS_ENSURE_SUCCESS(rv, rv); - return rv; -} - class NS_STACK_CLASS DragDataProducer { public: DragDataProducer(nsIDOMWindow* aWindow, nsIContent* aTarget, nsIContent* aSelectionTargetNode, bool aIsAltKeyPressed); nsresult Produce(nsDOMDataTransfer* aDataTransfer, bool* aCanDrag, - bool* aDragSelection, + nsISelection** aSelection, nsIContent** aDragNode); private: void AddString(nsDOMDataTransfer* aDataTransfer, const nsAString& aFlavor, const nsAString& aData, nsIPrincipal* aPrincipal); nsresult AddStringsToDataTransfer(nsIContent* aDragNode, @@ -167,26 +138,26 @@ private: nsresult nsContentAreaDragDrop::GetDragData(nsIDOMWindow* aWindow, nsIContent* aTarget, nsIContent* aSelectionTargetNode, bool aIsAltKeyPressed, nsDOMDataTransfer* aDataTransfer, bool* aCanDrag, - bool* aDragSelection, + nsISelection** aSelection, nsIContent** aDragNode) { NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG); *aCanDrag = true; DragDataProducer provider(aWindow, aTarget, aSelectionTargetNode, aIsAltKeyPressed); - return provider.Produce(aDataTransfer, aCanDrag, aDragSelection, aDragNode); + return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode); } NS_IMPL_ISUPPORTS1(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider) // SaveURIToFile // used on platforms where it's possible to drag items (e.g. images) // into the file system @@ -407,55 +378,94 @@ DragDataProducer::GetNodeString(nsIConte range->ToString(outNodeString); } } } nsresult DragDataProducer::Produce(nsDOMDataTransfer* aDataTransfer, bool* aCanDrag, - bool* aDragSelection, + nsISelection** aSelection, nsIContent** aDragNode) { - NS_PRECONDITION(aCanDrag && aDragSelection && aDataTransfer && aDragNode, + NS_PRECONDITION(aCanDrag && aSelection && aDataTransfer && aDragNode, "null pointer passed to Produce"); NS_ASSERTION(mWindow, "window not set"); NS_ASSERTION(mSelectionTargetNode, "selection target node should have been set"); *aDragNode = nsnull; nsresult rv; nsIContent* dragNode = nsnull; - - // find the selection to see what we could be dragging and if - // what we're dragging is in what is selected. - nsCOMPtr<nsISelection> selection; - mWindow->GetSelection(getter_AddRefs(selection)); - if (!selection) { - return NS_OK; - } + *aSelection = nsnull; - // check if the node is inside a form control. If so, dragging will be - // handled in editor code (nsPlaintextDataTransfer::DoDrag). Don't set - // aCanDrag to false however, as we still want to allow the drag. - nsCOMPtr<nsIContent> findFormNode = mSelectionTargetNode; - nsIContent* findFormParent = findFormNode->GetParent(); - while (findFormParent) { - nsCOMPtr<nsIFormControl> form(do_QueryInterface(findFormParent)); - if (form && !form->AllowDraggableChildren()) { + // Find the selection to see what we could be dragging and if what we're + // dragging is in what is selected. If this is an editable textbox, use + // the textbox's selection, otherwise use the window's selection. + 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; + } + } + 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. + nsCOMPtr<nsIContent> findFormNode = mSelectionTargetNode; + nsIContent* findFormParent = findFormNode->GetParent(); + while (findFormParent) { + nsCOMPtr<nsIFormControl> form(do_QueryInterface(findFormParent)); + if (form && !form->AllowDraggableChildren()) { + return NS_OK; + } + findFormParent = findFormParent->GetParent(); } - findFormParent = findFormParent->GetParent(); } // if set, serialize the content under this node nsCOMPtr<nsIContent> nodeToSerialize; - *aDragSelection = false; + + bool isChromeShell = false; + nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(mWindow); + nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav); + if (dsti) { + PRInt32 type = -1; + if (NS_SUCCEEDED(dsti->GetItemType(&type)) && + type == nsIDocShellTreeItem::typeChrome) { + isChromeShell = true; + } + } - { + // 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); + } + 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; // possible parent link node nsCOMPtr<nsIContent> parentLink; nsCOMPtr<nsIContent> draggedNode; { // only drag form elements by using the alt key, @@ -485,17 +495,17 @@ DragDataProducer::Produce(nsDOMDataTrans if (haveSelectedContent) { link = do_QueryInterface(selectedImageOrLinkNode); if (link && mIsAltKeyPressed) { // if alt is pressed, select the link text instead of drag the link *aCanDrag = false; return NS_OK; } - *aDragSelection = true; + selection.swap(*aSelection); } else if (selectedImageOrLinkNode) { // an image is selected image = do_QueryInterface(selectedImageOrLinkNode); } else { // nothing is selected - // // look for draggable elements under the mouse // @@ -655,30 +665,38 @@ DragDataProducer::Produce(nsDOMDataTrans if (linkNode) { mIsAnchor = true; GetAnchorURL(linkNode, mUrlString); dragNode = linkNode; } } } - if (nodeToSerialize || *aDragSelection) { - // if we have selected text, use it in preference to the node - if (*aDragSelection) { - nodeToSerialize = nsnull; - } - + if (nodeToSerialize || *aSelection) { mHtmlString.Truncate(); mContextString.Truncate(); mInfoString.Truncate(); mTitleString.Truncate(); + + nsCOMPtr<nsIDOMDocument> domDoc; + mWindow->GetDocument(getter_AddRefs(domDoc)); + nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc); + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); + + // if we have selected text, use it in preference to the node nsCOMPtr<nsITransferable> transferable; - rv = ::GetTransferableForNodeOrSelection(mWindow, nodeToSerialize, - getter_AddRefs(transferable)); - NS_ENSURE_SUCCESS(rv, rv); + if (*aSelection) { + rv = nsCopySupport::GetTransferableForSelection(*aSelection, doc, + getter_AddRefs(transferable)); + } + else { + rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc, + getter_AddRefs(transferable)); + } + nsCOMPtr<nsISupportsString> data; PRUint32 dataSize; rv = transferable->GetTransferData(kHTMLMime, getter_AddRefs(data), &dataSize); if (NS_SUCCEEDED(rv)) { data->GetData(mHtmlString); } rv = transferable->GetTransferData(kHTMLContext, getter_AddRefs(data), &dataSize); if (NS_SUCCEEDED(rv)) { @@ -742,25 +760,27 @@ DragDataProducer::AddStringsToDataTransf dragData += mTitleString; AddString(aDataTransfer, NS_LITERAL_STRING(kURLMime), dragData, principal); AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal); AddString(aDataTransfer, NS_LITERAL_STRING(kURLDescriptionMime), mTitleString, principal); AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal); } - // add a special flavor, even if we don't have html context data - AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal); + // add a special flavor for the html context data + if (!mContextString.IsEmpty()) + AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal); // add a special flavor if we have html info data if (!mInfoString.IsEmpty()) AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), mInfoString, principal); // add the full html - AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal); + if (!mHtmlString.IsEmpty()) + AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal); // add the plain text. we use the url for text/plain data if an anchor is // being dragged, rather than the title text of the link or the alt text for // an anchor image. AddString(aDataTransfer, NS_LITERAL_STRING(kTextMime), mIsAnchor ? mUrlString : mTitleString, principal); // add image data, if present. For now, all we're going to do with
--- a/content/base/src/nsContentAreaDragDrop.h +++ b/content/base/src/nsContentAreaDragDrop.h @@ -73,28 +73,28 @@ public: * aSelectionTargetNode - the node where the drag event should be fired * aIsAltKeyPressed - true if the Alt key is pressed. In some cases, this * will prevent the drag from occuring. For example, * holding down Alt over a link should select the text, * not drag the link. * aDataTransfer - the dataTransfer for the drag event. * aCanDrag - [out] set to true if the drag may proceed, false to stop the * drag entirely - * aDragSelection - [out] set to true to indicate that a selection is being - * dragged, rather than a specific node + * aSelection - [out] set to the selection being dragged, or null if no + * selection is being dragged. * aDragNode - [out] the link, image or area being dragged, or null if the * drag occurred on another element. */ static nsresult GetDragData(nsIDOMWindow* aWindow, nsIContent* aTarget, nsIContent* aSelectionTargetNode, bool aIsAltKeyPressed, nsDOMDataTransfer* aDataTransfer, bool* aCanDrag, - bool* aDragSelection, + nsISelection** aSelection, nsIContent** aDragNode); }; // this is used to save images to disk lazily when the image data is asked for // during the drop instead of when it is added to the drag data transfer. This // ensures that the image data is only created when an image drop is allowed. class nsContentAreaDragDropDataProvider : public nsIFlavorDataProvider {
--- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -357,18 +357,16 @@ nsresult nsContentUtils::Init() { if (sInitialized) { NS_WARNING("Init() called twice"); return NS_OK; } - nsINode::Init(); - nsresult rv = NS_GetNameSpaceManager(&sNameSpaceManager); NS_ENSURE_SUCCESS(rv, rv); nsXPConnect* xpconnect = nsXPConnect::GetXPConnect(); NS_ENSURE_TRUE(xpconnect, NS_ERROR_FAILURE); sXPConnect = xpconnect; sThreadJSContextStack = xpconnect;
--- a/content/base/src/nsCopySupport.cpp +++ b/content/base/src/nsCopySupport.cpp @@ -86,20 +86,16 @@ using namespace mozilla; nsresult NS_NewDomSelection(nsISelection **aDomSelection); static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); static NS_DEFINE_CID(kHTMLConverterCID, NS_HTMLFORMATCONVERTER_CID); -// private clipboard data flavors for html copy, used by editor when pasting -#define kHTMLContext "text/_moz_htmlcontext" -#define kHTMLInfo "text/_moz_htmlinfo" - // copy string data onto the transferable static nsresult AppendString(nsITransferable *aTransferable, const nsAString& aString, const char* aFlavor); // copy HTML node data static nsresult AppendDOMNode(nsITransferable *aTransferable, nsIDOMNode *aDOMNode);
--- a/content/base/src/nsDOMAttribute.cpp +++ b/content/base/src/nsDOMAttribute.cpp @@ -86,17 +86,16 @@ nsDOMAttribute::nsDOMAttribute(nsDOMAttr content->AddMutationObserver(this); } } nsDOMAttribute::~nsDOMAttribute() { if (mChild) { static_cast<nsTextNode*>(mChild)->UnbindFromAttribute(); - mChild->MarkAsOrphan(); NS_RELEASE(mChild); mFirstChild = nsnull; } nsIContent* content = GetContentInternal(); if (content) { content->RemoveMutationObserver(this); } @@ -117,17 +116,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMAttribute) nsINode::Trace(tmp, aCallback, aClosure); NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttribute) nsINode::Unlink(tmp); if (tmp->mChild) { static_cast<nsTextNode*>(tmp->mChild)->UnbindFromAttribute(); - tmp->mChild->MarkAsOrphan(); NS_RELEASE(tmp->mChild); tmp->mFirstChild = nsnull; } NS_IMPL_CYCLE_COLLECTION_UNLINK_END DOMCI_NODE_DATA(Attr, nsDOMAttribute) // QueryInterface implementation for nsDOMAttribute @@ -723,17 +721,16 @@ nsDOMAttribute::EnsureChildState() NS_PRECONDITION(!mChild, "Someone screwed up"); nsAutoString value; GetValue(value); if (!value.IsEmpty()) { NS_NewTextNode(&mChild, mNodeInfo->NodeInfoManager()); - mChild->MarkAsNonOrphan(); static_cast<nsTextNode*>(mChild)->BindToAttribute(this); mFirstChild = mChild; mChild->SetText(value, false); } } void @@ -791,11 +788,10 @@ nsDOMAttribute::doRemoveChild(bool aNoti NS_RELEASE(mChild); mFirstChild = nsnull; if (aNotify) { nsNodeUtils::AttributeChildRemoved(this, child); } child->UnbindFromAttribute(); - child->MarkAsOrphan(); }
--- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -854,17 +854,17 @@ TransferZoomLevels(nsIDocument* aFromDoc if (!toShell) return; nsPresContext* toCtxt = toShell->GetPresContext(); if (!toCtxt) return; toCtxt->SetFullZoom(fromCtxt->GetFullZoom()); - toCtxt->SetMinFontSize(fromCtxt->MinFontSize()); + toCtxt->SetMinFontSize(fromCtxt->MinFontSize(nsnull)); toCtxt->SetTextZoom(fromCtxt->TextZoom()); } void TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc) { NS_ABORT_IF_FALSE(aFromDoc && aToDoc, "transferring showing state from/to null doc"); @@ -1574,18 +1574,17 @@ nsDocument::~nsDocument() PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG, ("DOCUMENT %p destroyed", this)); #endif #ifdef DEBUG nsCycleCollector_DEBUG_wasFreed(static_cast<nsIDocument*>(this)); #endif - NS_ASSERTION(!mIsShowing, "Deleting a currently-showing document"); - NS_ASSERTION(IsOrphan(), "Deleted document not an orphan?"); + NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document"); mInDestructor = true; mInUnlinkOrDeletion = true; // Clear mObservers to keep it in sync with the mutationobserver list mObservers.Clear(); if (mStyleSheetSetList) { @@ -2114,17 +2113,16 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI #ifdef PR_LOGGING if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) { nsCAutoString spec; aURI->GetSpec(spec); PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get()); } #endif - SetPrincipal(nsnull); mSecurityInfo = nsnull; mDocumentLoadGroup = nsnull; // Delete references to sub-documents and kill the subdocument map, // if any. It holds strong references if (mSubDocuments) { PL_DHashTableDestroy(mSubDocuments); @@ -2164,16 +2162,22 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI if (mListenerManager) { mListenerManager->Disconnect(); mListenerManager = nsnull; } // Release the stylesheets list. mDOMStyleSheets = nsnull; + // Release our principal after tearing down the document, rather than before. + // This ensures that, during teardown, the document and the dying window (which + // already nulled out its document pointer and cached the principal) have + // matching principals. + SetPrincipal(nsnull); + // Clear the original URI so SetDocumentURI sets it. mOriginalURI = nsnull; SetDocumentURI(aURI); // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns // mDocumentURI. mDocumentBaseURI = nsnull; @@ -5124,21 +5128,19 @@ nsDocument::CreateTreeWalker(nsIDOMNode return NS_OK; } NS_IMETHODIMP nsDocument::GetDefaultView(nsIDOMWindow** aDefaultView) { *aDefaultView = nsnull; - nsPIDOMWindow* win = GetWindow(); - if (!win) { - return NS_OK; - } - return CallQueryInterface(win, aDefaultView); + nsCOMPtr<nsPIDOMWindow> win = GetWindow(); + win.forget(aDefaultView); + return NS_OK; } NS_IMETHODIMP nsDocument::GetLocation(nsIDOMLocation **_retval) { NS_ENSURE_ARG_POINTER(_retval); *_retval = nsnull; @@ -8111,17 +8113,17 @@ nsIDocument::ScheduleFrameRequestCallbac { if (mFrameRequestCallbackCounter == PR_INT32_MAX) { // Can't increment without overflowing; bail out return NS_ERROR_NOT_AVAILABLE; } PRInt32 newHandle = ++mFrameRequestCallbackCounter; bool alreadyRegistered = !mFrameRequestCallbacks.IsEmpty(); - FrameRequest *request = + DebugOnly<FrameRequest*> request = mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle)); NS_ASSERTION(request, "This is supposed to be infallible!"); if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) { mPresShell->GetPresContext()->RefreshDriver()-> ScheduleFrameRequestCallbacks(this); } *aHandle = newHandle; @@ -8294,21 +8296,18 @@ nsDocument::NotifyAudioAvailableListener } nsresult nsDocument::RemoveImage(imgIRequest* aImage) { NS_ENSURE_ARG_POINTER(aImage); // Get the old count. It should exist and be > 0. - PRUint32 count; -#ifdef DEBUG - bool found = -#endif - mImageTracker.Get(aImage, &count); + PRUint32 count = 0; + DebugOnly<bool> found = mImageTracker.Get(aImage, &count); NS_ABORT_IF_FALSE(found, "Removing image that wasn't in the tracker!"); NS_ABORT_IF_FALSE(count > 0, "Entry in the cache tracker with count 0!"); // We're removing, so decrement the count. count--; // If the count is now zero, remove from the tracker. // Otherwise, set the new value. @@ -8522,17 +8521,17 @@ nsDocument::MozCancelFullScreen() class nsSetWindowFullScreen : public nsRunnable { public: nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue) : mDoc(aDoc), mValue(aValue) {} NS_IMETHOD Run() { if (mDoc->GetWindow()) { - mDoc->GetWindow()->SetFullScreen(mValue); + mDoc->GetWindow()->SetFullScreenInternal(mValue, false); } return NS_OK; } private: nsCOMPtr<nsIDocument> mDoc; bool mValue; };
--- a/content/base/src/nsFrameLoader.cpp +++ b/content/base/src/nsFrameLoader.cpp @@ -71,16 +71,17 @@ #include "nsIJSContextStack.h" #include "nsUnicharUtils.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptSecurityManager.h" #include "nsIScrollable.h" #include "nsFrameLoader.h" #include "nsIDOMEventTarget.h" #include "nsIFrame.h" +#include "nsIScrollableFrame.h" #include "nsSubDocumentFrame.h" #include "nsDOMError.h" #include "nsGUIEvent.h" #include "nsEventDispatcher.h" #include "nsISHistory.h" #include "nsISHistoryInternal.h" #include "nsIDocShellHistory.h" #include "nsIDOMHTMLDocument.h" @@ -325,16 +326,17 @@ nsFrameLoader::nsFrameLoader(Element* aO , mInSwap(false) , mInShow(false) , mHideCalled(false) , mNetworkCreated(aNetworkCreated) , mDelayRemoteDialogs(false) , mRemoteBrowserShown(false) , mRemoteFrame(false) , mClipSubdocument(true) + , mClampScrollPosition(true) , mCurrentRemoteFrame(nsnull) , mRemoteBrowser(nsnull) , mRenderMode(RENDER_MODE_DEFAULT) , mEventMode(EVENT_MODE_NORMAL_DISPATCH) { } nsFrameLoader* @@ -1750,16 +1752,48 @@ nsFrameLoader::SetClipSubdocument(bool a FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); } } } } return NS_OK; } +NS_IMETHODIMP +nsFrameLoader::GetClampScrollPosition(bool* aResult) +{ + *aResult = mClampScrollPosition; + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::SetClampScrollPosition(bool aClamp) +{ + mClampScrollPosition = aClamp; + + // When turning clamping on, make sure the current position is clamped. + if (aClamp) { + nsIFrame* frame = GetPrimaryFrameOfOwningContent(); + if (frame) { + nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame); + if (subdocFrame) { + nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame(); + if (subdocRootFrame) { + nsIScrollableFrame* subdocRootScrollFrame = subdocRootFrame->PresContext()->PresShell()-> + GetRootScrollFrameAsScrollable(); + if (subdocRootScrollFrame) { + subdocRootScrollFrame->ScrollTo(subdocRootScrollFrame->GetScrollPosition(), nsIScrollableFrame::INSTANT); + } + } + } + } + } + return NS_OK; +} + nsIntSize nsFrameLoader::GetSubDocumentSize(const nsIFrame *aIFrame) { nsSize docSizeAppUnits; nsPresContext* presContext = aIFrame->PresContext(); nsCOMPtr<nsIDOMHTMLFrameElement> frameElem = do_QueryInterface(aIFrame->GetContent()); if (frameElem) {
--- a/content/base/src/nsFrameLoader.h +++ b/content/base/src/nsFrameLoader.h @@ -284,16 +284,18 @@ public: } nsFrameMessageManager* GetFrameMessageManager() { return mMessageManager; } mozilla::dom::Element* GetOwnerContent() { return mOwnerContent; } void SetOwnerContent(mozilla::dom::Element* aContent); bool ShouldClipSubdocument() { return mClipSubdocument; } + bool ShouldClampScrollPosition() { return mClampScrollPosition; } + private: bool ShouldUseRemoteProcess(); /** * If we are an IPC frame, set mRemoteFrame. Otherwise, create and * initialize mDocShell. */ @@ -337,16 +339,17 @@ private: // created using NS_FROM_PARSER_NETWORK flag. If the element is modified, // it may lose the flag. bool mNetworkCreated : 1; bool mDelayRemoteDialogs : 1; bool mRemoteBrowserShown : 1; bool mRemoteFrame : 1; bool mClipSubdocument : 1; + bool mClampScrollPosition : 1; // XXX leaking nsCOMPtr<nsIObserver> mChildHost; RenderFrameParent* mCurrentRemoteFrame; TabParent* mRemoteBrowser; // See nsIFrameLoader.idl. Short story, if !(mRenderMode & // RENDER_MODE_ASYNC_SCROLL), all the fields below are ignored in
--- a/content/base/src/nsFrameMessageManager.cpp +++ b/content/base/src/nsFrameMessageManager.cpp @@ -344,17 +344,18 @@ nsFrameMessageManager::Atob(const nsAStr { return NS_OK; } class MMListenerRemover { public: MMListenerRemover(nsFrameMessageManager* aMM) - : mMM(aMM), mWasHandlingMessage(aMM->mHandlingMessage) + : mWasHandlingMessage(aMM->mHandlingMessage) + , mMM(aMM) { mMM->mHandlingMessage = true; } ~MMListenerRemover() { if (!mWasHandlingMessage) { mMM->mHandlingMessage = false; if (mMM->mDisconnected) {
--- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -208,26 +208,19 @@ nsINode::nsSlots::Unlink() if (mChildNodes) { mChildNodes->DropReference(); NS_RELEASE(mChildNodes); } } //---------------------------------------------------------------------- -nsINode *nsINode::sOrphanNodeHead = nsnull; - nsINode::~nsINode() { NS_ASSERTION(!HasSlots(), "nsNodeUtils::LastRelease was not called?"); - - MOZ_ASSERT(IsOrphan(), "Node should be orphan by the time it's deleted!"); - - mPreviousOrphanNode->mNextOrphanNode = mNextOrphanNode; - mNextOrphanNode->mPreviousOrphanNode = mPreviousOrphanNode; } void* nsINode::GetProperty(PRUint16 aCategory, nsIAtom *aPropertyName, nsresult *aStatus) const { return OwnerDoc()->PropertyTable(aCategory)->GetProperty(this, aPropertyName, aStatus); @@ -3264,17 +3257,17 @@ nsGenericElement::UnbindFromTree(bool aD DeleteProperty(nsGkAtoms::transitionsProperty); DeleteProperty(nsGkAtoms::animationsOfBeforeProperty); DeleteProperty(nsGkAtoms::animationsOfAfterProperty); DeleteProperty(nsGkAtoms::animationsProperty); } // Unset this since that's what the old code effectively did. UnsetFlags(NODE_FORCE_XBL_BINDINGS); - + #ifdef MOZ_XUL nsXULElement* xulElem = nsXULElement::FromContent(this); if (xulElem) { xulElem->SetXULBindingParent(nsnull); } else #endif { @@ -3858,32 +3851,17 @@ nsGenericElement::GetTextContent(nsAStri } NS_IMETHODIMP nsGenericElement::SetTextContent(const nsAString& aTextContent) { return nsContentUtils::SetNodeTextContent(this, aTextContent, false); } -// static -void -nsINode::Init() -{ - // Allocate static storage for the head of the list of orphan nodes - static MOZ_ALIGNED_DECL(char orphanNodeListHead[sizeof(nsINode)], 8); - sOrphanNodeHead = reinterpret_cast<nsINode *>(&orphanNodeListHead[0]); - - sOrphanNodeHead->mNextOrphanNode = sOrphanNodeHead; - sOrphanNodeHead->mPreviousOrphanNode = sOrphanNodeHead; - - sOrphanNodeHead->mFirstChild = reinterpret_cast<nsIContent *>(0xdeadbeef); - sOrphanNodeHead->mParent = reinterpret_cast<nsIContent *>(0xdeadbeef); -} - -// static +/* static */ nsresult nsGenericElement::DispatchEvent(nsPresContext* aPresContext, nsEvent* aEvent, nsIContent* aTarget, bool aFullDispatch, nsEventStatus* aStatus) { NS_PRECONDITION(aTarget, "Must have target");
--- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -1095,16 +1095,17 @@ GK_ATOM(window, "window") GK_ATOM(headerWindowTarget, "window-target") GK_ATOM(withParam, "with-param") GK_ATOM(wizard, "wizard") GK_ATOM(wrap, "wrap") GK_ATOM(headerDNSPrefetchControl,"x-dns-prefetch-control") GK_ATOM(headerCSP, "x-content-security-policy") GK_ATOM(headerCSPReportOnly, "x-content-security-policy-report-only") GK_ATOM(headerXFO, "x-frame-options") +GK_ATOM(x_western, "x-western") GK_ATOM(xml, "xml") GK_ATOM(xml_stylesheet, "xml-stylesheet") GK_ATOM(xmlns, "xmlns") GK_ATOM(xmp, "xmp") GK_ATOM(xulcontentsgenerated, "xulcontentsgenerated") GK_ATOM(yes, "yes") GK_ATOM(z_index, "z-index") GK_ATOM(zeroDigit, "zero-digit")
--- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -1275,18 +1275,20 @@ nsXMLHttpRequest::CloseRequestWithError( if (mTimeoutTimer) { mTimeoutTimer->Cancel(); } PRUint32 responseLength = mResponseBody.Length(); ResetResponse(); mState |= aFlag; // If we're in the destructor, don't risk dispatching an event. - if (mState & XML_HTTP_REQUEST_DELETED) + if (mState & XML_HTTP_REQUEST_DELETED) { + mState &= ~XML_HTTP_REQUEST_SYNCLOOPING; return; + } if (!(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_DONE))) { ChangeState(XML_HTTP_REQUEST_DONE, true); if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) { DispatchProgressEvent(this, aType, mLoadLengthComputable, responseLength, @@ -1312,55 +1314,102 @@ nsXMLHttpRequest::CloseRequestWithError( /* void abort (); */ NS_IMETHODIMP nsXMLHttpRequest::Abort() { CloseRequestWithError(NS_LITERAL_STRING(ABORT_STR), XML_HTTP_REQUEST_ABORTED); return NS_OK; } -/* string getAllResponseHeaders (); */ +/* DOMString getAllResponseHeaders(); */ NS_IMETHODIMP -nsXMLHttpRequest::GetAllResponseHeaders(char **_retval) +nsXMLHttpRequest::GetAllResponseHeaders(nsAString& aResponseHeaders) { - NS_ENSURE_ARG_POINTER(_retval); - *_retval = nsnull; + aResponseHeaders.Truncate(); + + // If the state is UNSENT or OPENED, + // return the empty string and terminate these steps. + if (mState & (XML_HTTP_REQUEST_UNSENT | + XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) { + return NS_OK; + } if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) { - *_retval = ToNewCString(EmptyString()); return NS_OK; } - nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel(); - - if (httpChannel) { + if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) { nsRefPtr<nsHeaderVisitor> visitor = new nsHeaderVisitor(); - nsresult rv = httpChannel->VisitResponseHeaders(visitor); - if (NS_SUCCEEDED(rv)) - *_retval = ToNewCString(visitor->Headers()); + if (NS_SUCCEEDED(httpChannel->VisitResponseHeaders(visitor))) { + aResponseHeaders = NS_ConvertUTF8toUTF16(visitor->Headers()); + } + return NS_OK; + } + + if (!mChannel) { + return NS_OK; } - - if (!*_retval) { - *_retval = ToNewCString(EmptyString()); + + // Even non-http channels supply content type. + nsCAutoString value; + if (NS_SUCCEEDED(mChannel->GetContentType(value))) { + aResponseHeaders.AppendLiteral("Content-Type: "); + aResponseHeaders.Append(NS_ConvertUTF8toUTF16(value)); + if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && + !value.IsEmpty()) { + aResponseHeaders.AppendLiteral(";charset="); + aResponseHeaders.Append(NS_ConvertUTF8toUTF16(value)); + } + aResponseHeaders.Append('\n'); } - return NS_OK; } /* ACString getResponseHeader (in AUTF8String header); */ NS_IMETHODIMP nsXMLHttpRequest::GetResponseHeader(const nsACString& header, nsACString& _retval) { nsresult rv = NS_OK; _retval.SetIsVoid(true); nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel(); if (!httpChannel) { + // If the state is UNSENT or OPENED, + // return null and terminate these steps. + if (mState & (XML_HTTP_REQUEST_UNSENT | + XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) { + return NS_OK; + } + + // Even non-http channels supply content type. + // Remember we don't leak header information from denied cross-site + // requests. + nsresult status; + if (!mChannel || + NS_FAILED(mChannel->GetStatus(&status)) || + NS_FAILED(status) || + !header.LowerCaseEqualsASCII("content-type")) { + return NS_OK; + } + + if (NS_FAILED(mChannel->GetContentType(_retval))) { + // Means no content type + _retval.SetIsVoid(true); + return NS_OK; + } + + nsCString value; + if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && + !value.IsEmpty()) { + _retval.Append(";charset="); + _retval.Append(value); + } + return NS_OK; } // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts. bool chrome = false; // default to false in case IsCapabilityEnabled fails IsCapabilityEnabled("UniversalXPConnect", &chrome); if (!chrome && (header.LowerCaseEqualsASCII("set-cookie") ||
--- a/content/base/test/test_XHR.html +++ b/content/base/test/test_XHR.html @@ -17,43 +17,52 @@ SimpleTest.waitForExplicitFinish(); var gen = runTests(); function continueTest() { gen.next(); } function runTests() { var path = "/tests/content/base/test/"; -var passFiles = [['file_XHR_pass1.xml', 'GET', 200], - ['file_XHR_pass2.txt', 'GET', 200], - ['file_XHR_pass3.txt', 'GET', 200], - ['data:text/xml,%3Cres%3Ehello%3C/res%3E%0A', 'GET', 0], - ['data:text/plain,hello%20pass%0A', 'GET', 0], +var passFiles = [['file_XHR_pass1.xml', 'GET', 200, 'text/xml'], + ['file_XHR_pass2.txt', 'GET', 200, 'text/plain'], + ['file_XHR_pass3.txt', 'GET', 200, 'text/plain'], + ['data:text/xml,%3Cres%3Ehello%3C/res%3E%0A', 'GET', 0, 'text/xml'], + ['data:text/plain,hello%20pass%0A', 'GET', 0, 'text/plain'], + ['data:,foo', 'GET', 0, 'text/plain;charset=US-ASCII', 'foo'], + ['data:text/plain;base64,Zm9v', 'GET', 0, 'text/plain', 'foo'], + ['data:text/plain,foo#bar', 'GET', 0, 'text/plain', 'foo'], + ['data:text/plain,foo%23bar', 'GET', 0, 'text/plain', 'foo#bar'], ]; var failFiles = [['//example.com' + path + 'file_XHR_pass1.xml', 'GET'], ['ftp://localhost' + path + 'file_XHR_pass1.xml', 'GET'], ['file_XHR_fail1.txt', 'GET'], ]; for (i = 0; i < passFiles.length; ++i) { xhr = new XMLHttpRequest(); + is(xhr.getResponseHeader("Content-Type"), null, "should be null"); + is(xhr.getAllResponseHeaders(), "", "should be empty string"); is(xhr.responseType, "", "wrong initial responseType"); xhr.open(passFiles[i][1], passFiles[i][0], false); xhr.send(null); is(xhr.status, passFiles[i][2], "wrong status"); + is(xhr.getResponseHeader("Content-Type"), passFiles[i][3], "wrong content type"); + var headers = xhr.getAllResponseHeaders(); + ok(/(?:^|\n)Content-Type:\s*([^\n]*)\n/i.test(headers) && + RegExp.$1 === passFiles[i][3], "wrong response headers"); if (xhr.responseXML) { is((new XMLSerializer()).serializeToString(xhr.responseXML.documentElement), - "<res>hello</res>", - "wrong responseXML"); - is(xhr.response, "<res>hello</res>\n", "wrong response"); + passFiles[i][4] || "<res>hello</res>", "wrong responseXML"); + is(xhr.response, passFiles[i][4] || "<res>hello</res>\n", "wrong response"); } else { - is(xhr.responseText, "hello pass\n", "wrong responseText"); - is(xhr.response, "hello pass\n", "wrong response"); + is(xhr.responseText, passFiles[i][4] || "hello pass\n", "wrong responseText"); + is(xhr.response, passFiles[i][4] || "hello pass\n", "wrong response"); } } for (i = 0; i < failFiles.length; ++i) { xhr = new XMLHttpRequest(); var didthrow = false; try { xhr.open(failFiles[i][1], failFiles[i][0], false);
--- a/content/canvas/src/CustomQS_WebGL.h +++ b/content/canvas/src/CustomQS_WebGL.h @@ -440,17 +440,17 @@ nsIDOMWebGLRenderingContext_TexImage2D(J JSObject *argv5 = JSVAL_TO_OBJECT(argv[5]); jsval js_width, js_height, js_data; JS_GetProperty(cx, argv5, "width", &js_width); JS_GetProperty(cx, argv5, "height", &js_height); JS_GetProperty(cx, argv5, "data", &js_data); if (js_width == JSVAL_VOID || js_height == JSVAL_VOID || - js_data == JSVAL_VOID) + !js_data.isObject()) { xpc_qsThrowBadArg(cx, NS_ERROR_FAILURE, vp, 5); return JS_FALSE; } int32_t int_width, int_height; JSObject *obj_data = JSVAL_TO_OBJECT(js_data); if (!JS_ValueToECMAInt32(cx, js_width, &int_width) || !JS_ValueToECMAInt32(cx, js_height, &int_height)) @@ -561,17 +561,17 @@ nsIDOMWebGLRenderingContext_TexSubImage2 // failed to interprete argv[6] as a DOMElement, now try to interprete it as ImageData JSObject *argv6 = JSVAL_TO_OBJECT(argv[6]); jsval js_width, js_height, js_data; JS_GetProperty(cx, argv6, "width", &js_width); JS_GetProperty(cx, argv6, "height", &js_height); JS_GetProperty(cx, argv6, "data", &js_data); if (js_width == JSVAL_VOID || js_height == JSVAL_VOID || - js_data == JSVAL_VOID) + !js_data.isObject()) { xpc_qsThrowBadArg(cx, NS_ERROR_FAILURE, vp, 6); return JS_FALSE; } int32_t int_width, int_height; JSObject *obj_data = JSVAL_TO_OBJECT(js_data); if (!JS_ValueToECMAInt32(cx, js_width, &int_width) || !JS_ValueToECMAInt32(cx, js_height, &int_height))
--- a/content/canvas/src/Makefile.in +++ b/content/canvas/src/Makefile.in @@ -63,16 +63,17 @@ CPPSRCS = \ $(NULL) ifdef MOZ_WEBGL CPPSRCS += \ WebGLContext.cpp \ WebGLContextGL.cpp \ WebGLContextUtils.cpp \ + WebGLContextReporter.cpp \ WebGLContextValidate.cpp \ WebGLExtensionStandardDerivatives.cpp \ WebGLExtensionLoseContext.cpp \ $(NULL) DEFINES += -DUSE_ANGLE USE_ANGLE=1
--- a/content/canvas/src/WebGLContext.cpp +++ b/content/canvas/src/WebGLContext.cpp @@ -70,143 +70,16 @@ #include "mozilla/Preferences.h" #include "mozilla/Telemetry.h" using namespace mozilla; using namespace mozilla::gl; using namespace mozilla::layers; -WebGLMemoryReporter* WebGLMemoryReporter::sUniqueInstance = nsnull; - -NS_MEMORY_REPORTER_IMPLEMENT(WebGLTextureMemoryUsed, - "webgl-texture-memory", - KIND_OTHER, - UNITS_BYTES, - WebGLMemoryReporter::GetTextureMemoryUsed, - "Memory used by WebGL textures. The OpenGL implementation is free to store these textures in either video memory or main memory. This measurement is only a lower bound, actual memory usage may be higher for example if the storage is strided.") - -NS_MEMORY_REPORTER_IMPLEMENT(WebGLTextureCount, - "webgl-texture-count", - KIND_OTHER, - UNITS_COUNT, - WebGLMemoryReporter::GetTextureCount, - "Number of WebGL textures.") - -NS_MEMORY_REPORTER_IMPLEMENT(WebGLBufferMemoryUsed, - "webgl-buffer-memory", - KIND_OTHER, - UNITS_BYTES, - WebGLMemoryReporter::GetBufferMemoryUsed, - "Memory used by WebGL buffers. The OpenGL implementation is free to store these buffers in either video memory or main memory. This measurement is only a lower bound, actual memory usage may be higher for example if the storage is strided.") - -NS_MEMORY_REPORTER_IMPLEMENT(WebGLBufferCacheMemoryUsed, - "explicit/webgl/buffer-cache-memory", - KIND_HEAP, - UNITS_BYTES, - WebGLMemoryReporter::GetBufferCacheMemoryUsed, - "Memory used by WebGL buffer caches. The WebGL implementation caches the contents of element array buffers only. This adds up with the webgl-buffer-memory value, but contrary to it, this one represents bytes on the heap, not managed by OpenGL.") - -NS_MEMORY_REPORTER_IMPLEMENT(WebGLBufferCount, - "webgl-buffer-count", - KIND_OTHER, - UNITS_COUNT, - WebGLMemoryReporter::GetBufferCount, - "Number of WebGL buffers.") - -NS_MEMORY_REPORTER_IMPLEMENT(WebGLRenderbufferMemoryUsed, - "webgl-renderbuffer-memory", - KIND_OTHER, - UNITS_BYTES, - WebGLMemoryReporter::GetRenderbufferMemoryUsed, - "Memory used by WebGL renderbuffers. The OpenGL implementation is free to store these renderbuffers in either video memory or main memory. This measurement is only a lower bound, actual memory usage may be higher for example if the storage is strided.") - -NS_MEMORY_REPORTER_IMPLEMENT(WebGLRenderbufferCount, - "webgl-renderbuffer-count", - KIND_OTHER, - UNITS_COUNT, - WebGLMemoryReporter::GetRenderbufferCount, - "Number of WebGL renderbuffers.") - -NS_MEMORY_REPORTER_IMPLEMENT(WebGLShaderSourcesSize, - "explicit/webgl/shader-sources-size", - KIND_HEAP, - UNITS_BYTES, - WebGLMemoryReporter::GetShaderSourcesSize, - "Combined size of WebGL shader ASCII sources, cached on the heap. This should always be at most a few kilobytes, or dozen kilobytes for very shader-intensive WebGL demos.") - -NS_MEMORY_REPORTER_IMPLEMENT(WebGLShaderTranslationLogsSize, - "explicit/webgl/shader-translationlogs-size", - KIND_HEAP, - UNITS_BYTES, - WebGLMemoryReporter::GetShaderTranslationLogsSize, - "Combined size of WebGL shader ASCII translation logs, cached on the heap.") - -NS_MEMORY_REPORTER_IMPLEMENT(WebGLShaderCount, - "webgl-shader-count", - KIND_OTHER, - UNITS_COUNT, - WebGLMemoryReporter::GetShaderCount, - "Number of WebGL shaders.") - -NS_MEMORY_REPORTER_IMPLEMENT(WebGLContextCount, - "webgl-context-count", - KIND_OTHER, - UNITS_COUNT, - WebGLMemoryReporter::GetContextCount, - "Number of WebGL contexts.") - -WebGLMemoryReporter* WebGLMemoryReporter::UniqueInstance() -{ - if (!sUniqueInstance) { - sUniqueInstance = new WebGLMemoryReporter; - } - return sUniqueInstance; -} - -WebGLMemoryReporter::WebGLMemoryReporter() - : mTextureMemoryUsageReporter(new NS_MEMORY_REPORTER_NAME(WebGLTextureMemoryUsed)) - , mTextureCountReporter(new NS_MEMORY_REPORTER_NAME(WebGLTextureCount)) - , mBufferMemoryUsageReporter(new NS_MEMORY_REPORTER_NAME(WebGLBufferMemoryUsed)) - , mBufferCacheMemoryUsageReporter(new NS_MEMORY_REPORTER_NAME(WebGLBufferCacheMemoryUsed)) - , mBufferCountReporter(new NS_MEMORY_REPORTER_NAME(WebGLBufferCount)) - , mRenderbufferMemoryUsageReporter(new NS_MEMORY_REPORTER_NAME(WebGLRenderbufferMemoryUsed)) - , mRenderbufferCountReporter(new NS_MEMORY_REPORTER_NAME(WebGLRenderbufferCount)) - , mShaderSourcesSizeReporter(new NS_MEMORY_REPORTER_NAME(WebGLShaderSourcesSize)) - , mShaderTranslationLogsSizeReporter(new NS_MEMORY_REPORTER_NAME(WebGLShaderTranslationLogsSize)) - , mShaderCountReporter(new NS_MEMORY_REPORTER_NAME(WebGLShaderCount)) - , mContextCountReporter(new NS_MEMORY_REPORTER_NAME(WebGLContextCount)) -{ - NS_RegisterMemoryReporter(mTextureMemoryUsageReporter); - NS_RegisterMemoryReporter(mTextureCountReporter); - NS_RegisterMemoryReporter(mBufferMemoryUsageReporter); - NS_RegisterMemoryReporter(mBufferCacheMemoryUsageReporter); - NS_RegisterMemoryReporter(mBufferCountReporter); - NS_RegisterMemoryReporter(mRenderbufferMemoryUsageReporter); - NS_RegisterMemoryReporter(mRenderbufferCountReporter); - NS_RegisterMemoryReporter(mShaderSourcesSizeReporter); - NS_RegisterMemoryReporter(mShaderTranslationLogsSizeReporter); - NS_RegisterMemoryReporter(mShaderCountReporter); - NS_RegisterMemoryReporter(mContextCountReporter); -} - -WebGLMemoryReporter::~WebGLMemoryReporter() -{ - NS_UnregisterMemoryReporter(mTextureMemoryUsageReporter); - NS_UnregisterMemoryReporter(mTextureCountReporter); - NS_UnregisterMemoryReporter(mBufferMemoryUsageReporter); - NS_UnregisterMemoryReporter(mBufferCacheMemoryUsageReporter); - NS_UnregisterMemoryReporter(mBufferCountReporter); - NS_UnregisterMemoryReporter(mRenderbufferMemoryUsageReporter); - NS_UnregisterMemoryReporter(mRenderbufferCountReporter); - NS_UnregisterMemoryReporter(mShaderSourcesSizeReporter); - NS_UnregisterMemoryReporter(mShaderTranslationLogsSizeReporter); - NS_UnregisterMemoryReporter(mShaderCountReporter); - NS_UnregisterMemoryReporter(mContextCountReporter); -} nsresult NS_NewCanvasRenderingContextWebGL(nsIDOMWebGLRenderingContext** aResult); nsresult NS_NewCanvasRenderingContextWebGL(nsIDOMWebGLRenderingContext** aResult) { Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1); nsIDOMWebGLRenderingContext* ctx = new WebGLContext(); @@ -284,31 +157,31 @@ WebGLContext::WebGLContext() mGLMaxVaryingVectors = 0; mGLMaxFragmentUniformVectors = 0; mGLMaxVertexUniformVectors = 0; // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13 mPixelStorePackAlignment = 4; mPixelStoreUnpackAlignment = 4; - WebGLMemoryReporter::AddWebGLContext(this); + WebGLMemoryMultiReporterWrapper::AddWebGLContext(this); mAllowRestore = true; mRobustnessTimerRunning = false; mDrawSinceRobustnessTimerSet = false; mContextRestorer = do_CreateInstance("@mozilla.org/timer;1"); mContextStatus = ContextStable; mContextLostErrorSet = false; mContextLostDueToTest = false; } WebGLContext::~WebGLContext() { DestroyResourcesAndContext(); - WebGLMemoryReporter::RemoveWebGLContext(this); + WebGLMemoryMultiReporterWrapper::RemoveWebGLContext(this); TerminateRobustnessTimer(); mContextRestorer = nsnull; } void WebGLContext::DestroyResourcesAndContext() { if (!gl)
--- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -50,18 +50,18 @@ #include "nsIDocShell.h" #include "nsIDOMWebGLRenderingContext.h" #include "nsICanvasRenderingContextInternal.h" #include "nsHTMLCanvasElement.h" #include "nsWeakReference.h" #include "nsIDOMHTMLElement.h" +#include "nsIMemoryReporter.h" #include "nsIJSNativeInitializer.h" -#include "nsIMemoryReporter.h" #include "nsContentUtils.h" #include "GLContextProvider.h" #include "Layers.h" #include "CheckedInt.h" /* @@ -509,17 +509,17 @@ struct WebGLContextOptions { class WebGLContext : public nsIDOMWebGLRenderingContext, public nsICanvasRenderingContextInternal, public nsSupportsWeakReference, public nsITimerCallback, public WebGLRectangleObject { - friend class WebGLMemoryReporter; + friend class WebGLMemoryMultiReporterWrapper; friend class WebGLExtensionLoseContext; friend class WebGLContextUserData; public: WebGLContext(); virtual ~WebGLContext(); NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -1050,16 +1050,20 @@ public: mContext->MakeContextCurrent(); mContext->gl->fDeleteBuffers(1, &mGLName); free(mData); mData = nsnull; mByteLength = 0; mContext->mBuffers.RemoveElement(mMonotonicHandle); } + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const { + return aMallocSizeOf(this) + aMallocSizeOf(mData); + } + bool HasEverBeenBound() { return mHasEverBeenBound; } void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; } GLuint GLName() const { return mGLName; } GLuint ByteLength() const { return mByteLength; } GLenum Target() const { return mTarget; } const void *Data() const { return mData; } void SetByteLength(GLuint byteLength) { mByteLength = byteLength; } @@ -1622,16 +1626,22 @@ public: mContext->MakeContextCurrent(); mGLName = mContext->gl->fCreateShader(mType); mMonotonicHandle = mContext->mShaders.AppendElement(this); } ~WebGLShader() { DeleteOnce(); } + + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) { + return aMallocSizeOf(this) + + mSource.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + + mTranslationLog.SizeOfExcludingThisIfUnshared(aMallocSizeOf); + } void Delete() { mSource.Truncate(); mTranslationLog.Truncate(); mContext->MakeContextCurrent(); mContext->gl->fDeleteShader(mGLName); mContext->mShaders.RemoveElement(mMonotonicHandle); } @@ -2510,136 +2520,103 @@ WebGLContext::CanGetConcreteObject(const BaseInterfaceType *aInterface, bool *isNull, bool *isDeleted) { ConcreteObjectType *aConcreteObject; return GetConcreteObject(info, aInterface, &aConcreteObject, isNull, isDeleted, false); } -class WebGLMemoryReporter +class WebGLMemoryMultiReporterWrapper { - WebGLMemoryReporter(); - ~WebGLMemoryReporter(); - static WebGLMemoryReporter* sUniqueInstance; - - // here we store plain pointers, not RefPtrs: we don't want the WebGLMemoryReporter unique instance to keep alive all + WebGLMemoryMultiReporterWrapper(); + ~WebGLMemoryMultiReporterWrapper(); + static WebGLMemoryMultiReporterWrapper* sUniqueInstance; + + // here we store plain pointers, not RefPtrs: we don't want the + // WebGLMemoryMultiReporterWrapper unique instance to keep alive all // WebGLContexts ever created. typedef nsTArray<const WebGLContext*> ContextsArrayType; ContextsArrayType mContexts; - nsCOMPtr<nsIMemoryReporter> mTextureMemoryUsageReporter; - nsCOMPtr<nsIMemoryReporter> mTextureCountReporter; - nsCOMPtr<nsIMemoryReporter> mBufferMemoryUsageReporter; - nsCOMPtr<nsIMemoryReporter> mBufferCacheMemoryUsageReporter; - nsCOMPtr<nsIMemoryReporter> mBufferCountReporter; - nsCOMPtr<nsIMemoryReporter> mRenderbufferMemoryUsageReporter; - nsCOMPtr<nsIMemoryReporter> mRenderbufferCountReporter; - nsCOMPtr<nsIMemoryReporter> mShaderSourcesSizeReporter; - nsCOMPtr<nsIMemoryReporter> mShaderTranslationLogsSizeReporter; - nsCOMPtr<nsIMemoryReporter> mShaderCountReporter; - nsCOMPtr<nsIMemoryReporter> mContextCountReporter; - - static WebGLMemoryReporter* UniqueInstance(); + nsCOMPtr<nsIMemoryMultiReporter> mReporter; + + static WebGLMemoryMultiReporterWrapper* UniqueInstance(); static ContextsArrayType & Contexts() { return UniqueInstance()->mContexts; } public: static void AddWebGLContext(const WebGLContext* c) { Contexts().AppendElement(c); } static void RemoveWebGLContext(const WebGLContext* c) { ContextsArrayType & contexts = Contexts(); contexts.RemoveElement(c); if (contexts.IsEmpty()) { - delete sUniqueInstance; + delete sUniqueInstance; sUniqueInstance = nsnull; } } static PRInt64 GetTextureMemoryUsed() { const ContextsArrayType & contexts = Contexts(); PRInt64 result = 0; for(size_t i = 0; i < contexts.Length(); ++i) - for (size_t t = 0; t < contexts[i]->mTextures.Length(); ++t) - result += contexts[i]->mTextures[t]->MemoryUsage(); + for (size_t j = 0; j < contexts[i]->mTextures.Length(); ++j) + result += contexts[i]->mTextures[j]->MemoryUsage(); return result; } static PRInt64 GetTextureCount() { const ContextsArrayType & contexts = Contexts(); PRInt64 result = 0; for(size_t i = 0; i < contexts.Length(); ++i) result += contexts[i]->mTextures.Length(); return result; } static PRInt64 GetBufferMemoryUsed() { const ContextsArrayType & contexts = Contexts(); PRInt64 result = 0; for(size_t i = 0; i < contexts.Length(); ++i) - for (size_t b = 0; b < contexts[i]->mBuffers.Length(); ++b) - result += contexts[i]->mBuffers[b]->ByteLength(); + for (size_t j = 0; j < contexts[i]->mBuffers.Length(); ++j) + result += contexts[i]->mBuffers[j]->ByteLength(); return result; } - static PRInt64 GetBufferCacheMemoryUsed() { - const ContextsArrayType & contexts = Contexts(); - PRInt64 result = 0; - for(size_t i = 0; i < contexts.Length(); ++i) - for (size_t b = 0; b < contexts[i]->mBuffers.Length(); ++b) - if (contexts[i]->mBuffers[b]->Target() == LOCAL_GL_ELEMENT_ARRAY_BUFFER) - result += contexts[i]->mBuffers[b]->ByteLength(); - return result; - } + static PRInt64 GetBufferCacheMemoryUsed(); static PRInt64 GetBufferCount() { const ContextsArrayType & contexts = Contexts(); PRInt64 result = 0; for(size_t i = 0; i < contexts.Length(); ++i) result += contexts[i]->mBuffers.Length(); return result; } static PRInt64 GetRenderbufferMemoryUsed() { const ContextsArrayType & contexts = Contexts(); PRInt64 result = 0; for(size_t i = 0; i < contexts.Length(); ++i) - for (size_t r = 0; r < contexts[i]->mRenderbuffers.Length(); ++r) - result += contexts[i]->mRenderbuffers[r]->MemoryUsage(); + for (size_t j = 0; j < contexts[i]->mRenderbuffers.Length(); ++j) + result += contexts[i]->mRenderbuffers[j]->MemoryUsage(); return result; } static PRInt64 GetRenderbufferCount() { const ContextsArrayType & contexts = Contexts(); PRInt64 result = 0; for(size_t i = 0; i < contexts.Length(); ++i) result += contexts[i]->mRenderbuffers.Length(); return result; } - static PRInt64 GetShaderSourcesSize() { - const ContextsArrayType & contexts = Contexts(); - PRInt64 result = 0; - for(size_t i = 0; i < contexts.Length(); ++i) - for (size_t s = 0; s < contexts[i]->mShaders.Length(); ++s) - result += contexts[i]->mShaders[s]->Source().Length(); - return result; - } - - static PRInt64 GetShaderTranslationLogsSize() { - const ContextsArrayType & contexts = Contexts(); - PRInt64 result = 0; - for(size_t i = 0; i < contexts.Length(); ++i) - for (size_t s = 0; s < contexts[i]->mShaders.Length(); ++s) - result += contexts[i]->mShaders[s]->TranslationLog().Length(); - return result; - } + static PRInt64 GetShaderSize(); static PRInt64 GetShaderCount() { const ContextsArrayType & contexts = Contexts(); PRInt64 result = 0; for(size_t i = 0; i < contexts.Length(); ++i) result += contexts[i]->mShaders.Length(); return result; }
new file mode 100644 --- /dev/null +++ b/content/canvas/src/WebGLContextReporter.cpp @@ -0,0 +1,218 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * 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 ***** */ + +#include "WebGLContext.h" +#include "nsIMemoryReporter.h" + +using namespace mozilla; + + +class WebGLMemoryMultiReporter : public nsIMemoryMultiReporter +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIMEMORYMULTIREPORTER +}; + +NS_IMPL_ISUPPORTS1(WebGLMemoryMultiReporter, nsIMemoryMultiReporter) + +NS_IMETHODIMP +WebGLMemoryMultiReporter::GetName(nsACString &aName) +{ + aName.AssignLiteral("webgl"); + return NS_OK; +} + +NS_IMETHODIMP +WebGLMemoryMultiReporter::GetExplicitNonHeap(PRInt64 *aAmount) +{ + // WebGLMemoryMultiReporterWrapper has no KIND_NONHEAP measurements. + *aAmount = 0; + return NS_OK; +} + +NS_IMETHODIMP +WebGLMemoryMultiReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, + nsISupports* aClosure) +{ + aCb->Callback( + EmptyCString(), + NS_LITERAL_CSTRING("webgl-texture-memory"), + nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES, + WebGLMemoryMultiReporterWrapper::GetTextureMemoryUsed(), + NS_LITERAL_CSTRING("Memory used by WebGL textures.The OpenGL" + " implementation is free to store these textures in either video" + " memory or main memory. This measurement is only a lower bound," + " actual memory usage may be higher for example if the storage" + " is strided."), + aClosure); + + aCb->Callback( + EmptyCString(), + NS_LITERAL_CSTRING("webgl-texture-count"), + nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT, + WebGLMemoryMultiReporterWrapper::GetTextureCount(), + NS_LITERAL_CSTRING("Number of WebGL textures."), + aClosure); + + aCb->Callback( + EmptyCString(), + NS_LITERAL_CSTRING("webgl-buffer-memory"), + nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES, + WebGLMemoryMultiReporterWrapper::GetBufferMemoryUsed(), + NS_LITERAL_CSTRING("Memory used by WebGL buffers. The OpenGL" + " implementation is free to store these buffers in either video" + " memory or main memory. This measurement is only a lower bound," + " actual memory usage may be higher for example if the storage" + " is strided."), + aClosure); + + aCb->Callback( + EmptyCString(), + NS_LITERAL_CSTRING("explicit/webgl/buffer-cache-memory"), + nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES, + WebGLMemoryMultiReporterWrapper::GetBufferCacheMemoryUsed(), + NS_LITERAL_CSTRING("Memory used by WebGL buffer caches. The WebGL" + " implementation caches the contents of element array buffers" + " only.This adds up with the webgl-buffer-memory value, but" + " contrary to it, this one represents bytes on the heap," + " not managed by OpenGL."), + aClosure); + + aCb->Callback( + EmptyCString(), NS_LITERAL_CSTRING("webgl-buffer-count"), + nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT, + WebGLMemoryMultiReporterWrapper::GetBufferCount(), + NS_LITERAL_CSTRING("Number of WebGL buffers."), + aClosure); + + aCb->Callback( + EmptyCString(), + NS_LITERAL_CSTRING("webgl-renderbuffer-memory"), + nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES, + WebGLMemoryMultiReporterWrapper::GetRenderbufferMemoryUsed(), + NS_LITERAL_CSTRING("Memory used by WebGL renderbuffers. The OpenGL" + " implementation is free to store these renderbuffers in either" + " video memory or main memory. This measurement is only a lower" + " bound, actual memory usage may be higher for example if the" + " storage is strided."), + aClosure); + + aCb->Callback( + EmptyCString(), + NS_LITERAL_CSTRING("webgl-renderbuffer-count"), + nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT, + WebGLMemoryMultiReporterWrapper::GetRenderbufferCount(), + NS_LITERAL_CSTRING("Number of WebGL renderbuffers."), + aClosure); + + aCb->Callback( + EmptyCString(), + NS_LITERAL_CSTRING("explicit/webgl/shader"), + nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES, + WebGLMemoryMultiReporterWrapper::GetShaderSize(), + NS_LITERAL_CSTRING("Combined size of WebGL shader ASCII sources and" + " translation logs cached on the heap."), + aClosure); + + aCb->Callback( + EmptyCString(), + NS_LITERAL_CSTRING("webgl-shader-count"), + nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT, + WebGLMemoryMultiReporterWrapper::GetShaderCount(), + NS_LITERAL_CSTRING("Number of WebGL shaders."), + aClosure); + + aCb->Callback( + EmptyCString(), + NS_LITERAL_CSTRING("webgl-context-count"), + nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT, + WebGLMemoryMultiReporterWrapper::GetContextCount(), + NS_LITERAL_CSTRING("Number of WebGL contexts."), + aClosure); + + return NS_OK; +} + +WebGLMemoryMultiReporterWrapper* WebGLMemoryMultiReporterWrapper::sUniqueInstance = nsnull; + +WebGLMemoryMultiReporterWrapper* WebGLMemoryMultiReporterWrapper::UniqueInstance() +{ + if (!sUniqueInstance) { + sUniqueInstance = new WebGLMemoryMultiReporterWrapper; + } + return sUniqueInstance; +} + +WebGLMemoryMultiReporterWrapper::WebGLMemoryMultiReporterWrapper() +{ + mReporter = new WebGLMemoryMultiReporter; + NS_RegisterMemoryMultiReporter(mReporter); +} + +WebGLMemoryMultiReporterWrapper::~WebGLMemoryMultiReporterWrapper() +{ + NS_UnregisterMemoryMultiReporter(mReporter); +} + +NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(WebGLBufferMallocSizeOfFun, "webgl-buffer") + +PRInt64 +WebGLMemoryMultiReporterWrapper::GetBufferCacheMemoryUsed() { + const ContextsArrayType & contexts = Contexts(); + PRInt64 result = 0; + for (size_t i = 0; i < contexts.Length(); ++i) { + for (size_t j = 0; j < contexts[i]->mBuffers.Length(); ++j) + if (contexts[i]->mBuffers[j]->Target() == LOCAL_GL_ELEMENT_ARRAY_BUFFER) + result += contexts[i]->mBuffers[j]->SizeOfIncludingThis(WebGLBufferMallocSizeOfFun); + } + return result; +} + +NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(WebGLShaderMallocSizeOfFun, "webgl-shader") + +PRInt64 +WebGLMemoryMultiReporterWrapper::GetShaderSize() { + const ContextsArrayType & contexts = Contexts(); + PRInt64 result = 0; + for (size_t i = 0; i < contexts.Length(); ++i) { + for (size_t j = 0; j < contexts[i]->mShaders.Length(); ++j) + result += contexts[i]->mShaders[j]->SizeOfIncludingThis(WebGLShaderMallocSizeOfFun); + } + return result; +}
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp @@ -1287,54 +1287,53 @@ nsCanvasRenderingContext2DAzure::Initial Reset(); NS_ASSERTION(mCanvasElement, "Must have a canvas element!"); mDocShell = nsnull; mWidth = width; mHeight = height; - mTarget = target; + // This first time this is called on this object is via + // nsHTMLCanvasElement::GetContext. If target was non-null then mTarget is + // non-null, otherwise we'll return an error here and GetContext won't + // return this context object and we'll never enter this code again. + // All other times this method is called, if target is null then + // mTarget won't be changed, i.e. it will remain non-null, or else it + // will be set to non-null. + // In all cases, any usable canvas context will have non-null mTarget. + + if (target) { + mValid = true; + mTarget = target; + } else { + mValid = false; + } mResetLayer = true; - /* Create dummy surfaces here - target can be null when a canvas was created - * that is too large to support. - */ - if (!target) - { - mTarget = gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(IntSize(1, 1), FORMAT_B8G8R8A8); - if (!mTarget) { - // SupportsAzure() is controlled by the "gfx.canvas.azure.prefer-skia" - // pref so that may be the reason rather than an OOM. - mValid = false; - return NS_ERROR_OUT_OF_MEMORY; - } - } else { - mValid = true; - } - // set up the initial canvas defaults mStyleStack.Clear(); mPathBuilder = nsnull; mPath = nsnull; mDSPathBuilder = nsnull; ContextState *state = mStyleStack.AppendElement(); state->globalAlpha = 1.0; state->colorStyles[STYLE_FILL] = NS_RGB(0,0,0); state->colorStyles[STYLE_STROKE] = NS_RGB(0,0,0); state->shadowColor = NS_RGBA(0,0,0,0); - mTarget->ClearRect(mgfx::Rect(Point(0, 0), Size(mWidth, mHeight))); - - // always force a redraw, because if the surface dimensions were reset - // then the surface became cleared, and we need to redraw everything. - Redraw(); + if (mTarget) { + mTarget->ClearRect(mgfx::Rect(Point(0, 0), Size(mWidth, mHeight))); + // always force a redraw, because if the surface dimensions were reset + // then the surface became cleared, and we need to redraw everything. + Redraw(); + } return mValid ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } NS_IMETHODIMP nsCanvasRenderingContext2DAzure::SetIsOpaque(bool isOpaque) { if (isOpaque == mOpaque)
--- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -175,18 +175,17 @@ nsDOMEvent::nsDOMEvent(nsPresContext* aP void nsDOMEvent::InitPresContextData(nsPresContext* aPresContext) { mPresContext = aPresContext; // Get the explicit original target (if it's anonymous make it null) { nsCOMPtr<nsIContent> content = GetTargetFromFrame(); - mTmpRealOriginalTarget = do_QueryInterface(content); - mExplicitOriginalTarget = mTmpRealOriginalTarget; + mExplicitOriginalTarget = do_QueryInterface(content); if (content && content->IsInAnonymousSubtree()) { mExplicitOriginalTarget = nsnull; } } } nsDOMEvent::~nsDOMEvent() { @@ -232,20 +231,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns case NS_MUTATION_EVENT: static_cast<nsMutationEvent*>(tmp->mEvent)->mRelatedNode = nsnull; break; default: break; } } NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPresContext); - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTmpRealOriginalTarget) - // Always set mExplicitOriginalTarget to null, when - // mTmpRealOriginalTarget doesn't point to any object! - tmp->mExplicitOriginalTarget = nsnull; + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mExplicitOriginalTarget); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMEvent) if (tmp->mEventIsInternal) { NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEvent->target) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEvent->currentTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEvent->originalTarget) switch (tmp->mEvent->eventStructType) { @@ -270,17 +266,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN( cb.NoteXPCOMChild( static_cast<nsMutationEvent*>(tmp->mEvent)->mRelatedNode); break; default: break; } } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mPresContext.get(), nsPresContext) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTmpRealOriginalTarget) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mExplicitOriginalTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END // nsIDOMEventInterface NS_METHOD nsDOMEvent::GetType(nsAString& aType) { if (!mCachedType.IsEmpty()) { aType = mCachedType; return NS_OK; @@ -351,28 +347,16 @@ nsDOMEvent::GetExplicitOriginalTarget(ns NS_ADDREF(*aRealEventTarget); return NS_OK; } return GetTarget(aRealEventTarget); } NS_IMETHODIMP -nsDOMEvent::GetTmpRealOriginalTarget(nsIDOMEventTarget** aRealEventTarget) -{ - if (mTmpRealOriginalTarget) { - *aRealEventTarget = mTmpRealOriginalTarget; - NS_ADDREF(*aRealEventTarget); - return NS_OK; - } - - return GetOriginalTarget(aRealEventTarget); -} - -NS_IMETHODIMP nsDOMEvent::GetOriginalTarget(nsIDOMEventTarget** aOriginalTarget) { if (mEvent->originalTarget) { return GetDOMEventTarget(mEvent->originalTarget, aOriginalTarget); } return GetTarget(aOriginalTarget); }
--- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -262,18 +262,17 @@ public: protected: // Internal helper functions nsresult SetEventType(const nsAString& aEventTypeArg); already_AddRefed<nsIContent> GetTargetFromFrame(); nsEvent* mEvent; nsRefPtr<nsPresContext> mPresContext; - nsCOMPtr<nsIDOMEventTarget> mTmpRealOriginalTarget; - nsIDOMEventTarget* mExplicitOriginalTarget; + nsCOMPtr<nsIDOMEventTarget> mExplicitOriginalTarget; nsString mCachedType; bool mEventIsInternal; bool mPrivateDataDuplicated; }; #define NS_FORWARD_TO_NSDOMEVENT \ NS_FORWARD_NSIDOMEVENT(nsDOMEvent::)
--- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -2076,24 +2076,22 @@ nsEventStateManager::GenerateDragGesture // it takes a long time KillClickHoldTimer(); } nsRefPtr<nsDOMDataTransfer> dataTransfer = new nsDOMDataTransfer(); if (!dataTransfer) return; - bool isInEditor = false; - bool isSelection = false; + nsCOMPtr<nsISelection> selection; nsCOMPtr<nsIContent> eventContent, targetContent; mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(eventContent)); if (eventContent) DetermineDragTarget(aPresContext, eventContent, dataTransfer, - &isSelection, &isInEditor, - getter_AddRefs(targetContent)); + getter_AddRefs(selection), getter_AddRefs(targetContent)); // Stop tracking the drag gesture now. This should stop us from // reentering GenerateDragGesture inside DOM event processing. StopTrackingDragGesture(); if (!targetContent) return; @@ -2124,36 +2122,35 @@ nsEventStateManager::GenerateDragGesture // Set the current target to the content for the mouse down mCurrentTargetContent = targetContent; // Dispatch both the dragstart and draggesture events to the DOM. For // elements in an editor, only fire the draggesture event so that the // editor code can handle it but content doesn't see a dragstart. nsEventStatus status = nsEventStatus_eIgnore; - if (!isInEditor) - nsEventDispatcher::Dispatch(targetContent, aPresContext, &startEvent, nsnull, - &status); + nsEventDispatcher::Dispatch(targetContent, aPresContext, &startEvent, nsnull, + &status); nsDragEvent* event = &startEvent; if (status != nsEventStatus_eConsumeNoDefault) { status = nsEventStatus_eIgnore; nsEventDispatcher::Dispatch(targetContent, aPresContext, &gestureEvent, nsnull, &status); event = &gestureEvent; } // now that the dataTransfer has been updated in the dragstart and // draggesture events, make it read only so that the data doesn't // change during the drag. dataTransfer->SetReadOnly(); if (status != nsEventStatus_eConsumeNoDefault) { bool dragStarted = DoDefaultDragStart(aPresContext, event, dataTransfer, - targetContent, isSelection); + targetContent, selection); if (dragStarted) { sActiveESM = nsnull; aEvent->flags |= NS_EVENT_FLAG_STOP_DISPATCH; } } // Note that frame event handling doesn't care about NS_DRAGDROP_GESTURE, // which is just as well since we don't really know which frame to @@ -2168,64 +2165,54 @@ nsEventStateManager::GenerateDragGesture FlushPendingEvents(aPresContext); } } // GenerateDragGesture void nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext, nsIContent* aSelectionTarget, nsDOMDataTransfer* aDataTransfer, - bool* aIsSelection, - bool* aIsInEditor, + nsISelection** aSelection, nsIContent** aTargetNode) { *aTargetNode = nsnull; - *aIsInEditor = false; nsCOMPtr<nsISupports> container = aPresContext->GetContainer(); nsCOMPtr<nsIDOMWindow> window = do_GetInterface(container); // GetDragData determines if a selection, link or image in the content // should be dragged, and places the data associated with the drag in the - // data transfer. Skip this check for chrome shells. + // data transfer. + // mGestureDownContent is the node where the mousedown event for the drag + // occurred, and aSelectionTarget is the node to use when a selection is used bool canDrag; nsCOMPtr<nsIContent> dragDataNode; - nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(container); - if (dsti) { - PRInt32 type = -1; - if (NS_SUCCEEDED(dsti->GetItemType(&type)) && - type != nsIDocShellTreeItem::typeChrome) { - // mGestureDownContent is the node where the mousedown event for the drag - // occurred, and aSelectionTarget is the node to use when a selection is used - nsresult rv = - nsContentAreaDragDrop::GetDragData(window, mGestureDownContent, - aSelectionTarget, mGestureDownAlt, - aDataTransfer, &canDrag, aIsSelection, - getter_AddRefs(dragDataNode)); - if (NS_FAILED(rv) || !canDrag) - return; - } - } + nsresult rv = nsContentAreaDragDrop::GetDragData(window, mGestureDownContent, + aSelectionTarget, mGestureDownAlt, + aDataTransfer, &canDrag, aSelection, + getter_AddRefs(dragDataNode)); + if (NS_FAILED(rv) || !canDrag) + return; // if GetDragData returned a node, use that as the node being dragged. // Otherwise, if a selection is being dragged, use the node within the // selection that was dragged. Otherwise, just use the mousedown target. nsIContent* dragContent = mGestureDownContent; if (dragDataNode) dragContent = dragDataNode; - else if (*aIsSelection) + else if (*aSelection) dragContent = aSelectionTarget; nsIContent* originalDragContent = dragContent; // If a selection isn't being dragged, look for an ancestor with the // draggable property set. If one is found, use that as the target of the // drag instead of the node that was clicked on. If a draggable node wasn't // found, just use the clicked node. - if (!*aIsSelection) { + if (!*aSelection) { while (dragContent) { nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(dragContent); if (htmlElement) { bool draggable = false; htmlElement->GetDraggable(&draggable); if (draggable) break; } @@ -2240,27 +2227,16 @@ nsEventStateManager::DetermineDragTarget // better way to specify whether something is draggable than just // on/off. dragContent = mGestureDownContent; break; } // otherwise, it's not an HTML or XUL element, so just keep looking } dragContent = dragContent->GetParent(); - - // if an editable parent is encountered, then we don't look at any - // ancestors. This is used because the editor attaches a draggesture - // listener to the editable element and we want to call it without - // making the editable element draggable. This should be removed once - // the editor is switched over to using the proper drag and drop api. - nsCOMPtr<nsIDOMNSEditableElement> editableElement = do_QueryInterface(dragContent); - if (editableElement) { - *aIsInEditor = true; - break; - } } } // if no node in the hierarchy was found to drag, but the GetDragData method // returned a node, use that returned node. Otherwise, nothing is draggable. if (!dragContent && dragDataNode) dragContent = dragDataNode; @@ -2274,17 +2250,17 @@ nsEventStateManager::DetermineDragTarget } } bool nsEventStateManager::DoDefaultDragStart(nsPresContext* aPresContext, nsDragEvent* aDragEvent, nsDOMDataTransfer* aDataTransfer, nsIContent* aDragTarget, - bool aIsSelection) + nsISelection* aSelection) { nsCOMPtr<nsIDragService> dragService = do_GetService("@mozilla.org/widget/dragservice;1"); if (!dragService) return false; // Default handling for the draggesture/dragstart event. // @@ -2328,48 +2304,37 @@ nsEventStateManager::DoDefaultDragStart( action = nsIDragService::DRAGDROP_ACTION_COPY | nsIDragService::DRAGDROP_ACTION_MOVE | nsIDragService::DRAGDROP_ACTION_LINK; // get any custom drag image that was set PRInt32 imageX, imageY; nsIDOMElement* dragImage = aDataTransfer->GetDragImage(&imageX, &imageY); - // If a selection is being dragged, and no custom drag image was - // set, get the selection so that the drag region can be created - // from the selection area. If a custom image was set, it doesn't - // matter what the selection is since the image will be used instead. - nsISelection* selection = nsnull; - if (aIsSelection && !dragImage) { - nsIDocument* doc = aDragTarget->GetCurrentDoc(); - if (doc) { - nsIPresShell* presShell = doc->GetShell(); - if (presShell) { - selection = presShell->GetCurrentSelection( - nsISelectionController::SELECTION_NORMAL); - } - } - } - nsCOMPtr<nsISupportsArray> transArray; aDataTransfer->GetTransferables(getter_AddRefs(transArray)); if (!transArray) return false; // XXXndeakin don't really want to create a new drag DOM event // here, but we need something to pass to the InvokeDragSession // methods. nsCOMPtr<nsIDOMEvent> domEvent; NS_NewDOMDragEvent(getter_AddRefs(domEvent), aPresContext, aDragEvent); nsCOMPtr<nsIDOMDragEvent> domDragEvent = do_QueryInterface(domEvent); // if creating a drag event failed, starting a drag session will // just fail. - if (selection) { - dragService->InvokeDragSessionWithSelection(selection, transArray, + + // Use InvokeDragSessionWithSelection if a selection is being dragged, + // such that the image can be generated from the selected text. However, + // use InvokeDragSessionWithImage if a custom image was set or something + // other than a selection is being dragged. + if (!dragImage && aSelection) { + dragService->InvokeDragSessionWithSelection(aSelection, transArray, action, domDragEvent, aDataTransfer); } else { // if dragging within a XUL tree and no custom drag image was // set, the region argument to InvokeDragSessionWithImage needs // to be set to the area encompassing the selected rows of the // tree to ensure that the drag feedback gets clipped to those
--- a/content/events/src/nsEventStateManager.h +++ b/content/events/src/nsEventStateManager.h @@ -412,42 +412,40 @@ protected: /** * Determine which node the drag should be targeted at. * This is either the node clicked when there is a selection, or, for HTML, * the element with a draggable property set to true. * * aSelectionTarget - target to check for selection * aDataTransfer - data transfer object that will contain the data to drag - * aIsSelection - [out] set to true if a selection is being dragged - * aIsInEditor - [out] set to true if the content is in an editor field + * aSelection - [out] set to the selection to be dragged * aTargetNode - [out] the draggable node, or null if there isn't one */ void DetermineDragTarget(nsPresContext* aPresContext, nsIContent* aSelectionTarget, nsDOMDataTransfer* aDataTransfer, - bool* aIsSelection, - bool* aIsInEditor, + nsISelection** aSelection, nsIContent** aTargetNode); /* * Perform the default handling for the dragstart/draggesture event and set up a * drag for aDataTransfer if it contains any data. Returns true if a drag has * started. * * aDragEvent - the dragstart/draggesture event * aDataTransfer - the data transfer that holds the data to be dragged * aDragTarget - the target of the drag - * aIsSelection - true if a selection is being dragged + * aSelection - the selection to be dragged */ bool DoDefaultDragStart(nsPresContext* aPresContext, nsDragEvent* aDragEvent, nsDOMDataTransfer* aDataTransfer, nsIContent* aDragTarget, - bool aIsSelection); + nsISelection* aSelection); bool IsTrackingDragGesture ( ) const { return mGestureDownContent != nsnull; } /** * Set the fields of aEvent to reflect the mouse position and modifier keys * that were set when the user first pressed the mouse button (stored by * BeginTrackingDragGesture). aEvent->widget must be * mCurrentTarget->GetNearestWidget(). */
--- a/content/html/content/public/nsHTMLMediaElement.h +++ b/content/html/content/public/nsHTMLMediaElement.h @@ -452,16 +452,22 @@ protected: void QueueLoadFromSourceTask(); /** * Runs the media resource selection algorithm. */ void SelectResource(); /** + * A wrapper function that allows us to cleanly reset flags after a call + * to SelectResource() + */ + void SelectResourceWrapper(); + + /** * Asynchronously awaits a stable state, and then causes SelectResource() * to be run on the main thread's event loop. */ void QueueSelectResourceTask(); /** * The resource-fetch algorithm step of the load algorithm. */ @@ -734,16 +740,19 @@ protected: // True if we're delaying the "load" event. They are delayed until either // an error occurs, or the first frame is loaded. bool mDelayingLoadEvent; // True when we've got a task queued to call SelectResource(), // or while we're running SelectResource(). bool mIsRunningSelectResource; + // True when we already have select resource call queued + bool mHaveQueuedSelectResource; + // True if we suspended the decoder because we were paused, // preloading metadata is enabled, autoplay was not enabled, and we loaded // the first frame. bool mSuspendedAfterFirstFrame; // True if we are allowed to suspend the decoder because we were paused, // preloading metdata was enabled, autoplay was not enabled, and we loaded // the first frame.
--- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -533,16 +533,17 @@ void nsHTMLMediaElement::AbortExistingLo } mError = nsnull; mLoadedFirstFrame = false; mAutoplaying = true; mIsLoadingFromSourceChildren = false; mSuspendedAfterFirstFrame = false; mAllowSuspendAfterFirstFrame = true; + mHaveQueuedSelectResource = false; mLoadIsSuspended = false; mSourcePointer = nsnull; // TODO: The playback rate must be set to the default playback rate. if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) { mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY; ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING); @@ -620,21 +621,21 @@ void nsHTMLMediaElement::QueueLoadFromSo ChangeDelayLoadStatus(true); mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING; AsyncAwaitStableState(this, &nsHTMLMediaElement::LoadFromSourceChildren); } void nsHTMLMediaElement::QueueSelectResourceTask() { // Don't allow multiple async select resource calls to be queued. - if (mIsRunningSelectResource) + if (mHaveQueuedSelectResource) return; - mIsRunningSelectResource = true; + mHaveQueuedSelectResource = true; mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE; - AsyncAwaitStableState(this, &nsHTMLMediaElement::SelectResource); + AsyncAwaitStableState(this, &nsHTMLMediaElement::SelectResourceWrapper); } /* void load (); */ NS_IMETHODIMP nsHTMLMediaElement::Load() { if (mIsRunningLoadMethod) return NS_OK; SetPlayedOrSeeked(false); @@ -653,69 +654,78 @@ static bool HasSourceChildren(nsIContent if (child->IsHTML(nsGkAtoms::source)) { return true; } } return false; } +void nsHTMLMediaElement::SelectResourceWrapper() +{ + SelectResource(); + mIsRunningSelectResource = false; + mHaveQueuedSelectResource = false; +} + void nsHTMLMediaElement::SelectResource() { if (!HasAttr(kNameSpaceID_None, nsGkAtoms::src) && !HasSourceChildren(this)) { // The media element has neither a src attribute nor any source // element children, abort the load. mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY; // This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called ChangeDelayLoadStatus(false); - mIsRunningSelectResource = false; return; } ChangeDelayLoadStatus(true); mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING; // Load event was delayed, and still is, so no need to call // AddRemoveSelfReference, since it must still be held DispatchAsyncEvent(NS_LITERAL_STRING("loadstart")); + // Delay setting mIsRunningSeletResource until after UpdatePreloadAction + // so that we don't lose our state change by bailing out of the preload + // state update + UpdatePreloadAction(); + mIsRunningSelectResource = true; + // If we have a 'src' attribute, use that exclusively. nsAutoString src; if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) { nsCOMPtr<nsIURI> uri; nsresult rv = NewURIFromString(src, getter_AddRefs(uri)); if (NS_SUCCEEDED(rv)) { LOG(PR_LOG_DEBUG, ("%p Trying load from src=%s", this, NS_ConvertUTF16toUTF8(src).get())); NS_ASSERTION(!mIsLoadingFromSourceChildren, "Should think we're not loading from source children by default"); mLoadingSrc = uri; if (mPreloadAction == nsHTMLMediaElement::PRELOAD_NONE) { // preload:none media, suspend the load here before we make any // network requests. SuspendLoad(); - mIsRunningSelectResource = false; return; } rv = LoadResource(); if (NS_SUCCEEDED(rv)) { - mIsRunningSelectResource = false; return; } } else { const PRUnichar* params[] = { src.get() }; ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params)); } NoSupportedMediaSourceError(); } else { // Otherwise, the source elements will be used. mIsLoadingFromSourceChildren = true; LoadFromSourceChildren(); } - mIsRunningSelectResource = false; } void nsHTMLMediaElement::NotifyLoadError() { if (!mIsLoadingFromSourceChildren) { LOG(PR_LOG_DEBUG, ("NotifyLoadError(), no supported media error")); NoSupportedMediaSourceError(); } else if (mSourceLoadCandidate) { @@ -1436,16 +1446,17 @@ nsHTMLMediaElement::nsHTMLMediaElement(a mMuted(false), mPlayingBeforeSeek(false), mPausedForInactiveDocument(false), mWaitingFired(false), mIsRunningLoadMethod(false), mIsLoadingFromSourceChildren(false), mDelayingLoadEvent(false), mIsRunningSelectResource(false), + mHaveQueuedSelectResource(false), mSuspendedAfterFirstFrame(false), mAllowSuspendAfterFirstFrame(true), mHasPlayedOrSeeked(false), mHasSelfReference(false), mShuttingDown(false), mLoadIsSuspended(false), mMediaSecurityVerified(false), mCORSMode(CORS_NONE)
--- a/content/html/content/src/nsHTMLTableElement.cpp +++ b/content/html/content/src/nsHTMLTableElement.cpp @@ -896,17 +896,17 @@ nsHTMLTableElement::ParseAttribute(PRInt nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult) { /* ignore summary, just a string */ if (aNamespaceID == kNameSpaceID_None) { if (aAttribute == nsGkAtoms::cellspacing || aAttribute == nsGkAtoms::cellpadding) { - return aResult.ParseSpecialIntValue(aValue); + return aResult.ParseNonNegativeIntValue(aValue); } if (aAttribute == nsGkAtoms::cols || aAttribute == nsGkAtoms::border) { return aResult.ParseIntWithBounds(aValue, 0); } if (aAttribute == nsGkAtoms::height) { return aResult.ParseSpecialIntValue(aValue); } @@ -966,29 +966,20 @@ MapAttributesIntoRule(const nsMappedAttr nsPresContext* presContext = aData->mPresContext; nsCompatibility mode = presContext->CompatibilityMode(); if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TableBorder)) { // cellspacing const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::cellspacing); nsCSSValue* borderSpacing = aData->ValueForBorderSpacing(); - if (value && value->Type() == nsAttrValue::eInteger) { - if (borderSpacing->GetUnit() == eCSSUnit_Null) { - borderSpacing-> - SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel); - } - } - else if (value && value->Type() == nsAttrValue::ePercent && - eCompatibility_NavQuirks == mode) { - // in quirks mode, treat a % cellspacing value a pixel value. - if (borderSpacing->GetUnit() == eCSSUnit_Null) { - borderSpacing-> - SetFloatValue(100.0f * value->GetPercentValue(), eCSSUnit_Pixel); - } + if (value && value->Type() == nsAttrValue::eInteger && + borderSpacing->GetUnit() == eCSSUnit_Null) { + borderSpacing-> + SetFloatValue(float(value->GetIntegerValue()), eCSSUnit_Pixel); } } if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Table)) { const nsAttrValue* value; // layout nsCSSValue* tableLayout = aData->ValueForTableLayout(); if (tableLayout->GetUnit() == eCSSUnit_Null) { value = aAttributes->GetAttr(nsGkAtoms::layout); @@ -1154,49 +1145,39 @@ nsHTMLTableElement::GetAttributeMappingF } static void MapInheritedTableAttributesIntoRule(const nsMappedAttributes* aAttributes, nsRuleData* aData) { if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Padding)) { const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::cellpadding); - if (value) { - nsAttrValue::ValueType valueType = value->Type(); - if (valueType == nsAttrValue::eInteger || - valueType == nsAttrValue::ePercent) { - // We have cellpadding. This will override our padding values if we - // don't have any set. - nsCSSValue padVal; - if (valueType == nsAttrValue::eInteger) - padVal.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel); - else { - // when we support % cellpadding in standard mode, uncomment the - // following - float pctVal = value->GetPercentValue(); - //if (eCompatibility_NavQuirks == mode) { - // in quirks mode treat a pct cellpadding value as a pixel value - padVal.SetFloatValue(100.0f * pctVal, eCSSUnit_Pixel); - //} - //else { - // padVal.SetPercentValue(pctVal); - //} - } - nsCSSValue* paddingLeft = aData->ValueForPaddingLeftValue(); - if (paddingLeft->GetUnit() == eCSSUnit_Null) - *paddingLeft = padVal; - nsCSSValue* paddingRight = aData->ValueForPaddingRightValue(); - if (paddingRight->GetUnit() == eCSSUnit_Null) - *paddingRight = padVal; - nsCSSValue* paddingTop = aData->ValueForPaddingTop(); - if (paddingTop->GetUnit() == eCSSUnit_Null) - *paddingTop = padVal; - nsCSSValue* paddingBottom = aData->ValueForPaddingBottom(); - if (paddingBottom->GetUnit() == eCSSUnit_Null) - *paddingBottom = padVal; + if (value && value->Type() == nsAttrValue::eInteger) { + // We have cellpadding. This will override our padding values if we + // don't have any set. + nsCSSValue padVal(float(value->GetIntegerValue()), eCSSUnit_Pixel); + + nsCSSValue* paddingLeft = aData->ValueForPaddingLeftValue(); + if (paddingLeft->GetUnit() == eCSSUnit_Null) { + *paddingLeft = padVal; + } + + nsCSSValue* paddingRight = aData->ValueForPaddingRightValue(); + if (paddingRight->GetUnit() == eCSSUnit_Null) { + *paddingRight = padVal; + } + + nsCSSValue* paddingTop = aData->ValueForPaddingTop(); + if (paddingTop->GetUnit() == eCSSUnit_Null) { + *paddingTop = padVal; + } + + nsCSSValue* paddingBottom = aData->ValueForPaddingBottom(); + if (paddingBottom->GetUnit() == eCSSUnit_Null) { + *paddingBottom = padVal; } } } } nsMappedAttributes* nsHTMLTableElement::GetAttributesMappedForCell() {
--- a/content/html/content/test/test_fullscreen-api.html +++ b/content/html/content/test/test_fullscreen-api.html @@ -65,15 +65,21 @@ function nextTest() { gTestIndex++; } else { SpecialPowers.setBoolPref("full-screen-api.enabled", prevEnabled); SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", prevTrusted); SimpleTest.finish(); } } +try { + window.fullScreen = true; +} catch (e) { +} +is(window.fullScreen, false, "Shouldn't be able to set window fullscreen from content"); + addLoadEvent(nextTest); SimpleTest.waitForExplicitFinish(); </script> </pre> </body> </html>
--- a/content/media/nsBuiltinDecoder.cpp +++ b/content/media/nsBuiltinDecoder.cpp @@ -322,17 +322,19 @@ nsresult nsBuiltinDecoder::Seek(double a // If the position we want to seek to is not in a seekable range, we seek // to the closest position in the seekable ranges instead. If two positions // are equally close, we seek to the closest position from the currentTime. // See seeking spec, point 7 : // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#seeking PRInt32 range = 0; if (!IsInRanges(seekable, aTime, range)) { if (range != -1) { - if (range + 1 < length) { + // |range + 1| can't be negative, because the only possible negative value + // for |range| is -1. + if (PRUint32(range + 1) < length) { double leftBound, rightBound; res = seekable.End(range, &leftBound); NS_ENSURE_SUCCESS(res, NS_OK); res = seekable.Start(range + 1, &rightBound); NS_ENSURE_SUCCESS(res, NS_OK); double distanceLeft = NS_ABS(leftBound - aTime); double distanceRight = NS_ABS(rightBound - aTime); if (distanceLeft == distanceRight) {
--- a/content/media/nsBuiltinDecoderStateMachine.cpp +++ b/content/media/nsBuiltinDecoderStateMachine.cpp @@ -44,16 +44,17 @@ #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/Util.h" using namespace mozilla; using namespace mozilla::layers; #ifdef PR_LOGGING extern PRLogModuleInfo* gBuiltinDecoderLog; #define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg) #else @@ -302,19 +303,18 @@ StateMachineTracker& StateMachineTracker } void StateMachineTracker::EnsureGlobalStateMachine() { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); ReentrantMonitorAutoEnter mon(mMonitor); if (mStateMachineCount == 0) { NS_ASSERTION(!mStateMachineThread, "Should have null state machine thread!"); - nsresult res = NS_NewThread(&mStateMachineThread, - nsnull); - NS_ABORT_IF_FALSE(NS_SUCCEEDED(res), "Can't create media state machine thread"); + DebugOnly<nsresult> rv = NS_NewThread(&mStateMachineThread, nsnull); + NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Can't create media state machine thread"); } mStateMachineCount++; } #ifdef DEBUG bool StateMachineTracker::IsQueued(nsBuiltinDecoderStateMachine* aStateMachine) { ReentrantMonitorAutoEnter mon(mMonitor);
--- a/content/media/test/Makefile.in +++ b/content/media/test/Makefile.in @@ -104,17 +104,16 @@ include $(topsrcdir)/config/rules.mk test_bug448534.html \ test_bug463162.xhtml \ test_bug465498.html \ test_bug493187.html \ test_bug495145.html \ test_bug495300.html \ test_bug686942.html \ test_can_play_type.html \ - test_closing_connections.html \ test_constants.html \ test_controls.html \ test_currentTime.html \ test_decode_error.html \ test_decoder_disable.html \ test_defaultMuted.html \ test_delay_load.html \ test_error_on_404.html \ @@ -171,16 +170,18 @@ endif # Bug 492821: # test_videoDocumentTitle.html # Bug 493692: # test_preload_suspend.html # Bug 567954 and Bug 574586: # test_mixed_principals.html # Disabled since we don't play Wave files standalone, for now # test_audioDocumentTitle.html +# Bug 634564: +# test_closing_connections.html \ # sample files _TEST_FILES += \ 320x240.ogv \ 448636.ogv \ audio-overhang.ogg \ audio-gaps.ogg \ beta-phrasebook.ogg \
--- a/content/media/test/test_preload_actions.html +++ b/content/media/test/test_preload_actions.html @@ -380,42 +380,42 @@ var tests = [ v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false); v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false); v.addEventListener("canplaythrough", this.canplaythrough, false); v.src = test.name; // Causes implicit load. document.body.appendChild(v); v.preload = "metadata"; }, }, - { - // 13. Change preload value from metadata to none after load started, - // should still load up to metadata, should not halt immediately. - loadeddata: + // 13. Change preload value from auto to none after specifying a src + // should load according to preload none, no buffering should have taken place + suspend: function(e) { var v = e.target; is(v._gotLoadStart, true, "(13) Must get loadstart."); - is(v._gotLoadedMetaData, true, "(13) Must get loadedmetadata."); - ok(v.readyState >= v.HAVE_CURRENT_DATA, "(13) ReadyState must be >= HAVE_CURRENT_DATA."); - is(v.networkState, v.NETWORK_IDLE, "(13) NetworkState must be NETWORK_IDLE."); + is(v._gotLoadedMetaData, false, "(13) Must not get loadedmetadata."); + is(v.readyState, v.HAVE_NOTHING, "(13) ReadyState must be HAVE_NOTHING"); + is(v.networkState, v.NETWORK_IDLE, "(13) NetworkState must be NETWORK_IDLE"); maybeFinish(v, 13); }, setup: function(v) { v._gotLoadStart = false; v._gotLoadedMetaData = false; - v.preload = "metadata"; - v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false); + v.preload = "auto"; + v.src = test.name; + v.preload = "none"; v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false); - v.addEventListener("loadeddata", this.loadeddata, false); - v.src = test.name; // Causes implicit load. - document.body.appendChild(v); - v.preload = "none"; - }, + v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false); + v.addEventListener("suspend", this.suspend, false); + document.body.appendChild(v); // Causes implicit load, should load according to preload none + var s = document.createElement("source"); + } }, { // 14. Add preload:metadata video with src to document. Play(), should play through. loadeddata: function(e) { var v = e.target; is(v._gotLoadStart, true, "(14) Must get loadstart."); is(v._gotLoadedMetaData, true, "(14) Must get loadedmetadata."); @@ -516,17 +516,45 @@ var tests = [ setup: function(v) { v.addEventListener("ended", this.ended, false); v.preload = "none"; v.src = test.name; // Schedules async section to continue load algorithm. document.body.appendChild(v); v.play(); // Should cause preload:none to be overridden. }, + }, + { + // 19. Set preload='auto' on first video source then switching preload='none' and swapping the video source to another. + // The second video should not start playing as it's preload state has been changed to 'none' from 'auto' + loadedmetadata: function(e) { + var v = e.target; + is(v.preload === "auto", true, "(19) preload is initially auto"); + setTimeout(function() { + // set preload state to none and switch video sources + v.preload="none"; + v.src = test.name + "?asdf"; + setTimeout(function() { + is(v.readyState === 0, true, "(19) no buffering has taken place"); + maybeFinish(v, 19); + }, 2000); + }, 2000); + + }, + + setup: + function(v) { + var that = this; + v.preload = "auto"; + v.src = test.name; + // add a listener for when the video has loaded, so we know preload auto has worked + v.addEventListener( "loadedmetadata", this.loadedmetadata, false); + document.body.appendChild(v); } + } ]; var iterationCount = 0; function startTest(test, token) { if (test == tests[0]) { ++iterationCount; } if (iterationCount == 2) {
--- a/content/svg/content/src/SVGPathData.cpp +++ b/content/svg/content/src/SVGPathData.cpp @@ -530,36 +530,32 @@ SVGPathData::ToFlattenedPath(const gfxMa ctx->SetMatrix(aMatrix); ConstructPath(ctx); ctx->IdentityMatrix(); return ctx->GetFlattenedPath(); } -static float AngleOfVector(gfxPoint v) +static double +AngleOfVector(const gfxPoint& aVector) { // C99 says about atan2 "A domain error may occur if both arguments are // zero" and "On a domain error, the function returns an implementation- // defined value". In the case of atan2 the implementation-defined value // seems to commonly be zero, but it could just as easily be a NaN value. // We specifically want zero in this case, hence the check: - return (v != gfxPoint(0.0f, 0.0f)) ? atan2(v.y, v.x) : 0.0f; + return (aVector != gfxPoint(0.0, 0.0)) ? atan2(aVector.y, aVector.x) : 0.0; } -// TODO replace callers with calls to AngleOfVector -static double -CalcVectorAngle(double ux, double uy, double vx, double vy) +static float +AngleOfVectorF(const gfxPoint& aVector) { - double ta = atan2(uy, ux); - double tb = atan2(vy, vx); - if (tb >= ta) - return tb-ta; - return 2 * M_PI - (ta-tb); + return static_cast<float>(AngleOfVector(aVector)); } void SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const { // This code should assume that ANY type of segment can appear at ANY index. // It should also assume that segments such as M and Z can appear in weird // places, and repeat multiple times consecutively. @@ -583,41 +579,41 @@ SVGPathData::GetMarkerPositioningData(ns gfxPoint &segStart = prevSegEnd; gfxPoint segEnd; float segStartAngle, segEndAngle; switch (segType) // to find segStartAngle, segEnd and segEndAngle { case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH: segEnd = pathStart; - segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart); + segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart); break; case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS: case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL: if (segType == nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS) { segEnd = gfxPoint(mData[i], mData[i+1]); } else { segEnd = segStart + gfxPoint(mData[i], mData[i+1]); } pathStart = segEnd; // If authors are going to specify multiple consecutive moveto commands // with markers, me might as well make the angle do something useful: - segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart); + segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart); i += 2; break; case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS: case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL: if (segType == nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS) { segEnd = gfxPoint(mData[i], mData[i+1]); } else { segEnd = segStart + gfxPoint(mData[i], mData[i+1]); } - segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart); + segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart); i += 2; break; case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL: { gfxPoint cp1, cp2; // control points if (segType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS) { @@ -631,36 +627,36 @@ SVGPathData::GetMarkerPositioningData(ns } prevCP = cp2; if (cp1 == segStart) { cp1 = cp2; } if (cp2 == segEnd) { cp2 = cp1; } - segStartAngle = AngleOfVector(cp1 - segStart); - segEndAngle = AngleOfVector(segEnd - cp2); + segStartAngle = AngleOfVectorF(cp1 - segStart); + segEndAngle = AngleOfVectorF(segEnd - cp2); i += 6; break; } case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL: { gfxPoint cp1, cp2; // control points if (segType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS) { cp1 = gfxPoint(mData[i], mData[i+1]); segEnd = gfxPoint(mData[i+2], mData[i+3]); } else { cp1 = segStart + gfxPoint(mData[i], mData[i+1]); segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]); } prevCP = cp1; - segStartAngle = AngleOfVector(cp1 - segStart); - segEndAngle = AngleOfVector(segEnd - cp1); + segStartAngle = AngleOfVectorF(cp1 - segStart); + segEndAngle = AngleOfVectorF(segEnd - cp1); i += 4; break; } case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS: case nsIDOMSVGPathSeg::PATHSEG_ARC_REL: { double rx = mData[i]; @@ -690,17 +686,17 @@ SVGPathData::GetMarkerPositioningData(ns } // Below we have funny interleaving of F.6.6 (Correction of out-of-range // radii) and F.6.5 (Conversion from endpoint to center parameterization) // which is designed to avoid some unnecessary calculations. if (rx == 0.0 || ry == 0.0) { // F.6.6 step 1 - straight line or coincidental points - segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart); + segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart); i += 7; break; } rx = fabs(rx); // F.6.6.1 ry = fabs(ry); // F.6.5.1: angle = angle * M_PI/180.0; @@ -731,19 +727,20 @@ SVGPathData::GetMarkerPositioningData(ns ry *= s; root = 0.0; } double cxp = root * rx * y1p / ry; // F.6.5.2 double cyp = -root * ry * x1p / rx; double theta, delta; - theta = CalcVectorAngle(1.0, 0.0, (x1p-cxp)/rx, (y1p-cyp)/ry); // F.6.5.5 - delta = CalcVectorAngle((x1p-cxp)/rx, (y1p-cyp)/ry, - (-x1p-cxp)/rx, (-y1p-cyp)/ry); // F.6.5.6 + theta = AngleOfVector(gfxPoint((x1p-cxp)/rx, (y1p-cyp)/ry) - // F.6.5.5 + gfxPoint(1.0, 0.0)); + delta = AngleOfVector(gfxPoint((-x1p-cxp)/rx, (-y1p-cyp)/ry) - // F.6.5.6 + gfxPoint((x1p-cxp)/rx, (y1p-cyp)/ry)); if (!sweepFlag && delta > 0) delta -= 2.0 * M_PI; else if (sweepFlag && delta < 0) delta += 2.0 * M_PI; double tx1, ty1, tx2, ty2; tx1 = -cos(angle)*rx*sin(theta) - sin(angle)*ry*cos(theta); ty1 = -sin(angle)*rx*sin(theta) + cos(angle)*ry*cos(theta); @@ -752,40 +749,40 @@ SVGPathData::GetMarkerPositioningData(ns if (delta < 0.0f) { tx1 = -tx1; ty1 = -ty1; tx2 = -tx2; ty2 = -ty2; } - segStartAngle = atan2(ty1, tx1); - segEndAngle = atan2(ty2, tx2); + segStartAngle = static_cast<float>(atan2(ty1, tx1)); + segEndAngle = static_cast<float>(atan2(ty2, tx2)); i += 7; break; } case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS: case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL: if (segType == nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS) { segEnd = gfxPoint(mData[i++], segStart.y); } else { segEnd = segStart + gfxPoint(mData[i++], 0.0f); } - segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart); + segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart); break; case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS: case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL: if (segType == nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS) { segEnd = gfxPoint(segStart.x, mData[i++]); } else { segEnd = segStart + gfxPoint(0.0f, mData[i++]); } - segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart); + segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart); break; case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL: { gfxPoint cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segStart * 2 - prevCP : segStart; gfxPoint cp2; @@ -798,36 +795,36 @@ SVGPathData::GetMarkerPositioningData(ns } prevCP = cp2; if (cp1 == segStart) { cp1 = cp2; } if (cp2 == segEnd) { cp2 = cp1; } - segStartAngle = AngleOfVector(cp1 - segStart); - segEndAngle = AngleOfVector(segEnd - cp2); + segStartAngle = AngleOfVectorF(cp1 - segStart); + segEndAngle = AngleOfVectorF(segEnd - cp2); i += 4; break; } case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: { gfxPoint cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segStart * 2 - prevCP : segStart; gfxPoint cp2; if (segType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS) { segEnd = gfxPoint(mData[i], mData[i+1]); } else { segEnd = segStart + gfxPoint(mData[i], mData[i+1]); } prevCP = cp1; - segStartAngle = AngleOfVector(cp1 - segStart); - segEndAngle = AngleOfVector(segEnd - cp1); + segStartAngle = AngleOfVectorF(cp1 - segStart); + segEndAngle = AngleOfVectorF(segEnd - cp1); i += 2; break; } default: // Leave any existing marks in aMarks so we have a visual indication of // when things went wrong. NS_ABORT_IF_FALSE(false, "Unknown segment type - path corruption?"); @@ -847,17 +844,18 @@ SVGPathData::GetMarkerPositioningData(ns } else { if (!(segType == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH && prevSegType == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH)) mark.angle = nsSVGUtils::AngleBisect(prevSegEndAngle, segStartAngle); } } // Add the mark at the end of this segment, and set its position: - if (!aMarks->AppendElement(nsSVGMark(segEnd.x, segEnd.y, 0))) { + if (!aMarks->AppendElement(nsSVGMark(static_cast<float>(segEnd.x), + static_cast<float>(segEnd.y), 0))) { aMarks->Clear(); // OOM, so try to free some return; } if (segType == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH && prevSegType != nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH) { aMarks->ElementAt(aMarks->Length() - 1).angle = //aMarks->ElementAt(pathStartIndex).angle =
--- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -903,16 +903,25 @@ nsSVGElement::WalkContentStyleRules(nsRu animContentStyleRule->RuleMatched(); aRuleWalker->Forward(animContentStyleRule); } } return NS_OK; } +NS_IMETHODIMP_(bool) +nsSVGElement::IsAttributeMapped(const nsIAtom* name) const +{ + if (name == nsGkAtoms::lang) { + return true; + } + return nsSVGElementBase::IsAttributeMapped(name); +} + // PresentationAttributes-FillStroke /* static */ const nsGenericElement::MappedAttributeEntry nsSVGElement::sFillStrokeMap[] = { { &nsGkAtoms::fill }, { &nsGkAtoms::fill_opacity }, { &nsGkAtoms::fill_rule }, { &nsGkAtoms::stroke }, { &nsGkAtoms::stroke_dasharray }, @@ -1150,19 +1159,34 @@ MappedAttrParser::ParseMappedAttrValue(n if (!mDecl) { mDecl = new css::Declaration(); mDecl->InitializeEmpty(); } // Get the nsCSSProperty ID for our mapped attribute. nsCSSProperty propertyID = nsCSSProps::LookupProperty(nsDependentAtomString(aMappedAttrName)); - bool changed; // outparam for ParseProperty. (ignored) - mParser.ParseProperty(propertyID, aMappedAttrValue, mDocURI, mBaseURI, - mNodePrincipal, mDecl, &changed, false); + if (propertyID != eCSSProperty_UNKNOWN) { + bool changed; // outparam for ParseProperty. (ignored) + mParser.ParseProperty(propertyID, aMappedAttrValue, mDocURI, mBaseURI, + mNodePrincipal, mDecl, &changed, false); + return; + } + NS_ABORT_IF_FALSE(aMappedAttrName == nsGkAtoms::lang, + "Only 'lang' should be unrecognized!"); + // nsCSSParser doesn't know about 'lang', so we need to handle it specially. + if (aMappedAttrName == nsGkAtoms::lang) { + propertyID = eCSSProperty__x_lang; + nsCSSExpandedDataBlock block; + mDecl->ExpandTo(&block); + nsCSSValue cssValue(PromiseFlatString(aMappedAttrValue), eCSSUnit_Ident); + block.AddLonghandProperty(propertyID, cssValue); + mDecl->ValueAppended(propertyID); + mDecl->CompressFrom(&block); + } } already_AddRefed<css::StyleRule> MappedAttrParser::CreateStyleRule() { if (!mDecl) { return nsnull; // No mapped attributes were parsed } @@ -1198,16 +1222,26 @@ nsSVGElement::UpdateContentStyleRule() MappedAttrParser mappedAttrParser(doc->CSSLoader(), doc->GetDocumentURI(), GetBaseURI(), NodePrincipal()); for (PRUint32 i = 0; i < attrCount; ++i) { const nsAttrName* attrName = mAttrsAndChildren.AttrNameAt(i); if (!attrName->IsAtom() || !IsAttributeMapped(attrName->Atom())) continue; + if (attrName->NamespaceID() != kNameSpaceID_None && + !attrName->Equals(nsGkAtoms::lang, kNameSpaceID_XML)) { + continue; + } + + if (attrName->Equals(nsGkAtoms::lang, kNameSpaceID_None) && + HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) { + continue; // xml:lang has precedence + } + if (Tag() == nsGkAtoms::svg) { // Special case: we don't want <svg> 'width'/'height' mapped into style // if the attribute value isn't a valid <length> according to SVG (which // only supports a subset of the CSS <length> values). We don't enforce // this by checking the attribute value in nsSVGSVGElement:: // IsAttributeMapped since we don't want that method to depend on the // value of the attribute that is being checked. Rather we just prevent // the actual mapping here, as necessary. @@ -1481,17 +1515,18 @@ nsSVGElement::GetCtx() const ancestor = ancestor->GetFlattenedTreeParent(); } // we don't have an ancestor <svg> element... return nsnull; } /* virtual */ gfxMatrix -nsSVGElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const +nsSVGElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich) const { return aMatrix; } nsSVGElement::LengthAttributesInfo nsSVGElement::GetLengthInfo() { return LengthAttributesInfo(nsnull, nsnull, 0);
--- a/content/svg/content/src/nsSVGElement.h +++ b/content/svg/content/src/nsSVGElement.h @@ -119,16 +119,18 @@ public: virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, PRInt32 aModType) const; virtual bool IsNodeOfType(PRUint32 aFlags) const; NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker); + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const; + static const MappedAttributeEntry sFillStrokeMap[]; static const MappedAttributeEntry sGraphicsMap[]; static const MappedAttributeEntry sTextContentElementsMap[]; static const MappedAttributeEntry sFontSpecificationMap[]; static const MappedAttributeEntry sGradientStopMap[]; static const MappedAttributeEntry sViewportsMap[]; static const MappedAttributeEntry sMarkersMap[]; static const MappedAttributeEntry sColorMap[]; @@ -146,21 +148,46 @@ public: NS_IMETHOD GetOwnerSVGElement(nsIDOMSVGSVGElement** aOwnerSVGElement); NS_IMETHOD GetViewportElement(nsIDOMSVGElement** aViewportElement); // Gets the element that establishes the rectangular viewport against which // we should resolve percentage lengths (our "coordinate context"). Returns // nsnull for outer <svg> or SVG without an <svg> parent (invalid SVG). nsSVGSVGElement* GetCtx() const; + enum TransformTypes { + eAllTransforms + ,eUserSpaceToParent + ,eChildToUserSpace + }; /** - * Returns aMatrix post-multiplied by the transform from the userspace - * established by this element to the userspace established by its parent. + * Returns aMatrix pre-multiplied by (explicit or implicit) transforms that + * are introduced by attributes on this element. + * + * If aWhich is eAllTransforms, then all the transforms from the coordinate + * space established by this element for its children to the coordinate + * space established by this element's parent element for this element, are + * included. + * + * If aWhich is eUserSpaceToParent, then only the transforms from this + * element's userspace to the coordinate space established by its parent is + * included. This includes any transforms introduced by the 'transform' + * attribute, transform animations and animateMotion, but not any offsets + * due to e.g. 'x'/'y' attributes, or any transform due to a 'viewBox' + * attribute. (SVG userspace is defined to be the coordinate space in which + * coordinates on an element apply.) + * + * If aWhich is eChildToUserSpace, then only the transforms from the + * coordinate space established by this element for its childre to this + * elements userspace are included. This includes any offsets due to e.g. + * 'x'/'y' attributes, and any transform due to a 'viewBox' attribute, but + * does not include any transforms due to the 'transform' attribute. */ - virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix) const; + virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich = eAllTransforms) const; // Setter for to set the current <animateMotion> transformation // Only visible for nsSVGGraphicElement, so it's a no-op here, and that // subclass has the useful implementation. virtual void SetAnimateMotionTransform(const gfxMatrix* aMatrix) {/*no-op*/} bool IsStringAnimatable(PRUint8 aAttrEnum) { return GetStringInfo().mStringInfo[aAttrEnum].mIsAnimatable; @@ -203,32 +230,32 @@ public: void DidChangePointList(const nsAttrValue& aEmptyOrOldValue); void DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue); void DidChangeTransformList(const nsAttrValue& aEmptyOrOldValue); void DidChangeString(PRUint8 aAttrEnum) {} void DidChangeStringList(bool aIsConditionalProcessingAttribute, PRUint8 aAttrEnum, const nsAttrValue& aEmptyOrOldValue); - virtual void DidAnimateLength(PRUint8 aAttrEnum); - virtual void DidAnimateNumber(PRUint8 aAttrEnum); - virtual void DidAnimateNumberPair(PRUint8 aAttrEnum); - virtual void DidAnimateInteger(PRUint8 aAttrEnum); - virtual void DidAnimateIntegerPair(PRUint8 aAttrEnum); - virtual void DidAnimateAngle(PRUint8 aAttrEnum); - virtual void DidAnimateBoolean(PRUint8 aAttrEnum); - virtual void DidAnimateEnum(PRUint8 aAttrEnum); - virtual void DidAnimateViewBox(); - virtual void DidAnimatePreserveAspectRatio(); - virtual void DidAnimateNumberList(PRUint8 aAttrEnum); - virtual void DidAnimateLengthList(PRUint8 aAttrEnum); - virtual void DidAnimatePointList(); - virtual void DidAnimatePathSegList(); - virtual void DidAnimateTransformList(); - virtual void DidAnimateString(PRUint8 aAttrEnum); + void DidAnimateLength(PRUint8 aAttrEnum); + void DidAnimateNumber(PRUint8 aAttrEnum); + void DidAnimateNumberPair(PRUint8 aAttrEnum); + void DidAnimateInteger(PRUint8 aAttrEnum); + void DidAnimateIntegerPair(PRUint8 aAttrEnum); + void DidAnimateAngle(PRUint8 aAttrEnum); + void DidAnimateBoolean(PRUint8 aAttrEnum); + void DidAnimateEnum(PRUint8 aAttrEnum); + void DidAnimateViewBox(); + void DidAnimatePreserveAspectRatio(); + void DidAnimateNumberList(PRUint8 aAttrEnum); + void DidAnimateLengthList(PRUint8 aAttrEnum); + void DidAnimatePointList(); + void DidAnimatePathSegList(); + void DidAnimateTransformList(); + void DidAnimateString(PRUint8 aAttrEnum); nsSVGLength2* GetAnimatedLength(const nsIAtom *aAttrName); void GetAnimatedLengthValues(float *aFirst, ...); void GetAnimatedNumberValues(float *aFirst, ...); void GetAnimatedIntegerValues(PRInt32 *aFirst, ...); SVGAnimatedNumberList* GetAnimatedNumberList(PRUint8 aAttrEnum); SVGAnimatedNumberList* GetAnimatedNumberList(nsIAtom *aAttrName); void GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...);
--- a/content/svg/content/src/nsSVGForeignObjectElement.cpp +++ b/content/svg/content/src/nsSVGForeignObjectElement.cpp @@ -107,26 +107,38 @@ NS_IMETHODIMP nsSVGForeignObjectElement: { return mLengthAttributes[HEIGHT].ToDOMAnimatedLength(aHeight, this); } //---------------------------------------------------------------------- // nsSVGElement methods /* virtual */ gfxMatrix -nsSVGForeignObjectElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const +nsSVGForeignObjectElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich) const { + NS_ABORT_IF_FALSE(aWhich != eChildToUserSpace || aMatrix.IsIdentity(), + "Skipping eUserSpaceToParent transforms makes no sense"); + // 'transform' attribute: - gfxMatrix matrix = nsSVGForeignObjectElementBase::PrependLocalTransformTo(aMatrix); - - // now translate by our 'x' and 'y': + gfxMatrix fromUserSpace = + nsSVGForeignObjectElementBase::PrependLocalTransformsTo(aMatrix, aWhich); + if (aWhich == eUserSpaceToParent) { + return fromUserSpace; + } + // our 'x' and 'y' attributes: float x, y; const_cast<nsSVGForeignObjectElement*>(this)-> GetAnimatedLengthValues(&x, &y, nsnull); - return gfxMatrix().Translate(gfxPoint(x, y)) * matrix; + gfxMatrix toUserSpace = gfxMatrix().Translate(gfxPoint(x, y)); + if (aWhich == eChildToUserSpace) { + return toUserSpace; + } + NS_ABORT_IF_FALSE(aWhich == eAllTransforms, "Unknown TransformTypes"); + return toUserSpace * fromUserSpace; } //---------------------------------------------------------------------- // nsIContent methods NS_IMETHODIMP_(bool) nsSVGForeignObjectElement::IsAttributeMapped(const nsIAtom* name) const {
--- a/content/svg/content/src/nsSVGForeignObjectElement.h +++ b/content/svg/content/src/nsSVGForeignObjectElement.h @@ -64,17 +64,18 @@ public: NS_DECL_NSIDOMSVGFOREIGNOBJECTELEMENT // xxx I wish we could use virtual inheritance NS_FORWARD_NSIDOMNODE(nsSVGForeignObjectElementBase::) NS_FORWARD_NSIDOMELEMENT(nsSVGForeignObjectElementBase::) NS_FORWARD_NSIDOMSVGELEMENT(nsSVGForeignObjectElementBase::) // nsSVGElement specializations: - virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix) const; + virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich = eAllTransforms) const; // nsIContent interface NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const; virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; virtual nsXPCClassInfo* GetClassInfo(); protected:
--- a/content/svg/content/src/nsSVGGraphicElement.cpp +++ b/content/svg/content/src/nsSVGGraphicElement.cpp @@ -184,20 +184,35 @@ nsSVGGraphicElement::IsAttributeMapped(c bool nsSVGGraphicElement::IsEventName(nsIAtom* aName) { return nsContentUtils::IsEventAttributeName(aName, EventNameType_SVGGraphic); } gfxMatrix -nsSVGGraphicElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const +nsSVGGraphicElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich) const { + NS_ABORT_IF_FALSE(aWhich != eChildToUserSpace || aMatrix.IsIdentity(), + "Skipping eUserSpaceToParent transforms makes no sense"); + gfxMatrix result(aMatrix); + if (aWhich == eChildToUserSpace) { + // We don't have anything to prepend. + // eChildToUserSpace is not the common case, which is why we return + // 'result' to benefit from NRVO rather than returning aMatrix before + // creating 'result'. + return result; + } + + NS_ABORT_IF_FALSE(aWhich == eAllTransforms || aWhich == eUserSpaceToParent, + "Unknown TransformTypes"); + // animateMotion's resulting transform is supposed to apply *on top of* // any transformations from the |transform| attribute. So since we're // PRE-multiplying, we need to apply the animateMotion transform *first*. if (mAnimateMotionTransform) { result.PreMultiply(*mAnimateMotionTransform); } if (mTransforms) {
--- a/content/svg/content/src/nsSVGGraphicElement.h +++ b/content/svg/content/src/nsSVGGraphicElement.h @@ -57,17 +57,18 @@ public: // interfaces: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMSVGLOCATABLE NS_DECL_NSIDOMSVGTRANSFORMABLE // nsIContent interface NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const; - virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix) const; + virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich = eAllTransforms) const; virtual void SetAnimateMotionTransform(const gfxMatrix* aMatrix); virtual mozilla::SVGAnimatedTransformList* GetAnimatedTransformList(); virtual nsIAtom* GetTransformListAttrName() const { return nsGkAtoms::transform; } protected:
--- a/content/svg/content/src/nsSVGPathElement.cpp +++ b/content/svg/content/src/nsSVGPathElement.cpp @@ -435,17 +435,17 @@ nsSVGPathElement::GetPathLengthScale(Pat if (mPathLength.IsExplicitlySet()) { float authorsPathLengthEstimate = mPathLength.GetAnimValue(); if (authorsPathLengthEstimate > 0) { gfxMatrix matrix; if (aFor == eForTextPath) { // For textPath, a transform on the referenced path affects the // textPath layout, so when calculating the actual path length // we need to take that into account. - matrix = PrependLocalTransformTo(matrix); + matrix = PrependLocalTransformsTo(matrix); } nsRefPtr<gfxFlattenedPath> path = GetFlattenedPath(matrix); if (path) { return path->GetLength() / authorsPathLengthEstimate; } } } return 1.0;
--- a/content/svg/content/src/nsSVGSVGElement.cpp +++ b/content/svg/content/src/nsSVGSVGElement.cpp @@ -745,17 +745,26 @@ nsSVGSVGElement::GetCTM(nsIDOMSVGMatrix NS_IF_ADDREF(*aCTM); return NS_OK; } /* nsIDOMSVGMatrix getScreenCTM (); */ NS_IMETHODIMP nsSVGSVGElement::GetScreenCTM(nsIDOMSVGMatrix **aCTM) { - gfxMatrix m = nsSVGUtils::GetCTM(this, true); + gfxMatrix m; + if (IsRoot()) { + // Consistency with other elements would have us return only the + // eFromUserSpace transforms, but this is what we've been doing for + // a while, and it keeps us consistent with WebKit and Opera (if not + // really with the ambiguous spec). + m = PrependLocalTransformsTo(m); + } else { + m = nsSVGUtils::GetCTM(this, true); + } *aCTM = m.IsSingular() ? nsnull : new DOMSVGMatrix(m); NS_IF_ADDREF(*aCTM); return NS_OK; } /* nsIDOMSVGMatrix getTransformToElement (in nsIDOMSVGElement element); */ NS_IMETHODIMP nsSVGSVGElement::GetTransformToElement(nsIDOMSVGElement *element, @@ -987,16 +996,20 @@ nsSVGSVGElement::GetViewBoxTransform() c nsSVGSVGElement *ctx = GetCtx(); viewportWidth = mLengthAttributes[WIDTH].GetAnimValue(ctx); viewportHeight = mLengthAttributes[HEIGHT].GetAnimValue(ctx); } else { viewportWidth = mViewportWidth; viewportHeight = mViewportHeight; } + if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) { + return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular + } + nsSVGViewBoxRect viewBox; if (mViewBox.IsValid()) { viewBox = mViewBox.GetAnimValue(); } else { viewBox.x = viewBox.y = 0.0f; if (ShouldSynthesizeViewBox()) { // Special case -- fake a viewBox, using height & width attrs. // (Use |this| as context, since if we get here, we're outermost <svg>.) @@ -1175,22 +1188,39 @@ nsSVGSVGElement::GetLength(PRUint8 aCtxT } return 0; } //---------------------------------------------------------------------- // nsSVGElement methods /* virtual */ gfxMatrix -nsSVGSVGElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const +nsSVGSVGElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich) const { + NS_ABORT_IF_FALSE(aWhich != eChildToUserSpace || aMatrix.IsIdentity(), + "Skipping eUserSpaceToParent transforms makes no sense"); + if (IsInner()) { float x, y; const_cast<nsSVGSVGElement*>(this)->GetAnimatedLengthValues(&x, &y, nsnull); - return GetViewBoxTransform() * gfxMatrix().Translate(gfxPoint(x, y)) * aMatrix; + if (aWhich == eAllTransforms) { + // the common case + return GetViewBoxTransform() * gfxMatrix().Translate(gfxPoint(x, y)) * aMatrix; + } + if (aWhich == eUserSpaceToParent) { + return gfxMatrix().Translate(gfxPoint(x, y)) * aMatrix; + } + NS_ABORT_IF_FALSE(aWhich == eChildToUserSpace, "Unknown TransformTypes"); + return GetViewBoxTransform(); // no need to multiply identity aMatrix + } + + if (aWhich == eUserSpaceToParent) { + // only inner-<svg> has eUserSpaceToParent transforms + return aMatrix; } if (IsRoot()) { gfxMatrix zoomPanTM; zoomPanTM.Translate(gfxPoint(mCurrentTranslate.GetX(), mCurrentTranslate.GetY())); zoomPanTM.Scale(mCurrentScale, mCurrentScale); return GetViewBoxTransform() * zoomPanTM * aMatrix; }