зеркало из https://github.com/mozilla/gecko-dev.git
Merge last PGO-green changeset of mozilla-inbound to mozilla-central
This commit is contained in:
Коммит
e302449899
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
|
@ -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)
|
|
@ -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>
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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 = \
|
||||
|
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
|||
X-Content-Security-Policy: default-src 'self', allow *
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
|||
X-Content-Security-Policy: default-src 'self' , allow *
|
|
@ -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) { }
|
||||
|
|
|
@ -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>
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
После Ширина: | Высота: | Размер: 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
|
||||
|
|
|
@ -2806,12 +2806,14 @@ arm*-*)
|
|||
AC_DEFINE(JS_NUNBOX32)
|
||||
;;
|
||||
sparc*-*)
|
||||
if test ! "$HAVE_64BIT_OS" ; then
|
||||
ENABLE_METHODJIT=1
|
||||
ENABLE_MONOIC=1
|
||||
ENABLE_POLYIC=1
|
||||
ENABLE_METHODJIT_TYPED_ARRAY=1
|
||||
AC_DEFINE(JS_CPU_SPARC)
|
||||
AC_DEFINE(JS_NUNBOX32)
|
||||
fi
|
||||
;;
|
||||
mips*-*)
|
||||
ENABLE_METHODJIT=1
|
||||
|
|
|
@ -983,7 +983,7 @@ static bool
|
|||
EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
|
||||
{
|
||||
const size_t len = 1 + UINT32_INDEX_LEN;
|
||||
JS_ASSERT(js_CodeSpec[op].length == len);
|
||||
JS_ASSERT(size_t(js_CodeSpec[op].length) == len);
|
||||
ptrdiff_t offset = EmitCheck(cx, bce, len);
|
||||
if (offset < 0)
|
||||
return false;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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, {}));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -701,7 +701,7 @@ JSRuntime::JSRuntime()
|
|||
tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
execAlloc_(NULL),
|
||||
bumpAlloc_(NULL),
|
||||
repCache_(NULL),
|
||||
reCache_(NULL),
|
||||
nativeStackBase(0),
|
||||
nativeStackQuota(0),
|
||||
interpreterFrames(NULL),
|
||||
|
@ -849,7 +849,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
|
||||
|
|
|
@ -95,6 +95,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,
|
||||
|
@ -214,11 +219,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) {
|
||||
|
@ -227,11 +232,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. */
|
||||
|
|
|
@ -216,7 +216,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)
|
||||
{
|
||||
|
|
|
@ -5662,6 +5662,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.
|
||||
|
|
|
@ -1159,6 +1159,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)
|
||||
|
|
|
@ -994,10 +994,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
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -54,10 +54,10 @@
|
|||
#endif
|
||||
#include "jscompartment.h"
|
||||
|
||||
#include "vm/RegExpObject.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/RegExpObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
|
@ -76,13 +76,13 @@ js::IsWrapper(const JSObject *wrapper)
|
|||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js::UnwrapObject(JSObject *wrapped, uintN *flagsp)
|
||||
js::UnwrapObject(JSObject *wrapped, bool stopAtOuter, uintN *flagsp)
|
||||
{
|
||||
uintN flags = 0;
|
||||
while (wrapped->isWrapper()) {
|
||||
flags |= static_cast<Wrapper *>(GetProxyHandler(wrapped))->flags();
|
||||
wrapped = GetProxyPrivate(wrapped).toObjectOrNull();
|
||||
if (wrapped->getClass()->ext.innerObject)
|
||||
if (stopAtOuter && wrapped->getClass()->ext.innerObject)
|
||||
break;
|
||||
}
|
||||
if (flagsp)
|
||||
|
@ -334,6 +334,12 @@ Wrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent)
|
|||
return str;
|
||||
}
|
||||
|
||||
RegExpShared *
|
||||
Wrapper::regexp_toShared(JSContext *cx, JSObject *wrapper)
|
||||
{
|
||||
return wrappedObject(wrapper)->asRegExp().getShared(cx);
|
||||
}
|
||||
|
||||
bool
|
||||
Wrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp)
|
||||
{
|
||||
|
@ -881,5 +887,13 @@ SecurityWrapper<Base>::objectClassIs(JSObject *obj, ESClassValue classValue, JSC
|
|||
return Base::objectClassIs(obj, classValue, cx);
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
RegExpShared *
|
||||
SecurityWrapper<Base>::regexp_toShared(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
return Base::regexp_toShared(cx, obj);
|
||||
}
|
||||
|
||||
|
||||
template class js::SecurityWrapper<Wrapper>;
|
||||
template class js::SecurityWrapper<CrossCompartmentWrapper>;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче