diff --git a/accessible/src/html/nsHyperTextAccessible.cpp b/accessible/src/html/nsHyperTextAccessible.cpp
index 1881230a7ab6..3bcce90b9b16 100644
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -908,17 +908,9 @@ nsresult nsHyperTextAccessible::GetTextHelper(EGetTextType aType, nsAccessibleTe
// or the start of a new line. Getting text at the line should provide the line with the visual caret,
// otherwise screen readers will announce the wrong line as the user presses up or down arrow and land
// at the end of a line.
- nsCOMPtr domSel;
- nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
- nsnull, getter_AddRefs(domSel));
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr privateSelection(do_QueryInterface(domSel));
- nsRefPtr frameSelection;
- rv = privateSelection->GetFrameSelection(getter_AddRefs(frameSelection));
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (frameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
+ nsRefPtr frameSelection = FrameSelection();
+ if (frameSelection &&
+ frameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
-- aOffset; // We are at the start of a line
}
}
@@ -1587,29 +1579,27 @@ nsHyperTextAccessible::SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos)
// If range 0 was successfully set, clear any additional selection
// ranges remaining from previous selection
- nsCOMPtr domSel;
- nsCOMPtr selCon;
- GetSelections(nsISelectionController::SELECTION_NORMAL,
- getter_AddRefs(selCon), getter_AddRefs(domSel));
- if (domSel) {
- PRInt32 numRanges;
- domSel->GetRangeCount(&numRanges);
+ nsRefPtr frameSelection = FrameSelection();
+ NS_ENSURE_STATE(frameSelection);
- for (PRInt32 count = 0; count < numRanges - 1; count ++) {
- nsCOMPtr range;
- domSel->GetRangeAt(1, getter_AddRefs(range));
- domSel->RemoveRange(range);
- }
- }
-
- if (selCon) {
- // XXX I'm not sure this can do synchronous scrolling. If the last param is
- // set to true, this calling might flush the pending reflow. See bug 418470.
- selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
- nsISelectionController::SELECTION_FOCUS_REGION, 0);
+ nsCOMPtr domSel =
+ frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
+ NS_ENSURE_STATE(domSel);
+
+ PRInt32 numRanges = 0;
+ domSel->GetRangeCount(&numRanges);
+
+ for (PRInt32 count = 0; count < numRanges - 1; count ++) {
+ nsCOMPtr range;
+ domSel->GetRangeAt(1, getter_AddRefs(range));
+ domSel->RemoveRange(range);
}
- return NS_OK;
+ // XXX I'm not sure this can do synchronous scrolling. If the last param is
+ // set to true, this calling might flush the pending reflow. See bug 418470.
+ return frameSelection->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
+ nsISelectionController::SELECTION_FOCUS_REGION,
+ 0);
}
NS_IMETHODIMP
@@ -1635,13 +1625,15 @@ nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
// Turn the focus node and offset of the selection into caret hypretext
// offset.
- nsCOMPtr domSel;
- nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
- nsnull, getter_AddRefs(domSel));
- NS_ENSURE_SUCCESS(rv, rv);
+ nsRefPtr frameSelection = FrameSelection();
+ NS_ENSURE_STATE(frameSelection);
+
+ nsISelection* domSel =
+ frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
+ NS_ENSURE_STATE(domSel);
nsCOMPtr focusDOMNode;
- rv = domSel->GetFocusNode(getter_AddRefs(focusDOMNode));
+ nsresult rv = domSel->GetFocusNode(getter_AddRefs(focusDOMNode));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 focusOffset;
@@ -1665,18 +1657,19 @@ nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
return NS_OK;
}
-PRInt32 nsHyperTextAccessible::GetCaretLineNumber()
+PRInt32
+nsHyperTextAccessible::GetCaretLineNumber()
{
// Provide the line number for the caret, relative to the
// currently focused node. Use a 1-based index
- nsCOMPtr domSel;
- GetSelections(nsISelectionController::SELECTION_NORMAL, nsnull,
- getter_AddRefs(domSel));
- nsCOMPtr privateSelection(do_QueryInterface(domSel));
- NS_ENSURE_TRUE(privateSelection, -1);
- nsRefPtr frameSelection;
- privateSelection->GetFrameSelection(getter_AddRefs(frameSelection));
- NS_ENSURE_TRUE(frameSelection, -1);
+ nsRefPtr frameSelection = FrameSelection();
+ if (!frameSelection)
+ return -1;
+
+ nsISelection* domSel =
+ frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
+ if (!domSel)
+ return - 1;
nsCOMPtr caretNode;
domSel->GetFocusNode(getter_AddRefs(caretNode));
@@ -1731,104 +1724,70 @@ PRInt32 nsHyperTextAccessible::GetCaretLineNumber()
return lineNumber;
}
-nsresult
-nsHyperTextAccessible::GetSelections(PRInt16 aType,
- nsISelectionController **aSelCon,
- nsISelection **aDomSel,
- nsCOMArray* aRanges)
+already_AddRefed
+nsHyperTextAccessible::FrameSelection()
{
- if (IsDefunct())
- return NS_ERROR_FAILURE;
+ nsIFrame* frame = GetFrame();
+ return frame->GetFrameSelection();
+}
- if (aSelCon) {
- *aSelCon = nsnull;
- }
- if (aDomSel) {
- *aDomSel = nsnull;
- }
- if (aRanges) {
- aRanges->Clear();
- }
-
- nsCOMPtr domSel;
- nsCOMPtr selCon;
+void
+nsHyperTextAccessible::GetSelectionDOMRanges(PRInt16 aType,
+ nsCOMArray* aRanges)
+{
+ nsRefPtr frameSelection = FrameSelection();
+ if (!frameSelection)
+ return;
+
+ nsISelection* domSel = frameSelection->GetSelection(aType);
+ if (!domSel)
+ return;
+
+ nsCOMPtr startNode = GetNode();
nsCOMPtr editor;
GetAssociatedEditor(getter_AddRefs(editor));
- nsCOMPtr peditor(do_QueryInterface(editor));
- if (peditor) {
- // Case 1: plain text editor
- // This is for form controls which have their own
- // selection controller separate from the document, for example
- // HTML:input, HTML:textarea, XUL:textbox, etc.
- editor->GetSelectionController(getter_AddRefs(selCon));
- }
- else {
- // Case 2: rich content subtree (can be rich editor)
- // This uses the selection controller from the entire document
- nsIFrame *frame = GetFrame();
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
-
- // Get the selection and selection controller
- frame->GetSelectionController(GetPresContext(),
- getter_AddRefs(selCon));
- }
- NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
-
- selCon->GetSelection(aType, getter_AddRefs(domSel));
- NS_ENSURE_TRUE(domSel, NS_ERROR_FAILURE);
-
- if (aSelCon) {
- NS_ADDREF(*aSelCon = selCon);
- }
- if (aDomSel) {
- NS_ADDREF(*aDomSel = domSel);
+ if (editor) {
+ nsCOMPtr editorRoot;
+ editor->GetRootElement(getter_AddRefs(editorRoot));
+ startNode = do_QueryInterface(editorRoot);
}
- if (aRanges) {
- nsCOMPtr privSel(do_QueryInterface(domSel));
+ if (!startNode)
+ return;
- nsCOMPtr startNode = GetNode();
- if (peditor) {
- nsCOMPtr editorRoot;
- editor->GetRootElement(getter_AddRefs(editorRoot));
- startNode = do_QueryInterface(editorRoot);
- }
- NS_ENSURE_STATE(startNode);
+ PRUint32 childCount = startNode->GetChildCount();
+ nsCOMPtr startDOMNode(do_QueryInterface(startNode));
+ nsCOMPtr privSel(do_QueryInterface(domSel));
+ nsresult rv = privSel->
+ GetRangesForIntervalCOMArray(startDOMNode, 0, startDOMNode, childCount,
+ true, aRanges);
+ NS_ENSURE_SUCCESS(rv,);
- PRUint32 childCount = startNode->GetChildCount();
- nsCOMPtr startDOMNode(do_QueryInterface(startNode));
- nsresult rv = privSel->
- GetRangesForIntervalCOMArray(startDOMNode, 0, startDOMNode, childCount,
- PR_TRUE, aRanges);
- NS_ENSURE_SUCCESS(rv, rv);
- // Remove collapsed ranges
- PRInt32 numRanges = aRanges->Count();
- for (PRInt32 count = 0; count < numRanges; count ++) {
- bool isCollapsed;
- (*aRanges)[count]->GetCollapsed(&isCollapsed);
- if (isCollapsed) {
- aRanges->RemoveObjectAt(count);
- -- numRanges;
- -- count;
- }
+ // Remove collapsed ranges
+ PRInt32 numRanges = aRanges->Count();
+ for (PRInt32 count = 0; count < numRanges; count ++) {
+ bool isCollapsed = false;
+ (*aRanges)[count]->GetCollapsed(&isCollapsed);
+ if (isCollapsed) {
+ aRanges->RemoveObjectAt(count);
+ --numRanges;
+ --count;
}
}
-
- return NS_OK;
}
/*
* Gets the number of selected regions.
*/
-NS_IMETHODIMP nsHyperTextAccessible::GetSelectionCount(PRInt32 *aSelectionCount)
+NS_IMETHODIMP
+nsHyperTextAccessible::GetSelectionCount(PRInt32* aSelectionCount)
{
- nsCOMPtr domSel;
- nsCOMArray ranges;
- nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
- nsnull, nsnull, &ranges);
- NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_ARG_POINTER(aSelectionCount);
+ *aSelectionCount = 0;
+ nsCOMArray ranges;
+ GetSelectionDOMRanges(nsISelectionController::SELECTION_NORMAL, &ranges);
*aSelectionCount = ranges.Count();
return NS_OK;
@@ -1837,15 +1796,17 @@ NS_IMETHODIMP nsHyperTextAccessible::GetSelectionCount(PRInt32 *aSelectionCount)
/*
* Gets the start and end offset of the specified selection.
*/
-NS_IMETHODIMP nsHyperTextAccessible::GetSelectionBounds(PRInt32 aSelectionNum, PRInt32 *aStartOffset, PRInt32 *aEndOffset)
+NS_IMETHODIMP
+nsHyperTextAccessible::GetSelectionBounds(PRInt32 aSelectionNum,
+ PRInt32* aStartOffset,
+ PRInt32* aEndOffset)
{
+ NS_ENSURE_ARG_POINTER(aStartOffset);
+ NS_ENSURE_ARG_POINTER(aEndOffset);
*aStartOffset = *aEndOffset = 0;
- nsCOMPtr domSel;
nsCOMArray ranges;
- nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
- nsnull, getter_AddRefs(domSel), &ranges);
- NS_ENSURE_SUCCESS(rv, rv);
+ GetSelectionDOMRanges(nsISelectionController::SELECTION_NORMAL, &ranges);
PRInt32 rangeCount = ranges.Count();
if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
@@ -1857,18 +1818,19 @@ NS_IMETHODIMP nsHyperTextAccessible::GetSelectionBounds(PRInt32 aSelectionNum, P
nsCOMPtr startDOMNode;
range->GetStartContainer(getter_AddRefs(startDOMNode));
nsCOMPtr startNode(do_QueryInterface(startDOMNode));
- PRInt32 startOffset;
+ PRInt32 startOffset = 0;
range->GetStartOffset(&startOffset);
// Get end point
nsCOMPtr endDOMNode;
range->GetEndContainer(getter_AddRefs(endDOMNode));
nsCOMPtr endNode(do_QueryInterface(endDOMNode));
- PRInt32 endOffset;
+ PRInt32 endOffset = 0;
range->GetEndOffset(&endOffset);
- PRInt16 rangeCompareResult;
- rv = range->CompareBoundaryPoints(nsIDOMRange::START_TO_END, range, &rangeCompareResult);
+ PRInt16 rangeCompareResult = 0;
+ nsresult rv = range->CompareBoundaryPoints(nsIDOMRange::START_TO_END, range,
+ &rangeCompareResult);
NS_ENSURE_SUCCESS(rv, rv);
if (rangeCompareResult < 0) {
@@ -1898,15 +1860,17 @@ nsHyperTextAccessible::SetSelectionBounds(PRInt32 aSelectionNum,
PRInt32 aStartOffset,
PRInt32 aEndOffset)
{
- nsCOMPtr domSel;
- nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
- nsnull, getter_AddRefs(domSel));
- NS_ENSURE_SUCCESS(rv, rv);
+ nsRefPtr frameSelection = FrameSelection();
+ NS_ENSURE_STATE(frameSelection);
+
+ nsCOMPtr domSel =
+ frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
+ NS_ENSURE_STATE(domSel);
// Caret is a collapsed selection
bool isOnlyCaret = (aStartOffset == aEndOffset);
- PRInt32 rangeCount;
+ PRInt32 rangeCount = 0;
domSel->GetRangeCount(&rangeCount);
nsCOMPtr range;
if (aSelectionNum == rangeCount) { // Add a range
@@ -1921,12 +1885,12 @@ nsHyperTextAccessible::SetSelectionBounds(PRInt32 aSelectionNum,
NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
}
- PRInt32 startOffset, endOffset;
+ PRInt32 startOffset = 0, endOffset = 0;
nsCOMPtr startNode, endNode;
- rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset,
- getter_AddRefs(startNode), &startOffset,
- getter_AddRefs(endNode), &endOffset);
+ nsresult rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset,
+ getter_AddRefs(startNode), &startOffset,
+ getter_AddRefs(endNode), &endOffset);
NS_ENSURE_SUCCESS(rv, rv);
rv = range->SetStart(startNode, startOffset);
@@ -1936,23 +1900,30 @@ nsHyperTextAccessible::SetSelectionBounds(PRInt32 aSelectionNum,
range->SetEnd(endNode, endOffset);
NS_ENSURE_SUCCESS(rv, rv);
- if (aSelectionNum == rangeCount) { // Add successfully created new range
+ // If new range was created then add it, otherwise notify selection listeners
+ // that existing selection range was changed.
+ if (aSelectionNum == rangeCount)
return domSel->AddRange(range);
- }
+
+ domSel->RemoveRange(range);
+ domSel->AddRange(range);
return NS_OK;
}
/*
* Adds a selection bounded by the specified offsets.
*/
-NS_IMETHODIMP nsHyperTextAccessible::AddSelection(PRInt32 aStartOffset, PRInt32 aEndOffset)
+NS_IMETHODIMP
+nsHyperTextAccessible::AddSelection(PRInt32 aStartOffset, PRInt32 aEndOffset)
{
- nsCOMPtr domSel;
- nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
- nsnull, getter_AddRefs(domSel));
- NS_ENSURE_SUCCESS(rv, rv);
+ nsRefPtr frameSelection = FrameSelection();
+ NS_ENSURE_STATE(frameSelection);
- PRInt32 rangeCount;
+ nsCOMPtr domSel =
+ frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
+ NS_ENSURE_STATE(domSel);
+
+ PRInt32 rangeCount = 0;
domSel->GetRangeCount(&rangeCount);
return SetSelectionBounds(rangeCount, aStartOffset, aEndOffset);
@@ -1961,12 +1932,15 @@ NS_IMETHODIMP nsHyperTextAccessible::AddSelection(PRInt32 aStartOffset, PRInt32
/*
* Removes the specified selection.
*/
-NS_IMETHODIMP nsHyperTextAccessible::RemoveSelection(PRInt32 aSelectionNum)
+NS_IMETHODIMP
+nsHyperTextAccessible::RemoveSelection(PRInt32 aSelectionNum)
{
- nsCOMPtr domSel;
- nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
- nsnull, getter_AddRefs(domSel));
- NS_ENSURE_SUCCESS(rv, rv);
+ nsRefPtr frameSelection = FrameSelection();
+ NS_ENSURE_STATE(frameSelection);
+
+ nsCOMPtr domSel =
+ frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
+ NS_ENSURE_STATE(domSel);
PRInt32 rangeCount;
domSel->GetRangeCount(&rangeCount);
@@ -2348,9 +2322,7 @@ nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
nsIPersistentProperties *aAttributes)
{
nsCOMArray ranges;
- nsresult rv = GetSelections(nsISelectionController::SELECTION_SPELLCHECK,
- nsnull, nsnull, &ranges);
- NS_ENSURE_SUCCESS(rv, rv);
+ GetSelectionDOMRanges(nsISelectionController::SELECTION_SPELLCHECK, &ranges);
PRInt32 rangeCount = ranges.Count();
if (!rangeCount)
@@ -2362,7 +2334,7 @@ nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
NS_ENSURE_STATE(nsrange);
PRInt16 result;
- rv = nsrange->ComparePoint(aNode, aNodeOffset, &result);
+ nsresult rv = nsrange->ComparePoint(aNode, aNodeOffset, &result);
NS_ENSURE_SUCCESS(rv, rv);
// ComparePoint checks boundary points, but we need to check that
// text at aNodeOffset is inside the range.
@@ -2381,8 +2353,8 @@ nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
if (result == 1) { // range is before point
PRInt32 startHTOffset = 0;
- rv = DOMRangeBoundToHypertextOffset(range, PR_FALSE, PR_TRUE,
- &startHTOffset);
+ nsresult rv = DOMRangeBoundToHypertextOffset(range, PR_FALSE, PR_TRUE,
+ &startHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (startHTOffset > *aHTStartOffset)
@@ -2390,8 +2362,8 @@ nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
} else if (result == -1) { // range is after point
PRInt32 endHTOffset = 0;
- rv = DOMRangeBoundToHypertextOffset(range, PR_TRUE, PR_FALSE,
- &endHTOffset);
+ nsresult rv = DOMRangeBoundToHypertextOffset(range, PR_TRUE, PR_FALSE,
+ &endHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (endHTOffset < *aHTEndOffset)
@@ -2399,8 +2371,8 @@ nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
} else { // point is in range
PRInt32 startHTOffset = 0;
- rv = DOMRangeBoundToHypertextOffset(range, PR_TRUE, PR_TRUE,
- &startHTOffset);
+ nsresult rv = DOMRangeBoundToHypertextOffset(range, PR_TRUE, PR_TRUE,
+ &startHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 endHTOffset = 0;
diff --git a/accessible/src/html/nsHyperTextAccessible.h b/accessible/src/html/nsHyperTextAccessible.h
index 5b396b00407b..45ad50e890d2 100644
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -351,22 +351,15 @@ protected:
// Selection helpers
- /**
- * Get the relevant selection interfaces and ranges for the current hyper
- * text.
- *
- * @param aType [in] the selection type
- * @param aSelCon [out, optional] the selection controller for the current
- * hyper text
- * @param aDomSel [out, optional] the selection interface for the current
- * hyper text
- * @param aRanges [out, optional] the selected ranges within the current
- * subtree
+ /**
+ * Return frame selection object for the accessible.
*/
- nsresult GetSelections(PRInt16 aType,
- nsISelectionController **aSelCon,
- nsISelection **aDomSel = nsnull,
- nsCOMArray* aRanges = nsnull);
+ virtual already_AddRefed FrameSelection();
+
+ /**
+ * Return selection ranges within the accessible subtree.
+ */
+ void GetSelectionDOMRanges(PRInt16 aType, nsCOMArray* aRanges);
nsresult SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos);
diff --git a/accessible/src/xul/nsXULFormControlAccessible.cpp b/accessible/src/xul/nsXULFormControlAccessible.cpp
index 28a041962160..ce460adf0fe2 100644
--- a/accessible/src/xul/nsXULFormControlAccessible.cpp
+++ b/accessible/src/xul/nsXULFormControlAccessible.cpp
@@ -882,6 +882,17 @@ nsXULTextFieldAccessible::CacheChildren()
while ((child = walker.NextChild()) && AppendChild(child));
}
+////////////////////////////////////////////////////////////////////////////////
+// nsXULTextFieldAccessible: nsHyperTextAccessible protected
+
+already_AddRefed
+nsXULTextFieldAccessible::FrameSelection()
+{
+ nsCOMPtr inputContent(GetInputField());
+ nsIFrame* frame = inputContent->GetPrimaryFrame();
+ return frame->GetFrameSelection();
+}
+
////////////////////////////////////////////////////////////////////////////////
// nsXULTextFieldAccessible protected
diff --git a/accessible/src/xul/nsXULFormControlAccessible.h b/accessible/src/xul/nsXULFormControlAccessible.h
index 49ac22c6b11a..726a78993abb 100644
--- a/accessible/src/xul/nsXULFormControlAccessible.h
+++ b/accessible/src/xul/nsXULFormControlAccessible.h
@@ -276,6 +276,9 @@ protected:
// nsAccessible
virtual void CacheChildren();
+ // nsHyperTextAccessible
+ virtual already_AddRefed FrameSelection();
+
// nsXULTextFieldAccessible
already_AddRefed GetInputField() const;
};
diff --git a/accessible/tests/mochitest/Makefile.in b/accessible/tests/mochitest/Makefile.in
index ac5954b8533c..b019906c397b 100644
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -56,6 +56,7 @@ DIRS = \
states \
table \
text \
+ textselection \
tree \
treeupdate \
value \
diff --git a/accessible/tests/mochitest/events.js b/accessible/tests/mochitest/events.js
index 00fc7653c99f..789ebba89681 100644
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -23,6 +23,7 @@ const EVENT_TEXT_ATTRIBUTE_CHANGED = nsIAccessibleEvent.EVENT_TEXT_ATTRIBUTE_CHA
const EVENT_TEXT_CARET_MOVED = nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED;
const EVENT_TEXT_INSERTED = nsIAccessibleEvent.EVENT_TEXT_INSERTED;
const EVENT_TEXT_REMOVED = nsIAccessibleEvent.EVENT_TEXT_REMOVED;
+const EVENT_TEXT_SELECTION_CHANGED = nsIAccessibleEvent.EVENT_TEXT_SELECTION_CHANGED;
const EVENT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_VALUE_CHANGE;
////////////////////////////////////////////////////////////////////////////////
diff --git a/accessible/tests/mochitest/textselection/Makefile.in b/accessible/tests/mochitest/textselection/Makefile.in
new file mode 100644
index 000000000000..d4a8eacef243
--- /dev/null
+++ b/accessible/tests/mochitest/textselection/Makefile.in
@@ -0,0 +1,53 @@
+#
+# ***** 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) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Alexander Surkov (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 *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+relativesrcdir = accessible/textselection
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES = \
+ test_general.html \
+ $(NULL)
+
+libs:: $(_TEST_FILES)
+ $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
diff --git a/accessible/tests/mochitest/textselection/test_general.html b/accessible/tests/mochitest/textselection/test_general.html
new file mode 100644
index 000000000000..2cb1fb60676d
--- /dev/null
+++ b/accessible/tests/mochitest/textselection/test_general.html
@@ -0,0 +1,170 @@
+
+
+
+ Text selection testing
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Mozilla Bug 688126
+
+
+
+
+
+
+ hello
+
+
+
+
+
diff --git a/config/autoconf.mk.in b/config/autoconf.mk.in
index 0265e09c59db..dfe9f3ffec63 100644
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -362,11 +362,6 @@ LIBS_DESC_SUFFIX = @LIBS_DESC_SUFFIX@
USE_N32 = @USE_N32@
HAVE_64BIT_OS = @HAVE_64BIT_OS@
-# Temp hack. It is not my intention to leave this crap in here for ever.
-# Im talking to fur right now to solve the problem without introducing
-# NS_USE_NATIVE to the build system -ramiro.
-NS_USE_NATIVE = @NS_USE_NATIVE@
-
CC = @CC@
CXX = @CXX@
diff --git a/configure.in b/configure.in
index 6ce53031b210..c1b3a54d19fa 100644
--- a/configure.in
+++ b/configure.in
@@ -2488,7 +2488,6 @@ ia64*-hpux*)
if test "$SOLARIS_SUNPRO_CC"; then
LDFLAGS="$LDFLAGS -z ignore -R '\$\$ORIGIN:\$\$ORIGIN/..' -z lazyload -z combreloc -z muldefs"
LIBS="-lCrun -lCstd -lc $LIBS"
- NS_USE_NATIVE=1
AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES)
CFLAGS="$CFLAGS -xlibmieee -xstrconst -xbuiltin=%all -D__FUNCTION__=__func__"
CXXFLAGS="$CXXFLAGS -xlibmieee -xbuiltin=%all -features=tmplife,tmplrefstatic,extensions -norunpath -D__FUNCTION__=__func__ -template=no%extdef"
@@ -8209,7 +8208,6 @@ AC_SUBST(WINDRES)
AC_SUBST(IMPLIB)
AC_SUBST(FILTER)
AC_SUBST(BIN_FLAGS)
-AC_SUBST(NS_USE_NATIVE)
AC_SUBST(MOZ_WIDGET_TOOLKIT)
AC_SUBST(MOZ_UPDATE_XTERM)
AC_SUBST(MOZ_PLATFORM_MAEMO)
diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp
index b6bd21c94bb7..91a97f5aa835 100644
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1099,8 +1099,18 @@ nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
nsIScriptSecurityManager::STANDARD);
NS_ENSURE_SUCCESS(rv, rv);
- rv = requestingPrincipal->CheckMayLoad(aURI, PR_TRUE);
- NS_ENSURE_SUCCESS(rv, rv);
+ // Allow data URIs (let them skip the CheckMayLoad call), since we want
+ // to allow external resources from data URIs regardless of the difference
+ // in URI scheme.
+ bool doesInheritSecurityContext;
+ rv =
+ NS_URIChainHasFlags(aURI,
+ nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
+ &doesInheritSecurityContext);
+ if (NS_FAILED(rv) || !doesInheritSecurityContext) {
+ rv = requestingPrincipal->CheckMayLoad(aURI, PR_TRUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER,
diff --git a/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp
index f5210df21da5..da91aaaac0a6 100644
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -105,6 +105,10 @@ static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc");
#define LOG(args) PR_LOG(gObjectLog, PR_LOG_DEBUG, args)
#define LOG_ENABLED() PR_LOG_TEST(gObjectLog, PR_LOG_DEBUG)
+#ifdef ANDROID
+#include "nsXULAppAPI.h"
+#endif
+
class nsAsyncInstantiateEvent : public nsRunnable {
public:
// This stores both the content and the frame so that Instantiate calls can be
@@ -200,6 +204,9 @@ nsPluginErrorEvent::Run()
mContent.get()));
nsString type;
switch (mState) {
+ case ePluginClickToPlay:
+ type = NS_LITERAL_STRING("PluginClickToPlay");
+ break;
case ePluginUnsupported:
type = NS_LITERAL_STRING("PluginNotFound");
break;
@@ -1056,7 +1063,11 @@ nsObjectLoadingContent::ObjectState() const
case eType_Image:
return ImageState();
case eType_Plugin:
- case eType_Document:
+#ifdef ANDROID
+ if (XRE_GetProcessType() == GeckoProcessType_Content)
+ return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY;
+#endif
+ case eType_Document:
// These are OK. If documents start to load successfully, they display
// something, and are thus not broken in this sense. The same goes for
// plugins.
@@ -1070,6 +1081,8 @@ nsObjectLoadingContent::ObjectState() const
// Otherwise, broken
nsEventStates state = NS_EVENT_STATE_BROKEN;
switch (mFallbackReason) {
+ case ePluginClickToPlay:
+ return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY;
case ePluginDisabled:
state |= NS_EVENT_STATE_HANDLER_DISABLED;
break;
@@ -1949,6 +1962,10 @@ nsObjectLoadingContent::GetPluginSupportState(nsIContent* aContent,
/* static */ PluginSupportState
nsObjectLoadingContent::GetPluginDisabledState(const nsCString& aContentType)
{
+#ifdef ANDROID
+ if (XRE_GetProcessType() == GeckoProcessType_Content)
+ return ePluginClickToPlay;
+#endif
nsCOMPtr pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
nsPluginHost *pluginHost = static_cast(pluginHostCOM.get());
if (!pluginHost) {
diff --git a/content/base/src/nsObjectLoadingContent.h b/content/base/src/nsObjectLoadingContent.h
index 003f4dd9e996..29be88848bb1 100644
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -65,7 +65,8 @@ enum PluginSupportState {
ePluginBlocklisted, // The plugin is blocklisted and disabled
ePluginOutdated, // The plugin is considered outdated, but not disabled
ePluginOtherState, // Something else (e.g. uninitialized or not a plugin)
- ePluginCrashed
+ ePluginCrashed,
+ ePluginClickToPlay
};
/**
diff --git a/content/base/src/nsRange.cpp b/content/base/src/nsRange.cpp
index 581f763d9abd..7eb76b070a39 100644
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -357,7 +357,7 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument,
DoSetRange(newStartNode, newStartOffset, newEndNode, newEndOffset,
newRoot ? newRoot : mRoot.get()
#ifdef DEBUG
- , !newEndNode->GetParent()
+ , !newEndNode->GetParent() || !newStartNode->GetParent()
#endif
);
}
diff --git a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
index f943d9b249f0..58c26e54e60a 100644
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -3066,6 +3066,8 @@ struct NS_STACK_CLASS nsCanvasBidiProcessorAzure : public nsBidiPresUtils::BidiP
Point baselineOrigin =
Point(point.x * devUnitsPerAppUnit, point.y * devUnitsPerAppUnit);
+ float advanceSum = 0;
+
for (PRUint32 c = 0; c < numRuns; c++) {
gfxFont *font = runs[c].mFont;
PRUint32 endRun = 0;
@@ -3084,8 +3086,6 @@ struct NS_STACK_CLASS nsCanvasBidiProcessorAzure : public nsBidiPresUtils::BidiP
std::vector glyphBuf;
- float advanceSum = 0;
-
for (PRUint32 i = runs[c].mCharacterOffset; i < endRun; i++) {
Glyph newGlyph;
if (glyphs[i].IsSimpleGlyph()) {
diff --git a/content/events/public/nsEventStates.h b/content/events/public/nsEventStates.h
index 97078ce864a8..6f2fd4e2c483 100644
--- a/content/events/public/nsEventStates.h
+++ b/content/events/public/nsEventStates.h
@@ -264,6 +264,8 @@ private:
// Content is the full screen element, or a frame containing the
// current full-screen element.
#define NS_EVENT_STATE_FULL_SCREEN NS_DEFINE_EVENT_STATE_MACRO(34)
+// Handler for click to play plugin
+#define NS_EVENT_STATE_TYPE_CLICK_TO_PLAY NS_DEFINE_EVENT_STATE_MACRO(35)
/**
* NOTE: do not go over 63 without updating nsEventStates::InternalType!
diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp
index 88c0c143774b..f41230b49b75 100644
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -4888,14 +4888,7 @@ nsWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
NS_ASSERTION(sgo, "nativeObj not a global object!");
nsGlobalWindow *win = nsGlobalWindow::FromSupports(nativeObj);
- if (win->IsOuterWindow()) {
- if (!win->EnsureInnerWindow()) {
- return NS_ERROR_FAILURE;
- }
-
- *parentObj = win->GetCurrentInnerWindowInternal()->FastGetGlobalJSObject();
- return NS_OK;
- }
+ NS_ASSERTION(win->IsInnerWindow(), "Should be inner window.");
JSObject *winObj = win->FastGetGlobalJSObject();
if (!winObj) {
diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
index 8c4f00f26822..758f21f0bba7 100644
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -299,6 +299,16 @@ public:
// nsISupports
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ // nsWrapperCache
+ JSObject *WrapObject(JSContext *cx, XPCWrappedNativeScope *scope,
+ bool *triedToWrap)
+ {
+ NS_ASSERTION(IsOuterWindow(),
+ "Inner window supports nsWrapperCache, fix WrapObject!");
+ *triedToWrap = true;
+ return EnsureInnerWindow() ? GetWrapper() : nsnull;
+ }
+
// nsIScriptGlobalObject
virtual nsIScriptContext *GetContext();
virtual JSObject *GetGlobalJSObject();
diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp
index a1e9675d0c55..3921578b7dde 100644
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -94,6 +94,7 @@
#include "nsXPCOMCID.h"
#include "nsISupportsPrimitives.h"
+#include "nsXULAppAPI.h"
#include "nsIXULRuntime.h"
// for the dialog
@@ -2251,6 +2252,11 @@ nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
nsresult nsPluginHost::LoadPlugins()
{
+#ifdef ANDROID
+ if (XRE_GetProcessType() == GeckoProcessType_Content) {
+ return NS_OK;
+ }
+#endif
// do not do anything if it is already done
// use ReloadPlugins() to enforce loading
if (mPluginsLoaded)
diff --git a/gfx/cairo/cairo/src/cairo-image-surface.c b/gfx/cairo/cairo/src/cairo-image-surface.c
index 2f0cf031aca7..57548322a38c 100644
--- a/gfx/cairo/cairo/src/cairo-image-surface.c
+++ b/gfx/cairo/cairo/src/cairo-image-surface.c
@@ -326,7 +326,7 @@ _cairo_image_surface_create_with_pixman_format (unsigned char *data,
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
}
- pixman_image = pixman_image_create_bits (pixman_format, width ? width : 1, height ? height : 1,
+ pixman_image = pixman_image_create_bits (pixman_format, width, height,
(uint32_t *) data, stride ? stride : 4);
if (unlikely (pixman_image == NULL))
diff --git a/gfx/layers/d3d10/ContainerLayerD3D10.cpp b/gfx/layers/d3d10/ContainerLayerD3D10.cpp
index 9a39d1709fb1..bb2030334c97 100644
--- a/gfx/layers/d3d10/ContainerLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ContainerLayerD3D10.cpp
@@ -201,9 +201,17 @@ ContainerLayerD3D10::RenderLayer()
desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
desc.SampleDesc.Count = 1;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
- device()->CreateTexture2D(&desc, NULL, getter_AddRefs(renderTexture));
-
- device()->CreateRenderTargetView(renderTexture, NULL, getter_AddRefs(rtView));
+ HRESULT hr;
+ hr = device()->CreateTexture2D(&desc, NULL, getter_AddRefs(renderTexture));
+
+ if (FAILED(hr)) {
+ LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("Failed to create new texture for ContainerLayerD3D10!"),
+ hr);
+ return;
+ }
+
+ hr = device()->CreateRenderTargetView(renderTexture, NULL, getter_AddRefs(rtView));
+ NS_ASSERTION(SUCCEEDED(hr), "Failed to create render target view for ContainerLayerD3D10!");
effect()->GetVariableByName("vRenderTargetOffset")->
GetRawValue(previousRenderTargetOffset, 0, 8);
diff --git a/ipc/chromium/src/base/dir_reader_fallback.h b/ipc/chromium/src/base/dir_reader_fallback.h
new file mode 100644
index 000000000000..398f51b2557f
--- /dev/null
+++ b/ipc/chromium/src/base/dir_reader_fallback.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_DIR_READER_FALLBACK_H_
+#define BASE_DIR_READER_FALLBACK_H_
+#pragma once
+
+namespace base {
+
+class DirReaderFallback {
+ public:
+ // Open a directory. If |IsValid| is true, then |Next| can be called to start
+ // the iteration at the beginning of the directory.
+ explicit DirReaderFallback(const char* directory_path) { }
+ // After construction, IsValid returns true iff the directory was
+ // successfully opened.
+ bool IsValid() const { return false; }
+ // Move to the next entry returning false if the iteration is complete.
+ bool Next() { return false; }
+ // Return the name of the current directory entry.
+ const char* name() { return 0;}
+ // Return the file descriptor which is being used.
+ int fd() const { return -1; }
+ // Returns true if this is a no-op fallback class (for testing).
+ static bool IsFallback() { return true; }
+};
+
+} // namespace base
+
+#endif // BASE_DIR_READER_FALLBACK_H_
diff --git a/ipc/chromium/src/base/dir_reader_linux.h b/ipc/chromium/src/base/dir_reader_linux.h
new file mode 100644
index 000000000000..f1ec4b7144f1
--- /dev/null
+++ b/ipc/chromium/src/base/dir_reader_linux.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_DIR_READER_LINUX_H_
+#define BASE_DIR_READER_LINUX_H_
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#include "base/logging.h"
+#include "base/eintr_wrapper.h"
+
+// See the comments in dir_reader_posix.h about this.
+
+namespace base {
+
+struct linux_dirent {
+ uint64_t d_ino;
+ int64_t d_off;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ char d_name[0];
+};
+
+class DirReaderLinux {
+ public:
+ explicit DirReaderLinux(const char* directory_path)
+ : fd_(open(directory_path, O_RDONLY | O_DIRECTORY)),
+ offset_(0),
+ size_(0) {
+ memset(buf_, 0, sizeof(buf_));
+ }
+
+ ~DirReaderLinux() {
+ if (fd_ >= 0) {
+ if (HANDLE_EINTR(close(fd_)))
+ DLOG(ERROR) << "Failed to close directory handle";
+ }
+ }
+
+ bool IsValid() const {
+ return fd_ >= 0;
+ }
+
+ // Move to the next entry returning false if the iteration is complete.
+ bool Next() {
+ if (size_) {
+ linux_dirent* dirent = reinterpret_cast(&buf_[offset_]);
+ offset_ += dirent->d_reclen;
+ }
+
+ if (offset_ != size_)
+ return true;
+
+ const int r = syscall(__NR_getdents64, fd_, buf_, sizeof(buf_));
+ if (r == 0)
+ return false;
+ if (r == -1) {
+ DLOG(ERROR) << "getdents64 returned an error: " << errno;
+ return false;
+ }
+ size_ = r;
+ offset_ = 0;
+ return true;
+ }
+
+ const char* name() const {
+ if (!size_)
+ return NULL;
+
+ const linux_dirent* dirent =
+ reinterpret_cast(&buf_[offset_]);
+ return dirent->d_name;
+ }
+
+ int fd() const {
+ return fd_;
+ }
+
+ static bool IsFallback() {
+ return false;
+ }
+
+ private:
+ const int fd_;
+ unsigned char buf_[512];
+ size_t offset_, size_;
+
+ DISALLOW_COPY_AND_ASSIGN(DirReaderLinux);
+};
+
+} // namespace base
+
+#endif // BASE_DIR_READER_LINUX_H_
diff --git a/ipc/chromium/src/base/dir_reader_posix.h b/ipc/chromium/src/base/dir_reader_posix.h
new file mode 100644
index 000000000000..f591ae05783b
--- /dev/null
+++ b/ipc/chromium/src/base/dir_reader_posix.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_DIR_READER_POSIX_H_
+#define BASE_DIR_READER_POSIX_H_
+#pragma once
+
+#include "build/build_config.h"
+
+// This header provides a class, DirReaderPosix, which allows one to open and
+// read from directories without allocating memory. For the interface, see
+// the generic fallback in dir_reader_fallback.h.
+
+// Mac note: OS X has getdirentries, but it only works if we restrict Chrome to
+// 32-bit inodes. There is a getdirentries64 syscall in 10.6, but it's not
+// wrapped and the direct syscall interface is unstable. Using an unstable API
+// seems worse than falling back to enumerating all file descriptors so we will
+// probably never implement this on the Mac.
+
+#if defined(OS_LINUX)
+#include "base/dir_reader_linux.h"
+#else
+#include "base/dir_reader_fallback.h"
+#endif
+
+namespace base {
+
+#if defined(OS_LINUX)
+typedef DirReaderLinux DirReaderPosix;
+#else
+typedef DirReaderFallback DirReaderPosix;
+#endif
+
+} // namespace base
+
+#endif // BASE_DIR_READER_POSIX_H_
diff --git a/ipc/chromium/src/base/file_descriptor_shuffle.cc b/ipc/chromium/src/base/file_descriptor_shuffle.cc
index 28447c950be5..5cce963fa99d 100644
--- a/ipc/chromium/src/base/file_descriptor_shuffle.cc
+++ b/ipc/chromium/src/base/file_descriptor_shuffle.cc
@@ -12,26 +12,36 @@
namespace base {
-bool PerformInjectiveMultimap(const InjectiveMultimap& m_in,
- InjectionDelegate* delegate) {
- InjectiveMultimap m(m_in);
- std::vector extra_fds;
+bool PerformInjectiveMultimapDestructive(
+ InjectiveMultimap* m, InjectionDelegate* delegate) {
+ static const size_t kMaxExtraFDs = 16;
+ int extra_fds[kMaxExtraFDs];
+ unsigned next_extra_fd = 0;
- for (InjectiveMultimap::iterator i = m.begin(); i != m.end(); ++i) {
+ // DANGER: this function may not allocate.
+
+ for (InjectiveMultimap::iterator i = m->begin(); i != m->end(); ++i) {
int temp_fd = -1;
// We DCHECK the injectiveness of the mapping.
- for (InjectiveMultimap::iterator j = i + 1; j != m.end(); ++j)
- DCHECK(i->dest != j->dest);
+ for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) {
+ DCHECK(i->dest != j->dest) << "Both fd " << i->source
+ << " and " << j->source << " map to " << i->dest;
+ }
const bool is_identity = i->source == i->dest;
- for (InjectiveMultimap::iterator j = i + 1; j != m.end(); ++j) {
+ for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) {
if (!is_identity && i->dest == j->source) {
if (temp_fd == -1) {
if (!delegate->Duplicate(&temp_fd, i->dest))
return false;
- extra_fds.push_back(temp_fd);
+ if (next_extra_fd < kMaxExtraFDs) {
+ extra_fds[next_extra_fd++] = temp_fd;
+ } else {
+ DLOG(ERROR) << "PerformInjectiveMultimapDestructive overflowed "
+ << "extra_fds. Leaking file descriptors!";
+ }
}
j->source = temp_fd;
@@ -56,14 +66,18 @@ bool PerformInjectiveMultimap(const InjectiveMultimap& m_in,
delegate->Close(i->source);
}
- for (std::vector::const_iterator
- i = extra_fds.begin(); i != extra_fds.end(); ++i) {
- delegate->Close(*i);
- }
+ for (unsigned i = 0; i < next_extra_fd; i++)
+ delegate->Close(extra_fds[i]);
return true;
}
+bool PerformInjectiveMultimap(const InjectiveMultimap& m_in,
+ InjectionDelegate* delegate) {
+ InjectiveMultimap m(m_in);
+ return PerformInjectiveMultimapDestructive(&m, delegate);
+}
+
bool FileDescriptorTableInjection::Duplicate(int* result, int fd) {
*result = HANDLE_EINTR(dup(fd));
return *result >= 0;
diff --git a/ipc/chromium/src/base/file_descriptor_shuffle.h b/ipc/chromium/src/base/file_descriptor_shuffle.h
index 8ee774097e8f..a28866ca95bc 100644
--- a/ipc/chromium/src/base/file_descriptor_shuffle.h
+++ b/ipc/chromium/src/base/file_descriptor_shuffle.h
@@ -65,10 +65,13 @@ typedef std::vector InjectiveMultimap;
bool PerformInjectiveMultimap(const InjectiveMultimap& map,
InjectionDelegate* delegate);
+bool PerformInjectiveMultimapDestructive(InjectiveMultimap* map,
+ InjectionDelegate* delegate);
-static inline bool ShuffleFileDescriptors(const InjectiveMultimap& map) {
+// This function will not call malloc but will mutate |map|
+static inline bool ShuffleFileDescriptors(InjectiveMultimap *map) {
FileDescriptorTableInjection delegate;
- return PerformInjectiveMultimap(map, &delegate);
+ return PerformInjectiveMultimapDestructive(map, &delegate);
}
} // namespace base
diff --git a/ipc/chromium/src/base/process_util_linux.cc b/ipc/chromium/src/base/process_util_linux.cc
index 3a0d59195349..858e3c54f6da 100644
--- a/ipc/chromium/src/base/process_util_linux.cc
+++ b/ipc/chromium/src/base/process_util_linux.cc
@@ -48,39 +48,34 @@ bool LaunchApp(const std::vector& argv,
const environment_map& env_vars_to_set,
bool wait, ProcessHandle* process_handle,
ProcessArchitecture arch) {
-#ifdef MOZ_MEMORY_ANDROID
- /* We specifically don't call pthread_atfork in jemalloc because it is not
- available in bionic until 2.3. However without it, jemalloc could
- potentially deadlock, when stl allocates memory through jemalloc, after
- fork and before execvp. Therefore, we must manually inform jemalloc here */
- ::_malloc_prefork();
-#endif
+ scoped_array argv_cstr(new char*[argv.size() + 1]);
+ // Illegal to allocate memory after fork and before execvp
+ InjectiveMultimap fd_shuffle1, fd_shuffle2;
+ fd_shuffle1.reserve(fds_to_remap.size());
+ fd_shuffle2.reserve(fds_to_remap.size());
+
pid_t pid = fork();
-#ifdef MOZ_MEMORY_ANDROID
- ::_malloc_postfork();
-#endif
if (pid < 0)
return false;
if (pid == 0) {
- InjectiveMultimap fd_shuffle;
for (file_handle_mapping_vector::const_iterator
it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
- fd_shuffle.push_back(InjectionArc(it->first, it->second, false));
+ fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
+ fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
}
- if (!ShuffleFileDescriptors(fd_shuffle))
- exit(127);
+ if (!ShuffleFileDescriptors(&fd_shuffle1))
+ _exit(127);
- CloseSuperfluousFds(fd_shuffle);
+ CloseSuperfluousFds(fd_shuffle2);
for (environment_map::const_iterator it = env_vars_to_set.begin();
it != env_vars_to_set.end(); ++it) {
if (setenv(it->first.c_str(), it->second.c_str(), 1/*overwrite*/))
- exit(127);
+ _exit(127);
}
- scoped_array argv_cstr(new char*[argv.size() + 1]);
for (size_t i = 0; i < argv.size(); i++)
argv_cstr[i] = const_cast(argv[i].c_str());
argv_cstr[argv.size()] = NULL;
diff --git a/ipc/chromium/src/base/process_util_posix.cc b/ipc/chromium/src/base/process_util_posix.cc
index 7d83431635e3..b7411bc81e32 100644
--- a/ipc/chromium/src/base/process_util_posix.cc
+++ b/ipc/chromium/src/base/process_util_posix.cc
@@ -1,3 +1,4 @@
+//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -25,6 +26,7 @@
#include "base/sys_info.h"
#include "base/time.h"
#include "base/waitable_event.h"
+#include "base/dir_reader_posix.h"
const int kMicrosecondsPerSecond = 1000000;
@@ -86,6 +88,10 @@ bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) {
return result;
}
+#ifdef ANDROID
+typedef unsigned long int rlim_t;
+#endif
+
// A class to handle auto-closing of DIR*'s.
class ScopedDIRClose {
public:
@@ -97,19 +103,20 @@ class ScopedDIRClose {
};
typedef scoped_ptr_malloc ScopedDIR;
-#ifdef ANDROID
-typedef unsigned long int rlim_t;
-#endif
void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
-#if defined(OS_LINUX)
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+#if defined(ANDROID)
+ static const rlim_t kSystemDefaultMaxFds = 1024;
+ static const char kFDDir[] = "/proc/self/fd";
+#elif defined(OS_LINUX)
static const rlim_t kSystemDefaultMaxFds = 8192;
- static const char fd_dir[] = "/proc/self/fd";
+ static const char kFDDir[] = "/proc/self/fd";
#elif defined(OS_MACOSX)
static const rlim_t kSystemDefaultMaxFds = 256;
- static const char fd_dir[] = "/dev/fd";
+ static const char kFDDir[] = "/dev/fd";
#endif
- std::set saved_fds;
// Get the maximum number of FDs possible.
struct rlimit nofile;
@@ -125,52 +132,63 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
if (max_fds > INT_MAX)
max_fds = INT_MAX;
- // Don't close stdin, stdout and stderr
- saved_fds.insert(STDIN_FILENO);
- saved_fds.insert(STDOUT_FILENO);
- saved_fds.insert(STDERR_FILENO);
-
- for (base::InjectiveMultimap::const_iterator
- i = saved_mapping.begin(); i != saved_mapping.end(); ++i) {
- saved_fds.insert(i->dest);
- }
-
- ScopedDIR dir_closer(opendir(fd_dir));
- DIR *dir = dir_closer.get();
- if (NULL == dir) {
- DLOG(ERROR) << "Unable to open " << fd_dir;
+ DirReaderPosix fd_dir(kFDDir);
+ if (!fd_dir.IsValid()) {
// Fallback case: Try every possible fd.
for (rlim_t i = 0; i < max_fds; ++i) {
const int fd = static_cast(i);
- if (saved_fds.find(fd) != saved_fds.end())
+ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
+ continue;
+ InjectiveMultimap::const_iterator j;
+ for (j = saved_mapping.begin(); j != saved_mapping.end(); j++) {
+ if (fd == j->dest)
+ break;
+ }
+ if (j != saved_mapping.end())
continue;
+ // Since we're just trying to close anything we can find,
+ // ignore any error return values of close().
HANDLE_EINTR(close(fd));
}
return;
}
- struct dirent *ent;
- while ((ent = readdir(dir))) {
+ const int dir_fd = fd_dir.fd();
+
+ for ( ; fd_dir.Next(); ) {
// Skip . and .. entries.
- if (ent->d_name[0] == '.')
+ if (fd_dir.name()[0] == '.')
continue;
char *endptr;
errno = 0;
- const long int fd = strtol(ent->d_name, &endptr, 10);
- if (ent->d_name[0] == 0 || *endptr || fd < 0 || errno)
+ const long int fd = strtol(fd_dir.name(), &endptr, 10);
+ if (fd_dir.name()[0] == 0 || *endptr || fd < 0 || errno)
continue;
- if (saved_fds.find(fd) != saved_fds.end())
+ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
+ continue;
+ InjectiveMultimap::const_iterator i;
+ for (i = saved_mapping.begin(); i != saved_mapping.end(); i++) {
+ if (fd == i->dest)
+ break;
+ }
+ if (i != saved_mapping.end())
+ continue;
+ if (fd == dir_fd)
continue;
// When running under Valgrind, Valgrind opens several FDs for its
// own use and will complain if we try to close them. All of
// these FDs are >= |max_fds|, so we can check against that here
// before closing. See https://bugs.kde.org/show_bug.cgi?id=191758
- if (fd < static_cast(max_fds))
- HANDLE_EINTR(close(fd));
+ if (fd < static_cast(max_fds)) {
+ int ret = HANDLE_EINTR(close(fd));
+ if (ret != 0) {
+ DLOG(ERROR) << "Problem closing fd";
+ }
+ }
}
}
@@ -419,6 +437,13 @@ bool GetAppOutput(const CommandLine& cl, std::string* output) {
int pipe_fd[2];
pid_t pid;
+ // Illegal to allocate memory after fork and before execvp
+ InjectiveMultimap fd_shuffle1, fd_shuffle2;
+ fd_shuffle1.reserve(3);
+ fd_shuffle2.reserve(3);
+ const std::vector& argv = cl.argv();
+ scoped_array argv_cstr(new char*[argv.size() + 1]);
+
if (pipe(pipe_fd) < 0)
return false;
@@ -429,27 +454,35 @@ bool GetAppOutput(const CommandLine& cl, std::string* output) {
return false;
case 0: // child
{
+ // Obscure fork() rule: in the child, if you don't end up doing exec*(),
+ // you call _exit() instead of exit(). This is because _exit() does not
+ // call any previously-registered (in the parent) exit handlers, which
+ // might do things like block waiting for threads that don't even exist
+ // in the child.
int dev_null = open("/dev/null", O_WRONLY);
if (dev_null < 0)
- exit(127);
+ _exit(127);
- InjectiveMultimap fd_shuffle;
- fd_shuffle.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true));
- fd_shuffle.push_back(InjectionArc(dev_null, STDERR_FILENO, true));
- fd_shuffle.push_back(InjectionArc(dev_null, STDIN_FILENO, true));
+ fd_shuffle1.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true));
+ fd_shuffle1.push_back(InjectionArc(dev_null, STDERR_FILENO, true));
+ fd_shuffle1.push_back(InjectionArc(dev_null, STDIN_FILENO, true));
+ // Adding another element here? Remeber to increase the argument to
+ // reserve(), above.
- if (!ShuffleFileDescriptors(fd_shuffle))
- exit(127);
+ std::copy(fd_shuffle1.begin(), fd_shuffle1.end(),
+ std::back_inserter(fd_shuffle2));
- CloseSuperfluousFds(fd_shuffle);
+ // fd_shuffle1 is mutated by this call because it cannot malloc.
+ if (!ShuffleFileDescriptors(&fd_shuffle1))
+ _exit(127);
+
+ CloseSuperfluousFds(fd_shuffle2);
- const std::vector argv = cl.argv();
- scoped_array argv_cstr(new char*[argv.size() + 1]);
for (size_t i = 0; i < argv.size(); i++)
argv_cstr[i] = const_cast(argv[i].c_str());
argv_cstr[argv.size()] = NULL;
execvp(argv_cstr[0], argv_cstr.get());
- exit(127);
+ _exit(127);
}
default: // parent
{
diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp
index c85943550b6f..2b494011ea14 100644
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -310,7 +310,7 @@ DEFINE_STATIC_SETTER(static_input_setter,
DEFINE_STATIC_SETTER(static_multiline_setter,
if (!JSVAL_IS_BOOLEAN(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp))
return false;
- res->setMultiline(!!JSVAL_TO_BOOLEAN(*vp)))
+ res->setMultiline(cx, !!JSVAL_TO_BOOLEAN(*vp)))
const uint8 REGEXP_STATIC_PROP_ATTRS = JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE;
const uint8 RO_REGEXP_STATIC_PROP_ATTRS = REGEXP_STATIC_PROP_ATTRS | JSPROP_READONLY;
diff --git a/js/src/config/autoconf.mk.in b/js/src/config/autoconf.mk.in
index a3c7fbe31931..8d8b4a542016 100644
--- a/js/src/config/autoconf.mk.in
+++ b/js/src/config/autoconf.mk.in
@@ -191,11 +191,6 @@ LIBS_DESC_SUFFIX = @LIBS_DESC_SUFFIX@
USE_N32 = @USE_N32@
HAVE_64BIT_OS = @HAVE_64BIT_OS@
-# Temp hack. It is not my intention to leave this crap in here for ever.
-# Im talking to fur right now to solve the problem without introducing
-# NS_USE_NATIVE to the build system -ramiro.
-NS_USE_NATIVE = @NS_USE_NATIVE@
-
CC = @CC@
CXX = @CXX@
diff --git a/js/src/configure.in b/js/src/configure.in
index ba3b72a567b9..0bf21eabccf0 100644
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -2334,7 +2334,6 @@ ia64*-hpux*)
if test "$SOLARIS_SUNPRO_CC"; then
LDFLAGS="$LDFLAGS -z ignore -R '\$\$ORIGIN:\$\$ORIGIN/..' -z lazyload -z combreloc -z muldefs"
LIBS="-lCrun -lCstd -lc $LIBS"
- NS_USE_NATIVE=1
AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES)
CFLAGS="$CFLAGS -xlibmieee -xstrconst -xbuiltin=%all -D__FUNCTION__=__func__"
CXXFLAGS="$CXXFLAGS -xlibmieee -xbuiltin=%all -features=tmplife,tmplrefstatic,extensions -norunpath -D__FUNCTION__=__func__ -template=no%extdef"
@@ -5079,7 +5078,6 @@ AC_SUBST(WINDRES)
AC_SUBST(IMPLIB)
AC_SUBST(FILTER)
AC_SUBST(BIN_FLAGS)
-AC_SUBST(NS_USE_NATIVE)
AC_SUBST(MOZ_JS_LIBS)
AC_SUBST(MOZ_PSM)
AC_SUBST(MOZ_DEBUG)
diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h
index 0fa9388a4585..31ed88b2e663 100644
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -92,19 +92,18 @@ class BumpChunk
JS_ASSERT(bump == AlignPtr(bump));
}
- void clobberUnused() {
-#ifdef DEBUG
- memset(bump, 0xcd, limit - bump);
-#endif
- }
-
void setBump(void *ptr) {
JS_ASSERT(bumpBase() <= ptr);
JS_ASSERT(ptr <= limit);
DebugOnly prevBump = bump;
bump = static_cast(ptr);
- if (prevBump < bump)
- clobberUnused();
+#ifdef DEBUG
+ JS_ASSERT(contains(prevBump));
+
+ /* Clobber the now-free space. */
+ if (prevBump > bump)
+ memset(bump, 0xcd, prevBump - bump);
+#endif
}
public:
diff --git a/js/src/jit-test/tests/basic/bug649939.js b/js/src/jit-test/tests/basic/bug649939.js
index 9c2cd6141db9..2f1b054730bd 100644
--- a/js/src/jit-test/tests/basic/bug649939.js
+++ b/js/src/jit-test/tests/basic/bug649939.js
@@ -20,7 +20,7 @@ function assertRaises(exc, callback) {
try {
callback();
} catch (e) {
- assertEq(e instanceof InternalError, true);
+ assertEq(e instanceof StopIteration, true);
caught = true;
}
assertEq(caught, true);
diff --git a/js/src/jit-test/tests/basic/regexp-multiline.js b/js/src/jit-test/tests/basic/regexp-multiline.js
new file mode 100644
index 000000000000..7455b4e89ef8
--- /dev/null
+++ b/js/src/jit-test/tests/basic/regexp-multiline.js
@@ -0,0 +1,12 @@
+// visibility of updates to RegExp.multiline
+
+function foo(value) {
+ for (var i = 0; i < 50; i++) {
+ var re = /erwe/;
+ assertEq(re.multiline, value);
+ }
+}
+
+foo(false);
+RegExp.multiline = true;
+foo(true);
diff --git a/js/src/jit-test/tests/basic/testStackIter.js b/js/src/jit-test/tests/basic/testStackIter.js
index 9bd1aa8af3dd..ad3741e5c30b 100644
--- a/js/src/jit-test/tests/basic/testStackIter.js
+++ b/js/src/jit-test/tests/basic/testStackIter.js
@@ -45,7 +45,6 @@ eval("assertStackIs(['eval-code', 'global-code'])");
this['eval']("assertStackIs(['eval-code', eval, 'global-code'])");
eval.bind(null, "assertStackIs(['eval-code', eval, 'bound(eval)', 'global-code'])")();
(function f() { assertStackIs([f, Function.prototype.call, 'global-code']) }).call(null);
-(function f() { assertStackIs([f, Function.prototype.apply, 'global-code']) }).apply(null, {});
(function f() { (function g(x,y,z) { assertStackIs([g,f,'global-code']); })() })(1);
/***********/
@@ -135,10 +134,9 @@ new proxy();
/***********/
for (var i = 0; i < 10; ++i) {
+ eval("'no imacros please'");
+
/* No loss for scripts. */
- (function f() {
- assertStackIs([f, Function.prototype.apply, 'global-code']);
- }).apply(null, {});
(function f() {
assertStackIs([f, Function.prototype.call, 'global-code']);
}).call(null);
@@ -155,26 +153,4 @@ for (var i = 0; i < 10; ++i) {
assertEq(stack[1], Function.prototype.call);
}
}).bind().call(null);
- (function f() {
- var stack = dumpStack();
- assertEq(stack[0], f);
- if (stack.length === 4) {
- assertEq(stack[1].name, 'f');
- assertEq(stack[2], Function.prototype.apply);
- } else {
- assertEq(stack.length, 3);
- assertEq(stack[1], Function.prototype.apply);
- }
- }).bind().apply(null, {});
- (function f() {
- var stack = dumpStack();
- assertEq(stack[0], f);
- if (stack.length === 4) {
- assertEq(stack[1].name, 'f');
- assertEq(stack[2], Function.prototype.apply);
- } else {
- assertEq(stack.length, 3);
- assertEq(stack[1], Function.prototype.apply);
- }
- }).bind().apply(null, [1,2,3,4,5]);
}
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 71c403ae4e56..2a4d1934cc6f 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5944,7 +5944,7 @@ JS_SetRegExpInput(JSContext *cx, JSObject *obj, JSString *input, JSBool multilin
CHECK_REQUEST(cx);
assertSameCompartment(cx, input);
- obj->asGlobal()->getRegExpStatics()->reset(input, !!multiline);
+ obj->asGlobal()->getRegExpStatics()->reset(cx, input, !!multiline);
}
JS_PUBLIC_API(void)
diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h
index 399e21abd919..f213efc948f0 100644
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -301,37 +301,40 @@ enum {
* Some objects are not dense arrays, or are dense arrays whose length
* property does not fit in an int32.
*/
- OBJECT_FLAG_NON_DENSE_ARRAY = 0x0010000,
+ OBJECT_FLAG_NON_DENSE_ARRAY = 0x00010000,
/* Whether any objects this represents are not packed arrays. */
- OBJECT_FLAG_NON_PACKED_ARRAY = 0x0020000,
+ OBJECT_FLAG_NON_PACKED_ARRAY = 0x00020000,
/* Whether any objects this represents are not typed arrays. */
- OBJECT_FLAG_NON_TYPED_ARRAY = 0x0040000,
+ OBJECT_FLAG_NON_TYPED_ARRAY = 0x00040000,
/* Whether any represented script has had arguments objects created. */
- OBJECT_FLAG_CREATED_ARGUMENTS = 0x0080000,
+ OBJECT_FLAG_CREATED_ARGUMENTS = 0x00080000,
/* Whether any represented script is considered uninlineable. */
- OBJECT_FLAG_UNINLINEABLE = 0x0100000,
+ OBJECT_FLAG_UNINLINEABLE = 0x00100000,
/* Whether any objects have an equality hook. */
- OBJECT_FLAG_SPECIAL_EQUALITY = 0x0200000,
+ OBJECT_FLAG_SPECIAL_EQUALITY = 0x00200000,
/* Whether any objects have been iterated over. */
- OBJECT_FLAG_ITERATED = 0x0400000,
+ OBJECT_FLAG_ITERATED = 0x00400000,
/* Outer function which has been marked reentrant. */
- OBJECT_FLAG_REENTRANT_FUNCTION = 0x0800000,
+ OBJECT_FLAG_REENTRANT_FUNCTION = 0x00800000,
+
+ /* For a global object, whether flags were set on the RegExpStatics. */
+ OBJECT_FLAG_REGEXP_FLAGS_SET = 0x01000000,
/* Flags which indicate dynamic properties of represented objects. */
- OBJECT_FLAG_DYNAMIC_MASK = 0x0ff0000,
+ OBJECT_FLAG_DYNAMIC_MASK = 0x01ff0000,
/*
* Whether all properties of this object are considered unknown.
* If set, all flags in DYNAMIC_MASK will also be set.
*/
- OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x1000000,
+ OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x80000000,
/* Mask for objects created with unknown properties. */
OBJECT_FLAG_UNKNOWN_MASK =
diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp
index 5ed5f1027785..e656f5bff157 100644
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -574,8 +574,7 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
if (obj) {
/* Enumerate Iterator.prototype directly. */
- JSIteratorOp op = obj->getClass()->ext.iteratorObject;
- if (op && (obj->getClass() != &IteratorClass || obj->getNativeIterator())) {
+ if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
if (!iterobj)
return false;
@@ -973,12 +972,10 @@ js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
if (iterobj->isIterator()) {
/* Key iterators are handled by fast-paths. */
ni = iterobj->getNativeIterator();
- if (ni) {
- bool more = ni->props_cursor < ni->props_end;
- if (ni->isKeyIter() || !more) {
- rval->setBoolean(more);
- return true;
- }
+ bool more = ni->props_cursor < ni->props_end;
+ if (ni->isKeyIter() || !more) {
+ rval->setBoolean(more);
+ return true;
}
}
@@ -1033,7 +1030,7 @@ js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
* read-only and permanent.
*/
NativeIterator *ni = iterobj->getNativeIterator();
- if (ni && ni->isKeyIter()) {
+ if (ni->isKeyIter()) {
JS_ASSERT(ni->props_cursor < ni->props_end);
*rval = IdToValue(*ni->current());
ni->incCursor();
@@ -1459,6 +1456,14 @@ InitIteratorClass(JSContext *cx, GlobalObject *global)
if (!iteratorProto)
return false;
+ AutoIdVector blank(cx);
+ NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
+ if (!ni)
+ return false;
+ ni->init(NULL, 0 /* flags */, 0, 0);
+
+ iteratorProto->setNativeIterator(ni);
+
JSFunction *ctor = global->createConstructor(cx, Iterator, &IteratorClass,
CLASS_ATOM(cx, Iterator), 2);
if (!ctor)
diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp
index ef1f354ffa97..16573c452cfd 100644
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1539,8 +1539,8 @@ MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
return res->createLastMatch(cx, &v) && arrayobj->defineElement(cx, count, v);
}
-static JSBool
-str_match(JSContext *cx, uintN argc, Value *vp)
+JSBool
+js::str_match(JSContext *cx, uintN argc, Value *vp)
{
JSString *str = ThisToStringForStringProto(cx, vp);
if (!str)
@@ -1572,8 +1572,8 @@ str_match(JSContext *cx, uintN argc, Value *vp)
return true;
}
-static JSBool
-str_search(JSContext *cx, uintN argc, Value *vp)
+JSBool
+js::str_search(JSContext *cx, uintN argc, Value *vp)
{
JSString *str = ThisToStringForStringProto(cx, vp);
if (!str)
@@ -2436,8 +2436,8 @@ class SplitStringMatcher {
};
/* ES5 15.5.4.14 */
-static JSBool
-str_split(JSContext *cx, uintN argc, Value *vp)
+JSBool
+js::str_split(JSContext *cx, uintN argc, Value *vp)
{
/* Steps 1-2. */
JSString *str = ThisToStringForStringProto(cx, vp);
diff --git a/js/src/jsstr.h b/js/src/jsstr.h
index be76219c0bb4..5dc596556db4 100644
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -337,6 +337,15 @@ FileEscapedString(FILE *fp, JSLinearString *str, uint32 quote)
return PutEscapedStringImpl(NULL, 0, fp, str, quote) != size_t(-1);
}
+JSBool
+str_match(JSContext *cx, uintN argc, Value *vp);
+
+JSBool
+str_search(JSContext *cx, uintN argc, Value *vp);
+
+JSBool
+str_split(JSContext *cx, uintN argc, Value *vp);
+
} /* namespace js */
extern JSBool
diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h
index 248a6c1bb589..98b0095eb152 100644
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -1305,10 +1305,15 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist
storePtr(ImmPtr((void *) templateObject->capacity), Address(result, offsetof(JSObject, capacity)));
storePtr(ImmPtr(templateObject->type()), Address(result, JSObject::offsetOfType()));
- /* Fixed slots of non-array objects are required to be initialized. */
+ /*
+ * Fixed slots of non-array objects are required to be initialized;
+ * Use the values currently in the template object.
+ */
if (!templateObject->isDenseArray()) {
- for (unsigned i = 0; i < templateObject->numFixedSlots(); i++)
- storeValue(UndefinedValue(), Address(result, JSObject::getFixedSlotOffset(i)));
+ for (unsigned i = 0; i < templateObject->numFixedSlots(); i++) {
+ storeValue(templateObject->getFixedSlot(i),
+ Address(result, JSObject::getFixedSlotOffset(i)));
+ }
}
return jump;
diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp
index 5edc2247567a..3a22e0d402e6 100644
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -61,9 +61,15 @@
#include "jsopcodeinlines.h"
#include "jshotloop.h"
+#include "builtin/RegExp.h"
+#include "vm/RegExpStatics.h"
+#include "vm/RegExpObject.h"
+
#include "jsautooplen.h"
#include "jstypedarrayinlines.h"
+#include "vm/RegExpObject-inl.h"
+
using namespace js;
using namespace js::mjit;
#if defined(JS_POLYIC) || defined(JS_MONOIC)
@@ -2719,14 +2725,7 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_SETGNAME)
BEGIN_CASE(JSOP_REGEXP)
- {
- JSObject *regex = script->getRegExp(fullAtomIndex(PC));
- prepareStubCall(Uses(0));
- masm.move(ImmPtr(regex), Registers::ArgReg1);
- INLINE_STUBCALL(stubs::RegExp, REJOIN_PUSH_OBJECT);
- frame.takeReg(Registers::ReturnReg);
- frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
- }
+ jsop_regexp();
END_CASE(JSOP_REGEXP)
BEGIN_CASE(JSOP_OBJECT)
@@ -6576,6 +6575,87 @@ mjit::Compiler::jsop_newinit()
return true;
}
+void
+mjit::Compiler::jsop_regexp()
+{
+ JSObject *obj = script->getRegExp(fullAtomIndex(PC));
+ RegExpStatics *res = globalObj ? globalObj->getRegExpStatics() : NULL;
+
+ if (!globalObj ||
+ !cx->typeInferenceEnabled() ||
+ analysis->localsAliasStack() ||
+ types::TypeSet::HasObjectFlags(cx, globalObj->getType(cx),
+ types::OBJECT_FLAG_REGEXP_FLAGS_SET)) {
+ prepareStubCall(Uses(0));
+ masm.move(ImmPtr(obj), Registers::ArgReg1);
+ INLINE_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH);
+ frame.pushSynced(JSVAL_TYPE_OBJECT);
+ return;
+ }
+
+ RegExpPrivate *regexp = static_cast(obj)->getPrivate();
+
+ DebugOnly origFlags = regexp->getFlags();
+ DebugOnly staticsFlags = res->getFlags();
+ JS_ASSERT((origFlags & staticsFlags) == staticsFlags);
+
+ /*
+ * JS semantics require regular expression literals to create different
+ * objects every time they execute. We only need to do this cloning if the
+ * script could actually observe the effect of such cloning, by getting
+ * or setting properties on it. Particular RegExp and String natives take
+ * regular expressions as 'this' or an argument, and do not let that
+ * expression escape and be accessed by the script, so avoid cloning in
+ * these cases.
+ */
+ analyze::SSAUseChain *uses =
+ analysis->useChain(analyze::SSAValue::PushedValue(PC - script->code, 0));
+ if (uses && uses->popped && !uses->next) {
+ jsbytecode *use = script->code + uses->offset;
+ uint32 which = uses->u.which;
+ if (JSOp(*use) == JSOP_CALLPROP) {
+ JSObject *callee = analysis->pushedTypes(use, 0)->getSingleton(cx);
+ if (callee && callee->isFunction()) {
+ Native native = callee->getFunctionPrivate()->maybeNative();
+ if (native == js::regexp_exec || native == js::regexp_test) {
+ frame.push(ObjectValue(*obj));
+ return;
+ }
+ }
+ } else if (JSOp(*use) == JSOP_CALL && which == 0) {
+ uint32 argc = GET_ARGC(use);
+ JSObject *callee = analysis->poppedTypes(use, argc + 1)->getSingleton(cx);
+ if (callee && callee->isFunction() && argc >= 1 && which == argc - 1) {
+ Native native = callee->getFunctionPrivate()->maybeNative();
+ if (native == js::str_match ||
+ native == js::str_search ||
+ native == js::str_replace ||
+ native == js::str_split) {
+ frame.push(ObjectValue(*obj));
+ return;
+ }
+ }
+ }
+ }
+
+ RegisterID result = frame.allocReg();
+ Jump emptyFreeList = masm.getNewObject(cx, result, obj);
+
+ stubcc.linkExit(emptyFreeList, Uses(0));
+ stubcc.leave();
+
+ stubcc.masm.move(ImmPtr(obj), Registers::ArgReg1);
+ OOL_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH);
+
+ /* Bump the refcount on the wrapped RegExp. */
+ size_t *refcount = regexp->addressOfRefCount();
+ masm.add32(Imm32(1), AbsoluteAddress(refcount));
+
+ frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result);
+
+ stubcc.rejoin(Changes(1));
+}
+
bool
mjit::Compiler::startLoop(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
{
diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h
index 373337087afc..bd4dd9be91c3 100644
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -711,6 +711,7 @@ private:
bool jsop_arginc(JSOp op, uint32 slot);
bool jsop_localinc(JSOp op, uint32 slot);
bool jsop_newinit();
+ void jsop_regexp();
void jsop_initmethod();
void jsop_initprop();
void jsop_initelem();
diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp
index f9d9f1e02281..2aa0cd218a49 100644
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1430,7 +1430,7 @@ stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun)
return obj;
}
-JSObject * JS_FASTCALL
+void JS_FASTCALL
stubs::RegExp(VMFrame &f, JSObject *regex)
{
/*
@@ -1443,12 +1443,12 @@ stubs::RegExp(VMFrame &f, JSObject *regex)
*/
JSObject *proto;
if (!js_GetClassPrototype(f.cx, &f.fp()->scopeChain(), JSProto_RegExp, &proto))
- THROWV(NULL);
+ THROW();
JS_ASSERT(proto);
JSObject *obj = js_CloneRegExpObject(f.cx, regex, proto);
if (!obj)
- THROWV(NULL);
- return obj;
+ THROW();
+ f.regs.sp[0].setObject(*obj);
}
JSObject * JS_FASTCALL
diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h
index 41c712a24b7e..86e92a5773ae 100644
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -152,7 +152,7 @@ void JS_FASTCALL SetConst(VMFrame &f, JSAtom *atom);
template void JS_FASTCALL DefFun(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL DefLocalFun(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL DefLocalFun_FC(VMFrame &f, JSFunction *fun);
-JSObject * JS_FASTCALL RegExp(VMFrame &f, JSObject *regex);
+void JS_FASTCALL RegExp(VMFrame &f, JSObject *regex);
JSObject * JS_FASTCALL Lambda(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL LambdaJoinableForInit(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL LambdaJoinableForSet(VMFrame &f, JSFunction *fun);
diff --git a/js/src/vm/RegExpObject-inl.h b/js/src/vm/RegExpObject-inl.h
index c57f84000933..1ed596a234ba 100644
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -46,6 +46,7 @@
#include "jsobjinlines.h"
#include "jsstrinlines.h"
+#include "RegExpStatics-inl.h"
inline js::RegExpObject *
JSObject::asRegExp()
diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h
index 74b6b2fa304c..1449d5625567 100644
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -284,6 +284,9 @@ class RegExpPrivate
void incref(JSContext *cx);
void decref(JSContext *cx);
+ /* For JIT access. */
+ size_t *addressOfRefCount() { return &refCount; }
+
/* Accessors */
JSLinearString *getSource() const { return source; }
diff --git a/js/src/vm/RegExpStatics-inl.h b/js/src/vm/RegExpStatics-inl.h
index 722055ac966a..c0c25f177e6f 100644
--- a/js/src/vm/RegExpStatics-inl.h
+++ b/js/src/vm/RegExpStatics-inl.h
@@ -181,6 +181,45 @@ RegExpStatics::getRightContext(JSSubString *out) const
out->length = matchPairsInput->length() - get(0, 1);
}
+inline void
+RegExpStatics::setMultiline(JSContext *cx, bool enabled)
+{
+ aboutToWrite();
+ if (enabled) {
+ flags = RegExpFlag(flags | MultilineFlag);
+ markFlagsSet(cx);
+ } else {
+ flags = RegExpFlag(flags & ~MultilineFlag);
+ }
+}
+
+inline void
+RegExpStatics::markFlagsSet(JSContext *cx)
+{
+ /*
+ * Flags set on the RegExp function get propagated to constructed RegExp
+ * objects, which interferes with optimizations that inline RegExp cloning
+ * or avoid cloning entirely. Scripts making this assumption listen to
+ * type changes on RegExp.prototype, so mark a state change to trigger
+ * recompilation of all such code (when recompiling, a stub call will
+ * always be performed).
+ */
+ GlobalObject *global = GetGlobalForScopeChain(cx);
+ JS_ASSERT(this == global->getRegExpStatics());
+
+ types::MarkTypeObjectFlags(cx, global, types::OBJECT_FLAG_REGEXP_FLAGS_SET);
+}
+
+inline void
+RegExpStatics::reset(JSContext *cx, JSString *newInput, bool newMultiline)
+{
+ aboutToWrite();
+ clear();
+ pendingInput = newInput;
+ setMultiline(cx, newMultiline);
+ checkInvariants();
+}
+
} /* namespace js */
#endif
diff --git a/js/src/vm/RegExpStatics.h b/js/src/vm/RegExpStatics.h
index a9d89bc593c1..e383f137bb34 100644
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -148,6 +148,8 @@ class RegExpStatics
*/
bool makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, Value *out) const;
+ void markFlagsSet(JSContext *cx);
+
struct InitBuffer {};
explicit RegExpStatics(InitBuffer) : bufferLink(NULL), copied(false) {}
@@ -157,7 +159,6 @@ class RegExpStatics
RegExpStatics() : bufferLink(NULL), copied(false) { clear(); }
static JSObject *create(JSContext *cx, GlobalObject *parent);
- static RegExpStatics *extractFrom(GlobalObject *globalObj);
/* Mutators. */
@@ -177,13 +178,7 @@ class RegExpStatics
return true;
}
- void setMultiline(bool enabled) {
- aboutToWrite();
- if (enabled)
- flags = RegExpFlag(flags | MultilineFlag);
- else
- flags = RegExpFlag(flags & ~MultilineFlag);
- }
+ inline void setMultiline(JSContext *cx, bool enabled);
void clear() {
aboutToWrite();
@@ -194,13 +189,7 @@ class RegExpStatics
}
/* Corresponds to JSAPI functionality to set the pending RegExp input. */
- void reset(JSString *newInput, bool newMultiline) {
- aboutToWrite();
- clear();
- pendingInput = newInput;
- setMultiline(newMultiline);
- checkInvariants();
- }
+ inline void reset(JSContext *cx, JSString *newInput, bool newMultiline);
void setPendingInput(JSString *newInput) {
aboutToWrite();
diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp
index 50503e6268af..ffc2b86d6769 100644
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1028,19 +1028,20 @@ StackIter::settleOnNewState()
*
* regs.sp == vp + 2 + argc
*
- * The mjit Function.prototype.apply optimization breaks this
- * invariant (see ic::SplatApplyArgs). Thus, for JSOP_FUNAPPLY we
- * need to (slowly) reconstruct the depth.
- *
- * Additionally, the Function.prototype.{call,apply} optimizations
- * leave no record when 'this' is a native function. Thus, if the
- * following expression runs and breaks in the debugger, the call
- * to 'replace' will not appear on the callstack.
+ * The Function.prototype.call optimization leaves no record when
+ * 'this' is a native function. Thus, if the following expression
+ * runs and breaks in the debugger, the call to 'replace' will not
+ * appear on the callstack.
*
* (String.prototype.replace).call('a',/a/,function(){debugger});
*
* Function.prototype.call will however appear, hence the debugger
* can, by inspecting 'args.thisv', give some useful information.
+ *
+ * For Function.prototype.apply, the situation is even worse: since
+ * a dynamic number of arguments have been pushed onto the stack
+ * (see SplatApplyArgs), there is no efficient way to know how to
+ * find the callee. Thus, calls to apply are lost completely.
*/
JSOp op = js_GetOpcode(cx_, fp_->script(), pc_);
if (op == JSOP_CALL || op == JSOP_FUNCALL) {
@@ -1056,30 +1057,12 @@ StackIter::settleOnNewState()
args_ = CallArgsFromVp(argc, vp);
return;
}
- } else if (op == JSOP_FUNAPPLY) {
- JS_ASSERT(!fp_->hasImacropc());
- uintN argc = GET_ARGC(pc_);
- uintN spoff = js_ReconstructStackDepth(cx_, fp_->script(), pc_);
- Value *sp = fp_->base() + spoff;
- Value *vp = sp - (2 + argc);
-
- CrashIfInvalidSlot(fp_, vp);
- if (IsNativeFunction(*vp)) {
- if (sp_ != sp) {
- JS_ASSERT(argc == 2);
- JS_ASSERT(vp[0].toObject().getFunctionPrivate()->native() == js_fun_apply);
- JS_ASSERT(sp_ >= vp + 3);
- argc = sp_ - (vp + 2);
- }
- state_ = IMPLICIT_NATIVE;
- args_ = CallArgsFromVp(argc, vp);
- return;
- }
}
state_ = SCRIPTED;
- JS_ASSERT(sp_ >= fp_->base() && sp_ <= fp_->slots() + fp_->script()->nslots);
DebugOnly script = fp_->script();
+ JS_ASSERT_IF(op != JSOP_FUNAPPLY,
+ sp_ >= fp_->base() && sp_ <= fp_->slots() + script->nslots);
JS_ASSERT_IF(!fp_->hasImacropc(),
pc_ >= script->code && pc_ < script->code + script->length);
return;
diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp
index b8c4dbf3a928..a53a6df4e5b2 100644
--- a/js/src/xpconnect/src/xpcconvert.cpp
+++ b/js/src/xpconnect/src/xpcconvert.cpp
@@ -1175,9 +1175,6 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx,
&triedToWrap);
if(!flat && triedToWrap)
return JS_FALSE;
- if (!flat) {
- flat = ConstructProxyObject(ccx, aHelper, xpcscope);
- }
}
if(flat) {
diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h
index f58ecc4b63c0..bfdd0e8f1ad3 100644
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -2370,10 +2370,6 @@ private:
};
class xpcObjectHelper;
-JSObject *
-ConstructProxyObject(XPCCallContext &ccx,
- xpcObjectHelper &aHelper,
- XPCWrappedNativeScope *xpcscope);
extern JSBool ConstructSlimWrapper(XPCCallContext &ccx,
xpcObjectHelper &aHelper,
XPCWrappedNativeScope* xpcScope,
diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp
index b0c8b0302ec8..5598ef7ea460 100644
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -3923,34 +3923,6 @@ MorphSlimWrapper(JSContext *cx, JSObject *obj)
static PRUint32 sSlimWrappers;
#endif
-JSObject *
-ConstructProxyObject(XPCCallContext &ccx,
- xpcObjectHelper &aHelper,
- XPCWrappedNativeScope *xpcscope)
-{
- nsISupports *identityObj = aHelper.GetCanonical();
- nsXPCClassInfo *classInfoHelper = aHelper.GetXPCClassInfo();
-
-#ifdef DEBUG
- {
- JSUint32 flagsInt;
- nsresult debug_rv = classInfoHelper->GetScriptableFlags(&flagsInt);
- XPCNativeScriptableFlags flags(flagsInt);
- NS_ASSERTION(NS_SUCCEEDED(debug_rv) && flags.WantPreCreate(),
- "bad flags, cache->IsProxy() implies WantPreCreate()");
- }
-#endif
-
- // We re-use the PreCreate hook to create the actual proxy object.
- JSObject* parent = xpcscope->GetGlobalJSObject();
- nsresult rv = classInfoHelper->PreCreate(identityObj, ccx, parent, &parent);
- NS_ENSURE_SUCCESS(rv, nsnull);
-
- nsWrapperCache *cache = aHelper.GetWrapperCache();
- JSObject *flat = cache->GetWrapper();
- return flat;
-}
-
JSBool
ConstructSlimWrapper(XPCCallContext &ccx,
xpcObjectHelper &aHelper,
diff --git a/layout/generic/TextOverflow.cpp b/layout/generic/TextOverflow.cpp
index 79cfa84eed52..2e230010a845 100644
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -277,11 +277,11 @@ TextOverflow::WillProcessLines(nsDisplayListBuilder* aBuilder,
scroll->GetScrollbarStyles().mHorizontal != NS_STYLE_OVERFLOW_HIDDEN;
textOverflow->mContentArea.MoveBy(scroll->GetScrollPosition());
}
- textOverflow->mBlockIsRTL =
- aBlockFrame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
+ PRUint8 direction = aBlockFrame->GetStyleVisibility()->mDirection;
+ textOverflow->mBlockIsRTL = direction == NS_STYLE_DIRECTION_RTL;
const nsStyleTextReset* style = aBlockFrame->GetStyleTextReset();
- textOverflow->mLeft.Init(style->mTextOverflow.mLeft);
- textOverflow->mRight.Init(style->mTextOverflow.mRight);
+ textOverflow->mLeft.Init(style->mTextOverflow.GetLeft(direction));
+ textOverflow->mRight.Init(style->mTextOverflow.GetRight(direction));
// The left/right marker string is setup in ExamineLineFrames when a line
// has overflow on that side.
@@ -402,31 +402,44 @@ TextOverflow::ExamineLineFrames(nsLineBox* aLine,
FrameHashtable* aFramesToHide,
AlignmentEdges* aAlignmentEdges)
{
+ // No ellipsing for 'clip' style.
+ bool suppressLeft = mLeft.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
+ bool suppressRight = mRight.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
+ if (mCanHaveHorizontalScrollbar) {
+ nsIScrollableFrame* scroll = nsLayoutUtils::GetScrollableFrameFor(mBlock);
+ nsPoint pos = scroll->GetScrollPosition();
+ nsRect scrollRange = scroll->GetScrollRange();
+ // No ellipsing when nothing to scroll to on that side (this includes
+ // overflow:auto that doesn't trigger a horizontal scrollbar).
+ if (pos.x <= scrollRange.x) {
+ suppressLeft = true;
+ }
+ if (pos.x >= scrollRange.XMost()) {
+ suppressRight = true;
+ }
+ }
+
// Scrolling to the end position can leave some text still overflowing due to
// pixel snapping behaviour in our scrolling code so we move the edges 1px
// outward to avoid triggering a text-overflow marker for such overflow.
nsRect contentArea = mContentArea;
const nscoord scrollAdjust = mCanHaveHorizontalScrollbar ?
mBlock->PresContext()->AppUnitsPerDevPixel() : 0;
- InflateLeft(&contentArea,
- mLeft.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP,
- scrollAdjust);
- InflateRight(&contentArea,
- mRight.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP,
- scrollAdjust);
+ InflateLeft(&contentArea, suppressLeft, scrollAdjust);
+ InflateRight(&contentArea, suppressRight, scrollAdjust);
nsRect lineRect = aLine->GetScrollableOverflowArea();
- const bool leftOverflow = lineRect.x < contentArea.x;
- const bool rightOverflow = lineRect.XMost() > contentArea.XMost();
+ const bool leftOverflow =
+ !suppressLeft && lineRect.x < contentArea.x;
+ const bool rightOverflow =
+ !suppressRight && lineRect.XMost() > contentArea.XMost();
if (!leftOverflow && !rightOverflow) {
- // The line does not overflow - no need to traverse the frame tree.
+ // The line does not overflow on a side we should ellipsize.
return;
}
PRUint32 pass = 0;
- bool guessLeft =
- mLeft.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP && leftOverflow;
- bool guessRight =
- mRight.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP && rightOverflow;
+ bool guessLeft = leftOverflow;
+ bool guessRight = rightOverflow;
do {
// Setup marker strings as needed.
if (guessLeft) {
diff --git a/layout/generic/crashtests/688996-1.html b/layout/generic/crashtests/688996-1.html
new file mode 100644
index 000000000000..f2a32802d29c
--- /dev/null
+++ b/layout/generic/crashtests/688996-1.html
@@ -0,0 +1,18 @@
+
+b
+
diff --git a/layout/generic/crashtests/688996-2.html b/layout/generic/crashtests/688996-2.html
new file mode 100644
index 000000000000..d4132d91fbe9
--- /dev/null
+++ b/layout/generic/crashtests/688996-2.html
@@ -0,0 +1,15 @@
+
+b
+
diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list
index 54ccc19a632d..7be438ed9341 100644
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -379,3 +379,5 @@ asserts(14) asserts-if(Android,8) load 673770.html # bug 569193 and bug 459597
load 679933-1.html
load 682649-1.html
load 683702-1.xhtml
+load 688996-1.html
+load 688996-2.html
diff --git a/layout/reftests/canvas/693610-1-notref.html b/layout/reftests/canvas/693610-1-notref.html
new file mode 100644
index 000000000000..6f2642da1e13
--- /dev/null
+++ b/layout/reftests/canvas/693610-1-notref.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/canvas/693610-1.html b/layout/reftests/canvas/693610-1.html
new file mode 100644
index 000000000000..77745ae5f587
--- /dev/null
+++ b/layout/reftests/canvas/693610-1.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/canvas/reftest.list b/layout/reftests/canvas/reftest.list
index 9298988b6d41..6c44daf407fc 100644
--- a/layout/reftests/canvas/reftest.list
+++ b/layout/reftests/canvas/reftest.list
@@ -67,3 +67,6 @@ fails == ctm-singular-sanity.html data:text/html,Pass # Bug 612033
fails-if(cocoaWidget) == 672646-alpha-radial-gradient.html 672646-alpha-radial-gradient-ref.html # Bug 673333
== 674003-alpha-radial-gradient-superlum.html 674003-alpha-radial-gradient-superlum-ref.html
+
+!= 693610-1.html 693610-1-notref.html # bug 693610: multiple glyph runs should not be overprinted
+
diff --git a/layout/reftests/selection/splitText-normalize.js b/layout/reftests/selection/splitText-normalize.js
index c0b1e976e339..820c2bd2112d 100644
--- a/layout/reftests/selection/splitText-normalize.js
+++ b/layout/reftests/selection/splitText-normalize.js
@@ -16,6 +16,7 @@ var tests = [
[ {endNode:1}, [0,4], "012345678", "" ],
[ {endNode:1}, [0,4], "01234567", "8" ],
[ {endNode:1}, [1,4], "0", "12345678" ],
+ [ {startOffset:1,endNode:1}, [0,0], "0", "12345678" ],
[ {endNode:2}, [1,4], "0", "12345", "678" ],
]
diff --git a/layout/reftests/svg/filter-extref-differentOrigin-01.svg b/layout/reftests/svg/filter-extref-differentOrigin-01.svg
new file mode 100644
index 000000000000..c10d5b89fdde
--- /dev/null
+++ b/layout/reftests/svg/filter-extref-differentOrigin-01.svg
@@ -0,0 +1,12 @@
+
+
diff --git a/layout/reftests/svg/mask-extref-dataURI-01.svg b/layout/reftests/svg/mask-extref-dataURI-01.svg
new file mode 100644
index 000000000000..b79258fb1763
--- /dev/null
+++ b/layout/reftests/svg/mask-extref-dataURI-01.svg
@@ -0,0 +1,38 @@
+
+
+
+]>
+
diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list
index ce2ec1c6a2e7..51d2bf6e7159 100644
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -105,6 +105,7 @@ random == dynamic-use-nested-01.svg dynamic-use-nested-01-ref.svg # bug 467498
== use-01.svg pass.svg
== use-01-extref.svg pass.svg
== use-02-extref.svg use-02-extref-ref.svg
+== use-extref-dataURI-01.svg pass.svg
== use-children.svg pass.svg
== fallback-color-01a.svg pass.svg
== fallback-color-01b.svg pass.svg
@@ -116,6 +117,7 @@ random == dynamic-use-nested-01.svg dynamic-use-nested-01-ref.svg # bug 467498
== filter-basic-03.svg pass.svg
== filter-bounds-01.svg pass.svg
== filter-bounds-02.svg pass.svg
+== filter-extref-differentOrigin-01.svg pass.svg
== filter-foreignObject-01.svg pass.svg
== filter-invalidation-01.svg pass.svg
== filter-scaled-01.svg pass.svg
@@ -146,6 +148,7 @@ fails == inline-in-xul-basic-01.xul pass.svg
== marker-viewBox-01.svg marker-viewBox-01-ref.svg
== mask-basic-01.svg pass.svg
== mask-basic-02.svg mask-basic-02-ref.svg
+== mask-extref-dataURI-01.svg pass.svg
== mask-containing-masked-content-01.svg pass.svg
== mask-transformed-01.svg mask-transformed-01-ref.svg
== nested-viewBox-01.svg pass.svg
diff --git a/layout/reftests/svg/use-extref-dataURI-01.svg b/layout/reftests/svg/use-extref-dataURI-01.svg
new file mode 100644
index 000000000000..3274a21264c4
--- /dev/null
+++ b/layout/reftests/svg/use-extref-dataURI-01.svg
@@ -0,0 +1,12 @@
+
+
diff --git a/layout/reftests/text-overflow/bidi-simple-scrolled.html b/layout/reftests/text-overflow/bidi-simple-scrolled.html
index 5036958687c8..489535ab83f3 100644
--- a/layout/reftests/text-overflow/bidi-simple-scrolled.html
+++ b/layout/reftests/text-overflow/bidi-simple-scrolled.html
@@ -17,7 +17,7 @@ html,body {
}
.test {
overflow:auto;
- text-overflow:ellipsis;
+ text-overflow:ellipsis ellipsis;
white-space:nowrap;
width: 4.4em;
margin-bottom:1em;
diff --git a/layout/reftests/text-overflow/ellipsis-font-fallback.html b/layout/reftests/text-overflow/ellipsis-font-fallback.html
index 4e4c32e4864c..f02d569d0a6c 100644
--- a/layout/reftests/text-overflow/ellipsis-font-fallback.html
+++ b/layout/reftests/text-overflow/ellipsis-font-fallback.html
@@ -31,7 +31,7 @@ html,body {
}
.o {
- text-overflow: ellipsis;
+ text-overflow: ellipsis ellipsis;
color:blue;
}
.o span {
diff --git a/layout/reftests/text-overflow/false-marker-overlap.html b/layout/reftests/text-overflow/false-marker-overlap.html
index 55ead4533cf7..28720226c093 100644
--- a/layout/reftests/text-overflow/false-marker-overlap.html
+++ b/layout/reftests/text-overflow/false-marker-overlap.html
@@ -19,7 +19,7 @@ html,body {
.test {
overflow: hidden;
white-space: nowrap;
- text-overflow: ellipsis;
+ text-overflow: ellipsis ellipsis;
color: black;
height: 6em;
width: 32.5em;
diff --git a/layout/reftests/text-overflow/marker-basic-ref.html b/layout/reftests/text-overflow/marker-basic-ref.html
index 42736eee178e..6f10b17a6323 100644
--- a/layout/reftests/text-overflow/marker-basic-ref.html
+++ b/layout/reftests/text-overflow/marker-basic-ref.html
@@ -134,6 +134,8 @@ x1 m { position:absolute; right:0; font-size:16px; }
#test9b { top:320px; left:100px; position:absolute; border:1px solid black; }
#test9c { top:320px; left:200px; position:absolute; border:1px solid black; }
#test9d { top:320px; left:300px; position:absolute; border:1px solid black; }
+#test10a { top:360px; left:0; position:absolute; }
+#test10b { top:360px; left:100px; position:absolute; }
@@ -299,6 +301,9 @@ x1 m { position:absolute; right:0; font-size:16px; }
+
+
+