Merge from mozilla-central.

This commit is contained in:
David Anderson 2012-02-03 14:40:50 -08:00
commit c9cedc55d9
281 changed files with 15432 additions and 6715 deletions

View File

@ -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 \

View File

@ -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;
};

View File

@ -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

View File

@ -0,0 +1,221 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Eitan Isaacson <eitan@monotonous.org> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
typedef short TextBoundaryType;
interface nsIAccessible;
interface nsIAccessibleText;
interface nsIAccessibleTraversalRule;
interface nsIAccessiblePivotObserver;
/**
* The pivot interface encapsulates a reference to a single place in an accessible
* subtree. The pivot is a point or a range in the accessible tree. This interface
* provides traversal methods to move the pivot to next/prev state that complies
* to a given rule.
*/
[scriptable, uuid(689058ae-e301-444f-acb0-b5c2b189f350)]
interface nsIAccessiblePivot : nsISupports
{
const TextBoundaryType CHAR_BOUNDARY = 0;
const TextBoundaryType WORD_BOUNDARY = 1;
const TextBoundaryType LINE_BOUNDARY = 2;
const TextBoundaryType ATTRIBUTE_RANGE_BOUNDARY = 3;
/**
* The accessible the pivot is currently pointed at.
*/
attribute nsIAccessible position;
/**
* The root of the subtree in which the pivot traverses.
*/
readonly attribute nsIAccessible root;
/**
* The start offset of the text range the pivot points at, otherwise -1.
*/
readonly attribute long startOffset;
/**
* The end offset of the text range the pivot points at, otherwise -1.
*/
readonly attribute long endOffset;
/**
* Set the pivot's text range in a text accessible.
*
* @param aTextAccessible [in] the text accessible that contains the desired
* range.
* @param aStartOffset [in] the start offset to set.
* @param aEndOffset [in] the end offset to set.
* @throws NS_ERROR_INVALID_ARG when the offset exceeds the accessible's
* character count.
*/
void setTextRange(in nsIAccessibleText aTextAccessible,
in long aStartOffset, in long aEndOffset);
/**
* Move pivot to next object complying to given traversal rule.
*
* @param aRule [in] traversal rule to use.
* @return true on success, false if there are no new nodes to traverse to.
*/
boolean moveNext(in nsIAccessibleTraversalRule aRule);
/**
* Move pivot to previous object complying to given traversal rule.
*
* @param aRule [in] traversal rule to use.
* @return true on success, false if there are no new nodes to traverse to.
*/
boolean movePrevious(in nsIAccessibleTraversalRule aRule);
/**
* Move pivot to first object in subtree complying to given traversal rule.
*
* @param aRule [in] traversal rule to use.
* @return true on success, false if there are no new nodes to traverse to.
*/
boolean moveFirst(in nsIAccessibleTraversalRule aRule);
/**
* Move pivot to last object in subtree complying to given traversal rule.
*
* @param aRule [in] traversal rule to use.
* @return true on success, false if there are no new nodes to traverse to.
*/
boolean moveLast(in nsIAccessibleTraversalRule aRule);
/**
* Move pivot to next text range.
*
* @param aBoundary [in] type of boundary for next text range, character, word,
* etc.
* @return true on success, false if there are is no more text.
*/
boolean moveNextByText(in TextBoundaryType aBoundary);
/**
* Move pivot to previous text range.
*
* @param aBoundary [in] type of boundary for previous text range, character,
* word, etc.
* @return true on success, false if there are is no more text.
*/
boolean movePreviousByText(in TextBoundaryType aBoundary);
/**
* Add an observer for pivot changes.
*
* @param aObserver [in] the observer object to be notified of pivot changes.
*/
void addObserver(in nsIAccessiblePivotObserver aObserver);
/**
* Remove an observer for pivot changes.
*
* @param aObserver [in] the observer object to remove from being notified.
*/
void removeObserver(in nsIAccessiblePivotObserver aObserver);
};
/**
* An observer interface for pivot changes.
*/
[scriptable, uuid(b6508c5e-c081-467d-835c-613eedf9ee9b)]
interface nsIAccessiblePivotObserver : nsISupports
{
/**
* Called when the pivot changes.
*
* @param aPivot [in] the pivot that has changed.
* @param aOldAccessible [in] the old pivot position before the change, or null.
* @param aOldStart [in] the old start offset, or -1.
* @param aOldEnd [in] the old end offset, or -1.
*/
void onPivotChanged(in nsIAccessiblePivot aPivot,
in nsIAccessible aOldAccessible,
in long aOldStart, in long aOldEnd);
};
[scriptable, uuid(307d98b6-dba9-49cf-ba17-ef8b053044eb)]
interface nsIAccessibleTraversalRule : nsISupports
{
/* Ignore this accessible object */
const unsigned short FILTER_IGNORE = 0x0;
/* Accept this accessible object */
const unsigned short FILTER_MATCH = 0x1;
/* Don't traverse accessibles children */
const unsigned short FILTER_IGNORE_SUBTREE = 0x2;
/* Pre-filters */
const unsigned long PREFILTER_INVISIBLE = 0x00000001;
const unsigned long PREFILTER_OFFSCREEN = 0x00000002;
const unsigned long PREFILTER_NOT_FOCUSABLE = 0x00000004;
/**
* Pre-filter bitfield to filter out obviously ignorable nodes and lighten
* the load on match().
*/
readonly attribute unsigned long preFilter;
/**
* Retrieve a list of roles that the traversal rule should test for. Any node
* with a role not in this list will automatically be ignored. An empty list
* will match all roles. It should be assumed that this method is called once
* at the start of a traversal, so changing the method's return result after
* that would have no affect.
*
* @param aRoles [out] an array of the roles to match.
* @param aCount [out] the length of the array.
*/
void getMatchRoles([array, size_is(aCount)]out unsigned long aRoles,
[retval]out unsigned long aCount);
/**
* Determines if a given accessible is to be accepted in our traversal rule
*
* @param aAccessible [in] accessible to examine.
* @return a bitfield of FILTER_MATCH and FILTER_IGNORE_SUBTREE.
*/
unsigned short match(in nsIAccessible aAccessible);
};

View File

@ -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);
};

View File

@ -65,6 +65,7 @@ CPPSRCS = \
nsAccUtils.cpp \
nsAccessibilityService.cpp \
nsAccessible.cpp \
nsAccessiblePivot.cpp \
nsAccTreeWalker.cpp \
nsBaseWidgetAccessible.cpp \
nsEventShell.cpp \

View File

@ -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,

View File

@ -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
};
/**

View File

@ -0,0 +1,526 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Eitan Isaacson <eitan@monotonous.org> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsAccessiblePivot.h"
#include "nsAccessible.h"
#include "nsAccUtils.h"
#include "nsHyperTextAccessible.h"
#include "States.h"
#include "nsArrayUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsISupportsPrimitives.h"
using namespace mozilla::a11y;
/**
* An object that stores a given traversal rule during
*/
class RuleCache
{
public:
RuleCache(nsIAccessibleTraversalRule* aRule) : mRule(aRule),
mAcceptRoles(nsnull) { }
~RuleCache () {
if (mAcceptRoles)
nsMemory::Free(mAcceptRoles);
}
nsresult ApplyFilter(nsAccessible* aAccessible, PRUint16* aResult);
private:
nsCOMPtr<nsIAccessibleTraversalRule> mRule;
PRUint32* mAcceptRoles;
PRUint32 mAcceptRolesLength;
PRUint32 mPreFilter;
};
////////////////////////////////////////////////////////////////////////////////
// nsAccessiblePivot
nsAccessiblePivot::nsAccessiblePivot(nsAccessible* aRoot) :
mRoot(aRoot), mPosition(nsnull),
mStartOffset(-1), mEndOffset(-1)
{
NS_ASSERTION(aRoot, "A root accessible is required");
}
////////////////////////////////////////////////////////////////////////////////
// nsISupports
NS_IMPL_CYCLE_COLLECTION_CLASS(nsAccessiblePivot)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAccessiblePivot)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRoot, nsIAccessible)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mPosition, nsIAccessible)
PRUint32 i, length = tmp->mObservers.Length(); \
for (i = 0; i < length; ++i) {
cb.NoteXPCOMChild(tmp->mObservers.ElementAt(i).get());
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAccessiblePivot)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPosition)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mObservers)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessiblePivot)
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
NS_INTERFACE_MAP_ENTRY(nsAccessiblePivot)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccessiblePivot)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAccessiblePivot)
////////////////////////////////////////////////////////////////////////////////
// nsIAccessiblePivot
NS_IMETHODIMP
nsAccessiblePivot::GetRoot(nsIAccessible** aRoot)
{
NS_ENSURE_ARG_POINTER(aRoot);
NS_IF_ADDREF(*aRoot = mRoot);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::GetPosition(nsIAccessible** aPosition)
{
NS_ENSURE_ARG_POINTER(aPosition);
NS_IF_ADDREF(*aPosition = mPosition);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::SetPosition(nsIAccessible* aPosition)
{
nsRefPtr<nsAccessible> secondPosition;
if (aPosition) {
secondPosition = do_QueryObject(aPosition);
if (!secondPosition || !IsRootDescendant(secondPosition))
return NS_ERROR_INVALID_ARG;
}
// Swap old position with new position, saves us an AddRef/Release.
mPosition.swap(secondPosition);
PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
mStartOffset = mEndOffset = -1;
NotifyPivotChanged(secondPosition, oldStart, oldEnd);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::GetStartOffset(PRInt32* aStartOffset)
{
NS_ENSURE_ARG_POINTER(aStartOffset);
*aStartOffset = mStartOffset;
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::GetEndOffset(PRInt32* aEndOffset)
{
NS_ENSURE_ARG_POINTER(aEndOffset);
*aEndOffset = mEndOffset;
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::SetTextRange(nsIAccessibleText* aTextAccessible,
PRInt32 aStartOffset, PRInt32 aEndOffset)
{
NS_ENSURE_ARG(aTextAccessible);
// Check that start offset is smaller than end offset, and that if a value is
// smaller than 0, both should be -1.
NS_ENSURE_TRUE(aStartOffset <= aEndOffset &&
(aStartOffset >= 0 || (aStartOffset != -1 && aEndOffset != -1)),
NS_ERROR_INVALID_ARG);
nsRefPtr<nsHyperTextAccessible> newPosition = do_QueryObject(aTextAccessible);
if (!newPosition || !IsRootDescendant(newPosition))
return NS_ERROR_INVALID_ARG;
// Make sure the given offsets don't exceed the character count.
PRInt32 charCount = newPosition->CharacterCount();
if (aEndOffset > charCount)
return NS_ERROR_FAILURE;
PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
mStartOffset = aStartOffset;
mEndOffset = aEndOffset;
nsRefPtr<nsAccessible> oldPosition = mPosition.forget();
mPosition = newPosition.forget();
NotifyPivotChanged(oldPosition, oldStart, oldEnd);
return NS_OK;
}
// Traversal functions
NS_IMETHODIMP
nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule, bool* aResult)
{
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
nsresult rv = NS_OK;
nsAccessible* accessible = SearchForward(mPosition, aRule, false, &rv);
NS_ENSURE_SUCCESS(rv, rv);
*aResult = accessible;
if (*aResult)
MovePivotInternal(accessible);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule, bool* aResult)
{
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
nsresult rv = NS_OK;
nsAccessible* accessible = SearchBackward(mPosition, aRule, false, &rv);
NS_ENSURE_SUCCESS(rv, rv);
*aResult = accessible;
if (*aResult)
MovePivotInternal(accessible);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult)
{
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
nsresult rv = NS_OK;
nsAccessible* accessible = SearchForward(mRoot, aRule, true, &rv);
NS_ENSURE_SUCCESS(rv, rv);
*aResult = accessible;
if (*aResult)
MovePivotInternal(accessible);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule, bool* aResult)
{
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
*aResult = false;
nsresult rv = NS_OK;
nsAccessible* lastAccessible = mRoot;
nsAccessible* accessible = nsnull;
// First got to the last accessible in pre-order
while (lastAccessible->HasChildren())
lastAccessible = lastAccessible->LastChild();
// Search backwards from last accessible and find the last occurrence in the doc
accessible = SearchBackward(lastAccessible, aRule, true, &rv);
NS_ENSURE_SUCCESS(rv, rv);
*aResult = accessible;
if (*aResult)
MovePivotInternal(accessible);
return NS_OK;
}
// TODO: Implement
NS_IMETHODIMP
nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary, bool* aResult)
{
NS_ENSURE_ARG(aResult);
*aResult = false;
return NS_ERROR_NOT_IMPLEMENTED;
}
// TODO: Implement
NS_IMETHODIMP
nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult)
{
NS_ENSURE_ARG(aResult);
*aResult = false;
return NS_ERROR_NOT_IMPLEMENTED;
}
// Observer functions
NS_IMETHODIMP
nsAccessiblePivot::AddObserver(nsIAccessiblePivotObserver* aObserver)
{
NS_ENSURE_ARG(aObserver);
mObservers.AppendElement(aObserver);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver* aObserver)
{
NS_ENSURE_ARG(aObserver);
return mObservers.RemoveElement(aObserver) ? NS_OK : NS_ERROR_FAILURE;
}
// Private utility methods
bool
nsAccessiblePivot::IsRootDescendant(nsAccessible* aAccessible)
{
nsAccessible* accessible = aAccessible;
do {
if (accessible == mRoot)
return true;
} while ((accessible = accessible->Parent()));
return false;
}
void
nsAccessiblePivot::MovePivotInternal(nsAccessible* aPosition)
{
nsRefPtr<nsAccessible> oldPosition = mPosition.forget();
mPosition = aPosition;
PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
mStartOffset = mEndOffset = -1;
NotifyPivotChanged(oldPosition, oldStart, oldEnd);
}
nsAccessible*
nsAccessiblePivot::SearchBackward(nsAccessible* aAccessible,
nsIAccessibleTraversalRule* aRule,
bool searchCurrent,
nsresult* rv)
{
*rv = NS_OK;
// Initial position could be unset, in that case return null.
if (!aAccessible)
return nsnull;
RuleCache cache(aRule);
nsAccessible* accessible = aAccessible;
PRUint16 filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
if (searchCurrent) {
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
return accessible;
}
while (accessible != mRoot) {
nsAccessible* parent = accessible->Parent();
PRInt32 idxInParent = accessible->IndexInParent();
while (idxInParent > 0) {
if (!(accessible = parent->GetChildAt(--idxInParent)))
continue;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
nsAccessible* lastChild;
while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
(lastChild = accessible->LastChild())) {
parent = accessible;
accessible = lastChild;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
}
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
return accessible;
}
if (!(accessible = parent))
break;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
return accessible;
}
return nsnull;
}
nsAccessible*
nsAccessiblePivot::SearchForward(nsAccessible* aAccessible,
nsIAccessibleTraversalRule* aRule,
bool searchCurrent,
nsresult* rv)
{
*rv = NS_OK;
// Initial position could be not set, in that case begin search from root.
nsAccessible *accessible = (!aAccessible) ? mRoot.get() : aAccessible;
RuleCache cache(aRule);
PRUint16 filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
if (searchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH))
return accessible;
while (true) {
nsAccessible* firstChild = nsnull;
while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
(firstChild = accessible->FirstChild())) {
accessible = firstChild;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
return accessible;
}
nsAccessible* sibling = nsnull;
nsAccessible* temp = accessible;
do {
if (temp == mRoot)
break;
sibling = temp->NextSibling();
if (sibling)
break;
} while ((temp = temp->Parent()));
if (!sibling)
break;
accessible = sibling;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
return accessible;
}
return nsnull;
}
void
nsAccessiblePivot::NotifyPivotChanged(nsAccessible* aOldPosition,
PRInt32 aOldStart, PRInt32 aOldEnd)
{
nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> >::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
nsIAccessiblePivotObserver* obs = iter.GetNext();
obs->OnPivotChanged(this, aOldPosition, aOldStart, aOldEnd);
}
}
nsresult
RuleCache::ApplyFilter(nsAccessible* aAccessible, PRUint16* aResult)
{
*aResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
if (!mAcceptRoles) {
nsresult rv = mRule->GetMatchRoles(&mAcceptRoles, &mAcceptRolesLength);
NS_ENSURE_SUCCESS(rv, rv);
rv = mRule->GetPreFilter(&mPreFilter);
NS_ENSURE_SUCCESS(rv, rv);
}
if (mPreFilter) {
PRUint64 state = aAccessible->State();
if ((nsIAccessibleTraversalRule::PREFILTER_INVISIBLE & mPreFilter) &&
(state & states::INVISIBLE))
return NS_OK;
if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN & mPreFilter) &&
(state & states::OFFSCREEN))
return NS_OK;
if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
!(state & states::FOCUSABLE))
return NS_OK;
}
if (mAcceptRolesLength > 0) {
PRUint32 accessibleRole = aAccessible->Role();
bool matchesRole = false;
for (PRUint32 idx = 0; idx < mAcceptRolesLength; idx++) {
matchesRole = mAcceptRoles[idx] == accessibleRole;
if (matchesRole)
break;
}
if (!matchesRole)
return NS_OK;
}
return mRule->Match(aAccessible, aResult);
}

View File

@ -0,0 +1,134 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Eitan Isaacson <eitan@monotonous.org> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _nsAccessiblePivot_H_
#define _nsAccessiblePivot_H_
#include "nsIAccessiblePivot.h"
#include "nsAutoPtr.h"
#include "nsTObserverArray.h"
#include "nsCycleCollectionParticipant.h"
class nsAccessible;
class nsIAccessibleTraversalRule;
/**
* Class represents an accessible pivot.
*/
class nsAccessiblePivot: public nsIAccessiblePivot
{
public:
nsAccessiblePivot(nsAccessible* aRoot);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsAccessiblePivot, nsIAccessiblePivot)
NS_DECL_NSIACCESSIBLEPIVOT
/*
* A simple getter for the pivot's position.
*/
nsAccessible* Position() { return mPosition; }
private:
nsAccessiblePivot() MOZ_DELETE;
nsAccessiblePivot(const nsAccessiblePivot&) MOZ_DELETE;
void operator = (const nsAccessiblePivot&) MOZ_DELETE;
/*
* Notify all observers on a pivot change.
*/
void NotifyPivotChanged(nsAccessible* aOldAccessible,
PRInt32 aOldStart, PRInt32 aOldEnd);
/*
* Check to see that the given accessible is in the pivot's subtree.
*/
bool IsRootDescendant(nsAccessible* aAccessible);
/*
* Search in preorder for the first accessible to match the rule.
*/
nsAccessible* SearchForward(nsAccessible* aAccessible,
nsIAccessibleTraversalRule* aRule,
bool searchCurrent,
nsresult* rv);
/*
* Reverse search in preorder for the first accessible to match the rule.
*/
nsAccessible* SearchBackward(nsAccessible* aAccessible,
nsIAccessibleTraversalRule* aRule,
bool searchCurrent,
nsresult* rv);
/*
* Update the pivot, and notify observers.
*/
void MovePivotInternal(nsAccessible* aPosition);
/*
* The root accessible.
*/
nsRefPtr<nsAccessible> mRoot;
/*
* The current pivot position.
*/
nsRefPtr<nsAccessible> mPosition;
/*
* The text start offset ofthe pivot.
*/
PRInt32 mStartOffset;
/*
* The text end offset ofthe pivot.
*/
PRInt32 mEndOffset;
/*
* The list of pivot-changed observers.
*/
nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> > mObservers;
};
#endif

View File

@ -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

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
/**

View File

@ -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
};

View File

@ -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 \

View File

@ -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;

View File

@ -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

View File

@ -0,0 +1,217 @@
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
////////////////////////////////////////////////////////////////////////////////
// Constants
const PREFILTER_INVISIBLE = nsIAccessibleTraversalRule.PREFILTER_INVISIBLE;
const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
////////////////////////////////////////////////////////////////////////////////
// Traversal rules
/**
* Rule object to traverse all focusable nodes and text nodes.
*/
var HeadersTraversalRule =
{
getMatchRoles: function(aRules)
{
aRules.value = [ROLE_HEADING];
return aRules.value.length;
},
preFilter: PREFILTER_INVISIBLE,
match: function(aAccessible)
{
return FILTER_MATCH;
},
QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
}
/**
* Traversal rule for all focusable nodes or leafs.
*/
var ObjectTraversalRule =
{
getMatchRoles: function(aRules)
{
aRules.value = [];
return 0;
},
preFilter: PREFILTER_INVISIBLE,
match: function(aAccessible)
{
var rv = FILTER_IGNORE;
var role = aAccessible.role;
if (hasState(aAccessible, STATE_FOCUSABLE) &&
(role != ROLE_DOCUMENT && role != ROLE_INTERNAL_FRAME))
rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
else if (aAccessible.childCount == 0 &&
role != ROLE_STATICTEXT && aAccessible.name.trim())
rv = FILTER_MATCH;
return rv;
},
QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
};
////////////////////////////////////////////////////////////////////////////////
// Virtual state invokers and checkers
/**
* A checker for virtual cursor changed events.
*/
function virtualCursorChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets)
{
this.__proto__ = new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc);
this.check = function virtualCursorChangedChecker_check(aEvent)
{
var position = aDocAcc.virtualCursor.position;
position.QueryInterface(nsIAccessNode);
var idMatches = position.DOMNode.id == aIdOrNameOrAcc;
var nameMatches = position.name == aIdOrNameOrAcc;
var accMatches = position == aIdOrNameOrAcc;
SimpleTest.ok(idMatches || nameMatches || accMatches, "id or name matches",
"expecting " + aIdOrNameOrAcc + ", got '" +
prettyName(position));
if (aTextOffsets) {
SimpleTest.is(aDocAcc.virtualCursor.startOffset, aTextOffsets[0],
"wrong start offset");
SimpleTest.is(aDocAcc.virtualCursor.endOffset, aTextOffsets[1],
"wrong end offset");
}
};
}
/**
* Set a text range in the pivot and wait for virtual cursor change event.
*
* @param aDocAcc document that manages the virtual cursor
* @param aTextAccessible accessible to set to virtual cursor's position
* @param aTextOffsets start and end offsets of text range to set in virtual
* cursor
*/
function setVirtualCursorRangeInvoker(aDocAcc, aTextAccessible, aTextOffsets)
{
this.invoke = function virtualCursorChangedInvoker_invoke()
{
SimpleTest.info(prettyName(aTextAccessible) + " " + aTextOffsets);
aDocAcc.virtualCursor.setTextRange(aTextAccessible,
aTextOffsets[0],
aTextOffsets[1]);
};
this.getID = function setVirtualCursorRangeInvoker_getID()
{
return "Set offset in " + prettyName(aTextAccessible) +
" to (" + aTextOffsets[0] + ", " + aTextOffsets[1] + ")";
}
this.eventSeq = [
new virtualCursorChangedChecker(aDocAcc, aTextAccessible, aTextOffsets)
];
}
/**
* Move the pivot and wait for virtual cursor change event.
*
* @param aDocAcc document that manages the virtual cursor
* @param aPivotMoveMethod method to test (ie. "moveNext", "moveFirst", etc.)
* @param aRule traversal rule object
* @param aIdOrNameOrAcc id, accessivle or accessible name to expect virtual
* cursor to land on after performing move method.
*/
function setVirtualCursorPosInvoker(aDocAcc, aPivotMoveMethod, aRule,
aIdOrNameOrAcc)
{
this.invoke = function virtualCursorChangedInvoker_invoke()
{
var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule);
SimpleTest.ok((aIdOrNameOrAcc && moved) || (!aIdOrNameOrAcc && !moved),
"moved pivot");
};
this.getID = function setVirtualCursorPosInvoker_getID()
{
return "Do " + (aIdOrNameOrAcc ? "" : "no-op ") + aPivotMoveMethod;
}
if (aIdOrNameOrAcc) {
this.eventSeq = [ new virtualCursorChangedChecker(aDocAcc, aIdOrNameOrAcc) ];
} else {
this.eventSeq = [];
this.unexpectedEventSeq = [
new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
];
}
}
/**
* Add invokers to a queue to test a rule and an expected sequence of element ids
* or accessible names for that rule in the given document.
*
* @param aQueue event queue in which to push invoker sequence.
* @param aDocAcc the managing document of the virtual cursor we are testing
* @param aRule the traversal rule to use in the invokers
* @param aSequence a sequence of accessible names or elemnt ids to expect with
* the given rule in the given document
*/
function queueTraversalSequence(aQueue, aDocAcc, aRule, aSequence)
{
aDocAcc.virtualCursor.position = null;
for (var i = 0; i < aSequence.length; i++) {
var invoker = new setVirtualCursorPosInvoker(aDocAcc, "moveNext",
aRule, aSequence[i]);
aQueue.push(invoker);
}
// No further more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "moveNext", aRule, null));
for (var i = aSequence.length-2; i >= 0; i--) {
var invoker = new setVirtualCursorPosInvoker(aDocAcc, "movePrevious",
aRule, aSequence[i])
aQueue.push(invoker);
}
// No previous more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "movePrevious", aRule, null));
aQueue.push(new setVirtualCursorPosInvoker(
aDocAcc, "moveLast", aRule, aSequence[aSequence.length - 1]));
// No further more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "moveNext", aRule, null));
aQueue.push(new setVirtualCursorPosInvoker(
aDocAcc, "moveFirst", aRule, aSequence[0]));
// No previous more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "movePrevious", aRule, null));
}
/**
* A debug utility for writing proper sequences for queueTraversalSequence.
*/
function dumpTraversalSequence(aPivot, aRule)
{
var sequence = []
if (aPivot.moveFirst(aRule)) {
do {
sequence.push("'" + prettyName(aPivot.position) + "'");
} while (aPivot.moveNext(aRule))
}
SimpleTest.info("\n[" + sequence.join(", ") + "]\n");
}

View File

@ -0,0 +1,54 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Eitan Isaacson <eitan@monotonous.org> (original author)
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = accessible/pivot
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
doc_virtualcursor.html \
test_virtualcursor.html \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<title>Pivot test document</title>
<meta charset="utf-8" />
</head>
<body>
<h1 id="heading-1-1">Main Title</h1>
<h2 id="heading-2-1">First Section Title</h2>
<p id="paragraph-1">
Lorem ipsum <strong>dolor</strong> sit amet. Integer vitae urna
leo, id <a href="#">semper</a> nulla.
</p>
<h2 id="heading-2-2">Second Section Title</h2>
<p id="paragraph-2">
Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
<iframe
src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
</iframe>
<p>
<a href="http://mozilla.org" title="Link 1 title">Link 1</a>
<a href="http://mozilla.org" title="Link 2 title">Link 2</a>
<a href="http://mozilla.org" title="Link 3 title">Link 3</a>
</p>
</body>
</html>

View File

@ -0,0 +1,95 @@
<!DOCTYPE html>
<html>
<head>
<title>Tests pivot functionality in virtual cursors</title>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
</script>
<script type="application/javascript" src="../common.js"></script>
<script type="application/javascript" src="../browser.js"></script>
<script type="application/javascript" src="../events.js"></script>
<script type="application/javascript" src="../role.js"></script>
<script type="application/javascript" src="../states.js"></script>
<script type="application/javascript" src="../pivot.js"></script>
<script type="application/javascript">
var gBrowserWnd = null;
var gQueue = null;
function doTest()
{
var rootAcc = getRootAccessible(browserWindow().document);
try {
rootAcc.QueryInterface(nsIAccessibleCursorable);
} catch (e) {
ok(false, "Root accessible does not support nsIAccessibleCursorable");
}
var doc = currentTabDocument();
var docAcc = getAccessible(doc, [nsIAccessibleDocument,
nsIAccessibleCursorable]);
// Test that embedded documents don't have their own virtual cursor.
is(docAcc.childDocumentCount, 1, "Expecting one child document");
var childDoc = docAcc.getChildDocumentAt(0);
var supportsVC = true;
try {
childDoc.QueryInterface(nsIAccessibleCursorable);
} catch (e) {
supportsVC = false;
}
ok(!supportsVC, "no nsIAccessibleCursorable support in child document");
gQueue = new eventQueue();
gQueue.onFinish = function onFinish()
{
closeBrowserWindow();
}
queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule,
['heading-1-1', 'heading-2-1', 'heading-2-2']);
queueTraversalSequence(
gQueue, docAcc, ObjectTraversalRule,
['Main Title', 'First Section Title', 'Lorem ipsum ',
'dolor', ' sit amet. Integer vitae urna leo, id ',
'semper', ' nulla. ', 'Second Section Title',
'Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.',
'An ', 'embedded', ' document.', 'Link 1', 'Link 2', 'Link 3']);
// Just a random smoke test to see if our setTextRange works.
gQueue.push(
new setVirtualCursorRangeInvoker(
docAcc,
getAccessible(doc.getElementById('paragraph-2'), nsIAccessibleText),
[2,6]));
gQueue.invoke();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(function () {
/* We open a new browser because we need to test with a top-level content
document. */
openBrowserWindow(
doTest,
"chrome://mochitests/content/a11y/accessible/pivot/doc_virtualcursor.html");
});
</script>
</head>
<body id="body">
<a target="_blank"
title="Introduce virtual cursor/soft focus functionality to a11y API"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=698823">Mozilla Bug 698823</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -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);

View File

@ -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() {
}

View File

@ -5,7 +5,9 @@
// - https://github.com/mozilla/gcli/blob/master/docs/index.md
// - https://wiki.mozilla.org/DevTools/Features/GCLI
Components.utils.import("resource:///modules/gcli.jsm");
let tmp = {};
Components.utils.import("resource:///modules/gcli.jsm", tmp);
let gcli = tmp.gcli;
let hud;
let gcliterm;

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -0,0 +1,115 @@
#filter substitution
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Firefox Mobile Test Framework.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Joel Maher <joel.maher@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package @ANDROID_PACKAGE_NAME@;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import android.util.Log;
public class FennecTalosAssert implements Assert {
private String logFile = null;
public FennecTalosAssert() {
}
public void dumpLog(String message) {
File file = new File(logFile);
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(logFile, true));
bw.write(message);
bw.newLine();
} catch(IOException e) {
Log.e("Robocop", "exception with file writer on: " + logFile);
} finally {
try {
if (bw != null) {
bw.flush();
bw.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
Log.i("Robocop", message);
}
// Set the filename used for dumpLog.
public void setLogFile(String filename)
{
logFile = filename;
}
public void setTestName(String testName)
{
}
public void finalize()
{
}
public void ok(boolean condition, String name, String diag) {
}
public void is(Object a, Object b, String name) {
}
public void isnot(Object a, Object b, String name) {
}
public void ispixel(int actual, int r, int g, int b, String name) {
}
public void todo(boolean condition, String name, String diag) {
}
public void todo_is(Object a, Object b, String name) {
}
public void todo_isnot(Object a, Object b, String name) {
}
public void info(String name, String message) {
}
}

View File

@ -56,7 +56,8 @@ _JAVA_HARNESS = \
Driver.java \
Element.java \
FennecNativeActions.java \
FennecNativeAssert.java \
FennecMochitestAssert.java \
FennecTalosAssert.java \
FennecNativeDriver.java \
FennecNativeElement.java \
RoboCopException.java \

View File

@ -4616,6 +4616,7 @@ MOZ_SAFE_BROWSING=
MOZ_HELP_VIEWER=
MOZ_SPELLCHECK=1
MOZ_JAVA_COMPOSITOR=
MOZ_ONLY_TOUCH_EVENTS=
MOZ_SVG_DLISTS=
MOZ_TOOLKIT_SEARCH=1
MOZ_UI_LOCALE=en-US
@ -8463,6 +8464,7 @@ AC_SUBST(MOZ_UNIVERSALCHARDET)
AC_SUBST(ACCESSIBILITY)
AC_SUBST(MOZ_SPELLCHECK)
AC_SUBST(MOZ_JAVA_COMPOSITOR)
AC_SUBST(MOZ_ONLY_TOUCH_EVENTS)
AC_SUBST(MOZ_USER_DIR)
AC_SUBST(MOZ_CRASHREPORTER)
AC_SUBST(MOZ_MAINTENANCE_SERVICE)

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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")

View File

@ -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 = \

View File

@ -0,0 +1,12 @@
<html>
<body>
<!-- these should be stopped by CSP after fixing bug 717511. :) -->
<img src="http://example.org/tests/content/base/test/file_CSP.sjs?testid=img_bad&type=img/png"> </img>
<script src='http://example.org/tests/content/base/test/file_CSP.sjs?testid=script_bad&type=text/javascript'></script>
<!-- these should load ok after fixing bug 717511. :) -->
<img src="file_CSP.sjs?testid=img_good&type=img/png" />
<script src='file_CSP.sjs?testid=script_good&type=text/javascript'></script>
</body>
</html>

View File

@ -0,0 +1 @@
X-Content-Security-Policy: default-src 'self', allow *

View File

@ -0,0 +1,12 @@
<html>
<body>
<!-- these should be stopped by CSP after fixing bug 717511. :) -->
<img src="http://example.org/tests/content/base/test/file_CSP.sjs?testid=img2_bad&type=img/png"> </img>
<script src='http://example.org/tests/content/base/test/file_CSP.sjs?testid=script2_bad&type=text/javascript'></script>
<!-- these should load ok after fixing bug 717511. :) -->
<img src="file_CSP.sjs?testid=img2_good&type=img/png" />
<script src='file_CSP.sjs?testid=script2_good&type=text/javascript'></script>
</body>
</html>

View File

@ -0,0 +1 @@
X-Content-Security-Policy: default-src 'self' , allow *

View File

@ -0,0 +1,124 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=717511
-->
<head>
<title>Test for Bug 717511</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
<iframe style="width:200px;height:200px;" id='cspframe2'></iframe>
<script class="testbody" type="text/javascript">
var path = "/tests/content/base/test/";
// These are test results: -1 means it hasn't run,
// true/false is the pass/fail result.
// This is not exhaustive, just double-checking the 'self' vs * policy conflict in the two HTTP headers.
window.tests = {
img_good: -1,
img_bad: -1,
script_good: -1,
script_bad: -1,
img2_good: -1,
img2_bad: -1,
script2_good: -1,
script2_bad: -1,
};
// This is used to watch the blocked data bounce off CSP and allowed data
// get sent out to the wire.
function examiner() {
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
SpecialPowers.addObserver(this, "http-on-modify-request", false);
}
examiner.prototype = {
observe: function(subject, topic, data) {
// subject should be an nsIHttpChannel (for http-on-modify-request)
// and nsIURI for csp-on-violate-policy, and the Request should be
//either allowed or blocked.
if(!SpecialPowers.can_QI(subject))
return;
var testpat = new RegExp("testid=([a-z0-9_]+)");
//_good things better be allowed!
//_bad things better be stopped!
if (topic === "http-on-modify-request") {
//these things were allowed by CSP
var asciiSpec = SpecialPowers.getPrivilegedProps(
SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"),
"URI.asciiSpec");
if (!testpat.test(asciiSpec)) return;
var testid = testpat.exec(asciiSpec)[1];
window.testResult(testid,
/_good/.test(testid),
asciiSpec + " allowed by csp");
}
if(topic === "csp-on-violate-policy") {
//these were blocked... record that they were blocked
var asciiSpec = SpecialPowers.getPrivilegedProps(
SpecialPowers.do_QueryInterface(subject, "nsIURI"),
"asciiSpec");
if (!testpat.test(asciiSpec)) return;
var testid = testpat.exec(asciiSpec)[1];
window.testResult(testid,
/_bad/.test(testid),
asciiSpec + " blocked by \"" + data + "\"");
}
},
// must eventually call this to remove the listener,
// or mochitests might get borked.
remove: function() {
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
SpecialPowers.removeObserver(this, "http-on-modify-request");
}
}
window.examiner = new examiner();
window.testResult = function(testname, result, msg) {
//test already complete.... forget it... remember the first result.
if (window.tests[testname] != -1)
return;
window.tests[testname] = result;
is(result, true, testname + ' test: ' + msg);
// if any test is incomplete, keep waiting
for (var v in window.tests)
if(tests[v] == -1)
return;
// ... otherwise, finish
window.examiner.remove();
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
// save this for last so that our listeners are registered.
// ... this loads the testbed of good and bad requests.
document.getElementById('cspframe').src = 'file_bug717511.html';
document.getElementById('cspframe2').src = 'file_bug717511_2.html';
</script>
</pre>
</body>
</html>

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -136,7 +136,7 @@ protected:
TimeReferenceElement mReferencedElement;
class EventListener : public nsIDOMEventListener
class EventListener MOZ_FINAL : public nsIDOMEventListener
{
public:
EventListener(nsSMILTimeValueSpec* aOwner) : mSpec(aOwner) { }

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html class="reftest-wait">
<body>
<img id="x">
<script>
var xhr = new XMLHttpRequest();
xhr.open("GET", "723441-resource.svg");
xhr.overrideMimeType( 'text/plain; charset=x-user-defined' );
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var svg = xhr.responseText.replace(/#/g, '%23');
var img = document.getElementById("x");
img.onload = function() {
document.documentElement.className = "";
}
img.src = "data:image/svg+xml;," + svg;
}
}
xhr.send();
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 422 KiB

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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>

View File

@ -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);

View File

@ -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; }

View File

@ -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()
{

View File

@ -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,

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -17,6 +17,9 @@ public:
TestSysVShmemParent() { }
virtual ~TestSysVShmemParent() { }
static bool RunTestInProcesses() { return true; }
static bool RunTestInThreads() { return true; }
void Main();
protected:

View File

@ -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 \

View File

@ -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;

View File

@ -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

View File

@ -2809,6 +2809,7 @@ arm*-*)
AC_DEFINE(JS_NUNBOX32)
;;
sparc*-*)
if test ! "$HAVE_64BIT_OS" ; then
ENABLE_METHODJIT=1
ENABLE_MONOIC=1
ENABLE_POLYIC=1
@ -2816,6 +2817,7 @@ sparc*-*)
dnl ENABLE_ION=0
AC_DEFINE(JS_CPU_SPARC)
AC_DEFINE(JS_NUNBOX32)
fi
;;
mips*-*)
ENABLE_METHODJIT=1

