Merge m-c to elm

This commit is contained in:
Nick Alexander 2013-12-11 14:08:02 -08:00
commit 3520f2dfa1
5936 changed files with 281584 additions and 112154 deletions

7
.lldbinit Normal file
View File

@ -0,0 +1,7 @@
# .lldbinit file for debugging Mozilla
# Mozilla's use of UNIFIED_SOURCES to include multiple source files into a
# single compiled file breaks lldb breakpoint setting. This works around that.
# See http://lldb.llvm.org/troubleshooting.html for more info.
settings set target.inline-breakpoint-strategy always

View File

@ -2,4 +2,4 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include $(topsrcdir)/config/rules.mk
REVIEWBOARD_URL = 'https://reviewboard.allizom.org/'

View File

@ -18,4 +18,4 @@
# Modifying this file will now automatically clobber the buildbot machines \o/
#
More Windows WebIDL changes.
Bug 946067 required a clobber on Windows because bug 928195

View File

@ -44,23 +44,23 @@ endif
endif
CLOBBER: $(topsrcdir)/CLOBBER
@echo "STOP! The CLOBBER file has changed."
@echo "Please run the build through a sanctioned build wrapper, such as"
@echo "'mach build' or client.mk."
@echo 'STOP! The CLOBBER file has changed.'
@echo 'Please run the build through a sanctioned build wrapper, such as'
@echo '"mach build" or client.mk.'
@exit 1
$(topsrcdir)/configure: $(topsrcdir)/configure.in
@echo "STOP! configure.in has changed, and your configure is out of date."
@echo "Please rerun autoconf and re-configure your build directory."
@echo "To ignore this message, touch 'configure' in the source directory,"
@echo "but your build might not succeed."
@echo 'STOP! configure.in has changed, and your configure is out of date.'
@echo 'Please rerun autoconf and re-configure your build directory.'
@echo 'To ignore this message, touch "configure" in the source directory,'
@echo 'but your build might not succeed.'
@exit 1
config.status: $(topsrcdir)/configure
@echo "STOP! configure has changed and needs to be run in this build directory."
@echo "Please rerun configure."
@echo "To ignore this message, touch 'config.status' in the build directory,"
@echo "but your build might not succeed."
@echo 'STOP! configure has changed and needs to be run in this build directory.'
@echo 'Please rerun configure.'
@echo 'To ignore this message, touch "config.status" in the build directory,'
@echo 'but your build might not succeed.'
@exit 1
# Regenerate the build backend if it is out of date. We only have this rule in
@ -71,7 +71,7 @@ config.status: $(topsrcdir)/configure
# builds. This cleanly avoids most of the pain.
backend.RecursiveMakeBackend:
@echo "Build configuration changed. Regenerating backend."
@echo 'Build configuration changed. Regenerating backend.'
$(PYTHON) config.status
Makefile: backend.RecursiveMakeBackend
@ -165,7 +165,7 @@ endif
ifeq ($(OS_ARCH),Darwin)
# need to pass arch flags for universal builds
ifdef UNIVERSAL_BINARY
MAKE_SYM_STORE_ARGS := -c -a "i386 x86_64" --vcs-info
MAKE_SYM_STORE_ARGS := -c -a 'i386 x86_64' --vcs-info
MAKE_SYM_STORE_PATH := $(DIST)/universal
else
MAKE_SYM_STORE_ARGS := -c -a $(OS_TEST) --vcs-info
@ -194,9 +194,9 @@ buildsymbols:
ifdef MOZ_CRASHREPORTER
echo building symbol store
$(RM) -r $(DIST)/crashreporter-symbols
$(RM) "$(DIST)/$(SYMBOL_ARCHIVE_BASENAME).zip"
$(RM) '$(DIST)/$(SYMBOL_ARCHIVE_BASENAME).zip'
$(NSINSTALL) -D $(DIST)/crashreporter-symbols
OBJCOPY="$(OBJCOPY)" \
OBJCOPY='$(OBJCOPY)' \
$(PYTHON) $(topsrcdir)/toolkit/crashreporter/tools/symbolstore.py \
$(MAKE_SYM_STORE_ARGS) \
$(foreach dir,$(SYM_STORE_SOURCE_DIRS),-s $(dir)) \
@ -207,17 +207,17 @@ ifdef MOZ_CRASHREPORTER
echo packing symbols
$(NSINSTALL) -D $(DIST)/$(PKG_PATH)
cd $(DIST)/crashreporter-symbols && \
zip -r9D "../$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip" . -x "*test*" -x "*Test*"
zip -r9D '../$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip' . -x '*test*' -x '*Test*'
cd $(DIST)/crashreporter-symbols && \
grep "sym" $(SYMBOL_INDEX_NAME) > $(SYMBOL_INDEX_NAME).tmp && \
grep 'sym' $(SYMBOL_INDEX_NAME) > $(SYMBOL_INDEX_NAME).tmp && \
mv $(SYMBOL_INDEX_NAME).tmp $(SYMBOL_INDEX_NAME)
cd $(DIST)/crashreporter-symbols && \
zip -r9D "../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip" . -i "*.sym" -i "*.txt" -x "*test*" -x "*Test*"
zip -r9D '../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip' . -i '*.sym' -i '*.txt' -x '*test*' -x '*Test*'
endif # MOZ_CRASHREPORTER
uploadsymbols:
ifdef MOZ_CRASHREPORTER
$(SHELL) $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.sh $(SYMBOL_INDEX_NAME) "$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip"
$(SHELL) $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.sh $(SYMBOL_INDEX_NAME) '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
endif
# MOZ_SOURCE_STAMP is defined in package-name.mk with a deferred assignment.
@ -238,7 +238,7 @@ endif
else
maybe_clobber_profiledbuild:
$(RM) $(DIST)/bin/*.pgc
find $(DIST)/$(MOZ_APP_NAME) -name "*.pgc" -exec mv {} $(DIST)/bin \;
find $(DIST)/$(MOZ_APP_NAME) -name '*.pgc' -exec mv {} $(DIST)/bin \;
endif
.PHONY: maybe_clobber_profiledbuild
@ -246,8 +246,8 @@ endif
# Look for R_386_PC32 relocations in shared libs, these
# break x86_64 builds and SELinux users.
ifeq ($(OS_TARGET)_$(TARGET_XPCOM_ABI),Linux_x86-gcc3)
scheck::
@relcount=`find $(DIST)/bin -name "*.so" | xargs objdump -R | grep R_386_PC32 | wc -l` && if test $$relcount -gt 0; then echo "FAILED: R_386_PC32 relocations detected in a shared library. Did you use a system header without adding it to config/system-headers?"; exit 1; else echo "PASSED"; fi
check::
@relcount=`find $(DIST)/bin -name '*.so' | xargs objdump -R | grep R_386_PC32 | wc -l` && if test $$relcount -gt 0; then echo 'FAILED: R_386_PC32 relocations detected in a shared library. Did you use a system header without adding it to config/system-headers?'; exit 1; else echo 'PASSED'; fi
endif
ifdef BUILD_JS

View File

@ -6,8 +6,6 @@ DEFFILE = $(win_srcdir)/IA2Marshal.def
IA2DIR = $(topsrcdir)/other-licenses/ia2
DEFINES += -DREGISTER_PROXY_DLL
GARBAGE += $(MIDL_GENERATED_FILES)
# Please keep this list in sync with the moz.build file until the rest of this

View File

@ -7,3 +7,5 @@
LIBRARY_NAME = 'IA2Marshal'
FORCE_SHARED_LIB = True
DEFINES['REGISTER_PROXY_DLL'] = True

View File

@ -4,8 +4,6 @@
DEFFILE = $(win_srcdir)/AccessibleMarshal.def
DEFINES += -DREGISTER_PROXY_DLL
GARBAGE += $(MIDL_GENERATED_FILES) done_gen dlldata.c
MIDL_GENERATED_FILES = \

View File

@ -17,3 +17,5 @@ GENERATED_SOURCES += [
]
FORCE_SHARED_LIB = True
DEFINES['REGISTER_PROXY_DLL'] = True

View File

@ -25,7 +25,7 @@
#include "States.h"
#include "nsISimpleEnumerator.h"
#include "mozilla/Util.h"
#include "mozilla/ArrayUtils.h"
#include "nsXPCOMStrings.h"
#include "nsComponentManagerUtils.h"
#include "nsIPersistentProperties2.h"

View File

@ -34,6 +34,7 @@ enum AccType {
eHTMLMediaType,
eHTMLRadioButtonType,
eHTMLRangeType,
eHTMLSpinnerType,
eHTMLTableType,
eHTMLTableCellType,
eHTMLTableRowType,

View File

@ -621,7 +621,7 @@ TextAttrsMgr::FontWeightTextAttr::
if (font->IsSyntheticBold())
return 700;
#ifdef MOZ_PANGO
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
// On Linux, font->GetStyle()->weight will give the absolute weight requested
// of the font face. The Linux code uses the gfxFontEntry constructor which
// doesn't initialize the weight field.

View File

@ -64,7 +64,7 @@ LOCAL_INCLUDES += [
'../../../layout/generic',
'../../../layout/style',
'../../../layout/svg',
'../../../layout/xul/base/src',
'../../../layout/xul',
'../../../layout/xul/tree/',
'../generic',
'../html',

View File

@ -63,9 +63,9 @@
#include "nsTreeUtils.h"
#include "nsXBLPrototypeBinding.h"
#include "nsXBLBinding.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/Util.h"
#include "nsDeckFrame.h"
#ifdef MOZ_XUL
@ -1006,8 +1006,8 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
}
}
// Elements may implement nsIAccessibleProvider via XBL. This allows them to
// say what kind of accessible to create.
// XBL bindings may use @role attribute to point the accessible type
// they belong to.
newAcc = CreateAccessibleByType(content, document);
// Any XUL box can be used as tabpanel, make sure we create a proper
@ -1253,11 +1253,7 @@ nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
accessible = new XULMenubarAccessible(aContent, aDoc);
} else if (role.EqualsLiteral("xul:menulist")) {
if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::droppable,
nsGkAtoms::_false, eCaseMatters))
accessible = new XULTextFieldAccessible(aContent, aDoc);
else
accessible = new XULComboboxAccessible(aContent, aDoc);
accessible = new XULComboboxAccessible(aContent, aDoc);
} else if (role.EqualsLiteral("xul:menuitem")) {
accessible = new XULMenuitemAccessibleWrap(aContent, aDoc);
@ -1314,7 +1310,7 @@ nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
accessible = new XULLabelAccessible(aContent, aDoc);
} else if (role.EqualsLiteral("xul:textbox")) {
accessible = new XULTextFieldAccessible(aContent, aDoc);
accessible = new EnumRoleAccessible(aContent, aDoc, roles::SECTION);
} else if (role.EqualsLiteral("xul:thumb")) {
accessible = new XULThumbAccessible(aContent, aDoc);
@ -1538,6 +1534,9 @@ nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame,
case eHTMLRangeType:
newAcc = new HTMLRangeAccessible(aContent, document);
break;
case eHTMLSpinnerType:
newAcc = new HTMLSpinnerAccessible(aContent, document);
break;
case eHTMLTableType:
newAcc = new HTMLTableAccessibleWrap(aContent, document);
break;

View File

@ -178,8 +178,7 @@ private:
void Shutdown();
/**
* Create accessible for the element implementing nsIAccessibleProvider
* interface.
* Create accessible for the element having XBL bindings.
*/
already_AddRefed<Accessible>
CreateAccessibleByType(nsIContent* aContent, DocAccessible* aDoc);

View File

@ -231,7 +231,7 @@ Accessible::Name(nsString& aName)
// for processing, the user agent shall choose the first one.
for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
childElm = childElm->GetNextSibling()) {
if (childElm->IsSVG(nsGkAtoms::title)) {
if (childElm->IsSVG(nsGkAtoms::desc)) {
nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
return eNameFromTooltip;
}
@ -294,7 +294,7 @@ Accessible::Description(nsString& aDescription)
} else if (mContent->IsSVG()) {
for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
childElm = childElm->GetNextSibling()) {
if (childElm->IsSVG(nsGkAtoms::title)) {
if (childElm->IsSVG(nsGkAtoms::desc)) {
nsTextEquivUtils::AppendTextEquivFromContent(this, childElm,
&aDescription);
break;
@ -1052,56 +1052,37 @@ Accessible::TakeFocus()
return NS_OK;
}
ENameValueFlag
Accessible::GetHTMLName(nsString& aLabel)
void
Accessible::XULElmName(DocAccessible* aDocument,
nsIContent* aElm, nsString& aName)
{
Accessible* labelAcc = nullptr;
HTMLLabelIterator iter(Document(), this);
while ((labelAcc = iter.Next())) {
nsTextEquivUtils::AppendTextEquivFromContent(this, labelAcc->GetContent(),
&aLabel);
aLabel.CompressWhitespace();
}
/**
* 3 main cases for XUL Controls to be labeled
* 1 - control contains label="foo"
* 2 - control has, as a child, a label element
* - label has either value="foo" or children
* 3 - non-child label contains control="controlID"
* - label has either value="foo" or children
* Once a label is found, the search is discontinued, so a control
* that has a label child as well as having a label external to
* the control that uses the control="controlID" syntax will use
* the child label for its Name.
*/
if (!aLabel.IsEmpty())
return eNameOK;
nsTextEquivUtils::GetNameFromSubtree(this, aLabel);
return aLabel.IsEmpty() ? eNameOK : eNameFromSubtree;
}
/**
* 3 main cases for XUL Controls to be labeled
* 1 - control contains label="foo"
* 2 - control has, as a child, a label element
* - label has either value="foo" or children
* 3 - non-child label contains control="controlID"
* - label has either value="foo" or children
* Once a label is found, the search is discontinued, so a control
* that has a label child as well as having a label external to
* the control that uses the control="controlID" syntax will use
* the child label for its Name.
*/
ENameValueFlag
Accessible::GetXULName(nsString& aName)
{
// CASE #1 (via label attribute) -- great majority of the cases
nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl =
do_QueryInterface(mContent);
nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl = do_QueryInterface(aElm);
if (labeledEl) {
labeledEl->GetLabel(aName);
} else {
nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl =
do_QueryInterface(mContent);
nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl = do_QueryInterface(aElm);
if (itemEl) {
itemEl->GetLabel(aName);
} else {
nsCOMPtr<nsIDOMXULSelectControlElement> select =
do_QueryInterface(mContent);
nsCOMPtr<nsIDOMXULSelectControlElement> select = do_QueryInterface(aElm);
// Use label if this is not a select control element which
// uses label attribute to indicate which option is selected
if (!select) {
nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(mContent));
nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(aElm));
if (xulEl)
xulEl->GetAttribute(NS_LITERAL_STRING("label"), aName);
}
@ -1111,7 +1092,7 @@ Accessible::GetXULName(nsString& aName)
// CASES #2 and #3 ------ label as a child or <label control="id" ... > </label>
if (aName.IsEmpty()) {
Accessible* labelAcc = nullptr;
XULLabelIterator iter(Document(), mContent);
XULLabelIterator iter(aDocument, aElm);
while ((labelAcc = iter.Next())) {
nsCOMPtr<nsIDOMXULLabelElement> xulLabel =
do_QueryInterface(labelAcc->GetContent());
@ -1120,30 +1101,27 @@ Accessible::GetXULName(nsString& aName)
// If no value attribute, a non-empty label must contain
// children that define its text -- possibly using HTML
nsTextEquivUtils::
AppendTextEquivFromContent(this, labelAcc->GetContent(), &aName);
AppendTextEquivFromContent(labelAcc, labelAcc->GetContent(), &aName);
}
}
}
aName.CompressWhitespace();
if (!aName.IsEmpty())
return eNameOK;
return;
// Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
nsIContent *bindingParent = mContent->GetBindingParent();
nsIContent *parent = bindingParent? bindingParent->GetParent() :
mContent->GetParent();
nsIContent *bindingParent = aElm->GetBindingParent();
nsIContent* parent =
bindingParent? bindingParent->GetParent() : aElm->GetParent();
while (parent) {
if (parent->Tag() == nsGkAtoms::toolbaritem &&
parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
aName.CompressWhitespace();
return eNameOK;
return;
}
parent = parent->GetParent();
}
nsTextEquivUtils::GetNameFromSubtree(this, aName);
return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
}
nsresult
@ -2478,18 +2456,37 @@ Accessible::ARIAName(nsString& aName)
ENameValueFlag
Accessible::NativeName(nsString& aName)
{
if (mContent->IsHTML())
return GetHTMLName(aName);
if (mContent->IsHTML()) {
Accessible* label = nullptr;
HTMLLabelIterator iter(Document(), this);
while ((label = iter.Next())) {
nsTextEquivUtils::AppendTextEquivFromContent(this, label->GetContent(),
&aName);
aName.CompressWhitespace();
}
if (mContent->IsXUL())
return GetXULName(aName);
if (!aName.IsEmpty())
return eNameOK;
nsTextEquivUtils::GetNameFromSubtree(this, aName);
return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
}
if (mContent->IsXUL()) {
XULElmName(mDoc, mContent, aName);
if (!aName.IsEmpty())
return eNameOK;
nsTextEquivUtils::GetNameFromSubtree(this, aName);
return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
}
if (mContent->IsSVG()) {
// If user agents need to choose among multiple desc or title elements
// for processing, the user agent shall choose the first one.
for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
childElm = childElm->GetNextSibling()) {
if (childElm->IsSVG(nsGkAtoms::desc)) {
if (childElm->IsSVG(nsGkAtoms::title)) {
nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
return eNameOK;
}

View File

@ -879,10 +879,10 @@ protected:
void ARIAName(nsString& aName);
/**
* Compute the name of HTML/XUL node.
* Return the name for XUL element.
*/
mozilla::a11y::ENameValueFlag GetHTMLName(nsString& aName);
mozilla::a11y::ENameValueFlag GetXULName(nsString& aName);
static void XULElmName(DocAccessible* aDocument,
nsIContent* aElm, nsString& aName);
// helper method to verify frames
static nsresult GetFullKeyName(const nsAString& aModifierName, const nsAString& aKeyName, nsAString& aStringOut);

View File

@ -1451,10 +1451,22 @@ DocAccessible::CacheChildren()
if (!rootElm)
return;
// Ignore last HTML:br, copied from HyperTextAccessible.
TreeWalker walker(this, rootElm);
Accessible* lastChild = nullptr;
while (Accessible* child = walker.NextChild()) {
if (lastChild)
AppendChild(lastChild);
Accessible* child = nullptr;
while ((child = walker.NextChild()) && AppendChild(child));
lastChild = child;
}
if (lastChild) {
if (lastChild->IsHTMLBr())
Document()->UnbindFromDocument(lastChild);
else
AppendChild(lastChild);
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -39,16 +39,6 @@ HyperTextAccessible::IsValidRange(int32_t aStartOffset, int32_t aEndOffset)
return endOffset <= static_cast<int32_t>(CharacterCount());
}
inline nsIntRect
HyperTextAccessible::TextBounds(int32_t aStartOffset, int32_t aEndOffset,
uint32_t aCoordType)
{
nsIntRect bounds;
GetPosAndText(aStartOffset, aEndOffset, nullptr, nullptr, &bounds);
nsAccUtils::ConvertScreenCoordsTo(&bounds.x, &bounds.y, aCoordType, this);
return bounds;
}
inline bool
HyperTextAccessible::AddToSelection(int32_t aStartOffset, int32_t aEndOffset)
{

View File

@ -117,19 +117,18 @@ HyperTextAccessible::NativeState()
return states;
}
// Substring must be entirely within the same text node
nsIntRect
HyperTextAccessible::GetBoundsForString(nsIFrame* aFrame, uint32_t aStartRenderedOffset,
uint32_t aEndRenderedOffset)
HyperTextAccessible::GetBoundsInFrame(nsIFrame* aFrame,
uint32_t aStartRenderedOffset,
uint32_t aEndRenderedOffset)
{
nsPresContext* presContext = mDoc->PresContext();
if (aFrame->GetType() != nsGkAtoms::textFrame) {
// XXX fallback for non-text frames, happens for bullets right now
// but in the future bullets will have proper text frames
return aFrame->GetScreenRectInAppUnits().
ToNearestPixels(presContext->AppUnitsPerDevPixel());
}
// Substring must be entirely within the same text node.
int32_t startContentOffset, endContentOffset;
nsresult rv = RenderedToContentOffset(aFrame, aStartRenderedOffset, &startContentOffset);
NS_ENSURE_SUCCESS(rv, nsIntRect());
@ -189,7 +188,6 @@ HyperTextAccessible::GetBoundsForString(nsIFrame* aFrame, uint32_t aStartRendere
nsIFrame*
HyperTextAccessible::GetPosAndText(int32_t& aStartOffset, int32_t& aEndOffset,
nsAString* aText, nsIFrame** aEndFrame,
nsIntRect* aBoundsRect,
Accessible** aStartAcc,
Accessible** aEndAcc)
{
@ -218,9 +216,6 @@ HyperTextAccessible::GetPosAndText(int32_t& aStartOffset, int32_t& aEndOffset,
if (aEndFrame) {
*aEndFrame = nullptr;
}
if (aBoundsRect) {
aBoundsRect->SetEmpty();
}
if (aStartAcc)
*aStartAcc = nullptr;
if (aEndAcc)
@ -243,7 +238,6 @@ HyperTextAccessible::GetPosAndText(int32_t& aStartOffset, int32_t& aEndOffset,
if (!frame) {
continue;
}
nsIFrame *primaryFrame = frame;
endFrame = frame;
if (!nsAccUtils::IsEmbeddedObject(childAcc)) {
// We only need info up to rendered offset -- that is what we're
@ -304,11 +298,6 @@ HyperTextAccessible::GetPosAndText(int32_t& aStartOffset, int32_t& aEndOffset,
substringEndOffset - startOffset);
}
}
if (aBoundsRect) { // Caller wants the bounds of the text
aBoundsRect->UnionRect(*aBoundsRect,
GetBoundsForString(primaryFrame, startOffset,
substringEndOffset));
}
if (!startFrame) {
startFrame = frame;
aStartOffset = startOffset;
@ -347,11 +336,6 @@ HyperTextAccessible::GetPosAndText(int32_t& aStartOffset, int32_t& aEndOffset,
*aText += kEmbeddedObjectChar;
}
}
if (aBoundsRect) {
nsIntRect frameScreenRect = frame->GetScreenRectInAppUnits().
ToNearestPixels(frame->PresContext()->AppUnitsPerDevPixel());
aBoundsRect->UnionRect(*aBoundsRect, frameScreenRect);
}
}
if (!startFrame) {
startFrame = frame;
@ -595,7 +579,7 @@ HyperTextAccessible::HypertextOffsetsToDOMRange(int32_t aStartHTOffset,
int32_t startOffset = aStartHTOffset, endOffset = aEndHTOffset;
nsIFrame *startFrame = nullptr, *endFrame = nullptr;
startFrame = GetPosAndText(startOffset, endOffset, nullptr, &endFrame, nullptr,
startFrame = GetPosAndText(startOffset, endOffset, nullptr, &endFrame,
getter_AddRefs(startAcc), getter_AddRefs(endAcc));
if (!startAcc || !endAcc)
return NS_ERROR_FAILURE;
@ -617,76 +601,6 @@ HyperTextAccessible::HypertextOffsetsToDOMRange(int32_t aStartHTOffset,
return aRange->SetEnd(endPoint.node, endPoint.idx);
}
int32_t
HyperTextAccessible::GetRelativeOffset(nsIPresShell* aPresShell,
nsIFrame* aFromFrame,
int32_t aFromOffset,
Accessible* aFromAccessible,
nsSelectionAmount aAmount,
nsDirection aDirection,
bool aNeedsStart,
EWordMovementType aWordMovementType)
{
const bool kIsJumpLinesOk = true; // okay to jump lines
const bool kIsScrollViewAStop = false; // do not stop at scroll views
const bool kIsKeyboardSelect = true; // is keyboard selection
const bool kIsVisualBidi = false; // use visual order for bidi text
// Ask layout for the new node and offset, after moving the appropriate amount
nsresult rv;
int32_t contentOffset = aFromOffset;
nsIFrame *frame = aFromAccessible->GetFrame();
NS_ENSURE_TRUE(frame, -1);
if (frame->GetType() == nsGkAtoms::textFrame) {
rv = RenderedToContentOffset(frame, aFromOffset, &contentOffset);
NS_ENSURE_SUCCESS(rv, -1);
}
nsPeekOffsetStruct pos(aAmount, aDirection, contentOffset,
0, kIsJumpLinesOk, kIsScrollViewAStop, kIsKeyboardSelect, kIsVisualBidi,
aWordMovementType);
rv = aFromFrame->PeekOffset(&pos);
// PeekOffset fails on last/first lines of the text in certain cases.
if (NS_FAILED(rv) && aAmount == eSelectLine) {
pos.mAmount = (aDirection == eDirNext) ? eSelectEndLine : eSelectBeginLine;
aFromFrame->PeekOffset(&pos);
}
if (!pos.mResultContent)
return -1;
// Turn the resulting node and offset into a hyperTextOffset
// If finalAccessible is nullptr, then DOMPointToHypertextOffset() searched
// through the hypertext children without finding the node/offset position.
int32_t hyperTextOffset;
Accessible* finalAccessible =
DOMPointToHypertextOffset(pos.mResultContent, pos.mContentOffset,
&hyperTextOffset, aDirection == eDirNext);
if (!finalAccessible && aDirection == eDirPrevious) {
// If we reached the end during search, this means we didn't find the DOM point
// and we're actually at the start of the paragraph
hyperTextOffset = 0;
}
else if (aAmount == eSelectBeginLine) {
Accessible* firstChild = mChildren.SafeElementAt(0, nullptr);
// For line selection with needsStart, set start of line exactly to line break
if (pos.mContentOffset == 0 && firstChild &&
firstChild->Role() == roles::STATICTEXT &&
static_cast<int32_t>(nsAccUtils::TextLength(firstChild)) == hyperTextOffset) {
// XXX Bullet hack -- we should remove this once list bullets use anonymous content
hyperTextOffset = 0;
}
if (!aNeedsStart && hyperTextOffset > 0) {
-- hyperTextOffset;
}
}
return hyperTextOffset;
}
int32_t
HyperTextAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
nsSelectionAmount aAmount,
@ -697,7 +611,7 @@ HyperTextAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
nsRefPtr<Accessible> accAtOffset;
nsIFrame* frameAtOffset =
GetPosAndText(offsetInFrame, notUsedOffset, nullptr, nullptr,
nullptr, getter_AddRefs(accAtOffset));
getter_AddRefs(accAtOffset));
if (!frameAtOffset) {
if (aOffset == CharacterCount()) {
// Asking for start of line, while on last character.
@ -713,10 +627,48 @@ HyperTextAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
}
// Return hypertext offset of the boundary of the found word.
return GetRelativeOffset(mDoc->PresShell(), frameAtOffset, offsetInFrame,
accAtOffset, aAmount, aDirection,
(aWordMovementType == eStartWord || aAmount == eSelectBeginLine),
aWordMovementType);
int32_t contentOffset = offsetInFrame;
nsIFrame* primaryFrame = accAtOffset->GetFrame();
NS_ENSURE_TRUE(primaryFrame, -1);
nsresult rv = NS_OK;
if (primaryFrame->GetType() == nsGkAtoms::textFrame) {
rv = RenderedToContentOffset(primaryFrame, offsetInFrame, &contentOffset);
NS_ENSURE_SUCCESS(rv, -1);
}
const bool kIsJumpLinesOk = true; // okay to jump lines
const bool kIsScrollViewAStop = false; // do not stop at scroll views
const bool kIsKeyboardSelect = true; // is keyboard selection
const bool kIsVisualBidi = false; // use visual order for bidi text
nsPeekOffsetStruct pos(aAmount, aDirection, contentOffset,
0, kIsJumpLinesOk, kIsScrollViewAStop,
kIsKeyboardSelect, kIsVisualBidi,
aWordMovementType);
rv = frameAtOffset->PeekOffset(&pos);
// PeekOffset fails on last/first lines of the text in certain cases.
if (NS_FAILED(rv) && aAmount == eSelectLine) {
pos.mAmount = (aDirection == eDirNext) ? eSelectEndLine : eSelectBeginLine;
frameAtOffset->PeekOffset(&pos);
}
if (!pos.mResultContent)
return -1;
// Turn the resulting node and offset into a hyperTextOffset
// If finalAccessible is nullptr, then DOMPointToHypertextOffset() searched
// through the hypertext children without finding the node/offset position.
int32_t hyperTextOffset = 0;
Accessible* finalAccessible =
DOMPointToHypertextOffset(pos.mResultContent, pos.mContentOffset,
&hyperTextOffset, aDirection == eDirNext);
// If we reached the end during search, this means we didn't find the DOM point
// and we're actually at the start of the paragraph
if (!finalAccessible && aDirection == eDirPrevious)
return 0;
return hyperTextOffset;
}
int32_t
@ -1219,6 +1171,48 @@ HyperTextAccessible::OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType)
return -1; // Not found
}
nsIntRect
HyperTextAccessible::TextBounds(int32_t aStartOffset, int32_t aEndOffset,
uint32_t aCoordType)
{
int32_t startOffset = ConvertMagicOffset(aStartOffset);
int32_t endOffset = ConvertMagicOffset(aEndOffset);
NS_ASSERTION(startOffset < endOffset, "Wrong bad in!");
int32_t childIdx = GetChildIndexAtOffset(startOffset);
if (childIdx == -1)
return nsIntRect();
nsIntRect bounds;
int32_t prevOffset = GetChildOffset(childIdx);
int32_t offset1 = startOffset - prevOffset;
while (childIdx < ChildCount()) {
nsIFrame* frame = GetChildAt(childIdx)->GetFrame();
if (!frame) {
NS_NOTREACHED("No frame for a child!");
continue;
}
childIdx++;
int32_t nextOffset = GetChildOffset(childIdx);
if (nextOffset >= endOffset) {
bounds.UnionRect(bounds, GetBoundsInFrame(frame, offset1,
endOffset - prevOffset));
break;
}
bounds.UnionRect(bounds, GetBoundsInFrame(frame, offset1,
nextOffset - prevOffset));
prevOffset = nextOffset;
offset1 = 0;
}
nsAccUtils::ConvertScreenCoordsTo(&bounds.x, &bounds.y, aCoordType, this);
return bounds;
}
already_AddRefed<nsIEditor>
HyperTextAccessible::GetEditor() const
{

View File

@ -432,29 +432,9 @@ protected:
* Return an offset corresponding to the given direction and selection amount
* relative the given offset. A helper used to find word or line boundaries.
*/
int32_t FindOffset(int32_t aOffset, nsDirection aDirection,
nsSelectionAmount aAmount,
EWordMovementType aWordMovementType = eDefaultBehavior);
/**
* Used by FindOffset() to move backward/forward from a given point
* by word/line/etc.
*
* @param aPresShell the current presshell we're moving in
* @param aFromFrame the starting frame we're moving from
* @param aFromOffset the starting offset we're moving from
* @param aFromAccessible the starting accessible we're moving from
* @param aAmount how much are we moving (word/line/etc.) ?
* @param aDirection forward or backward?
* @param aNeedsStart for word and line cases, are we basing this on
* the start or end?
* @return the resulting offset into this hypertext
*/
int32_t GetRelativeOffset(nsIPresShell *aPresShell, nsIFrame *aFromFrame,
int32_t aFromOffset, Accessible* aFromAccessible,
nsSelectionAmount aAmount, nsDirection aDirection,
bool aNeedsStart,
EWordMovementType aWordMovementType);
virtual int32_t FindOffset(int32_t aOffset, nsDirection aDirection,
nsSelectionAmount aAmount,
EWordMovementType aWordMovementType = eDefaultBehavior);
/**
* Provides information for substring that is defined by the given start
@ -473,8 +453,6 @@ protected:
* @param aText [out, optional] return the substring's text
* @param aEndFrame [out, optional] return the end frame for this
* substring
* @param aBoundsRect [out, optional] return the bounds rectangle for this
* substring
* @param aStartAcc [out, optional] return the start accessible for this
* substring
* @param aEndAcc [out, optional] return the end accessible for this
@ -484,11 +462,16 @@ protected:
nsIFrame* GetPosAndText(int32_t& aStartOffset, int32_t& aEndOffset,
nsAString *aText = nullptr,
nsIFrame **aEndFrame = nullptr,
nsIntRect *aBoundsRect = nullptr,
Accessible** aStartAcc = nullptr,
Accessible** aEndAcc = nullptr);
nsIntRect GetBoundsForString(nsIFrame *aFrame, uint32_t aStartRenderedOffset, uint32_t aEndRenderedOffset);
/**
* Return the boundaries of the substring in case of textual frame or
* frame boundaries in case of non textual frame, offsets are ignored.
*/
nsIntRect GetBoundsInFrame(nsIFrame* aFrame,
uint32_t aStartRenderedOffset,
uint32_t aEndRenderedOffset);
// Selection helpers

View File

@ -5,7 +5,7 @@
#include "RootAccessible.h"
#include "mozilla/Util.h"
#include "mozilla/ArrayUtils.h"
#define CreateEvent CreateEventA
#include "nsIDOMDocument.h"

View File

@ -27,7 +27,7 @@ UNIFIED_SOURCES += [
LOCAL_INCLUDES += [
'../../../layout/generic',
'../../../layout/xul/base/src',
'../../../layout/xul',
'../base',
'../html',
'../xpcom',

View File

@ -302,7 +302,7 @@ HTMLTextFieldAccessible::NativeRole()
nsGkAtoms::password, eIgnoreCase)) {
return roles::PASSWORD_TEXT;
}
return roles::ENTRY;
}
@ -328,16 +328,10 @@ HTMLTextFieldAccessible::NativeName(nsString& aName)
if (!aName.IsEmpty())
return nameFlag;
if (mContent->GetBindingParent()) {
// XXX: bug 459640
// There's a binding parent.
// This means we're part of another control, so use parent accessible for name.
// This ensures that a textbox inside of a XUL widget gets
// an accessible name.
Accessible* parent = Parent();
if (parent)
parent->GetName(aName);
}
// If part of compound of XUL widget then grab a name from XUL widget element.
nsIContent* widgetElm = XULWidgetElm();
if (widgetElm)
XULElmName(mDoc, widgetElm, aName);
if (!aName.IsEmpty())
return eNameOK;
@ -369,8 +363,13 @@ void
HTMLTextFieldAccessible::ApplyARIAState(uint64_t* aState) const
{
HyperTextAccessibleWrap::ApplyARIAState(aState);
aria::MapToState(aria::eARIAAutoComplete, mContent->AsElement(), aState);
// If part of compound of XUL widget then pick up ARIA stuff from XUL widget
// element.
nsIContent* widgetElm = XULWidgetElm();
if (widgetElm)
aria::MapToState(aria::eARIAAutoComplete, widgetElm->AsElement(), aState);
}
uint64_t
@ -408,9 +407,8 @@ HTMLTextFieldAccessible::NativeState()
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::list))
return state | states::SUPPORTS_AUTOCOMPLETION | states::HASPOPUP;
// No parent can mean a fake widget created for XUL textbox. If accessible
// is unattached from tree then we don't care.
if (mParent && Preferences::GetBool("browser.formfill.enable")) {
// Ordinal XUL textboxes don't support autocomplete.
if (!XULWidgetElm() && Preferences::GetBool("browser.formfill.enable")) {
// Check to see if autocompletion is allowed on this input. We don't expose
// it for password fields even though the entire password can be remembered
// for a page if the user asks it to be. However, the kind of autocomplete
@ -542,6 +540,76 @@ HTMLFileInputAccessible::HandleAccEvent(AccEvent* aEvent)
}
////////////////////////////////////////////////////////////////////////////////
// HTMLSpinnerAccessible
////////////////////////////////////////////////////////////////////////////////
role
HTMLSpinnerAccessible::NativeRole()
{
return roles::SPINBUTTON;
}
void
HTMLSpinnerAccessible::Value(nsString& aValue)
{
AccessibleWrap::Value(aValue);
if (!aValue.IsEmpty())
return;
HTMLInputElement::FromContent(mContent)->GetValue(aValue);
}
double
HTMLSpinnerAccessible::MaxValue() const
{
double value = AccessibleWrap::MaxValue();
if (!IsNaN(value))
return value;
return HTMLInputElement::FromContent(mContent)->GetMaximum().toDouble();
}
double
HTMLSpinnerAccessible::MinValue() const
{
double value = AccessibleWrap::MinValue();
if (!IsNaN(value))
return value;
return HTMLInputElement::FromContent(mContent)->GetMinimum().toDouble();
}
double
HTMLSpinnerAccessible::Step() const
{
double value = AccessibleWrap::Step();
if (!IsNaN(value))
return value;
return HTMLInputElement::FromContent(mContent)->GetStep().toDouble();
}
double
HTMLSpinnerAccessible::CurValue() const
{
double value = AccessibleWrap::CurValue();
if (!IsNaN(value))
return value;
return HTMLInputElement::FromContent(mContent)->GetValueAsDecimal().toDouble();
}
bool
HTMLSpinnerAccessible::SetCurValue(double aValue)
{
ErrorResult er;
HTMLInputElement::FromContent(mContent)->SetValueAsNumber(aValue, er);
return !er.Failed();
}
////////////////////////////////////////////////////////////////////////////////
// HTMLRangeAccessible
////////////////////////////////////////////////////////////////////////////////
@ -578,7 +646,6 @@ HTMLRangeAccessible::MaxValue() const
return HTMLInputElement::FromContent(mContent)->GetMaximum().toDouble();
}
double
HTMLRangeAccessible::MinValue() const
{

View File

@ -143,6 +143,11 @@ public:
protected:
// Accessible
virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE;
/**
* Return a XUL widget element this input is part of.
*/
nsIContent* XULWidgetElm() const { return mContent->GetBindingParent(); }
};
@ -160,6 +165,30 @@ public:
};
/**
* Used for HTML input@type="number".
*/
class HTMLSpinnerAccessible : public AccessibleWrap
{
public:
HTMLSpinnerAccessible(nsIContent* aContent, DocAccessible* aDoc) :
AccessibleWrap(aContent, aDoc)
{
mStateFlags |= eHasNumericValue;
}
// Accessible
virtual mozilla::a11y::role NativeRole() MOZ_OVERRIDE;
virtual void Value(nsString& aValue) MOZ_OVERRIDE;
virtual double MaxValue() const MOZ_OVERRIDE;
virtual double MinValue() const MOZ_OVERRIDE;
virtual double CurValue() const MOZ_OVERRIDE;
virtual double Step() const MOZ_OVERRIDE;
virtual bool SetCurValue(double aValue) MOZ_OVERRIDE;
};
/**
* Used for input@type="range" element.
*/

View File

@ -7,6 +7,7 @@
#include "HTMLListAccessible.h"
#include "DocAccessible.h"
#include "nsAccUtils.h"
#include "Role.h"
#include "States.h"
@ -97,6 +98,33 @@ HTMLLIAccessible::GetBounds(int32_t* aX, int32_t* aY,
return NS_OK;
}
int32_t
HTMLLIAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
nsSelectionAmount aAmount,
EWordMovementType aWordMovementType)
{
Accessible* child = GetChildAtOffset(aOffset);
if (!child)
return -1;
if (child != mBullet) {
if (aDirection == eDirPrevious &&
(aAmount == eSelectBeginLine || aAmount == eSelectLine))
return 0;
return HyperTextAccessible::FindOffset(aOffset, aDirection,
aAmount, aWordMovementType);
}
if (aDirection == eDirPrevious)
return 0;
if (aAmount == eSelectEndLine || aAmount == eSelectLine)
return CharacterCount();
return nsAccUtils::TextLength(child);
}
////////////////////////////////////////////////////////////////////////////////
// HTMLLIAccessible: public

View File

@ -55,7 +55,12 @@ public:
virtual a11y::role NativeRole();
virtual uint64_t NativeState();
// nsHTMLLIAccessible
// HyperTextAccessible
virtual int32_t FindOffset(int32_t aOffset, nsDirection aDirection,
nsSelectionAmount aAmount,
EWordMovementType aWordMovementType) MOZ_OVERRIDE;
// HTMLLIAccessible
void UpdateBullet(bool aHasBullet);
protected:

View File

@ -18,7 +18,7 @@ UNIFIED_SOURCES += [
LOCAL_INCLUDES += [
'../../../layout/generic',
'../../../layout/tables',
'../../../layout/xul/base/src',
'../../../layout/xul',
'../base',
'../generic',
'../xpcom',

View File

@ -496,7 +496,9 @@ var Output = {
},
speechHelper: {
EARCONS: ['chrome://global/content/accessibility/tick.wav'],
EARCONS: ['virtual_cursor_move.ogg',
'virtual_cursor_key.ogg',
'clicked.ogg'],
earconBuffers: {},
@ -509,9 +511,10 @@ var Output = {
this.webspeechEnabled = !!window.speechSynthesis;
for (let earcon of this.EARCONS) {
let earconName = /.*\/(.*)\..*$/.exec(earcon)[1];
let earconName = /(^.*)\..*$/.exec(earcon)[1];
this.earconBuffers[earconName] = new WeakMap();
this.earconBuffers[earconName].set(window, new window.Audio(earcon));
this.earconBuffers[earconName].set(
window, new window.Audio('chrome://global/content/accessibility/' + earcon));
}
this.inited = true;
@ -741,6 +744,7 @@ var Input = {
this.contextAction('forward');
break;
case 'exploreend1':
case 'dwellend1':
this.activateCurrent(null, true);
break;
case 'swiperight2':

View File

@ -57,8 +57,6 @@ this.EventManager.prototype = {
Ci.nsIWebProgress.NOTIFY_LOCATION));
this.addEventListener('scroll', this, true);
this.addEventListener('resize', this, true);
// XXX: Ideally this would be an a11y event. Bug #742280.
this.addEventListener('DOMActivate', this, true);
}
this.present(Presentation.tabStateChanged(null, 'newtab'));
@ -79,8 +77,6 @@ this.EventManager.prototype = {
this.webProgress.removeProgressListener(this);
this.removeEventListener('scroll', this, true);
this.removeEventListener('resize', this, true);
// XXX: Ideally this would be an a11y event. Bug #742280.
this.removeEventListener('DOMActivate', this, true);
} catch (x) {
// contentScope is dead.
} finally {
@ -91,21 +87,6 @@ this.EventManager.prototype = {
handleEvent: function handleEvent(aEvent) {
try {
switch (aEvent.type) {
case 'DOMActivate':
{
let activatedAcc =
Utils.AccRetrieval.getAccessibleFor(aEvent.originalTarget);
let [state, extState] = Utils.getStates(activatedAcc);
// Checkable objects will have a state changed event that we will use
// instead of this hackish DOMActivate. We will also know the true
// action that was taken.
if (state & Ci.nsIAccessibleStates.STATE_CHECKABLE)
return;
this.present(Presentation.actionInvoked(activatedAcc, 'click'));
break;
}
case 'scroll':
case 'resize':
{

View File

@ -1,3 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

View File

@ -20,6 +20,8 @@ XPCOMUtils.defineLazyModuleGetter(this, 'UtteranceGenerator',
'resource://gre/modules/accessibility/OutputGenerator.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'BrailleGenerator',
'resource://gre/modules/accessibility/OutputGenerator.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
'resource://gre/modules/accessibility/Constants.jsm');
this.EXPORTED_SYMBOLS = ['Presentation'];
@ -304,6 +306,11 @@ AndroidPresenter.prototype = {
actionInvoked: function AndroidPresenter_actionInvoked(aObject, aActionName) {
let state = Utils.getStates(aObject)[0];
// Checkable objects will have a state changed event we will use instead.
if (state & Ci.nsIAccessibleStates.STATE_CHECKABLE)
return null;
return {
type: this.type,
details: [{
@ -451,7 +458,10 @@ SpeechPresenter.prototype = {
type: this.type,
details: {
actions: [
{method: 'playEarcon', data: 'tick', options: {}},
{method: 'playEarcon',
data: aContext.accessible.role === Roles.KEY ?
'virtual_cursor_key' : 'virtual_cursor_move',
options: {}},
{method: 'speak',
data: UtteranceGenerator.genForContext(aContext).output.join(' '),
options: {enqueue: true}}
@ -461,16 +471,17 @@ SpeechPresenter.prototype = {
},
actionInvoked: function SpeechPresenter_actionInvoked(aObject, aActionName) {
return {
type: this.type,
details: {
actions: [
{method: 'speak',
data: UtteranceGenerator.genForAction(aObject, aActionName).join(' '),
options: {enqueue: false}}
]
}
};
let actions = [];
if (aActionName === 'click') {
actions.push({method: 'playEarcon',
data: 'clicked',
options: {}});
} else {
actions.push({method: 'speak',
data: UtteranceGenerator.genForAction(aObject, aActionName).join(' '),
options: {enqueue: false}});
}
return { type: this.type, details: { actions: actions } };
},
liveRegion: function SpeechPresenter_liveRegion(aContext, aIsPolite, aIsHide,

View File

@ -392,6 +392,8 @@ TouchPoint.prototype = {
return {type: 'tap', x: this.startX, y: this.startY};
} else if (!this.done && duration == TouchAdapter.DWELL_THRESHOLD) {
return {type: 'dwell', x: this.startX, y: this.startY};
} else if (this.done && duration > TouchAdapter.DWELL_THRESHOLD) {
return {type: 'dwellend', x: this.startX, y: this.startY};
}
}

View File

@ -203,6 +203,12 @@ function activateCurrent(aMessage) {
dispatchMouseEvent('mousedown');
dispatchMouseEvent('mouseup');
}
if (aAccessible.role !== Roles.KEY) {
// Keys will typically have a sound of their own.
sendAsyncMessage('AccessFu:Present',
Presentation.actionInvoked(aAccessible, 'click'));
}
}
function moveCaretTo(aAccessible, aOffset) {

View File

@ -5,4 +5,6 @@
toolkit.jar:
content/global/accessibility/AccessFu.css (AccessFu.css)
content/global/accessibility/content-script.js (content-script.js)
content/global/accessibility/tick.wav (tick.wav)
content/global/accessibility/virtual_cursor_move.ogg (sounds/virtual_cursor_move.ogg)
content/global/accessibility/virtual_cursor_key.ogg (sounds/virtual_cursor_key.ogg)
content/global/accessibility/clicked.ogg (sounds/clicked.ogg)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -32,7 +32,7 @@ LOCAL_INCLUDES += [
'../html',
'../xul',
'/layout/generic',
'/layout/xul/base/src',
'/layout/xul',
'/widget/cocoa',
'/widget/xpwidgets',
]

View File

@ -232,7 +232,7 @@ ia2AccessibleText::get_textBeforeOffset(long aOffset,
if (textAcc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
if (textAcc->IsValidOffset(aOffset))
if (!textAcc->IsValidOffset(aOffset))
return E_INVALIDARG;
nsAutoString text;
@ -281,7 +281,7 @@ ia2AccessibleText::get_textAfterOffset(long aOffset,
if (textAcc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
if (textAcc->IsValidOffset(aOffset))
if (!textAcc->IsValidOffset(aOffset))
return E_INVALIDARG;
nsAutoString text;
@ -328,7 +328,7 @@ ia2AccessibleText::get_textAtOffset(long aOffset,
if (textAcc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
if (textAcc->IsValidOffset(aOffset))
if (!textAcc->IsValidOffset(aOffset))
return E_INVALIDARG;
nsAutoString text;

View File

@ -20,7 +20,7 @@ using namespace mozilla::a11y;
bool
IsModuleVersionLessThan(HMODULE aModuleHandle, DWORD aMajor, DWORD aMinor)
{
PRUnichar fileName[MAX_PATH];
wchar_t fileName[MAX_PATH];
::GetModuleFileNameW(aModuleHandle, fileName, MAX_PATH);
DWORD dummy = 0;

View File

@ -9,6 +9,7 @@
#define mozilla_a11y_IUnknownImpl_h_
#include <windows.h>
#undef CreateEvent // thank you windows you're such a helper
#include "nsError.h"
// Avoid warning C4509 like "nonstandard extension used:

View File

@ -22,7 +22,7 @@ using namespace mozilla::a11y;
// Window property used by ipc related code in identifying accessible
// tab windows.
const PRUnichar* kPropNameTabContent = L"AccessibleTabWindow";
const wchar_t* kPropNameTabContent = L"AccessibleTabWindow";
/**
* WindowProc to process WM_GETOBJECT messages, used in windows emulation mode.

View File

@ -185,7 +185,7 @@ sdnAccessible::get_attributesForNames(unsigned short aMaxAttribs,
if (aAttribNames[index]) {
nsAutoString attributeValue, nameSpaceURI;
nsAutoString attributeName(nsDependentString(
static_cast<PRUnichar*>(aAttribNames[index])));
static_cast<const wchar_t*>(aAttribNames[index])));
nsresult rv = NS_OK;
if (aNameSpaceID[index]>0 &&
@ -282,8 +282,7 @@ sdnAccessible::get_computedStyleForProperties(unsigned short aNumStyleProperties
for (index = 0; index < aNumStyleProperties; index++) {
nsAutoString value;
if (aStyleProperties[index])
cssDecl->GetPropertyValue(nsDependentString(static_cast<PRUnichar*>(
aStyleProperties[index])), value); // Get property value
cssDecl->GetPropertyValue(nsDependentString(aStyleProperties[index]), value); // Get property value
aStyleValues[index] = ::SysAllocString(value.get());
}

View File

@ -636,212 +636,3 @@ XULToolbarSeparatorAccessible::NativeState()
{
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// XULTextFieldAccessible
////////////////////////////////////////////////////////////////////////////////
XULTextFieldAccessible::
XULTextFieldAccessible(nsIContent* aContent, DocAccessible* aDoc) :
HyperTextAccessibleWrap(aContent, aDoc)
{
}
NS_IMPL_ISUPPORTS_INHERITED2(XULTextFieldAccessible,
Accessible,
nsIAccessibleText,
nsIAccessibleEditableText)
////////////////////////////////////////////////////////////////////////////////
// XULTextFieldAccessible: nsIAccessible
void
XULTextFieldAccessible::Value(nsString& aValue)
{
aValue.Truncate();
if (NativeRole() == roles::PASSWORD_TEXT) // Don't return password text!
return;
nsCOMPtr<nsIDOMXULTextBoxElement> textBox(do_QueryInterface(mContent));
if (textBox) {
textBox->GetValue(aValue);
return;
}
nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
if (menuList)
menuList->GetLabel(aValue);
}
void
XULTextFieldAccessible::ApplyARIAState(uint64_t* aState) const
{
HyperTextAccessibleWrap::ApplyARIAState(aState);
aria::MapToState(aria::eARIAAutoComplete, mContent->AsElement(), aState);
}
uint64_t
XULTextFieldAccessible::NativeState()
{
uint64_t state = HyperTextAccessibleWrap::NativeState();
nsCOMPtr<nsIContent> inputField(GetInputField());
NS_ENSURE_TRUE(inputField, state);
// Create a temporary accessible from the HTML text field to get
// the accessible state from. Doesn't add to cache into document cache.
nsRefPtr<HTMLTextFieldAccessible> tempAccessible =
new HTMLTextFieldAccessible(inputField, mDoc);
if (!tempAccessible)
return state;
state |= tempAccessible->NativeState();
nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
if (menuList) {
// <xul:menulist droppable="false">
if (!mContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::editable,
nsGkAtoms::_true, eIgnoreCase)) {
state |= states::READONLY;
}
}
return state;
}
role
XULTextFieldAccessible::NativeRole()
{
if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
nsGkAtoms::password, eIgnoreCase))
return roles::PASSWORD_TEXT;
return roles::ENTRY;
}
/**
* Only one actions available
*/
uint8_t
XULTextFieldAccessible::ActionCount()
{
return 1;
}
/**
* Return the name of our only action
*/
NS_IMETHODIMP
XULTextFieldAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
{
if (aIndex == eAction_Click) {
aName.AssignLiteral("activate");
return NS_OK;
}
return NS_ERROR_INVALID_ARG;
}
/**
* Tell the button to do its action
*/
NS_IMETHODIMP
XULTextFieldAccessible::DoAction(uint8_t index)
{
if (index == 0) {
nsCOMPtr<nsIDOMXULElement> element(do_QueryInterface(mContent));
if (element)
{
element->Focus();
return NS_OK;
}
return NS_ERROR_FAILURE;
}
return NS_ERROR_INVALID_ARG;
}
bool
XULTextFieldAccessible::CanHaveAnonChildren()
{
return false;
}
bool
XULTextFieldAccessible::IsAcceptableChild(Accessible* aPossibleChild) const
{
// XXX: entry shouldn't contain anything but text leafs. Currently it may
// contain a trailing fake HTML br element added for layout needs. We don't
// need to expose it since it'd be confusing for AT.
return aPossibleChild->IsTextLeaf();
}
already_AddRefed<nsIEditor>
XULTextFieldAccessible::GetEditor() const
{
nsCOMPtr<nsIContent> inputField = GetInputField();
nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(inputField));
if (!editableElt)
return nullptr;
nsCOMPtr<nsIEditor> editor;
editableElt->GetEditor(getter_AddRefs(editor));
return editor.forget();
}
////////////////////////////////////////////////////////////////////////////////
// XULTextFieldAccessible: Accessible protected
void
XULTextFieldAccessible::CacheChildren()
{
NS_ENSURE_TRUE_VOID(mDoc);
// Create child accessibles for native anonymous content of underlying HTML
// input element.
nsCOMPtr<nsIContent> inputContent(GetInputField());
if (!inputContent)
return;
TreeWalker walker(this, inputContent);
while (Accessible* child = walker.NextChild())
AppendChild(child);
}
////////////////////////////////////////////////////////////////////////////////
// XULTextFieldAccessible: HyperTextAccessible protected
already_AddRefed<nsFrameSelection>
XULTextFieldAccessible::FrameSelection() const
{
nsCOMPtr<nsIContent> inputContent(GetInputField());
NS_ASSERTION(inputContent, "No input content");
if (!inputContent)
return nullptr;
nsIFrame* frame = inputContent->GetPrimaryFrame();
return frame ? frame->GetFrameSelection() : nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// XULTextFieldAccessible protected
already_AddRefed<nsIContent>
XULTextFieldAccessible::GetInputField() const
{
nsCOMPtr<nsIDOMNode> inputFieldDOMNode;
nsCOMPtr<nsIDOMXULTextBoxElement> textBox = do_QueryInterface(mContent);
if (textBox) {
textBox->GetInputField(getter_AddRefs(inputFieldDOMNode));
} else {
// <xul:menulist droppable="false">
nsCOMPtr<nsIDOMXULMenuListElement> menuList = do_QueryInterface(mContent);
if (menuList)
menuList->GetInputField(getter_AddRefs(inputFieldDOMNode));
}
NS_ASSERTION(inputFieldDOMNode, "No input field for XULTextFieldAccessible");
nsCOMPtr<nsIContent> inputField = do_QueryInterface(inputFieldDOMNode);
return inputField.forget();
}

View File

@ -215,47 +215,6 @@ public:
virtual uint64_t NativeState();
};
/**
* Used for XUL textbox element.
*/
class XULTextFieldAccessible : public HyperTextAccessibleWrap
{
public:
enum { eAction_Click = 0 };
XULTextFieldAccessible(nsIContent* aContent, DocAccessible* aDoc);
NS_DECL_ISUPPORTS_INHERITED
// nsIAccessible
NS_IMETHOD GetActionName(uint8_t aIndex, nsAString& aName);
NS_IMETHOD DoAction(uint8_t index);
// HyperTextAccessible
virtual already_AddRefed<nsIEditor> GetEditor() const;
// Accessible
virtual void Value(nsString& aValue);
virtual void ApplyARIAState(uint64_t* aState) const;
virtual mozilla::a11y::role NativeRole();
virtual uint64_t NativeState();
virtual bool CanHaveAnonChildren();
virtual bool IsAcceptableChild(Accessible* aPossibleChild) const MOZ_OVERRIDE;
// ActionAccessible
virtual uint8_t ActionCount();
protected:
// Accessible
virtual void CacheChildren();
// HyperTextAccessible
virtual already_AddRefed<nsFrameSelection> FrameSelection() const;
// nsXULTextFieldAccessible
already_AddRefed<nsIContent> GetInputField() const;
};
} // namespace a11y
} // namespace mozilla

View File

@ -635,7 +635,7 @@ XULListitemAccessible::NativeName(nsString& aName)
}
}
return GetXULName(aName);
return Accessible::NativeName(aName);
}
role

View File

@ -75,10 +75,7 @@ public:
/**
* A tabpanel object, child elements of xul:tabpanels element. Note,the object
* is created from nsAccessibilityService::GetAccessibleForDeckChildren()
* method and we do not use nsIAccessibleProvider interface here because
* all children of xul:tabpanels element acts as xul:tabpanel element.
* A tabpanel object, child elements of xul:tabpanels element.
*
* XXX: we need to move the class logic into generic class since
* for example we do not create instance of this class for XUL textbox used as

View File

@ -21,8 +21,8 @@ UNIFIED_SOURCES += [
LOCAL_INCLUDES += [
'../../../layout/generic',
'../../../layout/xul/base/src',
'../../../layout/xul/tree//',
'../../../layout/xul',
'../../../layout/xul/tree',
'../base',
'../generic',
'../html',

View File

@ -3,39 +3,6 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
MOCHITEST_A11Y_FILES =\
dumbfile.xpi \
formimage.png \
letters.gif \
moz.png \
$(topsrcdir)/content/media/test/bug461281.ogg \
longdesc_src.html \
actions.js \
attributes.js \
autocomplete.js \
browser.js \
common.js \
events.js \
grid.js \
layout.js \
name.js \
pivot.js \
relations.js \
role.js \
selectable.js \
states.js \
table.js \
value.js \
test_aria_token_attrs.html \
test_bug420863.html \
test_descr.html \
test_nsIAccessibleDocument.html \
test_nsIAccessibleImage.html \
test_OuterDocAccessible.html \
test_textboxes.html \
test_textboxes.xul \
testTextboxes.js \
text.js \
treeview.css \
treeview.js \
$(NULL)
MOCHITEST_A11Y_FILES += \
$(topsrcdir)/content/media/test/bug461281.ogg \
$(NULL)

View File

@ -0,0 +1,33 @@
[DEFAULT]
support-files =
dumbfile.xpi
formimage.png
letters.gif
moz.png
longdesc_src.html
actions.js
attributes.js
autocomplete.js
browser.js
common.js
events.js
grid.js
layout.js
name.js
pivot.js
relations.js
role.js
selectable.js
states.js
table.js
value.js
text.js
treeview.css
treeview.js
[test_aria_token_attrs.html]
[test_bug420863.html]
[test_descr.html]
[test_nsIAccessibleDocument.html]
[test_nsIAccessibleImage.html]
[test_OuterDocAccessible.html]

View File

@ -91,6 +91,23 @@ function testActions(aArray)
gActionsQueue.invoke();
}
/**
* Test action names and descriptions.
*/
function testActionNames(aID, aActions)
{
var actions = (typeof aActions == "string") ?
[ aActions ] : (aActions || []);
var acc = getAccessible(aID);
is(acc.actionCount, actions.length, "Wong number of actions.");
for (var i = 0; i < actions.length; i++ ) {
is(acc.getActionName(i), actions[i], "Wrong action name at " + i + " index.");
is(acc.getActionDescription(0), gActionDescrMap[actions[i]],
"Wrong action description at " + i + "index.");
}
}
////////////////////////////////////////////////////////////////////////////////
// Private
@ -151,3 +168,20 @@ function checkerOfActionInvoker(aType, aTarget, aActionObj)
aActionObj.checkOnClickEvent(aEvent);
}
}
var gActionDescrMap =
{
jump: "Jump",
press: "Press",
check: "Check",
uncheck: "Uncheck",
select: "Select",
open: "Open",
close: "Close",
switch: "Switch",
click: "Click",
collapse: "Collapse",
expand: "Expand",
activate: "Activate",
cycle: "Cycle"
};

View File

@ -65,7 +65,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=483573
var audioElm = getAccessible("audio");
var playBtn = audioElm.firstChild;
var scrubber = playBtn.nextSibling.nextSibling.nextSibling;
var muteBtn = audioElm.lastChild;
var muteBtn = audioElm.lastChild.previousSibling;
var actions = [
{

View File

@ -79,7 +79,8 @@ const COORDTYPE_PARENT_RELATIVE = nsIAccessibleCoordinateType.COORDTYPE_PARENT_R
const kEmbedChar = String.fromCharCode(0xfffc);
const kDiscBulletText = String.fromCharCode(0x2022) + " ";
const kDiscBulletChar = String.fromCharCode(0x2022);
const kDiscBulletText = kDiscBulletChar + " ";
const kCircleBulletText = String.fromCharCode(0x25e6) + " ";
const kSquareBulletText = String.fromCharCode(0x25aa) + " ";
@ -328,6 +329,14 @@ function getApplicationAccessible()
QueryInterface(nsIAccessibleApplication);
}
/**
* A version of accessible tree testing, doesn't fail if tree is not complete.
*/
function testElm(aID, aTreeObj)
{
testAccessibleTree(aID, aTreeObj, kSkipTreeFullCheck);
}
/**
* Flags used for testAccessibleTree
*/
@ -370,11 +379,7 @@ function testAccessibleTree(aAccOrElmOrID, aAccTree, aFlags)
switch (prop) {
case "actions": {
var actions = (typeof accTree.actions == "string") ?
[ accTree.actions ] : (accTree.actions || []);
is(acc.actionCount, actions.length, "Wong number of actions.");
for (var i = 0; i < actions.length; i++ )
is(acc.getActionName(i), actions[i], "Wrong action name at " + i + " index.");
testActionNames(acc, accTree.actions);
break;
}

View File

@ -56,7 +56,7 @@ function editableTextTest(aID)
/**
* setTextContents test.
*/
this.setTextContents = function setTextContents(aValue, aTrailChar)
this.setTextContents = function setTextContents(aValue)
{
var testID = "setTextContents '" + aValue + "' for " + prettyName(aID);
@ -66,8 +66,7 @@ function editableTextTest(aID)
acc.setTextContents(aValue);
}
var newValue = aValue + (aTrailChar ? aTrailChar : "");
var insertTripple = newValue ? [0, newValue.length, newValue] : null;
var insertTripple = aValue ? [0, aValue.length, aValue] : null;
var oldValue = getValue(aID);
var removeTripple = oldValue ? [0, oldValue.length, oldValue] : null;

View File

@ -20,14 +20,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
<script type="application/javascript">
function addTestEditable(aID, aTestRun, aTrailChar)
function addTestEditable(aID, aTestRun)
{
var et = new editableTextTest(aID);
//////////////////////////////////////////////////////////////////////////
// setTextContents
et.scheduleTest(et.setTextContents, "hello");
et.scheduleTest(et.setTextContents, "olleh", aTrailChar); // due to some reason this reports '\n' in the end
et.scheduleTest(et.setTextContents, "olleh");
et.scheduleTest(et.setTextContents, "");
//////////////////////////////////////////////////////////////////////////
@ -80,9 +80,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
{
// Prepare tested elements.
// Design mode on/off trigger document accessible subtree recreation.
// Design mode on/off triggers an editable state change event on
// the document accessible.
var frame = getNode("frame");
waitForEvent(EVENT_REORDER, frame.contentDocument, runTest);
waitForEvent(EVENT_STATE_CHANGE, frame.contentDocument, runTest);
frame.contentDocument.designMode = "on";
}

View File

@ -10,6 +10,8 @@
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../actions.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript"
@ -22,11 +24,6 @@
src="../name.js"></script>
<script type="application/javascript">
function testElm(aID, aTreeObj)
{
testAccessibleTree(aID, aTreeObj, kSkipTreeFullCheck);
}
function doTest()
{
//////////////////////////////////////////////////////////////////////////
@ -685,6 +682,34 @@
//////////////////////////////////////////////////////////////////////////
// HTML:input@type="number" and etc
obj = {
role: ROLE_SPINBUTTON,
interfaces: [ nsIAccessibleValue ],
children: [
{
role: ROLE_ENTRY,
extraStates: EXT_STATE_EDITABLE | EXT_STATE_SINGLE_LINE,
actions: "activate",
interfaces: [ nsIAccessibleText, nsIAccessibleEditableText ],
children: [
{ role: ROLE_TEXT_LEAF }
]
},
{
role: ROLE_PUSHBUTTON,
actions: "press"
},
{
role: ROLE_PUSHBUTTON,
actions: "press"
}
]
};
testElm("input_number", obj);
//////////////////////////////////////////////////////////////////////////
// HTML:input@type="text" and etc
obj = {
role: ROLE_ENTRY,
extraStates: EXT_STATE_EDITABLE | EXT_STATE_SINGLE_LINE,
@ -694,7 +719,6 @@
{ role: ROLE_TEXT_LEAF }
]
};
// TODO: re-enable in bug 559761 testElm("input_number", obj);
testElm("input_email", obj);
testElm("input_search", obj);
testElm("input_tel", obj);

View File

@ -28,7 +28,6 @@
function doTests()
{
if (MAC) {
todo(false, "Make these tests pass on OSX (bug 650294)");
SimpleTest.finish();
@ -38,10 +37,11 @@
gQueue = new eventQueue(EVENT_TEXT_CARET_MOVED);
var id = "textbox";
gQueue.push(new synthFocus(id, new caretMoveChecker(5, id)));
gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, id)));
gQueue.push(new synthHomeKey(id, new caretMoveChecker(0, id)));
gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
var input = getNode(id).inputField;
gQueue.push(new synthFocus(id, new caretMoveChecker(5, input)));
gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, input)));
gQueue.push(new synthHomeKey(id, new caretMoveChecker(0, input)));
gQueue.push(new synthRightKey(id, new caretMoveChecker(1, input)));
gQueue.invoke(); // Will call SimpleTest.finish();
}

View File

@ -17,6 +17,7 @@
src="../events.js"></script>
<script type="application/javascript">
SimpleTest.expectAssertions(0, 1);
var gFocusHandler = {
handleEvent: function(aEvent) {

View File

@ -38,8 +38,10 @@
// Test focus events.
gQueue = new eventQueue();
gQueue.push(new synthFocus("textbox"));
gQueue.push(new synthFocus("textbox_multiline"));
gQueue.push(new synthFocus("textbox",
new focusChecker(getNode("textbox").inputField)));
gQueue.push(new synthFocus("textbox_multiline",
new focusChecker(getNode("textbox_multiline").inputField)));
gQueue.push(new synthFocus("scale"));
gQueue.push(new synthFocusOnFrame("editabledoc"));
gQueue.push(new synthFocus("radioclothes",

View File

@ -22,7 +22,7 @@
<script type="application/javascript">
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true; // debug stuff
gA11yEventDumpToConsole = true; // debug stuff
var gQueue = null;
function doTests()

View File

@ -36,10 +36,11 @@
// Test focus events.
gQueue = new eventQueue();
var textbox = getNode("textbox").inputField;
gQueue.push(new synthClick("tab1", new focusChecker("tab1")));
gQueue.push(new synthTab("tab1", new focusChecker("checkbox1")));
gQueue.push(new synthKey("tab1", "VK_TAB", { ctrlKey: true },
new focusChecker("textbox")));
new focusChecker(textbox)));
gQueue.push(new synthKey("tab2", "VK_TAB", { ctrlKey: true },
new focusChecker("tab3")));
gQueue.push(new synthKey("tab3", "VK_TAB", { ctrlKey: true },

View File

@ -2,6 +2,7 @@
support-files =
jsatcommon.js
output.js
doc_traversal.html
[test_alive.html]
[test_braille.html]
@ -9,4 +10,5 @@ support-files =
[test_landmarks.html]
[test_live_regions.html]
[test_tables.html]
[test_traversal.html]
[test_utterance_order.html]

View File

@ -0,0 +1,117 @@
<!DOCTYPE html>
<html>
<head>
<title>Traversal Rule test document</title>
<meta charset="utf-8" />
</head>
<body>
<h3 id="heading-1">A small first heading</h3>
<form>
<label for="input-1-1">Name:</label>
<input id="input-1-1">
<label id="label-1-2">Favourite Ice Cream Flavour:<input id="input-1-2"></label>
<button id="button-1-1">Magic Button</button>
<label for="radio-1-1">Radios are old: </label>
<input id="radio-1-1" type="radio">
<label for="radio-1-2">Radios are new: </label>
<input id="radio-1-2" type="radio">
<label for="input-1-3">Password:</label>
<input id="input-1-3" type="password">
<label for="input-1-4">Unlucky number:</label>
<input id="input-1-4" type="tel">
<input id="button-1-2" type="button" value="Fun">
<label for="checkbox-1-1">Check me: </label>
<input id="checkbox-1-1" type="checkbox">
<select id="select-1-1">
<option>Value 1</option>
<option>Value 2</option>
<option>Value 3</option>
</select>
<select id="select-1-2" multiple="true">
<option>Value 1</option>
<option>Value 2</option>
<option>Value 3</option>
</select>
<label for="checkbox-1-2">Check me too: </label>
<input id="checkbox-1-2" type="checkbox">
<label for="checkbox-1-3">But not me: </label>
<input id="checkbox-1-3" type="checkbox" aria-hidden="true">
<label for="checkbox-1-4">Or me! </label>
<input id="checkbox-1-3" type="checkbox" style="visibility:hidden">
<select id="select-1-3" size="3">
<option>Value 1</option>
<option>Value 2</option>
<option>Value 3</option>
</select>
<label for="input-1-5">Electronic mailing address:</label>
<input id="input-1-5" type="email">
<input id="button-1-3" type="submit" value="Submit">
</form>
<h2 id="heading-2">A larger second</h2>
<div id="heading-3" role="heading">ARIA is fun</div>
<input id="button-2-1" type="button" value="More Fun">
<div id="button-2-2" tabindex="0" role="button">ARIA fun</div>
<div id="button-2-3" tabindex="0" role="button" aria-pressed="false">My little togglebutton</div>
<div id="button-2-4" tabindex="0" role="spinbutton">ARIA fun</div>
<h1 id="heading-4" style="display:none">Invisible header</h1>
<dl id="list-1">
<dt id="listitem-1-1">Programming Language</dt>
<dd>A esoteric weapon wielded by only the most formidable warriors,
for its unrelenting strict power is unfathomable.</dd>
</dl>
<ul id="list-2">
<li id="listitem-2-1">Lists of Programming Languages</li>
<li id="listitem-2-2">Lisp
<ol id="list-3">
<li id="listitem-3-1">Scheme</li>
<li id="listitem-3-2">Racket</li>
<li id="listitem-3-3">Clojure</li>
</ol>
</li>
<li id="listitem-2-3">JavaScript</li>
</ul>
<h6 id="heading-5">The last (visible) one!</h6>
<img id="image-1" src="http://example.com" alt="">
<img id="image-2" src="../moz.png" alt="stuff">
<div id="image-3" tabindex="0" role="img">Not actually an image</div>
<h4 id="heading-6" aria-hidden="true">Hidden header</h4>
<a id="link-1" href="http://www.mozilla.org">Link</a>
<a id="anchor-1">Words</a>
<a id="link-2" href="http://www.mozilla.org">Link the second</a>
<a id="anchor-2">Sentences</a>
<a id="link-3" href="http://www.example.com">Link the third</a>
<hr id="separator-1">
<table id="table-1">
<tr>
<td>3</td>
<td>1</td>
</tr>
<tr>
<td>4</td>
<td>1</td>
</tr>
</table>
<div id="separator-2" role="separator">Just an innocuous separator</div>
<table id="table-2">
<thead>
<tr>
<th>Dirty Words</th>
<th>Meaning</th>
</tr>
</thead>
<tfoot>
<tr>
<td>Mud</td>
<td>Wet Dirt</td>
</tr>
</tfoot>
<tbody>
<tr>
<td>Dirt</td>
<td>Messy Stuff</td>
</tr>
</tbody>
</table>
</body>
</html>

View File

@ -0,0 +1,121 @@
<!DOCTYPE html>
<html>
<head>
<title>Tests AccessFu TraversalRules</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="chrome://mochikit/content/chrome-harness.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" src="../layout.js"></script>
<script type="application/javascript">
Components.utils.import("resource://gre/modules/accessibility/TraversalRules.jsm");
var gBrowserWnd = null;
var gQueue = null;
function doTest()
{
var doc = currentTabDocument();
var docAcc = getAccessible(doc, [nsIAccessibleDocument]);
gQueue = new eventQueue();
gQueue.onFinish = function onFinish()
{
closeBrowserWindow();
}
queueTraversalSequence(gQueue, docAcc, TraversalRules.Heading, null,
['heading-1', 'heading-2', 'heading-3', 'heading-5']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Entry, null,
['input-1-1', 'label-1-2', 'input-1-3',
'input-1-4', 'input-1-5']);
// move back an element to hit all the form elements, because the VC is
// currently at the first input element
gQueue.push(new setVCPosInvoker(docAcc, "movePrevious",
TraversalRules.Heading, "heading-1"));
queueTraversalSequence(gQueue, docAcc, TraversalRules.FormElement, null,
['input-1-1', 'label-1-2', 'button-1-1',
'radio-1-1', 'radio-1-2', 'input-1-3',
'input-1-4', 'button-1-2', 'checkbox-1-1',
'select-1-1', 'select-1-2', 'checkbox-1-2',
'select-1-3', 'input-1-5', 'button-1-3',
'button-2-1', 'button-2-2', 'button-2-3',
'button-2-4']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Button, null,
['button-1-1', 'button-1-2', 'button-1-3',
'button-2-1', 'button-2-2', 'button-2-3',
'button-2-4']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.RadioButton, null,
['radio-1-1', 'radio-1-2']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Checkbox, null,
['checkbox-1-1', 'checkbox-1-2']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Combobox, null,
['select-1-1', 'select-1-2', 'select-1-3']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.List, null,
['list-1', 'list-2', 'list-3']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.ListItem, null,
['listitem-1-1', 'listitem-2-1', 'listitem-2-2',
'listitem-3-1', 'listitem-3-2', 'listitem-3-3',
'listitem-2-3']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Graphic, null,
['image-2', 'image-3']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Link, null,
['link-1', 'link-2', 'link-3']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Anchor, null,
['anchor-1', 'anchor-2']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Separator, null,
['separator-1', 'separator-2']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Table, null,
['table-1', 'table-2']);
gQueue.invoke();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(function () {
/* We open a new browser because we need to test with a top-level content
document. */
openBrowserWindow(
doTest,
getRootDirectory(window.location.href) + "doc_traversal.html");
});
</script>
</head>
<body id="body">
<a target="_blank"
title="Add tests for AccessFu TraversalRules"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=933808">Mozilla Bug 933808</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -28,7 +28,11 @@ DIRS += [
'textattrs',
'textcaret',
'textselection',
'tree',
'treeupdate',
'value',
]
A11Y_MANIFESTS += [
'a11y.ini',
'tree/a11y.ini',
]

View File

@ -33,7 +33,7 @@
//////////////////////////////////////////////////////////////////////////
// aria-labelledby
// Single relation. The value of 'aria-labelledby' contains the ID of
// an element. Gets the name from text node of that element.
testName("btn_labelledby_text", "text");

View File

@ -44,12 +44,12 @@
</pre>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg1">
<title>A description</title>
<desc>A name</desc>
<title>A name</title>
<desc>A description</title>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg2">
<title>A tooltip</title>
<desc>A tooltip</desc>
</svg>
</body>
</html>

View File

@ -18,11 +18,16 @@
<script type="application/javascript">
<![CDATA[
function getInput(aID)
{
return getNode(aID).inputField;
}
function doTest()
{
//////////////////////////////////////////////////////////////////////////
// Ordinary textbox
testStates("textbox",
testStates(getInput("textbox"),
STATE_FOCUSABLE,
EXT_STATE_EDITABLE,
STATE_PROTECTED | STATE_UNAVAILABLE,
@ -31,7 +36,7 @@
//////////////////////////////////////////////////////////////////////////
// Password textbox
testStates("password",
testStates(getInput("password"),
STATE_FOCUSABLE | STATE_PROTECTED,
EXT_STATE_EDITABLE,
STATE_UNAVAILABLE,
@ -40,7 +45,7 @@
//////////////////////////////////////////////////////////////////////////
// Textarea
testStates("textarea",
testStates(getInput("textarea"),
STATE_FOCUSABLE,
EXT_STATE_EDITABLE | EXT_STATE_MULTI_LINE,
STATE_PROTECTED | STATE_UNAVAILABLE,
@ -49,7 +54,7 @@
//////////////////////////////////////////////////////////////////////////
// Readonly textbox
testStates("readonly_textbox",
testStates(getInput("readonly_textbox"),
STATE_FOCUSABLE | STATE_READONLY,
EXT_STATE_EDITABLE,
STATE_PROTECTED | STATE_UNAVAILABLE,
@ -58,7 +63,7 @@
//////////////////////////////////////////////////////////////////////////
// Disabled textbox
testStates("disabled_textbox",
testStates(getInput("disabled_textbox"),
STATE_UNAVAILABLE,
EXT_STATE_EDITABLE,
STATE_FOCUSABLE | STATE_PROTECTED,
@ -67,7 +72,7 @@
//////////////////////////////////////////////////////////////////////////
// Readonly textarea
testStates("readonly_textarea",
testStates(getInput("readonly_textarea"),
STATE_FOCUSABLE | STATE_READONLY,
EXT_STATE_EDITABLE | EXT_STATE_MULTI_LINE,
STATE_PROTECTED | STATE_UNAVAILABLE,
@ -76,7 +81,7 @@
//////////////////////////////////////////////////////////////////////////
// Disabled textarea
testStates("disabled_textarea",
testStates(getInput("disabled_textarea"),
STATE_UNAVAILABLE,
EXT_STATE_EDITABLE| EXT_STATE_MULTI_LINE,
STATE_PROTECTED | STATE_FOCUSABLE,
@ -86,7 +91,7 @@
//////////////////////////////////////////////////////////////////////////
// Search textbox without search button, searches as you type and filters
// a separate control.
testStates("searchbox",
testStates(getInput("searchbox"),
STATE_FOCUSABLE,
EXT_STATE_EDITABLE | EXT_STATE_SUPPORTS_AUTOCOMPLETION,
STATE_PROTECTED | STATE_UNAVAILABLE,
@ -95,7 +100,7 @@
//////////////////////////////////////////////////////////////////////////
// Search textbox with search button, does not support autoCompletion.
testStates("searchfield",
testStates(getInput("searchfield"),
STATE_FOCUSABLE,
EXT_STATE_EDITABLE,
STATE_PROTECTED | STATE_UNAVAILABLE,

View File

@ -1,33 +0,0 @@
function testValue(aID, aAcc, aValue, aRole)
{
is(aAcc.value, aValue, "Wrong value for " + aID + "!");
}
function testAction(aID, aAcc, aActionCount, aActionName, aActionDescription)
{
var actionCount = aAcc.actionCount;
is(actionCount, aActionCount, "Wrong number of actions for " + aID + "!");
if (actionCount != 0) {
// Test first action. Normally only 1 should be present.
is(aAcc.getActionName(0), aActionName,
"Wrong name of action for " + aID + "!");
is(aAcc.getActionDescription(0), aActionDescription,
"Wrong description of action for " + aID + "!");
}
}
function testThis(aID, aName, aValue, aDescription, aRole,
aActionCount, aActionName, aActionDescription)
{
var acc = getAccessible(aID);
if (!acc)
return;
is(acc.name, aName, "Wrong name for " + aID + "!");
testValue(aID, acc, aValue, aRole);
is(acc.description, aDescription, "Wrong description for " + aID + "!");
testRole(aID, aRole);
testAction(aID, acc, aActionCount, aActionName, aActionDescription);
}

View File

@ -1,164 +0,0 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=442648
-->
<head>
<title>nsIAccessible textboxes chrome tests</title>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="common.js"></script>
<script type="application/javascript"
src="role.js"></script>
<script type="application/javascript"
src="states.js"></script>
<script type="application/javascript"
src="testTextboxes.js"></script>
<script type="application/javascript">
function doTest()
{
//////////////////////////////////////////////////////////////////////////
// normal textbox without content and with no proper label
testThis("unlabelled_Textbox", // ID
null, // name
"", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// normal textbox without content and with a proper label
testThis("labelled_textbox", // ID
"Second textbox:", // name
"", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// normal textbox with content and with a proper label
testThis("prefilled_textbox", // ID
"Textbox with predefined value:", // name
"I have some text", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// password textbox with a proper label
testThis("password_textbox", // ID
"Enter some password here:", // name
"", // value
"", // description
ROLE_PASSWORD_TEXT, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// textarea without content and label
testThis("unlabelled_Textarea", // ID
null, // name
"", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// textarea without content and with proper label
testThis("labelled_textarea", // ID
"Labelled textarea:", // name
"", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// textarea with content and with proper label
testThis("prefilled_textarea", // ID
"Pre-filled textarea:", // name
" I also have some text.\n ", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// readonly textbox with content and with proper label
testThis("readonly_textbox", // ID
"The following is a read-only textbox:", // name
"You cannot change me.", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// readonly textarea with content and with proper label
testThis("readonly_textarea", // ID
"This textarea is readonly, too:", // name
" You cannot change me, either.\n ", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=442648">Mozilla Bug 442648</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<form action="test.php" method="post">
Text before input without labelling it:
<input type="text" name="unlabelled_Textbox" id="unlabelled_Textbox" size="50"/>
<br><label for="labelled_textbox">Second textbox:</label>
<input type="text" name="labelled_Textbox" id="labelled_textbox"/>
<br><label for="prefilled_textbox">Textbox with predefined value:</label>
<input type="text" name="prefilled_Textbox" id="prefilled_textbox" value="I have some text" size="80"/>
<br><label for="password_textbox">Enter some password here:</label>
<input type="password" name="password_Textbox" id="password_textbox"/>
<br>Textarea without label:<br>
<textarea id="unlabelled_Textarea" name="unlabelled_Textarea" cols="80" rows="5"></textarea>
<br><label for="labelled_textarea">Labelled textarea:</label><br>
<textarea id="labelled_textarea" name="labelled_Textarea" cols="80" rows="5"></textarea>
<br><label for="prefilled_textarea">Pre-filled textarea:</label><br>
<textarea id="prefilled_textarea" name="prefilled_Textarea" cols="80" rows="5">
I also have some text.
</textarea>
<br><label for="readonly_textbox">The following is a read-only textbox:</label>
<input type="text" readonly="true" name="readonly_Textbox" id="readonly_textbox" value="You cannot change me."/>
<br><label for="readonly_textarea">This textarea is readonly, too:</label><br>
<textarea name="readonly_Textarea" id="readonly_textarea" readonly="true" cols="80" rows="5">
You cannot change me, either.
</textarea>
</form>
</body>
</html>

View File

@ -1,217 +0,0 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="nsIAccessible XUL textboxes chrome tests">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="common.js" />
<script type="application/javascript"
src="role.js" />
<script type="application/javascript"
src="states.js" />
<script type="application/javascript"
src="testTextboxes.js" />
<script type="application/javascript">
<![CDATA[
function doTest()
{
//////////////////////////////////////////////////////////////////////////
// normal textbox without content and with no proper label
testThis("unlabelled_Textbox", // ID
null, // name
"", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// normal textbox without content and with a proper label
testThis("labelled_textbox", // ID
"Second textbox:", // name
"", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// normal textbox with content and with a proper label
testThis("prefilled_textbox", // ID
"Textbox with predefined value:", // name
"I have some text", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// password textbox with a proper label
testThis("password_textbox", // ID
"Enter some password here:", // name
"", // value
"", // description
ROLE_PASSWORD_TEXT, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// textarea without content and label
testThis("unlabelled_Textarea", // ID
null, // name
"", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// textarea without content and with proper label
testThis("labelled_textarea", // ID
"Labelled textarea:", // name
"", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// textarea with content and with proper label
testThis("prefilled_textarea", // ID
"Pre-filled textarea:", // name
"I also have some text.", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// readonly textbox with content and with proper label
testThis("readonly_textbox", // ID
"The following is a read-only textbox:", // name
"You cannot change me.", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// readonly textarea with content and with proper label
testThis("readonly_textarea", // ID
"This textarea is readonly, too:", // name
"You cannot change me, either.", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// Search textbox without search button, searches as you type and filters
// a separate control.
testThis("search-box", // ID
"Search History:", // name
"", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
//////////////////////////////////////////////////////////////////////////
// Search textbox with search button, does not support autoCompletion.
testThis("searchfield", // ID
"Search all add-ons", // name
"", // value
"", // description
ROLE_ENTRY, // role
1, // actionCount
"activate", // ActionName
"Activate"); // ActionDescription
testStates("searchfield", 0, 0, 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=442648">
Mozilla Bug 442648
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
</body>
<vbox>
<hbox>
<label value="Text before input without labelling it:"/>
<textbox id="unlabelled_Textbox" size="50"/>
</hbox>
<hbox>
<label control="labelled_textbox">Second textbox:</label>
<textbox id="labelled_textbox"/>
</hbox>
<hbox>
<label control="prefilled_textbox">Textbox with predefined value:</label>
<textbox id="prefilled_textbox" value="I have some text" size="80"/>
</hbox>
<hbox>
<label control="password_textbox">Enter some password here:</label>
<textbox type="password" id="password_textbox"/>
</hbox>
<vbox>
<label value="Textarea without label:"/>
<textbox multiline="true" id="unlabelled_Textarea" cols="80" rows="5"/>
</vbox>
<vbox>
<label control="labelled_textarea">Labelled textarea:</label>
<textbox multiline="true" id="labelled_textarea" cols="80" rows="5"/>
</vbox>
<vbox>
<label control="prefilled_textarea">Pre-filled textarea:</label>
<textbox multiline="true" id="prefilled_textarea" cols="80" rows="5"
value="I also have some text."/>
</vbox>
<hbox>
<label control="readonly_textbox">The following is a read-only textbox:</label>
<textbox readonly="true" id="readonly_textbox"
value="You cannot change me."/>
</hbox>
<vbox>
<label control="readonly_textarea">This textarea is readonly, too:</label>
<textbox multiline="true" id="readonly_textarea" readonly="true" cols="80"
rows="5" value="You cannot change me, either."/>
</vbox>
<hbox>
<label value="Search History:" accesskey="S"
control="search-box"/>
<textbox id="search-box" flex="1" type="search"
results="historyTree"/>
</hbox>
<textbox id="searchfield" placeholder="Search all add-ons"
type="search" searchbutton="true"/>
</vbox>
</window>

View File

@ -43,7 +43,7 @@
//////////////////////////////////////////////////////////////////////////
// XUL textbox
testTextAtOffset([ "tbox1" ], BOUNDARY_LINE_START,
testTextAtOffset([ getNode("tbox1").inputField ], BOUNDARY_LINE_START,
[ [ 0, 4, "test", 0, 4 ] ]);
SimpleTest.finish();

View File

@ -108,6 +108,12 @@
BOUNDARY_LINE_START,
[ [ 0, 3, "foo\n", 0, 4 ], [ 4, 4, "", 4, 4 ] ]);
//////////////////////////////////////////////////////////////////////////
// list items
testTextAtOffset([ "li1" ], BOUNDARY_LINE_START,
[ [ 0, 5, kDiscBulletChar + "Item", 0, 5 ] ]);
SimpleTest.finish();
}
@ -166,5 +172,9 @@ two words
<iframe id="ht_2" src="data:text/html,<div contentEditable='true'>foo<br/></div>"></iframe>
<iframe id="ht_3" src="data:text/html,<div contentEditable='true'>foo<br/><br/></div>"></iframe>
<ul>
<li id="li1">Item</li>
</ul>
</body>
</html>

View File

@ -1,43 +0,0 @@
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
MOCHITEST_A11Y_FILES =\
dockids.html \
$(filter disabled-temporarily--bug-561508, test_applicationacc.xul) \
test_aria_globals.html \
test_aria_grid.html \
test_aria_imgmap.html \
test_aria_list.html \
test_aria_menu.html \
test_aria_presentation.html \
test_brokencontext.html \
test_button.xul \
test_canvas.html \
test_combobox.xul \
test_cssoverflow.html \
test_dochierarchy.html \
test_dockids.html \
test_filectrl.html \
test_formctrl.html \
test_formctrl.xul \
test_gencontent.html \
test_groupbox.xul \
test_iframe.html \
test_img.html \
test_invalid_img.xhtml \
test_invalidationlist.html \
test_list.html \
test_map.html \
test_media.html \
test_select.html \
test_tabbox.xul \
test_tabbrowser.xul \
test_table.html \
test_tree.xul \
test_txtcntr.html \
test_txtctrl.html \
test_txtctrl.xul \
wnd.xul \
$(NULL)

View File

@ -0,0 +1,40 @@
[DEFAULT]
support-files =
dockids.html
wnd.xul
[test_applicationacc.xul]
skip-if = true # Bug 561508
[test_aria_globals.html]
[test_aria_grid.html]
[test_aria_imgmap.html]
[test_aria_list.html]
[test_aria_menu.html]
[test_aria_presentation.html]
[test_brokencontext.html]
[test_button.xul]
[test_canvas.html]
[test_combobox.xul]
[test_cssoverflow.html]
[test_dochierarchy.html]
[test_dockids.html]
[test_filectrl.html]
[test_formctrl.html]
[test_formctrl.xul]
[test_gencontent.html]
[test_groupbox.xul]
[test_iframe.html]
[test_img.html]
[test_invalid_img.xhtml]
[test_invalidationlist.html]
[test_list.html]
[test_map.html]
[test_media.html]
[test_select.html]
[test_tabbox.xul]
[test_tabbrowser.xul]
[test_table.html]
[test_tree.xul]
[test_txtcntr.html]
[test_txtctrl.html]
[test_txtctrl.xul]

View File

@ -1,6 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

View File

@ -63,6 +63,15 @@
accTree = { SLIDER: [ ] };
testAccessibleTree("range", accTree);
// input@type="number"
accTree =
{ SPINBUTTON: [
{ ENTRY: [ ] },
{ PUSHBUTTON: [ ] },
{ PUSHBUTTON: [ ] }
] };
testAccessibleTree("number", accTree);
// output
accTree = {
role: ROLE_SECTION,
@ -116,6 +125,7 @@
<input type="submit" id="submit">
<input type="image" id="image_submit">
<input type="range" id="range">
<input type="number" id="number">
<output id="output">1337</output>
</body>

View File

@ -3,9 +3,17 @@
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<!-- Firefox toolbar -->
<?xml-stylesheet href="chrome://browser/content/browser.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Accessible XUL checkbox and radio hierarchy tests">
<!-- Firefox toolbar -->
<script type="application/javascript"
src="chrome://browser/content/browser.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
@ -76,6 +84,9 @@
testAccessibleTree("toolbar2", accTree);
if (!SEAMONKEY)
testAccessibleTree("tb_customizable", { TOOLBAR: [] });
SimpleTest.finish()
}
@ -110,6 +121,8 @@
<toolbar id="toolbar2" toolbarname="2nd" aria-label="My second toolbar">
<toolbarbutton id="button2" label="hello"/>
</toolbar>
<toolbar id="tb_customizable" customizable="true"/>
</vbox>
</hbox>

View File

@ -49,7 +49,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=483573
role: ROLE_PUSHBUTTON,
name: "Mute",
children: []
}
},
{ // slider of volume bar
role: ROLE_SLIDER,
children: []
},
]
};
testAccessibleTree("audio", accTree);

View File

@ -23,64 +23,68 @@
function doTest()
{
////////////////////
// textbox
////////////////////
var accTree = {
role: ROLE_ENTRY,
children: [
{
role: ROLE_TEXT_LEAF,
children: []
}
]
};
//////////////////////////////////////////////////////////////////////////
// textboxes
var accTree =
{ SECTION: [
{ ENTRY: [ { TEXT_LEAF: [] } ] },
{ MENUPOPUP: [] }
] };
// default textbox
testAccessibleTree("txc1", accTree);
testAccessibleTree("txc", accTree);
// number textbox
testAccessibleTree("txc2", accTree);
// multiline
testAccessibleTree("txc_multiline", accTree);
//////////////////////////////////////////////////////////////////////////
// search textbox
testAccessibleTree("txc3", accTree);
// timed textbox
testAccessibleTree("txc4_deprecated", accTree);
if (MAC) {
accTree =
{ SECTION: [
{ ENTRY: [ { TEXT_LEAF: [] } ] },
{ MENUPOPUP: [] }
] };
} else {
accTree =
{ SECTION: [
{ ENTRY: [ { TEXT_LEAF: [] } ] },
{ PUSHBUTTON: [] },
{ MENUPOPUP: [] }
] };
}
////////////////////
testAccessibleTree("txc_search", accTree);
//////////////////////////////////////////////////////////////////////////
// number textbox
accTree =
{ SECTION: [
{ ENTRY: [ { TEXT_LEAF: [] } ] },
{ MENUPOPUP: [] },
{ PUSHBUTTON: [] },
{ PUSHBUTTON: [] }
] };
testAccessibleTree("txc_number", accTree);
//////////////////////////////////////////////////////////////////////////
// password textbox
////////////////////
accTree = {
role: ROLE_PASSWORD_TEXT,
children: [
{
role: ROLE_TEXT_LEAF,
children: []
}
]
};
testAccessibleTree("txc5", accTree);
accTree =
{ SECTION: [
{ PASSWORD_TEXT: [ { TEXT_LEAF: [] } ] },
{ MENUPOPUP: [] }
] };
////////////////////
// multiline textbox
////////////////////
accTree = {
role: ROLE_ENTRY,
children: [
{
role: ROLE_TEXT_LEAF,
children: []
}
]
};
testAccessibleTree("txc_password", accTree);
testAccessibleTree("txc6", accTree);
////////////////////
//////////////////////////////////////////////////////////////////////////
// autocomplete textbox
////////////////////
accTree = {
// textbox
role: ROLE_AUTOCOMPLETE,
@ -104,14 +108,14 @@
]
};
function test_txc7() {
testAccessibleTree("txc7", accTree);
function test_AutocompleteControl() {
testAccessibleTree("txc_autocomplete", accTree);
SimpleTest.finish();
}
// XPFE and Toolkit autocomplete widgets differ.
var txc7 = document.getElementById("txc7");
if ("clearResults" in txc7) {
var txc = document.getElementById("txc_autocomplete");
if ("clearResults" in txc) {
SimpleTest.ok(true, "Testing (Old) XPFE autocomplete widget.");
// Popup is always created. (See code below.)
@ -141,14 +145,14 @@
]
}
);
test_txc7();
test_AutocompleteControl();
} else {
SimpleTest.ok(true, "Testing (New) Toolkit autocomplete widget.");
// Dumb access to trigger popup lazy creation.
waitForEvent(EVENT_REORDER, txc7, test_txc7);
txc7.popup;
waitForEvent(EVENT_REORDER, txc, test_AutocompleteControl);
txc.popup;
accTree.children.push(
{
@ -189,15 +193,12 @@
</body>
<vbox flex="1">
<textbox id="txc1" value="hello"/>
<textbox id="txc2" type="number" value="44"/>
<textbox id="txc3" type="search" value="hello"/>
<!-- This textbox triggers "Warning: Timed textboxes are deprecated. Consider using type="search" instead.".
Yet let's test it too as long as it's (still) supported. -->
<textbox id="txc4_deprecated" type="timed" value="hello"/>
<textbox id="txc5" type="password" value="hello"/>
<textbox id="txc6" multiline="true" value="hello"/>
<textbox id="txc7" type="autocomplete" value="hello"/>
<textbox id="txc" value="hello"/>
<textbox id="txc_search" type="search" value="hello"/>
<textbox id="txc_number" type="number" value="44"/>
<textbox id="txc_password" type="password" value="hello"/>
<textbox id="txc_multiline" multiline="true" value="hello"/>
<textbox id="txc_autocomplete" type="autocomplete" value="hello"/>
</vbox>
</hbox>

View File

@ -1,6 +1,7 @@
[DEFAULT]
[test_general.html]
[test_number.html]
[test_progress.html]
[test_progress.xul]
[test_range.html]

View File

@ -0,0 +1,59 @@
<html>
<head>
<title>nsIAccessible value testing for input@type=range element</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../value.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/chrome-harness.js"></script>
<script type="application/javascript">
function doTest()
{
// HTML5 number element tests
testValue("number", "", 0, 0, 0, 1);
testValue("number_value", "1", 1, 0, 0, 1);
testValue("number_step", "", 0, 0, 0, 1);
testValue("number_min42", "", 0, 42, 0, 1);
testValue("number_max42", "", 0, 0, 42, 1);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=559761"
title="make HTML5 input@type=number element accessible">
Bug 559761
</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<!-- HTML5 input@type=number element -->
<input type="number" id="number">
<input type="number" id="number_value" value="1">
<input type="number" id="number_step" step="1">
<input type="number" id="number_min42" min="42">
<input type="number" id="number_max42" max="42">
</body>
</html>

1
aclocal.m4 vendored
View File

@ -30,6 +30,7 @@ builtin(include, build/autoconf/zlib.m4)dnl
builtin(include, build/autoconf/linux.m4)dnl
builtin(include, build/autoconf/python-virtualenv.m4)dnl
builtin(include, build/autoconf/winsdk.m4)dnl
builtin(include, build/autoconf/icu.m4)dnl
MOZ_PROG_CHECKMSYS()

View File

@ -96,7 +96,7 @@ package.json modified: please re-run 'cfx run'
alt="Mozilla icon widget" />
Run `cfx run` again, and it will run an instance of Firefox. In the
bottom-right corner of the browser you'll see an icon with the Firefox
bottom-right corner of the browser you'll see an icon with the Mozilla
logo. Click the icon, and a new tab will open with
[http://www.mozilla.org/](http://www.mozilla.org/) loaded into it.

View File

@ -35,13 +35,13 @@ geolocation API in Firefox.
## Using Geolocation in an Add-on ##
Suppose we want to use the
[geolocation API built into Firefox](https://developer.mozilla.org/en/using_geolocation).
[geolocation API built into Firefox](https://developer.mozilla.org/en-US/docs/WebAPI/Using_geolocation).
The SDK doesn't provide an API to access geolocation, but we can
[access the underlying XPCOM API using `require("chrome")`](dev-guide/guides/xul-migration.html#xpcom).
The following add-on adds a [button to the toolbar](dev-guide/tutorials/adding-toolbar-button.html):
when the user clicks the button, it loads the
[XPCOM nsIDOMGeoGeolocation](https://developer.mozilla.org/en/XPCOM_Interface_Reference/NsIDOMGeoGeolocation)
[XPCOM nsIDOMGeoGeolocation](https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/NsIDOMGeoGeolocation)
object, and retrieves the user's current position:
var {Cc, Ci} = require("chrome");
@ -88,7 +88,7 @@ info: longitude: 93.0785269
</pre>
So far, so good. But the geolocation guide on MDN tells us that we must
[ask the user for permission](https://developer.mozilla.org/en/using_geolocation#Prompting_for_permission)
[ask the user for permission](https://developer.mozilla.org/en-US/docs/WebAPI/Using_geolocation#Prompting_for_permission)
before using the API.
So we'll extend the add-on to include an adapted version of the code in
@ -384,4 +384,4 @@ to add your name as the author, choose a distribution license, and so on.
To see some of the modules people have already developed, see the page of
[community-developed modules](https://github.com/mozilla/addon-sdk/wiki/Community-developed-modules).
To learn how to use third-party modules in your own code, see the
[tutorial on adding menu items](dev-guide/tutorials/adding-menus.html).
[tutorial on adding menu items](dev-guide/tutorials/adding-menus.html).

View File

@ -13,15 +13,17 @@ When the method is invoked on an instance of the object, the original
function is called. It is passed the object instance (i.e. `this`) as
the first parameter, followed by any parameters passed into the method.
let { method } = require("sdk/lang/functional");
let myNumber = {
const { method } = require("sdk/lang/functional");
const times = (target, x) => target.number *= x;
const add = (target, x) => target.number += x;
const myNumber = {
times: method(times),
add: method(add),
number: 0
};
function times (target, x) { return target.number *= x; }
function add (target, x) { return target.number += x; }
console.log(myNumber.number); // 0
myNumber.add(10); // 10
@ -44,8 +46,8 @@ wait (i.e. `setTimeout(function () { ... }, 0)`), except that the wrapped functi
may be reused and does not need to be repeated each time. This also enables you
to use these functions as event listeners.
let { defer } = require("sdk/lang/functional");
let fn = defer(function myEvent (event, value) {
const { defer } = require("sdk/lang/functional");
const fn = defer((event, value) => {
console.log(event + " : " + value);
});
@ -74,16 +76,11 @@ An alias for [defer](modules/sdk/lang/functional.html#defer(fn)).
Invokes `callee`, passing `params` as an argument and `self` as `this`.
Returns the value that is returned by `callee`.
let { invoke } = require("sdk/lang/functional");
const { invoke } = require("sdk/lang/functional");
const sum = (...args) => args.reduce((a, b) => a + b);
invoke(sum, [1,2,3,4,5], null); // 15
function sum () {
return Array.slice(arguments).reduce(function (a, b) {
return a + b;
});
}
@param callee {function}
Function to invoke.
@param params {Array}
@ -98,9 +95,9 @@ Returns the value that is returned by `callee`.
@function
Takes a function and bind values to one or more arguments, returning a new function of smaller arity.
let { partial } = require("sdk/lang/functional");
let add = function add (x, y) { return x + y; }
let addOne = partial(add, 1);
const { partial } = require("sdk/lang/functional");
const add = (x, y) => x + y;
const addOne = partial(add, 1);
addOne(5); // 6
addOne(10); // 11
@ -122,14 +119,16 @@ Returns the [composition](http://en.wikipedia.org/wiki/Function_composition_(com
return value of the function that follows. In math terms, composing the functions
`f()`, `g()`, and `h()` produces `f(g(h()))`.
let { compose } = require("sdk/lang/functional");
const { compose } = require("sdk/lang/functional");
let welcome = compose(exclaim, greet);
const square = x => x * x;
const increment = x => x + 1;
welcome('moe'); // "hi: moe!";
const f1 = compose(increment, square);
f1(5); // => 26
function greet (name) { return "hi: " + name; }
function exclaim (statement) { return statement + "!"; }
const f2 = compose(square, increment);
f2(5); // => 36
@param fn... {function}
Takes a variable number of functions as arguments and composes them from right to left.
@ -144,16 +143,14 @@ Returns the first function passed as an argument to the second,
allowing you to adjust arguments, run code before and after, and
conditionally execute the original function.
let { wrap } = require("sdk/lang/functional");
const { wrap } = require("sdk/lang/functional");
let wrappedHello = wrap(hello, function (fn, name) {
return "before, " + fn(name) + "after";
});
const hello = name => "hello: " + name;
const wrappedHello = wrap(hello, (fn, name) =>
"before, " + fn(name) + "after");
wrappedHello("moe"); // "before, hello: moe, after"
function hello (name) { return "hello: " + name; }
@param fn {function}
The function to be passed into the `wrapper` function.
@ -170,8 +167,8 @@ conditionally execute the original function.
@function
Returns the same value that is used as the argument. In math: f(x) = x.
let { identity } = require("sdk/lang/functional");
let x = 5;
const { identity } = require("sdk/lang/functional");
const x = 5;
identity(x); // 5
@param value {mixed}
@ -190,15 +187,15 @@ storing the result, based on the arguments to the original function. The
default `hashFunction` just uses the first argument to the memoized function as
the key.
let { memoize } = require("sdk/lang/functional");
const { memoize } = require("sdk/lang/functional");
let memoizedFn = memoize(primeFactorization);
const memoizedFn = memoize(primeFactorization);
memoizedFn(50); // Returns [2, 5, 5], had to compute
memoizedFn(100); // Returns [2, 2, 5, 5], had to compute
memoizedFn(50); // Returns [2, 5, 5] again, but pulled from cache
function primeFactorization (x) {
const primeFactorization = x => {
// Some tricky stuff
}
@ -209,16 +206,14 @@ the key.
// function will just parse the last name, as our naive
// implementation assumes that they will share the same lineage
let getLineage = memoize(function (name) {
const getLineage = memoize(name => {
// computes lineage
return data;
}, hasher);
// Hashing function takes a string of first and last name
// and returns the last name.
function hasher (input) {
return input.split(" ")[1];
}
const hasher = input => input.split(" ")[1];
getLineage("homer simpson"); // Computes and returns information for "simpson"
getLineage("lisa simpson"); // Returns cached for "simpson"
@ -240,12 +235,11 @@ Much like `setTimeout`, `delay` invokes a function after waiting a set number of
milliseconds. If you pass additional, optional, arguments, they will be forwarded
on to the function when it is invoked.
let { delay } = require("sdk/lang/functional");
const { delay } = require("sdk/lang/functional");
const printAdd = (a, b) console.log(a + "+" + b + "=" + (a+b));
delay(printAdd, 2000, 5, 10);
// Prints "5+10=15" in two seconds (2000ms)
function printAdd (a, b) { console.log(a + "+" + b + "=" + (a+b)); }
@param fn {function}
A function to be delayed.
@ -264,8 +258,8 @@ Repeated calls to the modified function will have no effect, returning
the value from the original call. Useful for initialization functions, instead
of having to set a boolean flag and checking it later.
let { once } = require("sdk/lang/functional");
let setup = once(function (env) {
const { once } = require("sdk/lang/functional");
const setup = once(env => {
// initializing important things
console.log("successfully initialized " + env);
return 1; // Assume success and return 1
@ -273,7 +267,7 @@ of having to set a boolean flag and checking it later.
setup('dev'); // returns 1
// prints "successfully initialized dev"
// Future attempts to call this function just return the cached
// value that was returned previously
setup('production'); // Returns 1
@ -286,16 +280,156 @@ of having to set a boolean flag and checking it later.
The wrapped `fn` that can only be executed once.
</api>
<api name="chain">
<api name="cache">
@function
An alias for [once](modules/sdk/lang/functional.html#once(fn)).
</api>
<api name="complement">
@function
Takes a `f` function and returns a function that takes the same
arguments as `f`, has the same effects, if any, and returns the
opposite truth value.
const { complement } = require("sdk/lang/functional");
let isOdd = x => Boolean(x % 2);
isOdd(1) // => true
isOdd(2) // => false
let isEven = complement(isOdd);
isEven(1) // => false
isEven(2) // => true
@param lambda {function}
The function to compose from
@returns {boolean}
`!lambda(...)`
</api>
<api name="constant">
@function
Constructs function that returns `x` no matter what is it
invoked with.
const { constant } = require("sdk/lang/functional");
const one = constant(1);
one(); // => 1
one(2); // => 1
one.apply({}, 3); // => 1
@param x {object}
Value that will be returned by composed function
@returns {function}
</api>
<api name="apply">
@function
Apply function that behaves like `apply` in other functional
languages:
const { apply } = require("sdk/lang/functional");
const dashify = (...args) => args.join("-");
apply(dashify, 1, [2, 3]); // => "1-2-3"
apply(dashify, "a"); // => "a"
apply(dashify, ["a", "b"]); // => "a-b"
apply(dashify, ["a", "b"], "c"); // => "a,b-c"
apply(dashify, [1, 2], [3, 4]); // => "1,2-3-4"
@param f {function}
function to be invoked
</api>
<api name="flip">
@function
Returns function identical to given `f` but with flipped order
of arguments.
const { flip } = require("sdk/lang/functional");
const append = (left, right) => left + " " + right;
const prepend = flip(append);
append("hello", "world") // => "hello world"
prepend("hello", "world") // => "world hello"
@param f {function}
function whose arguments should to be flipped
@returns {function}
function with flipped arguments
</api>
<api name="when">
@function
Takes `p` predicate, `consequent` function and an optional
`alternate` function and composes function that returns
application of arguments over `consequent` if application over
`p` is `true` otherwise returns application over `alternate`.
If `alternate` is not a function returns `undefined`.
const { when } = require("sdk/lang/functional");
function Point(x, y) {
this.x = x
this.y = y
}
const isPoint = x => x instanceof Point;
const inc = when(isPoint, ({x, y}) => new Point(x + 1, y + 1));
inc({}); // => undefined
inc(new Point(0, 0)); // => { x: 1, y: 1 }
const axis = when(isPoint,
({ x, y }) => [x, y],
_ => [0, 0]);
axis(new Point(1, 4)); // => [1, 4]
axis({ foo: "bar" }); // => [0, 0]
@param p {function}
predicate function whose return value determines to which
function be delegated control.
@param consequent {function}
function to which arguments are applied if `predicate` returned
`true`.
@param alternate {function}
function to which arguments are applied if `predicate` returned
`false`.
@returns {object|string|number|function}
Return value from `consequent` if `p` returned `true` or return
value from `alternate` if `p` returned `false`. If `alternate`
is not provided and `p` returned `false` then `undefined` is
returned.
</api>
<api name="chainable">
@function
Creates a version of the input function that will return `this`.
let { chain } = require("sdk/lang/functional");
const { chainable } = require("sdk/lang/functional");
function Person (age) { this.age = age; }
Person.prototype.happyBirthday = chain(function () this.age++);
Person.prototype.happyBirthday = chainable(function() {
return this.age++
});
let person = new Person(30);
const person = new Person(30);
person
.happyBirthday()
@ -311,7 +445,126 @@ Creates a version of the input function that will return `this`.
The wrapped function that executes `fn` and returns `this`.
</api>
<api name="cache">
<api name="field">
@function
An alias for [once](modules/sdk/lang/functional.html#once(fn)).
Takes field `name` and `target` and returns value of that field.
If `target` is `null` or `undefined` it would be returned back
instead of attempt to access it's field. Function is implicitly
curried, this allows accessor function generation by calling it
with only `name` argument.
const { field } = require("sdk/lang/functional");
field("x", { x: 1, y: 2}); // => 1
field("x")({ x: 1 }); // => 1
field("x", { y: 2 }); // => undefiend
const getX = field("x");
getX({ x: 1 }); // => 1
getX({ y: 1 }); // => undefined
getX(null); // => null
@param name {string}
Name of the field to be returned
@param target {object}
Target to get a field by the given `name` from
@returns {object|function|string|number|boolean}
Field value
</api>
<api name="query">
@function
Takes `.` delimited string representing `path` to a nested field
and a `target` to get it from. For convinience function is
implicitly curried, there for accessors can be created by invoking
it with just a `path` argument.
const { query } = require("sdk/lang/functional");
query("x", { x: 1, y: 2}); // => 1
query("top.x", { x: 1 }); // => undefiend
query("top.x", { top: { x: 2 } }); // => 2
const topX = query("top.x");
topX({ top: { x: 1 } }); // => 1
topX({ y: 1 }); // => undefined
topX(null); // => null
@param path {string}
`.` delimited path to a field
@param target {object}
Target to get a field by the given `name` from
@returns {object|function|string|number|boolean}
Field value
</api>
<api name="isInstance">
@function
Takes `Type` (constructor function) and a `value` and returns
`true` if `value` is instance of the given `Type`. Function is
implicitly curried this allows predicate generation by calling
function with just first argument.
const { isInstance } = require("sdk/lang/functional");
function X() {}
function Y() {}
let isX = isInstance(X);
isInstance(X, new X); // true
isInstance(X)(new X); // true
isInstance(X, new Y); // false
isInstance(X)(new Y); // false
isX(new X); // true
isX(new Y); // false
@param Type {function}
Type (constructor function)
@param instance {object}
Instance to test
@returns {boolean}
</api>
<api name="is">
@function
Functions takes `expected` and `actual` values and returns `true` if
`expected === actual`. If invoked with just one argument returns pratially
applied function, which can be invoked to provide a second argument, this
is handy with `map`, `filter` and other high order functions:
const { is } = require("sdk/util/oops");
[ 1, 0, 1, 0, 1 ].map(is(1)) // => [ true, false, true, false, true ]
@param expected {object|string|number|boolean}
@param actual {object|string|number|boolean}
@returns {boolean}
</api>
<api name="isnt">
@function
Functions takes `expected` and `actual` values and returns `true` if
`expected !== actual`. If invoked with just one argument returns pratially
applied function, which can be invoked with a second argument, which is
handy with `map`, `filter` and other high order functions:
const { isnt } = require("sdk/util/oops");
[ 1, 0, 1, 0, 1 ].map(isnt(0)) // => [ true, false, true, false, true ]
@param expected {object|string|number|boolean}
@param actual {object|string|number|boolean}
@returns {boolean}
</api>

View File

@ -89,7 +89,7 @@ to support private browsing, refer to the
var utils = require('sdk/window/utils');
var browserWindow = utils.getMostRecentBrowserWindow();
var window = browserWindow.content; // `window` object for the current webpage
utils.getToplevelWindw(window) == browserWindow // => true
utils.getToplevelWindow(window) == browserWindow // => true
@param window {nsIDOMWindow}
@returns {nsIDOMWindow}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -137,6 +137,14 @@ const ContentWorker = Object.freeze({
registerMethod = chromeSetInterval;
else
throw new Error("Unknown timer kind: " + timer.kind);
if (typeof timer.fun == 'string') {
let code = timer.fun;
timer.fun = () => chromeAPI.sandbox.evaluate(exports, code);
} else if (typeof timer.fun != 'function') {
throw new Error('Unsupported callback type' + typeof timer.fun);
}
let id = registerMethod(onFire, timer.delay);
function onFire() {
try {
@ -145,12 +153,47 @@ const ContentWorker = Object.freeze({
timer.fun.apply(null, timer.args);
} catch(e) {
console.exception(e);
let wrapper = {
instanceOfError: instanceOf(e, Error),
value: e,
};
if (wrapper.instanceOfError) {
wrapper.value = {
message: e.message,
fileName: e.fileName,
lineNumber: e.lineNumber,
stack: e.stack,
name: e.name,
};
}
pipe.emit('error', wrapper);
}
}
_timers[id] = timer;
return id;
}
// copied from sdk/lang/type.js since modules are not available here
function instanceOf(value, Type) {
var isConstructorNameSame;
var isConstructorSourceSame;
// If `instanceof` returned `true` we know result right away.
var isInstanceOf = value instanceof Type;
// If `instanceof` returned `false` we do ducktype check since `Type` may be
// from a different sandbox. If a constructor of the `value` or a constructor
// of the value's prototype has same name and source we assume that it's an
// instance of the Type.
if (!isInstanceOf && value) {
isConstructorNameSame = value.constructor.name === Type.name;
isConstructorSourceSame = String(value.constructor) == String(Type);
isInstanceOf = (isConstructorNameSame && isConstructorSourceSame) ||
instanceOf(Object.getPrototypeOf(value), Type);
}
return isInstanceOf;
}
function unregisterTimer(id) {
if (!(id in _timers))
return;

View File

@ -13,6 +13,7 @@ const { validateOptions } = require('../deprecated/api-utils');
const { isValidURI, URL } = require('../url');
const file = require('../io/file');
const { contract } = require('../util/contract');
const { isString, instanceOf } = require('../lang/type');
const LOCAL_URI_SCHEMES = ['resource', 'data'];
@ -32,7 +33,7 @@ const valid = {
msg: 'The `contentURL` option must be a valid URL.'
},
contentScriptFile: {
is: ['undefined', 'null', 'string', 'array'],
is: ['undefined', 'null', 'string', 'array', 'object'],
map: ensureNull,
ok: function(value) {
if (value === null)
@ -40,8 +41,13 @@ const valid = {
value = [].concat(value);
// Make sure every item is a local file URL.
// Make sure every item is a string or an
// URL instance, and also a local file URL.
return value.every(function (item) {
if (!isString(item) && !(item instanceof URL))
return false;
try {
return ~LOCAL_URI_SCHEMES.indexOf(URL(item).scheme);
}

View File

@ -108,6 +108,27 @@ const Symbiont = Worker.resolve({
this._frame = frame;
if (getDocShell(frame)) {
this._reallyInitFrame(frame);
}
else {
if (this._waitForFrame) {
observers.remove('content-document-global-created', this._waitForFrame);
}
this._waitForFrame = this.__waitForFrame.bind(this, frame);
observers.add('content-document-global-created', this._waitForFrame);
}
},
__waitForFrame: function _waitForFrame(frame, win, topic) {
if (frame.contentWindow == win) {
observers.remove('content-document-global-created', this._waitForFrame);
delete this._waitForFrame;
this._reallyInitFrame(frame);
}
},
_reallyInitFrame: function _reallyInitFrame(frame) {
getDocShell(frame).allowJavascript = this.allow.script;
frame.setAttribute("src", this._contentURL);
@ -179,6 +200,11 @@ const Symbiont = Worker.resolve({
* This listener is registered in `Symbiont._initFrame`.
*/
_unregisterListener: function _unregisterListener() {
if (this._waitForFrame) {
observers.remove('content-document-global-created', this._waitForFrame);
delete this._waitForFrame;
}
if (!this._loadListener)
return;
if (this._loadEvent == "start") {

View File

@ -202,8 +202,15 @@ const WorkerSandbox = EventEmitter.compose({
clearInterval: 'r'
}
},
sandbox: {
evaluate: evaluate,
__exposedProps__: {
evaluate: 'r',
}
},
__exposedProps__: {
timers: 'r'
timers: 'r',
sandbox: 'r',
}
};
let onEvent = this._onContentEvent.bind(this);
@ -233,6 +240,19 @@ const WorkerSandbox = EventEmitter.compose({
self._addonWorker._onContentScriptEvent.apply(self._addonWorker, arguments);
});
// unwrap, recreate and propagate async Errors thrown from content-script
this.on("error", function onError({instanceOfError, value}) {
if (self._addonWorker) {
let error = value;
if (instanceOfError) {
error = new Error(value.message, value.fileName, value.lineNumber);
error.stack = value.stack;
error.name = value.name;
}
self._addonWorker._emit('error', error);
}
});
// Inject `addon` global into target document if document is trusted,
// `addon` in document is equivalent to `self` in content script.
if (worker._injectInDocument) {

View File

@ -36,7 +36,7 @@ const observers = function observers(target, type) {
* The listener function that processes the event.
*/
function on(target, type, listener) {
if (typeof(listener) !== 'function')
if (typeof(listener) !== 'function')
throw new Error(BAD_LISTENER);
let listeners = observers(target, type);
@ -56,9 +56,9 @@ exports.on = on;
* The listener function that processes the event.
*/
function once(target, type, listener) {
on(target, type, function observer() {
on(target, type, function observer(...args) {
off(target, type, observer);
listener.apply(target, arguments);
listener.apply(target, args);
});
}
exports.once = once;
@ -74,40 +74,24 @@ exports.once = once;
* Event target object.
* @param {String} type
* The type of event.
* @params {Object|Number|String|Boolean} message
* First argument that will be passed to listeners.
* @params {Object|Number|String|Boolean} ...
* More arguments that will be passed to listeners.
* @params {Object|Number|String|Boolean} args
* Arguments that will be passed to listeners.
*/
function emit(target, type, message /*, ...*/) {
for each (let item in emit.lazy.apply(emit.lazy, arguments)) {
// We just iterate, iterator take care of emitting events.
}
}
/**
* This is very experimental feature that you should not use unless absolutely
* need it. Also it may be removed at any point without any further notice.
*
* Creates lazy iterator of return values of listeners. You can think of it
* as lazy array of return values of listeners for the `emit` with the given
* arguments.
*/
emit.lazy = function lazy(target, type, message /*, ...*/) {
let args = Array.slice(arguments, 2);
function emit (target, type, ...args) {
let state = observers(target, type);
let listeners = state.slice();
let index = 0;
let count = listeners.length;
let index = 0;
// If error event and there are no handlers then print error message
// into a console.
if (count === 0 && type === 'error') console.exception(message);
if (count === 0 && type === 'error') console.exception(args[0]);
while (index < count) {
try {
let listener = listeners[index];
// Dispatch only if listener is still registered.
if (~state.indexOf(listener)) yield listener.apply(target, args);
if (~state.indexOf(listener))
listener.apply(target, args);
}
catch (error) {
// If exception is not thrown by a error listener and error listener is
@ -115,8 +99,10 @@ emit.lazy = function lazy(target, type, message /*, ...*/) {
if (type !== 'error') emit(target, 'error', error);
else console.exception(error);
}
index = index + 1;
index++;
}
// Also emit on `"*"` so that one could listen for all events.
if (type !== '*') emit(target, '*', type, ...args);
}
exports.emit = emit;
@ -145,7 +131,7 @@ function off(target, type, listener) {
}
else if (length === 1) {
let listeners = event(target);
Object.keys(listeners).forEach(function(type) delete listeners[type]);
Object.keys(listeners).forEach(type => delete listeners[type]);
}
}
exports.off = off;
@ -171,7 +157,7 @@ exports.count = count;
* Dictionary of listeners.
*/
function setListeners(target, listeners) {
Object.keys(listeners || {}).forEach(function onEach(key) {
Object.keys(listeners || {}).forEach(key => {
let match = EVENT_TYPE_PATTERN.exec(key);
let type = match && match[1].toLowerCase();
let listener = listeners[key];

View File

@ -9,7 +9,7 @@ module.metadata = {
};
const { on, once, off, setListeners } = require('./core');
const { method, chain } = require('../lang/functional');
const { method, chainable } = require('../lang/functional');
const { Class } = require('../core/heritage');
/**
@ -43,7 +43,7 @@ const EventTarget = Class({
* console.log('data received: ' + data)
* })
*/
on: chain(method(on)),
on: chainable(method(on)),
/**
* Registers an event `listener` that is called once the next time an event
* of the specified `type` is emitted.
@ -52,7 +52,7 @@ const EventTarget = Class({
* @param {Function} listener
* The listener function that processes the event.
*/
once: chain(method(once)),
once: chainable(method(once)),
/**
* Removes an event `listener` for the given event `type`.
* @param {String} type

View File

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Disclaimer: Most of the functions in this module implement APIs from
// Disclaimer: Some of the functions in this module implement APIs from
// Jeremy Ashkenas's http://underscorejs.org/ library and all credits for
// those goes to him.
@ -12,19 +12,33 @@ module.metadata = {
"stability": "unstable"
};
const { setImmediate, setTimeout } = require("../timers");
const { deprecateFunction } = require("../util/deprecate");
const { setImmediate, setTimeout } = require("../timers");
const arity = f => f.arity || f.length;
const name = f => f.displayName || f.name;
const derive = (f, source) => {
f.displayName = name(source);
f.arity = arity(source);
return f;
};
/**
* Takes `lambda` function and returns a method. When returned method is
* invoked it calls wrapped `lambda` and passes `this` as a first argument
* and given argument as rest.
* Takes variadic numeber of functions and returns composed one.
* Returned function pushes `this` pseudo-variable to the head
* of the passed arguments and invokes all the functions from
* left to right passing same arguments to them. Composite function
* returns return value of the right most funciton.
*/
function method(lambda) {
return function method() {
return lambda.apply(null, [this].concat(Array.slice(arguments)));
}
}
const method = (...lambdas) => {
return function method(...args) {
args.unshift(this);
return lambdas.reduce((_, lambda) => lambda.apply(this, args),
void(0));
};
};
exports.method = method;
/**
@ -34,24 +48,13 @@ exports.method = method;
* function is reused, instead of creating a new one each time. This also allows
* to use this functions as event listeners.
*/
function defer(f) {
return function deferred() setImmediate(invoke, f, arguments, this);
}
const defer = f => derive(function(...args) {
setImmediate(invoke, f, args, this);
}, f);
exports.defer = defer;
// Exporting `remit` alias as `defer` may conflict with promises.
exports.remit = defer;
/*
* Takes a funtion and returns a wrapped function that returns `this`
*/
function chain(f) {
return function chainable(...args) {
f.apply(this, args);
return this;
};
}
exports.chain = chain;
/**
* Invokes `callee` by passing `params` as an arguments and `self` as `this`
* pseudo-variable. Returns value that is returned by a callee.
@ -62,7 +65,7 @@ exports.chain = chain;
* @param {Object} self
* Object to be passed as a `this` pseudo variable.
*/
function invoke(callee, params, self) callee.apply(self, params);
const invoke = (callee, params, self) => callee.apply(self, params);
exports.invoke = invoke;
/**
@ -74,14 +77,16 @@ exports.invoke = invoke;
*
* @returns The new function with binded values
*/
function partial(fn) {
if (typeof fn !== "function")
throw new TypeError(String(fn) + " is not a function");
const partial = (f, ...curried) => {
if (typeof(f) !== "function")
throw new TypeError(String(f) + " is not a function");
let args = Array.slice(arguments, 1);
return function() fn.apply(this, args.concat(Array.slice(arguments)));
}
let fn = derive(function(...args) {
return f.apply(this, curried.concat(args));
}, f);
fn.arity = arity(f) - curried.length;
return fn;
};
exports.partial = partial;
/**
@ -98,12 +103,11 @@ exports.partial = partial;
* console.log(sum(2, 2)) // 4
* console.log(sum(2)(4)) // 6
*/
var curry = new function() {
function currier(fn, arity, params) {
const curry = new function() {
const currier = (fn, arity, params) => {
// Function either continues to curry arguments or executes function
// if desired arguments have being collected.
return function curried() {
var input = Array.slice(arguments);
const curried = function(...input) {
// Prepend all curried arguments to the given arguments.
if (params) input.unshift.apply(input, params);
// If expected number of arguments has being collected invoke fn,
@ -111,11 +115,12 @@ var curry = new function() {
return (input.length >= arity) ? fn.apply(this, input) :
currier(fn, arity, input);
};
}
curried.arity = arity - (params ? params.length : 0);
return function curry(fn) {
return currier(fn, fn.length);
}
return curried;
};
return fn => currier(fn, arity(fn));
};
exports.curry = curry;
@ -131,12 +136,12 @@ exports.curry = curry;
*
* welcome('moe'); // => 'hi: moe!'
*/
function compose() {
let lambdas = Array.slice(arguments);
return function composed() {
let args = Array.slice(arguments), index = lambdas.length;
function compose(...lambdas) {
return function composed(...args) {
let index = lambdas.length;
while (0 <= --index)
args = [ lambdas[index].apply(this, args) ];
args = [lambdas[index].apply(this, args)];
return args[0];
};
}
@ -155,16 +160,15 @@ exports.compose = compose;
*
* hello(); // => 'before, hello: moe, after'
*/
function wrap(f, wrapper) {
return function wrapped()
wrapper.apply(this, [ f ].concat(Array.slice(arguments)))
};
const wrap = (f, wrapper) => derive(function wrapped(...args) {
return wrapper.apply(this, [f].concat(args));
}, f);
exports.wrap = wrap;
/**
* Returns the same value that is used as the argument. In math: f(x) = x
*/
function identity(value) value
const identity = value => value;
exports.identity = identity;
/**
@ -174,14 +178,25 @@ exports.identity = identity;
* the arguments to the original function. The default hashFunction just uses
* the first argument to the memoized function as the key.
*/
function memoize(f, hasher) {
const memoize = (f, hasher) => {
let memo = Object.create(null);
let cache = new WeakMap();
hasher = hasher || identity;
return function memoizer() {
let key = hasher.apply(this, arguments);
return key in memo ? memo[key] : (memo[key] = f.apply(this, arguments));
};
}
return derive(function memoizer(...args) {
const key = hasher.apply(this, args);
const type = typeof(key);
if (key && (type === "object" || type === "function")) {
if (!cache.has(key))
cache.set(key, f.apply(this, args));
return cache.get(key);
}
else {
if (!(key in memo))
memo[key] = f.apply(this, args);
return memo[key];
}
}, f);
};
exports.memoize = memoize;
/**
@ -189,9 +204,8 @@ exports.memoize = memoize;
* the optional arguments, they will be forwarded on to the function when it is
* invoked.
*/
function delay(f, ms) {
let args = Array.slice(arguments, 2);
setTimeout(function(context) { return f.apply(context, args); }, ms, this);
const delay = function delay(f, ms, ...args) {
setTimeout(() => f.apply(this, args), ms);
};
exports.delay = delay;
@ -201,10 +215,116 @@ exports.delay = delay;
* the original call. Useful for initialization functions, instead of having to
* set a boolean flag and then check it later.
*/
function once(f) {
const once = f => {
let ran = false, cache;
return function() ran ? cache : (ran = true, cache = f.apply(this, arguments))
return derive(function(...args) {
return ran ? cache : (ran = true, cache = f.apply(this, args));
}, f);
};
exports.once = once;
// export cache as once will may be conflicting with event once a lot.
exports.cache = once;
// Takes a `f` function and returns a function that takes the same
// arguments as `f`, has the same effects, if any, and returns the
// opposite truth value.
const complement = f => derive(function(...args) {
return args.length < arity(f) ? complement(partial(f, ...args)) :
!f.apply(this, args);
}, f);
exports.complement = complement;
// Constructs function that returns `x` no matter what is it
// invoked with.
const constant = x => _ => x;
exports.constant = constant;
// Takes `p` predicate, `consequent` function and an optional
// `alternate` function and composes function that returns
// application of arguments over `consequent` if application over
// `p` is `true` otherwise returns application over `alternate`.
// If `alternate` is not a function returns `undefined`.
const when = (p, consequent, alternate) => {
if (typeof(alternate) !== "function" && alternate !== void(0))
throw TypeError("alternate must be a function");
if (typeof(consequent) !== "function")
throw TypeError("consequent must be a function");
return function(...args) {
return p.apply(this, args) ?
consequent.apply(this, args) :
alternate && alternate.apply(this, args);
};
};
exports.when = when;
// Apply function that behaves as `apply` does in lisp:
// apply(f, x, [y, z]) => f.apply(f, [x, y, z])
// apply(f, x) => f.apply(f, [x])
const apply = (f, ...rest) => f.apply(f, rest.concat(rest.pop()));
exports.apply = apply;
// Returns function identical to given `f` but with flipped order
// of arguments.
const flip = f => derive(function(...args) {
return f.apply(this, args.reverse());
}, f);
exports.flip = flip;
// Takes field `name` and `target` and returns value of that field.
// If `target` is `null` or `undefined` it would be returned back
// instead of attempt to access it's field. Function is implicitly
// curried, this allows accessor function generation by calling it
// with only `name` argument.
const field = curry((name, target) =>
// Note: Permisive `==` is intentional.
target == null ? target : target[name]);
exports.field = field;
// Takes `.` delimited string representing `path` to a nested field
// and a `target` to get it from. For convinience function is
// implicitly curried, there for accessors can be created by invoking
// it with just a `path` argument.
const query = curry((path, target) => {
const names = path.split(".");
const count = names.length;
let index = 0;
let result = target;
// Note: Permisive `!=` is intentional.
while (result != null && index < count) {
result = result[names[index]];
index = index + 1;
}
return result;
});
exports.query = query;
// Takes `Type` (constructor function) and a `value` and returns
// `true` if `value` is instance of the given `Type`. Function is
// implicitly curried this allows predicate generation by calling
// function with just first argument.
const isInstance = curry((Type, value) => value instanceof Type);
exports.isInstance = isInstance;
/*
* Takes a funtion and returns a wrapped function that returns `this`
*/
const chainable = f => derive(function(...args) {
f.apply(this, args);
return this;
}, f);
exports.chainable = chainable;
exports.chain =
deprecateFunction(chainable, "Function `chain` was renamed to `chainable`");
// Functions takes `expected` and `actual` values and returns `true` if
// `expected === actual`. Returns curried function if called with less then
// two arguments.
//
// [ 1, 0, 1, 0, 1 ].map(is(1)) // => [ true, false, true, false, true ]
const is = curry((expected, actual) => actual === expected);
exports.is = is;
const isnt = complement(is);
exports.isnt = isnt;

View File

@ -11,6 +11,10 @@ module.metadata = {
const { Cc, Ci, Cr } = require("chrome");
const apiUtils = require("./deprecated/api-utils");
const errors = require("./deprecated/errors");
const { isString, isUndefined, instanceOf } = require('./lang/type');
const { URL } = require('./url');
const NOTIFICATION_DIRECTIONS = ["auto", "ltr", "rtl"];
try {
let alertServ = Cc["@mozilla.org/alerts-service;1"].
@ -36,7 +40,7 @@ exports.notify = function notifications_notify(options) {
};
function notifyWithOpts(notifyFn) {
notifyFn(valOpts.iconURL, valOpts.title, valOpts.text, !!clickObserver,
valOpts.data, clickObserver);
valOpts.data, clickObserver, valOpts.tag, valOpts.dir, valOpts.lang);
}
try {
notifyWithOpts(notify);
@ -66,15 +70,32 @@ function validateOptions(options) {
is: ["string", "undefined"]
},
iconURL: {
is: ["string", "undefined"]
is: ["string", "undefined", "object"],
ok: function(value) {
return isUndefined(value) || isString(value) || (value instanceof URL);
},
msg: "`iconURL` must be a string or an URL instance."
},
onClick: {
is: ["function", "undefined"]
},
text: {
is: ["string", "undefined"]
is: ["string", "undefined", "number"]
},
title: {
is: ["string", "undefined", "number"]
},
tag: {
is: ["string", "undefined", "number"]
},
dir: {
is: ["string", "undefined"],
ok: function(value) {
return isUndefined(value) || ~NOTIFICATION_DIRECTIONS.indexOf(value);
},
msg: '`dir` option must be one of: "auto", "ltr" or "rtl".'
},
lang: {
is: ["string", "undefined"]
}
});

View File

@ -136,18 +136,18 @@ const PageMod = Loader.compose(EventEmitter, {
_applyOnExistingDocuments: function _applyOnExistingDocuments() {
let mod = this;
// Returns true if the tab match one rule
let tabs = getAllTabs().filter(function (tab) {
return mod.include.matchesAny(getTabURI(tab));
});
let tabs = getAllTabs();
tabs.forEach(function (tab) {
// Fake a newly created document
let window = getTabContentWindow(tab);
if (has(mod.attachTo, "top"))
if (has(mod.attachTo, "top") && mod.include.matchesAny(getTabURI(tab)))
mod._onContent(window);
if (has(mod.attachTo, "frame"))
getFrames(window).forEach(mod._onContent);
if (has(mod.attachTo, "frame")) {
getFrames(window).
filter((iframe) => mod.include.matchesAny(iframe.location.href)).
forEach(mod._onContent);
}
});
},

View File

@ -130,6 +130,10 @@ const Request = Class({
request(this).contentType = validateSingleOption('contentType', value);
},
get response() { return request(this).response; },
delete: function() {
runRequest('DELETE', this);
return this;
},
get: function() {
runRequest('GET', this);
return this;

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