mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge from mozilla-central.
This commit is contained in:
commit
c9cedc55d9
@ -62,10 +62,12 @@ XPIDLSRCS = \
|
||||
nsIAccessibleProvider.idl \
|
||||
nsIAccessibleSelectable.idl \
|
||||
nsIAccessNode.idl \
|
||||
nsIAccessibleCursorable.idl \
|
||||
nsIAccessibleEvent.idl \
|
||||
nsIAccessibleEditableText.idl \
|
||||
nsIAccessibleHyperLink.idl \
|
||||
nsIAccessibleHyperText.idl \
|
||||
nsIAccessiblePivot.idl \
|
||||
nsIAccessibleTable.idl \
|
||||
nsIAccessibleText.idl \
|
||||
nsIAccessibleValue.idl \
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=78:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** 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
|
||||
@ -17,15 +16,16 @@
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Jim Blandy
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Eitan Isaacson <eitan@monotonous.org> (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"),
|
||||
* 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
|
||||
@ -37,13 +37,23 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*
|
||||
* This header implements the functionality of <stdint.h>, even on systems that
|
||||
* lack it. It does so by completely delegating to a separate header, which
|
||||
* could just as easily be used. However, as this header is part of the
|
||||
* SpiderMonkey interface, we retain it for compatibility.
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIAccessiblePivot;
|
||||
|
||||
/**
|
||||
* An interface implemented by an accessible object that has an associated
|
||||
* virtual cursor. Typically, a top-level application or content document.
|
||||
* A virtual cursor is an implementation of nsIAccessiblePivot that provides an
|
||||
* exclusive spot in the cursorable's subtree, this could be used to create a
|
||||
* pseudo-focus or caret browsing experience that is centered around the
|
||||
* accessibility API.
|
||||
*/
|
||||
#ifndef jsstdint_h___
|
||||
#define jsstdint_h___
|
||||
#include "mozilla/StdInt.h"
|
||||
#endif /* jsstdint_h___ */
|
||||
[scriptable, uuid(5452dea5-d235-496f-8757-3ca016ff49ff)]
|
||||
interface nsIAccessibleCursorable : nsISupports
|
||||
{
|
||||
/**
|
||||
* The virtual cursor pivot this object manages.
|
||||
*/
|
||||
readonly attribute nsIAccessiblePivot virtualCursor;
|
||||
};
|
@ -441,10 +441,15 @@ interface nsIAccessibleEvent : nsISupports
|
||||
*/
|
||||
const unsigned long EVENT_OBJECT_ATTRIBUTE_CHANGED = 0x0055;
|
||||
|
||||
/**
|
||||
* A cursorable's virtual cursor has changed.
|
||||
*/
|
||||
const unsigned long EVENT_VIRTUALCURSOR_CHANGED = 0x0056;
|
||||
|
||||
/**
|
||||
* Help make sure event map does not get out-of-line.
|
||||
*/
|
||||
const unsigned long EVENT_LAST_ENTRY = 0x0056;
|
||||
const unsigned long EVENT_LAST_ENTRY = 0x0057;
|
||||
|
||||
/**
|
||||
* The type of event, based on the enumerated event values
|
||||
|
221
accessible/public/nsIAccessiblePivot.idl
Normal file
221
accessible/public/nsIAccessiblePivot.idl
Normal file
@ -0,0 +1,221 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** 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):
|
||||
* Eitan Isaacson <eitan@monotonous.org> (original author)
|
||||
*
|
||||
* 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 "nsISupports.idl"
|
||||
|
||||
typedef short TextBoundaryType;
|
||||
|
||||
interface nsIAccessible;
|
||||
interface nsIAccessibleText;
|
||||
interface nsIAccessibleTraversalRule;
|
||||
interface nsIAccessiblePivotObserver;
|
||||
|
||||
/**
|
||||
* The pivot interface encapsulates a reference to a single place in an accessible
|
||||
* subtree. The pivot is a point or a range in the accessible tree. This interface
|
||||
* provides traversal methods to move the pivot to next/prev state that complies
|
||||
* to a given rule.
|
||||
*/
|
||||
[scriptable, uuid(689058ae-e301-444f-acb0-b5c2b189f350)]
|
||||
interface nsIAccessiblePivot : nsISupports
|
||||
{
|
||||
const TextBoundaryType CHAR_BOUNDARY = 0;
|
||||
const TextBoundaryType WORD_BOUNDARY = 1;
|
||||
const TextBoundaryType LINE_BOUNDARY = 2;
|
||||
const TextBoundaryType ATTRIBUTE_RANGE_BOUNDARY = 3;
|
||||
|
||||
/**
|
||||
* The accessible the pivot is currently pointed at.
|
||||
*/
|
||||
attribute nsIAccessible position;
|
||||
|
||||
/**
|
||||
* The root of the subtree in which the pivot traverses.
|
||||
*/
|
||||
readonly attribute nsIAccessible root;
|
||||
|
||||
/**
|
||||
* The start offset of the text range the pivot points at, otherwise -1.
|
||||
*/
|
||||
readonly attribute long startOffset;
|
||||
|
||||
/**
|
||||
* The end offset of the text range the pivot points at, otherwise -1.
|
||||
*/
|
||||
readonly attribute long endOffset;
|
||||
|
||||
/**
|
||||
* Set the pivot's text range in a text accessible.
|
||||
*
|
||||
* @param aTextAccessible [in] the text accessible that contains the desired
|
||||
* range.
|
||||
* @param aStartOffset [in] the start offset to set.
|
||||
* @param aEndOffset [in] the end offset to set.
|
||||
* @throws NS_ERROR_INVALID_ARG when the offset exceeds the accessible's
|
||||
* character count.
|
||||
*/
|
||||
void setTextRange(in nsIAccessibleText aTextAccessible,
|
||||
in long aStartOffset, in long aEndOffset);
|
||||
|
||||
/**
|
||||
* Move pivot to next object complying to given traversal rule.
|
||||
*
|
||||
* @param aRule [in] traversal rule to use.
|
||||
* @return true on success, false if there are no new nodes to traverse to.
|
||||
*/
|
||||
boolean moveNext(in nsIAccessibleTraversalRule aRule);
|
||||
|
||||
/**
|
||||
* Move pivot to previous object complying to given traversal rule.
|
||||
*
|
||||
* @param aRule [in] traversal rule to use.
|
||||
* @return true on success, false if there are no new nodes to traverse to.
|
||||
*/
|
||||
boolean movePrevious(in nsIAccessibleTraversalRule aRule);
|
||||
|
||||
/**
|
||||
* Move pivot to first object in subtree complying to given traversal rule.
|
||||
*
|
||||
* @param aRule [in] traversal rule to use.
|
||||
* @return true on success, false if there are no new nodes to traverse to.
|
||||
*/
|
||||
boolean moveFirst(in nsIAccessibleTraversalRule aRule);
|
||||
|
||||
/**
|
||||
* Move pivot to last object in subtree complying to given traversal rule.
|
||||
*
|
||||
* @param aRule [in] traversal rule to use.
|
||||
* @return true on success, false if there are no new nodes to traverse to.
|
||||
*/
|
||||
boolean moveLast(in nsIAccessibleTraversalRule aRule);
|
||||
|
||||
/**
|
||||
* Move pivot to next text range.
|
||||
*
|
||||
* @param aBoundary [in] type of boundary for next text range, character, word,
|
||||
* etc.
|
||||
* @return true on success, false if there are is no more text.
|
||||
*/
|
||||
boolean moveNextByText(in TextBoundaryType aBoundary);
|
||||
|
||||
/**
|
||||
* Move pivot to previous text range.
|
||||
*
|
||||
* @param aBoundary [in] type of boundary for previous text range, character,
|
||||
* word, etc.
|
||||
* @return true on success, false if there are is no more text.
|
||||
*/
|
||||
boolean movePreviousByText(in TextBoundaryType aBoundary);
|
||||
|
||||
/**
|
||||
* Add an observer for pivot changes.
|
||||
*
|
||||
* @param aObserver [in] the observer object to be notified of pivot changes.
|
||||
*/
|
||||
void addObserver(in nsIAccessiblePivotObserver aObserver);
|
||||
|
||||
/**
|
||||
* Remove an observer for pivot changes.
|
||||
*
|
||||
* @param aObserver [in] the observer object to remove from being notified.
|
||||
*/
|
||||
void removeObserver(in nsIAccessiblePivotObserver aObserver);
|
||||
};
|
||||
|
||||
/**
|
||||
* An observer interface for pivot changes.
|
||||
*/
|
||||
[scriptable, uuid(b6508c5e-c081-467d-835c-613eedf9ee9b)]
|
||||
interface nsIAccessiblePivotObserver : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called when the pivot changes.
|
||||
*
|
||||
* @param aPivot [in] the pivot that has changed.
|
||||
* @param aOldAccessible [in] the old pivot position before the change, or null.
|
||||
* @param aOldStart [in] the old start offset, or -1.
|
||||
* @param aOldEnd [in] the old end offset, or -1.
|
||||
*/
|
||||
void onPivotChanged(in nsIAccessiblePivot aPivot,
|
||||
in nsIAccessible aOldAccessible,
|
||||
in long aOldStart, in long aOldEnd);
|
||||
};
|
||||
|
||||
[scriptable, uuid(307d98b6-dba9-49cf-ba17-ef8b053044eb)]
|
||||
interface nsIAccessibleTraversalRule : nsISupports
|
||||
{
|
||||
/* Ignore this accessible object */
|
||||
const unsigned short FILTER_IGNORE = 0x0;
|
||||
/* Accept this accessible object */
|
||||
const unsigned short FILTER_MATCH = 0x1;
|
||||
/* Don't traverse accessibles children */
|
||||
const unsigned short FILTER_IGNORE_SUBTREE = 0x2;
|
||||
|
||||
/* Pre-filters */
|
||||
const unsigned long PREFILTER_INVISIBLE = 0x00000001;
|
||||
const unsigned long PREFILTER_OFFSCREEN = 0x00000002;
|
||||
const unsigned long PREFILTER_NOT_FOCUSABLE = 0x00000004;
|
||||
|
||||
/**
|
||||
* Pre-filter bitfield to filter out obviously ignorable nodes and lighten
|
||||
* the load on match().
|
||||
*/
|
||||
readonly attribute unsigned long preFilter;
|
||||
|
||||
/**
|
||||
* Retrieve a list of roles that the traversal rule should test for. Any node
|
||||
* with a role not in this list will automatically be ignored. An empty list
|
||||
* will match all roles. It should be assumed that this method is called once
|
||||
* at the start of a traversal, so changing the method's return result after
|
||||
* that would have no affect.
|
||||
*
|
||||
* @param aRoles [out] an array of the roles to match.
|
||||
* @param aCount [out] the length of the array.
|
||||
*/
|
||||
void getMatchRoles([array, size_is(aCount)]out unsigned long aRoles,
|
||||
[retval]out unsigned long aCount);
|
||||
|
||||
/**
|
||||
* Determines if a given accessible is to be accepted in our traversal rule
|
||||
*
|
||||
* @param aAccessible [in] accessible to examine.
|
||||
* @return a bitfield of FILTER_MATCH and FILTER_IGNORE_SUBTREE.
|
||||
*/
|
||||
unsigned short match(in nsIAccessible aAccessible);
|
||||
};
|
@ -45,7 +45,7 @@ interface nsIPresShell;
|
||||
interface nsIDOMWindow;
|
||||
interface nsIAccessNode;
|
||||
interface nsIDOMDOMStringList;
|
||||
|
||||
interface nsIAccessiblePivot;
|
||||
|
||||
/**
|
||||
* An interface for in-process accessibility clients
|
||||
@ -112,6 +112,14 @@ interface nsIAccessibleRetrieval : nsISupports
|
||||
* @return cached accessible for the given DOM node if any
|
||||
*/
|
||||
nsIAccessible getAccessibleFromCache(in nsIDOMNode aNode);
|
||||
|
||||
/**
|
||||
* Create a new pivot for tracking a position and traversing a subtree.
|
||||
*
|
||||
* @param aRoot [in] the accessible root for the pivot
|
||||
* @return a new pivot
|
||||
*/
|
||||
nsIAccessiblePivot createAccessiblePivot(in nsIAccessible aRoot);
|
||||
};
|
||||
|
||||
|
||||
|
@ -65,6 +65,7 @@ CPPSRCS = \
|
||||
nsAccUtils.cpp \
|
||||
nsAccessibilityService.cpp \
|
||||
nsAccessible.cpp \
|
||||
nsAccessiblePivot.cpp \
|
||||
nsAccTreeWalker.cpp \
|
||||
nsBaseWidgetAccessible.cpp \
|
||||
nsEventShell.cpp \
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
// NOTE: alphabetically ordered
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccessiblePivot.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsApplicationAccessibleWrap.h"
|
||||
@ -864,6 +865,23 @@ nsAccessibilityService::GetAccessibleFromCache(nsIDOMNode* aNode,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessibilityService::CreateAccessiblePivot(nsIAccessible* aRoot,
|
||||
nsIAccessiblePivot** aPivot)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPivot);
|
||||
NS_ENSURE_ARG(aRoot);
|
||||
*aPivot = nsnull;
|
||||
|
||||
nsRefPtr<nsAccessible> accessibleRoot(do_QueryObject(aRoot));
|
||||
NS_ENSURE_TRUE(accessibleRoot, NS_ERROR_INVALID_ARG);
|
||||
|
||||
nsAccessiblePivot* pivot = new nsAccessiblePivot(accessibleRoot);
|
||||
NS_ADDREF(*aPivot = pivot);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAccesibilityService
|
||||
nsAccessible*
|
||||
nsAccessibilityService::GetAccessibleInShell(nsINode* aNode,
|
||||
|
@ -534,6 +534,7 @@ static const char kEventTypeNames[][40] = {
|
||||
"hypertext changed", // EVENT_HYPERTEXT_CHANGED
|
||||
"hypertext links count changed", // EVENT_HYPERTEXT_NLINKS_CHANGED
|
||||
"object attribute changed", // EVENT_OBJECT_ATTRIBUTE_CHANGED
|
||||
"virtual cursor changed" // EVENT_VIRTUALCURSOR_CHANGED
|
||||
};
|
||||
|
||||
/**
|
||||
|
526
accessible/src/base/nsAccessiblePivot.cpp
Normal file
526
accessible/src/base/nsAccessiblePivot.cpp
Normal file
@ -0,0 +1,526 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** 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):
|
||||
* Eitan Isaacson <eitan@monotonous.org> (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 "nsAccessiblePivot.h"
|
||||
|
||||
#include "nsAccessible.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsHyperTextAccessible.h"
|
||||
#include "States.h"
|
||||
|
||||
#include "nsArrayUtils.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
|
||||
/**
|
||||
* An object that stores a given traversal rule during
|
||||
*/
|
||||
class RuleCache
|
||||
{
|
||||
public:
|
||||
RuleCache(nsIAccessibleTraversalRule* aRule) : mRule(aRule),
|
||||
mAcceptRoles(nsnull) { }
|
||||
~RuleCache () {
|
||||
if (mAcceptRoles)
|
||||
nsMemory::Free(mAcceptRoles);
|
||||
}
|
||||
|
||||
nsresult ApplyFilter(nsAccessible* aAccessible, PRUint16* aResult);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIAccessibleTraversalRule> mRule;
|
||||
PRUint32* mAcceptRoles;
|
||||
PRUint32 mAcceptRolesLength;
|
||||
PRUint32 mPreFilter;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccessiblePivot
|
||||
|
||||
nsAccessiblePivot::nsAccessiblePivot(nsAccessible* aRoot) :
|
||||
mRoot(aRoot), mPosition(nsnull),
|
||||
mStartOffset(-1), mEndOffset(-1)
|
||||
{
|
||||
NS_ASSERTION(aRoot, "A root accessible is required");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsISupports
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsAccessiblePivot)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAccessiblePivot)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRoot, nsIAccessible)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mPosition, nsIAccessible)
|
||||
PRUint32 i, length = tmp->mObservers.Length(); \
|
||||
for (i = 0; i < length; ++i) {
|
||||
cb.NoteXPCOMChild(tmp->mObservers.ElementAt(i).get());
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAccessiblePivot)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPosition)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mObservers)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessiblePivot)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
|
||||
NS_INTERFACE_MAP_ENTRY(nsAccessiblePivot)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccessiblePivot)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAccessiblePivot)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsIAccessiblePivot
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::GetRoot(nsIAccessible** aRoot)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRoot);
|
||||
|
||||
NS_IF_ADDREF(*aRoot = mRoot);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::GetPosition(nsIAccessible** aPosition)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPosition);
|
||||
|
||||
NS_IF_ADDREF(*aPosition = mPosition);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::SetPosition(nsIAccessible* aPosition)
|
||||
{
|
||||
nsRefPtr<nsAccessible> secondPosition;
|
||||
|
||||
if (aPosition) {
|
||||
secondPosition = do_QueryObject(aPosition);
|
||||
if (!secondPosition || !IsRootDescendant(secondPosition))
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Swap old position with new position, saves us an AddRef/Release.
|
||||
mPosition.swap(secondPosition);
|
||||
PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
|
||||
mStartOffset = mEndOffset = -1;
|
||||
NotifyPivotChanged(secondPosition, oldStart, oldEnd);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::GetStartOffset(PRInt32* aStartOffset)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aStartOffset);
|
||||
|
||||
*aStartOffset = mStartOffset;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::GetEndOffset(PRInt32* aEndOffset)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEndOffset);
|
||||
|
||||
*aEndOffset = mEndOffset;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::SetTextRange(nsIAccessibleText* aTextAccessible,
|
||||
PRInt32 aStartOffset, PRInt32 aEndOffset)
|
||||
{
|
||||
NS_ENSURE_ARG(aTextAccessible);
|
||||
|
||||
// Check that start offset is smaller than end offset, and that if a value is
|
||||
// smaller than 0, both should be -1.
|
||||
NS_ENSURE_TRUE(aStartOffset <= aEndOffset &&
|
||||
(aStartOffset >= 0 || (aStartOffset != -1 && aEndOffset != -1)),
|
||||
NS_ERROR_INVALID_ARG);
|
||||
|
||||
nsRefPtr<nsHyperTextAccessible> newPosition = do_QueryObject(aTextAccessible);
|
||||
if (!newPosition || !IsRootDescendant(newPosition))
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
// Make sure the given offsets don't exceed the character count.
|
||||
PRInt32 charCount = newPosition->CharacterCount();
|
||||
|
||||
if (aEndOffset > charCount)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
|
||||
mStartOffset = aStartOffset;
|
||||
mEndOffset = aEndOffset;
|
||||
|
||||
nsRefPtr<nsAccessible> oldPosition = mPosition.forget();
|
||||
mPosition = newPosition.forget();
|
||||
|
||||
NotifyPivotChanged(oldPosition, oldStart, oldEnd);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Traversal functions
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule, bool* aResult)
|
||||
{
|
||||
NS_ENSURE_ARG(aResult);
|
||||
NS_ENSURE_ARG(aRule);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
nsAccessible* accessible = SearchForward(mPosition, aRule, false, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aResult = accessible;
|
||||
if (*aResult)
|
||||
MovePivotInternal(accessible);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule, bool* aResult)
|
||||
{
|
||||
NS_ENSURE_ARG(aResult);
|
||||
NS_ENSURE_ARG(aRule);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
nsAccessible* accessible = SearchBackward(mPosition, aRule, false, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aResult = accessible;
|
||||
if (*aResult)
|
||||
MovePivotInternal(accessible);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult)
|
||||
{
|
||||
NS_ENSURE_ARG(aResult);
|
||||
NS_ENSURE_ARG(aRule);
|
||||
nsresult rv = NS_OK;
|
||||
nsAccessible* accessible = SearchForward(mRoot, aRule, true, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aResult = accessible;
|
||||
if (*aResult)
|
||||
MovePivotInternal(accessible);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule, bool* aResult)
|
||||
{
|
||||
NS_ENSURE_ARG(aResult);
|
||||
NS_ENSURE_ARG(aRule);
|
||||
|
||||
*aResult = false;
|
||||
nsresult rv = NS_OK;
|
||||
nsAccessible* lastAccessible = mRoot;
|
||||
nsAccessible* accessible = nsnull;
|
||||
|
||||
// First got to the last accessible in pre-order
|
||||
while (lastAccessible->HasChildren())
|
||||
lastAccessible = lastAccessible->LastChild();
|
||||
|
||||
// Search backwards from last accessible and find the last occurrence in the doc
|
||||
accessible = SearchBackward(lastAccessible, aRule, true, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aResult = accessible;
|
||||
if (*aResult)
|
||||
MovePivotInternal(accessible);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary, bool* aResult)
|
||||
{
|
||||
NS_ENSURE_ARG(aResult);
|
||||
|
||||
*aResult = false;
|
||||
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult)
|
||||
{
|
||||
NS_ENSURE_ARG(aResult);
|
||||
|
||||
*aResult = false;
|
||||
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// Observer functions
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::AddObserver(nsIAccessiblePivotObserver* aObserver)
|
||||
{
|
||||
NS_ENSURE_ARG(aObserver);
|
||||
|
||||
mObservers.AppendElement(aObserver);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver* aObserver)
|
||||
{
|
||||
NS_ENSURE_ARG(aObserver);
|
||||
|
||||
return mObservers.RemoveElement(aObserver) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Private utility methods
|
||||
|
||||
bool
|
||||
nsAccessiblePivot::IsRootDescendant(nsAccessible* aAccessible)
|
||||
{
|
||||
nsAccessible* accessible = aAccessible;
|
||||
do {
|
||||
if (accessible == mRoot)
|
||||
return true;
|
||||
} while ((accessible = accessible->Parent()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
nsAccessiblePivot::MovePivotInternal(nsAccessible* aPosition)
|
||||
{
|
||||
nsRefPtr<nsAccessible> oldPosition = mPosition.forget();
|
||||
mPosition = aPosition;
|
||||
PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
|
||||
mStartOffset = mEndOffset = -1;
|
||||
|
||||
NotifyPivotChanged(oldPosition, oldStart, oldEnd);
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsAccessiblePivot::SearchBackward(nsAccessible* aAccessible,
|
||||
nsIAccessibleTraversalRule* aRule,
|
||||
bool searchCurrent,
|
||||
nsresult* rv)
|
||||
{
|
||||
*rv = NS_OK;
|
||||
|
||||
// Initial position could be unset, in that case return null.
|
||||
if (!aAccessible)
|
||||
return nsnull;
|
||||
|
||||
RuleCache cache(aRule);
|
||||
nsAccessible* accessible = aAccessible;
|
||||
|
||||
PRUint16 filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
||||
|
||||
if (searchCurrent) {
|
||||
*rv = cache.ApplyFilter(accessible, &filtered);
|
||||
NS_ENSURE_SUCCESS(*rv, nsnull);
|
||||
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
|
||||
return accessible;
|
||||
}
|
||||
|
||||
while (accessible != mRoot) {
|
||||
nsAccessible* parent = accessible->Parent();
|
||||
PRInt32 idxInParent = accessible->IndexInParent();
|
||||
while (idxInParent > 0) {
|
||||
if (!(accessible = parent->GetChildAt(--idxInParent)))
|
||||
continue;
|
||||
|
||||
*rv = cache.ApplyFilter(accessible, &filtered);
|
||||
NS_ENSURE_SUCCESS(*rv, nsnull);
|
||||
|
||||
nsAccessible* lastChild;
|
||||
while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
|
||||
(lastChild = accessible->LastChild())) {
|
||||
parent = accessible;
|
||||
accessible = lastChild;
|
||||
*rv = cache.ApplyFilter(accessible, &filtered);
|
||||
NS_ENSURE_SUCCESS(*rv, nsnull);
|
||||
}
|
||||
|
||||
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
|
||||
return accessible;
|
||||
}
|
||||
|
||||
if (!(accessible = parent))
|
||||
break;
|
||||
|
||||
*rv = cache.ApplyFilter(accessible, &filtered);
|
||||
NS_ENSURE_SUCCESS(*rv, nsnull);
|
||||
|
||||
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
|
||||
return accessible;
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
nsAccessiblePivot::SearchForward(nsAccessible* aAccessible,
|
||||
nsIAccessibleTraversalRule* aRule,
|
||||
bool searchCurrent,
|
||||
nsresult* rv)
|
||||
{
|
||||
*rv = NS_OK;
|
||||
|
||||
// Initial position could be not set, in that case begin search from root.
|
||||
nsAccessible *accessible = (!aAccessible) ? mRoot.get() : aAccessible;
|
||||
|
||||
RuleCache cache(aRule);
|
||||
|
||||
PRUint16 filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
||||
*rv = cache.ApplyFilter(accessible, &filtered);
|
||||
NS_ENSURE_SUCCESS(*rv, nsnull);
|
||||
if (searchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH))
|
||||
return accessible;
|
||||
|
||||
while (true) {
|
||||
nsAccessible* firstChild = nsnull;
|
||||
while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
|
||||
(firstChild = accessible->FirstChild())) {
|
||||
accessible = firstChild;
|
||||
*rv = cache.ApplyFilter(accessible, &filtered);
|
||||
NS_ENSURE_SUCCESS(*rv, nsnull);
|
||||
|
||||
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
|
||||
return accessible;
|
||||
}
|
||||
|
||||
nsAccessible* sibling = nsnull;
|
||||
nsAccessible* temp = accessible;
|
||||
do {
|
||||
if (temp == mRoot)
|
||||
break;
|
||||
|
||||
sibling = temp->NextSibling();
|
||||
|
||||
if (sibling)
|
||||
break;
|
||||
} while ((temp = temp->Parent()));
|
||||
|
||||
if (!sibling)
|
||||
break;
|
||||
|
||||
accessible = sibling;
|
||||
*rv = cache.ApplyFilter(accessible, &filtered);
|
||||
NS_ENSURE_SUCCESS(*rv, nsnull);
|
||||
|
||||
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
|
||||
return accessible;
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
nsAccessiblePivot::NotifyPivotChanged(nsAccessible* aOldPosition,
|
||||
PRInt32 aOldStart, PRInt32 aOldEnd)
|
||||
{
|
||||
nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> >::ForwardIterator iter(mObservers);
|
||||
while (iter.HasMore()) {
|
||||
nsIAccessiblePivotObserver* obs = iter.GetNext();
|
||||
obs->OnPivotChanged(this, aOldPosition, aOldStart, aOldEnd);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
RuleCache::ApplyFilter(nsAccessible* aAccessible, PRUint16* aResult)
|
||||
{
|
||||
*aResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
||||
|
||||
if (!mAcceptRoles) {
|
||||
nsresult rv = mRule->GetMatchRoles(&mAcceptRoles, &mAcceptRolesLength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mRule->GetPreFilter(&mPreFilter);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (mPreFilter) {
|
||||
PRUint64 state = aAccessible->State();
|
||||
|
||||
if ((nsIAccessibleTraversalRule::PREFILTER_INVISIBLE & mPreFilter) &&
|
||||
(state & states::INVISIBLE))
|
||||
return NS_OK;
|
||||
|
||||
if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN & mPreFilter) &&
|
||||
(state & states::OFFSCREEN))
|
||||
return NS_OK;
|
||||
|
||||
if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
|
||||
!(state & states::FOCUSABLE))
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mAcceptRolesLength > 0) {
|
||||
PRUint32 accessibleRole = aAccessible->Role();
|
||||
bool matchesRole = false;
|
||||
for (PRUint32 idx = 0; idx < mAcceptRolesLength; idx++) {
|
||||
matchesRole = mAcceptRoles[idx] == accessibleRole;
|
||||
if (matchesRole)
|
||||
break;
|
||||
}
|
||||
if (!matchesRole)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return mRule->Match(aAccessible, aResult);
|
||||
}
|
134
accessible/src/base/nsAccessiblePivot.h
Normal file
134
accessible/src/base/nsAccessiblePivot.h
Normal file
@ -0,0 +1,134 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** 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):
|
||||
* Eitan Isaacson <eitan@monotonous.org> (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 _nsAccessiblePivot_H_
|
||||
#define _nsAccessiblePivot_H_
|
||||
|
||||
#include "nsIAccessiblePivot.h"
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTObserverArray.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsAccessible;
|
||||
class nsIAccessibleTraversalRule;
|
||||
|
||||
/**
|
||||
* Class represents an accessible pivot.
|
||||
*/
|
||||
class nsAccessiblePivot: public nsIAccessiblePivot
|
||||
{
|
||||
public:
|
||||
nsAccessiblePivot(nsAccessible* aRoot);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsAccessiblePivot, nsIAccessiblePivot)
|
||||
|
||||
NS_DECL_NSIACCESSIBLEPIVOT
|
||||
|
||||
/*
|
||||
* A simple getter for the pivot's position.
|
||||
*/
|
||||
nsAccessible* Position() { return mPosition; }
|
||||
|
||||
private:
|
||||
nsAccessiblePivot() MOZ_DELETE;
|
||||
nsAccessiblePivot(const nsAccessiblePivot&) MOZ_DELETE;
|
||||
void operator = (const nsAccessiblePivot&) MOZ_DELETE;
|
||||
|
||||
/*
|
||||
* Notify all observers on a pivot change.
|
||||
*/
|
||||
void NotifyPivotChanged(nsAccessible* aOldAccessible,
|
||||
PRInt32 aOldStart, PRInt32 aOldEnd);
|
||||
|
||||
/*
|
||||
* Check to see that the given accessible is in the pivot's subtree.
|
||||
*/
|
||||
bool IsRootDescendant(nsAccessible* aAccessible);
|
||||
|
||||
|
||||
/*
|
||||
* Search in preorder for the first accessible to match the rule.
|
||||
*/
|
||||
nsAccessible* SearchForward(nsAccessible* aAccessible,
|
||||
nsIAccessibleTraversalRule* aRule,
|
||||
bool searchCurrent,
|
||||
nsresult* rv);
|
||||
|
||||
/*
|
||||
* Reverse search in preorder for the first accessible to match the rule.
|
||||
*/
|
||||
nsAccessible* SearchBackward(nsAccessible* aAccessible,
|
||||
nsIAccessibleTraversalRule* aRule,
|
||||
bool searchCurrent,
|
||||
nsresult* rv);
|
||||
|
||||
/*
|
||||
* Update the pivot, and notify observers.
|
||||
*/
|
||||
void MovePivotInternal(nsAccessible* aPosition);
|
||||
|
||||
/*
|
||||
* The root accessible.
|
||||
*/
|
||||
nsRefPtr<nsAccessible> mRoot;
|
||||
|
||||
/*
|
||||
* The current pivot position.
|
||||
*/
|
||||
nsRefPtr<nsAccessible> mPosition;
|
||||
|
||||
/*
|
||||
* The text start offset ofthe pivot.
|
||||
*/
|
||||
PRInt32 mStartOffset;
|
||||
|
||||
/*
|
||||
* The text end offset ofthe pivot.
|
||||
*/
|
||||
PRInt32 mEndOffset;
|
||||
|
||||
/*
|
||||
* The list of pivot-changed observers.
|
||||
*/
|
||||
nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> > mObservers;
|
||||
};
|
||||
|
||||
#endif
|
@ -39,6 +39,7 @@
|
||||
#include "AccIterator.h"
|
||||
#include "nsAccCache.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccessiblePivot.h"
|
||||
#include "nsAccTreeWalker.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsRootAccessible.h"
|
||||
@ -105,7 +106,8 @@ nsDocAccessible::
|
||||
nsIWeakReference *aShell) :
|
||||
nsHyperTextAccessibleWrap(aRootContent, aShell),
|
||||
mDocument(aDocument), mScrollPositionChangedTicks(0),
|
||||
mLoadState(eTreeConstructionPending), mLoadEventType(0)
|
||||
mLoadState(eTreeConstructionPending), mLoadEventType(0),
|
||||
mVirtualCursor(nsnull)
|
||||
{
|
||||
mFlags |= eDocAccessible;
|
||||
|
||||
@ -125,6 +127,10 @@ nsDocAccessible::
|
||||
// nsAccDocManager creates document accessible when scrollable frame is
|
||||
// available already, it should be safe time to add scroll listener.
|
||||
AddScrollListener();
|
||||
|
||||
// We provide a virtual cursor if this is a root doc or if it's a tab doc.
|
||||
mIsCursorable = (!(mDocument->GetParentDocument()) ||
|
||||
nsCoreUtils::IsTabDocument(mDocument));
|
||||
}
|
||||
|
||||
nsDocAccessible::~nsDocAccessible()
|
||||
@ -142,6 +148,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNotificationController,
|
||||
NotificationController)
|
||||
|
||||
if (tmp->mVirtualCursor) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mVirtualCursor,
|
||||
nsAccessiblePivot)
|
||||
}
|
||||
|
||||
PRUint32 i, length = tmp->mChildDocuments.Length();
|
||||
for (i = 0; i < length; ++i) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mChildDocuments[i],
|
||||
@ -154,6 +165,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNotificationController)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mVirtualCursor)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments)
|
||||
tmp->mDependentIDsHash.Clear();
|
||||
tmp->mNodeToAccessibleMap.Clear();
|
||||
@ -167,7 +179,10 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDocAccessible)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleCursorable,
|
||||
mIsCursorable)
|
||||
foundInterface = 0;
|
||||
|
||||
nsresult status;
|
||||
@ -516,6 +531,27 @@ nsDocAccessible::GetChildDocumentAt(PRUint32 aIndex,
|
||||
return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// nsIAccessibleVirtualCursor method
|
||||
NS_IMETHODIMP
|
||||
nsDocAccessible::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aVirtualCursor);
|
||||
*aVirtualCursor = nsnull;
|
||||
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
NS_ENSURE_TRUE(mIsCursorable, NS_ERROR_NOT_IMPLEMENTED);
|
||||
|
||||
if (!mVirtualCursor) {
|
||||
mVirtualCursor = new nsAccessiblePivot(this);
|
||||
mVirtualCursor->AddObserver(this);
|
||||
}
|
||||
|
||||
NS_ADDREF(*aVirtualCursor = mVirtualCursor);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAccessibleHyperText method
|
||||
NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor)
|
||||
{
|
||||
@ -637,6 +673,11 @@ nsDocAccessible::Shutdown()
|
||||
|
||||
mChildDocuments.Clear();
|
||||
|
||||
if (mVirtualCursor) {
|
||||
mVirtualCursor->RemoveObserver(this);
|
||||
mVirtualCursor = nsnull;
|
||||
}
|
||||
|
||||
mWeakShell = nsnull; // Avoid reentrancy
|
||||
|
||||
mDependentIDsHash.Clear();
|
||||
@ -888,6 +929,20 @@ NS_IMETHODIMP nsDocAccessible::Observe(nsISupports *aSubject, const char *aTopic
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsIAccessiblePivotObserver
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocAccessible::OnPivotChanged(nsIAccessiblePivot* aPivot,
|
||||
nsIAccessible* aOldAccessible,
|
||||
PRInt32 aOldStart, PRInt32 aOldEnd)
|
||||
{
|
||||
nsRefPtr<AccEvent> event = new AccEvent(nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED, this);
|
||||
nsEventShell::FireEvent(event);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsIDocumentObserver
|
||||
|
||||
|
@ -39,7 +39,9 @@
|
||||
#ifndef _nsDocAccessible_H_
|
||||
#define _nsDocAccessible_H_
|
||||
|
||||
#include "nsIAccessibleCursorable.h"
|
||||
#include "nsIAccessibleDocument.h"
|
||||
#include "nsIAccessiblePivot.h"
|
||||
|
||||
#include "nsEventShell.h"
|
||||
#include "nsHyperTextAccessibleWrap.h"
|
||||
@ -58,6 +60,7 @@
|
||||
#include "nsIDocShellTreeNode.h"
|
||||
|
||||
class nsIScrollableView;
|
||||
class nsAccessiblePivot;
|
||||
|
||||
const PRUint32 kDefaultCacheSize = 256;
|
||||
|
||||
@ -74,8 +77,10 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap,
|
||||
public nsIDocumentObserver,
|
||||
public nsIObserver,
|
||||
public nsIScrollPositionListener,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public nsSupportsWeakReference,
|
||||
public nsIAccessibleCursorable,
|
||||
public nsIAccessiblePivotObserver
|
||||
{
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDocAccessible, nsAccessible)
|
||||
|
||||
@ -84,6 +89,10 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap,
|
||||
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
NS_DECL_NSIACCESSIBLECURSORABLE
|
||||
|
||||
NS_DECL_NSIACCESSIBLEPIVOTOBSERVER
|
||||
|
||||
public:
|
||||
using nsAccessible::GetParent;
|
||||
|
||||
@ -595,6 +604,16 @@ protected:
|
||||
|
||||
nsTArray<nsRefPtr<nsDocAccessible> > mChildDocuments;
|
||||
|
||||
/**
|
||||
* Whether we support nsIAccessibleCursorable, used when querying the interface.
|
||||
*/
|
||||
bool mIsCursorable;
|
||||
|
||||
/**
|
||||
* The virtual cursor of the document when it supports nsIAccessibleCursorable.
|
||||
*/
|
||||
nsRefPtr<nsAccessiblePivot> mVirtualCursor;
|
||||
|
||||
/**
|
||||
* A storage class for pairing content with one of its relation attributes.
|
||||
*/
|
||||
|
@ -63,14 +63,6 @@
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
/// the accessible library and cached methods
|
||||
HINSTANCE nsAccessNodeWrap::gmAccLib = nsnull;
|
||||
HINSTANCE nsAccessNodeWrap::gmUserLib = nsnull;
|
||||
LPFNACCESSIBLEOBJECTFROMWINDOW nsAccessNodeWrap::gmAccessibleObjectFromWindow = nsnull;
|
||||
LPFNLRESULTFROMOBJECT nsAccessNodeWrap::gmLresultFromObject = NULL;
|
||||
LPFNNOTIFYWINEVENT nsAccessNodeWrap::gmNotifyWinEvent = nsnull;
|
||||
LPFNGETGUITHREADINFO nsAccessNodeWrap::gmGetGUIThreadInfo = nsnull;
|
||||
|
||||
AccTextChangeEvent* nsAccessNodeWrap::gTextEvent = nsnull;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -591,17 +583,6 @@ __try {
|
||||
|
||||
void nsAccessNodeWrap::InitAccessibility()
|
||||
{
|
||||
if (!gmUserLib) {
|
||||
gmUserLib =::LoadLibraryW(L"USER32.DLL");
|
||||
}
|
||||
|
||||
if (gmUserLib) {
|
||||
if (!gmNotifyWinEvent)
|
||||
gmNotifyWinEvent = (LPFNNOTIFYWINEVENT)GetProcAddress(gmUserLib,"NotifyWinEvent");
|
||||
if (!gmGetGUIThreadInfo)
|
||||
gmGetGUIThreadInfo = (LPFNGETGUITHREADINFO)GetProcAddress(gmUserLib,"GetGUIThreadInfo");
|
||||
}
|
||||
|
||||
Compatibility::Init();
|
||||
|
||||
nsWinUtils::MaybeStartWindowEmulation();
|
||||
@ -678,8 +659,8 @@ nsAccessNodeWrap::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
IAccessible* msaaAccessible = NULL;
|
||||
document->GetNativeInterface((void**)&msaaAccessible); // does an addref
|
||||
if (msaaAccessible) {
|
||||
LRESULT result = LresultFromObject(IID_IAccessible, wParam,
|
||||
msaaAccessible); // does an addref
|
||||
LRESULT result = ::LresultFromObject(IID_IAccessible, wParam,
|
||||
msaaAccessible); // does an addref
|
||||
msaaAccessible->Release(); // release extra addref
|
||||
return result;
|
||||
}
|
||||
@ -698,21 +679,3 @@ nsAccessNodeWrap::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
|
||||
return ::DefWindowProcW(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(LRESULT)
|
||||
nsAccessNodeWrap::LresultFromObject(REFIID riid, WPARAM wParam, LPUNKNOWN pAcc)
|
||||
{
|
||||
// open the dll dynamically
|
||||
if (!gmAccLib)
|
||||
gmAccLib =::LoadLibraryW(L"OLEACC.DLL");
|
||||
|
||||
if (gmAccLib) {
|
||||
if (!gmLresultFromObject)
|
||||
gmLresultFromObject = (LPFNLRESULTFROMOBJECT)GetProcAddress(gmAccLib,"LresultFromObject");
|
||||
|
||||
if (gmLresultFromObject)
|
||||
return gmLresultFromObject(riid, wParam, pAcc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -67,8 +67,6 @@
|
||||
|
||||
#include "nsRefPtrHashtable.h"
|
||||
|
||||
typedef LRESULT (STDAPICALLTYPE *LPFNNOTIFYWINEVENT)(DWORD event,HWND hwnd,LONG idObjectType,LONG idObject);
|
||||
typedef LRESULT (STDAPICALLTYPE *LPFNGETGUITHREADINFO)(DWORD idThread, GUITHREADINFO* pgui);
|
||||
|
||||
class AccTextChangeEvent;
|
||||
|
||||
@ -149,18 +147,8 @@ public: // construction, destruction
|
||||
static void InitAccessibility();
|
||||
static void ShutdownAccessibility();
|
||||
|
||||
/// the accessible library and cached methods
|
||||
static HINSTANCE gmAccLib;
|
||||
static HINSTANCE gmUserLib;
|
||||
static LPFNACCESSIBLEOBJECTFROMWINDOW gmAccessibleObjectFromWindow;
|
||||
static LPFNLRESULTFROMOBJECT gmLresultFromObject;
|
||||
static LPFNNOTIFYWINEVENT gmNotifyWinEvent;
|
||||
static LPFNGETGUITHREADINFO gmGetGUIThreadInfo;
|
||||
|
||||
static int FilterA11yExceptions(unsigned int aCode, EXCEPTION_POINTERS *aExceptionInfo);
|
||||
|
||||
static STDMETHODIMP_(LRESULT) LresultFromObject(REFIID riid, WPARAM wParam, LPUNKNOWN pAcc);
|
||||
|
||||
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT Msg,
|
||||
WPARAM WParam, LPARAM lParam);
|
||||
|
||||
|
@ -163,38 +163,6 @@ __try {
|
||||
// IAccessible methods
|
||||
//-----------------------------------------------------
|
||||
|
||||
|
||||
STDMETHODIMP nsAccessibleWrap::AccessibleObjectFromWindow(HWND hwnd,
|
||||
DWORD dwObjectID,
|
||||
REFIID riid,
|
||||
void **ppvObject)
|
||||
{
|
||||
// open the dll dynamically
|
||||
if (!gmAccLib)
|
||||
gmAccLib =::LoadLibraryW(L"OLEACC.DLL");
|
||||
|
||||
if (gmAccLib) {
|
||||
if (!gmAccessibleObjectFromWindow)
|
||||
gmAccessibleObjectFromWindow = (LPFNACCESSIBLEOBJECTFROMWINDOW)GetProcAddress(gmAccLib,"AccessibleObjectFromWindow");
|
||||
|
||||
if (gmAccessibleObjectFromWindow)
|
||||
return gmAccessibleObjectFromWindow(hwnd, dwObjectID, riid, ppvObject);
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
STDMETHODIMP nsAccessibleWrap::NotifyWinEvent(DWORD event,
|
||||
HWND hwnd,
|
||||
LONG idObjectType,
|
||||
LONG idObject)
|
||||
{
|
||||
if (gmNotifyWinEvent)
|
||||
return gmNotifyWinEvent(event, hwnd, idObjectType, idObject);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
STDMETHODIMP nsAccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
|
||||
{
|
||||
__try {
|
||||
@ -211,9 +179,9 @@ __try {
|
||||
nsWinUtils::IsWindowEmulationStarted() &&
|
||||
nsCoreUtils::IsTabDocument(doc->GetDocumentNode())) {
|
||||
HWND hwnd = static_cast<HWND>(doc->GetNativeWindow());
|
||||
if (hwnd && SUCCEEDED(AccessibleObjectFromWindow(hwnd, OBJID_WINDOW,
|
||||
IID_IAccessible,
|
||||
(void**)ppdispParent))) {
|
||||
if (hwnd && SUCCEEDED(::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW,
|
||||
IID_IAccessible,
|
||||
(void**)ppdispParent))) {
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
@ -1590,13 +1558,13 @@ nsAccessibleWrap::FirePlatformEvent(AccEvent* aEvent)
|
||||
#endif
|
||||
|
||||
// Fire MSAA event for client area window.
|
||||
NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
|
||||
::NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
|
||||
|
||||
// JAWS announces collapsed combobox navigation based on focus events.
|
||||
if (Compatibility::IsJAWS()) {
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SELECTION &&
|
||||
accessible->Role() == roles::COMBOBOX_OPTION) {
|
||||
NotifyWinEvent(EVENT_OBJECT_FOCUS, hWnd, OBJID_CLIENT, childID);
|
||||
::NotifyWinEvent(EVENT_OBJECT_FOCUS, hWnd, OBJID_CLIENT, childID);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1729,8 +1697,9 @@ IDispatch *nsAccessibleWrap::NativeAccessible(nsIAccessible *aXPAccessible)
|
||||
accObject->GetHwnd(&hwnd);
|
||||
if (hwnd) {
|
||||
IDispatch *retval = nsnull;
|
||||
AccessibleObjectFromWindow(reinterpret_cast<HWND>(hwnd),
|
||||
OBJID_WINDOW, IID_IAccessible, (void **) &retval);
|
||||
::AccessibleObjectFromWindow(reinterpret_cast<HWND>(hwnd),
|
||||
OBJID_WINDOW, IID_IAccessible,
|
||||
(void **) &retval);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
@ -331,11 +331,6 @@ public: // construction, destruction
|
||||
|
||||
NS_IMETHOD GetNativeInterface(void **aOutAccessible);
|
||||
|
||||
// NT4 does not have the oleacc that defines these methods. So we define copies here that automatically
|
||||
// load the library only if needed.
|
||||
static STDMETHODIMP AccessibleObjectFromWindow(HWND hwnd,DWORD dwObjectID,REFIID riid,void **ppvObject);
|
||||
static STDMETHODIMP NotifyWinEvent(DWORD event,HWND hwnd,LONG idObjectType,LONG idObject);
|
||||
|
||||
static IDispatch *NativeAccessible(nsIAccessible *aXPAccessible);
|
||||
|
||||
/**
|
||||
|
@ -131,6 +131,7 @@ static const PRUint32 gWinEventMap[] = {
|
||||
IA2_EVENT_HYPERTEXT_CHANGED, // nsIAccessibleEvent::EVENT_HYPERTEXT_CHANGED
|
||||
IA2_EVENT_HYPERTEXT_NLINKS_CHANGED, // nsIAccessibleEvent::EVENT_HYPERTEXT_NLINKS_CHANGED
|
||||
IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED, // nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
|
||||
kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED
|
||||
kEVENT_LAST_ENTRY // nsIAccessibleEvent::EVENT_LAST_ENTRY
|
||||
};
|
||||
|
||||
|
@ -52,6 +52,7 @@ DIRS = \
|
||||
hyperlink \
|
||||
hypertext \
|
||||
name \
|
||||
pivot \
|
||||
relations \
|
||||
selectable \
|
||||
states \
|
||||
@ -81,6 +82,7 @@ _TEST_FILES =\
|
||||
grid.js \
|
||||
layout.js \
|
||||
name.js \
|
||||
pivot.js \
|
||||
relations.js \
|
||||
role.js \
|
||||
selectable.js \
|
||||
|
@ -30,10 +30,13 @@ const nsIAccessibleEditableText = Components.interfaces.nsIAccessibleEditableTex
|
||||
const nsIAccessibleHyperLink = Components.interfaces.nsIAccessibleHyperLink;
|
||||
const nsIAccessibleHyperText = Components.interfaces.nsIAccessibleHyperText;
|
||||
|
||||
const nsIAccessibleCursorable = Components.interfaces.nsIAccessibleCursorable;
|
||||
const nsIAccessibleImage = Components.interfaces.nsIAccessibleImage;
|
||||
const nsIAccessiblePivot = Components.interfaces.nsIAccessiblePivot;
|
||||
const nsIAccessibleSelectable = Components.interfaces.nsIAccessibleSelectable;
|
||||
const nsIAccessibleTable = Components.interfaces.nsIAccessibleTable;
|
||||
const nsIAccessibleTableCell = Components.interfaces.nsIAccessibleTableCell;
|
||||
const nsIAccessibleTraversalRule = Components.interfaces.nsIAccessibleTraversalRule;
|
||||
const nsIAccessibleValue = Components.interfaces.nsIAccessibleValue;
|
||||
|
||||
const nsIObserverService = Components.interfaces.nsIObserverService;
|
||||
|
@ -27,6 +27,7 @@ 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;
|
||||
const EVENT_VIRTUALCURSOR_CHANGED = nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// General
|
||||
|
217
accessible/tests/mochitest/pivot.js
Normal file
217
accessible/tests/mochitest/pivot.js
Normal file
@ -0,0 +1,217 @@
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Constants
|
||||
|
||||
const PREFILTER_INVISIBLE = nsIAccessibleTraversalRule.PREFILTER_INVISIBLE;
|
||||
const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
|
||||
const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Traversal rules
|
||||
|
||||
/**
|
||||
* Rule object to traverse all focusable nodes and text nodes.
|
||||
*/
|
||||
var HeadersTraversalRule =
|
||||
{
|
||||
getMatchRoles: function(aRules)
|
||||
{
|
||||
aRules.value = [ROLE_HEADING];
|
||||
return aRules.value.length;
|
||||
},
|
||||
|
||||
preFilter: PREFILTER_INVISIBLE,
|
||||
|
||||
match: function(aAccessible)
|
||||
{
|
||||
return FILTER_MATCH;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
|
||||
}
|
||||
|
||||
/**
|
||||
* Traversal rule for all focusable nodes or leafs.
|
||||
*/
|
||||
var ObjectTraversalRule =
|
||||
{
|
||||
getMatchRoles: function(aRules)
|
||||
{
|
||||
aRules.value = [];
|
||||
return 0;
|
||||
},
|
||||
|
||||
preFilter: PREFILTER_INVISIBLE,
|
||||
|
||||
match: function(aAccessible)
|
||||
{
|
||||
var rv = FILTER_IGNORE;
|
||||
var role = aAccessible.role;
|
||||
if (hasState(aAccessible, STATE_FOCUSABLE) &&
|
||||
(role != ROLE_DOCUMENT && role != ROLE_INTERNAL_FRAME))
|
||||
rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
|
||||
else if (aAccessible.childCount == 0 &&
|
||||
role != ROLE_STATICTEXT && aAccessible.name.trim())
|
||||
rv = FILTER_MATCH;
|
||||
|
||||
return rv;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Virtual state invokers and checkers
|
||||
|
||||
/**
|
||||
* A checker for virtual cursor changed events.
|
||||
*/
|
||||
function virtualCursorChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets)
|
||||
{
|
||||
this.__proto__ = new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc);
|
||||
|
||||
this.check = function virtualCursorChangedChecker_check(aEvent)
|
||||
{
|
||||
var position = aDocAcc.virtualCursor.position;
|
||||
position.QueryInterface(nsIAccessNode);
|
||||
|
||||
var idMatches = position.DOMNode.id == aIdOrNameOrAcc;
|
||||
var nameMatches = position.name == aIdOrNameOrAcc;
|
||||
var accMatches = position == aIdOrNameOrAcc;
|
||||
|
||||
SimpleTest.ok(idMatches || nameMatches || accMatches, "id or name matches",
|
||||
"expecting " + aIdOrNameOrAcc + ", got '" +
|
||||
prettyName(position));
|
||||
|
||||
if (aTextOffsets) {
|
||||
SimpleTest.is(aDocAcc.virtualCursor.startOffset, aTextOffsets[0],
|
||||
"wrong start offset");
|
||||
SimpleTest.is(aDocAcc.virtualCursor.endOffset, aTextOffsets[1],
|
||||
"wrong end offset");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a text range in the pivot and wait for virtual cursor change event.
|
||||
*
|
||||
* @param aDocAcc document that manages the virtual cursor
|
||||
* @param aTextAccessible accessible to set to virtual cursor's position
|
||||
* @param aTextOffsets start and end offsets of text range to set in virtual
|
||||
* cursor
|
||||
*/
|
||||
function setVirtualCursorRangeInvoker(aDocAcc, aTextAccessible, aTextOffsets)
|
||||
{
|
||||
this.invoke = function virtualCursorChangedInvoker_invoke()
|
||||
{
|
||||
SimpleTest.info(prettyName(aTextAccessible) + " " + aTextOffsets);
|
||||
aDocAcc.virtualCursor.setTextRange(aTextAccessible,
|
||||
aTextOffsets[0],
|
||||
aTextOffsets[1]);
|
||||
};
|
||||
|
||||
this.getID = function setVirtualCursorRangeInvoker_getID()
|
||||
{
|
||||
return "Set offset in " + prettyName(aTextAccessible) +
|
||||
" to (" + aTextOffsets[0] + ", " + aTextOffsets[1] + ")";
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new virtualCursorChangedChecker(aDocAcc, aTextAccessible, aTextOffsets)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the pivot and wait for virtual cursor change event.
|
||||
*
|
||||
* @param aDocAcc document that manages the virtual cursor
|
||||
* @param aPivotMoveMethod method to test (ie. "moveNext", "moveFirst", etc.)
|
||||
* @param aRule traversal rule object
|
||||
* @param aIdOrNameOrAcc id, accessivle or accessible name to expect virtual
|
||||
* cursor to land on after performing move method.
|
||||
*/
|
||||
function setVirtualCursorPosInvoker(aDocAcc, aPivotMoveMethod, aRule,
|
||||
aIdOrNameOrAcc)
|
||||
{
|
||||
this.invoke = function virtualCursorChangedInvoker_invoke()
|
||||
{
|
||||
var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule);
|
||||
SimpleTest.ok((aIdOrNameOrAcc && moved) || (!aIdOrNameOrAcc && !moved),
|
||||
"moved pivot");
|
||||
};
|
||||
|
||||
this.getID = function setVirtualCursorPosInvoker_getID()
|
||||
{
|
||||
return "Do " + (aIdOrNameOrAcc ? "" : "no-op ") + aPivotMoveMethod;
|
||||
}
|
||||
|
||||
if (aIdOrNameOrAcc) {
|
||||
this.eventSeq = [ new virtualCursorChangedChecker(aDocAcc, aIdOrNameOrAcc) ];
|
||||
} else {
|
||||
this.eventSeq = [];
|
||||
this.unexpectedEventSeq = [
|
||||
new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add invokers to a queue to test a rule and an expected sequence of element ids
|
||||
* or accessible names for that rule in the given document.
|
||||
*
|
||||
* @param aQueue event queue in which to push invoker sequence.
|
||||
* @param aDocAcc the managing document of the virtual cursor we are testing
|
||||
* @param aRule the traversal rule to use in the invokers
|
||||
* @param aSequence a sequence of accessible names or elemnt ids to expect with
|
||||
* the given rule in the given document
|
||||
*/
|
||||
function queueTraversalSequence(aQueue, aDocAcc, aRule, aSequence)
|
||||
{
|
||||
aDocAcc.virtualCursor.position = null;
|
||||
|
||||
for (var i = 0; i < aSequence.length; i++) {
|
||||
var invoker = new setVirtualCursorPosInvoker(aDocAcc, "moveNext",
|
||||
aRule, aSequence[i]);
|
||||
aQueue.push(invoker);
|
||||
}
|
||||
|
||||
// No further more matches for given rule, expect no virtual cursor changes.
|
||||
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "moveNext", aRule, null));
|
||||
|
||||
for (var i = aSequence.length-2; i >= 0; i--) {
|
||||
var invoker = new setVirtualCursorPosInvoker(aDocAcc, "movePrevious",
|
||||
aRule, aSequence[i])
|
||||
aQueue.push(invoker);
|
||||
}
|
||||
|
||||
// No previous more matches for given rule, expect no virtual cursor changes.
|
||||
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "movePrevious", aRule, null));
|
||||
|
||||
aQueue.push(new setVirtualCursorPosInvoker(
|
||||
aDocAcc, "moveLast", aRule, aSequence[aSequence.length - 1]));
|
||||
|
||||
// No further more matches for given rule, expect no virtual cursor changes.
|
||||
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "moveNext", aRule, null));
|
||||
|
||||
aQueue.push(new setVirtualCursorPosInvoker(
|
||||
aDocAcc, "moveFirst", aRule, aSequence[0]));
|
||||
|
||||
// No previous more matches for given rule, expect no virtual cursor changes.
|
||||
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "movePrevious", aRule, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* A debug utility for writing proper sequences for queueTraversalSequence.
|
||||
*/
|
||||
function dumpTraversalSequence(aPivot, aRule)
|
||||
{
|
||||
var sequence = []
|
||||
if (aPivot.moveFirst(aRule)) {
|
||||
do {
|
||||
sequence.push("'" + prettyName(aPivot.position) + "'");
|
||||
} while (aPivot.moveNext(aRule))
|
||||
}
|
||||
SimpleTest.info("\n[" + sequence.join(", ") + "]\n");
|
||||
}
|
54
accessible/tests/mochitest/pivot/Makefile.in
Normal file
54
accessible/tests/mochitest/pivot/Makefile.in
Normal file
@ -0,0 +1,54 @@
|
||||
#
|
||||
# ***** 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) 2010
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Eitan Isaacson <eitan@monotonous.org> (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/pivot
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES = \
|
||||
doc_virtualcursor.html \
|
||||
test_virtualcursor.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
|
26
accessible/tests/mochitest/pivot/doc_virtualcursor.html
Normal file
26
accessible/tests/mochitest/pivot/doc_virtualcursor.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Pivot test document</title>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="heading-1-1">Main Title</h1>
|
||||
<h2 id="heading-2-1">First Section Title</h2>
|
||||
<p id="paragraph-1">
|
||||
Lorem ipsum <strong>dolor</strong> sit amet. Integer vitae urna
|
||||
leo, id <a href="#">semper</a> nulla.
|
||||
</p>
|
||||
<h2 id="heading-2-2">Second Section Title</h2>
|
||||
<p id="paragraph-2">
|
||||
Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
|
||||
<iframe
|
||||
src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
|
||||
</iframe>
|
||||
<p>
|
||||
<a href="http://mozilla.org" title="Link 1 title">Link 1</a>
|
||||
<a href="http://mozilla.org" title="Link 2 title">Link 2</a>
|
||||
<a href="http://mozilla.org" title="Link 3 title">Link 3</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
95
accessible/tests/mochitest/pivot/test_virtualcursor.html
Normal file
95
accessible/tests/mochitest/pivot/test_virtualcursor.html
Normal file
@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tests pivot functionality in virtual cursors</title>
|
||||
<meta charset="utf-8" />
|
||||
<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="../browser.js"></script>
|
||||
<script type="application/javascript" src="../events.js"></script>
|
||||
<script type="application/javascript" src="../role.js"></script>
|
||||
<script type="application/javascript" src="../states.js"></script>
|
||||
<script type="application/javascript" src="../pivot.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
var gBrowserWnd = null;
|
||||
var gQueue = null;
|
||||
|
||||
function doTest()
|
||||
{
|
||||
var rootAcc = getRootAccessible(browserWindow().document);
|
||||
try {
|
||||
rootAcc.QueryInterface(nsIAccessibleCursorable);
|
||||
} catch (e) {
|
||||
ok(false, "Root accessible does not support nsIAccessibleCursorable");
|
||||
}
|
||||
var doc = currentTabDocument();
|
||||
var docAcc = getAccessible(doc, [nsIAccessibleDocument,
|
||||
nsIAccessibleCursorable]);
|
||||
|
||||
// Test that embedded documents don't have their own virtual cursor.
|
||||
is(docAcc.childDocumentCount, 1, "Expecting one child document");
|
||||
var childDoc = docAcc.getChildDocumentAt(0);
|
||||
var supportsVC = true;
|
||||
try {
|
||||
childDoc.QueryInterface(nsIAccessibleCursorable);
|
||||
} catch (e) {
|
||||
supportsVC = false;
|
||||
}
|
||||
|
||||
ok(!supportsVC, "no nsIAccessibleCursorable support in child document");
|
||||
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.onFinish = function onFinish()
|
||||
{
|
||||
closeBrowserWindow();
|
||||
}
|
||||
|
||||
queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule,
|
||||
['heading-1-1', 'heading-2-1', 'heading-2-2']);
|
||||
|
||||
queueTraversalSequence(
|
||||
gQueue, docAcc, ObjectTraversalRule,
|
||||
['Main Title', 'First Section Title', 'Lorem ipsum ',
|
||||
'dolor', ' sit amet. Integer vitae urna leo, id ',
|
||||
'semper', ' nulla. ', 'Second Section Title',
|
||||
'Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.',
|
||||
'An ', 'embedded', ' document.', 'Link 1', 'Link 2', 'Link 3']);
|
||||
|
||||
// Just a random smoke test to see if our setTextRange works.
|
||||
gQueue.push(
|
||||
new setVirtualCursorRangeInvoker(
|
||||
docAcc,
|
||||
getAccessible(doc.getElementById('paragraph-2'), nsIAccessibleText),
|
||||
[2,6]));
|
||||
|
||||
gQueue.invoke();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function () {
|
||||
/* We open a new browser because we need to test with a top-level content
|
||||
document. */
|
||||
openBrowserWindow(
|
||||
doTest,
|
||||
"chrome://mochitests/content/a11y/accessible/pivot/doc_virtualcursor.html");
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body id="body">
|
||||
|
||||
<a target="_blank"
|
||||
title="Introduce virtual cursor/soft focus functionality to a11y API"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=698823">Mozilla Bug 698823</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -421,3 +421,6 @@ pref("b2g.ignoreXFrameOptions", true);
|
||||
// secondary bug isn't really worth investigating since it's obseleted
|
||||
// by bug 710563.
|
||||
pref("layout.frame_rate.precise", true);
|
||||
|
||||
// Screen timeout in minutes
|
||||
pref("power.screen.timeout", 60);
|
||||
|
@ -57,6 +57,10 @@ 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
|
||||
@ -286,7 +290,20 @@ var shell = {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
(function PowerManager() {
|
||||
let idleHandler = {
|
||||
observe: function(subject, topic, time) {
|
||||
if (topic === "idle") {
|
||||
// TODO: Check wakelock status. See bug 697132.
|
||||
shell.turnScreenOff();
|
||||
}
|
||||
},
|
||||
}
|
||||
let idleTimeout = Services.prefs.getIntPref("power.screen.timeout");
|
||||
if (idleTimeout) {
|
||||
Services.idle.addIdleObserver(idleHandler, idleTimeout);
|
||||
}
|
||||
})();
|
||||
|
||||
function nsBrowserAccess() {
|
||||
}
|
||||
|
@ -5,7 +5,9 @@
|
||||
// - https://github.com/mozilla/gcli/blob/master/docs/index.md
|
||||
// - https://wiki.mozilla.org/DevTools/Features/GCLI
|
||||
|
||||
Components.utils.import("resource:///modules/gcli.jsm");
|
||||
let tmp = {};
|
||||
Components.utils.import("resource:///modules/gcli.jsm", tmp);
|
||||
let gcli = tmp.gcli;
|
||||
|
||||
let hud;
|
||||
let gcliterm;
|
||||
|
@ -43,7 +43,7 @@
|
||||
!define MinSupportedVer "Microsoft Windows Vista x64"
|
||||
#else
|
||||
!define ARCH "x86"
|
||||
!define MinSupportedVer "Microsoft Windows 2000"
|
||||
!define MinSupportedVer "Microsoft Windows XP SP2"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_MAINTENANCE_SERVICE
|
||||
|
@ -69,6 +69,9 @@ public interface Driver {
|
||||
void startFrameRecording();
|
||||
int stopFrameRecording();
|
||||
|
||||
void startCheckerboardRecording();
|
||||
int stopCheckerboardRecording();
|
||||
|
||||
/**
|
||||
* Get a copy of the painted content region.
|
||||
* @return A 2-D array of pixels (indexed by y, then x). The pixels
|
||||
|
@ -15,12 +15,10 @@
|
||||
* The Original Code is Firefox Mobile Test Framework.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Trevor Fairey <tnfairey@gmail.com>
|
||||
* David Burns <dburns@mozilla.com>
|
||||
* Joel Maher <joel.maher@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
@ -49,16 +47,13 @@ import java.util.Date;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class FennecNativeAssert implements Assert {
|
||||
public class FennecMochitestAssert implements Assert {
|
||||
private String logFile = null;
|
||||
|
||||
// Objects for reflexive access of fennec classes.
|
||||
|
||||
private LinkedList<testInfo> testList = new LinkedList<testInfo>();
|
||||
|
||||
// If waiting for an event.
|
||||
private boolean asleep = false;
|
||||
|
||||
// Internal state variables to make logging match up with existing mochitests
|
||||
private int lineNumber = 0;
|
||||
private int passed = 0;
|
||||
@ -74,12 +69,10 @@ public class FennecNativeAssert implements Assert {
|
||||
// Measure the time it takes to run test case
|
||||
private long startTime = 0;
|
||||
|
||||
public FennecNativeAssert() {
|
||||
public FennecMochitestAssert() {
|
||||
}
|
||||
|
||||
// Write information to a logfile and logcat
|
||||
public void dumpLog(String message)
|
||||
{
|
||||
public void dumpLog(String message) {
|
||||
File file = new File(logFile);
|
||||
BufferedWriter bw = null;
|
||||
|
||||
@ -108,17 +101,15 @@ public class FennecNativeAssert implements Assert {
|
||||
{
|
||||
logFile = filename;
|
||||
|
||||
//TODO: these two messages are mochitest specific
|
||||
String message;
|
||||
if (!logStarted) {
|
||||
message = Integer.toString(lineNumber++) + " INFO SimpleTest START";
|
||||
dumpLog(message);
|
||||
dumpLog(Integer.toString(lineNumber++) + " INFO SimpleTest START");
|
||||
logStarted = true;
|
||||
}
|
||||
|
||||
if (logTestName != "") {
|
||||
message = Integer.toString(lineNumber++) + " INFO TEST-END | " + logTestName;
|
||||
long diff = (new Date().getTime()) - startTime;
|
||||
message = Integer.toString(lineNumber++) + " INFO TEST-END | " + logTestName;
|
||||
message += " | finished in " + diff + "ms";
|
||||
dumpLog(message);
|
||||
logTestName = "";
|
||||
@ -131,9 +122,7 @@ public class FennecNativeAssert implements Assert {
|
||||
logTestName = nameParts[nameParts.length - 1];
|
||||
startTime = new Date().getTime();
|
||||
|
||||
//TODO: this is mochitest specific
|
||||
String message = Integer.toString(lineNumber++) + " INFO TEST-START | " + logTestName;
|
||||
dumpLog(message);
|
||||
dumpLog(Integer.toString(lineNumber++) + " INFO TEST-START | " + logTestName);
|
||||
}
|
||||
|
||||
class testInfo {
|
||||
@ -197,16 +186,12 @@ public class FennecNativeAssert implements Assert {
|
||||
|
||||
if (logTestName != "") {
|
||||
long diff = (new Date().getTime()) - startTime;
|
||||
|
||||
//TODO: this is mochitest specific
|
||||
message = Integer.toString(lineNumber++) + " INFO TEST-END | " + logTestName;
|
||||
message += " | finished in " + diff + "ms";
|
||||
dumpLog(message);
|
||||
|
||||
logTestName = "";
|
||||
}
|
||||
|
||||
//TODO: this is all mochitest specific
|
||||
message = Integer.toString(lineNumber++) + " INFO TEST-START | Shutdown";
|
||||
dumpLog(message);
|
||||
message = Integer.toString(lineNumber++) + " INFO Passed: " + Integer.toString(passed);
|
@ -83,6 +83,8 @@ public class FennecNativeDriver implements Driver {
|
||||
private Method sendGE;
|
||||
private Method _startFrameRecording;
|
||||
private Method _stopFrameRecording;
|
||||
private Method _startCheckerboardRecording;
|
||||
private Method _stopCheckerboardRecording;
|
||||
private Method _getPixels;
|
||||
|
||||
public FennecNativeDriver(Activity activity, Solo robocop){
|
||||
@ -110,6 +112,8 @@ public class FennecNativeDriver implements Driver {
|
||||
Class gfx = classLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI");
|
||||
_startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording");
|
||||
_stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording");
|
||||
_startCheckerboardRecording = gfx.getDeclaredMethod("startCheckerboardRecording");
|
||||
_stopCheckerboardRecording = gfx.getDeclaredMethod("stopCheckerboardRecording");
|
||||
|
||||
Class layerView = classLoader.loadClass("org.mozilla.gecko.gfx.LayerView");
|
||||
_getPixels = layerView.getDeclaredMethod("getPixels");
|
||||
@ -222,6 +226,42 @@ public class FennecNativeDriver implements Driver {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void startCheckerboardRecording() {
|
||||
try {
|
||||
Object [] params = null;
|
||||
_startCheckerboardRecording.invoke(null, params);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public int stopCheckerboardRecording() {
|
||||
Class [] parameters = new Class[1];
|
||||
parameters[0] = null;
|
||||
List checkerboard;
|
||||
|
||||
try {
|
||||
Object [] params = null;
|
||||
checkerboard = (List)_stopCheckerboardRecording.invoke(null, params);
|
||||
Object [] amountarray = checkerboard.toArray();
|
||||
int numIncompleteFrames = 0;
|
||||
for (int i=0; i < amountarray.length; i++) {
|
||||
Float val = (Float)amountarray[i];
|
||||
if (val > 0.0f)
|
||||
numIncompleteFrames++;
|
||||
}
|
||||
return numIncompleteFrames;
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private GLSurfaceView getSurfaceView() {
|
||||
for (View v : solo.getCurrentViews()) {
|
||||
if (v instanceof GLSurfaceView) {
|
||||
|
115
build/mobile/robocop/FennecTalosAssert.java.in
Normal file
115
build/mobile/robocop/FennecTalosAssert.java.in
Normal file
@ -0,0 +1,115 @@
|
||||
#filter substitution
|
||||
/* ***** 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 Firefox Mobile Test Framework.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Joel Maher <joel.maher@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* 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 ***** */
|
||||
|
||||
package @ANDROID_PACKAGE_NAME@;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class FennecTalosAssert implements Assert {
|
||||
private String logFile = null;
|
||||
|
||||
public FennecTalosAssert() {
|
||||
}
|
||||
|
||||
public void dumpLog(String message) {
|
||||
File file = new File(logFile);
|
||||
BufferedWriter bw = null;
|
||||
|
||||
try {
|
||||
bw = new BufferedWriter(new FileWriter(logFile, true));
|
||||
bw.write(message);
|
||||
bw.newLine();
|
||||
} catch(IOException e) {
|
||||
Log.e("Robocop", "exception with file writer on: " + logFile);
|
||||
} finally {
|
||||
try {
|
||||
if (bw != null) {
|
||||
bw.flush();
|
||||
bw.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Log.i("Robocop", message);
|
||||
}
|
||||
|
||||
// Set the filename used for dumpLog.
|
||||
public void setLogFile(String filename)
|
||||
{
|
||||
logFile = filename;
|
||||
}
|
||||
|
||||
public void setTestName(String testName)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public void finalize()
|
||||
{
|
||||
}
|
||||
|
||||
public void ok(boolean condition, String name, String diag) {
|
||||
}
|
||||
|
||||
public void is(Object a, Object b, String name) {
|
||||
}
|
||||
|
||||
public void isnot(Object a, Object b, String name) {
|
||||
}
|
||||
|
||||
public void ispixel(int actual, int r, int g, int b, String name) {
|
||||
}
|
||||
|
||||
public void todo(boolean condition, String name, String diag) {
|
||||
}
|
||||
|
||||
public void todo_is(Object a, Object b, String name) {
|
||||
}
|
||||
|
||||
public void todo_isnot(Object a, Object b, String name) {
|
||||
}
|
||||
|
||||
public void info(String name, String message) {
|
||||
}
|
||||
}
|
@ -56,7 +56,8 @@ _JAVA_HARNESS = \
|
||||
Driver.java \
|
||||
Element.java \
|
||||
FennecNativeActions.java \
|
||||
FennecNativeAssert.java \
|
||||
FennecMochitestAssert.java \
|
||||
FennecTalosAssert.java \
|
||||
FennecNativeDriver.java \
|
||||
FennecNativeElement.java \
|
||||
RoboCopException.java \
|
||||
|
@ -4616,6 +4616,7 @@ MOZ_SAFE_BROWSING=
|
||||
MOZ_HELP_VIEWER=
|
||||
MOZ_SPELLCHECK=1
|
||||
MOZ_JAVA_COMPOSITOR=
|
||||
MOZ_ONLY_TOUCH_EVENTS=
|
||||
MOZ_SVG_DLISTS=
|
||||
MOZ_TOOLKIT_SEARCH=1
|
||||
MOZ_UI_LOCALE=en-US
|
||||
@ -8463,6 +8464,7 @@ AC_SUBST(MOZ_UNIVERSALCHARDET)
|
||||
AC_SUBST(ACCESSIBILITY)
|
||||
AC_SUBST(MOZ_SPELLCHECK)
|
||||
AC_SUBST(MOZ_JAVA_COMPOSITOR)
|
||||
AC_SUBST(MOZ_ONLY_TOUCH_EVENTS)
|
||||
AC_SUBST(MOZ_USER_DIR)
|
||||
AC_SUBST(MOZ_CRASHREPORTER)
|
||||
AC_SUBST(MOZ_MAINTENANCE_SERVICE)
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "jspubtd.h"
|
||||
#include "nsDOMMemoryReporter.h"
|
||||
#include "nsIVariant.h"
|
||||
#include "nsGkAtoms.h"
|
||||
|
||||
// Including 'windows.h' will #define GetClassInfo to something else.
|
||||
#ifdef XP_WIN
|
||||
@ -994,6 +995,22 @@ public:
|
||||
*/
|
||||
virtual already_AddRefed<nsIURI> GetBaseURI() const = 0;
|
||||
|
||||
/**
|
||||
* Facility for explicitly setting a base URI on a node.
|
||||
*/
|
||||
nsresult SetExplicitBaseURI(nsIURI* aURI);
|
||||
/**
|
||||
* The explicit base URI, if set, otherwise null
|
||||
*/
|
||||
protected:
|
||||
nsIURI* GetExplicitBaseURI() const {
|
||||
if (HasExplicitBaseURI()) {
|
||||
return static_cast<nsIURI*>(GetProperty(nsGkAtoms::baseURIProperty));
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
public:
|
||||
nsresult GetDOMBaseURI(nsAString &aURI) const;
|
||||
|
||||
// Note! This function must never fail. It only return an nsresult so that
|
||||
@ -1230,6 +1247,8 @@ private:
|
||||
// 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,
|
||||
// Guard value
|
||||
BooleanFlagCount
|
||||
};
|
||||
@ -1300,6 +1319,8 @@ protected:
|
||||
void ClearHasName() { ClearBoolFlag(ElementHasName); }
|
||||
void SetMayHaveContentEditableAttr()
|
||||
{ SetBoolFlag(ElementMayHaveContentEditableAttr); }
|
||||
bool HasExplicitBaseURI() const { return GetBoolFlag(NodeHasExplicitBaseURI); }
|
||||
void SetHasExplicitBaseURI() { SetBoolFlag(NodeHasExplicitBaseURI); }
|
||||
|
||||
public:
|
||||
// Optimized way to get classinfo.
|
||||
|
@ -2458,25 +2458,41 @@ nsDocument::InitCSP()
|
||||
// regular-strength CSP.
|
||||
if (cspHeaderValue.IsEmpty()) {
|
||||
mCSP->SetReportOnlyMode(true);
|
||||
mCSP->RefinePolicy(cspROHeaderValue, chanURI);
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSP (report only) refined, policy: \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(cspROHeaderValue).get()));
|
||||
}
|
||||
|
||||
// Need to tokenize the header value since multiple headers could be
|
||||
// concatenated into one comma-separated list of policies.
|
||||
// See RFC2616 section 4.2 (last paragraph)
|
||||
nsCharSeparatedTokenizer tokenizer(cspROHeaderValue, ',');
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsSubstring& policy = tokenizer.nextToken();
|
||||
mCSP->RefinePolicy(policy, chanURI);
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSP (report only) refined with policy: \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
//XXX(sstamm): maybe we should post a warning when both read only and regular
|
||||
// CSP headers are present.
|
||||
mCSP->RefinePolicy(cspHeaderValue, chanURI);
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSP refined, policy: \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(cspHeaderValue).get()));
|
||||
}
|
||||
|
||||
// Need to tokenize the header value since multiple headers could be
|
||||
// concatenated into one comma-separated list of policies.
|
||||
// See RFC2616 section 4.2 (last paragraph)
|
||||
nsCharSeparatedTokenizer tokenizer(cspHeaderValue, ',');
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsSubstring& policy = tokenizer.nextToken();
|
||||
mCSP->RefinePolicy(policy, chanURI);
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSP refined with policy: \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Check for frame-ancestor violation
|
||||
|
@ -1537,6 +1537,12 @@ nsIContent::GetBaseURI() const
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsIURI* explicitBaseURI = elem->GetExplicitBaseURI();
|
||||
if (explicitBaseURI) {
|
||||
base = explicitBaseURI;
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise check for xml:base attribute
|
||||
elem->GetAttr(kNameSpaceID_XML, nsGkAtoms::base, attr);
|
||||
@ -1566,6 +1572,27 @@ nsIContent::GetBaseURI() const
|
||||
return base.forget();
|
||||
}
|
||||
|
||||
static void
|
||||
ReleaseURI(void*, /* aObject*/
|
||||
nsIAtom*, /* aPropertyName */
|
||||
void* aPropertyValue,
|
||||
void* /* aData */)
|
||||
{
|
||||
nsIURI* uri = static_cast<nsIURI*>(aPropertyValue);
|
||||
NS_RELEASE(uri);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsINode::SetExplicitBaseURI(nsIURI* aURI)
|
||||
{
|
||||
nsresult rv = SetProperty(nsGkAtoms::baseURIProperty, aURI, ReleaseURI);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
SetHasExplicitBaseURI();
|
||||
NS_ADDREF(aURI);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsChildContentList)
|
||||
|
@ -1851,6 +1851,7 @@ GK_ATOM(transitionsOfBeforeProperty, "TransitionsOfBeforeProperty") // FrameTran
|
||||
GK_ATOM(transitionsOfAfterProperty, "TransitionsOfAfterProperty") // FrameTransitions*
|
||||
GK_ATOM(genConInitializerProperty, "QuoteNodeProperty")
|
||||
GK_ATOM(labelMouseDownPtProperty, "LabelMouseDownPtProperty")
|
||||
GK_ATOM(baseURIProperty, "baseURIProperty")
|
||||
|
||||
// Languages for lang-specific transforms
|
||||
GK_ATOM(Japanese, "ja")
|
||||
|
@ -547,6 +547,11 @@ _TEST_FILES2 = \
|
||||
test_XHR_timeout.html \
|
||||
test_XHR_timeout.js \
|
||||
file_XHR_timeout.sjs \
|
||||
test_bug717511.html \
|
||||
file_bug717511.html \
|
||||
file_bug717511.html^headers^ \
|
||||
file_bug717511_2.html \
|
||||
file_bug717511_2.html^headers^ \
|
||||
$(NULL)
|
||||
|
||||
_CHROME_FILES = \
|
||||
|
12
content/base/test/file_bug717511.html
Normal file
12
content/base/test/file_bug717511.html
Normal file
@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<body>
|
||||
<!-- these should be stopped by CSP after fixing bug 717511. :) -->
|
||||
<img src="http://example.org/tests/content/base/test/file_CSP.sjs?testid=img_bad&type=img/png"> </img>
|
||||
<script src='http://example.org/tests/content/base/test/file_CSP.sjs?testid=script_bad&type=text/javascript'></script>
|
||||
|
||||
<!-- these should load ok after fixing bug 717511. :) -->
|
||||
<img src="file_CSP.sjs?testid=img_good&type=img/png" />
|
||||
<script src='file_CSP.sjs?testid=script_good&type=text/javascript'></script>
|
||||
|
||||
</body>
|
||||
</html>
|
1
content/base/test/file_bug717511.html^headers^
Normal file
1
content/base/test/file_bug717511.html^headers^
Normal file
@ -0,0 +1 @@
|
||||
X-Content-Security-Policy: default-src 'self', allow *
|
12
content/base/test/file_bug717511_2.html
Normal file
12
content/base/test/file_bug717511_2.html
Normal file
@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<body>
|
||||
<!-- these should be stopped by CSP after fixing bug 717511. :) -->
|
||||
<img src="http://example.org/tests/content/base/test/file_CSP.sjs?testid=img2_bad&type=img/png"> </img>
|
||||
<script src='http://example.org/tests/content/base/test/file_CSP.sjs?testid=script2_bad&type=text/javascript'></script>
|
||||
|
||||
<!-- these should load ok after fixing bug 717511. :) -->
|
||||
<img src="file_CSP.sjs?testid=img2_good&type=img/png" />
|
||||
<script src='file_CSP.sjs?testid=script2_good&type=text/javascript'></script>
|
||||
|
||||
</body>
|
||||
</html>
|
1
content/base/test/file_bug717511_2.html^headers^
Normal file
1
content/base/test/file_bug717511_2.html^headers^
Normal file
@ -0,0 +1 @@
|
||||
X-Content-Security-Policy: default-src 'self' , allow *
|
124
content/base/test/test_bug717511.html
Normal file
124
content/base/test/test_bug717511.html
Normal file
@ -0,0 +1,124 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=717511
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 717511</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
|
||||
<iframe style="width:200px;height:200px;" id='cspframe2'></iframe>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var path = "/tests/content/base/test/";
|
||||
|
||||
// These are test results: -1 means it hasn't run,
|
||||
// true/false is the pass/fail result.
|
||||
// This is not exhaustive, just double-checking the 'self' vs * policy conflict in the two HTTP headers.
|
||||
window.tests = {
|
||||
img_good: -1,
|
||||
img_bad: -1,
|
||||
script_good: -1,
|
||||
script_bad: -1,
|
||||
img2_good: -1,
|
||||
img2_bad: -1,
|
||||
script2_good: -1,
|
||||
script2_bad: -1,
|
||||
};
|
||||
|
||||
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsIHttpChannel (for http-on-modify-request)
|
||||
// and nsIURI for csp-on-violate-policy, and the Request should be
|
||||
//either allowed or blocked.
|
||||
if(!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
var testpat = new RegExp("testid=([a-z0-9_]+)");
|
||||
|
||||
//_good things better be allowed!
|
||||
//_bad things better be stopped!
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
//these things were allowed by CSP
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(
|
||||
SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"),
|
||||
"URI.asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
window.testResult(testid,
|
||||
/_good/.test(testid),
|
||||
asciiSpec + " allowed by csp");
|
||||
|
||||
}
|
||||
|
||||
if(topic === "csp-on-violate-policy") {
|
||||
//these were blocked... record that they were blocked
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(
|
||||
SpecialPowers.do_QueryInterface(subject, "nsIURI"),
|
||||
"asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
window.testResult(testid,
|
||||
/_bad/.test(testid),
|
||||
asciiSpec + " blocked by \"" + data + "\"");
|
||||
}
|
||||
},
|
||||
|
||||
// must eventually call this to remove the listener,
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
}
|
||||
}
|
||||
|
||||
window.examiner = new examiner();
|
||||
|
||||
window.testResult = function(testname, result, msg) {
|
||||
|
||||
//test already complete.... forget it... remember the first result.
|
||||
if (window.tests[testname] != -1)
|
||||
return;
|
||||
|
||||
window.tests[testname] = result;
|
||||
is(result, true, testname + ' test: ' + msg);
|
||||
|
||||
// if any test is incomplete, keep waiting
|
||||
for (var v in window.tests)
|
||||
if(tests[v] == -1)
|
||||
return;
|
||||
|
||||
// ... otherwise, finish
|
||||
window.examiner.remove();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// save this for last so that our listeners are registered.
|
||||
// ... this loads the testbed of good and bad requests.
|
||||
|
||||
document.getElementById('cspframe').src = 'file_bug717511.html';
|
||||
document.getElementById('cspframe2').src = 'file_bug717511_2.html';
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -109,7 +109,7 @@ public:
|
||||
nsAutoPtr<ImageCacheEntryData> mData;
|
||||
};
|
||||
|
||||
class ImageCache : public nsExpirationTracker<ImageCacheEntryData,4> {
|
||||
class ImageCache MOZ_FINAL : public nsExpirationTracker<ImageCacheEntryData,4> {
|
||||
public:
|
||||
// We use 3 generations of 1 second each to get a 2-3 seconds timeout.
|
||||
enum { GENERATION_MS = 1000 };
|
||||
@ -134,7 +134,7 @@ public:
|
||||
|
||||
static ImageCache* gImageCache = nsnull;
|
||||
|
||||
class CanvasImageCacheShutdownObserver : public nsIObserver
|
||||
class CanvasImageCacheShutdownObserver MOZ_FINAL : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -227,6 +227,7 @@ protected:
|
||||
nsTArray<GradientStop> mRawStops;
|
||||
RefPtr<GradientStops> mStops;
|
||||
Type mType;
|
||||
virtual ~nsCanvasGradientAzure() {}
|
||||
};
|
||||
|
||||
class nsCanvasRadialGradientAzure : public nsCanvasGradientAzure
|
||||
@ -287,7 +288,7 @@ NS_INTERFACE_MAP_END
|
||||
**/
|
||||
#define NS_CANVASPATTERNAZURE_PRIVATE_IID \
|
||||
{0xc9bacc25, 0x28da, 0x421e, {0x9a, 0x4b, 0xbb, 0xd6, 0x93, 0x05, 0x12, 0xbc}}
|
||||
class nsCanvasPatternAzure : public nsIDOMCanvasPattern
|
||||
class nsCanvasPatternAzure MOZ_FINAL : public nsIDOMCanvasPattern
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERNAZURE_PRIVATE_IID)
|
||||
|
@ -68,7 +68,7 @@ protected:
|
||||
* forwards them along to the iframe so it can fire a mozbrowsertitlechange
|
||||
* event if appropriate.
|
||||
*/
|
||||
class TitleChangedListener : public nsIDOMEventListener
|
||||
class TitleChangedListener MOZ_FINAL : public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
TitleChangedListener(nsGenericHTMLFrameElement *aElement,
|
||||
|
@ -102,7 +102,7 @@ private:
|
||||
nsresult aReason);
|
||||
|
||||
public:
|
||||
class nsListener : public nsIDNSListener
|
||||
class nsListener MOZ_FINAL : public nsIDNSListener
|
||||
{
|
||||
// This class exists to give a safe callback no-op DNSListener
|
||||
public:
|
||||
@ -114,9 +114,9 @@ public:
|
||||
~nsListener() {}
|
||||
};
|
||||
|
||||
class nsDeferrals : public nsIWebProgressListener
|
||||
, public nsSupportsWeakReference
|
||||
, public nsIObserver
|
||||
class nsDeferrals MOZ_FINAL: public nsIWebProgressListener
|
||||
, public nsSupportsWeakReference
|
||||
, public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -159,7 +159,7 @@ SuppressEventHandlers(nsPresContext* aPresContext)
|
||||
return suppressHandlers;
|
||||
}
|
||||
|
||||
class nsAnonDivObserver : public nsStubMutationObserver
|
||||
class nsAnonDivObserver MOZ_FINAL : public nsStubMutationObserver
|
||||
{
|
||||
public:
|
||||
nsAnonDivObserver(nsTextEditorState* aTextEditorState)
|
||||
@ -174,8 +174,8 @@ private:
|
||||
nsTextEditorState* mTextEditorState;
|
||||
};
|
||||
|
||||
class nsTextInputSelectionImpl : public nsSupportsWeakReference
|
||||
, public nsISelectionController
|
||||
class nsTextInputSelectionImpl MOZ_FINAL : public nsSupportsWeakReference
|
||||
, public nsISelectionController
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
@ -344,7 +344,7 @@ static int PrefChanged(const char* aPref, void* aClosure)
|
||||
gVolumeScale = NS_MAX<double>(0, PR_strtod(utf8.get(), nsnull));
|
||||
}
|
||||
} else if (strcmp(aPref, PREF_USE_CUBEB) == 0) {
|
||||
bool value = Preferences::GetBool(aPref, true);
|
||||
bool value = Preferences::GetBool(aPref, false);
|
||||
mozilla::MutexAutoLock lock(*gAudioPrefsLock);
|
||||
gUseCubeb = value;
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ protected:
|
||||
|
||||
TimeReferenceElement mReferencedElement;
|
||||
|
||||
class EventListener : public nsIDOMEventListener
|
||||
class EventListener MOZ_FINAL : public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
EventListener(nsSMILTimeValueSpec* aOwner) : mSpec(aOwner) { }
|
||||
|
22
content/svg/content/src/crashtests/723441-1.html
Normal file
22
content/svg/content/src/crashtests/723441-1.html
Normal file
@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<body>
|
||||
<img id="x">
|
||||
<script>
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "723441-resource.svg");
|
||||
xhr.overrideMimeType( 'text/plain; charset=x-user-defined' );
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
var svg = xhr.responseText.replace(/#/g, '%23');
|
||||
var img = document.getElementById("x");
|
||||
img.onload = function() {
|
||||
document.documentElement.className = "";
|
||||
}
|
||||
img.src = "data:image/svg+xml;," + svg;
|
||||
}
|
||||
}
|
||||
xhr.send();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
4922
content/svg/content/src/crashtests/723441-resource.svg
Normal file
4922
content/svg/content/src/crashtests/723441-resource.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 422 KiB |
@ -58,3 +58,4 @@ load 603145-1.svg
|
||||
load 613899-1.svg
|
||||
load 613899-2.svg
|
||||
load zero-size-image.svg
|
||||
load 723441-1.html
|
||||
|
@ -267,7 +267,7 @@ nsSVGUseElement::CreateAnonymousContent()
|
||||
|
||||
LookupHref();
|
||||
nsIContent* targetContent = mSource.get();
|
||||
if (!targetContent)
|
||||
if (!targetContent || !targetContent->IsSVG())
|
||||
return nsnull;
|
||||
|
||||
// make sure target is valid type for <use>
|
||||
@ -391,10 +391,7 @@ nsSVGUseElement::CreateAnonymousContent()
|
||||
nsCOMPtr<nsIURI> baseURI = targetContent->GetBaseURI();
|
||||
if (!baseURI)
|
||||
return nsnull;
|
||||
nsCAutoString spec;
|
||||
baseURI->GetSpec(spec);
|
||||
newcontent->SetAttr(kNameSpaceID_XML, nsGkAtoms::base,
|
||||
NS_ConvertUTF8toUTF16(spec), false);
|
||||
newcontent->SetExplicitBaseURI(baseURI);
|
||||
|
||||
targetContent->AddMutationObserver(this);
|
||||
mClone = newcontent;
|
||||
|
@ -2730,6 +2730,9 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent)
|
||||
nsresult
|
||||
nsPluginInstanceOwner::Destroy()
|
||||
{
|
||||
if (mObjectFrame)
|
||||
mObjectFrame->SetInstanceOwner(nsnull);
|
||||
|
||||
#ifdef MAC_CARBON_PLUGINS
|
||||
// stop the timer explicitly to reduce reference count.
|
||||
CancelTimer();
|
||||
|
@ -171,7 +171,7 @@ GetImageFromSourceSurface(SourceSurface *aSurface)
|
||||
return static_cast<SourceSurfaceCGBitmapContext*>(aSurface)->GetImage();
|
||||
else if (aSurface->GetType() == SURFACE_DATA)
|
||||
return static_cast<DataSourceSurfaceCG*>(aSurface)->GetImage();
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
|
||||
TemporaryRef<SourceSurface>
|
||||
|
@ -193,9 +193,8 @@ AllocateTextureIOSurface(MacIOSurfaceImage *aIOImage, mozilla::gl::GLContext* aG
|
||||
LOCAL_GL_NEAREST);
|
||||
|
||||
void *nativeCtx = aGL->GetNativeData(GLContext::NativeGLContext);
|
||||
NSOpenGLContext* nsCtx = (NSOpenGLContext*)nativeCtx;
|
||||
|
||||
aIOImage->GetIOSurface()->CGLTexImageIOSurface2D(nsCtx,
|
||||
aIOImage->GetIOSurface()->CGLTexImageIOSurface2D(nativeCtx,
|
||||
LOCAL_GL_RGBA, LOCAL_GL_BGRA,
|
||||
LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV, 0);
|
||||
|
||||
|
@ -512,7 +512,18 @@ public:
|
||||
mIsBadUnderlineFamily(false)
|
||||
{ }
|
||||
|
||||
virtual ~gfxFontFamily() { }
|
||||
virtual ~gfxFontFamily() {
|
||||
// clear Family pointers in our faces; the font entries might stay
|
||||
// alive due to cached font objects, but they can no longer refer
|
||||
// to their families.
|
||||
PRUint32 i = mAvailableFonts.Length();
|
||||
while (i) {
|
||||
gfxFontEntry *fe = mAvailableFonts[--i];
|
||||
if (fe) {
|
||||
fe->SetFamily(nsnull);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const nsString& Name() { return mName; }
|
||||
|
||||
|
@ -141,6 +141,20 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// clear family pointer for all entries and remove them from the family;
|
||||
// we need to do this explicitly before inserting the entries into a new
|
||||
// family, in case the old one is not actually deleted until later
|
||||
void DetachFontEntries() {
|
||||
PRUint32 i = mAvailableFonts.Length();
|
||||
while (i--) {
|
||||
gfxFontEntry *fe = mAvailableFonts[i];
|
||||
if (fe) {
|
||||
fe->SetFamily(nsnull);
|
||||
}
|
||||
}
|
||||
mAvailableFonts.Clear();
|
||||
}
|
||||
|
||||
// temp method to determine if all proxies are loaded
|
||||
bool AllLoaded()
|
||||
{
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "nsDebug.h"
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <AppKit/NSOpenGL.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define IOSURFACE_FRAMEWORK_PATH \
|
||||
@ -383,10 +384,11 @@ nsIOSurface::GetAsSurface() {
|
||||
}
|
||||
|
||||
CGLError
|
||||
nsIOSurface::CGLTexImageIOSurface2D(NSOpenGLContext *ctxt,
|
||||
nsIOSurface::CGLTexImageIOSurface2D(void *c,
|
||||
GLenum internalFormat, GLenum format,
|
||||
GLenum type, GLuint plane)
|
||||
{
|
||||
NSOpenGLContext *ctxt = static_cast<NSOpenGLContext*>(c);
|
||||
return nsIOSurfaceLib::CGLTexImageIOSurface2D((CGLContextObj)[ctxt CGLContextObj],
|
||||
GL_TEXTURE_RECTANGLE_ARB,
|
||||
internalFormat,
|
||||
|
@ -45,7 +45,8 @@
|
||||
|
||||
class gfxASurface;
|
||||
class _CGLContextObject;
|
||||
class NSOpenGLContext;
|
||||
// We would like to forward declare NSOpenGLContext, but it is an @interface and this
|
||||
// file is also used from c++.
|
||||
|
||||
typedef _CGLContextObject* CGLContextObj;
|
||||
typedef uint32_t IOSurfaceID;
|
||||
@ -66,7 +67,7 @@ public:
|
||||
size_t GetBytesPerRow();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
CGLError CGLTexImageIOSurface2D(NSOpenGLContext *ctxt,
|
||||
CGLError CGLTexImageIOSurface2D(void *ctxt,
|
||||
GLenum internalFormat, GLenum format,
|
||||
GLenum type, GLuint plane);
|
||||
already_AddRefed<gfxASurface> GetAsSurface();
|
||||
|
@ -71,7 +71,6 @@
|
||||
#include "mozilla/StdInt.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::image;
|
||||
@ -177,8 +176,6 @@ DiscardingEnabled()
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
/* static */ nsRefPtr<RasterImage::DecodeWorker> RasterImage::DecodeWorker::sSingleton;
|
||||
|
||||
#ifndef DEBUG
|
||||
NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
|
||||
nsISupportsWeakReference)
|
||||
@ -197,7 +194,7 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker) :
|
||||
mObserver(nsnull),
|
||||
mLockCount(0),
|
||||
mDecoder(nsnull),
|
||||
mDecodeRequest(this),
|
||||
mWorker(nsnull),
|
||||
mBytesDecoded(0),
|
||||
mDecodeCount(0),
|
||||
#ifdef DEBUG
|
||||
@ -210,6 +207,7 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker) :
|
||||
mHasSourceData(false),
|
||||
mDecoded(false),
|
||||
mHasBeenDecoded(false),
|
||||
mWorkerPending(false),
|
||||
mInDecoder(false),
|
||||
mAnimationFinished(false)
|
||||
{
|
||||
@ -1495,9 +1493,11 @@ RasterImage::AddSourceData(const char *aBuffer, PRUint32 aCount)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
// If there's a decoder open, that means we want to do more decoding.
|
||||
// Wake up the worker.
|
||||
if (mDecoder) {
|
||||
DecodeWorker::Singleton()->RequestDecode(this);
|
||||
// Wake up the worker if it's not up already
|
||||
if (mDecoder && !mWorkerPending) {
|
||||
NS_ABORT_IF_FALSE(mWorker, "We should have a worker here!");
|
||||
rv = mWorker->Run();
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1560,21 +1560,19 @@ RasterImage::SourceDataComplete()
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
}
|
||||
|
||||
// If there's a decoder open, synchronously decode the beginning of the image
|
||||
// to check for errors and get the image's size. (If we already have the
|
||||
// image's size, this does nothing.) Then kick off an async decode of the
|
||||
// rest of the image.
|
||||
if (mDecoder) {
|
||||
nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
|
||||
// If there's a decoder open, we need to wake up the worker if it's not
|
||||
// already. This is so the worker can account for the fact that the source
|
||||
// data is complete. For some decoders, DecodingComplete() is only called
|
||||
// when the decoder is Close()-ed, and thus the SourceDataComplete() call
|
||||
// is the only way we can transition to a 'decoded' state. Furthermore,
|
||||
// it's always possible for any image type to have the data stream stop
|
||||
// abruptly at any point, in which case we need to trigger an error.
|
||||
if (mDecoder && !mWorkerPending) {
|
||||
NS_ABORT_IF_FALSE(mWorker, "We should have a worker here!");
|
||||
nsresult rv = mWorker->Run();
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
}
|
||||
|
||||
// If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
|
||||
// finish decoding this image.
|
||||
if (mDecoder) {
|
||||
DecodeWorker::Singleton()->RequestDecode(this);
|
||||
}
|
||||
|
||||
// Free up any extra space in the backing buffer
|
||||
mSourceData.Compact();
|
||||
|
||||
@ -2280,11 +2278,15 @@ RasterImage::InitDecoder(bool aDoSizeDecode)
|
||||
mDecoder->Init();
|
||||
CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
|
||||
|
||||
// Create a decode worker
|
||||
mWorker = new imgDecodeWorker(this);
|
||||
|
||||
if (!aDoSizeDecode) {
|
||||
Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
|
||||
mDecodeCount++;
|
||||
Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
|
||||
}
|
||||
CONTAINER_ENSURE_TRUE(mWorker, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2320,16 +2322,16 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
||||
decoder->Finish();
|
||||
mInDecoder = false;
|
||||
|
||||
// Kill off our decode request, if it's pending. (If not, this call is
|
||||
// harmless.)
|
||||
DecodeWorker::Singleton()->StopDecoding(this);
|
||||
|
||||
nsresult decoderStatus = decoder->GetDecoderError();
|
||||
if (NS_FAILED(decoderStatus)) {
|
||||
DoError();
|
||||
return decoderStatus;
|
||||
}
|
||||
|
||||
// Kill off the worker
|
||||
mWorker = nsnull;
|
||||
mWorkerPending = false;
|
||||
|
||||
// We just shut down the decoder. If we didn't get what we want, but expected
|
||||
// to, flag an error
|
||||
bool failed = false;
|
||||
@ -2463,6 +2465,10 @@ RasterImage::RequestDecode()
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
}
|
||||
|
||||
// If we already have a pending worker, we're done
|
||||
if (mWorkerPending)
|
||||
return NS_OK;
|
||||
|
||||
// If we've read all the data we have, we're done
|
||||
if (mBytesDecoded == mSourceData.Length())
|
||||
return NS_OK;
|
||||
@ -2474,9 +2480,7 @@ RasterImage::RequestDecode()
|
||||
// If we get this far, dispatch the worker. We do this instead of starting
|
||||
// any immediate decoding to guarantee that all our decode notifications are
|
||||
// dispatched asynchronously, and to ensure we stay responsive.
|
||||
DecodeWorker::Singleton()->RequestDecode(this);
|
||||
|
||||
return NS_OK;
|
||||
return mWorker->Dispatch();
|
||||
}
|
||||
|
||||
// Synchronously decodes as much data as possible
|
||||
@ -2583,10 +2587,6 @@ RasterImage::Draw(gfxContext *aContext,
|
||||
// We use !mDecoded && mHasSourceData to mean discarded.
|
||||
if (!mDecoded && mHasSourceData) {
|
||||
mDrawStartTime = TimeStamp::Now();
|
||||
|
||||
// We're drawing this image, so indicate that we should decode it as soon
|
||||
// as possible.
|
||||
DecodeWorker::Singleton()->MarkAsASAP(this);
|
||||
}
|
||||
|
||||
// If a synchronous draw is requested, flush anything that might be sitting around
|
||||
@ -2764,7 +2764,143 @@ RasterImage::DoError()
|
||||
LOG_CONTAINER_ERROR;
|
||||
}
|
||||
|
||||
// nsIInputStream callback to copy the incoming image data directly to the
|
||||
// Decodes some data, then re-posts itself to the end of the event queue if
|
||||
// there's more processing to be done
|
||||
NS_IMETHODIMP
|
||||
imgDecodeWorker::Run()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// If we shutdown the decoder in this function, we could lose ourselves
|
||||
nsCOMPtr<nsIRunnable> kungFuDeathGrip(this);
|
||||
|
||||
// The container holds a strong reference to us. Cycles are bad.
|
||||
nsCOMPtr<imgIContainer> iContainer(do_QueryReferent(mContainer));
|
||||
if (!iContainer)
|
||||
return NS_OK;
|
||||
RasterImage* image = static_cast<RasterImage*>(iContainer.get());
|
||||
|
||||
NS_ABORT_IF_FALSE(image->mInitialized,
|
||||
"Worker active for uninitialized container!");
|
||||
|
||||
// If we were pending, we're not anymore
|
||||
image->mWorkerPending = false;
|
||||
|
||||
// If an error is flagged, it probably happened while we were waiting
|
||||
// in the event queue. Bail early, but no need to bother the run queue
|
||||
// by returning an error.
|
||||
if (image->mError)
|
||||
return NS_OK;
|
||||
|
||||
// If we don't have a decoder, we must have finished already (for example,
|
||||
// a synchronous decode request came while the worker was pending).
|
||||
if (!image->mDecoder)
|
||||
return NS_OK;
|
||||
|
||||
nsRefPtr<Decoder> decoderKungFuDeathGrip = image->mDecoder;
|
||||
|
||||
// Size decodes are cheap and we more or less want them to be
|
||||
// synchronous. Write all the data in that case, otherwise write a
|
||||
// chunk
|
||||
PRUint32 maxBytes = image->mDecoder->IsSizeDecode()
|
||||
? image->mSourceData.Length() : gDecodeBytesAtATime;
|
||||
|
||||
// Loop control
|
||||
bool haveMoreData = true;
|
||||
PRInt32 chunkCount = 0;
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
TimeStamp deadline = start + TimeDuration::FromMilliseconds(gMaxMSBeforeYield);
|
||||
|
||||
// We keep decoding chunks until one of three possible events occur:
|
||||
// 1) We don't have any data left to decode
|
||||
// 2) The decode completes
|
||||
// 3) We hit the deadline and need to yield to keep the UI snappy
|
||||
while (haveMoreData && !image->IsDecodeFinished() &&
|
||||
(TimeStamp::Now() < deadline)) {
|
||||
|
||||
// Decode a chunk of data
|
||||
chunkCount++;
|
||||
rv = image->DecodeSomeData(maxBytes);
|
||||
if (NS_FAILED(rv)) {
|
||||
image->DoError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Figure out if we still have more data
|
||||
haveMoreData =
|
||||
image->mSourceData.Length() > image->mBytesDecoded;
|
||||
}
|
||||
|
||||
TimeDuration decodeLatency = TimeStamp::Now() - start;
|
||||
if (chunkCount && !image->mDecoder->IsSizeDecode()) {
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_LATENCY, PRInt32(decodeLatency.ToMicroseconds()));
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, chunkCount);
|
||||
}
|
||||
// accumulate the total decode time
|
||||
mDecodeTime += decodeLatency;
|
||||
|
||||
// Flush invalidations _after_ we've written everything we're going to.
|
||||
// Furthermore, if we have all of the data, we don't want to do progressive
|
||||
// display at all. In that case, let Decoder::PostFrameStop() do the
|
||||
// flush once the whole frame is ready.
|
||||
if (!image->mHasSourceData) {
|
||||
image->mInDecoder = true;
|
||||
image->mDecoder->FlushInvalidations();
|
||||
image->mInDecoder = false;
|
||||
}
|
||||
|
||||
// If the decode finished, shutdown the decoder
|
||||
if (image->mDecoder && image->IsDecodeFinished()) {
|
||||
|
||||
if (!image->mDecoder->IsSizeDecode()) {
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME, PRInt32(mDecodeTime.ToMicroseconds()));
|
||||
|
||||
// We only record the speed for some decoders. The rest have SpeedHistogram return HistogramCount.
|
||||
Telemetry::ID id = image->mDecoder->SpeedHistogram();
|
||||
if (id < Telemetry::HistogramCount) {
|
||||
PRInt32 KBps = PRInt32((image->mBytesDecoded/1024.0)/mDecodeTime.ToSeconds());
|
||||
Telemetry::Accumulate(id, KBps);
|
||||
}
|
||||
}
|
||||
|
||||
rv = image->ShutdownDecoder(RasterImage::eShutdownIntent_Done);
|
||||
if (NS_FAILED(rv)) {
|
||||
image->DoError();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// If Conditions 1 & 2 are still true, then the only reason we bailed was
|
||||
// because we hit the deadline. Repost ourselves to the end of the event
|
||||
// queue.
|
||||
if (image->mDecoder && !image->IsDecodeFinished() && haveMoreData)
|
||||
return this->Dispatch();
|
||||
|
||||
// Otherwise, return success
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Queues the worker up at the end of the event queue
|
||||
NS_METHOD imgDecodeWorker::Dispatch()
|
||||
{
|
||||
// The container holds a strong reference to us. Cycles are bad.
|
||||
nsCOMPtr<imgIContainer> iContainer(do_QueryReferent(mContainer));
|
||||
if (!iContainer)
|
||||
return NS_OK;
|
||||
RasterImage* image = static_cast<RasterImage*>(iContainer.get());
|
||||
|
||||
// We should not be called if there's already a pending worker
|
||||
NS_ABORT_IF_FALSE(!image->mWorkerPending,
|
||||
"Trying to queue up worker with one already pending!");
|
||||
|
||||
// Flag that we're pending
|
||||
image->mWorkerPending = true;
|
||||
|
||||
// Dispatch
|
||||
return NS_DispatchToCurrentThread(this);
|
||||
}
|
||||
|
||||
// nsIInputStream callback to copy the incoming image data directly to the
|
||||
// RasterImage without processing. The RasterImage is passed as the closure.
|
||||
// Always reads everything it gets, even if the data is erroneous.
|
||||
NS_METHOD
|
||||
@ -2801,6 +2937,7 @@ RasterImage::ShouldAnimate()
|
||||
!mAnimationFinished;
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* readonly attribute PRUint32 framesNotified; */
|
||||
#ifdef DEBUG
|
||||
NS_IMETHODIMP
|
||||
@ -2814,247 +2951,5 @@ RasterImage::GetFramesNotified(PRUint32 *aFramesNotified)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* static */ RasterImage::DecodeWorker*
|
||||
RasterImage::DecodeWorker::Singleton()
|
||||
{
|
||||
if (!sSingleton) {
|
||||
sSingleton = new DecodeWorker();
|
||||
ClearOnShutdown(&sSingleton);
|
||||
}
|
||||
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg)
|
||||
{
|
||||
DecodeRequest* request = &aImg->mDecodeRequest;
|
||||
|
||||
// If we're already an ASAP request, there's nothing to do here.
|
||||
if (request->mIsASAP) {
|
||||
return;
|
||||
}
|
||||
|
||||
request->mIsASAP = true;
|
||||
|
||||
if (request->isInList()) {
|
||||
// If the decode request is in a list, it must be in the normal decode
|
||||
// requests list -- if it had been in the ASAP list, then mIsASAP would
|
||||
// have been true above. Move the request to the ASAP list.
|
||||
request->remove();
|
||||
mASAPDecodeRequests.insertBack(request);
|
||||
|
||||
// Since request is in a list, one of the decode worker's lists is
|
||||
// non-empty, so the worker should be pending in the event loop.
|
||||
//
|
||||
// (Note that this invariant only holds while we are not in Run(), because
|
||||
// DecodeSomeOfImage adds requests to the decode worker using
|
||||
// AddDecodeRequest, not RequestDecode, and AddDecodeRequest does not call
|
||||
// EnsurePendingInEventLoop. Therefore, it is an error to call MarkAsASAP
|
||||
// from within DecodeWorker::Run.)
|
||||
MOZ_ASSERT(mPendingInEventLoop);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::AddDecodeRequest(DecodeRequest* aRequest)
|
||||
{
|
||||
if (aRequest->isInList()) {
|
||||
// The image is already in our list of images to decode, so we don't have
|
||||
// to do anything here.
|
||||
return;
|
||||
}
|
||||
|
||||
if (aRequest->mIsASAP) {
|
||||
mASAPDecodeRequests.insertBack(aRequest);
|
||||
} else {
|
||||
mNormalDecodeRequests.insertBack(aRequest);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::RequestDecode(RasterImage* aImg)
|
||||
{
|
||||
AddDecodeRequest(&aImg->mDecodeRequest);
|
||||
EnsurePendingInEventLoop();
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::EnsurePendingInEventLoop()
|
||||
{
|
||||
if (!mPendingInEventLoop) {
|
||||
mPendingInEventLoop = true;
|
||||
NS_DispatchToCurrentThread(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::StopDecoding(RasterImage* aImg)
|
||||
{
|
||||
DecodeRequest* request = &aImg->mDecodeRequest;
|
||||
if (request->isInList()) {
|
||||
request->remove();
|
||||
}
|
||||
request->mDecodeTime = TimeDuration(0);
|
||||
request->mIsASAP = false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RasterImage::DecodeWorker::Run()
|
||||
{
|
||||
// We just got called back by the event loop; therefore, we're no longer
|
||||
// pending.
|
||||
mPendingInEventLoop = false;
|
||||
|
||||
TimeStamp eventStart = TimeStamp::Now();
|
||||
|
||||
// Now decode until we either run out of time or run out of images.
|
||||
do {
|
||||
// Try to get an ASAP request to handle. If there isn't one, try to get a
|
||||
// normal request. If no normal request is pending either, then we're done
|
||||
// here.
|
||||
DecodeRequest* request = mASAPDecodeRequests.popFirst();
|
||||
if (!request)
|
||||
request = mNormalDecodeRequests.popFirst();
|
||||
if (!request)
|
||||
break;
|
||||
|
||||
RasterImage *image = request->mImage;
|
||||
DecodeSomeOfImage(image);
|
||||
|
||||
// If we aren't yet finished decoding and we have more data in hand, add
|
||||
// this request to the back of the list.
|
||||
if (image->mDecoder &&
|
||||
!image->mError &&
|
||||
!image->IsDecodeFinished() &&
|
||||
image->mSourceData.Length() > image->mBytesDecoded) {
|
||||
AddDecodeRequest(request);
|
||||
}
|
||||
|
||||
} while ((TimeStamp::Now() - eventStart).ToMilliseconds() <= gMaxMSBeforeYield);
|
||||
|
||||
// If decode requests are pending, re-post ourself to the event loop.
|
||||
if (!mASAPDecodeRequests.isEmpty() || !mNormalDecodeRequests.isEmpty()) {
|
||||
EnsurePendingInEventLoop();
|
||||
}
|
||||
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_LATENCY,
|
||||
PRUint32((TimeStamp::Now() - eventStart).ToMilliseconds()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
|
||||
{
|
||||
return DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
RasterImage::DecodeWorker::DecodeSomeOfImage(
|
||||
RasterImage* aImg,
|
||||
DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aImg->mInitialized,
|
||||
"Worker active for uninitialized container!");
|
||||
|
||||
if (aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize)
|
||||
return NS_OK;
|
||||
|
||||
// If an error is flagged, it probably happened while we were waiting
|
||||
// in the event queue.
|
||||
if (aImg->mError)
|
||||
return NS_OK;
|
||||
|
||||
// If mDecoded or we don't have a decoder, we must have finished already (for
|
||||
// example, a synchronous decode request came while the worker was pending).
|
||||
if (!aImg->mDecoder || aImg->mDecoded)
|
||||
return NS_OK;
|
||||
|
||||
nsRefPtr<Decoder> decoderKungFuDeathGrip = aImg->mDecoder;
|
||||
|
||||
PRUint32 maxBytes;
|
||||
if (aImg->mDecoder->IsSizeDecode()) {
|
||||
// Decode all available data if we're a size decode; they're cheap, and we
|
||||
// want them to be more or less synchronous.
|
||||
maxBytes = aImg->mSourceData.Length();
|
||||
} else {
|
||||
// We're only guaranteed to decode this many bytes, so in particular,
|
||||
// gDecodeBytesAtATime should be set high enough for us to read the size
|
||||
// from most images.
|
||||
maxBytes = gDecodeBytesAtATime;
|
||||
}
|
||||
|
||||
PRInt32 chunkCount = 0;
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
TimeStamp deadline = start + TimeDuration::FromMilliseconds(gMaxMSBeforeYield);
|
||||
|
||||
// Decode some chunks of data.
|
||||
do {
|
||||
chunkCount++;
|
||||
nsresult rv = aImg->DecodeSomeData(maxBytes);
|
||||
if (NS_FAILED(rv)) {
|
||||
aImg->DoError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
// We keep decoding chunks until either:
|
||||
// * we're an UNTIL_SIZE decode and we get the size,
|
||||
// * we don't have any data left to decode,
|
||||
// * the decode completes, or
|
||||
// * we run out of time.
|
||||
|
||||
if (aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize)
|
||||
break;
|
||||
}
|
||||
while (aImg->mSourceData.Length() > aImg->mBytesDecoded &&
|
||||
!aImg->IsDecodeFinished() &&
|
||||
TimeStamp::Now() < deadline);
|
||||
|
||||
aImg->mDecodeRequest.mDecodeTime += (TimeStamp::Now() - start);
|
||||
|
||||
if (chunkCount && !aImg->mDecoder->IsSizeDecode()) {
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, chunkCount);
|
||||
}
|
||||
|
||||
// Flush invalidations _after_ we've written everything we're going to.
|
||||
// Furthermore, if we have all of the data, we don't want to do progressive
|
||||
// display at all. In that case, let Decoder::PostFrameStop() do the
|
||||
// flush once the whole frame is ready.
|
||||
if (!aImg->mHasSourceData) {
|
||||
aImg->mInDecoder = true;
|
||||
aImg->mDecoder->FlushInvalidations();
|
||||
aImg->mInDecoder = false;
|
||||
}
|
||||
|
||||
// If the decode finished, shut down the decoder.
|
||||
if (aImg->mDecoder && aImg->IsDecodeFinished()) {
|
||||
|
||||
// Do some telemetry if this isn't a size decode.
|
||||
DecodeRequest* request = &aImg->mDecodeRequest;
|
||||
if (!aImg->mDecoder->IsSizeDecode()) {
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
|
||||
PRInt32(request->mDecodeTime.ToMicroseconds()));
|
||||
|
||||
// We record the speed for only some decoders. The rest have
|
||||
// SpeedHistogram return HistogramCount.
|
||||
Telemetry::ID id = aImg->mDecoder->SpeedHistogram();
|
||||
if (id < Telemetry::HistogramCount) {
|
||||
PRInt32 KBps = PRInt32(request->mImage->mBytesDecoded /
|
||||
(1024 * request->mDecodeTime.ToSeconds()));
|
||||
Telemetry::Accumulate(id, KBps);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = aImg->ShutdownDecoder(RasterImage::eShutdownIntent_Done);
|
||||
if (NS_FAILED(rv)) {
|
||||
aImg->DoError();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
@ -66,7 +66,6 @@
|
||||
#include "DiscardTracker.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#ifdef DEBUG
|
||||
#include "imgIContainerDebug.h"
|
||||
#endif
|
||||
@ -165,6 +164,7 @@ class ImageContainer;
|
||||
}
|
||||
namespace image {
|
||||
|
||||
class imgDecodeWorker;
|
||||
class Decoder;
|
||||
|
||||
class RasterImage : public Image
|
||||
@ -376,123 +376,6 @@ private:
|
||||
~Anim() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* DecodeWorker keeps a linked list of DecodeRequests to keep track of the
|
||||
* images it needs to decode.
|
||||
*
|
||||
* Each RasterImage has a single DecodeRequest member.
|
||||
*/
|
||||
struct DecodeRequest : public LinkedListElement<DecodeRequest>
|
||||
{
|
||||
DecodeRequest(RasterImage* aImage)
|
||||
: mImage(aImage)
|
||||
, mIsASAP(false)
|
||||
{
|
||||
}
|
||||
|
||||
RasterImage* const mImage;
|
||||
|
||||
/* Keeps track of how much time we've burned decoding this particular decode
|
||||
* request. */
|
||||
TimeDuration mDecodeTime;
|
||||
|
||||
/* True if we need to handle this decode as soon as possible. */
|
||||
bool mIsASAP;
|
||||
};
|
||||
|
||||
/*
|
||||
* DecodeWorker is a singleton class we use when decoding large images.
|
||||
*
|
||||
* When we wish to decode an image larger than
|
||||
* image.mem.max_bytes_for_sync_decode, we call DecodeWorker::RequestDecode()
|
||||
* for the image. This adds the image to a queue of pending requests and posts
|
||||
* the DecodeWorker singleton to the event queue, if it's not already pending
|
||||
* there.
|
||||
*
|
||||
* When the DecodeWorker is run from the event queue, it decodes the image (and
|
||||
* all others it's managing) in chunks, periodically yielding control back to
|
||||
* the event loop.
|
||||
*
|
||||
* An image being decoded may have one of two priorities: normal or ASAP. ASAP
|
||||
* images are always decoded before normal images. (We currently give ASAP
|
||||
* priority to images which appear onscreen but are not yet decoded.)
|
||||
*/
|
||||
class DecodeWorker : public nsRunnable
|
||||
{
|
||||
public:
|
||||
static DecodeWorker* Singleton();
|
||||
|
||||
/**
|
||||
* Ask the DecodeWorker to asynchronously decode this image.
|
||||
*/
|
||||
void RequestDecode(RasterImage* aImg);
|
||||
|
||||
/**
|
||||
* Give this image ASAP priority; it will be decoded before all non-ASAP
|
||||
* images. You can call MarkAsASAP before or after you call RequestDecode
|
||||
* for the image, but if you MarkAsASAP before you call RequestDecode, you
|
||||
* still need to call RequestDecode.
|
||||
*
|
||||
* StopDecoding() resets the image's ASAP flag.
|
||||
*/
|
||||
void MarkAsASAP(RasterImage* aImg);
|
||||
|
||||
/**
|
||||
* Ask the DecodeWorker to stop decoding this image. Internally, we also
|
||||
* call this function when we finish decoding an image.
|
||||
*
|
||||
* Since the DecodeWorker keeps raw pointers to RasterImages, make sure you
|
||||
* call this before a RasterImage is destroyed!
|
||||
*/
|
||||
void StopDecoding(RasterImage* aImg);
|
||||
|
||||
/**
|
||||
* Synchronously decode the beginning of the image until we run out of
|
||||
* bytes or we get the image's size. Note that this done on a best-effort
|
||||
* basis; if the size is burried too deep in the image, we'll give up.
|
||||
*
|
||||
* @return NS_ERROR if an error is encountered, and NS_OK otherwise. (Note
|
||||
* that we return NS_OK even when the size was not found.)
|
||||
*/
|
||||
nsresult DecodeUntilSizeAvailable(RasterImage* aImg);
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
private: /* statics */
|
||||
static nsRefPtr<DecodeWorker> sSingleton;
|
||||
|
||||
private: /* methods */
|
||||
DecodeWorker()
|
||||
: mPendingInEventLoop(false)
|
||||
{}
|
||||
|
||||
/* Post ourselves to the event loop if we're not currently pending. */
|
||||
void EnsurePendingInEventLoop();
|
||||
|
||||
/* Add the given request to the appropriate list of decode requests, but
|
||||
* don't ensure that we're pending in the event loop. */
|
||||
void AddDecodeRequest(DecodeRequest* aRequest);
|
||||
|
||||
enum DecodeType {
|
||||
DECODE_TYPE_NORMAL,
|
||||
DECODE_TYPE_UNTIL_SIZE
|
||||
};
|
||||
|
||||
/* Decode some chunks of the given image. If aDecodeType is UNTIL_SIZE,
|
||||
* decode until we have the image's size, then stop. */
|
||||
nsresult DecodeSomeOfImage(RasterImage* aImg,
|
||||
DecodeType aDecodeType = DECODE_TYPE_NORMAL);
|
||||
|
||||
private: /* members */
|
||||
|
||||
LinkedList<DecodeRequest> mASAPDecodeRequests;
|
||||
LinkedList<DecodeRequest> mNormalDecodeRequests;
|
||||
|
||||
/* True if we've posted ourselves to the event loop and expect Run() to
|
||||
* be called sometime in the future. */
|
||||
bool mPendingInEventLoop;
|
||||
};
|
||||
|
||||
/**
|
||||
* Advances the animation. Typically, this will advance a single frame, but it
|
||||
* may advance multiple frames. This may happen if we have infrequently
|
||||
@ -641,11 +524,12 @@ private: // data
|
||||
nsCString mSourceDataMimeType;
|
||||
nsCString mURIString;
|
||||
|
||||
friend class imgDecodeWorker;
|
||||
friend class DiscardTracker;
|
||||
|
||||
// Decoder and friends
|
||||
nsRefPtr<Decoder> mDecoder;
|
||||
DecodeRequest mDecodeRequest;
|
||||
nsRefPtr<imgDecodeWorker> mWorker;
|
||||
PRUint32 mBytesDecoded;
|
||||
|
||||
// How many times we've decoded this image.
|
||||
@ -670,6 +554,8 @@ private: // data
|
||||
bool mDecoded:1;
|
||||
bool mHasBeenDecoded:1;
|
||||
|
||||
// Helpers for decoder
|
||||
bool mWorkerPending:1;
|
||||
bool mInDecoder:1;
|
||||
|
||||
// Whether the animation can stop, due to running out
|
||||
@ -705,6 +591,29 @@ protected:
|
||||
bool ShouldAnimate();
|
||||
};
|
||||
|
||||
// XXXdholbert These helper classes should move to be inside the
|
||||
// scope of the RasterImage class.
|
||||
// Decoding Helper Class
|
||||
//
|
||||
// We use this class to mimic the interactivity benefits of threading
|
||||
// in a single-threaded event loop. We want to progressively decode
|
||||
// and keep a responsive UI while we're at it, so we have a runnable
|
||||
// class that does a bit of decoding, and then "yields" by dispatching
|
||||
// itself to the end of the event queue.
|
||||
class imgDecodeWorker : public nsRunnable
|
||||
{
|
||||
public:
|
||||
imgDecodeWorker(imgIContainer* aContainer) {
|
||||
mContainer = do_GetWeakReference(aContainer);
|
||||
}
|
||||
NS_IMETHOD Run();
|
||||
NS_METHOD Dispatch();
|
||||
|
||||
private:
|
||||
nsWeakPtr mContainer;
|
||||
TimeDuration mDecodeTime; // the default constructor initializes to 0
|
||||
};
|
||||
|
||||
// Asynchronous Decode Requestor
|
||||
//
|
||||
// We use this class when someone calls requestDecode() from within a decode
|
||||
|
@ -2,11 +2,11 @@
|
||||
// Autogenerated from Python template. Hands off.
|
||||
//
|
||||
|
||||
#include "IPDLUnitTests.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "IPDLUnitTests.h"
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/thread.h"
|
||||
@ -20,17 +20,20 @@
|
||||
${INCLUDES}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
using mozilla::_ipdltest::IPDLUnitTestSubprocess;
|
||||
using namespace base;
|
||||
using namespace std;
|
||||
|
||||
void* mozilla::_ipdltest::gParentActor;
|
||||
IPDLUnitTestSubprocess* mozilla::_ipdltest::gSubprocess;
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
void* mozilla::_ipdltest::gChildActor;
|
||||
void* gParentActor;
|
||||
IPDLUnitTestSubprocess* gSubprocess;
|
||||
|
||||
void* gChildActor;
|
||||
|
||||
// Note: in threaded mode, this will be non-null (for both parent and
|
||||
// child, since they share one set of globals).
|
||||
Thread* mozilla::_ipdltest::gChildThread;
|
||||
Thread* gChildThread;
|
||||
MessageLoop *gParentMessageLoop;
|
||||
bool gParentDone;
|
||||
bool gChildDone;
|
||||
@ -38,26 +41,19 @@ bool gChildDone;
|
||||
//-----------------------------------------------------------------------------
|
||||
// data/functions accessed by both parent and child processes
|
||||
|
||||
namespace {
|
||||
char* gIPDLUnitTestName = NULL;
|
||||
}
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
const char* const
|
||||
IPDLUnitTestName()
|
||||
{
|
||||
if (!gIPDLUnitTestName) {
|
||||
#if defined(OS_WIN)
|
||||
std::vector<std::wstring> args =
|
||||
vector<wstring> args =
|
||||
CommandLine::ForCurrentProcess()->GetLooseValues();
|
||||
gIPDLUnitTestName = strdup(WideToUTF8(args[0]).c_str());
|
||||
#elif defined(OS_POSIX)
|
||||
std::vector<std::string> argv =
|
||||
CommandLine::ForCurrentProcess()->argv();
|
||||
gIPDLUnitTestName = strdup(argv[1].c_str());
|
||||
vector<string> argv = CommandLine::ForCurrentProcess()->argv();
|
||||
gIPDLUnitTestName = ::moz_xstrdup(argv[1].c_str());
|
||||
#else
|
||||
# error Sorry
|
||||
#endif
|
||||
|
@ -17,6 +17,9 @@ public:
|
||||
TestSysVShmemParent() { }
|
||||
virtual ~TestSysVShmemParent() { }
|
||||
|
||||
static bool RunTestInProcesses() { return true; }
|
||||
static bool RunTestInThreads() { return true; }
|
||||
|
||||
void Main();
|
||||
|
||||
protected:
|
||||
|
@ -185,14 +185,12 @@ CPPSRCS = \
|
||||
INSTALLED_HEADERS = \
|
||||
js-config.h \
|
||||
jscpucfg.h \
|
||||
$(CURDIR)/jsautokw.h \
|
||||
js.msg \
|
||||
jsalloc.h \
|
||||
jsapi.h \
|
||||
jsatom.h \
|
||||
jsclass.h \
|
||||
jsclist.h \
|
||||
jsclone.h \
|
||||
jscompat.h \
|
||||
jsdbgapi.h \
|
||||
jsdhash.h \
|
||||
@ -204,14 +202,11 @@ INSTALLED_HEADERS = \
|
||||
jshash.h \
|
||||
jslock.h \
|
||||
json.h \
|
||||
jsopcode.tbl \
|
||||
jsopcode.h \
|
||||
jsproxy.h \
|
||||
jsprf.h \
|
||||
jsproto.tbl \
|
||||
jsprvtd.h \
|
||||
jspubtd.h \
|
||||
jsstdint.h \
|
||||
jstypedarray.h \
|
||||
jstypes.h \
|
||||
jsutil.h \
|
||||
@ -225,11 +220,7 @@ INSTALLED_HEADERS = \
|
||||
# BEGIN exported headers that are only exported
|
||||
# because of inclusion by an INSTALLED_HEADER
|
||||
#
|
||||
EXPORTS_NAMESPACES += vm ds gc
|
||||
|
||||
EXPORTS_vm = \
|
||||
String.h \
|
||||
$(NULL)
|
||||
EXPORTS_NAMESPACES += ds gc
|
||||
|
||||
EXPORTS_ds = \
|
||||
BitArray.h \
|
||||
|
@ -159,7 +159,7 @@ ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, T *re, JSLinearString *inpu
|
||||
}
|
||||
|
||||
bool
|
||||
js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpMatcher &matcher, JSLinearString *input,
|
||||
js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, const RegExpMatcher &matcher, JSLinearString *input,
|
||||
const jschar *chars, size_t length,
|
||||
size_t *lastIndex, RegExpExecType type, Value *rval)
|
||||
{
|
||||
@ -207,7 +207,7 @@ EscapeNakedForwardSlashes(JSContext *cx, JSLinearString *unescaped)
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile a new |RegExpPrivate| for the |RegExpObject|.
|
||||
* Compile a new |RegExpShared| for the |RegExpObject|.
|
||||
*
|
||||
* Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
|
||||
* arguments:
|
||||
@ -218,20 +218,19 @@ EscapeNakedForwardSlashes(JSContext *cx, JSLinearString *unescaped)
|
||||
* flags := ToString(flags) if defined(flags) else ''
|
||||
*/
|
||||
static bool
|
||||
CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder,
|
||||
uintN argc, Value *argv, Value *rval)
|
||||
CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
|
||||
{
|
||||
if (argc == 0) {
|
||||
if (args.length() == 0) {
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpObject *reobj = builder.build(cx->runtime->emptyString, res->getFlags());
|
||||
if (!reobj)
|
||||
return false;
|
||||
*rval = ObjectValue(*reobj);
|
||||
args.rval() = ObjectValue(*reobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
Value sourceValue = argv[0];
|
||||
if (ValueIsRegExp(sourceValue)) {
|
||||
Value sourceValue = args[0];
|
||||
if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) {
|
||||
/*
|
||||
* If we get passed in a |RegExpObject| source we return a new
|
||||
* object with the same source/flags.
|
||||
@ -239,15 +238,21 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder,
|
||||
* Note: the regexp static flags are not taken into consideration here.
|
||||
*/
|
||||
JSObject &sourceObj = sourceValue.toObject();
|
||||
if (argc >= 2 && !argv[1].isUndefined()) {
|
||||
if (args.length() >= 2 && !args[1].isUndefined()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED);
|
||||
return false;
|
||||
}
|
||||
|
||||
RegExpObject *reobj = builder.build(&sourceObj.asRegExp());
|
||||
RegExpShared *shared = RegExpToShared(cx, sourceObj);
|
||||
if (!shared)
|
||||
return false;
|
||||
|
||||
shared->incref(cx);
|
||||
RegExpObject *reobj = builder.build(AlreadyIncRefed<RegExpShared>(shared));
|
||||
if (!reobj)
|
||||
return false;
|
||||
*rval = ObjectValue(*reobj);
|
||||
|
||||
args.rval() = ObjectValue(*reobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -265,11 +270,11 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder,
|
||||
}
|
||||
|
||||
RegExpFlag flags = RegExpFlag(0);
|
||||
if (argc > 1 && !argv[1].isUndefined()) {
|
||||
JSString *flagStr = ToString(cx, argv[1]);
|
||||
if (args.length() > 1 && !args[1].isUndefined()) {
|
||||
JSString *flagStr = ToString(cx, args[1]);
|
||||
if (!flagStr)
|
||||
return false;
|
||||
argv[1].setString(flagStr);
|
||||
args[1].setString(flagStr);
|
||||
if (!ParseRegExpFlags(cx, flagStr, &flags))
|
||||
return false;
|
||||
}
|
||||
@ -286,7 +291,7 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder,
|
||||
if (!reobj)
|
||||
return NULL;
|
||||
|
||||
*rval = ObjectValue(*reobj);
|
||||
args.rval() = ObjectValue(*reobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -301,32 +306,30 @@ regexp_compile(JSContext *cx, uintN argc, Value *vp)
|
||||
return ok;
|
||||
|
||||
RegExpObjectBuilder builder(cx, &obj->asRegExp());
|
||||
return CompileRegExpObject(cx, builder, args.length(), args.array(), &args.rval());
|
||||
return CompileRegExpObject(cx, builder, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
regexp_construct(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
Value *argv = JS_ARGV(cx, vp);
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!IsConstructing(vp)) {
|
||||
if (!IsConstructing(args)) {
|
||||
/*
|
||||
* If first arg is regexp and no flags are given, just return the arg.
|
||||
* Otherwise, delegate to the standard constructor.
|
||||
* See ECMAv5 15.10.3.1.
|
||||
*/
|
||||
if (argc >= 1 && ValueIsRegExp(argv[0]) && (argc == 1 || argv[1].isUndefined())) {
|
||||
*vp = argv[0];
|
||||
if (args.length() >= 1 && IsObjectWithClass(args[0], ESClass_RegExp, cx) &&
|
||||
(args.length() == 1 || args[1].isUndefined()))
|
||||
{
|
||||
args.rval() = args[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
RegExpObjectBuilder builder(cx);
|
||||
if (!CompileRegExpObject(cx, builder, argc, argv, &JS_RVAL(cx, vp)))
|
||||
return false;
|
||||
|
||||
*vp = ObjectValue(*builder.reobj());
|
||||
return true;
|
||||
return CompileRegExpObject(cx, builder, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -514,15 +517,17 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
|
||||
if (!obj)
|
||||
return ok;
|
||||
|
||||
RegExpObject *reobj = &obj->asRegExp();
|
||||
RegExpObject &reobj = obj->asRegExp();
|
||||
|
||||
RegExpMatcher matcher(cx);
|
||||
if (reobj->startsWithAtomizedGreedyStar()) {
|
||||
if (!matcher.resetWithTestOptimized(reobj))
|
||||
if (reobj.startsWithAtomizedGreedyStar()) {
|
||||
if (!matcher.initWithTestOptimized(reobj))
|
||||
return false;
|
||||
} else {
|
||||
if (!matcher.reset(reobj))
|
||||
RegExpShared *shared = reobj.getShared(cx);
|
||||
if (!shared)
|
||||
return false;
|
||||
matcher.init(NeedsIncRef<RegExpShared>(shared));
|
||||
}
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
@ -540,7 +545,7 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
|
||||
size_t length = input->length();
|
||||
|
||||
/* Step 4. */
|
||||
const Value &lastIndex = reobj->getLastIndex();
|
||||
const Value &lastIndex = reobj.getLastIndex();
|
||||
|
||||
/* Step 5. */
|
||||
jsdouble i;
|
||||
@ -553,7 +558,7 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
|
||||
|
||||
/* Step 9a. */
|
||||
if (i < 0 || i > length) {
|
||||
reobj->zeroLastIndex();
|
||||
reobj.zeroLastIndex();
|
||||
args.rval() = NullValue();
|
||||
return true;
|
||||
}
|
||||
@ -569,9 +574,9 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
|
||||
/* Step 11 (with sticky extension). */
|
||||
if (matcher.global() || (!args.rval().isNull() && matcher.sticky())) {
|
||||
if (args.rval().isNull())
|
||||
reobj->zeroLastIndex();
|
||||
reobj.zeroLastIndex();
|
||||
else
|
||||
reobj->setLastIndex(lastIndexInt);
|
||||
reobj.setLastIndex(lastIndexInt);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -59,13 +59,13 @@ namespace js {
|
||||
* |chars| and |length|.
|
||||
*/
|
||||
bool
|
||||
ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj, JSLinearString *input,
|
||||
const jschar *chars, size_t length,
|
||||
ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj,
|
||||
JSLinearString *input, const jschar *chars, size_t length,
|
||||
size_t *lastIndex, RegExpExecType type, Value *rval);
|
||||
|
||||
bool
|
||||
ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpMatcher &matcher, JSLinearString *input,
|
||||
const jschar *chars, size_t length,
|
||||
ExecuteRegExp(JSContext *cx, RegExpStatics *res, const RegExpMatcher &matcher,
|
||||
JSLinearString *input, const jschar *chars, size_t length,
|
||||
size_t *lastIndex, RegExpExecType type, Value *rval);
|
||||
|
||||
extern JSBool
|
||||
|
@ -2809,6 +2809,7 @@ arm*-*)
|
||||
AC_DEFINE(JS_NUNBOX32)
|
||||
;;
|
||||
sparc*-*)
|
||||
if test ! "$HAVE_64BIT_OS" ; then
|
||||
ENABLE_METHODJIT=1
|
||||
ENABLE_MONOIC=1
|
||||
ENABLE_POLYIC=1
|
||||
@ -2816,6 +2817,7 @@ sparc*-*)
|
||||
dnl ENABLE_ION=0
|
||||
AC_DEFINE(JS_CPU_SPARC)
|
||||
AC_DEFINE(JS_NUNBOX32)
|
||||
fi
|
||||
;;
|
||||
mips*-*)
|
||||
ENABLE_METHODJIT=1
|
||||
|
@ -1294,8 +1294,7 @@ Parser::functionArguments(TreeContext &funtc, FunctionBox *funbox, ParseNode **l
|
||||
rhs->pn_cookie.set(funtc.staticLevel, slot);
|
||||
rhs->pn_dflags |= PND_BOUND;
|
||||
|
||||
ParseNode *item =
|
||||
ParseNode::newBinaryOrAppend(PNK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
|
||||
ParseNode *item = new_<BinaryNode>(PNK_ASSIGN, JSOP_NOP, lhs->pn_pos, lhs, rhs);
|
||||
if (!item)
|
||||
return false;
|
||||
if (!list) {
|
||||
@ -6919,6 +6918,7 @@ Parser::primaryExpr(TokenKind tt, bool afterDoubleDot)
|
||||
for (;;) {
|
||||
JSAtom *atom;
|
||||
TokenKind ltok = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
|
||||
TokenPtr begin = tokenStream.currentToken().pos.begin;
|
||||
switch (ltok) {
|
||||
case TOK_NUMBER:
|
||||
pn3 = NullaryNode::create(PNK_NUMBER, tc);
|
||||
@ -6985,7 +6985,10 @@ Parser::primaryExpr(TokenKind tt, bool afterDoubleDot)
|
||||
|
||||
/* NB: Getter function in { get x(){} } is unnamed. */
|
||||
pn2 = functionDef(NULL, op == JSOP_GETTER ? Getter : Setter, Expression);
|
||||
pn2 = ParseNode::newBinaryOrAppend(PNK_COLON, op, pn3, pn2, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
TokenPos pos = {begin, pn2->pn_pos.end};
|
||||
pn2 = new_<BinaryNode>(PNK_COLON, op, pos, pn3, pn2);
|
||||
goto skip;
|
||||
}
|
||||
case TOK_STRING: {
|
||||
@ -7015,16 +7018,16 @@ Parser::primaryExpr(TokenKind tt, bool afterDoubleDot)
|
||||
tt = tokenStream.getToken();
|
||||
if (tt == TOK_COLON) {
|
||||
pnval = assignExpr();
|
||||
if (!pnval)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Treat initializers which mutate __proto__ as non-constant,
|
||||
* so that we can later assume singleton objects delegate to
|
||||
* the default Object.prototype.
|
||||
*/
|
||||
if ((pnval && !pnval->isConstant()) ||
|
||||
atom == context->runtime->atomState.protoAtom) {
|
||||
if (!pnval->isConstant() || atom == context->runtime->atomState.protoAtom)
|
||||
pn->pn_xflags |= PNX_NONCONST;
|
||||
}
|
||||
}
|
||||
#if JS_HAS_DESTRUCTURING_SHORTHAND
|
||||
else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
|
||||
@ -7047,7 +7050,10 @@ Parser::primaryExpr(TokenKind tt, bool afterDoubleDot)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pn2 = ParseNode::newBinaryOrAppend(PNK_COLON, op, pn3, pnval, tc);
|
||||
{
|
||||
TokenPos pos = {begin, pnval->pn_pos.end};
|
||||
pn2 = new_<BinaryNode>(PNK_COLON, op, pos, pn3, pnval);
|
||||
}
|
||||
skip:
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
|
@ -262,18 +262,16 @@ struct TokenPos {
|
||||
TokenPtr end; /* index 1 past last char, last line */
|
||||
|
||||
static TokenPos make(const TokenPtr &begin, const TokenPtr &end) {
|
||||
// Assertions temporarily disabled by jorendorff. See bug 695922.
|
||||
//JS_ASSERT(begin <= end);
|
||||
JS_ASSERT(begin <= end);
|
||||
TokenPos pos = {begin, end};
|
||||
return pos;
|
||||
}
|
||||
|
||||
/* Return a TokenPos that covers left, right, and anything in between. */
|
||||
static TokenPos box(const TokenPos &left, const TokenPos &right) {
|
||||
// Assertions temporarily disabled by jorendorff. See bug 695922.
|
||||
//JS_ASSERT(left.begin <= left.end);
|
||||
//JS_ASSERT(left.end <= right.begin);
|
||||
//JS_ASSERT(right.begin <= right.end);
|
||||
JS_ASSERT(left.begin <= left.end);
|
||||
JS_ASSERT(left.end <= right.begin);
|
||||
JS_ASSERT(right.begin <= right.end);
|
||||
TokenPos pos = {left.begin, right.end};
|
||||
return pos;
|
||||
}
|
||||
|
2
js/src/jit-test/tests/basic/bug695922-syntax.js
Normal file
2
js/src/jit-test/tests/basic/bug695922-syntax.js
Normal file
@ -0,0 +1,2 @@
|
||||
load(libdir + "asserts.js");
|
||||
assertThrowsInstanceOf(function () { eval("({p:"); }, SyntaxError); // don't crash
|
@ -27,7 +27,6 @@ function test(str, f) {
|
||||
f(g1.eval("new Object()"));
|
||||
} catch (e) {
|
||||
assertEq(Object.prototype.toString.call(e), "[object Error]");
|
||||
assertEq(e.name, "TypeError");
|
||||
threw = true;
|
||||
}
|
||||
assertEq(threw, true);
|
||||
@ -36,7 +35,6 @@ function test(str, f) {
|
||||
f(g2.eval("new Object()"));
|
||||
} catch (e) {
|
||||
assertEq(Object.prototype.toString.call(e), "[object Error]");
|
||||
assertEq(e.name, "TypeError");
|
||||
threw = true;
|
||||
}
|
||||
assertEq(threw, true);
|
||||
@ -45,7 +43,6 @@ function test(str, f) {
|
||||
f(proxy1);
|
||||
} catch (e) {
|
||||
assertEq(Object.prototype.toString.call(e), "[object Error]");
|
||||
assertEq(e.name, "TypeError");
|
||||
threw = true;
|
||||
}
|
||||
assertEq(threw, true);
|
||||
@ -54,7 +51,6 @@ function test(str, f) {
|
||||
f(proxy2);
|
||||
} catch (e) {
|
||||
assertEq(Object.prototype.toString.call(e), "[object Error]");
|
||||
assertEq(e.name, "TypeError");
|
||||
threw = true;
|
||||
}
|
||||
assertEq(threw, true);
|
||||
@ -78,6 +74,13 @@ test("new RegExp('1')", function(r) RegExp.prototype.toString.call(r));
|
||||
test("new RegExp('1')", function(r) RegExp.prototype.exec.call(r, '1').toString());
|
||||
test("new RegExp('1')", function(r) RegExp.prototype.test.call(r, '1'));
|
||||
test("new RegExp('1')", function(r) RegExp.prototype.compile.call(r, '1').toString());
|
||||
test("new RegExp('1')", function(r) assertEq("a1".search(r), 1));
|
||||
test("new RegExp('1')", function(r) assertEq("a1".match(r)[0], '1'));
|
||||
test("new RegExp('1')", function(r) assertEq("a1".replace(r, 'A'), 'aA'));
|
||||
test("new RegExp('1')", function(r) assertEq(String("a1b".split(r)), "a,b"));
|
||||
test("new RegExp('1')", function(r) assertEq(r, RegExp(r)));
|
||||
test("new RegExp('1')", function(r) assertEq(String(new RegExp(r)), String(r)));
|
||||
test("new RegExp('1')", function(r) assertEq(String(/x/.compile(r)), String(r)));
|
||||
test("new WeakMap()", function(w) WeakMap.prototype.has.call(w, {}));
|
||||
test("new WeakMap()", function(w) WeakMap.prototype.get.call(w, {}));
|
||||
test("new WeakMap()", function(w) WeakMap.prototype.delete.call(w, {}));
|
||||
|
7
js/src/jit-test/tests/basic/testLocaleCompare.js
Normal file
7
js/src/jit-test/tests/basic/testLocaleCompare.js
Normal file
@ -0,0 +1,7 @@
|
||||
assertEq("a".localeCompare(), 0);
|
||||
assertEq("a".localeCompare("b"), -1);
|
||||
assertEq("a".localeCompare("b", "a"), -1);
|
||||
assertEq("b".localeCompare("a"), 1);
|
||||
assertEq("b".localeCompare("a", "b"), 1);
|
||||
assertEq("a".localeCompare("a"), 0);
|
||||
assertEq("a".localeCompare("a", "b", "c"), 0);
|
@ -0,0 +1,9 @@
|
||||
// Debugger.prototype.getNewestFrame() ignores dummy frames.
|
||||
// See bug 678086.
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
g.f = function () { return dbg.getNewestFrame(); };
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
var fw = gw.getOwnPropertyDescriptor("f").value;
|
||||
assertEq(fw.call().return, null);
|
@ -1438,6 +1438,9 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
|
||||
}
|
||||
|
||||
case JSOP_THROW:
|
||||
case JSOP_RETURN:
|
||||
case JSOP_STOP:
|
||||
case JSOP_RETRVAL:
|
||||
mergeAllExceptionTargets(cx, values, exceptionTargets);
|
||||
break;
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "jscompat.h"
|
||||
#include "jscpucfg.h"
|
||||
#include "jspubtd.h"
|
||||
#include "jsstdint.h"
|
||||
#include "jstypes.h"
|
||||
#include "jsval.h"
|
||||
#include "jsxdrapi.h"
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "tests.h"
|
||||
#include "jsatom.h"
|
||||
|
||||
#include "vm/String.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
BEGIN_TEST(testAtomizedIsNotInterned)
|
||||
|
@ -705,7 +705,7 @@ JSRuntime::JSRuntime()
|
||||
tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
execAlloc_(NULL),
|
||||
bumpAlloc_(NULL),
|
||||
repCache_(NULL),
|
||||
reCache_(NULL),
|
||||
nativeStackBase(0),
|
||||
nativeStackQuota(0),
|
||||
interpreterFrames(NULL),
|
||||
@ -857,7 +857,7 @@ JSRuntime::~JSRuntime()
|
||||
|
||||
delete_<JSC::ExecutableAllocator>(execAlloc_);
|
||||
delete_<WTF::BumpPointerAllocator>(bumpAlloc_);
|
||||
JS_ASSERT(!repCache_);
|
||||
JS_ASSERT(!reCache_);
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
|
||||
|
@ -3567,9 +3567,7 @@ static JSBool
|
||||
array_isArray(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
bool isArray = args.length() > 0 &&
|
||||
args[0].isObject() &&
|
||||
ObjectClassIs(args[0].toObject(), ESClass_Array, cx);
|
||||
bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx);
|
||||
args.rval().setBoolean(isArray);
|
||||
return true;
|
||||
}
|
||||
|
@ -51,7 +51,6 @@
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
#include "js/HashTable.h"
|
||||
#include "vm/String.h"
|
||||
|
||||
struct JSIdArray {
|
||||
jsint length;
|
||||
@ -219,22 +218,11 @@ struct AtomHasher
|
||||
const JSAtom *atom; /* Optional. */
|
||||
|
||||
Lookup(const jschar *chars, size_t length) : chars(chars), length(length), atom(NULL) {}
|
||||
Lookup(const JSAtom *atom) : chars(atom->chars()), length(atom->length()), atom(atom) {}
|
||||
inline Lookup(const JSAtom *atom);
|
||||
};
|
||||
|
||||
static HashNumber hash(const Lookup &l) {
|
||||
return HashChars(l.chars, l.length);
|
||||
}
|
||||
|
||||
static bool match(const AtomStateEntry &entry, const Lookup &lookup) {
|
||||
JSAtom *key = entry.asPtr();
|
||||
|
||||
if (lookup.atom)
|
||||
return lookup.atom == key;
|
||||
if (key->length() != lookup.length)
|
||||
return false;
|
||||
return PodEqual(key->chars(), lookup.chars, lookup.length);
|
||||
}
|
||||
static HashNumber hash(const Lookup &l) { return HashChars(l.chars, l.length); }
|
||||
static inline bool match(const AtomStateEntry &entry, const Lookup &lookup);
|
||||
};
|
||||
|
||||
typedef HashSet<AtomStateEntry, AtomHasher, SystemAllocPolicy> AtomSet;
|
||||
@ -262,6 +250,8 @@ enum FlationCoding
|
||||
CESU8Encoding
|
||||
};
|
||||
|
||||
class PropertyName;
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
struct JSAtomState
|
||||
@ -558,6 +548,17 @@ js_InitCommonAtoms(JSContext *cx);
|
||||
extern void
|
||||
js_FinishCommonAtoms(JSContext *cx);
|
||||
|
||||
namespace js {
|
||||
|
||||
/* N.B. must correspond to boolean tagging behavior. */
|
||||
enum InternBehavior
|
||||
{
|
||||
DoNotInternAtom = false,
|
||||
InternAtom = true
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
extern JSAtom *
|
||||
js_Atomize(JSContext *cx, const char *bytes, size_t length,
|
||||
js::InternBehavior ib = js::DoNotInternAtom,
|
||||
@ -567,6 +568,9 @@ extern JSAtom *
|
||||
js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length,
|
||||
js::InternBehavior ib = js::DoNotInternAtom);
|
||||
|
||||
extern JSAtom *
|
||||
js_AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom);
|
||||
|
||||
/*
|
||||
* Return an existing atom for the given char array or null if the char
|
||||
* sequence is currently not atomized.
|
||||
|
@ -40,13 +40,14 @@
|
||||
#ifndef jsatominlines_h___
|
||||
#define jsatominlines_h___
|
||||
|
||||
#include "mozilla/RangedPtr.h"
|
||||
|
||||
#include "jsatom.h"
|
||||
#include "jsnum.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsstr.h"
|
||||
|
||||
#include "mozilla/RangedPtr.h"
|
||||
#include "vm/String.h"
|
||||
|
||||
inline JSAtom *
|
||||
js::AtomStateEntry::asPtr() const
|
||||
{
|
||||
@ -196,6 +197,22 @@ IdToString(JSContext *cx, jsid id)
|
||||
return ToStringSlow(cx, IdToValue(id))->ensureFlat(cx);
|
||||
}
|
||||
|
||||
inline
|
||||
AtomHasher::Lookup::Lookup(const JSAtom *atom)
|
||||
: chars(atom->chars()), length(atom->length()), atom(atom)
|
||||
{}
|
||||
|
||||
inline bool
|
||||
AtomHasher::match(const AtomStateEntry &entry, const Lookup &lookup)
|
||||
{
|
||||
JSAtom *key = entry.asPtr();
|
||||
if (lookup.atom)
|
||||
return lookup.atom == key;
|
||||
if (key->length() != lookup.length)
|
||||
return false;
|
||||
return PodEqual(key->chars(), lookup.chars, lookup.length);
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* jsatominlines_h___ */
|
||||
|
@ -407,7 +407,9 @@ Valueify(const JSClass *c)
|
||||
* Enumeration describing possible values of the [[Class]] internal property
|
||||
* value of objects.
|
||||
*/
|
||||
enum ESClassValue { ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boolean };
|
||||
enum ESClassValue {
|
||||
ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boolean, ESClass_RegExp
|
||||
};
|
||||
|
||||
/*
|
||||
* Return whether the given object has the given [[Class]] internal property
|
||||
@ -418,6 +420,10 @@ enum ESClassValue { ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boole
|
||||
inline bool
|
||||
ObjectClassIs(JSObject &obj, ESClassValue classValue, JSContext *cx);
|
||||
|
||||
/* Just a helper that checks v.isObject before calling ObjectClassIs. */
|
||||
inline bool
|
||||
IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
@ -148,22 +148,21 @@ JSRuntime::createBumpPointerAllocator(JSContext *cx)
|
||||
return bumpAlloc_;
|
||||
}
|
||||
|
||||
RegExpPrivateCache *
|
||||
JSRuntime::createRegExpPrivateCache(JSContext *cx)
|
||||
RegExpCache *
|
||||
JSRuntime::createRegExpCache(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(!repCache_);
|
||||
JS_ASSERT(!reCache_);
|
||||
JS_ASSERT(cx->runtime == this);
|
||||
|
||||
RegExpPrivateCache *newCache = new_<RegExpPrivateCache>(this);
|
||||
|
||||
RegExpCache *newCache = new_<RegExpCache>(this);
|
||||
if (!newCache || !newCache->init()) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
delete_<RegExpPrivateCache>(newCache);
|
||||
delete_<RegExpCache>(newCache);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
repCache_ = newCache;
|
||||
return repCache_;
|
||||
reCache_ = newCache;
|
||||
return reCache_;
|
||||
}
|
||||
|
||||
JSScript *
|
||||
@ -1173,8 +1172,8 @@ JSRuntime::purge(JSContext *cx)
|
||||
/* FIXME: bug 506341 */
|
||||
propertyCache.purge(cx);
|
||||
|
||||
delete_<RegExpPrivateCache>(repCache_);
|
||||
repCache_ = NULL;
|
||||
delete_<RegExpCache>(reCache_);
|
||||
reCache_ = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -99,6 +99,11 @@ class InterpreterFrames;
|
||||
class ScriptOpcodeCounts;
|
||||
struct ScriptOpcodeCountsPair;
|
||||
|
||||
typedef HashMap<JSAtom *,
|
||||
detail::RegExpCacheValue,
|
||||
DefaultHasher<JSAtom *>,
|
||||
RuntimeAllocPolicy> RegExpCache;
|
||||
|
||||
/*
|
||||
* GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
|
||||
* given pc in a script. We use the script->code pointer to tag the cache,
|
||||
@ -218,11 +223,11 @@ struct JSRuntime : js::RuntimeFriendFields
|
||||
*/
|
||||
JSC::ExecutableAllocator *execAlloc_;
|
||||
WTF::BumpPointerAllocator *bumpAlloc_;
|
||||
js::RegExpPrivateCache *repCache_;
|
||||
js::RegExpCache *reCache_;
|
||||
|
||||
JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx);
|
||||
WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx);
|
||||
js::RegExpPrivateCache *createRegExpPrivateCache(JSContext *cx);
|
||||
js::RegExpCache *createRegExpCache(JSContext *cx);
|
||||
|
||||
public:
|
||||
JSC::ExecutableAllocator *getExecutableAllocator(JSContext *cx) {
|
||||
@ -231,11 +236,11 @@ struct JSRuntime : js::RuntimeFriendFields
|
||||
WTF::BumpPointerAllocator *getBumpPointerAllocator(JSContext *cx) {
|
||||
return bumpAlloc_ ? bumpAlloc_ : createBumpPointerAllocator(cx);
|
||||
}
|
||||
js::RegExpPrivateCache *maybeRegExpPrivateCache() {
|
||||
return repCache_;
|
||||
js::RegExpCache *maybeRegExpCache() {
|
||||
return reCache_;
|
||||
}
|
||||
js::RegExpPrivateCache *getRegExpPrivateCache(JSContext *cx) {
|
||||
return repCache_ ? repCache_ : createRegExpPrivateCache(cx);
|
||||
js::RegExpCache *getRegExpCache(JSContext *cx) {
|
||||
return reCache_ ? reCache_ : createRegExpCache(cx);
|
||||
}
|
||||
|
||||
/* Base address of the native stack for the current thread. */
|
||||
|
@ -246,7 +246,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
|
||||
|
||||
/* Don't unwrap an outer window proxy. */
|
||||
if (!obj->getClass()->ext.innerObject) {
|
||||
obj = UnwrapObject(&vp->toObject(), &flags);
|
||||
obj = UnwrapObject(&vp->toObject(), true, &flags);
|
||||
vp->setObject(*obj);
|
||||
if (obj->compartment() == this)
|
||||
return true;
|
||||
|
@ -45,7 +45,6 @@
|
||||
* JS debugger API.
|
||||
*/
|
||||
#include "jsapi.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsprvtd.h"
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
|
@ -336,6 +336,11 @@ struct Object {
|
||||
}
|
||||
};
|
||||
|
||||
struct Atom {
|
||||
size_t _;
|
||||
const jschar *chars;
|
||||
};
|
||||
|
||||
} /* namespace shadow */
|
||||
|
||||
extern JS_FRIEND_DATA(js::Class) AnyNameClass;
|
||||
@ -462,6 +467,18 @@ GetObjectShape(JSObject *obj)
|
||||
return reinterpret_cast<Shape *>(shape);
|
||||
}
|
||||
|
||||
inline const jschar *
|
||||
GetAtomChars(JSAtom *atom)
|
||||
{
|
||||
return reinterpret_cast<shadow::Atom *>(atom)->chars;
|
||||
}
|
||||
|
||||
inline JSLinearString *
|
||||
AtomToLinearString(JSAtom *atom)
|
||||
{
|
||||
return reinterpret_cast<JSLinearString *>(atom);
|
||||
}
|
||||
|
||||
static inline js::PropertyOp
|
||||
CastAsJSPropertyOp(JSObject *object)
|
||||
{
|
||||
|
@ -5674,6 +5674,9 @@ JSObject::splicePrototype(JSContext *cx, JSObject *proto)
|
||||
*/
|
||||
JS_ASSERT_IF(cx->typeInferenceEnabled(), hasSingletonType());
|
||||
|
||||
/* Inner objects may not appear on prototype chains. */
|
||||
JS_ASSERT_IF(proto, !proto->getClass()->ext.outerObject);
|
||||
|
||||
/*
|
||||
* Force type instantiation when splicing lazy types. This may fail,
|
||||
* in which case inference will be disabled for the compartment.
|
||||
|
@ -1164,6 +1164,9 @@ inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown)
|
||||
{
|
||||
PodZero(this);
|
||||
|
||||
/* Inner objects may not appear on prototype chains. */
|
||||
JS_ASSERT_IF(proto, !proto->getClass()->ext.outerObject);
|
||||
|
||||
this->proto = proto;
|
||||
|
||||
if (function)
|
||||
|
@ -1003,10 +1003,6 @@ EnterWith(JSContext *cx, jsint stackIndex)
|
||||
if (!parent)
|
||||
return JS_FALSE;
|
||||
|
||||
OBJ_TO_INNER_OBJECT(cx, obj);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
|
||||
JSObject *withobj = WithObject::create(cx, fp, *obj, *parent,
|
||||
sp + stackIndex - fp->base());
|
||||
if (!withobj)
|
||||
|
@ -186,26 +186,15 @@ obj_setProto(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
|
||||
}
|
||||
|
||||
if (!vp->isObjectOrNull())
|
||||
return JS_TRUE;
|
||||
return true;
|
||||
|
||||
JSObject *pobj = vp->toObjectOrNull();
|
||||
if (pobj) {
|
||||
/*
|
||||
* Innerize pobj here to avoid sticking unwanted properties on the
|
||||
* outer object. This ensures that any with statements only grant
|
||||
* access to the inner object.
|
||||
*/
|
||||
OBJ_TO_INNER_OBJECT(cx, pobj);
|
||||
if (!pobj)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
uintN attrs;
|
||||
id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
|
||||
if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs))
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
|
||||
return SetProto(cx, obj, pobj, JS_TRUE);
|
||||
return SetProto(cx, obj, pobj, true);
|
||||
}
|
||||
|
||||
#else /* !JS_HAS_OBJ_PROTO_PROP */
|
||||
@ -3553,6 +3542,19 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &
|
||||
js_memcpy(tmp, a, size);
|
||||
js_memcpy(a, b, size);
|
||||
js_memcpy(b, tmp, size);
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
/*
|
||||
* Trigger post barriers for fixed slots. JSObject bits are barriered
|
||||
* below, in common with the other case.
|
||||
*/
|
||||
for (size_t i = 0; i < a->numFixedSlots(); ++i) {
|
||||
HeapValue *slotA = &a->getFixedSlotRef(i);
|
||||
HeapValue *slotB = &b->getFixedSlotRef(i);
|
||||
HeapValue::writeBarrierPost(*slotA, slotA);
|
||||
HeapValue::writeBarrierPost(*slotB, slotB);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/*
|
||||
* If the objects are of differing sizes, use the space we reserved
|
||||
@ -3607,6 +3609,13 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &
|
||||
reserved.newaslots = NULL;
|
||||
reserved.newbslots = NULL;
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
Shape::writeBarrierPost(a->shape_, &a->shape_);
|
||||
Shape::writeBarrierPost(b->shape_, &b->shape_);
|
||||
types::TypeObject::writeBarrierPost(a->type_, &a->type_);
|
||||
types::TypeObject::writeBarrierPost(b->type_, &b->type_);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1953,11 +1953,20 @@ ObjectClassIs(JSObject &obj, ESClassValue classValue, JSContext *cx)
|
||||
case ESClass_Number: return obj.isNumber();
|
||||
case ESClass_String: return obj.isString();
|
||||
case ESClass_Boolean: return obj.isBoolean();
|
||||
case ESClass_RegExp: return obj.isRegExp();
|
||||
}
|
||||
JS_NOT_REACHED("bad classValue");
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx)
|
||||
{
|
||||
if (!v.isObject())
|
||||
return false;
|
||||
return ObjectClassIs(v.toObject(), classValue, cx);
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
ValueIsSpecial(JSObject *obj, Value *propval, SpecialId *sidp, JSContext *cx)
|
||||
{
|
||||
|
@ -271,9 +271,7 @@ struct JSCodeSpec {
|
||||
uint8_t prec; /* operator precedence */
|
||||
uint32_t format; /* immediate operand format */
|
||||
|
||||
#ifdef __cplusplus
|
||||
uint32_t type() const { return JOF_TYPE(format); }
|
||||
#endif
|
||||
};
|
||||
|
||||
extern const JSCodeSpec js_CodeSpec[];
|
||||
@ -359,7 +357,6 @@ js_GetIndexFromBytecode(JSScript *script, jsbytecode *pc, ptrdiff_t pcoff);
|
||||
(dbl) = (script)->getConst(index_).toDouble(); \
|
||||
JS_END_MACRO
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace js {
|
||||
|
||||
extern uintN
|
||||
@ -369,7 +366,6 @@ extern uintN
|
||||
StackDefs(JSScript *script, jsbytecode *pc);
|
||||
|
||||
} /* namespace js */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* Decompilers, for script, function, and expression pretty-printing.
|
||||
@ -429,7 +425,6 @@ JS_END_EXTERN_C
|
||||
#define JSDVG_IGNORE_STACK 0
|
||||
#define JSDVG_SEARCH_STACK 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
/*
|
||||
* Get the length of variable-length bytecode like JSOP_TABLESWITCH.
|
||||
*/
|
||||
@ -701,9 +696,8 @@ class OpcodeCounts
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#if defined(DEBUG) && defined(__cplusplus)
|
||||
#if defined(DEBUG)
|
||||
/*
|
||||
* Disassemblers, for debugging only.
|
||||
*/
|
||||
|
@ -301,6 +301,13 @@ ProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
|
||||
return fun_toStringHelper(cx, &fval.toObject(), indent);
|
||||
}
|
||||
|
||||
RegExpShared *
|
||||
ProxyHandler::regexp_toShared(JSContext *cx, JSObject *proxy)
|
||||
{
|
||||
JS_NOT_REACHED("This should have been a wrapped regexp");
|
||||
return (RegExpShared *)NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
ProxyHandler::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
|
||||
{
|
||||
@ -946,6 +953,14 @@ Proxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
|
||||
return GetProxyHandler(proxy)->fun_toString(cx, proxy, indent);
|
||||
}
|
||||
|
||||
RegExpShared *
|
||||
Proxy::regexp_toShared(JSContext *cx, JSObject *proxy)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return NULL);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return GetProxyHandler(proxy)->regexp_toShared(cx, proxy);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
|
||||
{
|
||||
|
@ -84,6 +84,7 @@ class JS_FRIEND_API(ProxyHandler) {
|
||||
virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx);
|
||||
virtual JSString *obj_toString(JSContext *cx, JSObject *proxy);
|
||||
virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
|
||||
virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy);
|
||||
virtual bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp);
|
||||
virtual void finalize(JSContext *cx, JSObject *proxy);
|
||||
virtual void trace(JSTracer *trc, JSObject *proxy);
|
||||
@ -137,6 +138,7 @@ class Proxy {
|
||||
static bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx);
|
||||
static JSString *obj_toString(JSContext *cx, JSObject *proxy);
|
||||
static JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
|
||||
static RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy);
|
||||
static bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp);
|
||||
};
|
||||
|
||||
|
@ -126,14 +126,14 @@ struct Class;
|
||||
class RegExpObject;
|
||||
class RegExpMatcher;
|
||||
class RegExpObjectBuilder;
|
||||
class RegExpShared;
|
||||
class RegExpStatics;
|
||||
class MatchPairs;
|
||||
|
||||
namespace detail {
|
||||
|
||||
class RegExpPrivate;
|
||||
class RegExpPrivateCode;
|
||||
class RegExpPrivateCacheValue;
|
||||
class RegExpCode;
|
||||
class RegExpCacheValue;
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
@ -221,12 +221,6 @@ class BreakpointSite;
|
||||
class Debugger;
|
||||
class WatchpointMap;
|
||||
|
||||
typedef HashMap<JSAtom *,
|
||||
detail::RegExpPrivateCacheValue,
|
||||
DefaultHasher<JSAtom *>,
|
||||
RuntimeAllocPolicy>
|
||||
RegExpPrivateCache;
|
||||
|
||||
/*
|
||||
* Env is the type of what ES5 calls "lexical environments" (runtime
|
||||
* activations of lexical scopes). This is currently just JSObject, and is
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "jsdbgapi.h"
|
||||
#include "jsclist.h"
|
||||
#include "jsinfer.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsscope.h"
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
|
726
js/src/jsstr.cpp
726
js/src/jsstr.cpp
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user