View File

@ -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;

View File

@ -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;
}

View File

@ -0,0 +1,2 @@
load(libdir + "asserts.js");
assertThrowsInstanceOf(function () { eval("({p:"); }, SyntaxError); // don't crash

View File

@ -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, {}));

View File

@ -0,0 +1,7 @@
assertEq("a".localeCompare(), 0);
assertEq("a".localeCompare("b"), -1);
assertEq("a".localeCompare("b", "a"), -1);
assertEq("b".localeCompare("a"), 1);
assertEq("b".localeCompare("a", "b"), 1);
assertEq("a".localeCompare("a"), 0);
assertEq("a".localeCompare("a", "b", "c"), 0);

View File

@ -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);

View File

@ -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;

View File

@ -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"

View File

@ -1,6 +1,8 @@
#include "tests.h"
#include "jsatom.h"
#include "vm/String.h"
using namespace mozilla;
BEGIN_TEST(testAtomizedIsNotInterned)

View File

@ -705,7 +705,7 @@ JSRuntime::JSRuntime()
tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
execAlloc_(NULL),
bumpAlloc_(NULL),
repCache_(NULL),
reCache_(NULL),
nativeStackBase(0),
nativeStackQuota(0),
interpreterFrames(NULL),
@ -857,7 +857,7 @@ JSRuntime::~JSRuntime()
delete_<JSC::ExecutableAllocator>(execAlloc_);
delete_<WTF::BumpPointerAllocator>(bumpAlloc_);
JS_ASSERT(!repCache_);
JS_ASSERT(!reCache_);
#ifdef DEBUG
/* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */

View File

@ -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;
}

View File

@ -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.

View File

@ -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___ */

View File

@ -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 */

View File

@ -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

View File

@ -99,6 +99,11 @@ class InterpreterFrames;
class ScriptOpcodeCounts;
struct ScriptOpcodeCountsPair;
typedef HashMap<JSAtom *,
detail::RegExpCacheValue,
DefaultHasher<JSAtom *>,
RuntimeAllocPolicy> RegExpCache;
/*
* GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
* given pc in a script. We use the script->code pointer to tag the cache,
@ -218,11 +223,11 @@ struct JSRuntime : js::RuntimeFriendFields
*/
JSC::ExecutableAllocator *execAlloc_;
WTF::BumpPointerAllocator *bumpAlloc_;
js::RegExpPrivateCache *repCache_;
js::RegExpCache *reCache_;
JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx);
WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx);
js::RegExpPrivateCache *createRegExpPrivateCache(JSContext *cx);
js::RegExpCache *createRegExpCache(JSContext *cx);
public:
JSC::ExecutableAllocator *getExecutableAllocator(JSContext *cx) {
@ -231,11 +236,11 @@ struct JSRuntime : js::RuntimeFriendFields
WTF::BumpPointerAllocator *getBumpPointerAllocator(JSContext *cx) {
return bumpAlloc_ ? bumpAlloc_ : createBumpPointerAllocator(cx);
}
js::RegExpPrivateCache *maybeRegExpPrivateCache() {
return repCache_;
js::RegExpCache *maybeRegExpCache() {
return reCache_;
}
js::RegExpPrivateCache *getRegExpPrivateCache(JSContext *cx) {
return repCache_ ? repCache_ : createRegExpPrivateCache(cx);
js::RegExpCache *getRegExpCache(JSContext *cx) {
return reCache_ ? reCache_ : createRegExpCache(cx);
}
/* Base address of the native stack for the current thread. */

View File

@ -246,7 +246,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
/* Don't unwrap an outer window proxy. */
if (!obj->getClass()->ext.innerObject) {
obj = UnwrapObject(&vp->toObject(), &flags);
obj = UnwrapObject(&vp->toObject(), true, &flags);
vp->setObject(*obj);
if (obj->compartment() == this)
return true;

View File

@ -45,7 +45,6 @@
* JS debugger API.
*/
#include "jsapi.h"
#include "jsopcode.h"
#include "jsprvtd.h"
JS_BEGIN_EXTERN_C

View File

@ -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)
{

View File

@ -5674,6 +5674,9 @@ JSObject::splicePrototype(JSContext *cx, JSObject *proto)
*/
JS_ASSERT_IF(cx->typeInferenceEnabled(), hasSingletonType());
/* Inner objects may not appear on prototype chains. */
JS_ASSERT_IF(proto, !proto->getClass()->ext.outerObject);
/*
* Force type instantiation when splicing lazy types. This may fail,
* in which case inference will be disabled for the compartment.

View File

@ -1164,6 +1164,9 @@ inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown)
{
PodZero(this);
/* Inner objects may not appear on prototype chains. */
JS_ASSERT_IF(proto, !proto->getClass()->ext.outerObject);
this->proto = proto;
if (function)

View File

@ -1003,10 +1003,6 @@ EnterWith(JSContext *cx, jsint stackIndex)
if (!parent)
return JS_FALSE;
OBJ_TO_INNER_OBJECT(cx, obj);
if (!obj)
return JS_FALSE;
JSObject *withobj = WithObject::create(cx, fp, *obj, *parent,
sp + stackIndex - fp->base());
if (!withobj)

View File

@ -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
}
/*

View File

@ -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)
{

View File

@ -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.
*/

View File

@ -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)
{

View File

@ -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);
};

View File

@ -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

View File

@ -48,6 +48,7 @@
#include "jsdbgapi.h"
#include "jsclist.h"
#include "jsinfer.h"
#include "jsopcode.h"
#include "jsscope.h"
#include "gc/Barrier.h"

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More