This commit is contained in:
Robert Sayre 2009-02-23 12:44:23 -05:00
Родитель 8a9bc02eaf 486b0aefbb
Коммит 2edd3c3ca6
1228 изменённых файлов: 43138 добавлений и 25704 удалений

Просмотреть файл

@ -176,33 +176,37 @@ DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
MAKE_SYM_STORE_PATH := $(DIST)/bin
endif
SYM_STORE_SOURCE_DIRS := $(topsrcdir)
include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
ifdef MOZ_SYMBOLS_EXTRA_BUILDID
EXTRA_BUILDID := -$(MOZ_SYMBOLS_EXTRA_BUILDID)
endif
SYMBOL_ARCHIVE_BASENAME = \
$(MOZ_APP_NAME)-$(MOZ_APP_VERSION)-$(OS_ARCH)-$(BUILDID)$(EXTRA_BUILDID)
SYMBOL_INDEX_NAME = \
$(MOZ_APP_NAME)-$(MOZ_APP_VERSION)-$(OS_ARCH)-$(BUILDID)$(EXTRA_BUILDID)-symbols.txt
buildsymbols:
ifdef MOZ_CRASHREPORTER
echo building symbol store
mkdir -p $(DIST)/crashreporter-symbols/$(BUILDID)
$(PYTHON) $(topsrcdir)/toolkit/crashreporter/tools/symbolstore.py \
$(MAKE_SYM_STORE_ARGS) -s $(topsrcdir) $(DUMP_SYMS_BIN) \
$(DIST)/crashreporter-symbols/$(BUILDID) \
$(MAKE_SYM_STORE_PATH) > \
$(DIST)/crashreporter-symbols/$(BUILDID)/$(SYMBOL_ARCHIVE_BASENAME)-symbols.txt
$(PYTHON) $(topsrcdir)/toolkit/crashreporter/tools/symbolstore.py \
$(MAKE_SYM_STORE_ARGS) \
$(foreach dir,$(SYM_STORE_SOURCE_DIRS),-s $(dir)) \
$(DUMP_SYMS_BIN) \
$(DIST)/crashreporter-symbols/$(BUILDID) \
$(MAKE_SYM_STORE_PATH) > \
$(DIST)/crashreporter-symbols/$(BUILDID)/$(SYMBOL_INDEX_NAME)
echo packing symbols
mkdir -p $(topsrcdir)/../$(BUILDID)
cd $(DIST)/crashreporter-symbols/$(BUILDID) && \
zip -r9D ../crashreporter-symbols-$(SYMBOL_ARCHIVE_BASENAME).zip .
mv $(DIST)/crashreporter-symbols/crashreporter-symbols-$(SYMBOL_ARCHIVE_BASENAME).zip \
$(topsrcdir)/../$(BUILDID)
zip -r9D ../../$(SYMBOL_ARCHIVE_BASENAME).zip .
endif # MOZ_CRASHREPORTER
uploadsymbols:
ifdef MOZ_CRASHREPORTER
$(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.sh $(topsrcdir)/../$(BUILDID)/crashreporter-symbols-$(SYMBOL_ARCHIVE_BASENAME).zip
$(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.sh $(DIST)/$(SYMBOL_ARCHIVE_BASENAME).zip
endif
ifeq ($(OS_ARCH),WINNT)

Просмотреть файл

@ -81,7 +81,6 @@ CPPSRCS = \
nsAccessibilityAtoms.cpp \
nsCoreUtils.cpp \
nsAccUtils.cpp \
nsNameUtils.cpp \
nsRelUtils.cpp \
nsAccessibilityService.cpp \
nsAccessible.cpp \
@ -93,7 +92,8 @@ CPPSRCS = \
nsApplicationAccessible.cpp \
nsCaretAccessible.cpp \
nsTextAccessible.cpp \
nsTextUtils.cpp \
nsTextEquivUtils.cpp \
nsTextAttrs.cpp \
$(NULL)
EXPORTS = \

Просмотреть файл

@ -65,6 +65,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_ALERT,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -73,6 +74,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_DIALOG,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -81,6 +83,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_APPLICATION,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -89,7 +92,8 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_DOCUMENT,
eNoValue,
eNoAction,
kNoReqStates,
eNoLiveAttr,
nsIAccessibleStates::STATE_READONLY,
kEndEntry
},
{
@ -97,9 +101,10 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_PUSHBUTTON,
eNoValue,
eClickAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_pressed, kBoolState, nsIAccessibleStates::STATE_PRESSED},
{&nsAccessibilityAtoms::aria_pressed, "mixed", nsIAccessibleStates::STATE_MIXED},
{&nsAccessibilityAtoms::aria_pressed, kBoolState, nsIAccessibleStates::STATE_PRESSED | nsIAccessibleStates::STATE_CHECKABLE},
{&nsAccessibilityAtoms::aria_pressed, "mixed", nsIAccessibleStates::STATE_MIXED | nsIAccessibleStates::STATE_CHECKABLE},
kEndEntry
},
{
@ -107,6 +112,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_CHECKBUTTON,
eNoValue,
eCheckUncheckAction,
eNoLiveAttr,
nsIAccessibleStates::STATE_CHECKABLE,
{&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED},
{&nsAccessibilityAtoms::aria_checked, "mixed", nsIAccessibleStates::STATE_MIXED},
@ -118,6 +124,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_COLUMNHEADER,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE},
{&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE},
@ -129,6 +136,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_COMBOBOX,
eHasValueMinMax,
eOpenCloseAction,
eNoLiveAttr,
nsIAccessibleStates::STATE_COLLAPSED | nsIAccessibleStates::STATE_HASPOPUP,
// Manually map EXT_STATE_SUPPORTS_AUTOCOMPLETION aria-autocomplete
{&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY},
@ -139,6 +147,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_DIALOG,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -147,7 +156,8 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_DOCUMENT,
eNoValue,
eNoAction,
kNoReqStates,
eNoLiveAttr,
nsIAccessibleStates::STATE_READONLY,
kEndEntry
},
{
@ -155,6 +165,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_TABLE,
eNoValue,
eNoAction,
eNoLiveAttr,
nsIAccessibleStates::STATE_FOCUSABLE,
{&nsAccessibilityAtoms::aria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE},
{&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY},
@ -165,6 +176,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_GRID_CELL,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE},
{&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE},
@ -176,6 +188,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_GROUPING,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -184,6 +197,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_HEADING,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -192,6 +206,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_GRAPHIC,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -200,6 +215,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_LABEL,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -208,6 +224,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_LINK,
eNoValue,
eJumpAction,
eNoLiveAttr,
nsIAccessibleStates::STATE_LINKED,
kEndEntry
},
@ -216,6 +233,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_LIST,
eNoValue,
eNoAction,
eNoLiveAttr,
nsIAccessibleStates::STATE_READONLY,
{&nsAccessibilityAtoms::aria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE},
kEndEntry
@ -225,6 +243,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_LISTBOX,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY},
{&nsAccessibilityAtoms::aria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE},
@ -235,6 +254,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_LISTITEM,
eNoValue,
eNoAction, // XXX: should depend on state, parent accessible
eNoLiveAttr,
nsIAccessibleStates::STATE_READONLY,
{&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE},
{&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE},
@ -243,11 +263,30 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
{&nsAccessibilityAtoms::aria_checked, "false", nsIAccessibleStates::STATE_CHECKABLE},
kEndEntry
},
{
"log",
nsIAccessibleRole::ROLE_NOTHING,
eNoValue,
eNoAction,
ePoliteLiveAttr,
kNoReqStates,
kEndEntry
},
{
"marquee",
nsIAccessibleRole::ROLE_NOTHING,
eNoValue,
eNoAction,
eOffLiveAttr,
kNoReqStates,
kEndEntry
},
{
"math",
nsIAccessibleRole::ROLE_FLAT_EQUATION,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -257,6 +296,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
eNoValue,
eNoAction, // XXX: technically accessibles of menupopup role haven't
// any action, but menu can be open or close.
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -265,6 +305,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_MENUBAR,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -273,6 +314,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_MENUITEM,
eNoValue,
eClickAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED | nsIAccessibleStates::STATE_CHECKABLE},
{&nsAccessibilityAtoms::aria_checked, "mixed", nsIAccessibleStates::STATE_MIXED | nsIAccessibleStates::STATE_CHECKABLE},
@ -284,6 +326,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_CHECK_MENU_ITEM,
eNoValue,
eClickAction,
eNoLiveAttr,
nsIAccessibleStates::STATE_CHECKABLE,
{&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED },
{&nsAccessibilityAtoms::aria_checked, "mixed", nsIAccessibleStates::STATE_MIXED},
@ -294,6 +337,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_RADIO_MENU_ITEM,
eNoValue,
eClickAction,
eNoLiveAttr,
nsIAccessibleStates::STATE_CHECKABLE,
{&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED },
kEndEntry
@ -303,6 +347,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_OPTION,
eNoValue,
eSelectAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE},
{&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE},
@ -316,6 +361,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_NOTHING,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -324,6 +370,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_PROGRESSBAR,
eHasValueMinMax,
eNoAction,
eNoLiveAttr,
nsIAccessibleStates::STATE_READONLY,
kEndEntry
},
@ -332,7 +379,8 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_RADIOBUTTON,
eNoValue,
eSelectAction,
kNoReqStates,
eNoLiveAttr,
nsIAccessibleStates::STATE_CHECKABLE,
{&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED},
kEndEntry
},
@ -341,6 +389,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_GROUPING,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -349,6 +398,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_PANE,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -357,6 +407,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_ROW,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE},
{&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE},
@ -367,6 +418,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_ROWHEADER,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE},
{&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE},
@ -378,6 +430,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_SECTION,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -386,6 +439,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_SEPARATOR,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -394,6 +448,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_SLIDER,
eHasValueMinMax,
eNoAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY},
kEndEntry
@ -403,6 +458,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_SPINBUTTON,
eHasValueMinMax,
eNoAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY},
kEndEntry
@ -412,6 +468,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_STATUSBAR,
eNoValue,
eNoAction,
ePoliteLiveAttr,
kNoReqStates,
kEndEntry
},
@ -420,6 +477,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_PAGETAB,
eNoValue,
eSwitchAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -428,6 +486,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_PAGETABLIST,
eNoValue,
eNoAction,
ePoliteLiveAttr,
kNoReqStates,
kEndEntry
},
@ -436,6 +495,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_PROPERTYPAGE,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -444,6 +504,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_ENTRY,
eNoValue,
eActivateAction,
eNoLiveAttr,
kNoReqStates,
// Manually map EXT_STATE_SINGLE_LINE and EXT_STATE_MULTI_LINE FROM aria-multiline
// Manually map EXT_STATE_SUPPORTS_AUTOCOMPLETION aria-autocomplete
@ -452,11 +513,21 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
{&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY},
kEndEntry
},
{
"timer",
nsIAccessibleRole::ROLE_NOTHING,
eNoValue,
eNoAction,
eOffLiveAttr,
kNoReqStates,
kEndEntry
},
{
"toolbar",
nsIAccessibleRole::ROLE_TOOLBAR,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -465,6 +536,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_TOOLTIP,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
},
@ -473,6 +545,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_OUTLINE,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY},
{&nsAccessibilityAtoms::aria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE},
@ -483,6 +556,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
nsIAccessibleRole::ROLE_TREE_TABLE,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY},
{&nsAccessibilityAtoms::aria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE},
@ -494,13 +568,15 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
eNoValue,
eActivateAction, // XXX: should expose second 'expand/collapse' action based
// on states
eNoLiveAttr,
kNoReqStates,
{&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE},
{&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE},
{&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED | nsIAccessibleStates::STATE_CHECKABLE},
{&nsAccessibilityAtoms::aria_checked, "mixed", nsIAccessibleStates::STATE_MIXED | nsIAccessibleStates::STATE_CHECKABLE},
{&nsAccessibilityAtoms::aria_checked, "false", nsIAccessibleStates::STATE_CHECKABLE},
},
kEndEntry
}
};
PRUint32 nsARIAMap::gWAIRoleMapLength = NS_ARRAY_LENGTH(nsARIAMap::gWAIRoleMap);
@ -510,6 +586,7 @@ nsRoleMapEntry nsARIAMap::gLandmarkRoleMap = {
nsIAccessibleRole::ROLE_NOTHING,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
};
@ -519,6 +596,7 @@ nsRoleMapEntry nsARIAMap::gEmptyRoleMap = {
nsIAccessibleRole::ROLE_NOTHING,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates,
kEndEntry
};
@ -540,3 +618,41 @@ nsStateMapEntry nsARIAMap::gWAIUnivStateMap[] = {
kEndEntry
};
/**
* ARIA attribute map for attribute characteristics
*
* @note ARIA attributes that don't have any flags are not included here
*/
nsAttributeCharacteristics nsARIAMap::gWAIUnivAttrMap[] = {
{&nsAccessibilityAtoms::aria_activedescendant, ATTR_EXPOSEOBJ },
{&nsAccessibilityAtoms::aria_atomic, ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_busy, ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_checked, ATTR_EXPOSEOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_controls, ATTR_EXPOSEOBJ },
{&nsAccessibilityAtoms::aria_describedby, ATTR_EXPOSEOBJ },
{&nsAccessibilityAtoms::aria_disabled, ATTR_EXPOSEOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_dropeffect, ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_expanded, ATTR_EXPOSEOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_flowto, ATTR_EXPOSEOBJ },
{&nsAccessibilityAtoms::aria_grabbed, ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_haspopup, ATTR_EXPOSEOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_invalid, ATTR_EXPOSEOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_labelledby, ATTR_EXPOSEOBJ },
{&nsAccessibilityAtoms::aria_live, ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_multiline, ATTR_EXPOSEOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_multiselectable, ATTR_EXPOSEOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_owns, ATTR_EXPOSEOBJ },
{&nsAccessibilityAtoms::aria_pressed, ATTR_EXPOSEOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_readonly, ATTR_EXPOSEOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_relevant, ATTR_EXPOSEOBJ },
{&nsAccessibilityAtoms::aria_required, ATTR_EXPOSEOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_selected, ATTR_EXPOSEOBJ | ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_sort, ATTR_VALTOKEN },
{&nsAccessibilityAtoms::aria_valuenow, ATTR_EXPOSEOBJ },
{&nsAccessibilityAtoms::aria_valuemin, ATTR_EXPOSEOBJ },
{&nsAccessibilityAtoms::aria_valuemax, ATTR_EXPOSEOBJ },
{&nsAccessibilityAtoms::aria_valuetext, ATTR_EXPOSEOBJ }
};
PRUint32 nsARIAMap::gWAIUnivAttrMapLength = NS_ARRAY_LENGTH(nsARIAMap::gWAIUnivAttrMap);

Просмотреть файл

@ -63,6 +63,28 @@ enum EActionRule
eSwitchAction
};
enum ELiveAttrRule
{
eNoLiveAttr,
eOffLiveAttr,
ePoliteLiveAttr
};
// ARIA attribute characteristic masks, grow as needed
/**
* This mask indicates the attribute should be exposed as an object attribute,
* used to expose semantics not traditionally found in a11y APIs.
* (See for example usage in nsAccessible::GetAttributes)
*/
const PRUint8 ATTR_EXPOSEOBJ = 0x0001;
/**
* This mask indicates the attribute is expected to have an NMTOKEN or bool value.
* (See for example usage in nsAccessible::GetAttributes)
*/
const PRUint8 ATTR_VALTOKEN = 0x0010;
// Used for an nsStateMapEntry if a given state attribute supports "true" and "false"
#define kBoolState 0
@ -78,6 +100,13 @@ struct nsStateMapEntry
PRUint32 state; // If match, this is the nsIAccessibleStates to map to
};
// Small footprint storage of persistent aria attribute characteristics
struct nsAttributeCharacteristics
{
nsIAtom** attributeName;
const PRUint8 characteristics;
};
// For each ARIA role, this maps the nsIAccessible information
struct nsRoleMapEntry
{
@ -93,6 +122,10 @@ struct nsRoleMapEntry
// Action mapping rule, how to expose nsIAccessible action
EActionRule actionRule;
// 'live' and 'container-live' object attributes mapping rule: how to expose
// these object attributes if ARIA 'live' attribute is missed.
ELiveAttrRule liveAttRule;
// Automatic state mapping rule: always include in nsIAccessibleStates
PRUint32 state; // or kNoReqStates if no nsIAccessibleStates are automatic for this role.
@ -142,6 +175,12 @@ struct nsARIAMap
* the role.
*/
static nsStateMapEntry gWAIUnivStateMap[];
/**
* Map of attribute to attribute characteristics.
*/
static nsAttributeCharacteristics gWAIUnivAttrMap[];
static PRUint32 gWAIUnivAttrMapLength;
};
#endif

Просмотреть файл

@ -272,21 +272,38 @@ nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
nsAutoString atomic, live, relevant, busy;
nsIContent *ancestor = aStartContent;
while (ancestor) {
// container-relevant attribute
if (relevant.IsEmpty() &&
nsAccUtils::HasDefinedARIAToken(ancestor, nsAccessibilityAtoms::aria_relevant) &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_relevant, relevant))
SetAccAttr(aAttributes, nsAccessibilityAtoms::containerRelevant, relevant);
if (live.IsEmpty() &&
nsAccUtils::HasDefinedARIAToken(ancestor, nsAccessibilityAtoms::aria_live) &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_live, live))
SetAccAttr(aAttributes, nsAccessibilityAtoms::containerLive, live);
// container-live attribute
if (live.IsEmpty()) {
if (nsAccUtils::HasDefinedARIAToken(ancestor,
nsAccessibilityAtoms::aria_live)) {
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_live,
live);
SetAccAttr(aAttributes, nsAccessibilityAtoms::containerLive, live);
} else {
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(ancestor));
nsRoleMapEntry *role = GetRoleMapEntry(node);
if (role) {
nsAutoString live;
GetLiveAttrValue(role->liveAttRule, live);
SetAccAttr(aAttributes, nsAccessibilityAtoms::containerLive, live);
}
}
}
// container-atomic attribute
if (atomic.IsEmpty() &&
nsAccUtils::HasDefinedARIAToken(ancestor, nsAccessibilityAtoms::aria_atomic) &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_atomic, atomic))
SetAccAttr(aAttributes, nsAccessibilityAtoms::containerAtomic, atomic);
// container-busy attribute
if (busy.IsEmpty() &&
nsAccUtils::HasDefinedARIAToken(ancestor, nsAccessibilityAtoms::aria_busy) &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_busy, busy))
@ -301,33 +318,6 @@ nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
}
}
PRBool
nsAccUtils::IsARIAPropForObjectAttr(nsIAtom *aAtom)
{
return aAtom != nsAccessibilityAtoms::aria_activedescendant &&
aAtom != nsAccessibilityAtoms::aria_checked &&
aAtom != nsAccessibilityAtoms::aria_controls &&
aAtom != nsAccessibilityAtoms::aria_describedby &&
aAtom != nsAccessibilityAtoms::aria_disabled &&
aAtom != nsAccessibilityAtoms::aria_expanded &&
aAtom != nsAccessibilityAtoms::aria_flowto &&
aAtom != nsAccessibilityAtoms::aria_invalid &&
aAtom != nsAccessibilityAtoms::aria_haspopup &&
aAtom != nsAccessibilityAtoms::aria_labelledby &&
aAtom != nsAccessibilityAtoms::aria_multiline &&
aAtom != nsAccessibilityAtoms::aria_multiselectable &&
aAtom != nsAccessibilityAtoms::aria_owns &&
aAtom != nsAccessibilityAtoms::aria_pressed &&
aAtom != nsAccessibilityAtoms::aria_readonly &&
aAtom != nsAccessibilityAtoms::aria_relevant &&
aAtom != nsAccessibilityAtoms::aria_required &&
aAtom != nsAccessibilityAtoms::aria_selected &&
aAtom != nsAccessibilityAtoms::aria_valuemax &&
aAtom != nsAccessibilityAtoms::aria_valuemin &&
aAtom != nsAccessibilityAtoms::aria_valuenow &&
aAtom != nsAccessibilityAtoms::aria_valuetext;
}
PRBool
nsAccUtils::HasDefinedARIAToken(nsIContent *aContent, nsIAtom *aAtom)
{
@ -686,6 +676,28 @@ nsAccUtils::GetRoleMapEntry(nsIDOMNode *aNode)
return &nsARIAMap::gLandmarkRoleMap;
}
PRUint8
nsAccUtils::GetAttributeCharacteristics(nsIAtom* aAtom)
{
for (PRUint32 i = 0; i < nsARIAMap::gWAIUnivAttrMapLength; i++)
if (*nsARIAMap::gWAIUnivAttrMap[i].attributeName == aAtom)
return nsARIAMap::gWAIUnivAttrMap[i].characteristics;
return 0;
}
void
nsAccUtils::GetLiveAttrValue(PRUint32 aRule, nsAString& aValue)
{
switch (aRule) {
case eOffLiveAttr:
aValue = NS_LITERAL_STRING("off");
break;
case ePoliteLiveAttr:
aValue = NS_LITERAL_STRING("polite");
break;
}
}
#ifdef DEBUG_A11Y

Просмотреть файл

@ -130,12 +130,6 @@ public:
nsIContent *aStartContent,
nsIContent *aTopContent);
/**
* Return PR_TRUE if the ARIA property should always be exposed as an object
* attribute.
*/
static PRBool IsARIAPropForObjectAttr(nsIAtom *aAtom);
/**
* Any ARIA property of type boolean or NMTOKEN is undefined if the ARIA
* property is not present, or is "" or "undefined". Do not call
@ -267,6 +261,21 @@ public:
return state;
}
/**
* Get the ARIA attribute characteristics for a given ARIA attribute.
*
* @param aAtom ARIA attribute
* @return A bitflag representing the attribute characteristics
* (see nsARIAMap.h for possible bit masks, prefixed "ARIA_")
*/
static PRUint8 GetAttributeCharacteristics(nsIAtom* aAtom);
/**
* Return the 'live' or 'container-live' object attribute value from the given
* ELiveAttrRule constant.
*/
static void GetLiveAttrValue(PRUint32 aRule, nsAString& aValue);
/**
* Query nsAccessNode from the given nsIAccessible.
*/

Просмотреть файл

@ -188,15 +188,23 @@ ACCESSIBILITY_ATOM(type, "type")
ACCESSIBILITY_ATOM(value, "value")
// Alphabetical list of object attributes
ACCESSIBILITY_ATOM(checkable, "checkable")
ACCESSIBILITY_ATOM(display, "display")
ACCESSIBILITY_ATOM(textAlign, "text-align")
ACCESSIBILITY_ATOM(textIndent, "text-indent")
// Alphabetical list of text attributes (AT API)
ACCESSIBILITY_ATOM(color, "color")
ACCESSIBILITY_ATOM(backgroundColor, "background-color")
ACCESSIBILITY_ATOM(fontFamily, "font-family")
ACCESSIBILITY_ATOM(fontStyle, "font-style")
ACCESSIBILITY_ATOM(fontWeight, "font-weight")
ACCESSIBILITY_ATOM(fontSize, "font-size")
ACCESSIBILITY_ATOM(invalid, "invalid")
ACCESSIBILITY_ATOM(language, "language")
ACCESSIBILITY_ATOM(textLineThroughStyle, "text-line-through-style")
ACCESSIBILITY_ATOM(textUnderlineStyle, "text-underline-style")
ACCESSIBILITY_ATOM(textPosition, "text-position")
// ARIA (DHTML accessibility) attributes
// Also add to nsARIAMap.cpp and nsARIAMap.h
@ -249,6 +257,7 @@ ACCESSIBILITY_ATOM(containerBusy, "container-busy")
ACCESSIBILITY_ATOM(containerLive, "container-live")
ACCESSIBILITY_ATOM(containerRelevant, "container-relevant")
ACCESSIBILITY_ATOM(level, "level")
ACCESSIBILITY_ATOM(live, "live")
ACCESSIBILITY_ATOM(lineNumber, "line-number")
ACCESSIBILITY_ATOM(posinset, "posinset")
ACCESSIBILITY_ATOM(setsize, "setsize")

Просмотреть файл

@ -40,7 +40,6 @@
#include "nsAccessible.h"
#include "nsAccessibleRelation.h"
#include "nsHyperTextAccessibleWrap.h"
#include "nsNameUtils.h"
#include "nsIAccessibleDocument.h"
#include "nsIAccessibleHyperText.h"
@ -329,7 +328,9 @@ NS_IMETHODIMP nsAccessible::GetDescription(nsAString& aDescription)
}
if (!content->IsNodeOfType(nsINode::eTEXT)) {
nsAutoString description;
nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_describedby, description);
nsresult rv = nsTextEquivUtils::
GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_describedby,
description);
if (NS_FAILED(rv)) {
PRBool isXUL = content->IsNodeOfType(nsINode::eXUL);
if (isXUL) {
@ -341,7 +342,8 @@ NS_IMETHODIMP nsAccessible::GetDescription(nsAString& aDescription)
if (descriptionContent) {
// We have a description content node
AppendFlatStringFromSubtree(descriptionContent, &description);
nsTextEquivUtils::
AppendTextEquivFromContent(this, descriptionContent, &description);
}
}
if (description.IsEmpty()) {
@ -1456,284 +1458,6 @@ nsAccessible::TakeFocus()
return NS_OK;
}
nsresult nsAccessible::AppendStringWithSpaces(nsAString *aFlatString, const nsAString& textEquivalent)
{
// Insert spaces to insure that words from controls aren't jammed together
if (!textEquivalent.IsEmpty()) {
if (!aFlatString->IsEmpty())
aFlatString->Append(PRUnichar(' '));
aFlatString->Append(textEquivalent);
aFlatString->Append(PRUnichar(' '));
}
return NS_OK;
}
nsresult nsAccessible::AppendNameFromAccessibleFor(nsIContent *aContent,
nsAString *aFlatString,
PRBool aFromValue)
{
nsAutoString textEquivalent, value;
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(aContent));
nsCOMPtr<nsIAccessible> accessible;
if (domNode == mDOMNode) {
accessible = this;
if (!aFromValue) {
// prevent recursive call GetName()
return NS_OK;
}
}
else {
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
accService->GetAccessibleInWeakShell(domNode, mWeakShell, getter_AddRefs(accessible));
}
if (accessible) {
if (aFromValue) {
accessible->GetValue(textEquivalent);
}
else {
accessible->GetName(textEquivalent);
}
}
textEquivalent.CompressWhitespace();
return AppendStringWithSpaces(aFlatString, textEquivalent);
}
/*
* AppendFlatStringFromContentNode and AppendFlatStringFromSubtree
*
* This method will glean useful text, in whatever form it exists, from any content node given to it.
* It is used by any decendant of nsAccessible that needs to get text from a single node, as
* well as by nsAccessible::AppendFlatStringFromSubtree, which gleans and concatenates text from any node and
* that node's decendants.
*/
nsresult nsAccessible::AppendFlatStringFromContentNode(nsIContent *aContent, nsAString *aFlatString)
{
if (aContent->IsNodeOfType(nsINode::eTEXT)) {
// If it's a text node, append the text
PRBool isHTMLBlock = PR_FALSE;
nsCOMPtr<nsIPresShell> shell = GetPresShell();
if (!shell) {
return NS_ERROR_FAILURE;
}
nsIContent *parentContent = aContent->GetParent();
nsCOMPtr<nsIContent> appendedSubtreeStart(do_QueryInterface(mDOMNode));
if (parentContent && parentContent != appendedSubtreeStart) {
nsIFrame *frame = shell->GetPrimaryFrameFor(parentContent);
if (frame) {
// If this text is inside a block level frame (as opposed to span level), we need to add spaces around that
// block's text, so we don't get words jammed together in final name
// Extra spaces will be trimmed out later
const nsStyleDisplay* display = frame->GetStyleDisplay();
if (display->IsBlockOutside() ||
display->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL) {
isHTMLBlock = PR_TRUE;
if (!aFlatString->IsEmpty()) {
aFlatString->Append(PRUnichar(' '));
}
}
}
}
if (aContent->TextLength() > 0) {
nsIFrame *frame = shell->GetPrimaryFrameFor(aContent);
if (frame) {
nsresult rv = frame->GetRenderedText(aFlatString);
NS_ENSURE_SUCCESS(rv, rv);
} else {
//if aContent is an object that is display: none, we have no a frame
aContent->AppendTextTo(*aFlatString);
}
if (isHTMLBlock && !aFlatString->IsEmpty()) {
aFlatString->Append(PRUnichar(' '));
}
}
return NS_OK;
}
nsAutoString textEquivalent;
if (!aContent->IsNodeOfType(nsINode::eHTML)) {
if (aContent->IsNodeOfType(nsINode::eXUL)) {
nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl(do_QueryInterface(aContent));
if (labeledEl) {
labeledEl->GetLabel(textEquivalent);
}
else {
if (aContent->NodeInfo()->Equals(nsAccessibilityAtoms::label, kNameSpaceID_XUL)) {
aContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::value, textEquivalent);
}
if (textEquivalent.IsEmpty()) {
aContent->GetAttr(kNameSpaceID_None,
nsAccessibilityAtoms::tooltiptext, textEquivalent);
}
}
AppendNameFromAccessibleFor(aContent, &textEquivalent, PR_TRUE /* use value */);
return AppendStringWithSpaces(aFlatString, textEquivalent);
}
return NS_OK; // Not HTML and not XUL -- we don't handle it yet
}
nsCOMPtr<nsIAtom> tag = aContent->Tag();
if (tag == nsAccessibilityAtoms::img) {
return AppendNameFromAccessibleFor(aContent, aFlatString);
}
if (tag == nsAccessibilityAtoms::input) {
static nsIContent::AttrValuesArray strings[] =
{&nsAccessibilityAtoms::button, &nsAccessibilityAtoms::submit,
&nsAccessibilityAtoms::reset, &nsAccessibilityAtoms::image, nsnull};
if (aContent->FindAttrValueIn(kNameSpaceID_None, nsAccessibilityAtoms::type,
strings, eIgnoreCase) >= 0) {
return AppendNameFromAccessibleFor(aContent, aFlatString);
}
}
if (tag == nsAccessibilityAtoms::object && !aContent->GetChildCount()) {
// If object has no alternative content children, try title
aContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::title, textEquivalent);
}
else if (tag == nsAccessibilityAtoms::br) {
// If it's a line break, insert a space so that words aren't jammed together
aFlatString->AppendLiteral("\r\n");
return NS_OK;
}
else if (tag != nsAccessibilityAtoms::a && tag != nsAccessibilityAtoms::area) {
AppendNameFromAccessibleFor(aContent, aFlatString, PR_TRUE /* use value */);
}
textEquivalent.CompressWhitespace();
return AppendStringWithSpaces(aFlatString, textEquivalent);
}
nsresult nsAccessible::AppendFlatStringFromSubtree(nsIContent *aContent, nsAString *aFlatString)
{
static PRBool isAlreadyHere; // Prevent recursion which can cause infinite loops
if (isAlreadyHere) {
return NS_OK;
}
isAlreadyHere = PR_TRUE;
nsCOMPtr<nsIPresShell> shell = GetPresShell();
NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
nsIFrame *frame = shell->GetPrimaryFrameFor(aContent);
PRBool isHidden = (!frame || !frame->GetStyleVisibility()->IsVisible());
nsresult rv = AppendFlatStringFromSubtreeRecurse(aContent, aFlatString,
isHidden);
isAlreadyHere = PR_FALSE;
if (NS_SUCCEEDED(rv) && !aFlatString->IsEmpty()) {
nsAString::const_iterator start, end;
aFlatString->BeginReading(start);
aFlatString->EndReading(end);
PRInt32 spacesToTruncate = 0;
while (-- end != start && *end == ' ')
++ spacesToTruncate;
if (spacesToTruncate > 0)
aFlatString->Truncate(aFlatString->Length() - spacesToTruncate);
}
return rv;
}
nsresult
nsAccessible::AppendFlatStringFromSubtreeRecurse(nsIContent *aContent,
nsAString *aFlatString,
PRBool aIsRootHidden)
{
// Depth first search for all text nodes that are decendants of content node.
// Append all the text into one flat string
PRUint32 numChildren = 0;
nsCOMPtr<nsIDOMXULSelectControlElement> selectControlEl(do_QueryInterface(aContent));
nsCOMPtr<nsIAtom> tag = aContent->Tag();
if (!selectControlEl &&
tag != nsAccessibilityAtoms::textarea &&
tag != nsAccessibilityAtoms::select) {
// Don't walk children of elements with options, just get label directly.
// Don't traverse the children of a textarea, we want the value, not the
// static text node.
// Don't traverse the children of a select element, we only want the
// current value.
numChildren = aContent->GetChildCount();
}
if (numChildren == 0) {
// There are no children or they are irrelvant: get the text from the current node
AppendFlatStringFromContentNode(aContent, aFlatString);
return NS_OK;
}
// There are relevant children: use them to get the text.
nsCOMPtr<nsIPresShell> shell = GetPresShell();
NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
PRUint32 index;
for (index = 0; index < numChildren; index++) {
nsCOMPtr<nsIContent> childContent = aContent->GetChildAt(index);
// Walk into hidden subtree if the the root parent is also hidden. This
// happens when the author explictly uses a hidden label or description.
if (!aIsRootHidden) {
nsIFrame *childFrame = shell->GetPrimaryFrameFor(childContent);
if (!childFrame || !childFrame->GetStyleVisibility()->IsVisible())
continue;
}
AppendFlatStringFromSubtreeRecurse(childContent, aFlatString,
aIsRootHidden);
}
return NS_OK;
}
nsresult nsAccessible::GetTextFromRelationID(nsIAtom *aIDProperty, nsString &aName)
{
// Get DHTML name from content subtree pointed to by ID attribute
aName.Truncate();
NS_ASSERTION(mDOMNode, "Called from shutdown accessible");
nsCOMPtr<nsIContent> content = nsCoreUtils::GetRoleContent(mDOMNode);
if (!content)
return NS_OK;
nsCOMPtr<nsIArray> refElms;
nsCoreUtils::GetElementsByIDRefsAttr(content, aIDProperty,
getter_AddRefs(refElms));
if (!refElms)
return NS_OK;
PRUint32 count = 0;
nsresult rv = refElms->GetLength(&count);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIContent> refContent;
for (PRUint32 idx = 0; idx < count; idx++) {
refContent = do_QueryElementAt(refElms, idx, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (!aName.IsEmpty())
aName += ' '; // Need whitespace between multiple labels or descriptions
rv = AppendFlatStringFromSubtree(refContent, &aName);
NS_ENSURE_SUCCESS(rv, rv);
}
aName.CompressWhitespace();
return NS_OK;
}
nsresult
nsAccessible::GetHTMLName(nsAString& aLabel)
{
@ -1746,7 +1470,8 @@ nsAccessible::GetHTMLName(nsAString& aLabel)
nsIContent *labelContent = nsCoreUtils::GetHTMLLabelContent(content);
if (labelContent) {
nsAutoString label;
nsresult rv = AppendFlatStringFromSubtree(labelContent, &label);
nsresult rv =
nsTextEquivUtils::AppendTextEquivFromContent(this, labelContent, &label);
NS_ENSURE_SUCCESS(rv, rv);
label.CompressWhitespace();
@ -1756,20 +1481,7 @@ nsAccessible::GetHTMLName(nsAString& aLabel)
}
}
PRUint32 role = nsAccUtils::Role(this);
PRUint32 canAggregateName =
nsNameUtils::gRoleToNameRulesMap[role] & eFromSubtree;
if (canAggregateName) {
// Don't use AppendFlatStringFromSubtree for container widgets like menulist
nsresult rv = AppendFlatStringFromSubtree(content, &aLabel);
NS_ENSURE_SUCCESS(rv, rv);
if (!aLabel.IsEmpty())
return NS_OK;
}
return NS_OK;
return nsTextEquivUtils::GetNameFromSubtree(this, aLabel);
}
/**
@ -1829,7 +1541,7 @@ nsAccessible::GetXULName(nsAString& aLabel)
if (xulLabel && NS_SUCCEEDED(xulLabel->GetValue(label)) && label.IsEmpty()) {
// If no value attribute, a non-empty label must contain
// children that define its text -- possibly using HTML
AppendFlatStringFromSubtree(labelContent, &label);
nsTextEquivUtils::AppendTextEquivFromContent(this, labelContent, &label);
}
}
@ -1854,12 +1566,7 @@ nsAccessible::GetXULName(nsAString& aLabel)
parent = parent->GetParent();
}
PRUint32 role = nsAccUtils::Role(this);
PRUint32 canAggregateName =
nsNameUtils::gRoleToNameRulesMap[role] & eFromSubtree;
return canAggregateName ?
AppendFlatStringFromSubtree(content, &aLabel) : NS_OK;
return nsTextEquivUtils::GetNameFromSubtree(this, aLabel);
}
NS_IMETHODIMP
@ -1926,7 +1633,8 @@ NS_IMETHODIMP nsAccessible::GetFinalRole(PRUint32 *aRole)
// gLandmarkRoleMap: can use role of accessible class impl
// gEmptyRoleMap and all others: cannot use role of accessible class impl
if (mRoleMapEntry != &nsARIAMap::gLandmarkRoleMap) {
// We can now expose ROLE_NOTHING when there is a role map entry, which
// We can now expose ROLE_NOTHING when there is a role map entry or used
// role is nothing, which
// will cause ATK to use ROLE_UNKNOWN and MSAA to use a BSTR role with
// the ARIA role or element's tag. In either case the AT can also use
// the object attributes tag and xml-roles to find out more.
@ -1983,36 +1691,20 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
attributes->SetStringProperty(NS_LITERAL_CSTRING("valuetext"), valuetext, oldValueUnused);
}
PRUint32 role = nsAccUtils::Role(this);
if (role == nsIAccessibleRole::ROLE_CHECKBUTTON ||
role == nsIAccessibleRole::ROLE_PUSHBUTTON ||
role == nsIAccessibleRole::ROLE_MENUITEM ||
role == nsIAccessibleRole::ROLE_LISTITEM ||
role == nsIAccessibleRole::ROLE_OPTION ||
role == nsIAccessibleRole::ROLE_RADIOBUTTON ||
role == nsIAccessibleRole::ROLE_RICH_OPTION ||
role == nsIAccessibleRole::ROLE_OUTLINEITEM ||
content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_checked)) {
// Might be checkable -- checking role & ARIA attribute first is faster than getting state
PRUint32 state = 0;
GetState(&state, nsnull);
if (state & nsIAccessibleStates::STATE_CHECKABLE) {
// No official state for checkable, so use object attribute to expose that
attributes->SetStringProperty(NS_LITERAL_CSTRING("checkable"), NS_LITERAL_STRING("true"),
oldValueUnused);
}
}
// Expose checkable object attribute if the accessible has checkable state
if (nsAccUtils::State(this) & nsIAccessibleStates::STATE_CHECKABLE)
nsAccUtils::SetAccAttr(attributes, nsAccessibilityAtoms::checkable, NS_LITERAL_STRING("true"));
// Group attributes (level/setsize/posinset)
if (!nsAccUtils::HasAccGroupAttrs(attributes)) {
// Calculate group attributes based on accessible hierarhy if they weren't
// provided by ARIA or by accessible class implementation.
PRUint32 role = nsAccUtils::Role(this);
rv = ComputeGroupAttributes(role, attributes);
NS_ENSURE_SUCCESS(rv, rv);
}
// Expose all ARIA attributes
// Expose object attributes from ARIA attributes.
PRUint32 numAttrs = content->GetAttrCount();
for (PRUint32 count = 0; count < numAttrs; count ++) {
const nsAttrName *attr = content->GetAttrNameAt(count);
@ -2022,8 +1714,12 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
attrAtom->GetUTF8String(&attrStr);
if (PL_strncmp(attrStr, "aria-", 5))
continue; // Not ARIA
if (!nsAccUtils::IsARIAPropForObjectAttr(attrAtom))
PRUint8 attrFlags = nsAccUtils::GetAttributeCharacteristics(attrAtom);
if (attrFlags & ATTR_EXPOSEOBJ)
continue; // No need to expose obj attribute -- will be exposed some other way
if ((attrFlags & ATTR_VALTOKEN) &&
!nsAccUtils::HasDefinedARIAToken(content, attrAtom))
continue; // only expose token based attributes if they are defined
nsAutoString value;
if (content->GetAttr(kNameSpaceID_None, attrAtom, value)) {
attributes->SetStringProperty(nsDependentCString(attrStr + 5), value, oldValueUnused);
@ -2031,6 +1727,17 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
}
}
// If there is no aria-live attribute then expose default value of 'live'
// object attribute used for ARIA role of this accessible.
if (mRoleMapEntry) {
nsAutoString live;
nsAccUtils::GetAccAttr(attributes, nsAccessibilityAtoms::live, live);
if (live.IsEmpty()) {
nsAccUtils::GetLiveAttrValue(mRoleMapEntry->liveAttRule, live);
nsAccUtils::SetAccAttr(attributes, nsAccessibilityAtoms::live, live);
}
}
return NS_OK;
}
@ -2330,7 +2037,11 @@ nsAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
*aExtraState |= nsIAccessibleStates::EXT_STATE_HORIZONTAL;
}
}
// If we are editable, force readonly bit off
if (*aExtraState & nsIAccessibleStates::EXT_STATE_EDITABLE)
*aState &= ~nsIAccessibleStates::STATE_READONLY;
return NS_OK;
}
@ -2349,7 +2060,8 @@ nsAccessible::GetARIAState(PRUint32 *aState)
}
if (mRoleMapEntry) {
// Once DHTML role is used, we're only readonly if DHTML readonly used
// Once an ARIA role is used, default to not-readonly. This can be overridden
// by aria-readonly, or if the ARIA role is mapped to readonly by default
*aState &= ~nsIAccessibleStates::STATE_READONLY;
if (content->HasAttr(kNameSpaceID_None, content->GetIDAttributeName())) {
@ -2381,6 +2093,7 @@ nsAccessible::GetARIAState(PRUint32 *aState)
if (!mRoleMapEntry)
return NS_OK;
// Note: the readonly bitflag will be overridden later if content is editable
*aState |= mRoleMapEntry->state;
if (MappedAttrState(content, aState, &mRoleMapEntry->attributeMap1) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap2) &&
@ -3368,14 +3081,18 @@ nsAccessible::GetARIAName(nsAString& aName)
// First check for label override via aria-label property
nsAutoString label;
if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label, label)) {
label.CompressWhitespace();
aName = label;
return NS_OK;
}
// Second check for label override via aria-labelledby relationship
nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_labelledby, label);
if (NS_SUCCEEDED(rv))
nsresult rv = nsTextEquivUtils::
GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_labelledby, label);
if (NS_SUCCEEDED(rv)) {
label.CompressWhitespace();
aName = label;
}
return rv;
}

Просмотреть файл

@ -41,6 +41,10 @@
#include "nsAccessNodeWrap.h"
#include "nsARIAMap.h"
#include "nsRelUtils.h"
#include "nsTextEquivUtils.h"
#include "nsIAccessible.h"
#include "nsPIAccessible.h"
#include "nsIAccessibleHyperLink.h"
@ -50,15 +54,12 @@
#include "nsIAccessibleStates.h"
#include "nsIAccessibleEvent.h"
#include "nsRelUtils.h"
#include "nsIDOMNodeList.h"
#include "nsINameSpaceManager.h"
#include "nsWeakReference.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsIDOMDOMStringList.h"
#include "nsARIAMap.h"
struct nsRect;
class nsIContent;
@ -181,18 +182,6 @@ protected:
virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
PRBool IsVisible(PRBool *aIsOffscreen);
// Relation helpers
/**
* For a given ARIA relation, such as labelledby or describedby, get the collated text
* for the subtree that's pointed to.
*
* @param aIDProperty The ARIA relationship property to get the text for
* @param aName Where to put the text
* @return error or success code
*/
nsresult GetTextFromRelationID(nsIAtom *aIDProperty, nsString &aName);
//////////////////////////////////////////////////////////////////////////////
// Name helpers.
@ -206,14 +195,6 @@ protected:
*/
nsresult GetXULName(nsAString& aName);
// For accessibles that are not lists of choices, the name of the subtree should be the
// sum of names in the subtree
nsresult AppendFlatStringFromSubtree(nsIContent *aContent, nsAString *aFlatString);
nsresult AppendNameFromAccessibleFor(nsIContent *aContent, nsAString *aFlatString,
PRBool aFromValue = PR_FALSE);
nsresult AppendFlatStringFromContentNode(nsIContent *aContent, nsAString *aFlatString);
nsresult AppendStringWithSpaces(nsAString *aFlatString, const nsAString& textEquivalent);
// helper method to verify frames
static nsresult GetFullKeyName(const nsAString& aModifierName, const nsAString& aKeyName, nsAString& aStringOut);
static nsresult GetTranslatedString(const nsAString& aKey, nsAString& aStringOut);

Просмотреть файл

@ -344,7 +344,7 @@ nsCaretAccessible::GetCaretRect(nsIWidget **aOutWidget)
rect += offsetFromWidget;
caretRect = nsRect::ToOutsidePixels(rect, presContext->AppUnitsPerDevPixel());
(*aOutWidget)->WidgetToScreen(caretRect, caretRect);
caretRect.MoveBy((*aOutWidget)->WidgetToScreenOffset());
// Correct for character size, so that caret always matches the size of the character
// This is important for font size transitions, and is necessary because the Gecko caret uses the

Просмотреть файл

@ -263,7 +263,9 @@ nsDocAccessible::GetDescription(nsAString& aDescription)
if (aDescription.IsEmpty()) {
nsAutoString description;
GetTextFromRelationID(nsAccessibilityAtoms::aria_describedby, description);
nsTextEquivUtils::
GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_describedby,
description);
aDescription = description;
}

Просмотреть файл

@ -1,169 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Foundation
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsNameUtils.h"
////////////////////////////////////////////////////////////////////////////////
// Name rules to role map.
PRUint32 nsNameUtils::gRoleToNameRulesMap[] =
{
eNoRule, // ROLE_NOTHING
eNoRule, // ROLE_TITLEBAR
eNoRule, // ROLE_MENUBAR
eNoRule, // ROLE_SCROLLBAR
eNoRule, // ROLE_GRIP
eNoRule, // ROLE_SOUND
eNoRule, // ROLE_CURSOR
eNoRule, // ROLE_CARET
eNoRule, // ROLE_ALERT
eNoRule, // ROLE_WINDOW
eNoRule, // ROLE_INTERNAL_FRAME
eNoRule, // ROLE_MENUPOPUP
eFromSubtree, // ROLE_MENUITEM
eFromSubtree, // ROLE_TOOLTIP
eNoRule, // ROLE_APPLICATION
eNoRule, // ROLE_DOCUMENT
eNoRule, // ROLE_PANE
eNoRule, // ROLE_CHART
eNoRule, // ROLE_DIALOG
eNoRule, // ROLE_BORDER
eNoRule, // ROLE_GROUPING
eNoRule, // ROLE_SEPARATOR
eNoRule, // ROLE_TOOLBAR
eNoRule, // ROLE_STATUSBAR
eNoRule, // ROLE_TABLE
eFromSubtree, // ROLE_COLUMNHEADER
eFromSubtree, // ROLE_ROWHEADER
eFromSubtree, // ROLE_COLUMN
eFromSubtree, // ROLE_ROW
eNoRule, // ROLE_CELL
eFromSubtree, // ROLE_LINK
eFromSubtree, // ROLE_HELPBALLOON
eNoRule, // ROLE_CHARACTER
eNoRule, // ROLE_LIST
eFromSubtree, // ROLE_LISTITEM
eNoRule, // ROLE_OUTLINE
eFromSubtree, // ROLE_OUTLINEITEM
eFromSubtree, // ROLE_PAGETAB
eNoRule, // ROLE_PROPERTYPAGE
eNoRule, // ROLE_INDICATOR
eNoRule, // ROLE_GRAPHIC
eNoRule, // ROLE_STATICTEXT
eNoRule, // ROLE_TEXT_LEAF
eFromSubtree, // ROLE_PUSHBUTTON
eFromSubtree, // ROLE_CHECKBUTTON
eFromSubtree, // ROLE_RADIOBUTTON
eNoRule, // ROLE_COMBOBOX
eNoRule, // ROLE_DROPLIST
eNoRule, // ROLE_PROGRESSBAR
eNoRule, // ROLE_DIAL
eNoRule, // ROLE_HOTKEYFIELD
eNoRule, // ROLE_SLIDER
eNoRule, // ROLE_SPINBUTTON
eNoRule, // ROLE_DIAGRAM
eNoRule, // ROLE_ANIMATION
eNoRule, // ROLE_EQUATION
eFromSubtree, // ROLE_BUTTONDROPDOWN
eFromSubtree, // ROLE_BUTTONMENU
eFromSubtree, // ROLE_BUTTONDROPDOWNGRID
eNoRule, // ROLE_WHITESPACE
eNoRule, // ROLE_PAGETABLIST
eNoRule, // ROLE_CLOCK
eNoRule, // ROLE_SPLITBUTTON
eNoRule, // ROLE_IPADDRESS
eNoRule, // ROLE_ACCEL_LABEL
eNoRule, // ROLE_ARROW
eNoRule, // ROLE_CANVAS
eFromSubtree, // ROLE_CHECK_MENU_ITEM
eNoRule, // ROLE_COLOR_CHOOSER
eNoRule, // ROLE_DATE_EDITOR
eNoRule, // ROLE_DESKTOP_ICON
eNoRule, // ROLE_DESKTOP_FRAME
eNoRule, // ROLE_DIRECTORY_PANE
eNoRule, // ROLE_FILE_CHOOSER
eNoRule, // ROLE_FONT_CHOOSER
eNoRule, // ROLE_CHROME_WINDOW
eNoRule, // ROLE_GLASS_PANE
eNoRule, // ROLE_HTML_CONTAINER
eNoRule, // ROLE_ICON
eNoRule, // ROLE_LABEL
eNoRule, // ROLE_LAYERED_PANE
eNoRule, // ROLE_OPTION_PANE
eNoRule, // ROLE_PASSWORD_TEXT
eNoRule, // ROLE_POPUP_MENU
eFromSubtree, // ROLE_RADIO_MENU_ITEM
eNoRule, // ROLE_ROOT_PANE
eNoRule, // ROLE_SCROLL_PANE
eNoRule, // ROLE_SPLIT_PANE
eFromSubtree, // ROLE_TABLE_COLUMN_HEADER
eFromSubtree, // ROLE_TABLE_ROW_HEADER
eFromSubtree, // ROLE_TEAR_OFF_MENU_ITEM
eNoRule, // ROLE_TERMINAL
eNoRule, // ROLE_TEXT_CONTAINER
eFromSubtree, // ROLE_TOGGLE_BUTTON
eNoRule, // ROLE_TREE_TABLE
eNoRule, // ROLE_VIEWPORT
eNoRule, // ROLE_HEADER
eNoRule, // ROLE_FOOTER
eNoRule, // ROLE_PARAGRAPH
eNoRule, // ROLE_RULER
eNoRule, // ROLE_AUTOCOMPLETE
eNoRule, // ROLE_EDITBAR
eNoRule, // ROLE_ENTRY
eNoRule, // ROLE_CAPTION
eNoRule, // ROLE_DOCUMENT_FRAME
eNoRule, // ROLE_HEADING
eNoRule, // ROLE_PAGE
eNoRule, // ROLE_SECTION
eNoRule, // ROLE_REDUNDANT_OBJECT
eNoRule, // ROLE_FORM
eNoRule, // ROLE_IME
eNoRule, // ROLE_APP_ROOT
eFromSubtree, // ROLE_PARENT_MENUITEM
eNoRule, // ROLE_CALENDAR
eNoRule, // ROLE_COMBOBOX_LIST
eFromSubtree, // ROLE_COMBOBOX_OPTION
eNoRule, // ROLE_IMAGE_MAP
eFromSubtree, // ROLE_OPTION
eFromSubtree, // ROLE_RICH_OPTION
eNoRule, // ROLE_LISTBOX
eNoRule, // ROLE_FLAT_EQUATION
eFromSubtree // ROLE_GRID_CELL
};

Просмотреть файл

@ -0,0 +1,605 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsTextAttrs.h"
#include "nsAccessNode.h"
#include "nsHyperTextAccessibleWrap.h"
////////////////////////////////////////////////////////////////////////////////
// Constants and structures
/**
* Item of the gCSSTextAttrsMap map.
*/
struct nsCSSTextAttrMapItem
{
const char* mCSSName;
const char* mCSSValue;
nsIAtom** mAttrName;
const char* mAttrValue;
};
/**
* The map of CSS properties to text attributes.
*/
const char* const kAnyValue = nsnull;
const char* const kCopyValue = nsnull;
static nsCSSTextAttrMapItem gCSSTextAttrsMap[] =
{
// CSS name CSS value Attribute name Attribute value
{ "color", kAnyValue, &nsAccessibilityAtoms::color, kCopyValue },
{ "font-family", kAnyValue, &nsAccessibilityAtoms::fontFamily, kCopyValue },
{ "font-style", kAnyValue, &nsAccessibilityAtoms::fontStyle, kCopyValue },
{ "font-weight", kAnyValue, &nsAccessibilityAtoms::fontWeight, kCopyValue },
{ "text-decoration", "line-through", &nsAccessibilityAtoms::textLineThroughStyle, "solid" },
{ "text-decoration", "underline", &nsAccessibilityAtoms::textUnderlineStyle, "solid" },
{ "vertical-align", kAnyValue, &nsAccessibilityAtoms::textPosition, kCopyValue }
};
////////////////////////////////////////////////////////////////////////////////
// nsTextAttrs
nsTextAttrsMgr::nsTextAttrsMgr(nsHyperTextAccessible *aHyperTextAcc,
nsIDOMNode *aHyperTextNode,
PRBool aIncludeDefAttrs,
nsIDOMNode *aOffsetNode) :
mHyperTextAcc(aHyperTextAcc), mHyperTextNode(aHyperTextNode),
mIncludeDefAttrs(aIncludeDefAttrs), mOffsetNode(aOffsetNode)
{
}
nsresult
nsTextAttrsMgr::GetAttributes(nsIPersistentProperties *aAttributes,
PRInt32 *aStartHTOffset,
PRInt32 *aEndHTOffset)
{
// 1. Hyper text accessible and its DOM node must be specified always.
// 2. Offset DOM node and result hyper text offsets must be specifed in
// the case of text attributes.
// 3. Offset DOM node and result hyper text offsets must not be specifed but
// include default text attributes flag and attributes list must be specified
// in the case of default text attributes.
NS_PRECONDITION(mHyperTextAcc && mHyperTextNode &&
((mOffsetNode && aStartHTOffset && aEndHTOffset) ||
(!mOffsetNode && !aStartHTOffset && !aEndHTOffset &&
mIncludeDefAttrs && aAttributes)),
"Wrong usage of nsTextAttrsMgr!");
nsCOMPtr<nsIDOMElement> hyperTextElm =
nsCoreUtils::GetDOMElementFor(mHyperTextNode);
nsCOMPtr<nsIDOMElement> offsetElm;
if (mOffsetNode)
offsetElm = nsCoreUtils::GetDOMElementFor(mOffsetNode);
nsIFrame *rootFrame = nsCoreUtils::GetFrameFor(hyperTextElm);
nsIFrame *frame = nsnull;
if (offsetElm)
frame = nsCoreUtils::GetFrameFor(offsetElm);
nsTPtrArray<nsITextAttr> textAttrArray(10);
// "language" text attribute
nsLangTextAttr langTextAttr(mHyperTextAcc, mHyperTextNode, mOffsetNode);
textAttrArray.AppendElement(static_cast<nsITextAttr*>(&langTextAttr));
// "color" text attribute
nsCSSTextAttr colorTextAttr(0, hyperTextElm, offsetElm);
textAttrArray.AppendElement(static_cast<nsITextAttr*>(&colorTextAttr));
// "font-family" text attribute
nsCSSTextAttr fontFamilyTextAttr(1, hyperTextElm, offsetElm);
textAttrArray.AppendElement(static_cast<nsITextAttr*>(&fontFamilyTextAttr));
// "font-style" text attribute
nsCSSTextAttr fontStyleTextAttr(2, hyperTextElm, offsetElm);
textAttrArray.AppendElement(static_cast<nsITextAttr*>(&fontStyleTextAttr));
// "font-weight" text attribute
nsCSSTextAttr fontWeightTextAttr(3, hyperTextElm, offsetElm);
textAttrArray.AppendElement(static_cast<nsITextAttr*>(&fontWeightTextAttr));
// "text-line-through-style" text attribute
nsCSSTextAttr lineThroughTextAttr(4, hyperTextElm, offsetElm);
textAttrArray.AppendElement(static_cast<nsITextAttr*>(&lineThroughTextAttr));
// "text-underline-style" text attribute
nsCSSTextAttr underlineTextAttr(5, hyperTextElm, offsetElm);
textAttrArray.AppendElement(static_cast<nsITextAttr*>(&underlineTextAttr));
// "text-position" text attribute
nsCSSTextAttr posTextAttr(6, hyperTextElm, offsetElm);
textAttrArray.AppendElement(static_cast<nsITextAttr*>(&posTextAttr));
// "background-color" text attribute
nsBGColorTextAttr bgColorTextAttr(rootFrame, frame);
textAttrArray.AppendElement(static_cast<nsITextAttr*>(&bgColorTextAttr));
// "font-size" text attribute
nsFontSizeTextAttr fontSizeTextAttr(rootFrame, frame);
textAttrArray.AppendElement(static_cast<nsITextAttr*>(&fontSizeTextAttr));
// Expose text attributes if applicable.
if (aAttributes) {
PRUint32 len = textAttrArray.Length();
for (PRUint32 idx = 0; idx < len; idx++) {
nsITextAttr *textAttr = textAttrArray[idx];
nsAutoString value;
if (textAttr->GetValue(value, mIncludeDefAttrs))
nsAccUtils::SetAccAttr(aAttributes, textAttr->GetName(), value);
}
}
nsresult rv = NS_OK;
// Expose text attributes range where they are applied if applicable.
if (mOffsetNode)
rv = GetRange(textAttrArray, aStartHTOffset, aEndHTOffset);
textAttrArray.Clear();
return rv;
}
nsresult
nsTextAttrsMgr::GetRange(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
PRInt32 *aStartHTOffset, PRInt32 *aEndHTOffset)
{
nsCOMPtr<nsIDOMElement> rootElm =
nsCoreUtils::GetDOMElementFor(mHyperTextNode);
NS_ENSURE_STATE(rootElm);
nsCOMPtr<nsIDOMNode> tmpNode(mOffsetNode);
nsCOMPtr<nsIDOMNode> currNode(mOffsetNode);
PRUint32 len = aTextAttrArray.Length();
// Navigate backwards and forwards from current node to the root node to
// calculate range bounds for the text attribute. Navigation sequence is the
// following:
// 1. Navigate through the siblings.
// 2. If the traversed sibling has children then navigate from its leaf child
// to it through whole tree of the traversed sibling.
// 3. Get the parent and cycle algorithm until the root node.
// Navigate backwards (find the start offset).
while (currNode && currNode != rootElm) {
nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(currNode));
NS_ENSURE_STATE(currElm);
if (currNode != mOffsetNode) {
PRBool stop = PR_FALSE;
for (PRUint32 idx = 0; idx < len; idx++) {
nsITextAttr *textAttr = aTextAttrArray[idx];
if (!textAttr->Equal(currElm)) {
PRInt32 startHTOffset = 0;
nsCOMPtr<nsIAccessible> startAcc;
nsresult rv = mHyperTextAcc->
DOMPointToHypertextOffset(tmpNode, -1, &startHTOffset,
getter_AddRefs(startAcc));
NS_ENSURE_SUCCESS(rv, rv);
if (!startAcc)
startHTOffset = 0;
if (startHTOffset > *aStartHTOffset)
*aStartHTOffset = startHTOffset;
stop = PR_TRUE;
break;
}
}
if (stop)
break;
}
currNode->GetPreviousSibling(getter_AddRefs(tmpNode));
if (tmpNode) {
// Navigate through the subtree of traversed children to calculate
// left bound of the range.
FindStartOffsetInSubtree(aTextAttrArray, tmpNode, currNode,
aStartHTOffset);
}
currNode->GetParentNode(getter_AddRefs(tmpNode));
currNode.swap(tmpNode);
}
// Navigate forwards (find the end offset).
PRBool moveIntoSubtree = PR_TRUE;
currNode = mOffsetNode;
while (currNode && currNode != rootElm) {
nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(currNode));
NS_ENSURE_STATE(currElm);
// Stop new end offset searching if the given text attribute changes its
// value.
PRBool stop = PR_FALSE;
for (PRUint32 idx = 0; idx < len; idx++) {
nsITextAttr *textAttr = aTextAttrArray[idx];
if (!textAttr->Equal(currElm)) {
PRInt32 endHTOffset = 0;
nsresult rv = mHyperTextAcc->
DOMPointToHypertextOffset(currNode, -1, &endHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (endHTOffset < *aEndHTOffset)
*aEndHTOffset = endHTOffset;
stop = PR_TRUE;
break;
}
}
if (stop)
break;
if (moveIntoSubtree) {
// Navigate through subtree of traversed node. We use 'moveIntoSubtree'
// flag to avoid traversing the same subtree twice.
currNode->GetFirstChild(getter_AddRefs(tmpNode));
if (tmpNode)
FindEndOffsetInSubtree(aTextAttrArray, tmpNode, aEndHTOffset);
}
currNode->GetNextSibling(getter_AddRefs(tmpNode));
moveIntoSubtree = PR_TRUE;
if (!tmpNode) {
currNode->GetParentNode(getter_AddRefs(tmpNode));
moveIntoSubtree = PR_FALSE;
}
currNode.swap(tmpNode);
}
return NS_OK;
}
PRBool
nsTextAttrsMgr::FindEndOffsetInSubtree(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
nsIDOMNode *aCurrNode,
PRInt32 *aHTOffset)
{
if (!aCurrNode)
return PR_FALSE;
nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(aCurrNode));
NS_ENSURE_STATE(currElm);
// If the given text attribute (pointed by nsTextAttr object) changes its
// value on the traversed element then fit the end of range.
PRUint32 len = aTextAttrArray.Length();
for (PRUint32 idx = 0; idx < len; idx++) {
nsITextAttr *textAttr = aTextAttrArray[idx];
if (!textAttr->Equal(currElm)) {
PRInt32 endHTOffset = 0;
nsresult rv = mHyperTextAcc->
DOMPointToHypertextOffset(aCurrNode, -1, &endHTOffset);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
if (endHTOffset < *aHTOffset)
*aHTOffset = endHTOffset;
return PR_TRUE;
}
}
// Deeply traverse into the tree to fit the end of range.
nsCOMPtr<nsIDOMNode> nextNode;
aCurrNode->GetFirstChild(getter_AddRefs(nextNode));
if (nextNode) {
PRBool res = FindEndOffsetInSubtree(aTextAttrArray, nextNode, aHTOffset);
if (res)
return res;
}
aCurrNode->GetNextSibling(getter_AddRefs(nextNode));
if (nextNode) {
if (FindEndOffsetInSubtree(aTextAttrArray, nextNode, aHTOffset))
return PR_TRUE;
}
return PR_FALSE;
}
PRBool
nsTextAttrsMgr::FindStartOffsetInSubtree(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
nsIDOMNode *aCurrNode,
nsIDOMNode *aPrevNode,
PRInt32 *aHTOffset)
{
if (!aCurrNode)
return PR_FALSE;
// Find the closest element back to the traversed element.
nsCOMPtr<nsIDOMNode> nextNode;
aCurrNode->GetLastChild(getter_AddRefs(nextNode));
if (nextNode) {
if (FindStartOffsetInSubtree(aTextAttrArray, nextNode, aPrevNode, aHTOffset))
return PR_TRUE;
}
nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(aCurrNode));
NS_ENSURE_STATE(currElm);
// If the given text attribute (pointed by nsTextAttr object) changes its
// value on the traversed element then fit the start of range.
PRUint32 len = aTextAttrArray.Length();
for (PRUint32 idx = 0; idx < len; idx++) {
nsITextAttr *textAttr = aTextAttrArray[idx];
if (!textAttr->Equal(currElm)) {
PRInt32 startHTOffset = 0;
nsCOMPtr<nsIAccessible> startAcc;
nsresult rv = mHyperTextAcc->
DOMPointToHypertextOffset(aPrevNode, -1, &startHTOffset,
getter_AddRefs(startAcc));
NS_ENSURE_SUCCESS(rv, PR_FALSE);
if (!startAcc)
startHTOffset = 0;
if (startHTOffset > *aHTOffset)
*aHTOffset = startHTOffset;
return PR_TRUE;
}
}
// Moving backwards to find the start of range.
aCurrNode->GetPreviousSibling(getter_AddRefs(nextNode));
if (nextNode) {
if (FindStartOffsetInSubtree(aTextAttrArray, nextNode, aCurrNode, aHTOffset))
return PR_TRUE;
}
return PR_FALSE;
}
////////////////////////////////////////////////////////////////////////////////
// nsLangTextAttr
nsLangTextAttr::nsLangTextAttr(nsHyperTextAccessible *aRootAcc,
nsIDOMNode *aRootNode, nsIDOMNode *aNode) :
nsTextAttr<nsAutoString>(aNode == nsnull)
{
mRootContent = do_QueryInterface(aRootNode);
nsresult rv = aRootAcc->GetLanguage(mRootNativeValue);
mIsRootDefined = NS_SUCCEEDED(rv) && !mRootNativeValue.IsEmpty();
if (aNode) {
nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
mIsDefined = GetLang(content, mNativeValue);
}
}
PRBool
nsLangTextAttr::GetValueFor(nsIDOMElement *aElm, nsAutoString *aValue)
{
nsCOMPtr<nsIContent> content = do_QueryInterface(aElm);
return GetLang(content, *aValue);
}
void
nsLangTextAttr::Format(const nsAutoString& aValue, nsAString& aFormattedValue)
{
aFormattedValue = aValue;
}
PRBool
nsLangTextAttr::GetLang(nsIContent *aContent, nsAString& aLang)
{
nsCoreUtils::GetLanguageFor(aContent, mRootContent, aLang);
return !aLang.IsEmpty();
}
////////////////////////////////////////////////////////////////////////////////
// nsCSSTextAttr
nsCSSTextAttr::nsCSSTextAttr(PRUint32 aIndex, nsIDOMElement *aRootElm,
nsIDOMElement *aElm) :
nsTextAttr<nsAutoString>(aElm == nsnull), mIndex(aIndex)
{
mIsRootDefined = GetValueFor(aRootElm, &mRootNativeValue);
if (aElm)
mIsDefined = GetValueFor(aElm, &mNativeValue);
}
nsIAtom*
nsCSSTextAttr::GetName()
{
return *gCSSTextAttrsMap[mIndex].mAttrName;
}
PRBool
nsCSSTextAttr::GetValueFor(nsIDOMElement *aElm, nsAutoString *aValue)
{
nsCOMPtr<nsIDOMCSSStyleDeclaration> currStyleDecl;
nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), aElm,
getter_AddRefs(currStyleDecl));
if (!currStyleDecl)
return PR_FALSE;
NS_ConvertASCIItoUTF16 cssName(gCSSTextAttrsMap[mIndex].mCSSName);
nsresult rv = currStyleDecl->GetPropertyValue(cssName, *aValue);
if (NS_FAILED(rv))
return PR_TRUE;
const char *cssValue = gCSSTextAttrsMap[mIndex].mCSSValue;
if (cssValue != kAnyValue && !aValue->EqualsASCII(cssValue))
return PR_FALSE;
return PR_TRUE;
}
void
nsCSSTextAttr::Format(const nsAutoString& aValue, nsAString& aFormattedValue)
{
const char *attrValue = gCSSTextAttrsMap[mIndex].mAttrValue;
if (attrValue != kCopyValue)
AppendASCIItoUTF16(attrValue, aFormattedValue);
else
aFormattedValue = aValue;
}
////////////////////////////////////////////////////////////////////////////////
// nsBackgroundTextAttr
nsBGColorTextAttr::nsBGColorTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame) :
nsTextAttr<nscolor>(aFrame == nsnull), mRootFrame(aRootFrame)
{
mIsRootDefined = GetColor(mRootFrame, &mRootNativeValue);
if (aFrame)
mIsDefined = GetColor(aFrame, &mNativeValue);
}
PRBool
nsBGColorTextAttr::GetValueFor(nsIDOMElement *aElm, nscolor *aValue)
{
nsIFrame *frame = nsCoreUtils::GetFrameFor(aElm);
if (!frame)
return PR_FALSE;
return GetColor(frame, aValue);
}
void
nsBGColorTextAttr::Format(const nscolor& aValue, nsAString& aFormattedValue)
{
// Combine the string like rgb(R, G, B) from nscolor.
nsAutoString value;
value.AppendLiteral("rgb(");
value.AppendInt(NS_GET_R(aValue));
value.AppendLiteral(", ");
value.AppendInt(NS_GET_G(aValue));
value.AppendLiteral(", ");
value.AppendInt(NS_GET_B(aValue));
value.Append(')');
aFormattedValue = value;
}
PRBool
nsBGColorTextAttr::GetColor(nsIFrame *aFrame, nscolor *aColor)
{
const nsStyleBackground *styleBackground = aFrame->GetStyleBackground();
if (NS_GET_A(styleBackground->mFallbackBackgroundColor) > 0) {
*aColor = styleBackground->mFallbackBackgroundColor;
return PR_TRUE;
}
nsIFrame *parentFrame = aFrame->GetParent();
if (!parentFrame) {
*aColor = aFrame->PresContext()->DefaultBackgroundColor();
return PR_TRUE;
}
// Each frame of parents chain for the initially passed 'aFrame' has
// transparent background color. So background color isn't changed from
// 'mRootFrame' to initially passed 'aFrame'.
if (parentFrame == mRootFrame)
return PR_FALSE;
return GetColor(parentFrame, aColor);
}
////////////////////////////////////////////////////////////////////////////////
// nsFontSizeTextAttr
nsFontSizeTextAttr::nsFontSizeTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame) :
nsTextAttr<nscoord>(aFrame == nsnull)
{
mDC = aRootFrame->PresContext()->DeviceContext();
mRootNativeValue = GetFontSize(aRootFrame);
mIsRootDefined = PR_TRUE;
if (aFrame) {
mNativeValue = GetFontSize(aFrame);
mIsDefined = PR_TRUE;
}
}
PRBool
nsFontSizeTextAttr::GetValueFor(nsIDOMElement *aElm, nscoord *aValue)
{
nsIFrame *frame = nsCoreUtils::GetFrameFor(aElm);
if (!frame)
return PR_FALSE;
*aValue = GetFontSize(frame);
return PR_TRUE;
}
void
nsFontSizeTextAttr::Format(const nscoord& aValue, nsAString& aFormattedValue)
{
// Convert from nscoord to pt.
//
// Note: according to IA2, "The conversion doesn't have to be exact.
// The intent is to give the user a feel for the size of the text."
//
// ATK does not specify a unit and will likely follow IA2 here.
//
// XXX todo: consider sharing this code with layout module? (bug 474621)
float inches = static_cast<float>(aValue) /
static_cast<float>(mDC->AppUnitsPerInch());
int pts = static_cast<int>(inches * 72 + .5); // 72 pts per inch
nsAutoString value;
value.AppendInt(pts);
value.Append(NS_LITERAL_STRING("pt"));
aFormattedValue = value;
}
nscoord
nsFontSizeTextAttr::GetFontSize(nsIFrame *aFrame)
{
nsStyleFont* styleFont =
(nsStyleFont*)(aFrame->GetStyleDataExternal(eStyleStruct_Font));
return styleFont->mSize;
}

Просмотреть файл

@ -0,0 +1,371 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsTextAttrs_h_
#define nsTextAttrs_h_
class nsHyperTextAccessible;
#include "nsAccessibilityAtoms.h"
#include "nsIDOMNode.h"
#include "nsIDOMElement.h"
#include "nsIDOMCSSStyleDeclaration.h"
#include "nsIContent.h"
#include "nsIFrame.h"
#include "nsIPersistentProperties2.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsTPtrArray.h"
class nsITextAttr;
/**
* Used to expose text attributes for the hyper text accessible (see
* nsHyperTextAccessible class). It is indended for the work with 'language' and
* CSS based text attributes.
*
* @note "invalid: spelling" text attrbiute is implemented entirerly in
* nsHyperTextAccessible class.
*/
class nsTextAttrsMgr
{
public:
/**
* Constructor. If instance of the class is intended to expose default text
* attributes then 'aIncludeDefAttrs' and 'oOffsetNode' argument must be
* skiped.
*
* @param aHyperTextAcc hyper text accessible text attributes are
* calculated for
* @param aHyperTextNode DOM node of the given hyper text accessbile
* @param aIncludeDefAttrs [optional] indicates whether default text
* attributes should be included into list of exposed
* text attributes.
* @param oOffsetNode [optional] DOM node represents hyper text offset
* inside hyper text accessible
*/
nsTextAttrsMgr(nsHyperTextAccessible *aHyperTextAcc,
nsIDOMNode *aHyperTextNode,
PRBool aIncludeDefAttrs = PR_TRUE,
nsIDOMNode *oOffsetNode = nsnull);
/*
* Return text attributes and hyper text offsets where these attributes are
* applied. Offsets are calculated in the case of non default attributes.
*
* @note In the case of default attributes pointers on hyper text offsets
* must be skiped.
*
* @param aAttributes [in, out] text attributes list
* @param aStartHTOffset [out, optional] start hyper text offset
* @param aEndHTOffset [out, optional] end hyper text offset
*/
nsresult GetAttributes(nsIPersistentProperties *aAttributes,
PRInt32 *aStartHTOffset = nsnull,
PRInt32 *aEndHTOffset = nsnull);
protected:
/**
* Calculates range (start and end offsets) of text where the text attributes
* are stretched. New offsets may be smaller if one of text attributes changes
* its value before or after the given offsets.
*
* @param aTextAttrArray [in] text attributes array
* @param aStartHTOffset [in, out] the start offset
* @param aEndHTOffset [in, out] the end offset
*/
nsresult GetRange(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
PRInt32 *aStartHTOffset, PRInt32 *aEndHTOffset);
/*
* Find new end offset for text attributes navigating through the tree. New
* end offset may be smaller if one of text attributes changes its value
* before the given end offset.
*
* @param aTextAttrArray [in] text attributes array
* @param aCurrNode [in] the first node of the tree
* @param aHTOffset [in, out] the end offset
* @return true if the end offset has been changed
*/
PRBool FindEndOffsetInSubtree(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
nsIDOMNode *aCurrNode, PRInt32 *aHTOffset);
/*
* Find the start offset for text attributes navigating through the tree. New
* start offset may be bigger if one of text attributes changes its value
* after the given start offset.
*
* @param aTextAttrArray [in] text attributes array
* @param aCurrNode [in] the node navigating through thee thee is
* started from
* @param aPrevNode [in] the previous node placed before the start node
* @param aHTOffset [in, out] the start offset
* @return true if the start offset has been changed
*/
PRBool FindStartOffsetInSubtree(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
nsIDOMNode *aCurrNode, nsIDOMNode *aPrevNode,
PRInt32 *aHTOffset);
private:
nsRefPtr<nsHyperTextAccessible> mHyperTextAcc;
nsCOMPtr<nsIDOMNode> mHyperTextNode;
PRBool mIncludeDefAttrs;
nsCOMPtr<nsIDOMNode> mOffsetNode;
};
////////////////////////////////////////////////////////////////////////////////
// Private implementation details
/**
* Interface class of text attribute class implementations.
*/
class nsITextAttr
{
public:
/**
* Return the name of text attribute.
*/
virtual nsIAtom* GetName() = 0;
/**
* Retrieve the value of text attribute in out param, return true if differs
* from the default value of text attribute or if include default attribute
* value flag is setted.
*
* @param aValue [in, out] the value of text attribute
* @param aIncludeDefAttrValue [in] include default attribute value flag
* @return true if text attribute value differs from
* default or include default attribute value
* flag is applied
*/
virtual PRBool GetValue(nsAString& aValue, PRBool aIncludeDefAttrValue) = 0;
/**
* Return true if the text attribute value on the given element equals with
* predefined attribute value.
*/
virtual PRBool Equal(nsIDOMElement *aElm) = 0;
};
/**
* Base class to work with text attributes. See derived classes below.
*/
template<class T>
class nsTextAttr : public nsITextAttr
{
public:
nsTextAttr(PRBool aGetRootValue) : mGetRootValue(aGetRootValue) {}
// nsITextAttr
virtual PRBool GetValue(nsAString& aValue, PRBool aIncludeDefAttrValue)
{
if (mGetRootValue) {
Format(mRootNativeValue, aValue);
return mIsRootDefined;
}
PRBool isDefined = mIsDefined;
T* nativeValue = &mNativeValue;
if (!isDefined) {
if (aIncludeDefAttrValue) {
isDefined = mIsRootDefined;
nativeValue = &mRootNativeValue;
}
} else if (!aIncludeDefAttrValue) {
isDefined = mRootNativeValue != mNativeValue;
}
if (!isDefined)
return PR_FALSE;
Format(*nativeValue, aValue);
return PR_TRUE;
}
virtual PRBool Equal(nsIDOMElement *aElm)
{
T nativeValue;
PRBool isDefined = GetValueFor(aElm, &nativeValue);
if (!mIsDefined && !isDefined)
return PR_TRUE;
if (mIsDefined && isDefined)
return nativeValue == mNativeValue;
if (mIsDefined)
return mNativeValue == mRootNativeValue;
return nativeValue == mRootNativeValue;
}
protected:
// Return native value for the given DOM element.
virtual PRBool GetValueFor(nsIDOMElement *aElm, T *aValue) = 0;
// Format native value to text attribute value.
virtual void Format(const T& aValue, nsAString& aFormattedValue) = 0;
// Indicates if root value should be exposed.
PRBool mGetRootValue;
// Native value and flag indicating if the value is defined (initialized in
// derived classes). Note, undefined native value means it is inherited
// from root.
T mNativeValue;
PRBool mIsDefined;
// Native root value and flag indicating if the value is defined (initialized
// in derived classes).
T mRootNativeValue;
PRBool mIsRootDefined;
};
/**
* Class is used for the work with 'language' text attribute in nsTextAttrsMgr
* class.
*/
class nsLangTextAttr : public nsTextAttr<nsAutoString>
{
public:
nsLangTextAttr(nsHyperTextAccessible *aRootAcc, nsIDOMNode *aRootNode,
nsIDOMNode *aNode);
// nsITextAttr
virtual nsIAtom *GetName() { return nsAccessibilityAtoms::language; }
protected:
// nsTextAttr
virtual PRBool GetValueFor(nsIDOMElement *aElm, nsAutoString *aValue);
virtual void Format(const nsAutoString& aValue, nsAString& aFormattedValue);
private:
PRBool GetLang(nsIContent *aContent, nsAString& aLang);
nsCOMPtr<nsIContent> mRootContent;
};
/**
* Class is used for the work with CSS based text attributes in nsTextAttrsMgr
* class.
*/
class nsCSSTextAttr : public nsTextAttr<nsAutoString>
{
public:
nsCSSTextAttr(PRUint32 aIndex, nsIDOMElement *aRootElm, nsIDOMElement *aElm);
// nsITextAttr
virtual nsIAtom *GetName();
protected:
// nsTextAttr
virtual PRBool GetValueFor(nsIDOMElement *aElm, nsAutoString *aValue);
virtual void Format(const nsAutoString& aValue, nsAString& aFormattedValue);
private:
PRInt32 mIndex;
};
/**
* Class is used for the work with 'background-color' text attribute in
* nsTextAttrsMgr class.
*/
class nsBGColorTextAttr : public nsTextAttr<nscolor>
{
public:
nsBGColorTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame);
// nsITextAttr
virtual nsIAtom *GetName() { return nsAccessibilityAtoms::backgroundColor; }
protected:
// nsTextAttr
virtual PRBool GetValueFor(nsIDOMElement *aElm, nscolor *aValue);
virtual void Format(const nscolor& aValue, nsAString& aFormattedValue);
private:
PRBool GetColor(nsIFrame *aFrame, nscolor *aColor);
nsIFrame *mRootFrame;
};
/**
* Class is used for the work with "font-size" text attribute in nsTextAttrsMgr
* class.
*/
class nsFontSizeTextAttr : public nsTextAttr<nscoord>
{
public:
nsFontSizeTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame);
// nsITextAttr
virtual nsIAtom *GetName() { return nsAccessibilityAtoms::fontSize; }
protected:
// nsTextAttr
virtual PRBool GetValueFor(nsIDOMElement *aElm, nscoord *aValue);
virtual void Format(const nscoord& aValue, nsAString& aFormattedValue);
private:
/**
* Return font size for the given frame.
*
* @param aFrame [in] the given frame to query font-size
* @return font size
*/
nscoord GetFontSize(nsIFrame *aFrame);
nsIDeviceContext *mDC;
};
#endif

Просмотреть файл

@ -0,0 +1,508 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Foundation
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsTextEquivUtils.h"
#include "nsAccessible.h"
#include "nsIDOMXULLabeledControlEl.h"
#include "nsArrayUtils.h"
#define NS_OK_NO_NAME_CLAUSE_HANDLED \
NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_GENERAL, 0x24)
////////////////////////////////////////////////////////////////////////////////
// nsTextEquivUtils. Public.
nsresult
nsTextEquivUtils::GetNameFromSubtree(nsIAccessible *aAccessible,
nsAString& aName)
{
aName.Truncate();
if (gInitiatorAcc)
return NS_OK;
gInitiatorAcc = aAccessible;
PRUint32 role = nsAccUtils::Role(aAccessible);
PRUint32 nameRule = gRoleToNameRulesMap[role];
if (nameRule == eFromSubtree) {
nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
nsCOMPtr<nsIDOMNode> DOMNode;
accessNode->GetDOMNode(getter_AddRefs(DOMNode));
nsCOMPtr<nsIContent> content(do_QueryInterface(DOMNode));
if (content) {
nsAutoString name;
AppendFromAccessibleChildren(aAccessible, &name);
name.CompressWhitespace();
aName = name;
}
}
gInitiatorAcc = nsnull;
return NS_OK;
}
nsresult
nsTextEquivUtils::GetTextEquivFromIDRefs(nsIAccessible *aAccessible,
nsIAtom *aIDRefsAttr,
nsAString& aTextEquiv)
{
aTextEquiv.Truncate();
nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
nsCOMPtr<nsIDOMNode> DOMNode;
accessNode->GetDOMNode(getter_AddRefs(DOMNode));
nsCOMPtr<nsIContent> content = nsCoreUtils::GetRoleContent(DOMNode);
if (!content)
return NS_OK;
nsCOMPtr<nsIArray> refElms;
nsCoreUtils::GetElementsByIDRefsAttr(content, aIDRefsAttr,
getter_AddRefs(refElms));
if (!refElms)
return NS_OK;
PRUint32 count = 0;
nsresult rv = refElms->GetLength(&count);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIContent> refContent;
for (PRUint32 idx = 0; idx < count; idx++) {
refContent = do_QueryElementAt(refElms, idx, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (!aTextEquiv.IsEmpty())
aTextEquiv += ' ';
rv = AppendTextEquivFromContent(aAccessible, refContent, &aTextEquiv);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
nsresult
nsTextEquivUtils::AppendTextEquivFromContent(nsIAccessible *aInitiatorAcc,
nsIContent *aContent,
nsAString *aString)
{
// Prevent recursion which can cause infinite loops.
if (gInitiatorAcc)
return NS_OK;
gInitiatorAcc = aInitiatorAcc;
nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(aContent));
nsCOMPtr<nsIPresShell> shell = nsCoreUtils::GetPresShellFor(DOMNode);
if (!shell) {
NS_ASSERTION(PR_TRUE, "There is no presshell!");
gInitiatorAcc = nsnull;
return NS_ERROR_UNEXPECTED;
}
// If the given content is not visible or isn't accessible then go down
// through the DOM subtree otherwise go down through accessible subtree and
// calculate the flat string.
nsIFrame *frame = shell->GetPrimaryFrameFor(aContent);
PRBool isVisible = frame && frame->GetStyleVisibility()->IsVisible();
nsresult rv;
PRBool goThroughDOMSubtree = PR_TRUE;
if (isVisible) {
nsCOMPtr<nsIAccessible> accessible;
rv = nsAccessNode::GetAccService()->
GetAccessibleInShell(DOMNode, shell, getter_AddRefs(accessible));
if (NS_SUCCEEDED(rv) && accessible) {
rv = AppendFromAccessible(accessible, aString);
goThroughDOMSubtree = PR_FALSE;
}
}
if (goThroughDOMSubtree)
rv = AppendFromDOMNode(aContent, aString);
gInitiatorAcc = nsnull;
return rv;
}
nsresult
nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent,
nsAString *aString)
{
if (aContent->IsNodeOfType(nsINode::eTEXT)) {
nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(aContent));
PRBool isHTMLBlock = PR_FALSE;
nsCOMPtr<nsIPresShell> shell = nsCoreUtils::GetPresShellFor(DOMNode);
NS_ENSURE_STATE(shell);
nsIContent *parentContent = aContent->GetParent();
if (parentContent) {
nsIFrame *frame = shell->GetPrimaryFrameFor(parentContent);
if (frame) {
// If this text is inside a block level frame (as opposed to span
// level), we need to add spaces around that block's text, so we don't
// get words jammed together in final name.
const nsStyleDisplay* display = frame->GetStyleDisplay();
if (display->IsBlockOutside() ||
display->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL) {
isHTMLBlock = PR_TRUE;
if (!aString->IsEmpty()) {
aString->Append(PRUnichar(' '));
}
}
}
}
if (aContent->TextLength() > 0) {
nsIFrame *frame = shell->GetPrimaryFrameFor(aContent);
if (frame) {
nsresult rv = frame->GetRenderedText(aString);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// If aContent is an object that is display: none, we have no a frame.
aContent->AppendTextTo(*aString);
}
if (isHTMLBlock && !aString->IsEmpty()) {
aString->Append(PRUnichar(' '));
}
}
return NS_OK;
}
if (aContent->IsNodeOfType(nsINode::eHTML) &&
aContent->NodeInfo()->Equals(nsAccessibilityAtoms::br)) {
aString->AppendLiteral("\r\n");
return NS_OK;
}
return NS_OK_NO_NAME_CLAUSE_HANDLED;
}
////////////////////////////////////////////////////////////////////////////////
// nsTextEquivUtils. Private.
nsCOMPtr<nsIAccessible> nsTextEquivUtils::gInitiatorAcc;
nsresult
nsTextEquivUtils::AppendFromAccessibleChildren(nsIAccessible *aAccessible,
nsAString *aString)
{
nsCOMPtr<nsIAccessible> accChild, accNextChild;
aAccessible->GetFirstChild(getter_AddRefs(accChild));
while (accChild) {
nsresult rv = AppendFromAccessible(accChild, aString);
NS_ENSURE_SUCCESS(rv, rv);
accChild->GetNextSibling(getter_AddRefs(accNextChild));
accChild.swap(accNextChild);
}
return NS_OK;
}
nsresult
nsTextEquivUtils::AppendFromAccessible(nsIAccessible *aAccessible,
nsAString *aString)
{
nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
nsCOMPtr<nsIDOMNode> DOMNode;
accessNode->GetDOMNode(getter_AddRefs(DOMNode));
nsCOMPtr<nsIContent> content(do_QueryInterface(DOMNode));
NS_ASSERTION(content, "There is no content!");
if (content) {
nsresult rv = AppendTextEquivFromTextContent(content, aString);
if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
return rv;
}
nsAutoString text;
nsresult rv = aAccessible->GetName(text);
NS_ENSURE_SUCCESS(rv, rv);
AppendString(aString, text);
PRUint32 role = nsAccUtils::Role(aAccessible);
PRUint32 nameRule = gRoleToNameRulesMap[role];
if (nameRule == eFromValue) {
// Implementation of step f) of text equivalent computation. If the given
// accessible is not root accessible (the accessible the text equivalent is
// computed for in the end) then append accessible value. Otherwise append
// value if and only if the given accessible is in the middle of its parent.
if (aAccessible != gInitiatorAcc) {
rv = aAccessible->GetValue(text);
NS_ENSURE_SUCCESS(rv, rv);
AppendString(aString, text);
} else {
nsCOMPtr<nsIAccessible> nextSibling;
aAccessible->GetNextSibling(getter_AddRefs(nextSibling));
if (nextSibling) {
nsCOMPtr<nsIAccessible> parent;
aAccessible->GetParent(getter_AddRefs(parent));
if (parent) {
nsCOMPtr<nsIAccessible> firstChild;
parent->GetFirstChild(getter_AddRefs(firstChild));
if (firstChild && firstChild != aAccessible) {
rv = aAccessible->GetValue(text);
NS_ENSURE_SUCCESS(rv, rv);
AppendString(aString, text);
}
}
}
}
}
// Implementation of g) step of text equivalent computation guide. Go down
// into subtree if accessible allows "text equivalent from subtree rule" or
// it's not root and not control.
if (text.IsEmpty() && (nameRule & eFromSubtreeIfRec))
return AppendFromAccessibleChildren(aAccessible, aString);
return NS_OK;
}
nsresult
nsTextEquivUtils::AppendFromDOMChildren(nsIContent *aContent,
nsAString *aString)
{
PRUint32 childCount = aContent->GetChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
nsCOMPtr<nsIContent> childContent = aContent->GetChildAt(childIdx);
nsresult rv = AppendFromDOMNode(childContent, aString);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
nsresult
nsTextEquivUtils::AppendFromDOMNode(nsIContent *aContent, nsAString *aString)
{
nsresult rv = AppendTextEquivFromTextContent(aContent, aString);
NS_ENSURE_SUCCESS(rv, rv);
if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
return NS_OK;
if (aContent->IsNodeOfType(nsINode::eXUL)) {
nsAutoString textEquivalent;
nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl =
do_QueryInterface(aContent);
if (labeledEl) {
labeledEl->GetLabel(textEquivalent);
} else {
if (aContent->NodeInfo()->Equals(nsAccessibilityAtoms::label,
kNameSpaceID_XUL))
aContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::value,
textEquivalent);
if (textEquivalent.IsEmpty())
aContent->GetAttr(kNameSpaceID_None,
nsAccessibilityAtoms::tooltiptext, textEquivalent);
}
AppendString(aString, textEquivalent);
}
return AppendFromDOMChildren(aContent, aString);
}
void
nsTextEquivUtils::AppendString(nsAString *aString,
const nsAString& aTextEquivalent)
{
// Insert spaces to insure that words from controls aren't jammed together.
if (aTextEquivalent.IsEmpty())
return;
if (!aString->IsEmpty())
aString->Append(PRUnichar(' '));
aString->Append(aTextEquivalent);
}
////////////////////////////////////////////////////////////////////////////////
// Name rules to role map.
PRUint32 nsTextEquivUtils::gRoleToNameRulesMap[] =
{
eNoRule, // ROLE_NOTHING
eNoRule, // ROLE_TITLEBAR
eNoRule, // ROLE_MENUBAR
eNoRule, // ROLE_SCROLLBAR
eNoRule, // ROLE_GRIP
eNoRule, // ROLE_SOUND
eNoRule, // ROLE_CURSOR
eNoRule, // ROLE_CARET
eNoRule, // ROLE_ALERT
eNoRule, // ROLE_WINDOW
eNoRule, // ROLE_INTERNAL_FRAME
eNoRule, // ROLE_MENUPOPUP
eFromSubtree, // ROLE_MENUITEM
eFromSubtree, // ROLE_TOOLTIP
eNoRule, // ROLE_APPLICATION
eNoRule, // ROLE_DOCUMENT
eNoRule, // ROLE_PANE
eNoRule, // ROLE_CHART
eNoRule, // ROLE_DIALOG
eNoRule, // ROLE_BORDER
eNoRule, // ROLE_GROUPING
eNoRule, // ROLE_SEPARATOR
eNoRule, // ROLE_TOOLBAR
eNoRule, // ROLE_STATUSBAR
eNoRule, // ROLE_TABLE
eFromSubtree, // ROLE_COLUMNHEADER
eFromSubtree, // ROLE_ROWHEADER
eFromSubtree, // ROLE_COLUMN
eFromSubtree, // ROLE_ROW
eFromSubtreeIfRec, // ROLE_CELL
eFromSubtree, // ROLE_LINK
eFromSubtree, // ROLE_HELPBALLOON
eNoRule, // ROLE_CHARACTER
eFromSubtreeIfRec, // ROLE_LIST
eFromSubtree, // ROLE_LISTITEM
eNoRule, // ROLE_OUTLINE
eFromSubtree, // ROLE_OUTLINEITEM
eFromSubtree, // ROLE_PAGETAB
eNoRule, // ROLE_PROPERTYPAGE
eNoRule, // ROLE_INDICATOR
eNoRule, // ROLE_GRAPHIC
eNoRule, // ROLE_STATICTEXT
eNoRule, // ROLE_TEXT_LEAF
eFromSubtree, // ROLE_PUSHBUTTON
eFromSubtree, // ROLE_CHECKBUTTON
eFromSubtree, // ROLE_RADIOBUTTON
eFromValue, // ROLE_COMBOBOX
eNoRule, // ROLE_DROPLIST
eFromValue, // ROLE_PROGRESSBAR
eNoRule, // ROLE_DIAL
eNoRule, // ROLE_HOTKEYFIELD
eNoRule, // ROLE_SLIDER
eNoRule, // ROLE_SPINBUTTON
eNoRule, // ROLE_DIAGRAM
eNoRule, // ROLE_ANIMATION
eNoRule, // ROLE_EQUATION
eFromSubtree, // ROLE_BUTTONDROPDOWN
eFromSubtree, // ROLE_BUTTONMENU
eFromSubtree, // ROLE_BUTTONDROPDOWNGRID
eNoRule, // ROLE_WHITESPACE
eNoRule, // ROLE_PAGETABLIST
eNoRule, // ROLE_CLOCK
eNoRule, // ROLE_SPLITBUTTON
eNoRule, // ROLE_IPADDRESS
eNoRule, // ROLE_ACCEL_LABEL
eNoRule, // ROLE_ARROW
eNoRule, // ROLE_CANVAS
eFromSubtree, // ROLE_CHECK_MENU_ITEM
eNoRule, // ROLE_COLOR_CHOOSER
eNoRule, // ROLE_DATE_EDITOR
eNoRule, // ROLE_DESKTOP_ICON
eNoRule, // ROLE_DESKTOP_FRAME
eNoRule, // ROLE_DIRECTORY_PANE
eNoRule, // ROLE_FILE_CHOOSER
eNoRule, // ROLE_FONT_CHOOSER
eNoRule, // ROLE_CHROME_WINDOW
eNoRule, // ROLE_GLASS_PANE
eFromSubtreeIfRec, // ROLE_HTML_CONTAINER
eNoRule, // ROLE_ICON
eFromSubtree, // ROLE_LABEL
eNoRule, // ROLE_LAYERED_PANE
eNoRule, // ROLE_OPTION_PANE
eNoRule, // ROLE_PASSWORD_TEXT
eNoRule, // ROLE_POPUP_MENU
eFromSubtree, // ROLE_RADIO_MENU_ITEM
eNoRule, // ROLE_ROOT_PANE
eNoRule, // ROLE_SCROLL_PANE
eNoRule, // ROLE_SPLIT_PANE
eFromSubtree, // ROLE_TABLE_COLUMN_HEADER
eFromSubtree, // ROLE_TABLE_ROW_HEADER
eFromSubtree, // ROLE_TEAR_OFF_MENU_ITEM
eNoRule, // ROLE_TERMINAL
eFromSubtreeIfRec, // ROLE_TEXT_CONTAINER
eFromSubtree, // ROLE_TOGGLE_BUTTON
eNoRule, // ROLE_TREE_TABLE
eNoRule, // ROLE_VIEWPORT
eNoRule, // ROLE_HEADER
eNoRule, // ROLE_FOOTER
eFromSubtreeIfRec, // ROLE_PARAGRAPH
eNoRule, // ROLE_RULER
eNoRule, // ROLE_AUTOCOMPLETE
eNoRule, // ROLE_EDITBAR
eFromValue, // ROLE_ENTRY
eNoRule, // ROLE_CAPTION
eNoRule, // ROLE_DOCUMENT_FRAME
eNoRule, // ROLE_HEADING
eNoRule, // ROLE_PAGE
eFromSubtreeIfRec, // ROLE_SECTION
eNoRule, // ROLE_REDUNDANT_OBJECT
eNoRule, // ROLE_FORM
eNoRule, // ROLE_IME
eNoRule, // ROLE_APP_ROOT
eFromSubtree, // ROLE_PARENT_MENUITEM
eNoRule, // ROLE_CALENDAR
eNoRule, // ROLE_COMBOBOX_LIST
eFromSubtree, // ROLE_COMBOBOX_OPTION
eNoRule, // ROLE_IMAGE_MAP
eFromSubtree, // ROLE_OPTION
eFromSubtree, // ROLE_RICH_OPTION
eNoRule, // ROLE_LISTBOX
eNoRule, // ROLE_FLAT_EQUATION
eFromSubtree // ROLE_GRID_CELL
};

Просмотреть файл

@ -0,0 +1,168 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Foundation
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _nsTextEquivUtils_H_
#define _nsTextEquivUtils_H_
#include "nsIAccessible.h"
#include "nsIContent.h"
#include "nsIStringBundle.h"
/**
* Text equivalent computation rules (see nsTextEquivUtils::gRoleToNameRulesMap)
*/
enum ETextEquivRule
{
// No rule.
eNoRule = 0x00,
// Walk into subtree only if the currently navigated accessible is not root
// accessible (i.e. if the accessible is part of text equivalent computation).
eFromSubtreeIfRec = 0x01,
// Text equivalent computation from subtree is allowed.
eFromSubtree = 0x03,
// The accessible allows to append its value to text equivalent.
// XXX: This is temporary solution. Once we move accessible value of links
// and linkable accessibles to MSAA part we can remove this.
eFromValue = 0x04
};
/**
* The class provides utils methods to compute the accessible name and
* description.
*/
class nsTextEquivUtils
{
public:
/**
* Calculates the name from accessible subtree if allowed.
*
* @param aAccessible [in] the given accessible
* @param aName [out] accessible name
*/
static nsresult GetNameFromSubtree(nsIAccessible *aAccessible,
nsAString& aName);
/**
* Calculates text equivalent for the given accessible from its IDRefs
* attribute (like aria-labelledby or aria-describedby).
*
* @param aAccessible [in] the accessible text equivalent is computed for
* @param aIDRefsAttr [in] IDRefs attribute on DOM node of the accessible
* @param aTextEquiv [out] result text equivalent
*/
static nsresult GetTextEquivFromIDRefs(nsIAccessible *aAccessible,
nsIAtom *aIDRefsAttr,
nsAString& aTextEquiv);
/**
* Calculates the text equivalent from the given content and its subtree if
* allowed and appends it to the given string.
*
* @param aInitiatorAcc [in] the accessible text equivalent is computed for
* in the end (root accessible of text equivalent
* calculation recursion)
* @param aContent [in] the given content the text equivalent is
* computed from
* @param aString [in, out] the string
*/
static nsresult AppendTextEquivFromContent(nsIAccessible *aInitiatorAcc,
nsIContent *aContent,
nsAString *aString);
/**
* Calculates the text equivalent from the given text content (may be text
* node or html:br) and appends it to the given string.
*
* @param aContent [in] the text content
* @param aString [in, out] the string
*/
static nsresult AppendTextEquivFromTextContent(nsIContent *aContent,
nsAString *aString);
private:
/**
* Iterates accessible children and calculates text equivalent from each
* child.
*/
static nsresult AppendFromAccessibleChildren(nsIAccessible *aAccessible,
nsAString *aString);
/**
* Calculates text equivalent from the given accessible and its subtree if
* allowed.
*/
static nsresult AppendFromAccessible(nsIAccessible *aAccessible,
nsAString *aString);
/**
* Iterates DOM children and calculates text equivalent from each child node.
*/
static nsresult AppendFromDOMChildren(nsIContent *aContent,
nsAString *aString);
/**
* Calculates text equivalent from the given DOM node and its subtree if
* allowed.
*/
static nsresult AppendFromDOMNode(nsIContent *aContent, nsAString *aString);
/**
* Concatenates strings and appends space between them.
*/
static void AppendString(nsAString *aString, const nsAString& aTextEquivalent);
/**
* Map array from roles to name rules (constants of ETextEquivRule).
*/
static PRUint32 gRoleToNameRulesMap[];
/**
* The accessible for which we are computing a text equivalent. It is useful
* for bailing out during recursive text computation, or for special cases
* like step f. of the ARIA implementation guide.
*/
static nsCOMPtr<nsIAccessible> gInitiatorAcc;
};
#endif

Просмотреть файл

@ -1,298 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsTextUtils.h"
#include "nsAccessNode.h"
////////////////////////////////////////////////////////////////////////////////
// nsLangTextAttr
PRBool
nsLangTextAttr::Equal(nsIDOMElement *aElm)
{
nsCOMPtr<nsIContent> content(do_QueryInterface(aElm));
if (!content)
return PR_FALSE;
nsAutoString lang;
nsCoreUtils::GetLanguageFor(content, mRootContent, lang);
return lang == mLang;
}
////////////////////////////////////////////////////////////////////////////////
// nsCSSTextAttr
/**
* Item of the gCSSTextAttrsMap map.
*/
struct nsCSSTextAttrMapItem
{
const char* mCSSName;
const char* mCSSValue;
const char* mAttrName;
const char* mAttrValue;
};
/**
* The map of CSS properties to text attributes.
*/
const char* const kAnyValue = nsnull;
const char* const kCopyName = nsnull;
const char* const kCopyValue = nsnull;
static nsCSSTextAttrMapItem gCSSTextAttrsMap[] = {
// CSS name CSS value Attribute name Attribute value
{ "color", kAnyValue, kCopyName, kCopyValue },
{ "font-family", kAnyValue, kCopyName, kCopyValue },
{ "font-style", kAnyValue, kCopyName, kCopyValue },
{ "font-weight", kAnyValue, kCopyName, kCopyValue },
{ "text-decoration", "line-through", "text-line-through-style", "solid" },
{ "text-decoration", "underline", "text-underline-style", "solid" },
{ "vertical-align", kAnyValue, "text-position", kCopyValue }
};
nsCSSTextAttr::nsCSSTextAttr(PRBool aIncludeDefAttrValue, nsIDOMElement *aElm,
nsIDOMElement *aRootElm) :
mIndex(-1), mIncludeDefAttrValue(aIncludeDefAttrValue)
{
nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), aElm,
getter_AddRefs(mStyleDecl));
if (!mIncludeDefAttrValue)
nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), aRootElm,
getter_AddRefs(mDefStyleDecl));
}
PRBool
nsCSSTextAttr::Equal(nsIDOMElement *aElm)
{
if (!aElm || !mStyleDecl)
return PR_FALSE;
nsCOMPtr<nsIDOMCSSStyleDeclaration> currStyleDecl;
nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), aElm,
getter_AddRefs(currStyleDecl));
if (!currStyleDecl)
return PR_FALSE;
NS_ConvertASCIItoUTF16 cssName(gCSSTextAttrsMap[mIndex].mCSSName);
nsAutoString currValue;
nsresult rv = currStyleDecl->GetPropertyValue(cssName, currValue);
if (NS_FAILED(rv))
return PR_FALSE;
nsAutoString value;
rv = mStyleDecl->GetPropertyValue(cssName, value);
return NS_SUCCEEDED(rv) && value == currValue;
}
PRBool
nsCSSTextAttr::Iterate()
{
return ++mIndex < static_cast<PRInt32>(NS_ARRAY_LENGTH(gCSSTextAttrsMap));
}
PRBool
nsCSSTextAttr::Get(nsACString& aName, nsAString& aValue)
{
if (!mStyleDecl)
return PR_FALSE;
NS_ConvertASCIItoUTF16 cssName(gCSSTextAttrsMap[mIndex].mCSSName);
nsresult rv = mStyleDecl->GetPropertyValue(cssName, aValue);
if (NS_FAILED(rv))
return PR_FALSE;
// Don't expose text attribute if corresponding CSS value on the element
// equals to CSS value on the root element and we don't want to include
// default values.
if (!mIncludeDefAttrValue) {
if (!mDefStyleDecl)
return PR_FALSE;
nsAutoString defValue;
mDefStyleDecl->GetPropertyValue(cssName, defValue);
if (defValue == aValue)
return PR_FALSE;
}
// Don't expose text attribute if its required specific CSS value isn't
// matched with the CSS value we got.
const char *cssValue = gCSSTextAttrsMap[mIndex].mCSSValue;
if (cssValue != kAnyValue && !aValue.EqualsASCII(cssValue))
return PR_FALSE;
// Get the name of text attribute.
if (gCSSTextAttrsMap[mIndex].mAttrName != kCopyName)
aName = gCSSTextAttrsMap[mIndex].mAttrName;
else
aName = gCSSTextAttrsMap[mIndex].mCSSName;
// Get the value of text attribute.
const char *attrValue = gCSSTextAttrsMap[mIndex].mAttrValue;
if (attrValue != kCopyValue)
AppendASCIItoUTF16(attrValue, aValue);
return PR_TRUE;
}
////////////////////////////////////////////////////////////////////////////////
// nsBackgroundTextAttr
nsBackgroundTextAttr::nsBackgroundTextAttr(nsIFrame *aFrame,
nsIFrame *aRootFrame) :
mFrame(aFrame), mRootFrame(aRootFrame)
{
}
PRBool
nsBackgroundTextAttr::Equal(nsIDOMElement *aElm)
{
nsIFrame *frame = nsCoreUtils::GetFrameFor(aElm);
if (!frame)
return PR_FALSE;
return GetColor(mFrame) == GetColor(frame);
}
PRBool
nsBackgroundTextAttr::Get(nsAString& aValue)
{
// Do not expose "background-color" text attribute if its value is matched
// with the default value.
nscolor color = GetColor(mFrame);
if (mRootFrame && color == GetColor(mRootFrame))
return PR_FALSE;
// Combine the string like rgb(R, G, B) from nscolor.
nsAutoString value;
value.AppendLiteral("rgb(");
value.AppendInt(NS_GET_R(color));
value.AppendLiteral(", ");
value.AppendInt(NS_GET_G(color));
value.AppendLiteral(", ");
value.AppendInt(NS_GET_B(color));
value.Append(')');
aValue = value;
return PR_TRUE;
}
nscolor
nsBackgroundTextAttr::GetColor(nsIFrame *aFrame)
{
const nsStyleBackground *styleBackground = aFrame->GetStyleBackground();
if (!styleBackground->IsTransparent())
return styleBackground->mBackgroundColor;
nsIFrame *parentFrame = aFrame->GetParent();
if (!parentFrame)
return aFrame->PresContext()->DefaultBackgroundColor();
// Each frame of parents chain for the initially passed 'aFrame' has
// transparent background color. So background color isn't changed from
// 'mRootFrame' to initially passed 'aFrame'.
if (parentFrame == mRootFrame)
return GetColor(mRootFrame);
return GetColor(parentFrame);
}
////////////////////////////////////////////////////////////////////////////////
// nsFontSizeTextAttr
nsFontSizeTextAttr::nsFontSizeTextAttr(nsIFrame *aFrame,
nsIFrame *aRootFrame) :
mFrame(aFrame), mRootFrame(aRootFrame)
{
}
PRBool
nsFontSizeTextAttr::Equal(nsIDOMElement *aElm)
{
nsIFrame *frame = nsCoreUtils::GetFrameFor(aElm);
if (!frame)
return PR_FALSE;
return GetFontSize(mFrame) == GetFontSize(frame);
}
PRBool
nsFontSizeTextAttr::Get(nsAString& aValue)
{
// Do not expose "font-size" text attribute if its value is the same
// as the default value.
nscoord fontsize = GetFontSize(mFrame);
if (mRootFrame && fontsize == GetFontSize(mRootFrame))
return PR_FALSE;
// Convert from nscoord to pt.
//
// Note: according to IA2, "The conversion doesn't have to be exact.
// The intent is to give the user a feel for the size of the text."
//
// ATK does not specify a unit and will likely follow IA2 here.
//
// XXX todo: consider sharing this code with layout module? (bug 474621)
nsIDeviceContext *dc = mFrame->PresContext()->DeviceContext();
float inches = static_cast<float>(GetFontSize(mFrame)) /
static_cast<float>(dc->AppUnitsPerInch());
int pts = inches * 72 + .5; // 72 pts per inch
nsAutoString value;
value.AppendInt(pts);
value.Append(NS_LITERAL_STRING("pt"));
aValue = value;
return PR_TRUE;
}
nscoord
nsFontSizeTextAttr::GetFontSize(nsIFrame *aFrame)
{
nsStyleFont* styleFont =
(nsStyleFont*)(aFrame->GetStyleDataExternal(eStyleStruct_Font));
return styleFont->mSize;
}

Просмотреть файл

@ -1,190 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsTextUtils_h_
#define nsTextUtils_h_
#include "nsIDOMElement.h"
#include "nsIDOMCSSStyleDeclaration.h"
#include "nsIContent.h"
#include "nsIFrame.h"
#include "nsCOMPtr.h"
#include "nsString.h"
/**
* Base class to work with text attributes. See derived classes below.
*/
class nsTextAttr
{
public:
/**
* Return true if the text attribute for the given element equals with
* predefined attribute.
*/
virtual PRBool Equal(nsIDOMElement *aElm) = 0;
};
/**
* Class is used for the work with 'lang' text attributes. Used in
* nsHyperTextAccessible.
*/
class nsLangTextAttr : public nsTextAttr
{
public:
nsLangTextAttr(nsAString& aLang, nsIContent *aRootContent) :
mLang(aLang), mRootContent(aRootContent) { }
virtual PRBool Equal(nsIDOMElement *aElm);
private:
nsString mLang;
nsCOMPtr<nsIContent> mRootContent;
};
/**
* Class is used for the work with CSS based text attributes. Used in
* nsHyperTextAccessible.
*/
class nsCSSTextAttr : public nsTextAttr
{
public:
nsCSSTextAttr(PRBool aIncludeDefAttrValue, nsIDOMElement *aElm,
nsIDOMElement *aRootElm);
// nsTextAttr
virtual PRBool Equal(nsIDOMElement *aElm);
// nsCSSTextAttr
/**
* Interates through attributes.
*/
PRBool Iterate();
/**
* Get name and value of attribute.
*/
PRBool Get(nsACString& aName, nsAString& aValue);
private:
PRInt32 mIndex;
PRBool mIncludeDefAttrValue;
nsCOMPtr<nsIDOMCSSStyleDeclaration> mStyleDecl;
nsCOMPtr<nsIDOMCSSStyleDeclaration> mDefStyleDecl;
};
/**
* Class is used for the work with "background-color" text attribute. It is
* used in nsHyperTextAccessible.
*/
class nsBackgroundTextAttr : public nsTextAttr
{
public:
nsBackgroundTextAttr(nsIFrame *aFrame, nsIFrame *aRootFrame);
// nsTextAttr
virtual PRBool Equal(nsIDOMElement *aElm);
// nsBackgroundTextAttr
/**
* Retrieve the "background-color" in out param, return true if differs from
* the default background-color.
*
* @param aValue [out] the background color in pts
* @return true if background color differs from default
*/
virtual PRBool Get(nsAString& aValue);
private:
/**
* Return background color for the given frame.
*
* @note If background color for the given frame is transparent then walk
* trhough the frame parents chain until we'll got either a frame with
* not transparent background color or the given root frame. In the
* last case return background color for the root frame.
*
* @param aFrame [in] the given frame to calculate background-color
* @return background color
*/
nscolor GetColor(nsIFrame *aFrame);
nsIFrame *mFrame;
nsIFrame *mRootFrame;
};
/**
* Class is used for the work with "font-size" text attribute. It is
* used in nsHyperTextAccessible.
*/
class nsFontSizeTextAttr : public nsTextAttr
{
public:
nsFontSizeTextAttr(nsIFrame *aFrame, nsIFrame *aRootFrame);
// nsTextAttr
virtual PRBool Equal(nsIDOMElement *aElm);
// nsFontSizeTextAttr
/**
* Retrieve the "font-size" in out param, return true if differs from
* the default font-size.
*
* @param aValue [out] the font size in pts
* @return true if font size differs from default
*/
virtual PRBool Get(nsAString& aValue);
private:
/**
* Return font size for the given frame.
*
* @param aFrame [in] the given frame to query font-size
* @return font size
*/
nscoord GetFontSize(nsIFrame *aFrame);
nsIFrame *mFrame;
nsIFrame *mRootFrame;
};
#endif

Просмотреть файл

@ -627,7 +627,8 @@ nsHTMLGroupboxAccessible::GetNameInternal(nsAString& aName)
nsIContent *legendContent = GetLegend();
if (legendContent) {
return AppendFlatStringFromSubtree(legendContent, &aName);
return nsTextEquivUtils::
AppendTextEquivFromContent(this, legendContent, &aName);
}
return NS_OK;

Просмотреть файл

@ -526,7 +526,8 @@ nsHTMLSelectOptionAccessible::GetNameInternal(nsAString& aName)
if (text->IsNodeOfType(nsINode::eTEXT)) {
nsAutoString txtValue;
nsresult rv = AppendFlatStringFromContentNode(text, &txtValue);
nsresult rv = nsTextEquivUtils::
AppendTextEquivFromTextContent(text, &txtValue);
NS_ENSURE_SUCCESS(rv, rv);
// Temp var (txtValue) needed until CompressWhitespace built for nsAString

Просмотреть файл

@ -967,7 +967,8 @@ NS_IMETHODIMP nsHTMLTableAccessible::GetDescription(nsAString& aDescription)
captionAccessNode->GetDOMNode(getter_AddRefs(captionNode));
nsCOMPtr<nsIContent> captionContent = do_QueryInterface(captionNode);
if (captionContent) {
AppendFlatStringFromSubtree(captionContent, &aDescription);
nsTextEquivUtils::
AppendTextEquivFromContent(this, captionContent, &aDescription);
}
}
#ifdef SHOW_LAYOUT_HEURISTIC

Просмотреть файл

@ -169,22 +169,9 @@ nsTextAccessible(aDomNode, aShell)
}
nsresult
nsHTMLLabelAccessible::GetNameInternal(nsAString& aReturn)
{
nsresult rv = NS_ERROR_FAILURE;
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
nsAutoString name;
if (content)
rv = AppendFlatStringFromSubtree(content, &name);
if (NS_SUCCEEDED(rv)) {
// Temp var needed until CompressWhitespace built for nsAString
name.CompressWhitespace();
aReturn = name;
}
return rv;
nsHTMLLabelAccessible::GetNameInternal(nsAString& aName)
{
return nsTextEquivUtils::GetNameFromSubtree(this, aName);
}
NS_IMETHODIMP nsHTMLLabelAccessible::GetRole(PRUint32 *aRole)

Просмотреть файл

@ -41,7 +41,7 @@
#include "nsAccessibilityAtoms.h"
#include "nsAccessibilityService.h"
#include "nsAccessibleTreeWalker.h"
#include "nsTextUtils.h"
#include "nsTextAttrs.h"
#include "nsIClipboard.h"
#include "nsContentCID.h"
@ -1174,17 +1174,8 @@ nsHyperTextAccessible::GetTextAttributes(PRBool aIncludeDefAttrs,
if (!node)
return NS_OK;
// Set 'lang' text attribute.
rv = GetLangTextAttributes(aIncludeDefAttrs, node,
aStartOffset, aEndOffset,
aAttributes ? *aAttributes : nsnull);
NS_ENSURE_SUCCESS(rv, rv);
// Set CSS based text attributes.
rv = GetCSSTextAttributes(aIncludeDefAttrs, node,
aStartOffset, aEndOffset,
aAttributes ? *aAttributes : nsnull);
return rv;
nsTextAttrsMgr textAttrsMgr(this, mDOMNode, aIncludeDefAttrs, node);
return textAttrsMgr.GetAttributes(*aAttributes, aStartOffset, aEndOffset);
}
// nsIPersistentProperties
@ -1204,35 +1195,8 @@ nsHyperTextAccessible::GetDefaultTextAttributes(nsIPersistentProperties **aAttri
if (!mDOMNode)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMElement> element = nsCoreUtils::GetDOMElementFor(mDOMNode);
nsCSSTextAttr textAttr(PR_TRUE, element, nsnull);
while (textAttr.Iterate()) {
nsCAutoString name;
nsAutoString value, oldValue;
if (textAttr.Get(name, value))
attributes->SetStringProperty(name, value, oldValue);
}
nsIFrame *sourceFrame = nsCoreUtils::GetFrameFor(element);
NS_ENSURE_STATE(sourceFrame);
// set font size
nsAutoString value;
nsFontSizeTextAttr fontSizeTextAttr(sourceFrame, nsnull);
fontSizeTextAttr.Get(value);
nsAccUtils::SetAccAttr(attributes,
nsAccessibilityAtoms::fontSize, value);
value.Truncate();
// set font background color
nsBackgroundTextAttr backgroundTextAttr(sourceFrame, nsnull);
backgroundTextAttr.Get(value);
nsAccUtils::SetAccAttr(attributes,
nsAccessibilityAtoms::backgroundColor, value);
return NS_OK;
nsTextAttrsMgr textAttrsMgr(this, mDOMNode, PR_TRUE, nsnull);
return textAttrsMgr.GetAttributes(*aAttributes);
}
nsresult
@ -2320,275 +2284,3 @@ nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
return NS_OK;
}
// nsHyperTextAccessible
nsresult
nsHyperTextAccessible::GetLangTextAttributes(PRBool aIncludeDefAttrs,
nsIDOMNode *aSourceNode,
PRInt32 *aStartHTOffset,
PRInt32 *aEndHTOffset,
nsIPersistentProperties *aAttributes)
{
nsCOMPtr<nsIDOMElement> sourceElm(nsCoreUtils::GetDOMElementFor(aSourceNode));
nsCOMPtr<nsIContent> content(do_QueryInterface(sourceElm));
nsCOMPtr<nsIContent> rootContent(do_QueryInterface(mDOMNode));
nsAutoString lang;
nsCoreUtils::GetLanguageFor(content, rootContent, lang);
nsAutoString rootLang;
nsresult rv = GetLanguage(rootLang);
NS_ENSURE_SUCCESS(rv, rv);
if (aAttributes) {
// Expose 'language' text attribute if the DOM 'lang' attribute is
// presented and it's different from the 'lang' attribute on the root
// element or we should include default values of text attribute.
const nsAString& resultLang = lang.IsEmpty() ? rootLang : lang;
if (!resultLang.IsEmpty() && (aIncludeDefAttrs || lang != rootLang))
nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::language,
resultLang);
}
nsLangTextAttr textAttr(lang, rootContent);
return GetRangeForTextAttr(aSourceNode, &textAttr,
aStartHTOffset, aEndHTOffset);
}
// nsHyperTextAccessible
nsresult
nsHyperTextAccessible::GetCSSTextAttributes(PRBool aIncludeDefAttrs,
nsIDOMNode *aSourceNode,
PRInt32 *aStartHTOffset,
PRInt32 *aEndHTOffset,
nsIPersistentProperties *aAttributes)
{
nsCOMPtr<nsIDOMElement> sourceElm(nsCoreUtils::GetDOMElementFor(aSourceNode));
nsCOMPtr<nsIDOMElement> rootElm(nsCoreUtils::GetDOMElementFor(mDOMNode));
nsCSSTextAttr textAttr(aIncludeDefAttrs, sourceElm, rootElm);
while (textAttr.Iterate()) {
nsCAutoString name;
nsAutoString value, oldValue;
if (aAttributes && textAttr.Get(name, value))
aAttributes->SetStringProperty(name, value, oldValue);
nsresult rv = GetRangeForTextAttr(aSourceNode, &textAttr,
aStartHTOffset, aEndHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
}
nsIFrame *sourceFrame = nsCoreUtils::GetFrameFor(sourceElm);
if (sourceFrame) {
nsIFrame *rootFrame = nsnull;
if (!aIncludeDefAttrs)
rootFrame = nsCoreUtils::GetFrameFor(rootElm);
nsFontSizeTextAttr fontSizeTextAttr(sourceFrame, rootFrame);
nsAutoString value;
if (fontSizeTextAttr.Get(value)) {
nsAccUtils::SetAccAttr(aAttributes,
nsAccessibilityAtoms::fontSize, value);
}
nsBackgroundTextAttr backgroundTextAttr(sourceFrame, rootFrame);
value.Truncate();
if (backgroundTextAttr.Get(value)) {
nsAccUtils::SetAccAttr(aAttributes,
nsAccessibilityAtoms::backgroundColor, value);
}
nsresult rv = GetRangeForTextAttr(aSourceNode, &backgroundTextAttr,
aStartHTOffset, aEndHTOffset);
return rv;
}
return NS_OK;
}
// nsHyperTextAccessible
nsresult
nsHyperTextAccessible::GetRangeForTextAttr(nsIDOMNode *aNode,
nsTextAttr *aComparer,
PRInt32 *aStartHTOffset,
PRInt32 *aEndHTOffset)
{
nsCOMPtr<nsIDOMElement> rootElm(nsCoreUtils::GetDOMElementFor(mDOMNode));
NS_ENSURE_STATE(rootElm);
nsCOMPtr<nsIDOMNode> tmpNode(aNode);
nsCOMPtr<nsIDOMNode> currNode(aNode);
// Navigate backwards and forwards from current node to the root node to
// calculate range bounds for the text attribute. Navigation sequence is the
// following:
// 1. Navigate through the siblings.
// 2. If the traversed sibling has children then navigate from its leaf child
// to it through whole tree of the traversed sibling.
// 3. Get the parent and cycle algorithm until the root node.
// Navigate backwards (find the start offset).
while (currNode && currNode != rootElm) {
nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(currNode));
NS_ENSURE_STATE(currElm);
if (currNode != aNode && !aComparer->Equal(currElm)) {
PRInt32 startHTOffset = 0;
nsCOMPtr<nsIAccessible> startAcc;
nsresult rv = DOMPointToHypertextOffset(tmpNode, -1, &startHTOffset,
getter_AddRefs(startAcc));
NS_ENSURE_SUCCESS(rv, rv);
if (!startAcc)
startHTOffset = 0;
if (startHTOffset > *aStartHTOffset)
*aStartHTOffset = startHTOffset;
break;
}
currNode->GetPreviousSibling(getter_AddRefs(tmpNode));
if (tmpNode) {
// Navigate through the subtree of traversed children to calculate
// left bound of the range.
FindStartOffsetInSubtree(tmpNode, currNode, aComparer, aStartHTOffset);
}
currNode->GetParentNode(getter_AddRefs(tmpNode));
currNode.swap(tmpNode);
}
// Navigate forwards (find the end offset).
PRBool moveIntoSubtree = PR_TRUE;
currNode = aNode;
while (currNode && currNode != rootElm) {
nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(currNode));
NS_ENSURE_STATE(currElm);
// Stop new end offset searching if the given text attribute changes its
// value.
if (!aComparer->Equal(currElm)) {
PRInt32 endHTOffset = 0;
nsresult rv = DOMPointToHypertextOffset(currNode, -1, &endHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (endHTOffset < *aEndHTOffset)
*aEndHTOffset = endHTOffset;
break;
}
if (moveIntoSubtree) {
// Navigate through subtree of traversed node. We use 'moveIntoSubtree'
// flag to avoid traversing the same subtree twice.
currNode->GetFirstChild(getter_AddRefs(tmpNode));
if (tmpNode)
FindEndOffsetInSubtree(tmpNode, aComparer, aEndHTOffset);
}
currNode->GetNextSibling(getter_AddRefs(tmpNode));
moveIntoSubtree = PR_TRUE;
if (!tmpNode) {
currNode->GetParentNode(getter_AddRefs(tmpNode));
moveIntoSubtree = PR_FALSE;
}
currNode.swap(tmpNode);
}
return NS_OK;
}
PRBool
nsHyperTextAccessible::FindEndOffsetInSubtree(nsIDOMNode *aCurrNode,
nsTextAttr *aComparer,
PRInt32 *aHTOffset)
{
if (!aCurrNode)
return PR_FALSE;
nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(aCurrNode));
NS_ENSURE_STATE(currElm);
// If the given text attribute (pointed by nsTextAttr object) changes its
// value on the traversed element then fit the end of range.
if (!aComparer->Equal(currElm)) {
PRInt32 endHTOffset = 0;
nsresult rv = DOMPointToHypertextOffset(aCurrNode, -1, &endHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (endHTOffset < *aHTOffset)
*aHTOffset = endHTOffset;
return PR_TRUE;
}
// Deeply traverse into the tree to fit the end of range.
nsCOMPtr<nsIDOMNode> nextNode;
aCurrNode->GetFirstChild(getter_AddRefs(nextNode));
if (nextNode) {
PRBool res = FindEndOffsetInSubtree(nextNode, aComparer, aHTOffset);
if (res)
return res;
}
aCurrNode->GetNextSibling(getter_AddRefs(nextNode));
if (nextNode) {
if (FindEndOffsetInSubtree(nextNode, aComparer, aHTOffset))
return PR_TRUE;
}
return PR_FALSE;
}
PRBool
nsHyperTextAccessible::FindStartOffsetInSubtree(nsIDOMNode *aCurrNode,
nsIDOMNode *aPrevNode,
nsTextAttr *aComparer,
PRInt32 *aHTOffset)
{
if (!aCurrNode)
return PR_FALSE;
// Find the closest element back to the traversed element.
nsCOMPtr<nsIDOMNode> nextNode;
aCurrNode->GetLastChild(getter_AddRefs(nextNode));
if (nextNode) {
if (FindStartOffsetInSubtree(nextNode, aPrevNode, aComparer, aHTOffset))
return PR_TRUE;
}
nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(aCurrNode));
NS_ENSURE_STATE(currElm);
// If the given text attribute (pointed by nsTextAttr object) changes its
// value on the traversed element then fit the start of range.
if (!aComparer->Equal(currElm)) {
PRInt32 startHTOffset = 0;
nsCOMPtr<nsIAccessible> startAcc;
nsresult rv = DOMPointToHypertextOffset(aPrevNode, -1, &startHTOffset,
getter_AddRefs(startAcc));
NS_ENSURE_SUCCESS(rv, rv);
if (!startAcc)
startHTOffset = 0;
if (startHTOffset > *aHTOffset)
*aHTOffset = startHTOffset;
return PR_TRUE;
}
// Moving backwards to find the start of range.
aCurrNode->GetPreviousSibling(getter_AddRefs(nextNode));
if (nextNode) {
if (FindStartOffsetInSubtree(nextNode, aCurrNode, aComparer, aHTOffset))
return PR_TRUE;
}
return PR_FALSE;
}

Просмотреть файл

@ -45,7 +45,7 @@
#include "nsIAccessibleHyperText.h"
#include "nsIAccessibleEditableText.h"
#include "nsAccessibleEventData.h"
#include "nsTextUtils.h"
#include "nsTextAttrs.h"
#include "nsFrameSelection.h"
#include "nsISelectionController.h"
@ -293,88 +293,6 @@ protected:
PRInt32 *aStartOffset,
PRInt32 *aEndOffset,
nsIPersistentProperties *aAttributes);
/**
* Set 'lang' text attribute and return range offsets where attibute is
* stretched. The method is used by GetTextAttributes() method.
*
* @param aIncludeDefAttrs [in] points whether text attributes having default
* values of attributes should be included
* @param aSourceNode [in] the node we start to traverse from
* @param aStartOffset [in, out] the start offset
* @param aEndOffset [in, out] the end offset
* @param aAttributes [out, optional] result attributes
*/
nsresult GetLangTextAttributes(PRBool aIncludeDefAttrs,
nsIDOMNode *aSourceNode,
PRInt32 *aStartOffset,
PRInt32 *aEndOffset,
nsIPersistentProperties *aAttributes);
/**
* Set CSS based text attribute and return range offsets where attibutes are
* stretched. The method is used by GetTextAttributes() method.
*
* @param aIncludeDefAttrs [in] points whether text attributes having default
* values of attributes should be included
* @param aSourceNode [in] the node we start to traverse from
* @param aStartOffset [in, out] the start offset
* @param aEndOffset [in, out] the end offset
* @param aAttributes [out, optional] result attributes
*/
nsresult GetCSSTextAttributes(PRBool aIncludeDefAttrs,
nsIDOMNode *aSourceNode,
PRInt32 *aStartOffset,
PRInt32 *aEndOffset,
nsIPersistentProperties *aAttributes);
/**
* Calculates range (start and end offsets) of text where the text attribute
* (pointed by nsTextAttr object) is stretched. New offsets may be smaller if
* the given text attribute changes its value before or after the given
* offsets.
*
* @param aNode [in] the node we start to traverse from
* @param aComparer [in] object used to describe the text attribute
* @param aStartHTOffset [in, out] the start offset
* @param aEndHTOffset [in, out] the end offset
*/
nsresult GetRangeForTextAttr(nsIDOMNode *aNode,
nsTextAttr *aComparer,
PRInt32 *aStartHTOffset,
PRInt32 *aEndHTOffset);
/**
* Find new end offset for text attributes navigating through the tree. New
* end offset may be smaller if the given text attribute (pointed by
* nsTextAttr object) changes its value before the given end offset.
*
* @param aCurrNode [in] the first node of the tree
* @param aComparer [in] object used to describe the text attribute
* @param aHTOffset [in, out] the end offset
* @return true if the end offset has been changed
*/
PRBool FindEndOffsetInSubtree(nsIDOMNode *aCurrNode,
nsTextAttr *aComparer,
PRInt32 *aHTOffset);
/**
* Find the start offset for text attributes navigating through the tree. New
* start offset may be bigger if the given text attribute (pointed by
* nsTextAttr object) changes its value after the given start offset.
*
* @param aCurrNode [in] the node navigating through thee thee is started
* from
* @param aPrevNode [in] the previous node placed before the start node
* @param aComparer [in] object used to describe the text attribute
* @param aHTOffset [in, out] the start offset
* @return true if the start offset has been changed
*/
PRBool FindStartOffsetInSubtree(nsIDOMNode *aCurrNode,
nsIDOMNode *aPrevNode,
nsTextAttr *aComparer,
PRInt32 *aHTOffset);
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsHyperTextAccessible,

Просмотреть файл

@ -234,7 +234,9 @@ NS_IMETHODIMP
nsXFormsAccessible::GetDescription(nsAString& aDescription)
{
nsAutoString description;
nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_describedby, description);
nsresult rv = nsTextEquivUtils::
GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_describedby,
description);
if (NS_SUCCEEDED(rv) && !description.IsEmpty()) {
aDescription = description;

Просмотреть файл

@ -66,7 +66,9 @@ NS_IMETHODIMP
nsXFormsLabelAccessible::GetDescription(nsAString& aDescription)
{
nsAutoString description;
nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_describedby, description);
nsresult rv = nsTextEquivUtils::
GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_describedby,
description);
aDescription = description;
return rv;
}

Просмотреть файл

@ -61,8 +61,6 @@ public:
protected:
nsresult ChangeSelection(PRInt32 aIndex, PRUint8 aMethod, PRBool *aSelState);
nsresult AppendFlatStringFromSubtree(nsIContent *aContent, nsAString *aFlatString)
{ return NS_OK; } // Overrides base impl in nsAccessible
// nsIDOMXULMultiSelectControlElement inherits from this, so we'll always have
// one of these if the widget is valid and not defunct

Просмотреть файл

@ -165,12 +165,15 @@ nsXULLinkAccessible::GetValue(nsAString& aValue)
nsresult
nsXULLinkAccessible::GetNameInternal(nsAString& aName)
{
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::value, aName);
if (!aName.IsEmpty())
return NS_OK;
return AppendFlatStringFromSubtree(content, &aName);
return nsTextEquivUtils::GetNameFromSubtree(this, aName);
}
NS_IMETHODIMP

Просмотреть файл

@ -78,12 +78,10 @@ _TEST_FILES =\
$(warning test_nsIAccessible_actions.xul temporarily disabled) \
test_nsIAccessible_applicationAccessible.html \
test_nsIAccessible_comboboxes.xul \
test_nsIAccessible_editablebody.html \
test_nsIAccessible_editabledoc.html \
test_nsIAccessible_name.html \
test_nsIAccessible_name_button.html \
test_nsIAccessible_name_link.html \
$(warning test_nsIAccessible_name.xul temporarily disabled) \
test_nsIAccessible_name.xul \
test_nsIAccessible_selects.html \
test_nsIAccessible_focus.html \
test_nsIAccessibleDocument.html \
@ -99,17 +97,26 @@ _TEST_FILES =\
$(warning test_nsIAccessibleTable_listboxes.xul temporarily disabled) \
test_nsIAccessNode_utils.html \
test_nsOuterDocAccessible.html \
test_objectattrs.html \
test_relations.html \
test_relations.xul \
test_role_nsHyperTextAcc.html \
test_role_table_cells.html \
test_states.html \
test_states_editablebody.html \
test_states_doc.html \
test_states_docarticle.html \
test_states_frames.html \
test_textattrs.html \
test_textboxes.html \
test_textboxes.xul \
testTextboxes.js \
test_bug429285.html \
test_bug434464.html \
z_states_frame.html \
z_states_framearticle.html \
z_states_framecheckbox.html \
z_states_frametextbox.html \
$(NULL)
libs:: $(_TEST_FILES)

Просмотреть файл

@ -1,162 +1,198 @@
////////////////////////////////////////////////////////////////////////////////
// Object attributes.
/**
* Test object attributes.
*
* @param aAccOrElmOrID [in] the ID, DOM node or accessible
* @param aAttrs [in] the map of expected object attributes
* (name/value pairs)
* @param aSkipUnexpectedAttrs [in] points this function doesn't fail if
* unexpected attribute is encountered
*/
function testAttrs(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs)
{
var accessible = getAccessible(aAccOrElmOrID);
if (!accessible)
return;
var attrs = null;
try {
attrs = accessible.attributes;
} catch (e) { }
if (!attrs) {
ok(false, "Can't get object attributes for " + aAccOrElmOrID);
return;
}
var errorMsg = " for " + aAccOrElmOrID;
compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs);
}
/**
* Test group object attributes (posinset, setsize and level)
*
* @param aAccOrElmOrID [in] the ID, DOM node or accessible
* @param aPosInSet [in] the value of 'posinset' attribute
* @param aSetSize [in] the value of 'setsize' attribute
* @param aLevel [in, optional] the value of 'level' attribute
*/
function testGroupAttrs(aAccOrElmOrID, aPosInSet, aSetSize, aLevel)
{
var attrs = {
"posinset": String(aPosInSet),
"setsize": String(aSetSize)
};
if (aLevel)
attrs["level"] = String(aLevel);
testAttrs(aAccOrElmOrID, attrs, true);
}
////////////////////////////////////////////////////////////////////////////////
// Text attributes.
/**
* Test text attributes.
*
* @param aID [in] the ID of DOM element having text
* accessible
* @param aOffset [in] the offset inside text accessible to fetch
* text attributes
* @param aAttrs [in] the map of expected text attributes
* (name/value pairs)
* @param aStartOffset [in] expected start offset where text attributes
* are applied
* @param aEndOffset [in] expected end offset where text attribute
* are applied
* @param aSkipUnexpectedAttrs [in] points the function doesn't fail if
* unexpected attribute is encountered
*/
function testTextAttrs(aID, aOffset, aAttrs, aStartOffset, aEndOffset,
aSkipUnexpectedAttrs)
{
var accessible = getAccessible(aID, [nsIAccessibleText]);
if (!accessible)
return;
var startOffset = { value: -1 };
var endOffset = { value: -1 };
var attrs = null;
try {
attrs = accessible.getTextAttributes(false, aOffset,
startOffset, endOffset);
} catch (e) {
}
if (!attrs) {
ok(false, "Can't get text attributes for " + aID);
return;
}
var errorMsg = " for " + aID + " at offset " + aOffset;
is(startOffset.value, aStartOffset, "Wrong start offset" + errorMsg);
is(endOffset.value, aEndOffset, "Wrong end offset" + errorMsg);
compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs);
}
/**
* Test default text attributes.
*
* @param aID [in] the ID of DOM element having text
* accessible
* @param aDefAttrs [in] the map of expected text attributes
* (name/value pairs)
* @param aSkipUnexpectedAttrs [in] points the function doesn't fail if
* unexpected attribute is encountered
*/
function testDefaultTextAttrs(aID, aDefAttrs, aSkipUnexpectedAttrs)
{
var accessible = getAccessible(aID, [nsIAccessibleText]);
if (!accessible)
return;
var defAttrs = null;
try{
defAttrs = accessible.defaultTextAttributes;
} catch (e) {
}
if (!defAttrs) {
ok(false, "Can't get default text attributes for " + aID);
return;
}
var errorMsg = ". Getting default text attributes for " + aID;
compareAttrs(errorMsg, defAttrs, aDefAttrs, aSkipUnexpectedAttrs);
}
////////////////////////////////////////////////////////////////////////////////
// Private.
function compareAttrs(aErrorMsg, aAttrs, aExpectedAttrs, aSkipUnexpectedAttrs)
{
var enumerate = aAttrs.enumerate();
while (enumerate.hasMoreElements()) {
var prop = enumerate.getNext().QueryInterface(nsIPropertyElement);
if (!(prop.key in aExpectedAttrs)) {
if (!aSkipUnexpectedAttrs)
ok(false, "Unexpected attribute '" + prop.key + "' having '" +
prop.value + "'" + aErrorMsg);
} else {
is(prop.value, aExpectedAttrs[prop.key],
"Attribute '" + prop.key + " 'has wrong value" + aErrorMsg);
}
}
for (var name in aExpectedAttrs) {
var value = "";
try {
value = aAttrs.getStringProperty(name);
} catch(e) { }
if (!value)
ok(false,
"There is no expected attribute '" + name + "' " + aErrorMsg);
}
}
////////////////////////////////////////////////////////////////////////////////
// Object attributes.
/**
* Test object attributes.
*
* @param aAccOrElmOrID [in] the ID, DOM node or accessible
* @param aAttrs [in] the map of expected object attributes
* (name/value pairs)
* @param aSkipUnexpectedAttrs [in] points this function doesn't fail if
* unexpected attribute is encountered
*/
function testAttrs(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs)
{
var accessible = getAccessible(aAccOrElmOrID);
if (!accessible)
return;
var attrs = null;
try {
attrs = accessible.attributes;
} catch (e) { }
if (!attrs) {
ok(false, "Can't get object attributes for " + aAccOrElmOrID);
return;
}
var errorMsg = " for " + aAccOrElmOrID;
compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs);
}
/**
* Test group object attributes (posinset, setsize and level)
*
* @param aAccOrElmOrID [in] the ID, DOM node or accessible
* @param aPosInSet [in] the value of 'posinset' attribute
* @param aSetSize [in] the value of 'setsize' attribute
* @param aLevel [in, optional] the value of 'level' attribute
*/
function testGroupAttrs(aAccOrElmOrID, aPosInSet, aSetSize, aLevel)
{
var attrs = {
"posinset": String(aPosInSet),
"setsize": String(aSetSize)
};
if (aLevel)
attrs["level"] = String(aLevel);
testAttrs(aAccOrElmOrID, attrs, true);
}
////////////////////////////////////////////////////////////////////////////////
// Text attributes.
/**
* Test text attributes.
*
* @param aID [in] the ID of DOM element having text
* accessible
* @param aOffset [in] the offset inside text accessible to fetch
* text attributes
* @param aAttrs [in] the map of expected text attributes
* (name/value pairs) exposed at the offset
* @param aDefAttrs [in] the map of expected text attributes
* (name/value pairs) exposed on hyper text
* accessible
* @param aStartOffset [in] expected start offset where text attributes
* are applied
* @param aEndOffset [in] expected end offset where text attribute
* are applied
* @param aSkipUnexpectedAttrs [in] points the function doesn't fail if
* unexpected attribute is encountered
*/
function testTextAttrs(aID, aOffset, aAttrs, aDefAttrs,
aStartOffset, aEndOffset, aSkipUnexpectedAttrs)
{
var accessible = getAccessible(aID, [nsIAccessibleText]);
if (!accessible)
return;
var startOffset = { value: -1 };
var endOffset = { value: -1 };
// do not include attributes exposed on hyper text accessbile
var attrs = getTextAttributes(aID, accessible, false, aOffset,
startOffset, endOffset);
if (!attrs)
return;
var errorMsg = " for " + aID + " at offset " + aOffset;
is(startOffset.value, aStartOffset, "Wrong start offset" + errorMsg);
is(endOffset.value, aEndOffset, "Wrong end offset" + errorMsg);
compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs);
// include attributes exposed on hyper text accessbile
var expectedAttrs = {};
for (var name in aAttrs)
expectedAttrs[name] = aAttrs[name];
for (var name in aDefAttrs) {
if (!(name in expectedAttrs))
expectedAttrs[name] = aDefAttrs[name];
}
attrs = getTextAttributes(aID, accessible, true, aOffset,
startOffset, endOffset);
if (!attrs)
return;
compareAttrs(errorMsg, attrs, expectedAttrs, aSkipUnexpectedAttrs);
}
/**
* Test default text attributes.
*
* @param aID [in] the ID of DOM element having text
* accessible
* @param aDefAttrs [in] the map of expected text attributes
* (name/value pairs)
* @param aSkipUnexpectedAttrs [in] points the function doesn't fail if
* unexpected attribute is encountered
*/
function testDefaultTextAttrs(aID, aDefAttrs, aSkipUnexpectedAttrs)
{
var accessible = getAccessible(aID, [nsIAccessibleText]);
if (!accessible)
return;
var defAttrs = null;
try{
defAttrs = accessible.defaultTextAttributes;
} catch (e) {
}
if (!defAttrs) {
ok(false, "Can't get default text attributes for " + aID);
return;
}
var errorMsg = ". Getting default text attributes for " + aID;
compareAttrs(errorMsg, defAttrs, aDefAttrs, aSkipUnexpectedAttrs);
}
////////////////////////////////////////////////////////////////////////////////
// Private.
function getTextAttributes(aID, aAccessible, aIncludeDefAttrs, aOffset,
aStartOffset, aEndOffset)
{
// This function expects the passed in accessible to already be queried for
// nsIAccessibleText.
var attrs = null;
try {
attrs = aAccessible.getTextAttributes(aIncludeDefAttrs, aOffset,
aStartOffset, aEndOffset);
} catch (e) {
}
if (attrs)
return attrs;
ok(false, "Can't get text attributes for " + aID);
return null;
}
function compareAttrs(aErrorMsg, aAttrs, aExpectedAttrs, aSkipUnexpectedAttrs)
{
var enumerate = aAttrs.enumerate();
while (enumerate.hasMoreElements()) {
var prop = enumerate.getNext().QueryInterface(nsIPropertyElement);
if (!(prop.key in aExpectedAttrs)) {
if (!aSkipUnexpectedAttrs)
ok(false, "Unexpected attribute '" + prop.key + "' having '" +
prop.value + "'" + aErrorMsg);
} else {
is(prop.value, aExpectedAttrs[prop.key],
"Attribute '" + prop.key + " 'has wrong value" + aErrorMsg);
}
}
for (var name in aExpectedAttrs) {
var value = "";
try {
value = aAttrs.getStringProperty(name);
} catch(e) { }
if (!value)
ok(false,
"There is no expected attribute '" + name + "' " + aErrorMsg);
}
}

Просмотреть файл

@ -178,6 +178,9 @@ function getNode(aNodeOrID)
*/
function getAccessible(aAccOrElmOrID, aInterfaces, aElmObj, aDoNotFailIfNoAcc)
{
if (!aAccOrElmOrID)
return;
var elm = null;
if (aAccOrElmOrID instanceof nsIAccessible) {
@ -246,6 +249,27 @@ function isAccessible(aAccOrElmOrID)
return getAccessible(aAccOrElmOrID, null, null, true) ? true : false;
}
/**
* Run through accessible tree of the given identifier so that we ensure
* accessible tree is created.
*/
function ensureAccessibleTree(aAccOrElmOrID)
{
var acc = getAccessible(aAccOrElmOrID);
if (!acc)
return;
var child = acc.firstChild;
while (child) {
ensureAccessibleTree(child);
try {
child = child.nextSibling;
} catch (e) {
child = null;
}
}
}
/**
* Convert role to human readable string.
*/

Просмотреть файл

@ -1,3 +1,9 @@
////////////////////////////////////////////////////////////////////////////////
// Constants
const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
const EVENT_DOM_DESTROY = nsIAccessibleEvent.EVENT_DOM_DESTROY;
////////////////////////////////////////////////////////////////////////////////
// General
@ -6,6 +12,38 @@
*/
var gA11yEventDumpID = "";
/**
* Executes the function when requested event is handled.
*
* @param aEventType [in] event type
* @param aTarget [in] event target
* @param aFunc [in] function to call when event is handled
* @param aContext [in, optional] object in which context the function is
* called
* @param aArg1 [in, optional] argument passed into the function
* @param aArg2 [in, optional] argument passed into the function
*/
function waitForEvent(aEventType, aTarget, aFunc, aContext, aArg1, aArg2)
{
var handler = {
handleEvent: function handleEvent(aEvent) {
if (!aTarget || aTarget == aEvent.DOMNode) {
unregisterA11yEventListener(aEventType, this);
window.setTimeout(
function ()
{
aFunc.call(aContext, aArg1, aArg2);
},
0
);
}
}
};
registerA11yEventListener(aEventType, handler);
}
/**
* Register accessibility event listener.
*
@ -386,6 +424,9 @@ function removeA11yEventListener(aEventType, aEventHandler)
function dumpInfoToDOM(aInfo)
{
if (!gA11yEventDumpID)
return;
var dumpElm = document.getElementById(gA11yEventDumpID);
var div = document.createElement("div");
div.textContent = aInfo;

Просмотреть файл

@ -119,6 +119,7 @@
<rule fromsubtree="true"/>
<rule attr="title" type="string"/>
</ruleset>
</ruledfn>
<rulesample>
@ -193,12 +194,13 @@
role="gridcell"
aria-label="test1"
aria-labelledby="l1 l2"
a11yname=" This is a paragraph This is a link This is a list"
a11yname="This is a paragraph This is a link • Listitem1 • Listitem2"
title="This is a paragraph This is a link This is a list">
<html:p>This is a paragraph</html:p>
<html:a href="#">This is a link</html:a>
<html:ul>
<html:li>This is a list</html:li>
<html:li>Listitem1</html:li>
<html:li>Listitem2</html:li>
</html:ul>
</html:td>
</html:tr>

Просмотреть файл

@ -1,207 +1,284 @@
function testName(aAccOrElmOrID, aName, aMsg)
{
var msg = aMsg ? aMsg : "";
var acc = getAccessible(aAccOrElmOrID);
if (!acc) {
ok(false, msg + "No accessible for " + aAccOrElmOrID + "!");
}
try {
is(acc.name, aName, msg + "Wrong name of the accessible for " + aAccOrElmOrID);
} catch (e) {
ok(false, msg + "Can't get name of the accessible for " + aAccOrElmOrID);
}
return acc;
}
////////////////////////////////////////////////////////////////////////////////
// Name tests described by "namerules.xml" file.
var gNameRulesFileURL =
"chrome://mochikit/content/a11y/accessible/namerules.xml";
var gRuleDoc = null;
/**
* Start name tests. Run through markup elements and test names for test
* element (see namerules.xml for details).
*/
function testNames()
{
var request = new XMLHttpRequest();
request.open("get", gNameRulesFileURL, false);
request.send();
gRuleDoc = request.responseXML;
var markupElms = evaluateXPath(gRuleDoc, "//rules/rulesample/markup");
for (var idx = 0; idx < markupElms.length; idx++)
testNamesForMarkup(markupElms[idx]);
}
////////////////////////////////////////////////////////////////////////////////
// Private section.
/**
* Process every 'markup' element and test names for it. Used by testNames
* function.
*/
function testNamesForMarkup(aMarkupElm)
{
var div = document.createElement("div");
div.setAttribute("test", "test");
var child = aMarkupElm.firstChild;
while (child) {
var newChild = document.importNode(child, true);
div.appendChild(newChild);
child = child.nextSibling;
}
document.body.appendChild(div);
var serializer = new XMLSerializer();
var expr = "//html/body/div[@test='test']/" + aMarkupElm.getAttribute("ref");
var elms = evaluateXPath(document, expr, htmlDocResolver);
var ruleId = aMarkupElm.getAttribute("ruleset");
var ruleElms = getRuleElmsByRulesetId(ruleId);
for (var idx = 0; idx < ruleElms.length; idx++)
testNameForRule(elms[0], ruleElms[idx]);
document.body.removeChild(div);
}
/**
* Test name for current rule and current 'markup' element. Used by
* testNamesForMarkup function.
*/
function testNameForRule(aElm, aRule)
{
var attr = aRule.getAttribute("attr");
if (attr) {
var name = "";
var attrValue = aElm.getAttribute(attr);
var type = aRule.getAttribute("type");
if (type == "string") {
name = attrValue;
} else if (type == "ref") {
var ids = attrValue.split(/\s+/);///\,\s*/);
for (var idx = 0; idx < ids.length; idx++) {
var labelElm = getNode(ids[idx]);
if (name != "")
name += " ";
name += labelElm.getAttribute("a11yname");
}
}
var msg = "Attribute '" + attr + "' test. ";
testName(aElm, name, msg);
aElm.removeAttribute(attr);
return;
}
var elm = aRule.getAttribute("elm");
var elmattr = aRule.getAttribute("elmattr");
if (elm && elmattr) {
var filter = {
acceptNode: function filter_acceptNode(aNode)
{
if (aNode.localName == this.mLocalName &&
aNode.getAttribute(this.mAttrName) == this.mAttrValue)
return NodeFilter.FILTER_ACCEPT;
return NodeFilter.FILTER_SKIP;
},
mLocalName: elm,
mAttrName: elmattr,
mAttrValue: aElm.getAttribute("id")
};
var treeWalker = document.createTreeWalker(document.body,
NodeFilter.SHOW_ELEMENT,
filter, false);
var labelElm = treeWalker.nextNode();
var msg = "Element '" + elm + "' test.";
testName(aElm, labelElm.getAttribute("a11yname"), msg);
labelElm.parentNode.removeChild(labelElm);
return;
}
var fromSubtree = aRule.getAttribute("fromsubtree");
if (fromSubtree == "true") {
var msg = "From subtree test.";
testName(aElm, aElm.getAttribute("a11yname"), msg);
while (aElm.firstChild)
aElm.removeChild(aElm.firstChild);
return;
}
}
/**
* Return array of 'rule' elements. Used in conjunction with
* getRuleElmsFromRulesetElm() function.
*/
function getRuleElmsByRulesetId(aRulesetId)
{
var expr = "//rules/ruledfn/ruleset[@id='" + aRulesetId + "']";
var rulesetElm = evaluateXPath(gRuleDoc, expr);
return getRuleElmsFromRulesetElm(rulesetElm[0]);
}
function getRuleElmsFromRulesetElm(aRulesetElm)
{
var rulesetId = aRulesetElm.getAttribute("ref");
if (rulesetId)
return getRuleElmsByRulesetId(rulesetId);
var ruleElms = [];
var child = aRulesetElm.firstChild;
while (child) {
if (child.localName == "ruleset")
ruleElms = ruleElms.concat(getRuleElmsFromRulesetElm(child));
if (child.localName == "rule")
ruleElms.push(child);
child = child.nextSibling;
}
return ruleElms;
}
/**
* Helper method to evaluate xpath expression.
*/
function evaluateXPath(aNode, aExpr, aResolver)
{
var xpe = new XPathEvaluator();
var resolver = aResolver;
if (!resolver) {
var node = aNode.ownerDocument == null ?
aNode.documentElement : aNode.ownerDocument.documentElement;
resolver = xpe.createNSResolver(node);
}
var result = xpe.evaluate(aExpr, aNode, resolver, 0, null);
var found = [];
var res;
while (res = result.iterateNext())
found.push(res);
return found;
}
function htmlDocResolver(aPrefix) {
var ns = {
'html' : 'http://www.w3.org/1999/xhtml'
};
return ns[aPrefix] || null;
}
function testName(aAccOrElmOrID, aName, aMsg)
{
var msg = aMsg ? aMsg : "";
var acc = getAccessible(aAccOrElmOrID);
if (!acc)
return;
try {
is(acc.name, aName, msg + "Wrong name of the accessible for " + aAccOrElmOrID);
} catch (e) {
ok(false, msg + "Can't get name of the accessible for " + aAccOrElmOrID);
}
return acc;
}
////////////////////////////////////////////////////////////////////////////////
// Name tests described by "namerules.xml" file.
var gNameRulesFileURL =
"chrome://mochikit/content/a11y/accessible/namerules.xml";
var gRuleDoc = null;
/**
* Start name tests. Run through markup elements and test names for test
* element (see namerules.xml for details).
*/
function testNames()
{
var request = new XMLHttpRequest();
request.open("get", gNameRulesFileURL, false);
request.send();
gRuleDoc = request.responseXML;
var markupElms = evaluateXPath(gRuleDoc, "//rules/rulesample/markup");
gTestIterator.iterateMarkups(markupElms);
}
////////////////////////////////////////////////////////////////////////////////
// Private section.
/**
* Helper class to interate through name tests.
*/
var gTestIterator =
{
iterateMarkups: function gTestIterator_iterateMarkups(aMarkupElms)
{
this.markupElms = aMarkupElms;
this.iterateNext();
},
iterateRules: function gTestIterator_iterateRules(aElm, aContainer, aRuleElms)
{
this.ruleElms = aRuleElms;
this.elm = aElm;
this.container = aContainer;
this.iterateNext();
},
iterateNext: function gTestIterator_iterateNext()
{
if (this.markupIdx == -1) {
this.markupIdx++;
testNamesForMarkup(this.markupElms[this.markupIdx]);
return;
}
this.ruleIdx++;
if (this.ruleIdx == this.ruleElms.length) {
this.markupIdx++;
if (this.markupIdx == this.markupElms.length) {
SimpleTest.finish();
return;
}
document.body.removeChild(this.container);
this.ruleIdx = -1;
testNamesForMarkup(this.markupElms[this.markupIdx]);
return;
}
testNameForRule(this.elm, this.ruleElms[this.ruleIdx]);
},
markupElms: null,
markupIdx: -1,
ruleElms: null,
ruleIdx: -1,
elm: null,
container: null
};
/**
* Process every 'markup' element and test names for it. Used by testNames
* function.
*/
function testNamesForMarkup(aMarkupElm)
{
var div = document.createElement("div");
div.setAttribute("id", "test");
var child = aMarkupElm.firstChild;
while (child) {
var newChild = document.importNode(child, true);
div.appendChild(newChild);
child = child.nextSibling;
}
waitForEvent(EVENT_REORDER, document, testNamesForMarkupRules,
null, aMarkupElm, div);
document.body.appendChild(div);
}
function testNamesForMarkupRules(aMarkupElm, aContainer)
{
ensureAccessibleTree(aContainer);
var serializer = new XMLSerializer();
var expr = "//html/body/div[@id='test']/" + aMarkupElm.getAttribute("ref");
var elms = evaluateXPath(document, expr, htmlDocResolver);
var ruleId = aMarkupElm.getAttribute("ruleset");
var ruleElms = getRuleElmsByRulesetId(ruleId);
gTestIterator.iterateRules(elms[0], aContainer, ruleElms);
}
/**
* Test name for current rule and current 'markup' element. Used by
* testNamesForMarkup function.
*/
function testNameForRule(aElm, aRuleElm)
{
if (aRuleElm.hasAttribute("attr"))
testNameForAttrRule(aElm, aRuleElm);
else if (aRuleElm.hasAttribute("elm") && aRuleElm.hasAttribute("elmattr"))
testNameForElmRule(aElm, aRuleElm);
else if (aRuleElm.getAttribute("fromsubtree") == "true")
testNameForSubtreeRule(aElm, aRuleElm);
}
function testNameForAttrRule(aElm, aRule)
{
var name = "";
var attr = aRule.getAttribute("attr");
var attrValue = aElm.getAttribute(attr);
var type = aRule.getAttribute("type");
if (type == "string") {
name = attrValue;
} else if (type == "ref") {
var ids = attrValue.split(/\s+/);
for (var idx = 0; idx < ids.length; idx++) {
var labelElm = getNode(ids[idx]);
if (name != "")
name += " ";
name += labelElm.getAttribute("a11yname");
}
}
var msg = "Attribute '" + attr + "' test. ";
testName(aElm, name, msg);
aElm.removeAttribute(attr);
gTestIterator.iterateNext();
}
function testNameForElmRule(aElm, aRule)
{
var elm = aRule.getAttribute("elm");
var elmattr = aRule.getAttribute("elmattr");
var filter = {
acceptNode: function filter_acceptNode(aNode)
{
if (aNode.localName == this.mLocalName &&
aNode.getAttribute(this.mAttrName) == this.mAttrValue)
return NodeFilter.FILTER_ACCEPT;
return NodeFilter.FILTER_SKIP;
},
mLocalName: elm,
mAttrName: elmattr,
mAttrValue: aElm.getAttribute("id")
};
var treeWalker = document.createTreeWalker(document.body,
NodeFilter.SHOW_ELEMENT,
filter, false);
var labelElm = treeWalker.nextNode();
var msg = "Element '" + elm + "' test.";
testName(aElm, labelElm.getAttribute("a11yname"), msg);
var parentNode = labelElm.parentNode;
waitForEvent(EVENT_REORDER, parentNode,
gTestIterator.iterateNext, gTestIterator);
parentNode.removeChild(labelElm);
}
function testNameForSubtreeRule(aElm, aRule)
{
var msg = "From subtree test.";
testName(aElm, aElm.getAttribute("a11yname"), msg);
waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator);
while (aElm.firstChild)
aElm.removeChild(aElm.firstChild);
}
/**
* Return array of 'rule' elements. Used in conjunction with
* getRuleElmsFromRulesetElm() function.
*/
function getRuleElmsByRulesetId(aRulesetId)
{
var expr = "//rules/ruledfn/ruleset[@id='" + aRulesetId + "']";
var rulesetElm = evaluateXPath(gRuleDoc, expr);
return getRuleElmsFromRulesetElm(rulesetElm[0]);
}
function getRuleElmsFromRulesetElm(aRulesetElm)
{
var rulesetId = aRulesetElm.getAttribute("ref");
if (rulesetId)
return getRuleElmsByRulesetId(rulesetId);
var ruleElms = [];
var child = aRulesetElm.firstChild;
while (child) {
if (child.localName == "ruleset")
ruleElms = ruleElms.concat(getRuleElmsFromRulesetElm(child));
if (child.localName == "rule")
ruleElms.push(child);
child = child.nextSibling;
}
return ruleElms;
}
/**
* Helper method to evaluate xpath expression.
*/
function evaluateXPath(aNode, aExpr, aResolver)
{
var xpe = new XPathEvaluator();
var resolver = aResolver;
if (!resolver) {
var node = aNode.ownerDocument == null ?
aNode.documentElement : aNode.ownerDocument.documentElement;
resolver = xpe.createNSResolver(node);
}
var result = xpe.evaluate(aExpr, aNode, resolver, 0, null);
var found = [];
var res;
while (res = result.iterateNext())
found.push(res);
return found;
}
function htmlDocResolver(aPrefix) {
var ns = {
'html' : 'http://www.w3.org/1999/xhtml'
};
return ns[aPrefix] || null;
}

Просмотреть файл

@ -55,11 +55,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452388
testStates("native_checkbox_nativeunchecked_ariaabsent", STATE_CHECKABLE, 0, STATE_CHECKED);
// test button aria-pressed states
testStates("button_pressed_true", STATE_PRESSED);
testStates("button_pressed_false", 0, 0, STATE_PRESSED);
testStates("button_pressed_empty", 0, 0, STATE_PRESSED);
testStates("button_pressed_undefined", 0, 0, STATE_PRESSED);
testStates("button_pressed_absent", 0, 0, STATE_PRESSED);
testStates("button_pressed_true", STATE_PRESSED | STATE_CHECKABLE);
testStates("button_pressed_false", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
testStates("button_pressed_empty", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
testStates("button_pressed_undefined", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
testStates("button_pressed_absent", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
// test aria-pressed state mapping to roles PUSHBUTTON vs TOGGLEBUTTON
var aButton = getAccessible("button_pressed_true");

Просмотреть файл

@ -1,138 +1,138 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=420863
-->
<head>
<title>Table indexes 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/MochiKit/packed.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript">
var gAccService = null;
var gTdClickAttr = false;
var gTdClickEventHandler = false;
var gClickHandler = null;
var gID = "";
var gNode = null;
var gAcc = null;
function doTest()
{
const nsIAccessibleRetrieval =
Components.interfaces.nsIAccessibleRetrieval;
gAccService = Components.classes["@mozilla.org/accessibleRetrieval;1"].
getService(nsIAccessibleRetrieval);
// Actions should be exposed on any accessible having related DOM node
// with registered 'click' event handler.
//////////////////////////////////////////////////////////////////////////
// generic td
gID = "td1";
gNode = document.getElementById(gID);
gAcc = gAccService.getAccessibleFor(gNode);
is(gAcc.numActions, 0, gID + ": shouldn't have actions");
//////////////////////////////////////////////////////////////////////////
// td with 'onclick' attribute
gID = "td2";
gNode = document.getElementById(gID);
gAcc = gAccService.getAccessibleFor(gNode);
is(gAcc.numActions, 1, gID + ": should have one action");
is(gAcc.getActionName(0), "click", gID + ": should have 'click' action");
gAcc.doAction(0);
// actions are performed via timeout
window.setTimeout(doTest2, 0);
}
function doTest2()
{
//////////////////////////////////////////////////////////////////////////
// td with 'onclick' attribute (sequel, see doTest1())
ok(gTdClickAttr, gID + ": 'click' action hasn't been performed");
//////////////////////////////////////////////////////////////////////////
// td with registered 'click' event handler
gID = "td3";
gNode = document.getElementById(gID);
gAcc = gAccService.getAccessibleFor(gNode);
// register 'click' event handler
gClickHandler = {
handleEvent: function handleEvent(aEvent)
{
gTdClickEventHandler = true;
}
};
gNode.addEventListener("click", gClickHandler, false);
// check actions
is(gAcc.numActions, 1, gID + ": should have one action");
is(gAcc.getActionName(0), "click", gID + ": should have 'click' action");
gAcc.doAction(0);
// actions are performed via timeout
window.setTimeout(doTest3, 0);
}
function doTest3()
{
//////////////////////////////////////////////////////////////////////////
// td with registered 'click' event handler (sequel, see doTest2())
ok(gTdClickEventHandler, gID + ": 'click' action hasn't been performed");
// unregister click event handler
gNode.removeEventListener("click", gClickHandler, false);
// check actions
// XXX see bug 456347, sometimes after removing the event listener, the
// accessible is no longer valid. When fixing that bug, remove the
// try/exception and simply test for the gAcc.numActions value directly.
var numActions = -1;
try {
numActions = gAcc.numActions;
} catch(e) {}
if (numActions == -1)
todo(false,
"gAcc.numActions should not throw after click handler was removed!");
else
is(numActions, 0, gID + ": shouldn't have actions");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
</script>
</head>
<body>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=420863"
title="If an HTML element has an onClick attribute, expose its click action on the element rather than its child text leaf node."
target="_blank">Mozilla Bug 420863</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<table>
<tr>
<td id="td1">Can't click this cell</td>
<td onclick="gTdClickAttr = true;"
id="td2">Cell with 'onclick' attribute</td>
<td id="td3">Cell with registered 'click' event handler</td>
</tr>
</table>
</body>
</html>
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=420863
-->
<head>
<title>Table indexes 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/MochiKit/packed.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript">
var gAccService = null;
var gTdClickAttr = false;
var gTdClickEventHandler = false;
var gClickHandler = null;
var gID = "";
var gNode = null;
var gAcc = null;
function doTest()
{
const nsIAccessibleRetrieval =
Components.interfaces.nsIAccessibleRetrieval;
gAccService = Components.classes["@mozilla.org/accessibleRetrieval;1"].
getService(nsIAccessibleRetrieval);
// Actions should be exposed on any accessible having related DOM node
// with registered 'click' event handler.
//////////////////////////////////////////////////////////////////////////
// generic td
gID = "td1";
gNode = document.getElementById(gID);
gAcc = gAccService.getAccessibleFor(gNode);
is(gAcc.numActions, 0, gID + ": shouldn't have actions");
//////////////////////////////////////////////////////////////////////////
// td with 'onclick' attribute
gID = "td2";
gNode = document.getElementById(gID);
gAcc = gAccService.getAccessibleFor(gNode);
is(gAcc.numActions, 1, gID + ": should have one action");
is(gAcc.getActionName(0), "click", gID + ": should have 'click' action");
gAcc.doAction(0);
// actions are performed via timeout
window.setTimeout(doTest2, 0);
}
function doTest2()
{
//////////////////////////////////////////////////////////////////////////
// td with 'onclick' attribute (sequel, see doTest1())
ok(gTdClickAttr, gID + ": 'click' action hasn't been performed");
//////////////////////////////////////////////////////////////////////////
// td with registered 'click' event handler
gID = "td3";
gNode = document.getElementById(gID);
gAcc = gAccService.getAccessibleFor(gNode);
// register 'click' event handler
gClickHandler = {
handleEvent: function handleEvent(aEvent)
{
gTdClickEventHandler = true;
}
};
gNode.addEventListener("click", gClickHandler, false);
// check actions
is(gAcc.numActions, 1, gID + ": should have one action");
is(gAcc.getActionName(0), "click", gID + ": should have 'click' action");
gAcc.doAction(0);
// actions are performed via timeout
window.setTimeout(doTest3, 0);
}
function doTest3()
{
//////////////////////////////////////////////////////////////////////////
// td with registered 'click' event handler (sequel, see doTest2())
ok(gTdClickEventHandler, gID + ": 'click' action hasn't been performed");
// unregister click event handler
gNode.removeEventListener("click", gClickHandler, false);
// check actions
// XXX see bug 456347, sometimes after removing the event listener, the
// accessible is no longer valid. When fixing that bug, remove the
// try/exception and simply test for the gAcc.numActions value directly.
var numActions = -1;
try {
numActions = gAcc.numActions;
} catch(e) {}
if (numActions == -1)
todo(false,
"gAcc.numActions should not throw after click handler was removed!");
else
is(numActions, 0, gID + ": shouldn't have actions");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
</script>
</head>
<body>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=420863"
title="If an HTML element has an onClick attribute, expose its click action on the element rather than its child text leaf node."
target="_blank">Mozilla Bug 420863</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<table>
<tr>
<td id="td1">Can't click this cell</td>
<td onclick="gTdClickAttr = true;"
id="td2">Cell with 'onclick' attribute</td>
<td id="td3">Cell with registered 'click' event handler</td>
</tr>
</table>
</body>
</html>

Просмотреть файл

@ -1,88 +1,88 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=434464
-->
<head>
<title>Test NSIAccessNode cache invalidation</title>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript">
function doTest()
{
var accRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
getService(Components.interfaces.nsIAccessibleRetrieval);
var parentElm = document.getElementById("parent");
if (!parentElm) {
ok(false, "no parent element for paragraph!");
SimpleTest.finish();
}
var elm = document.getElementById("para");
if (!elm) {
ok(false, "no element for paragraph!");
SimpleTest.finish();
}
// It's currently hidden. Ask for its parent's nsIAccessNode.
var parentElmAccNode = null;
try {
parentElmAccNode = accRetrieval.getAccessibleFor(parentElm).
QueryInterface(Components.interfaces.nsIAccessNode);
} catch(e) {}
if (!parentElmAccNode) {
ok(false, "No accessNode for parent of hidden paragraph!");
SimpleTest.finish();
}
// Get the paragraph's accessNode.
var elmAccNode = null;
try {
elmAccNode = parentElmAccNode.firstChildNode;
} catch(e) {}
if (!elmAccNode) {
ok(false, "No accessNode for hidden paragraph!");
SimpleTest.finish();
}
// Now make the paragraph visible. This invalidates the just-retrieved
// AccessNode. An nsIAccessible should then be retrievable.
elm.style.display = "block";
// Now, get an accessible for it. Use a timeout so the layout engine can
// catch up.
window.setTimeout(
function()
{
var elmAcc = null;
try {
elmAcc = accRetrieval.getAccessibleFor(elm);
} catch(e) {}
ok(elmAcc, "No accessible for paragraph after it became visible!");
SimpleTest.finish();
},
200);
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=434464">Mozilla Bug 434464</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<div id="parent"><p style="display: none;" id="para">I'm hidden initially, but then made visible.</p></div>
</body>
</html>
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=434464
-->
<head>
<title>Test NSIAccessNode cache invalidation</title>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript">
function doTest()
{
var accRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
getService(Components.interfaces.nsIAccessibleRetrieval);
var parentElm = document.getElementById("parent");
if (!parentElm) {
ok(false, "no parent element for paragraph!");
SimpleTest.finish();
}
var elm = document.getElementById("para");
if (!elm) {
ok(false, "no element for paragraph!");
SimpleTest.finish();
}
// It's currently hidden. Ask for its parent's nsIAccessNode.
var parentElmAccNode = null;
try {
parentElmAccNode = accRetrieval.getAccessibleFor(parentElm).
QueryInterface(Components.interfaces.nsIAccessNode);
} catch(e) {}
if (!parentElmAccNode) {
ok(false, "No accessNode for parent of hidden paragraph!");
SimpleTest.finish();
}
// Get the paragraph's accessNode.
var elmAccNode = null;
try {
elmAccNode = parentElmAccNode.firstChildNode;
} catch(e) {}
if (!elmAccNode) {
ok(false, "No accessNode for hidden paragraph!");
SimpleTest.finish();
}
// Now make the paragraph visible. This invalidates the just-retrieved
// AccessNode. An nsIAccessible should then be retrievable.
elm.style.display = "block";
// Now, get an accessible for it. Use a timeout so the layout engine can
// catch up.
window.setTimeout(
function()
{
var elmAcc = null;
try {
elmAcc = accRetrieval.getAccessibleFor(elm);
} catch(e) {}
ok(elmAcc, "No accessible for paragraph after it became visible!");
SimpleTest.finish();
},
200);
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=434464">Mozilla Bug 434464</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<div id="parent"><p style="display: none;" id="para">I'm hidden initially, but then made visible.</p></div>
</body>
</html>

Просмотреть файл

@ -1,7 +1,7 @@
<html>
<head>
<title>nsIAccessible::name calculation for HTML buttons</title>
<title>nsIAccessible::name calculation for elements</title>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
@ -10,19 +10,16 @@
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/events.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/nsIAccessible_name.js"></script>
<script type="application/javascript">
function doTest()
{
testNames();
SimpleTest.finish();
}
// gA11yEventDumpID = "eventdump";
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
addA11yLoadEvent(testNames);
</script>
</head>
@ -31,7 +28,7 @@
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=459635"
title="nsIAccessible::name calculation for HTML buttons">
title="nsIAccessible::name calculation for elements">
Mozilla Bug 459635
</a>
<p id="display"></p>
@ -39,6 +36,6 @@
<pre id="test">
</pre>
<div id="eventdump"></div>
</body>
</html>

Просмотреть файл

@ -35,6 +35,10 @@
// of elements. Gets the name from text nodes of those elements.
testName("btn_labelledby_texts", "text1 text2");
//////////////////////////////////////////////////////////////////////////
// Name from named accessible
testName("input_labelledby_namedacc", "Data");
//////////////////////////////////////////////////////////////////////////
// Name from subtree (single relation labelled_by).
@ -129,10 +133,21 @@
testName("textareawithchild", "Story: Bar");
/////////////////////////////////////////////////////////////////////////
// label with nested combobox
// label with nested combobox (test for 'f' item of name computation guide)
testName("comboinstart", "One day(s).");
testName("combo3", "day(s).");
testName("comboinmiddle", "Subscribe to ATOM feed.");
testName("combo4", "Subscribe to ATOM feed.");
testName("comboinmiddle2", "Play the Haliluya sound when new mail arrives");
testName("combo5", "Play the Haliluya sound when new mail arrives");
testName("checkbox", "Play the Haliluya sound when new mail arrives");
testName("comboinend", "This day was sunny");
testName("combo6", "This day was");
SimpleTest.finish();
}
@ -175,6 +190,12 @@
aria-labelledby="labelledby_text1 labelledby_text2">2</button>
<br/>
<!-- name from named accessible -->
<input id="labelledby_namedacc" type="checkbox"
aria-label="Data" />
<input id="input_labelledby_namedacc"
aria-labelledby="labelledby_namedacc" />
<!-- the name from subtree, mixed content -->
<span id="labelledby_mixed">no<span>more text</span></span>
<button id="btn_labelledby_mixed"
@ -286,16 +307,39 @@
</label>
</form>
<!-- A label with a nested control in the middle -->
<!-- a label with a nested control in the start, middle and end -->
<form>
<label id="comboinstart"><select id="combo3">
<option>One</option>
<option>Two</option>
</select>
day(s).
</label>
<label id="comboinmiddle">
Subscribe to
<select id="combo3" name="occupation">
<select id="combo4" name="occupation">
<option>ATOM</option>
<option>RSS</option>
</select>
feed.
</label>
<label id="comboinmiddle2" for="checkbox">Play the
<select id="combo5">
<option>Haliluya</option>
<option>Hurra</option>
</select>
sound when new mail arrives
</label>
<input id="checkbox" type="checkbox" />
<label id="comboinend">
This day was
<select id="combo6" name="occupation">
<option>sunny</option>
<option>rainy</option>
</select></label>
</form>
</body>

Просмотреть файл

@ -40,6 +40,10 @@
// of elements. Gets the name from text nodes of those elements.
testName("btn_labelledby_texts", "text1 text2");
// Trick cases. Self and recursive referencing.
testName("rememberHistoryDays", "Remember 3 days");
testName("historyDays", "Remember 3 days");
testName("rememberAfter", null); // XUL labels doesn't allow name from subtree
//////////////////////////////////////////////////////////////////////////
// Name from subtree (single relation labelled_by).
@ -101,7 +105,7 @@
// Get the name from anonymous label element for anonymous textbox
// (@anonid is used).
var ID = "box_label_anon1";
var box1Acc = testName(ID, "");
var box1Acc = testName(ID, null);
if (box1Acc) {
var textboxAcc = box1Acc.firstChild;
is(textboxAcc.name, "Label",
@ -111,7 +115,7 @@
// Get the name from anonymous label element for anonymous textbox
// (@anonid is used). Nested bindings.
ID = "box_label_anon2";
var box2Acc = testName(ID, "");
var box2Acc = testName(ID, null);
if (box2Acc) {
var textboxAcc = box2Acc.firstChild;
is(textboxAcc.name, "Label",
@ -195,6 +199,14 @@
<button id="btn_labelledby_texts"
aria-labelledby="labelledby_text1 labelledby_text2"/>
<!-- trick aria-labelledby -->
<checkbox id="rememberHistoryDays"
label="Remember "
aria-labelledby="rememberHistoryDays historyDays rememberAfter"/>
<textbox id="historyDays" type="number" size="3" value="3"
aria-labelledby="rememberHistoryDays historyDays rememberAfter"/>
<label id="rememberAfter">days</label>
<!-- the name from subtree, mixed content -->
<description id="labelledby_mixed">
no<description>more text</description>

Просмотреть файл

@ -47,8 +47,8 @@
names = [
"Search in: Newsticker", // label
"Search in:", // text leaf
"Search in: Newsticker", // combobox
"Search in:", // combobox_list
"Search in:", // combobox
"Search in: Newsticker", // combobox_list
"Newsticker", // option 1
"Entire site" // Option 2
];

Просмотреть файл

@ -0,0 +1,95 @@
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=475006
-->
<head>
<title>Group attributes tests</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/attributes.js"></script>
<script type="application/javascript">
function doTest()
{
// aria
testAttrs("atomic", {"atomic" : "true"}, true);
testAttrs("autocomplete", {"autocomplete" : "true"}, true);
testAttrs("checkbox", {"checkable" : "true"}, true);
testAttrs("checkedCheckbox", {"checkable" : "true"}, true);
testAttrs("checkedMenuitem", {"checkable" : "true"}, true);
testAttrs("checkedOption", {"checkable" : "true"}, true);
testAttrs("checkedRadio", {"checkable" : "true"}, true);
testAttrs("checkedTreeitem", {"checkable" : "true"}, true);
testAttrs("dropeffect", {"dropeffect" : "copy"}, true);
// live object attribute
testAttrs("live", {"live" : "polite"}, true);
testAttrs("live2", {"live" : "polite"}, true);
testAttrs("log", {"live" : "polite"}, true);
testAttrs("marquee", {"live" : "off"}, true);
testAttrs("status", {"live" : "polite"}, true);
testAttrs("timer", {"live" : "off"}, true);
// container-live object attribute
testAttrs("liveChild", {"container-live" : "polite"}, true);
testAttrs("live2Child", {"container-live" : "polite"}, true);
testAttrs("logChild", {"container-live" : "polite"}, true);
testAttrs("marqueeChild", {"container-live" : "off"}, true);
testAttrs("statusChild", {"container-live" : "polite"}, true);
testAttrs("timerChild", {"container-live" : "off"}, true);
// html
testAttrs("radio", {"checkable" : "true"}, true);
testAttrs("checkbox", {"checkable" : "true"}, true);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=475006"
title="Extend nsARIAMap to capture ARIA attribute characteristics">
Mozilla Bug 475006
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<!-- aria -->
<div id="atomic" aria-atomic="true"></div>
<div id="autocomplete" role="textbox" aria-autocomplete="true"></div>
<div id="checkbox" role="checkbox"></div>
<div id="checkedCheckbox" role="checkbox" aria-checked="true"></div>
<div id="checkedMenuitem" role="menuitem" aria-checked="true"></div>
<div id="checkedOption" role="option" aria-checked="true"></div>
<div id="checkedRadio" role="radio" aria-checked="true"></div>
<div id="checkedTreeitem" role="treeitem" aria-checked="true"></div>
<div id="dropeffect" aria-dropeffect="copy"></div>
<div id="live" aria-live="polite">excuse <div id="liveChild">me</div></div>
<div id="live2" role="marquee" aria-live="polite">excuse <div id="live2Child">me</div></div>
<div id="log" role="log">excuse <div id="logChild">me</div></div>
<div id="marquee" role="marquee">excuse <div id="marqueeChild">me</div></div>
<div id="status" role="status">excuse <div id="statusChild">me</div></div>
<div id="timer" role="timer">excuse <div id="timerChild">me</div></div>
<!-- html -->
<input id="radio" type="radio"/>
<input id="checkbox" type="checkbox"/>
</body>
</html>

Просмотреть файл

@ -2,9 +2,10 @@
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=454997
https://bugzilla.mozilla.org/show_bug.cgi?id=467387
-->
<head>
<title>nsIAccessible states tests of editable document</title>
<title>states of document</title>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
@ -20,10 +21,22 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=454997
<script type="application/javascript">
function doTest()
{
testStates(document, STATE_READONLY);
testStates("document", STATE_READONLY);
testStates("editable_document", 0, EXT_STATE_EDITABLE);
document.designMode = "on";
testStates(document, 0, EXT_STATE_EDITABLE);
testStates("p", 0, EXT_STATE_EDITABLE);
testStates("document", 0, EXT_STATE_EDITABLE);
testStates("editable_document", 0, EXT_STATE_EDITABLE);
document.designMode = "off";
testStates(document, STATE_READONLY);
testStates("document", STATE_READONLY);
testStates("editable_document", 0, EXT_STATE_EDITABLE);
SimpleTest.finish();
}
@ -33,16 +46,22 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=454997
</script>
</head>
<body id="body">
<body>
<a target="_blank"
title="nsIAccessible states tests of editable document"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=454997">Mozilla Bug 454997</a>
<a target="_blank"
title="nsIAccessible states tests of editable document"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=467387">Mozilla Bug 467387</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<p id="p">hello</p>
<div id="document" role="document">document</div>
<div id="editable_document" role="document" contentEditable="true">editable document</doc>
</body>
</html>

Просмотреть файл

@ -0,0 +1,62 @@
<html>
<head>
<title>states of document article</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/nsIAccessible_states.js"></script>
<script type="application/javascript">
function doTest()
{
var docAcc = getAccessible(document, [nsIAccessibleDocument]);
if (docAcc) {
testStates(docAcc, STATE_READONLY);
testStates("article", STATE_READONLY);
testStates("editable_article", 0, EXT_STATE_EDITABLE);
document.designMode = "on";
testStates(docAcc, 0, EXT_STATE_EDITABLE);
testStates("article", 0, EXT_STATE_EDITABLE);
testStates("editable_article", 0, EXT_STATE_EDITABLE);
document.designMode = "off";
testStates(docAcc, STATE_READONLY);
testStates("article", STATE_READONLY);
testStates("editable_article", 0, EXT_STATE_EDITABLE);
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
</script>
</head>
<body role="article">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=467387"
title="Expose non-editable documents as readonly, regardless of role">
Mozilla Bug 467387
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<div id="article" role="article">article</div>
<div id="editable_article" role="article" contentEditable="true">editable article</doc>
</body>
</html>

Просмотреть файл

@ -0,0 +1,76 @@
<html>
<head>
<title>frame based document testing</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/nsIAccessible_states.js"></script>
<script type="application/javascript">
function doTest()
{
frameDoc = document.getElementById("frame_doc").contentDocument;
frameDocArticle = document.getElementById("frame_doc_article").contentDocument;
frameDocCheckbox = document.getElementById("frame_doc_checkbox").contentDocument;
frameDocTextbox = document.getElementById("frame_doc_textbox").contentDocument;
testStates(frameDoc, STATE_READONLY);
testStates(frameDocArticle, STATE_READONLY);
testStates(frameDocCheckbox, 0, 0, STATE_READONLY);
var works = true;
try {
testStates(frameDocTextbox, 0, 0, STATE_READONLY);
}
catch (e) {
works = false;
}
todo(works, "Checking states of a textbox frame doc should not throw (Bug 478810)");
frameDoc.designMode = "on";
testStates(frameDoc, 0, EXT_STATE_EDITABLE);
testStates(frameDocArticle, STATE_READONLY);
testStates(frameDocCheckbox, 0, 0, STATE_READONLY);
frameDocArticle.designMode = "on";
testStates(frameDocArticle, 0, EXT_STATE_EDITABLE);
frameDocCheckbox.designMode = "on";
testStates(frameDocCheckbox, 0, 0, STATE_READONLY);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=467387"
title="Expose non-editable documents as readonly, regardless of role">
Mozilla Bug 467387
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<iframe id="frame_doc" src="z_states_frame.html"></iframe>
<iframe id="frame_doc_article" src="z_states_framearticle.html"></iframe>
<iframe id="frame_doc_checkbox" src="z_states_framecheckbox.html"></iframe>
<iframe id="frame_doc_textbox" src="z_states_frametextbox.html"></iframe>
</body>
</html>

Просмотреть файл

@ -1,262 +1,262 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=410052
-->
<head>
<title>Table indexes 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/MochiKit/packed.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript">
const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
const nsIAccessibleTable = Components.interfaces.nsIAccessibleTable;
var gAccService = null;
function doTest()
{
gAccService = Components.classes["@mozilla.org/accessibleRetrieval;1"].
getService(nsIAccessibleRetrieval);
//////////////////////////////////////////////////////////////////////////
// table
var tRow = new Array(0,0,0,1,1,1,2,2,3,3);
var tCol = new Array(0,1,2,0,1,2,0,1,1,2);
testTable("table", 10, tRow, tCol);
//////////////////////////////////////////////////////////////////////////
// tableinsane1
tRow = [0,0,0,1,1,1,2,2,3,3];
tCol = [0,1,2,0,1,2,0,1,1,2];
testTable("tableinsane1", 10, tRow, tCol);
//////////////////////////////////////////////////////////////////////////
// tableinsane2
tRow = [0,0,0,1,1,1,2,2,3,3,4,4,4];
tCol = [0,1,2,0,1,2,0,1,1,2,1,3,4];
testTable("tableinsane2", 13, tRow, tCol);
//////////////////////////////////////////////////////////////////////////
// tableinsane4
tRow = [0,0,0,1,1,1,2,2,3,4];
tCol = [0,1,2,0,1,2,0,2,0,0];
testTable("tableinsane4", 10, tRow, tCol);
//////////////////////////////////////////////////////////////////////////
// tableborder
tRow = [0,0,0,1,1,1,2,2,3,3];
tCol = [0,1,2,0,1,2,0,1,1,2];
testTable("tableborder", 10, tRow, tCol);
SimpleTest.finish();
}
function testTable(aId, aLen, aRowIdxes, aColIdxes)
{
var table = document.getElementById(aId);
var tableAcc = gAccService.getAccessibleFor(table).
QueryInterface(nsIAccessibleTable);
var row, column, index;
var cellAcc;
for (var i = 0; i < aLen; i++) {
row = tableAcc.getRowAtIndex(i);
column = tableAcc.getColumnAtIndex(i);
index = tableAcc.getIndexAt(aRowIdxes[i], aColIdxes[i]);
is(row, aRowIdxes[i], aId + ": row for index " + i +" is nor correct");
is(column, aColIdxes[i],
aId + ": column for index " + i +"is nor correct");
is(index, i,
aId + ": row " + row + " /column " + column + " and index " + index + " aren't inconsistent.");
try {
cellAcc = null;
cellAcc = tableAcc.cellRefAt(row, column);
} catch (e) { }
ok(cellAcc,
aId + ": Can't get cell accessible at row = " + row + ", column = " + column);
if (cellAcc) {
var attrs = cellAcc.attributes;
is (parseInt(attrs.getStringProperty("table-cell-index")), index,
aId + ": cell index from object attributes of cell accessible isn't corrent.");
}
}
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=410052">Mozilla Bug 410052</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<!--
If you change the structure of the table please make sure to change
the indexes count in 'for' statement in the script above.
-->
<table border="1" id="table">
<caption><strong><b><font size="29">this is a caption for this table</font></b></strong></caption>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td rowspan="0">4</td>
<td colspan="2">5</td>
</tr>
<tr>
<td>6</td>
<td>7</td>
</tr>
</tbody>
</table>
<table border="1" id="tableborder" style="border-collapse:collapse">
<caption><strong><b><font size="29">this is a caption for this bc table</font></b></strong></caption>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td rowspan="2">4</td>
<td colspan="2">5</td>
</tr>
<tr>
<td>6</td>
<td>7</td>
</tr>
</tbody>
</table>
<table border="1" id="tableinsane1">
<caption>test empty row groups</caption>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
</tr>
</thead>
<tbody></tbody>
<tbody></tbody>
<tbody></tbody>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td rowspan="2">4</td>
<td colspan="2">5</td>
</tr>
<tr>
<td>6</td>
<td>7</td>
</tr>
</tbody>
</table>
<table border="1" id="tableinsane2" >
<caption>empty rowgroup + empty rows</caption>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
</tr>
</thead>
<tbody><tr></tr></tbody>
<tbody></tbody>
<tbody></tbody>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td rowspan="0">4</td>
<td colspan="0">5</td>
</tr>
<tr>
<td>6</td>
<td rowspan="0">7</td>
</tr>
<tr>
<td>8</td>
<td>9</td>
<td>10</td>
</tr>
</tbody>
<table border="1" id="tableinsane4" >
<caption>test cellmap holes</caption>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
</tr>
</thead>
<tbody><tr></tr></tbody>
<tbody></tbody>
<tbody></tbody>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td colspan="2">4</td>
<td rowspan="2">5</td>
</tr>
<tr>
<td>6</td>
</tr>
<tr>
<td colspan="3">7</td>
</tr>
</tbody>
</table>
</body>
</html>
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=410052
-->
<head>
<title>Table indexes 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/MochiKit/packed.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript">
const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
const nsIAccessibleTable = Components.interfaces.nsIAccessibleTable;
var gAccService = null;
function doTest()
{
gAccService = Components.classes["@mozilla.org/accessibleRetrieval;1"].
getService(nsIAccessibleRetrieval);
//////////////////////////////////////////////////////////////////////////
// table
var tRow = new Array(0,0,0,1,1,1,2,2,3,3);
var tCol = new Array(0,1,2,0,1,2,0,1,1,2);
testTable("table", 10, tRow, tCol);
//////////////////////////////////////////////////////////////////////////
// tableinsane1
tRow = [0,0,0,1,1,1,2,2,3,3];
tCol = [0,1,2,0,1,2,0,1,1,2];
testTable("tableinsane1", 10, tRow, tCol);
//////////////////////////////////////////////////////////////////////////
// tableinsane2
tRow = [0,0,0,1,1,1,2,2,3,3,4,4,4];
tCol = [0,1,2,0,1,2,0,1,1,2,1,3,4];
testTable("tableinsane2", 13, tRow, tCol);
//////////////////////////////////////////////////////////////////////////
// tableinsane4
tRow = [0,0,0,1,1,1,2,2,3,4];
tCol = [0,1,2,0,1,2,0,2,0,0];
testTable("tableinsane4", 10, tRow, tCol);
//////////////////////////////////////////////////////////////////////////
// tableborder
tRow = [0,0,0,1,1,1,2,2,3,3];
tCol = [0,1,2,0,1,2,0,1,1,2];
testTable("tableborder", 10, tRow, tCol);
SimpleTest.finish();
}
function testTable(aId, aLen, aRowIdxes, aColIdxes)
{
var table = document.getElementById(aId);
var tableAcc = gAccService.getAccessibleFor(table).
QueryInterface(nsIAccessibleTable);
var row, column, index;
var cellAcc;
for (var i = 0; i < aLen; i++) {
row = tableAcc.getRowAtIndex(i);
column = tableAcc.getColumnAtIndex(i);
index = tableAcc.getIndexAt(aRowIdxes[i], aColIdxes[i]);
is(row, aRowIdxes[i], aId + ": row for index " + i +" is nor correct");
is(column, aColIdxes[i],
aId + ": column for index " + i +"is nor correct");
is(index, i,
aId + ": row " + row + " /column " + column + " and index " + index + " aren't inconsistent.");
try {
cellAcc = null;
cellAcc = tableAcc.cellRefAt(row, column);
} catch (e) { }
ok(cellAcc,
aId + ": Can't get cell accessible at row = " + row + ", column = " + column);
if (cellAcc) {
var attrs = cellAcc.attributes;
is (parseInt(attrs.getStringProperty("table-cell-index")), index,
aId + ": cell index from object attributes of cell accessible isn't corrent.");
}
}
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=410052">Mozilla Bug 410052</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<!--
If you change the structure of the table please make sure to change
the indexes count in 'for' statement in the script above.
-->
<table border="1" id="table">
<caption><strong><b><font size="29">this is a caption for this table</font></b></strong></caption>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td rowspan="0">4</td>
<td colspan="2">5</td>
</tr>
<tr>
<td>6</td>
<td>7</td>
</tr>
</tbody>
</table>
<table border="1" id="tableborder" style="border-collapse:collapse">
<caption><strong><b><font size="29">this is a caption for this bc table</font></b></strong></caption>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td rowspan="2">4</td>
<td colspan="2">5</td>
</tr>
<tr>
<td>6</td>
<td>7</td>
</tr>
</tbody>
</table>
<table border="1" id="tableinsane1">
<caption>test empty row groups</caption>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
</tr>
</thead>
<tbody></tbody>
<tbody></tbody>
<tbody></tbody>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td rowspan="2">4</td>
<td colspan="2">5</td>
</tr>
<tr>
<td>6</td>
<td>7</td>
</tr>
</tbody>
</table>
<table border="1" id="tableinsane2" >
<caption>empty rowgroup + empty rows</caption>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
</tr>
</thead>
<tbody><tr></tr></tbody>
<tbody></tbody>
<tbody></tbody>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td rowspan="0">4</td>
<td colspan="0">5</td>
</tr>
<tr>
<td>6</td>
<td rowspan="0">7</td>
</tr>
<tr>
<td>8</td>
<td>9</td>
<td>10</td>
</tr>
</tbody>
<table border="1" id="tableinsane4" >
<caption>test cellmap holes</caption>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
</tr>
</thead>
<tbody><tr></tr></tbody>
<tbody></tbody>
<tbody></tbody>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td colspan="2">4</td>
<td rowspan="2">5</td>
</tr>
<tr>
<td>6</td>
</tr>
<tr>
<td colspan="3">7</td>
</tr>
</tbody>
</table>
</body>
</html>

Просмотреть файл

@ -66,10 +66,10 @@
"invalid": "spelling"
};
testTextAttrs(ID, 0, attrs, 0, 11);
testTextAttrs(ID, 11, misspelledAttrs, 11, 17);
testTextAttrs(ID, 17, attrs, 17, 18);
testTextAttrs(ID, 18, misspelledAttrs, 18, 22);
testTextAttrs(ID, 0, attrs, defAttrs, 0, 11);
testTextAttrs(ID, 11, misspelledAttrs, defAttrs, 11, 17);
testTextAttrs(ID, 17, attrs, defAttrs, 17, 18);
testTextAttrs(ID, 18, misspelledAttrs, defAttrs, 18, 22);
is(gTextAttrChangedEventHandler.eventNumber, 2,
"Wrong count of 'text attribute changed' events for " + ID);
@ -101,15 +101,15 @@
testDefaultTextAttrs(ID, defAttrs);
var attrs = {};
testTextAttrs(ID, 0, attrs, 0, 7);
testTextAttrs(ID, 0, attrs, defAttrs, 0, 7);
tempElem = tempElem.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"font-weight": gComputedStyle.fontWeight};
testTextAttrs(ID, 7, attrs, 7, 11);
testTextAttrs(ID, 7, attrs, defAttrs, 7, 11);
attrs = {};
testTextAttrs(ID, 12, attrs, 11, 18);
testTextAttrs(ID, 12, attrs, defAttrs, 11, 18);
//////////////////////////////////////////////////////////////////////////
// area2
@ -129,26 +129,26 @@
testDefaultTextAttrs(ID, defAttrs);
attrs = {};
testTextAttrs(ID, 0, attrs, 0, 7);
testTextAttrs(ID, 0, attrs, defAttrs, 0, 7);
tempElem = tempElem.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"font-weight": gComputedStyle.fontWeight};
testTextAttrs(ID, 7, attrs, 7, 12);
testTextAttrs(ID, 7, attrs, defAttrs, 7, 12);
tempElem = tempElem.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"font-style": gComputedStyle.fontStyle,
"font-weight": gComputedStyle.fontWeight};
testTextAttrs(ID, 13, attrs, 12, 19);
testTextAttrs(ID, 13, attrs, defAttrs, 12, 19);
tempElem = tempElem.parentNode;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"font-weight": "401"};
testTextAttrs(ID, 20, attrs, 19, 23);
testTextAttrs(ID, 20, attrs, defAttrs, 19, 23);
attrs = {};
testTextAttrs(ID, 24, attrs, 23, 30);
testTextAttrs(ID, 24, attrs, defAttrs, 23, 30);
//////////////////////////////////////////////////////////////////////////
// area3
@ -170,23 +170,23 @@
tempElem = tempElem.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 0, attrs, 0, 6);
testTextAttrs(ID, 0, attrs, defAttrs, 0, 6);
tempElem = tempElem.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 6, attrs, 6, 26);
testTextAttrs(ID, 6, attrs, defAttrs, 6, 26);
tempElem = tempElem.parentNode;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 26, attrs, 26, 27);
testTextAttrs(ID, 26, attrs, defAttrs, 26, 27);
tempElem = tempElem.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color,
"background-color": gComputedStyle.backgroundColor};
testTextAttrs(ID, 27, attrs, 27, 50);
testTextAttrs(ID, 27, attrs, defAttrs, 27, 50);
//////////////////////////////////////////////////////////////////////////
// area4
@ -208,17 +208,17 @@
tempElem = tempElem.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 0, attrs, 0, 16);
testTextAttrs(ID, 0, attrs, defAttrs, 0, 16);
tempElem = tempElem.nextSibling.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 16, attrs, 16, 33);
testTextAttrs(ID, 16, attrs, defAttrs, 16, 33);
tempElem = tempElem.parentNode;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 34, attrs, 33, 46);
testTextAttrs(ID, 34, attrs, defAttrs, 33, 46);
//////////////////////////////////////////////////////////////////////////
// area5
@ -240,18 +240,18 @@
tempElem = tempElem.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 0, attrs, 0, 5);
testTextAttrs(ID, 0, attrs, defAttrs, 0, 5);
attrs = {};
testTextAttrs(ID, 7, attrs, 5, 8);
testTextAttrs(ID, 7, attrs, defAttrs, 5, 8);
tempElem = tempElem.nextSibling.nextSibling.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 9, attrs, 8, 11);
testTextAttrs(ID, 9, attrs, defAttrs, 8, 11);
attrs = {};
testTextAttrs(ID, 11, attrs, 11, 18);
testTextAttrs(ID, 11, attrs, defAttrs, 11, 18);
//////////////////////////////////////////////////////////////////////////
// area6 (CSS vertical-align property, bug 445938)
@ -271,44 +271,165 @@
testDefaultTextAttrs(ID, defAttrs);
attrs = {};
testTextAttrs(ID, 0, attrs, 0, 5);
testTextAttrs(ID, 0, attrs, defAttrs, 0, 5);
tempElem = tempElem.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"text-position": gComputedStyle.verticalAlign,
"font-size": "10pt"};
testTextAttrs(ID, 5, attrs, 5, 13);
testTextAttrs(ID, 5, attrs, defAttrs, 5, 13);
attrs = {};
testTextAttrs(ID, 13, attrs, 13, 27);
testTextAttrs(ID, 13, attrs, defAttrs, 13, 27);
tempElem = tempElem.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"text-position": gComputedStyle.verticalAlign};
testTextAttrs(ID, 27, attrs, 27, 35);
testTextAttrs(ID, 27, attrs, defAttrs, 27, 35);
attrs = {};
testTextAttrs(ID, 35, attrs, 35, 39);
testTextAttrs(ID, 35, attrs, defAttrs, 35, 39);
tempElem = tempElem.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"text-position": gComputedStyle.verticalAlign,
"font-size": "10pt"};
testTextAttrs(ID, 39, attrs, 39, 50);
testTextAttrs(ID, 39, attrs, defAttrs, 39, 50);
attrs = {};
testTextAttrs(ID, 50, attrs, 50, 55);
testTextAttrs(ID, 50, attrs, defAttrs, 50, 55);
tempElem = tempElem.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"text-position": gComputedStyle.verticalAlign};
testTextAttrs(ID, 55, attrs, 55, 64);
testTextAttrs(ID, 55, attrs, defAttrs, 55, 64);
//////////////////////////////////////////////////////////////////////////
// area7
ID = "area7";
tempElem = document.getElementById(ID);
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
defAttrs = {
"language": "en",
"font-style": gComputedStyle.fontStyle,
"font-size": "12pt",
"background-color": "rgb(255, 255, 255)",
"font-weight": gComputedStyle.fontWeight,
"color": gComputedStyle.color,
"font-family": gComputedStyle.fontFamily,
"text-position": gComputedStyle.verticalAlign
};
testDefaultTextAttrs(ID, defAttrs);
attrs = {"language": "ru"};
testTextAttrs(ID, 0, attrs, defAttrs, 0, 12);
attrs = {};
testTextAttrs(ID, 12, attrs, defAttrs, 12, 13);
tempElem = tempElem.firstChild.nextSibling.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = { "background-color": gComputedStyle.backgroundColor};
testTextAttrs(ID, 13, attrs, defAttrs, 13, 26);
attrs = {};
testTextAttrs(ID, 26, attrs, defAttrs, 26, 27);
attrs = {"language": "de"};
testTextAttrs(ID, 27, attrs, defAttrs, 27, 42);
attrs = {};
testTextAttrs(ID, 42, attrs, defAttrs, 42, 50);
attrs = {};
testTextAttrs(ID, 43, attrs, defAttrs, 42, 50);
tempElem = tempElem.nextSibling.nextSibling.nextSibling.nextSibling.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 50, attrs, defAttrs, 50, 57);
tempElem = tempElem.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"font-weight": gComputedStyle.fontWeight,
"color": gComputedStyle.color};
testTextAttrs(ID, 57, attrs, defAttrs, 57, 61);
tempElem = tempElem.parentNode;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 61, attrs, defAttrs, 61, 68);
//////////////////////////////////////////////////////////////////////////
// area9, different single style spans in styled paragraph
ID = "area9";
tempElem = document.getElementById(ID);
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
defAttrs = {
"font-style": gComputedStyle.fontStyle,
"font-size": "10pt",
"background-color": "rgb(255, 255, 255)",
"font-weight": gComputedStyle.fontWeight,
"color": gComputedStyle.color,
"font-family": gComputedStyle.fontFamily,
"text-position": gComputedStyle.verticalAlign
};
testDefaultTextAttrs(ID, defAttrs);
attrs = {};
testTextAttrs(ID, 0, attrs, defAttrs, 0, 6);
attrs = { "font-size": "12pt" };
testTextAttrs(ID, 7, attrs, defAttrs, 6, 12);
attrs = {};
testTextAttrs(ID, 13, attrs, defAttrs, 12, 21);
// Walk to the span with the different background color
tempElem = tempElem.firstChild.nextSibling.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = { "background-color": gComputedStyle.backgroundColor };
testTextAttrs(ID, 22, attrs, defAttrs, 21, 36);
attrs = {};
testTextAttrs(ID, 37, attrs, defAttrs, 36, 44);
// Walk from the background color span to the one with font-style
tempElem = tempElem.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = { "font-style": gComputedStyle.fontStyle };
testTextAttrs(ID, 45, attrs, defAttrs, 44, 61);
attrs = {};
testTextAttrs(ID, 62, attrs, defAttrs, 61, 69);
// Walk from span with font-style to the one with font-family.
tempElem = tempElem.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = { "font-family": gComputedStyle.fontFamily };
testTextAttrs(ID, 70, attrs, defAttrs, 69, 83);
attrs = {};
testTextAttrs(ID, 84, attrs, defAttrs, 83, 91);
attrs = { "text-underline-style": "solid" };
testTextAttrs(ID, 92, attrs, defAttrs, 91, 101);
attrs = {};
testTextAttrs(ID, 102, attrs, defAttrs, 101, 109);
attrs = { "text-line-through-style": "solid" };
testTextAttrs(ID, 110, attrs, defAttrs, 109, 122);
attrs = {};
testTextAttrs(ID, 123, attrs, defAttrs, 122, 130);
// area10, different single style spans in non-styled paragraph
ID = "area10";
tempElem = document.getElementById(ID);
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
defAttrs = {
"font-style": gComputedStyle.fontStyle,
"font-size": "12pt",
@ -321,45 +442,53 @@
testDefaultTextAttrs(ID, defAttrs);
attrs = {"language": "ru"};
testTextAttrs(ID, 0, attrs, 0, 12);
attrs = {};
testTextAttrs(ID, 0, attrs, defAttrs, 0, 7);
attrs = {"language": "en"};
testTextAttrs(ID, 12, attrs, 12, 13);
tempElem = tempElem.firstChild.nextSibling.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"language" :"en",
"background-color": gComputedStyle.backgroundColor};
testTextAttrs(ID, 13, attrs, 13, 26);
attrs = {"language": "en" };
testTextAttrs(ID, 26, attrs, 26, 27);
attrs = {"language": "de"};
testTextAttrs(ID, 27, attrs, 27, 42);
attrs = {"language": "en"};
testTextAttrs(ID, 42, attrs, 42, 43);
attrs = { "font-size": "14pt" };
testTextAttrs(ID, 7, attrs, defAttrs, 7, 13);
attrs = {};
testTextAttrs(ID, 43, attrs, 43, 50);
testTextAttrs(ID, 13, attrs, defAttrs, 13, 22);
tempElem = tempElem.nextSibling.nextSibling.nextSibling.nextSibling.firstChild.nextSibling;
// Walk to the span with the different background color
tempElem = tempElem.firstChild.nextSibling.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 50, attrs, 50, 57);
attrs = { "background-color": gComputedStyle.backgroundColor };
testTextAttrs(ID, 23, attrs, defAttrs, 22, 37);
tempElem = tempElem.firstChild.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"font-weight": gComputedStyle.fontWeight,
"color": gComputedStyle.color};
testTextAttrs(ID, 57, attrs, 57, 61);
attrs = {};
testTextAttrs(ID, 38, attrs, defAttrs, 37, 45);
tempElem = tempElem.parentNode;
// Walk from the background color span to the one with font-style
tempElem = tempElem.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 61, attrs, 61, 68);
attrs = {"font-style": gComputedStyle.fontStyle};
testTextAttrs(ID, 46, attrs, defAttrs, 45, 62);
attrs = {};
testTextAttrs(ID, 63, attrs, defAttrs, 62, 70);
// Walk from span with font-style to the one with font-family.
tempElem = tempElem.nextSibling.nextSibling;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"font-family": gComputedStyle.fontFamily};
testTextAttrs(ID, 71, attrs, defAttrs, 70, 84);
attrs = {};
testTextAttrs(ID, 85, attrs, defAttrs, 84, 92);
attrs = { "text-underline-style": "solid" };
testTextAttrs(ID, 93, attrs, defAttrs, 92, 102);
attrs = {};
testTextAttrs(ID, 103, attrs, defAttrs, 102, 110);
attrs = { "text-line-through-style": "solid" };
testTextAttrs(ID, 111, attrs, defAttrs, 110, 123);
attrs = {};
testTextAttrs(ID, 124, attrs, defAttrs, 123, 131);
//////////////////////////////////////////////////////////////////////////
// test spelling text attributes
@ -425,6 +554,22 @@
<input id="area8"/>
<p id="output"/>
<p id="area9" style="font-size: smaller">Small
<span style="font-size: 120%">bigger</span> smaller
<span style="background-color: blue;">background blue</span> normal
<span style="font-style: italic;">Different styling</span> normal
<span style="font-family: tahoma;">Different font</span> normal
<span style="text-decoration: underline;">underlined</span> normal
<span style="text-decoration: line-through;">strikethrough</span> normal
</p>
<p id="area10">Normal
<span style="font-size: 120%">bigger</span> smaller
<span style="background-color: blue;">background blue</span> normal
<span style="font-style: italic;">Different styling</span> normal
<span style="font-family: tahoma;">Different font</span> normal
<span style="text-decoration: underline;">underlined</span> normal
<span style="text-decoration: line-through;">strikethrough</span> normal
</p>
</body>
</html>

Просмотреть файл

@ -0,0 +1,11 @@
<html>
<!--
Auxilliary file used as frame source.
-->
<head>
</head>
<body>
<p>Frame source body has no role</p>
</body>
</html>

Просмотреть файл

@ -0,0 +1,11 @@
<html>
<!--
Auxilliary file used as frame source.
-->
<head>
</head>
<body role="article">
<p>Article</p>
</body>
</html>

Просмотреть файл

@ -0,0 +1,11 @@
<html>
<!--
Auxilliary file used as frame source.
-->
<head>
</head>
<body role="checkbox">
<p>Checkbox</p>
</body>
</html>

Просмотреть файл

@ -0,0 +1,11 @@
<html>
<!--
Auxilliary file used as frame source.
-->
<head>
</head>
<body role="textbox">
<p>Texbox</p>
</body>
</html>

Просмотреть файл

@ -3,6 +3,9 @@ pref("startup.homepage_welcome_url","http://www.mozilla.org/projects/%APP%/%VERS
// The time interval between checks for a new version (in seconds)
// nightly=8 hours, official=24 hours
pref("app.update.interval", 28800);
// The time interval between the downloading of mar file chunks in the
// background (in seconds)
pref("app.update.download.backgroundInterval", 60);
// URL user can browse to manually if for some reason all update installation
// attempts fail.
pref("app.update.url.manual", "http://www.mozilla.org/products/%APP%/");

Просмотреть файл

@ -20,6 +20,7 @@
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Robert Strong <robert.bugzilla@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
@ -54,9 +55,15 @@ endif
include $(topsrcdir)/config/rules.mk
PRE_RELEASE_SUFFIX = $(shell cat $(srcdir)/../config/version.txt | \
sed -e '/pre/s/.*//g' -e '/[ab][0-9]/!s/.*//g' \
-e 's/\(.*[0-9]\)a\([0-9]\+\)/ \1 Alpha \2/g' \
-e 's/\(.*[0-9]\)b\([0-9]\+\)/ \1 Beta \2/g')
DEFINES += \
-DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \
-DAPP_LICENSE_BLOCK=$(abs_srcdir)/content/overrides/app-license.html \
-DPRE_RELEASE_SUFFIX="$(PRE_RELEASE_SUFFIX)" \
$(NULL)
ifndef MOZ_BRANDING_DIRECTORY

Просмотреть файл

@ -1155,6 +1155,10 @@ function prepareForStartup() {
// setup our common DOMLinkAdded listener
gBrowser.addEventListener("DOMLinkAdded", DOMLinkHandler, false);
// setup our MozApplicationManifest listener
gBrowser.addEventListener("MozApplicationManifest",
OfflineApps, false);
// setup simple gestures support
gGestureSupport.init(true);
}
@ -2903,6 +2907,9 @@ const BrowserSearch = {
return;
var browser = gBrowser.getBrowserForDocument(targetDoc);
// ignore search engines from subframes (see bug 479408)
if (!browser)
return;
// Check to see whether we've already added an engine with this title
if (browser.engines) {
@ -2914,8 +2921,8 @@ const BrowserSearch = {
// Use documentURIObject in the check for shouldLoadFavIcon so that we
// do the right thing with about:-style error pages. Bug 453442
var iconURL = null;
if (gBrowser.shouldLoadFavIcon(browser.contentDocument.documentURIObject))
iconURL = browser.currentURI.prePath + "/favicon.ico";
if (gBrowser.shouldLoadFavIcon(targetDoc.documentURIObject))
iconURL = targetDoc.documentURIObject.prePath + "/favicon.ico";
var hidden = false;
// If this engine (identified by title) is already in the list, add it
@ -3872,10 +3879,6 @@ var XULBrowserWindow = {
this.endDocumentLoad(aRequest, aStatus);
if (!gBrowser.mTabbedMode && !gBrowser.mCurrentBrowser.mIconURL)
gBrowser.useDefaultIcon(gBrowser.mCurrentTab);
if (Components.isSuccessCode(aStatus) &&
content.document.documentElement.getAttribute("manifest"))
OfflineApps.offlineAppRequested(content);
}
}
@ -4909,7 +4912,7 @@ var contentAreaDNDObserver = {
var dragType = aXferData.flavour.contentType;
var dragData = aXferData.data;
if (dragType == "application/x-moz-tabbrowser-tab") {
if (dragType == TAB_DROP_TYPE) {
// If the tab was dragged from some other tab bar, its own dragend
// handler will take care of detaching the tab
if (dragData instanceof XULElement && dragData.localName == "tab" &&
@ -4958,7 +4961,7 @@ var contentAreaDNDObserver = {
getSupportedFlavours: function ()
{
var flavourSet = new FlavourSet();
flavourSet.appendFlavour("application/x-moz-tabbrowser-tab");
flavourSet.appendFlavour(TAB_DROP_TYPE);
flavourSet.appendFlavour("text/x-moz-url");
flavourSet.appendFlavour("text/plain");
flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
@ -5331,6 +5334,12 @@ var OfflineApps = {
obs.removeObserver(this, "offline-cache-update-completed");
},
handleEvent: function(event) {
if (event.type == "MozApplicationManifest") {
this.offlineAppRequested(event.originalTarget.defaultView);
}
},
/////////////////////////////////////////////////////////////////////////////
// OfflineApps Implementation Methods
@ -5357,6 +5366,7 @@ var OfflineApps = {
},
_getManifestURI: function(aWindow) {
if (!aWindow.document.documentElement) return null;
var attr = aWindow.document.documentElement.getAttribute("manifest");
if (!attr) return null;
@ -5426,10 +5436,25 @@ var OfflineApps = {
},
// XXX: duplicated in preferences/advanced.js
_getOfflineAppUsage: function (host)
_getOfflineAppUsage: function (host, groups)
{
// XXX Bug 442810: include offline cache usage.
var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
getService(Components.interfaces.nsIApplicationCacheService);
if (!groups) {
groups = cacheService.getGroups({});
}
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var usage = 0;
for (var i = 0; i < groups.length; i++) {
var uri = ios.newURI(groups[i], null, null);
if (uri.asciiHost == host) {
var cache = cacheService.getActiveCache(groups[i]);
usage += cache.usage;
}
}
var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"].
getService(Components.interfaces.nsIDOMStorageManager);
usage += storageManager.getUsage(host);
@ -5463,7 +5488,7 @@ var OfflineApps = {
var browser = this._getBrowserForContentWindow(browserWindow,
aContentWindow);
var currentURI = browser.webNavigation.currentURI;
var currentURI = aContentWindow.document.documentURIObject;
var pm = Cc["@mozilla.org/permissionmanager;1"].
getService(Ci.nsIPermissionManager);
@ -5481,19 +5506,32 @@ var OfflineApps = {
// this pref isn't set by default, ignore failures
}
var host = currentURI.asciiHost;
var notificationBox = gBrowser.getNotificationBox(browser);
var notification = notificationBox.getNotificationWithValue("offline-app-requested");
if (!notification) {
var notificationID = "offline-app-requested-" + host;
var notification = notificationBox.getNotificationWithValue(notificationID);
if (notification) {
notification.documents.push(aContentWindow.document);
} else {
var bundle_browser = document.getElementById("bundle_browser");
var buttons = [{
label: bundle_browser.getString("offlineApps.allow"),
accessKey: bundle_browser.getString("offlineApps.allowAccessKey"),
callback: function() { OfflineApps.allowSite(); }
callback: function() {
for (var i = 0; i < notification.documents.length; i++) {
OfflineApps.allowSite(notification.documents[i]);
}
}
},{
label: bundle_browser.getString("offlineApps.never"),
accessKey: bundle_browser.getString("offlineApps.neverAccessKey"),
callback: function() { OfflineApps.disallowSite(); }
callback: function() {
for (var i = 0; i < notification.documents.length; i++) {
OfflineApps.disallowSite(notification.documents[i]);
}
}
},{
label: bundle_browser.getString("offlineApps.notNow"),
accessKey: bundle_browser.getString("offlineApps.notNowAccessKey"),
@ -5502,51 +5540,55 @@ var OfflineApps = {
const priority = notificationBox.PRIORITY_INFO_LOW;
var message = bundle_browser.getFormattedString("offlineApps.available",
[ currentURI.host ]);
notificationBox.appendNotification(message, "offline-app-requested",
"chrome://browser/skin/Info.png",
priority, buttons);
[ host ]);
notification =
notificationBox.appendNotification(message, notificationID,
"chrome://browser/skin/Info.png",
priority, buttons);
notification.documents = [ aContentWindow.document ];
}
},
allowSite: function() {
var currentURI = gBrowser.selectedBrowser.webNavigation.currentURI;
allowSite: function(aDocument) {
var pm = Cc["@mozilla.org/permissionmanager;1"].
getService(Ci.nsIPermissionManager);
pm.add(currentURI, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
pm.add(aDocument.documentURIObject, "offline-app",
Ci.nsIPermissionManager.ALLOW_ACTION);
// When a site is enabled while loading, <link rel="offline-resource">
// resources will start fetching immediately. This one time we need to
// do it ourselves.
this._startFetching();
// When a site is enabled while loading, manifest resources will
// start fetching immediately. This one time we need to do it
// ourselves.
this._startFetching(aDocument);
},
disallowSite: function() {
var currentURI = gBrowser.selectedBrowser.webNavigation.currentURI;
disallowSite: function(aDocument) {
var pm = Cc["@mozilla.org/permissionmanager;1"].
getService(Ci.nsIPermissionManager);
pm.add(currentURI, "offline-app", Ci.nsIPermissionManager.DENY_ACTION);
pm.add(aDocument.documentURIObject, "offline-app",
Ci.nsIPermissionManager.DENY_ACTION);
},
manage: function() {
openAdvancedPreferences("networkTab");
},
_startFetching: function() {
var manifest = content.document.documentElement.getAttribute("manifest");
_startFetching: function(aDocument) {
if (!aDocument.documentElement)
return;
var manifest = aDocument.documentElement.getAttribute("manifest");
if (!manifest)
return;
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var contentURI = ios.newURI(content.location.href, null, null);
var manifestURI = ios.newURI(manifest, content.document.characterSet,
contentURI);
var manifestURI = ios.newURI(manifest, aDocument.characterSet,
aDocument.documentURIObject);
var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
updateService.scheduleUpdate(manifestURI, contentURI);
updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject);
},
/////////////////////////////////////////////////////////////////////////////
@ -6090,7 +6132,7 @@ var FeedHandler = {
// find which tab this is for, and set the attribute on the browser
var browserForLink = gBrowser.getBrowserForDocument(targetDoc);
if (!browserForLink) {
// ??? this really shouldn't happen..
// ignore feeds loaded in subframes (see bug 305472)
return;
}
@ -6100,7 +6142,7 @@ var FeedHandler = {
feeds.push(feed);
browserForLink.feeds = feeds;
if (browserForLink == gBrowser || browserForLink == gBrowser.mCurrentBrowser) {
if (browserForLink == gBrowser.mCurrentBrowser) {
var feedButton = document.getElementById("feed-button");
if (feedButton)
feedButton.collapsed = false;
@ -6868,6 +6910,8 @@ let gPrivateBrowsingUI = {
onEnterPrivateBrowsing: function PBUI_onEnterPrivateBrowsing() {
this._setPBMenuTitle("stop");
document.getElementById("menu_import").setAttribute("disabled", "true");
this._privateBrowsingAutoStarted = this._privateBrowsingService.autoStarted;
if (!this._privateBrowsingAutoStarted) {
@ -6893,6 +6937,8 @@ let gPrivateBrowsingUI = {
if (BrowserSearch.searchBar)
BrowserSearch.searchBar.textbox.reset();
document.getElementById("menu_import").removeAttribute("disabled");
gFindBar.getElement("findbar-textbox").reset();
this._setPBMenuTitle("start");

Просмотреть файл

@ -1,3 +1,4 @@
#filter substitution
<?xml version="1.0"?>
# -*- Mode: HTML -*-
#
@ -31,6 +32,7 @@
# Johnathan Nightingale <johnath@mozilla.com>
# Dão Gottwald <dao@mozilla.com>
# Ehsan Akhgari <ehsan.akhgari@gmail.com>
# Robert Strong <robert.bugzilla@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
@ -48,10 +50,7 @@
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://global/skin/toolbar.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
<?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
@ -68,19 +67,19 @@
onload="BrowserStartup()" onunload="BrowserShutdown()" onclose="return WindowIsClosing();"
ondragleave="gBrowser.browserWindowOnDragLeave(event);"
ondragenter="gBrowser.browserWindowOnDragEnter(event);"
title="&mainWindow.title;"
title_normal="&mainWindow.title;"
title="&mainWindow.title;@PRE_RELEASE_SUFFIX@"
title_normal="&mainWindow.title;@PRE_RELEASE_SUFFIX@"
#ifdef XP_MACOSX
title_privatebrowsing="&mainWindow.title;&mainWindow.titlemodifiermenuseparator;&mainWindow.titlePrivateBrowsingSuffix;"
titledefault="&mainWindow.title;"
title_privatebrowsing="&mainWindow.title;@PRE_RELEASE_SUFFIX@&mainWindow.titlemodifiermenuseparator;&mainWindow.titlePrivateBrowsingSuffix;"
titledefault="&mainWindow.title;@PRE_RELEASE_SUFFIX@"
titlemodifier=""
titlemodifier_normal=""
titlemodifier_privatebrowsing="&mainWindow.titlePrivateBrowsingSuffix;"
#else
title_privatebrowsing="&mainWindow.titlemodifierPrivateBrowsing;"
titlemodifier="&mainWindow.titlemodifier;"
titlemodifier_normal="&mainWindow.titlemodifier;"
titlemodifier_privatebrowsing="&mainWindow.titlemodifierPrivateBrowsing;"
title_privatebrowsing="&mainWindow.titlemodifier;@PRE_RELEASE_SUFFIX@ &mainWindow.titlePrivateBrowsingSuffix;"
titlemodifier="&mainWindow.titlemodifier;@PRE_RELEASE_SUFFIX@"
titlemodifier_normal="&mainWindow.titlemodifier;@PRE_RELEASE_SUFFIX@"
titlemodifier_privatebrowsing="&mainWindow.titlemodifier;@PRE_RELEASE_SUFFIX@ &mainWindow.titlePrivateBrowsingSuffix;"
#endif
titlemenuseparator="&mainWindow.titlemodifiermenuseparator;"
windowtype="navigator:browser"

Просмотреть файл

@ -1712,6 +1712,7 @@
Components.interfaces.nsIWebProgress.NOTIFY_ALL);
this.setTabTitle(aOurTab);
this.updateIcon(aOurTab);
// If the tab was already selected (this happpens in the scenario
// of replaceTabWithWindow), notify onLocationChange, etc.
@ -1911,10 +1912,13 @@
if (target.localName == "tab" &&
aEvent.originalTarget.localName != "toolbarbutton") {
var dt = aEvent.dataTransfer;
dt.mozSetDataAt("application/x-moz-tabbrowser-tab", target, 0);
dt.mozSetDataAt(TAB_DROP_TYPE, target, 0);
var uri = this.getBrowserForTab(aEvent.target).currentURI;
var spec = uri ? uri.spec : "about:blank";
dt.mozSetDataAt("text/x-moz-url", spec + "\n" + aEvent.target.label, 0);
// We must not set text/x-moz-url data, otherwise trying to deatch
// the tab by dropping it on the desktop would result in an
// "internet shortcut"
dt.mozSetDataAt("text/plain", spec, 0);
var canvas = tabPreviews.capture(target, false);
@ -1946,8 +1950,8 @@
var types = dt.mozTypesAt(0);
var sourceNode = null;
// tabs are always added as the first type
if (types[0] == "application/x-moz-tabbrowser-tab") {
var sourceNode = dt.mozGetDataAt("application/x-moz-tabbrowser-tab", 0);
if (types[0] == TAB_DROP_TYPE) {
var sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
if (sourceNode instanceof XULElement &&
sourceNode.localName == "tab" &&
(sourceNode.parentNode == this.mTabContainer ||
@ -2085,7 +2089,7 @@
var dropEffect = dt.dropEffect;
var draggedTab;
if (dropEffect != "link") { // copy or move
draggedTab = dt.mozGetDataAt("application/x-moz-tabbrowser-tab", 0);
draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
// not our drop then
if (!draggedTab)
return;
@ -2208,7 +2212,7 @@
// 2. Drop on some other tabbar
// 3. Drop on text fields (even outside our window)
if (dt.dropEffect == "none") {
var draggedTab = dt.mozGetDataAt("application/x-moz-tabbrowser-tab", 0);
var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
this.replaceTabWithWindow(draggedTab);
}
aEvent.stopPropagation();
@ -2824,18 +2828,59 @@
return document.getBindingParent(this).childNodes;
]]></body>
</method>
#ifndef XP_MACOSX
</implementation>
#else
#ifdef XP_MACOSX
<field name="_scrollButtonDownBox">
document.getAnonymousElementByAttribute(this, "anonid", "down-box");
</field>
<field name="_scrollButtonDownBoxAnimate">
document.getAnonymousElementByAttribute(this, "anonid", "down-box-animate");
</field>
#endif
</implementation>
<handlers>
<handler event="underflow"><![CDATA[
if (event.detail == 0)
return; // Ignore vertical events
var tabs = document.getBindingParent(this);
tabs.removeAttribute("overflow");
#ifdef XP_MACOSX
this._scrollButtonDownBox.collapsed = true;
this._scrollButtonDownBoxAnimate.collapsed = true;
#endif
]]></handler>
<handler event="overflow"><![CDATA[
if (event.detail == 0)
return; // Ignore vertical events
var tabs = document.getBindingParent(this);
tabs.setAttribute("overflow", "true");
#ifdef XP_MACOSX
this._scrollButtonDownBox.collapsed = false;
this._scrollButtonDownBoxAnimate.collapsed = false;
#endif
this.scrollBoxObject.ensureElementIsVisible(tabs.selectedItem);
]]></handler>
#ifdef XP_MACOSX
<handler event="UpdatedScrollButtonsDisabledState"><![CDATA[
// fix for bug #352353
// unlike the scrollup button on the tab strip (which is a
// simple toolbarbutton) the scrolldown button is
// a more complicated stack of boxes and a toolbarbutton
// so that we can animate when a tab is opened offscreen.
// in order to style the box with the actual background image
// we need to manually set the disable state to match the
// disable state of the toolbarbutton.
this._scrollButtonDownBox
.setAttribute("disabled", this._scrollButtonDown.disabled);
]]></handler>
#endif
</handlers>
#ifdef XP_MACOSX
<content>
<xul:toolbarbutton class="scrollbutton-up" collapsed="true"
xbl:inherits="orient"
@ -2865,54 +2910,6 @@
chromedir="&locale.dir;"/>
</xul:stack>
</content>
<handlers>
<handler event="underflow"><![CDATA[
// filter underflow events which were dispatched on nested scrollboxes
if (event.target != this)
return;
// Ignore vertical events.
if (event.detail == 0) {
return;
}
this._scrollButtonDownBox.collapsed = true;
this._scrollButtonDownBoxAnimate.collapsed = true;
]]></handler>
<handler event="overflow"><![CDATA[
// filter underflow events which were dispatched on nested scrollboxes
if (event.target != this)
return;
// Ignore vertical events.
if (event.detail == 0) {
return;
}
this._scrollButtonDownBox.collapsed = false;
this._scrollButtonDownBoxAnimate.collapsed = false;
]]></handler>
<handler event="UpdatedScrollButtonsDisabledState"><![CDATA[
// filter underflow events which were dispatched on nested scrollboxes
if (event.target != this)
return;
// fix for bug #352353
// unlike the scrollup button on the tab strip (which is a
// simple toolbarbutton) the scrolldown button is
// a more complicated stack of boxes and a toolbarbutton
// so that we can animate when a tab is opened offscreen.
// in order to style the box with the actual background image
// we need to manually set the disable state to match the
// disable state of the toolbarbutton.
this._scrollButtonDownBox
.setAttribute("disabled", this._scrollButtonDown.disabled);
]]></handler>
</handlers>
#endif
</binding>
@ -2998,14 +2995,6 @@
this._prefObserver, false);
window.addEventListener("resize", this, false);
// Listen to overflow/underflow events on the tabstrip,
// we cannot put these as xbl handlers on the entire binding because
// they would also get called for the all-tabs popup scrollbox.
// Also, we can't rely on event.target becuase these are all
// anonymous nodes.
this.mTabstrip.addEventListener("overflow", this, false);
this.mTabstrip.addEventListener("underflow", this, false);
]]>
</constructor>
@ -3021,9 +3010,6 @@
this._animateTimer.cancel();
this._animateTimer = null;
}
this.mTabstrip.removeEventListener("overflow", this, false);
this.mTabstrip.removeEventListener("underflow", this, false);
]]>
</destructor>
@ -3145,14 +3131,6 @@
<parameter name="aEvent"/>
<body><![CDATA[
switch (aEvent.type) {
case "overflow":
this.setAttribute("overflow", "true");
this.mTabstrip.scrollBoxObject
.ensureElementIsVisible(this.selectedItem);
break;
case "underflow":
this.removeAttribute("overflow");
break;
case "resize":
var width = this.mTabstrip.boxObject.width;
if (width != this.mTabstripWidth) {

Просмотреть файл

@ -50,6 +50,13 @@ _TEST_FILES = test_feed_discovery.html \
test_contextmenu.html \
subtst_contextmenu.html \
ctxmenu-image.png \
test_offlineNotification.html \
offlineChild.html \
offlineChild.cacheManifest \
offlineChild.cacheManifest^headers^ \
offlineChild2.html \
offlineChild2.cacheManifest \
offlineChild2.cacheManifest^headers^ \
$(NULL)
# browser_bug423833.js disabled temporarily since it's unreliable: bug 428712
@ -89,6 +96,8 @@ _BROWSER_FILES = browser_bug321000.js \
browser_sanitize-sitepermissions.js \
browser_bug356571.js \
browser_sanitize-passwordDisabledHosts.js \
browser_bug479408.js \
browser_bug479408_sample.html \
$(NULL)
ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))

Просмотреть файл

@ -3,6 +3,10 @@ const Ci = Components.interfaces;
const gCompleteState = Ci.nsIWebProgressListener.STATE_STOP +
Ci.nsIWebProgressListener.STATE_IS_NETWORK;
function LOG(str) {
dump(str + "\n");
}
var gFrontProgressListener = {
onProgressChange: function (aWebProgress, aRequest,
aCurSelfProgress, aMaxSelfProgress,
@ -11,6 +15,7 @@ var gFrontProgressListener = {
onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
var state = "onStateChange";
LOG("FrontProgress: " + state + " 0x" + aStateFlags.toString(16));
ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener");
is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener");
gFrontNotificationsPos++;
@ -18,6 +23,7 @@ var gFrontProgressListener = {
onLocationChange: function (aWebProgress, aRequest, aLocationURI) {
var state = "onLocationChange";
LOG("FrontProgress: " + state + " " + aLocationURI.spec);
ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener");
is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener");
gFrontNotificationsPos++;
@ -28,6 +34,7 @@ var gFrontProgressListener = {
onSecurityChange: function (aWebProgress, aRequest, aState) {
var state = "onSecurityChange";
LOG("FrontProgress: " + state + " 0x" + aState.toString(16));
ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener");
is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener");
gFrontNotificationsPos++;
@ -42,6 +49,7 @@ var gAllProgressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
var state = "onStateChange";
LOG("AllProgress: " + state + " 0x" + aStateFlags.toString(16));
ok(aBrowser == gTestBrowser, state + " notification came from the correct browser");
ok(gAllNotificationsPos < gAllNotifications.length, "Got an expected notification for the all notifications listener");
is(state, gAllNotifications[gAllNotificationsPos], "Got a notification for the all notifications listener");
@ -56,6 +64,7 @@ var gAllProgressListener = {
onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI) {
var state = "onLocationChange";
LOG("AllProgress: " + state + " " + aLocationURI.spec);
ok(aBrowser == gTestBrowser, state + " notification came from the correct browser");
ok(gAllNotificationsPos < gAllNotifications.length, "Got an expected notification for the all notifications listener");
is(state, gAllNotifications[gAllNotificationsPos], "Got a notification for the all notifications listener");
@ -69,6 +78,7 @@ var gAllProgressListener = {
onSecurityChange: function (aBrowser, aWebProgress, aRequest, aState) {
var state = "onSecurityChange";
LOG("AllProgress: " + state + " 0x" + aState.toString(16));
ok(aBrowser == gTestBrowser, state + " notification came from the correct browser");
ok(gAllNotificationsPos < gAllNotifications.length, "Got an expected notification for the all notifications listener");
is(state, gAllNotifications[gAllNotificationsPos], "Got a notification for the all notifications listener");
@ -82,6 +92,7 @@ var gTestPage = "/browser/browser/base/content/test/alltabslistener.html";
var gNextTest;
function test() {
LOG("Running tests from alltabslistener.js");
waitForExplicitFinish();
gBackgroundTab = gBrowser.addTab("about:blank");
@ -109,6 +120,7 @@ function startTests() {
}
function startTest1() {
LOG("\nTest 1");
gBrowser.addProgressListener(gFrontProgressListener);
gBrowser.addTabsProgressListener(gAllProgressListener);
@ -123,6 +135,7 @@ function startTest1() {
}
function startTest2() {
LOG("\nTest 2");
gAllNotifications = [
"onStateChange",
"onLocationChange",
@ -135,6 +148,7 @@ function startTest2() {
}
function startTest3() {
LOG("\nTest 3");
gAllNotifications = [
"onStateChange",
"onLocationChange",
@ -146,6 +160,7 @@ function startTest3() {
}
function startTest4() {
LOG("\nTest 4");
gAllNotifications = [
"onStateChange",
"onLocationChange",
@ -158,6 +173,7 @@ function startTest4() {
}
function startTest5() {
LOG("\nTest 5");
// Switch the foreground browser
[gForegroundBrowser, gBackgroundBrowser] = [gBackgroundBrowser, gForegroundBrowser];
[gForegroundTab, gBackgroundTab] = [gBackgroundTab, gForegroundTab];
@ -177,6 +193,7 @@ function startTest5() {
}
function startTest6() {
LOG("\nTest 6");
gAllNotifications = [
"onStateChange",
"onLocationChange",
@ -188,6 +205,7 @@ function startTest6() {
}
function finishTest() {
LOG("\nFinished tests from alltabslistener.js");
gBrowser.removeProgressListener(gFrontProgressListener);
gBrowser.removeTabsProgressListener(gAllProgressListener);
gBrowser.removeTab(gBackgroundTab);

Просмотреть файл

@ -0,0 +1,17 @@
function test() {
waitForExplicitFinish();
let tab = gBrowser.selectedTab = gBrowser.addTab(
"http://localhost:8888/browser/browser/base/content/test/browser_bug479408_sample.html");
gBrowser.addEventListener("DOMLinkAdded", function(aEvent) {
gBrowser.removeEventListener("DOMLinkAdded", arguments.callee, true);
executeSoon(function() {
ok(!tab.linkedBrowser.engines,
"the subframe's search engine wasn't detected");
gBrowser.removeTab(tab);
finish();
});
}, true);
}

Просмотреть файл

@ -0,0 +1,4 @@
<!DOCTYPE html>
<title>Testcase for bug 479408</title>
<iframe src='data:text/html,<link%20rel="search"%20type="application/opensearchdescription+xml"%20title="Search%20bug%20479408"%20href="http://example.com/search.xml">'>

Просмотреть файл

@ -85,6 +85,15 @@ function test_gestureListener(evt)
"evt.type (" + evt.type + ") does not match expected value");
is(evt.target, test_utils.elementFromPoint(20, 20, false, false),
"evt.target (" + evt.target + ") does not match expected value");
is(evt.clientX, 20,
"evt.clientX (" + evt.clientX + ") does not match expected value");
is(evt.clientY, 20,
"evt.clientY (" + evt.clientY + ") does not match expected value");
isnot(evt.screenX, 0,
"evt.screenX (" + evt.screenX + ") does not match expected value");
isnot(evt.screenY, 0,
"evt.screenY (" + evt.screenY + ") does not match expected value");
is(evt.direction, test_expectedDirection,
"evt.direction (" + evt.direction + ") does not match expected value");
is(evt.delta, test_expectedDelta,
@ -187,8 +196,11 @@ function test_helper2(type, direction, delta, altKey, ctrlKey, shiftKey, metaKey
ok(successful, "Unable to create SimpleGestureEvent");
try {
event.initSimpleGestureEvent(type, true, true, null, 0, direction,
delta, altKey, ctrlKey, shiftKey, metaKey);
event.initSimpleGestureEvent(type, true, true, window, 1,
10, 10, 10, 10,
ctrlKey, altKey, shiftKey, metaKey,
1, window,
direction, delta);
successful = true;
}
catch (ex) {
@ -204,6 +216,14 @@ function test_helper2(type, direction, delta, altKey, ctrlKey, shiftKey, metaKey
is(event.ctrlKey, ctrlKey, "Mismatch on evt.ctrlKey");
is(event.shiftKey, shiftKey, "Mismatch on evt.shiftKey");
is(event.metaKey, metaKey, "Mismatch on evt.metaKey");
is(event.view, window, "Mismatch on evt.view");
is(event.detail, 1, "Mismatch on evt.detail");
is(event.clientX, 10, "Mismatch on evt.clientX");
is(event.clientY, 10, "Mismatch on evt.clientY");
is(event.screenX, 10, "Mismatch on evt.screenX");
is(event.screenY, 10, "Mismatch on evt.screenY");
is(event.button, 1, "Mismatch on evt.button");
is(event.relatedTarget, window, "Mismatch on evt.relatedTarget");
// Test event dispatch
let expectedEventCount = test_eventCount + 1;

Просмотреть файл

@ -0,0 +1,2 @@
CACHE MANIFEST
offlineChild.html

Просмотреть файл

@ -1,2 +1 @@
Content-Type: text/cache-manifest
Cache-Control: no-cache

Просмотреть файл

@ -0,0 +1,20 @@
<html manifest="offlineChild.cacheManifest">
<head>
<title></title>
<script type="text/javascript">
function finish(success) {
window.parent.postMessage(success ? "success" : "failure", "*");
}
applicationCache.oncached = function() { finish(true); }
applicationCache.onnoupdate = function() { finish(true); }
applicationCache.onerror = function() { finish(false); }
</script>
</head>
<body>
<h1>Child</h1>
</body>
</html>

Просмотреть файл

@ -0,0 +1,2 @@
CACHE MANIFEST
offlineChild2.html

Просмотреть файл

@ -1,2 +1 @@
Cache-Control: no-cache
Content-Type: text/cache-manifest

Просмотреть файл

@ -0,0 +1,20 @@
<html manifest="offlineChild2.cacheManifest">
<head>
<title></title>
<script type="text/javascript">
function finish(success) {
window.parent.postMessage(success ? "success" : "failure", "*");
}
applicationCache.oncached = function() { finish(true); }
applicationCache.onnoupdate = function() { finish(true); }
applicationCache.onerror = function() { finish(false); }
</script>
</head>
<body>
<h1>Child</h1>
</body>
</html>

Просмотреть файл

@ -0,0 +1,68 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=462856
-->
<head>
<title>Test offline app notification</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="loaded()">
<p id="display">
<!-- Load the test frame twice from the same domain,
to make sure we get notifications for both -->
<iframe name="testFrame" src="offlineChild.html"></iframe>
<iframe name="testFrame2" src="offlineChild2.html"></iframe>
<!-- Load from another domain to make sure we get a second allow/deny
notification -->
<iframe name="testFrame3" src="http://example.com/tests/browser/base/content/test/offlineChild.html"></iframe>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
var numFinished = 0;
window.addEventListener("message", function(event) {
is(event.data, "success", "Child was successfully cached.");
if (++numFinished == 3) {
// Clean up after ourself
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var pm = Components.classes["@mozilla.org/permissionmanager;1"].
getService(Components.interfaces.nsIPermissionManager);
pm.remove(frames.testFrame.document.documentURIObject.host, "offline-app");
pm.remove(frames.testFrame3.document.documentURIObject.host, "offline-app");
SimpleTest.finish();
}
}, false);
function loaded() {
// Click the notification bar's "Allow" button. This should kick
// off updates, which will eventually lead to getting messages from
// the children.
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
getService(Components.interfaces.nsIWindowMediator);
var win = wm.getMostRecentWindow("navigator:browser");
var notificationBox = win.gBrowser.getNotificationBox();
var notification = notificationBox.getNotificationWithValue("offline-app-requested-localhost");
notification.childNodes[0].click();
notification = notificationBox.getNotificationWithValue("offline-app-requested-example.com");
notification.childNodes[0].click();
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -42,6 +42,8 @@
* for shared application glue for the Communicator suite of applications
**/
var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
var gBidiUI = false;
function getBrowserURL()

Просмотреть файл

@ -37,7 +37,7 @@
# NSIS branding defines for unofficial builds.
# The official release build branding.nsi is located in other-license/branding/firefox/
# The nightly build branding.nsi is located in browser/installer/windows/nsis/
!define BrandShortName "Shiretoko"
# BrandFullNameInternal is used for some registry and file system values
# instead of BrandFullName and typically should not be modified.
!define BrandFullNameInternal "Shiretoko"
@ -45,11 +45,3 @@
!define URLInfoAbout "http://www.mozilla.org"
!define URLUpdateInfo "http://www.mozilla.org/projects/firefox"
!define SurveyURL "https://survey.mozilla.com/1/Mozilla%20Firefox/${AppVersion}/${AB_CD}/exit.html"
# Everything below this line may be modified for Alpha / Beta releases.
!define BrandFullName "Shiretoko"
# Add !define NO_INSTDIR_FROM_REG to prevent finding a non-default installation
# directory in the registry and using that as the default. This prevents
# Beta releases built with official branding from finding an existing install
# of an official release and defaulting to its installation directory.

Просмотреть файл

@ -3,6 +3,9 @@ pref("startup.homepage_welcome_url","http://www.mozilla.org/projects/%APP%/%VERS
// The time interval between checks for a new version (in seconds)
// nightly=8 hours, official=24 hours
pref("app.update.interval", 28800);
// The time interval between the downloading of mar file chunks in the
// background (in seconds)
pref("app.update.download.backgroundInterval", 60);
// URL user can browse to manually if for some reason all update installation
// attempts fail.
pref("app.update.url.manual", "http://www.mozilla.org/products/%APP%/");

Просмотреть файл

@ -30,6 +30,7 @@ REQUIRES = \
places \
browserplaces \
microsummaries \
privatebrowsing \
$(NULL)
EXPORTS = nsBrowserCompsCID.h
@ -47,6 +48,7 @@ LOCAL_INCLUDES = \
-I$(srcdir)/../shell/src \
-I$(srcdir)/../feeds/src \
-I$(srcdir)/../places/src \
-I$(srcdir)/../privatebrowsing/src \
$(NULL)
ifeq ($(OS_ARCH),WINNT)
@ -56,6 +58,7 @@ endif
SHARED_LIBRARY_LIBS = \
../feeds/src/$(LIB_PREFIX)browser_feeds_s.$(LIB_SUFFIX) \
../places/src/$(LIB_PREFIX)browserplaces_s.$(LIB_SUFFIX) \
../privatebrowsing/src/$(LIB_PREFIX)privatebrowsing_s.$(LIB_SUFFIX) \
$(NULL)
ifneq (,$(filter windows mac cocoa gtk2, $(MOZ_WIDGET_TOOLKIT)))

Просмотреть файл

@ -97,3 +97,7 @@
#define NS_PLACESIMPORTEXPORTSERVICE_CONTRACTID \
"@mozilla.org/browser/places/import-export-service;1"
// 136e2c4d-c5a4-477c-b131-d93d7d704f64
#define NS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID \
{ 0x136e2c4d, 0xc5a4, 0x477c, { 0xb1, 0x31, 0xd9, 0x3d, 0x7d, 0x70, 0x4f, 0x64 } }

Просмотреть файл

@ -77,6 +77,9 @@
#include "nsAboutFeeds.h"
#include "nsIAboutModule.h"
#include "nsPrivateBrowsingServiceWrapper.h"
#include "nsNetCID.h"
/////////////////////////////////////////////////////////////////////////////
NS_GENERIC_FACTORY_CONSTRUCTOR(nsPlacesImportExportService)
@ -113,6 +116,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsICabProfileMigrator)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsFeedSniffer)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrivateBrowsingServiceWrapper, Init)
/////////////////////////////////////////////////////////////////////////////
static const nsModuleComponentInfo components[] =
@ -217,9 +222,14 @@ static const nsModuleComponentInfo components[] =
{ "Seamonkey Profile Migrator",
NS_SEAMONKEYPROFILEMIGRATOR_CID,
NS_BROWSERPROFILEMIGRATOR_CONTRACTID_PREFIX "seamonkey",
nsSeamonkeyProfileMigratorConstructor }
nsSeamonkeyProfileMigratorConstructor },
#endif /* WINCE */
{ "PrivateBrowsing Service C++ Wrapper",
NS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID,
NS_PRIVATE_BROWSING_SERVICE_CONTRACTID,
nsPrivateBrowsingServiceWrapperConstructor }
};
NS_IMPL_NSGETMODULE(nsBrowserCompsModule, components)

Просмотреть файл

@ -24,6 +24,7 @@
# Asaf Romano <mano@mozilla.com>
# Marco Bonardo <mak77@bonardo.net>
# Dietrich Ayala <dietrich@mozilla.com>
# Ehsan Akhgari <ehsan.akhgari@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
@ -323,7 +324,11 @@ BrowserGlue.prototype = {
showPrompt = this._prefs.getBoolPref("browser.tabs.warnOnClose");
} catch (ex) {}
if (!showPrompt)
// Never show a prompt inside the private browsing mode
var inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
getService(Ci.nsIPrivateBrowsingService).
privateBrowsingEnabled;
if (!showPrompt || inPrivateBrowsing)
return false;
var buttonChoice = 0;

Просмотреть файл

@ -1335,6 +1335,19 @@ var PlacesControllerDragHelper = {
var data = dt.mozGetDataAt(flavor, i);
// urls can be dropped on any insertionpoint
// XXXmano: // Remember: this method is called for each dragover event!
// Thus we shouldn't use unwrapNodes here at all if possible.
// I think it would be OK to accept bogus data here (e.g. text which was
// somehow wrapped as TAB_DROP_TYPE, this is not in our control, and
// will just case the actual drop to be a no-op), and only rule out valid
// expected cases, which are either unsupported flavors, or items which
// cannot be dropped in the current insertionpoint. The last case will
// likely force us to use unwrapNodes for the private data types of
// places.
if (flavor == TAB_DROP_TYPE)
continue;
try {
var dragged = PlacesUtils.unwrapNodes(data, flavor)[0];
} catch (e) {
@ -1363,6 +1376,7 @@ var PlacesControllerDragHelper = {
return true;
},
/**
* Determines if a node can be moved.
*
@ -1444,8 +1458,22 @@ var PlacesControllerDragHelper = {
return false;
var data = dt.mozGetDataAt(flavor, i);
// There's only ever one in the D&D case.
var unwrapped = PlacesUtils.unwrapNodes(data, flavor)[0];
var unwrapped;
if (flavor != TAB_DROP_TYPE) {
// There's only ever one in the D&D case.
unwrapped = PlacesUtils.unwrapNodes(data, flavor)[0];
}
else if (data instanceof XULElement && data.localName == "tab" &&
data.ownerDocument.defaultView instanceof ChromeWindow) {
var uri = data.linkedBrowser.currentURI;
var spec = uri ? uri.spec : "about:blank";
var title = data.label;
unwrapped = { uri: spec,
title: data.label,
type: PlacesUtils.TYPE_X_MOZ_URL};
}
else
throw("bogus data was passed as a tab")
var index = insertionPoint.index;
@ -1494,10 +1522,12 @@ var PlacesControllerDragHelper = {
PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
PlacesUtils.TYPE_X_MOZ_PLACE],
// The order matters.
GENERIC_VIEW_DROP_TYPES: [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
PlacesUtils.TYPE_X_MOZ_PLACE,
PlacesUtils.TYPE_X_MOZ_URL,
TAB_DROP_TYPE,
PlacesUtils.TYPE_UNICODE],
/**

Просмотреть файл

@ -417,6 +417,9 @@ var PlacesOrganizer = {
populateRestoreMenu: function PO_populateRestoreMenu() {
var restorePopup = document.getElementById("fileRestorePopup");
var dateSvc = Cc["@mozilla.org/intl/scriptabledateformat;1"].
getService(Ci.nsIScriptableDateFormat);
// remove existing menu items
// last item is the restoreFromFile item
while (restorePopup.childNodes.length > 1)
@ -429,13 +432,17 @@ var PlacesOrganizer = {
var files = this.bookmarksBackupDir.directoryEntries;
while (files.hasMoreElements()) {
var f = files.getNext().QueryInterface(Ci.nsIFile);
var rx = new RegExp("^(bookmarks|" + localizedFilenamePrefix + ")-.+\.json");
if (!f.isHidden() && f.leafName.match(rx))
fileList.push(f);
var rx = new RegExp("^(bookmarks|" + localizedFilenamePrefix +
")-([0-9]{4}-[0-9]{2}-[0-9]{2})\.json$");
if (!f.isHidden() && f.leafName.match(rx)) {
var date = f.leafName.match(rx)[2].replace(/-/g, "/");
var dateObj = new Date(date);
fileList.push({date: dateObj, filename: f.leafName});
}
}
fileList.sort(function PO_fileList_compare(a, b) {
return b.lastModifiedTime - a.lastModifiedTime;
return b.date - a.date;
});
if (fileList.length == 0)
@ -446,12 +453,13 @@ var PlacesOrganizer = {
var m = restorePopup.insertBefore
(document.createElement("menuitem"),
document.getElementById("restoreFromFile"));
var rx = new RegExp("^(bookmarks|" + localizedFilenamePrefix + ")-");
var dateStr = fileList[i].leafName.replace(rx, "").replace(/\.json$/, "");
if (!dateStr.length)
dateStr = fileList[i].leafName;
m.setAttribute("label", dateStr);
m.setAttribute("value", fileList[i].leafName);
m.setAttribute("label",
dateSvc.FormatDate("",
Ci.nsIScriptableDateFormat.dateFormatLong,
fileList[i].date.getFullYear(),
fileList[i].date.getMonth() + 1,
fileList[i].date.getDate()));
m.setAttribute("value", fileList[i].filename);
m.setAttribute("oncommand",
"PlacesOrganizer.onRestoreMenuItemClick(this);");
}
@ -1502,7 +1510,6 @@ var PlacesQueryBuilder = {
switch (id) {
case "scopeBarHistory":
PlacesSearchBox.filterCollection = "history";
folders = [];
break;
case "scopeBarFolder":
var selectedFolder = PlacesOrganizer._places.selectedNode.itemId;
@ -1510,7 +1517,8 @@ var PlacesQueryBuilder = {
// bookmark folders
if (selectedFolder != PlacesUIUtils.allBookmarksFolderId) {
PlacesSearchBox.filterCollection = "collection";
folders.push(PlacesOrganizer._places.selectedNode.itemId);
folders.push(PlacesUtils.getConcreteItemId(
PlacesOrganizer._places.selectedNode));
break;
}
default: // all bookmarks

Просмотреть файл

@ -386,8 +386,9 @@ var PlacesUIUtils = {
break;
default:
if (type == PlacesUtils.TYPE_X_MOZ_URL ||
type == PlacesUtils.TYPE_UNICODE) {
var title = (type == PlacesUtils.TYPE_X_MOZ_URL) ? data.title :
type == PlacesUtils.TYPE_UNICODE ||
type == TAB_DROP_TYPE) {
var title = (type != PlacesUtils.TYPE_UNICODE) ? data.title :
data.uri;
return this.ptm.createItem(PlacesUtils._uri(data.uri),
container, index, title);

Просмотреть файл

@ -37,6 +37,14 @@
// put cleanup of the bookmarks test here.
// Run the event loop to be more like the browser, which normally runs the
// event loop long before code like this would run.
// Not doing so could cause us to close the connection before all tasks have
// been completed, and that would crash badly.
let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
while (tm.mainThread.hasPendingEvents())
tm.mainThread.processNextEvent(false);
// XPCShell doesn't dispatch quit-application, to ensure cleanup we have to
// dispatch it after each test run.
var os = Cc['@mozilla.org/observer-service;1'].
@ -49,14 +57,6 @@ var pip = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService).
QueryInterface(Ci.nsPIPlacesDatabase);
if (pip.DBConnection.connectionReady) {
// Run the event loop to be more like the browser, which normally runs the
// event loop long before code like this would run.
// Not doing so could cause us to close the connection between all tasks have
// been completed, and that would crash badly.
let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
while (tm.mainThread.hasPendingEvents())
tm.mainThread.processNextEvent(false);
pip.commitPendingChanges();
pip.finalizeInternalStatements();
pip.DBConnection.close();

Просмотреть файл

@ -212,6 +212,8 @@ tests.push({
//------------------------------------------------------------------------------
function finish_test() {
// Simulate application closing to remove the idle observer and avoid leaks.
os.notifyObservers(null, "quit-application-granted", null);
do_test_finished();
}
@ -220,6 +222,9 @@ function next_test() {
// Clean up database from all bookmarks.
remove_all_bookmarks();
// Simulate application closing to remove the idle observer and avoid leaks.
os.notifyObservers(null, "quit-application-granted", null);
// nsBrowserGlue stops observing topics after first notification,
// so we add back the observer to test additional runs.
os.addObserver(bg, TOPIC_PLACES_INIT_COMPLETE, false);

Просмотреть файл

@ -216,10 +216,24 @@ var gAdvancedPane = {
},
// XXX: duplicated in browser.js
_getOfflineAppUsage: function (host)
_getOfflineAppUsage: function (host, groups)
{
// XXX Bug 442710: include offline cache usage.
var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
getService(Components.interfaces.nsIApplicationCacheService);
if (!groups) {
groups = cacheService.getGroups({});
}
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var usage = 0;
for (var i = 0; i < groups.length; i++) {
var uri = ios.newURI(groups[i], null, null);
if (uri.asciiHost == host) {
var cache = cacheService.getActiveCache(groups[i]);
usage += cache.usage;
}
}
var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"].
getService(Components.interfaces.nsIDOMStorageManager);
@ -241,6 +255,10 @@ var gAdvancedPane = {
list.removeChild(list.firstChild);
}
var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
getService(Components.interfaces.nsIApplicationCacheService);
var groups = cacheService.getGroups({});
var bundle = document.getElementById("bundlePreferences");
var enumerator = pm.enumerator;
@ -254,7 +272,7 @@ var gAdvancedPane = {
row.className = "offlineapp";
row.setAttribute("host", perm.host);
var converted = DownloadUtils.
convertByteUnits(this._getOfflineAppUsage(perm.host));
convertByteUnits(this._getOfflineAppUsage(perm.host, groups));
row.setAttribute("usage",
bundle.getFormattedString("offlineAppUsage",
converted));
@ -295,14 +313,18 @@ var gAdvancedPane = {
return;
// clear offline cache entries
var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
var cacheSession = cacheService.createSession("HTTP-offline",
Components.interfaces.nsICache.STORE_OFFLINE,
true)
.QueryInterface(Components.interfaces.nsIOfflineCacheSession);
cacheSession.clearKeysOwnedByDomain(host);
cacheSession.evictUnownedEntries();
var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
getService(Components.interfaces.nsIApplicationCacheService);
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var groups = cacheService.getGroups({});
for (var i = 0; i < groups.length; i++) {
var uri = ios.newURI(groups[i], null, null);
if (uri.asciiHost == host) {
var cache = cacheService.getActiveCache(groups[i]);
cache.discard();
}
}
// send out an offline-app-removed signal. The nsDOMStorage
// service will clear DOM storage for this host.

Просмотреть файл

@ -553,7 +553,7 @@ var gCookiesWindow = {
date.getMinutes(),
date.getSeconds());
}
return this._bundle.getString("AtEndOfSession");
return this._bundle.getString("expireAtEndOfSession");
},
_updateCookieData: function (aItem)

Просмотреть файл

@ -116,7 +116,7 @@
<separator class="thin"/>
<hbox align="center">
<label value="&location.label;" accesskey="&location.accesskey;" control="browserHomePage"/>
<textbox id="browserHomePage" class="padded" flex="1"
<textbox id="browserHomePage" class="padded uri-element" flex="1"
type="autocomplete" autocompletesearch="history"
preference="browser.startup.homepage"/>
</hbox>

Просмотреть файл

@ -42,6 +42,28 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = privatebrowsing
LIBRARY_NAME = privatebrowsing_s
FORCE_STATIC_LIB = 1
FORCE_USE_PIC = 1
ifndef MOZ_MEMORY
USE_STATIC_LIBS = 1
endif
REQUIRES = \
xpcom \
string \
necko \
js \
xpconnect \
$(NULL)
CPPSRCS = \
nsPrivateBrowsingServiceWrapper.cpp \
$(NULL)
LOCAL_INCLUDES = -I$(srcdir)/../../build
EXTRA_PP_COMPONENTS = \
nsPrivateBrowsingService.js \
aboutPrivateBrowsing.js \

Просмотреть файл

@ -80,6 +80,7 @@ const Cr = Components.results;
function PrivateBrowsingService() {
this._obs.addObserver(this, "profile-after-change", true);
this._obs.addObserver(this, "quit-application-granted", true);
this._obs.addObserver(this, "private-browsing", true);
}
PrivateBrowsingService.prototype = {
@ -132,22 +133,13 @@ PrivateBrowsingService.prototype = {
this.privateBrowsingEnabled = false;
},
_onPrivateBrowsingModeChanged: function PBS__onPrivateBrowsingModeChanged() {
_onBeforePrivateBrowsingModeChange: function PBS__onBeforePrivateBrowsingModeChange() {
// nothing needs to be done here if we're auto-starting
if (!this._autoStart) {
// clear all auth tokens
let sdr = Cc["@mozilla.org/security/sdr;1"].
getService(Ci.nsISecretDecoderRing);
sdr.logoutAndTeardown();
// clear plain HTTP auth sessions
let authMgr = Cc['@mozilla.org/network/http-auth-manager;1'].
getService(Ci.nsIHttpAuthManager);
authMgr.clearAll();
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
if (this.privateBrowsingEnabled) {
if (this._inPrivateBrowsing) {
// whether we should save and close the current session
this._saveSession = true;
var prefBranch = Cc["@mozilla.org/preferences-service;1"].
@ -158,65 +150,69 @@ PrivateBrowsingService.prototype = {
} catch (ex) {}
// save the whole browser state in order to restore all windows/tabs later
if (this._saveSession && !this._savedBrowserState) {
if (this._saveSession && !this._savedBrowserState)
this._savedBrowserState = ss.getBrowserState();
// Close all windows
this._closeAllWindows();
// Open about:privatebrowsing
this._openAboutPrivateBrowsing();
}
}
else {
// Clear the error console
let consoleService = Cc["@mozilla.org/consoleservice;1"].
getService(Ci.nsIConsoleService);
consoleService.logStringMessage(null); // trigger the listeners
consoleService.reset();
if (!this.quitting && this._saveSession) {
// dummy session used to transition from/to pb mode, see bug 476463
let transitionState = {
"windows": [{
"tabs": [{
"entries": [{
"url": "about:blank"
}]
}],
"_closedTabs": []
}]
};
// load dummy session to get a distinct separation between private and
// non-private sessions
ss.setBrowserState(JSON.stringify(transitionState));
// restore the windows/tabs which were open before entering the private mode
if (this._saveSession && this._savedBrowserState) {
if (!this._quitting) { // don't restore when shutting down!
ss.setBrowserState(this._savedBrowserState);
}
this._savedBrowserState = null;
}
let browser = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator).
getMostRecentWindow("navigator:browser").gBrowser;
// this ensures a clean slate from which to transition into or out of
// private browsing
browser.addTab();
browser.removeTab(browser.tabContainer.firstChild);
}
}
else
this._saveSession = false;
},
#ifndef XP_WIN
#define BROKEN_WM_Z_ORDER
#endif
_closeAllWindows: function PBS__closeAllWindows() {
let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator);
#ifdef BROKEN_WM_Z_ORDER
let windowsEnum = windowMediator.getEnumerator("navigator:browser");
#else
let windowsEnum = windowMediator.getZOrderDOMWindowEnumerator("navigator:browser", false);
#endif
while (windowsEnum.hasMoreElements()) {
let win = windowsEnum.getNext();
win.close();
_onAfterPrivateBrowsingModeChange: function PBS__onAfterPrivateBrowsingModeChange() {
// nothing to do here if we're auto-starting or the current session is being
// used
if (!this._autoStart && this._saveSession) {
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
// if we have transitioned out of private browsing mode and the session is
// to be restored, do it now
if (!this._inPrivateBrowsing) {
ss.setBrowserState(this._savedBrowserState);
this._savedBrowserState = null;
}
else {
// otherwise, if we have transitioned into private browsing mode, load
// about:privatebrowsing
let privateBrowsingState = {
"windows": [{
"tabs": [{
"entries": [{
"url": "about:privatebrowsing"
}]
}],
"_closedTabs": []
}]
};
// Transition into private browsing mode
ss.setBrowserState(JSON.stringify(privateBrowsingState));
}
}
},
_openAboutPrivateBrowsing: function PBS__openAboutPrivateBrowsing() {
let windowWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher);
let url = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
url.data = "about:privatebrowsing";
windowWatcher.openWindow(null, "chrome://browser/content/browser.xul",
null, "chrome,all,resizable=yes,dialog=no", url);
},
_canEnterPrivateBrowsingMode: function PBS__canEnterPrivateBrowsingMode() {
let cancelEnter = Cc["@mozilla.org/supports-PRBool;1"].
createInstance(Ci.nsISupportsPRBool);
@ -255,6 +251,25 @@ PrivateBrowsingService.prototype = {
case "quit-application-granted":
this._unload();
break;
case "private-browsing":
// clear all auth tokens
let sdr = Cc["@mozilla.org/security/sdr;1"].
getService(Ci.nsISecretDecoderRing);
sdr.logoutAndTeardown();
// clear plain HTTP auth sessions
let authMgr = Cc['@mozilla.org/network/http-auth-manager;1'].
getService(Ci.nsIHttpAuthManager);
authMgr.clearAll();
if (!this._inPrivateBrowsing) {
// Clear the error console
let consoleService = Cc["@mozilla.org/consoleservice;1"].
getService(Ci.nsIConsoleService);
consoleService.logStringMessage(null); // trigger the listeners
consoleService.reset();
}
break;
}
},
@ -301,9 +316,17 @@ PrivateBrowsingService.prototype = {
let quitting = Cc["@mozilla.org/supports-PRBool;1"].
createInstance(Ci.nsISupportsPRBool);
quitting.data = this._quitting;
// notify observers of the pending private browsing mode change
this._obs.notifyObservers(quitting, "private-browsing-change-granted", data);
// destroy the current session and start initial cleanup
this._onBeforePrivateBrowsingModeChange();
this._obs.notifyObservers(quitting, "private-browsing", data);
this._onPrivateBrowsingModeChanged();
// load the appropriate session
this._onAfterPrivateBrowsingModeChange();
}
} catch (ex) {
Cu.reportError("Exception thrown while processing the " +

Просмотреть файл

@ -0,0 +1,123 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Private Browsing.
*
* The Initial Developer of the Original Code is
* Ehsan Akhgari <ehsan.akhgari@gmail.com>
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsPrivateBrowsingServiceWrapper.h"
#include "nsServiceManagerUtils.h"
#include "jsapi.h"
#include "nsIJSContextStack.h"
class JSStackGuard
{
public:
JSStackGuard();
~JSStackGuard();
private:
nsCOMPtr<nsIJSContextStack> mJSStack;
};
NS_IMPL_ISUPPORTS2(nsPrivateBrowsingServiceWrapper, nsIPrivateBrowsingService, nsIObserver)
nsresult
nsPrivateBrowsingServiceWrapper::Init()
{
nsresult rv;
mPBService = do_GetService("@mozilla.org/privatebrowsing;1", &rv);
return rv;
}
JSStackGuard::JSStackGuard()
: mJSStack(nsnull)
{
nsresult rv;
mJSStack = do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
if (NS_SUCCEEDED(rv) && mJSStack) {
rv = mJSStack->Push(nsnull);
if (NS_FAILED(rv))
mJSStack = nsnull;
}
}
JSStackGuard::~JSStackGuard()
{
if (mJSStack) {
JSContext *cx;
mJSStack->Pop(&cx);
NS_ASSERTION(cx == nsnull, "JSContextStack mismatch");
}
}
NS_IMETHODIMP
nsPrivateBrowsingServiceWrapper::GetPrivateBrowsingEnabled(PRBool *aPrivateBrowsingEnabled)
{
if (!aPrivateBrowsingEnabled)
return NS_ERROR_NULL_POINTER;
JSStackGuard guard;
return mPBService->GetPrivateBrowsingEnabled(aPrivateBrowsingEnabled);
}
NS_IMETHODIMP
nsPrivateBrowsingServiceWrapper::SetPrivateBrowsingEnabled(PRBool aPrivateBrowsingEnabled)
{
JSStackGuard guard;
return mPBService->SetPrivateBrowsingEnabled(aPrivateBrowsingEnabled);
}
NS_IMETHODIMP
nsPrivateBrowsingServiceWrapper::GetAutoStarted(PRBool *aAutoStarted)
{
if (!aAutoStarted)
return NS_ERROR_NULL_POINTER;
JSStackGuard guard;
return mPBService->GetAutoStarted(aAutoStarted);
}
NS_IMETHODIMP
nsPrivateBrowsingServiceWrapper::RemoveDataFromDomain(const nsACString & aDomain)
{
JSStackGuard guard;
return mPBService->RemoveDataFromDomain(aDomain);
}
NS_IMETHODIMP
nsPrivateBrowsingServiceWrapper::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
{
JSStackGuard guard;
nsCOMPtr<nsIObserver> observer(do_QueryInterface(mPBService));
NS_ENSURE_TRUE(observer, NS_ERROR_FAILURE);
return observer->Observe(aSubject, aTopic, aData);
}

Просмотреть файл

@ -0,0 +1,55 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Private Browsing.
*
* The Initial Developer of the Original Code is
* Ehsan Akhgari <ehsan.akhgari@gmail.com>
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsCOMPtr.h"
#include "nsIPrivateBrowsingService.h"
#include "nsIObserver.h"
class nsIJSContextStack;
class nsPrivateBrowsingServiceWrapper : public nsIPrivateBrowsingService,
public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRIVATEBROWSINGSERVICE
NS_DECL_NSIOBSERVER
nsresult Init();
private:
nsCOMPtr<nsIPrivateBrowsingService> mPBService;
};

Просмотреть файл

@ -51,6 +51,8 @@ _BROWSER_TEST_FILES = \
browser_privatebrowsing_searchbar.js \
browser_privatebrowsing_findbar.js \
browser_privatebrowsing_zoom.js \
browser_privatebrowsing_transition.js \
browser_privatebrowsing_import.js \
$(NULL)
libs:: $(_BROWSER_TEST_FILES)

Просмотреть файл

@ -0,0 +1,69 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Private Browsing Tests.
*
* The Initial Developer of the Original Code is
* Ehsan Akhgari.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ehsan Akhgari <ehsan.akhgari@gmail.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// This test makes sure that the File->Import menu item is disabled inside the
// private browsing mode.
function test() {
// initialization
let prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
prefBranch.setBoolPref("browser.privatebrowsing.keep_current_session", true);
let pb = Cc["@mozilla.org/privatebrowsing;1"].
getService(Ci.nsIPrivateBrowsingService);
let importMenuItem = document.getElementById("menu_import");
// make sure the menu item is not disabled to begin with
ok(!importMenuItem.hasAttribute("disabled"),
"File->Import menu item should not be disabled outside of the private browsing mode");
// enter private browsing mode
pb.privateBrowsingEnabled = true;
ok(importMenuItem.hasAttribute("disabled"),
"File->Import menu item should be disabled inside of the private browsing mode");
// leave private browsing mode
pb.privateBrowsingEnabled = false;
ok(!importMenuItem.hasAttribute("disabled"),
"File->Import menu item should not be disabled after leaving the private browsing mode");
// cleanup
prefBranch.clearUserPref("browser.privatebrowsing.keep_current_session");
}

Просмотреть файл

@ -0,0 +1,100 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Private Browsing Tests.
*
* The Initial Developer of the Original Code is
* Nochum Sossonko.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Nochum Sossonko <nsossonko@hotmail.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// Tests the order of events and notifications when entering/exiting private
// browsing mode. This ensures that all private data is removed on exit, e.g.
// a cookie set in on unload handler, see bug 476463.
let cookieManager = Cc["@mozilla.org/cookiemanager;1"].
getService(Ci.nsICookieManager2);
let pb = Cc["@mozilla.org/privatebrowsing;1"].
getService(Ci.nsIPrivateBrowsingService);
let _obs = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
let observerNotified = 0, firstUnloadFired = 0, secondUnloadFired = 0;
let pbObserver = {
observe: function(aSubject, aTopic, aData) {
if (aTopic == "private-browsing") {
switch(aData) {
case "enter":
observerNotified++;
is(observerNotified, 1, "This should be the first notification");
is(firstUnloadFired, 1, "The first unload event should have been processed by now");
break;
case "exit":
_obs.removeObserver(this, "private-browsing");
observerNotified++;
is(observerNotified, 2, "This should be the second notification");
is(secondUnloadFired, 1, "The second unload event should have been processed by now");
break;
}
}
}
}
function test() {
waitForExplicitFinish();
_obs.addObserver(pbObserver, "private-browsing", false);
is(gBrowser.tabContainer.childNodes.length, 1, "There should only be one tab");
let testTab = gBrowser.addTab();
gBrowser.selectedTab = testTab;
testTab.linkedBrowser.addEventListener("unload", (function() {
testTab.linkedBrowser.removeEventListener("unload", arguments.callee, true);
firstUnloadFired++;
is(observerNotified, 0, "The notification shouldn't have been sent yet");
}), true);
pb.privateBrowsingEnabled = true;
let testTab = gBrowser.addTab();
gBrowser.selectedTab = testTab;
testTab.linkedBrowser.addEventListener("unload", (function() {
testTab.linkedBrowser.removeEventListener("unload", arguments.callee, true);
secondUnloadFired++;
is(observerNotified, 1, "The notification shouldn't have been sent yet");
cookieManager.add("example.com", "test/", "PB", "1", false, false, false, 1000000000000);
}), true);
pb.privateBrowsingEnabled = false;
gBrowser.tabContainer.lastChild.linkedBrowser.addEventListener("unload", (function() {
gBrowser.tabContainer.lastChild.linkedBrowser.removeEventListener("unload", arguments.callee, true);
let count = cookieManager.countCookiesFromHost("example.com");
is(count, 0, "There shouldn't be any cookies once pb mode has exited");
cookieManager.QueryInterface(Ci.nsICookieManager);
cookieManager.remove("example.com", "PB", "test/", false);
}), true);
gBrowser.removeCurrentTab();
finish();
}

Просмотреть файл

@ -0,0 +1,68 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Private Browsing Tests.
*
* The Initial Developer of the Original Code is
* Ehsan Akhgari.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ehsan Akhgari <ehsan.akhgari@gmail.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// This test checks the browser.privatebrowsing.autostart preference.
function do_test() {
// initialization
var prefsService = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
prefsService.setBoolPref("browser.privatebrowsing.autostart", true);
do_check_true(prefsService.getBoolPref("browser.privatebrowsing.autostart"));
var pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
getService(Ci.nsIPrivateBrowsingService).
QueryInterface(Ci.nsIObserver);
// private browsing not auto-started yet
do_check_false(pb.autoStarted);
// simulate startup to make the PB service read the prefs
pb.observe(null, "profile-after-change", "");
// the private mode should be entered automatically
do_check_true(pb.privateBrowsingEnabled);
// private browsing is auto-started
do_check_true(pb.autoStarted);
// leave private browsing mode
pb.privateBrowsingEnabled = false;
// private browsing not auto-started
do_check_false(pb.autoStarted);
}

Просмотреть файл

@ -0,0 +1,97 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Private Browsing Tests.
*
* The Initial Developer of the Original Code is
* Ehsan Akhgari.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ehsan Akhgari <ehsan.akhgari@gmail.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// This test makes sure that the private browsing mode is left at application
// shutdown.
function do_test() {
// initialization
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
var pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
getService(Ci.nsIPrivateBrowsingService);
var prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
prefBranch.setBoolPref("browser.privatebrowsing.keep_current_session", true);
var expectedQuitting;
var called = 0;
var observer = {
observe: function(aSubject, aTopic, aData) {
if (aTopic == kPrivateBrowsingNotification &&
aData == kExit) {
// increment the call counter
++ called;
do_check_neq(aSubject, null);
try {
aSubject.QueryInterface(Ci.nsISupportsPRBool);
} catch (ex) {
do_throw("aSubject was not null, but wasn't an nsISupportsPRBool");
}
// check the "quitting" argument
do_check_eq(aSubject.data, expectedQuitting);
// finish up the test
if (expectedQuitting) {
os.removeObserver(this, kPrivateBrowsingNotification);
prefBranch.clearUserPref("browser.privatebrowsing.keep_current_session");
do_test_finished();
}
}
}
};
// set the observer
os.addObserver(observer, kPrivateBrowsingNotification, false);
// enter the private browsing mode
pb.privateBrowsingEnabled = true;
// exit the private browsing mode
expectedQuitting = false;
pb.privateBrowsingEnabled = false;
do_check_eq(called, 1);
// enter the private browsing mode
pb.privateBrowsingEnabled = true;
// Simulate an exit
expectedQuitting = true;
do_test_pending();
os.notifyObservers(null, "quit-application-granted", null);
}

Просмотреть файл

@ -0,0 +1,618 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Private Browsing Tests.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Shawn Wilsher <me@shawnwilsher.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/**
* Test added with bug 460086 to test the behavior of the new API that was added
* to remove all traces of visiting a site.
*/
////////////////////////////////////////////////////////////////////////////////
//// Constants
let pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
getService(Ci.nsIPrivateBrowsingService);
const COOKIE_EXPIRY = Math.round(Date.now() / 1000) + 60;
const COOKIE_NAME = "testcookie";
const COOKIE_PATH = "/";
const LOGIN_USERNAME = "username";
const LOGIN_PASSWORD = "password";
const LOGIN_USERNAME_FIELD = "username_field";
const LOGIN_PASSWORD_FIELD = "password_field";
const PERMISSION_TYPE = "test-perm";
const PERMISSION_VALUE = Ci.nsIPermissionManager.ALLOW_ACTION;
const PREFERENCE_NAME = "test-pref";
////////////////////////////////////////////////////////////////////////////////
//// Utility Functions
/**
* Creates an nsIURI object for the given string representation of a URI.
*
* @param aURIString
* The spec of the URI to create.
* @returns an nsIURI representing aURIString.
*/
function uri(aURIString)
{
return Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService).
newURI(aURIString, null, null);
}
/**
* Adds a visit to history.
*
* @param aURI
* The URI to add.
*/
function add_visit(aURI)
{
check_visited(aURI, false);
let bh = Cc["@mozilla.org/browser/global-history;2"].
getService(Ci.nsIBrowserHistory);
bh.addPageWithDetails(aURI, aURI.spec, Date.now() * 1000);
check_visited(aURI, true);
}
/**
* Checks to ensure a URI string is visited or not.
*
* @param aURI
* The URI to check.
* @param aIsVisited
* True if the URI should be visited, false otherwise.
*/
function check_visited(aURI, aIsVisited)
{
let gh = Cc["@mozilla.org/browser/global-history;2"].
getService(Ci.nsIGlobalHistory2);
let checker = aIsVisited ? do_check_true : do_check_false;
checker(gh.isVisited(aURI));
}
/**
* Add a cookie to the cookie service.
*
* @param aDomain
*/
function add_cookie(aDomain)
{
check_cookie_exists(aDomain, false);
let cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
cm.add(aDomain, COOKIE_PATH, COOKIE_NAME, "", false, false, false,
COOKIE_EXPIRY);
check_cookie_exists(aDomain, true);
}
/**
* Checks to ensure that a cookie exists or not for a domain.
*
* @param aDomain
* The domain to check for the cookie.
* @param aExists
* True if the cookie should exist, false otherwise.
*/
function check_cookie_exists(aDomain, aExists)
{
let cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
let cookie = {
host: aDomain,
name: COOKIE_NAME,
path: COOKIE_PATH
}
let checker = aExists ? do_check_true : do_check_false;
checker(cm.cookieExists(cookie));
}
/**
* Adds a download to download history.
*
* @param aURIString
* The string of the URI to add.
* @param aIsActive
* If it should be set to an active state in the database. This does not
* make it show up in the list of active downloads however!
*/
function add_download(aURIString, aIsActive)
{
check_downloaded(aURIString, false);
let db = Cc["@mozilla.org/download-manager;1"].
getService(Ci.nsIDownloadManager).
DBConnection;
let stmt = db.createStatement(
"INSERT INTO moz_downloads (source, state) " +
"VALUES (:source, :state)"
);
stmt.params.source = aURIString;
stmt.params.state = aIsActive ? Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING :
Ci.nsIDownloadManager.DOWNLOAD_FINISHED;
try {
stmt.execute();
}
finally {
stmt.finalize();
}
check_downloaded(aURIString, true);
}
/**
* Checks to ensure a URI string is in download history or not.
*
* @param aURIString
* The string of the URI to check.
* @param aIsDownloaded
* True if the URI should be downloaded, false otherwise.
*/
function check_downloaded(aURIString, aIsDownloaded)
{
let db = Cc["@mozilla.org/download-manager;1"].
getService(Ci.nsIDownloadManager).
DBConnection;
let stmt = db.createStatement(
"SELECT * " +
"FROM moz_downloads " +
"WHERE source = :source"
);
stmt.params.source = aURIString;
let checker = aIsDownloaded ? do_check_true : do_check_false;
try {
checker(stmt.step());
}
finally {
stmt.finalize();
}
}
/**
* Adds a disabled host to the login manager.
*
* @param aHost
* The host to add to the list of disabled hosts.
*/
function add_disabled_host(aHost)
{
check_disabled_host(aHost, false);
let lm = Cc["@mozilla.org/login-manager;1"].
getService(Ci.nsILoginManager);
lm.setLoginSavingEnabled(aHost, false);
check_disabled_host(aHost, true);
}
/**
* Checks to see if a host is disabled for storing logins or not.
*
* @param aHost
* The host to check if it is disabled.
* @param aIsDisabled
* True if the host should be disabled, false otherwise.
*/
function check_disabled_host(aHost, aIsDisabled)
{
let lm = Cc["@mozilla.org/login-manager;1"].
getService(Ci.nsILoginManager);
let checker = aIsDisabled ? do_check_false : do_check_true;
checker(lm.getLoginSavingEnabled(aHost));
}
/**
* Adds a login for the specified host to the login manager.
*
* @param aHost
* The host to add the login for.
*/
function add_login(aHost)
{
check_login_exists(aHost, false);
let login = Cc["@mozilla.org/login-manager/loginInfo;1"].
createInstance(Ci.nsILoginInfo);
login.init(aHost, "", null, LOGIN_USERNAME, LOGIN_PASSWORD,
LOGIN_USERNAME_FIELD, LOGIN_PASSWORD_FIELD);
let lm = Cc["@mozilla.org/login-manager;1"].
getService(Ci.nsILoginManager);
lm.addLogin(login);
check_login_exists(aHost, true);
}
/**
* Checks to see if a login exists for a host.
*
* @param aHost
* The host to check for the test login.
* @param aExists
* True if the login should exist, false otherwise.
*/
function check_login_exists(aHost, aExists)
{
let lm = Cc["@mozilla.org/login-manager;1"].
getService(Ci.nsILoginManager);
let count = { value: 0 };
lm.findLogins(count, aHost, "", null);
do_check_eq(count.value, aExists ? 1 : 0);
}
/**
* Adds a permission for the specified URI to the permission manager.
*
* @param aURI
* The URI to add the test permission for.
*/
function add_permission(aURI)
{
check_permission_exists(aURI, false);
let pm = Cc["@mozilla.org/permissionmanager;1"].
getService(Ci.nsIPermissionManager);
pm.add(aURI, PERMISSION_TYPE, PERMISSION_VALUE);
check_permission_exists(aURI, true);
}
/**
* Checks to see if a permission exists for the given URI.
*
* @param aURI
* The URI to check if a permission exists.
* @param aExists
* True if the permission should exist, false otherwise.
*/
function check_permission_exists(aURI, aExists)
{
let pm = Cc["@mozilla.org/permissionmanager;1"].
getService(Ci.nsIPermissionManager);
let perm = pm.testExactPermission(aURI, PERMISSION_TYPE);
let checker = aExists ? do_check_eq : do_check_neq;
checker(perm, PERMISSION_VALUE);
}
/**
* Adds a content preference for the specified URI.
*
* @param aURI
* The URI to add a preference for.
*/
function add_preference(aURI)
{
check_preference_exists(aURI, false);
let cp = Cc["@mozilla.org/content-pref/service;1"].
getService(Ci.nsIContentPrefService);
cp.setPref(aURI, PREFERENCE_NAME, "foo");
check_preference_exists(aURI, true);
}
/**
* Checks to see if a preference exists for the given URI.
*
* @param aURI
* The URI to check if a preference exists.
* @param aExists
* True if the permission should exist, false otherwise.
*/
function check_preference_exists(aURI, aExists)
{
let cp = Cc["@mozilla.org/content-pref/service;1"].
getService(Ci.nsIContentPrefService);
let checker = aExists ? do_check_true : do_check_false;
checker(cp.hasPref(aURI, PREFERENCE_NAME));
}
////////////////////////////////////////////////////////////////////////////////
//// Test Functions
// History
function test_history_cleared_with_direct_match()
{
const TEST_URI = uri("http://mozilla.org/foo");
add_visit(TEST_URI);
pb.removeDataFromDomain("mozilla.org");
check_visited(TEST_URI, false);
}
function test_history_cleared_with_subdomain()
{
const TEST_URI = uri("http://www.mozilla.org/foo");
add_visit(TEST_URI);
pb.removeDataFromDomain("mozilla.org");
check_visited(TEST_URI, false);
}
function test_history_not_cleared_with_uri_contains_domain()
{
const TEST_URI = uri("http://ilovemozilla.org/foo");
add_visit(TEST_URI);
pb.removeDataFromDomain("mozilla.org");
check_visited(TEST_URI, true);
// Clear history since we left something there from this test.
let bh = Cc["@mozilla.org/browser/global-history;2"].
getService(Ci.nsIBrowserHistory);
bh.removeAllPages();
}
// Cookie Service
function test_cookie_cleared_with_direct_match()
{
const TEST_DOMAIN = "mozilla.org";
add_cookie(TEST_DOMAIN);
pb.removeDataFromDomain("mozilla.org");
check_cookie_exists(TEST_DOMAIN, false);
}
function test_cookie_cleared_with_subdomain()
{
const TEST_DOMAIN = "www.mozilla.org";
add_cookie(TEST_DOMAIN);
pb.removeDataFromDomain("mozilla.org");
check_cookie_exists(TEST_DOMAIN, false);
}
function test_cookie_not_cleared_with_uri_contains_domain()
{
const TEST_DOMAIN = "ilovemozilla.org";
add_cookie(TEST_DOMAIN);
pb.removeDataFromDomain("mozilla.org");
check_cookie_exists(TEST_DOMAIN, true);
}
// Download Manager
function test_download_history_cleared_with_direct_match()
{
const TEST_URI = "http://mozilla.org/foo";
add_download(TEST_URI, false);
pb.removeDataFromDomain("mozilla.org");
check_downloaded(TEST_URI, false);
}
function test_download_history_cleared_with_subdomain()
{
const TEST_URI = "http://www.mozilla.org/foo";
add_download(TEST_URI, false);
pb.removeDataFromDomain("mozilla.org");
check_downloaded(TEST_URI, false);
}
function test_download_history_not_cleared_with_active_direct_match()
{
// Tests that downloads marked as active in the db are not deleted from the db
const TEST_URI = "http://mozilla.org/foo";
add_download(TEST_URI, true);
pb.removeDataFromDomain("mozilla.org");
check_downloaded(TEST_URI, true);
// Reset state
let db = Cc["@mozilla.org/download-manager;1"].
getService(Ci.nsIDownloadManager).
DBConnection;
db.executeSimpleSQL("DELETE FROM moz_downloads");
check_downloaded(TEST_URI, false);
}
// Login Manager
function test_login_manager_disabled_hosts_cleared_with_direct_match()
{
const TEST_HOST = "http://mozilla.org";
add_disabled_host(TEST_HOST);
pb.removeDataFromDomain("mozilla.org");
check_disabled_host(TEST_HOST, false);
}
function test_login_manager_disabled_hosts_cleared_with_subdomain()
{
const TEST_HOST = "http://www.mozilla.org";
add_disabled_host(TEST_HOST);
pb.removeDataFromDomain("mozilla.org");
check_disabled_host(TEST_HOST, false);
}
function test_login_manager_disabled_hosts_not_cleared_with_uri_contains_domain()
{
const TEST_HOST = "http://ilovemozilla.org";
add_disabled_host(TEST_HOST);
pb.removeDataFromDomain("mozilla.org");
check_disabled_host(TEST_HOST, true);
// Reset state
let lm = Cc["@mozilla.org/login-manager;1"].
getService(Ci.nsILoginManager);
lm.setLoginSavingEnabled(TEST_HOST, true);
check_disabled_host(TEST_HOST, false);
}
function test_login_manager_logins_cleared_with_direct_match()
{
const TEST_HOST = "http://mozilla.org";
add_login(TEST_HOST);
pb.removeDataFromDomain("mozilla.org");
check_login_exists(TEST_HOST, false);
}
function test_login_manager_logins_cleared_with_subdomain()
{
const TEST_HOST = "http://www.mozilla.org";
add_login(TEST_HOST);
pb.removeDataFromDomain("mozilla.org");
check_login_exists(TEST_HOST, false);
}
function tets_login_manager_logins_not_cleared_with_uri_contains_domain()
{
const TEST_HOST = "http://ilovemozilla.org";
add_login(TEST_HOST);
pb.removeDataFromDomain("mozilla.org");
check_login_exists(TEST_HOST, true);
let lm = Cc["@mozilla.org/login-manager;1"].
getService(Ci.nsILoginManager);
lm.removeAllLogins();
check_login_exists(TEST_HOST, false);
}
// Permission Manager
function test_permission_manager_cleared_with_direct_match()
{
const TEST_URI = uri("http://mozilla.org");
add_permission(TEST_URI);
pb.removeDataFromDomain("mozilla.org");
check_permission_exists(TEST_URI, false);
}
function test_permission_manager_cleared_with_subdomain()
{
const TEST_URI = uri("http://www.mozilla.org");
add_permission(TEST_URI);
pb.removeDataFromDomain("mozilla.org");
check_permission_exists(TEST_URI, false);
}
function test_permission_manager_not_cleared_with_uri_contains_domain()
{
const TEST_URI = uri("http://ilovemozilla.org");
add_permission(TEST_URI);
pb.removeDataFromDomain("mozilla.org");
check_permission_exists(TEST_URI, true);
// Reset state
let pm = Cc["@mozilla.org/permissionmanager;1"].
getService(Ci.nsIPermissionManager);
pm.removeAll();
check_permission_exists(TEST_URI, false);
}
// Content Preferences
function test_content_preferences_cleared_with_direct_match()
{
const TEST_URI = uri("http://mozilla.org");
add_preference(TEST_URI);
pb.removeDataFromDomain("mozilla.org");
check_preference_exists(TEST_URI, false);
}
function test_content_preferences_cleared_with_subdomain()
{
const TEST_URI = uri("http://www.mozilla.org");
add_preference(TEST_URI);
pb.removeDataFromDomain("mozilla.org");
check_preference_exists(TEST_URI, false);
}
function test_content_preferecnes_not_cleared_with_uri_contains_domain()
{
const TEST_URI = uri("http://ilovemozilla.org");
add_preference(TEST_URI);
pb.removeDataFromDomain("mozilla.org");
check_preference_exists(TEST_URI, true);
// Reset state
let cp = Cc["@mozilla.org/content-pref/service;1"].
getService(Ci.nsIContentPrefService);
cp.removePref(TEST_URI, PREFERENCE_NAME);
check_preference_exists(TEST_URI, false);
}
// Cache
function test_cache_cleared()
{
// Because this test is asynchronous, it should be the last test
do_check_eq(tests[tests.length - 1], arguments.callee);
// NOTE: We could be more extensive with this test and actually add an entry
// to the cache, and then make sure it is gone. However, we trust that
// the API is well tested, and that when we get the observer
// notification, we have actually cleared the cache.
// This seems to happen asynchronously...
let os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
let observer = {
observe: function(aSubject, aTopic, aData)
{
os.removeObserver(observer, "cacheservice:empty-cache");
do_test_finished();
}
};
os.addObserver(observer, "cacheservice:empty-cache", false);
pb.removeDataFromDomain("mozilla.org");
do_test_pending();
}
let tests = [
// History
test_history_cleared_with_direct_match,
test_history_cleared_with_subdomain,
test_history_not_cleared_with_uri_contains_domain,
// Cookie Service
test_cookie_cleared_with_direct_match,
test_cookie_cleared_with_subdomain,
test_cookie_not_cleared_with_uri_contains_domain,
// Download Manager
// Note: active downloads tested in test_removeDataFromDomain_activeDownloads.js
test_download_history_cleared_with_direct_match,
test_download_history_cleared_with_subdomain,
test_download_history_not_cleared_with_active_direct_match,
// Login Manager
test_login_manager_disabled_hosts_cleared_with_direct_match,
test_login_manager_disabled_hosts_cleared_with_subdomain,
test_login_manager_disabled_hosts_not_cleared_with_uri_contains_domain,
test_login_manager_logins_cleared_with_direct_match,
test_login_manager_logins_cleared_with_subdomain,
tets_login_manager_logins_not_cleared_with_uri_contains_domain,
// Permission Manager
test_permission_manager_cleared_with_direct_match,
test_permission_manager_cleared_with_subdomain,
test_permission_manager_not_cleared_with_uri_contains_domain,
// Content Preferences
test_content_preferences_cleared_with_direct_match,
test_content_preferences_cleared_with_subdomain,
test_content_preferecnes_not_cleared_with_uri_contains_domain,
// Cache
test_cache_cleared,
];
function do_test()
{
for (let i = 0; i < tests.length; i++)
tests[i]();
}

Просмотреть файл

@ -0,0 +1,158 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Private Browsing Tests.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Shawn Wilsher <me@shawnwilsher.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/**
* Test added with bug 460086 to test the behavior of the new API that was added
* to remove all traces of visiting a site.
*/
////////////////////////////////////////////////////////////////////////////////
//// Constants
let pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
getService(Ci.nsIPrivateBrowsingService);
////////////////////////////////////////////////////////////////////////////////
//// Utility Functions
/**
* Creates an nsIURI object for the given file.
*
* @param aFile
* The nsIFile of the URI to create.
* @returns an nsIURI representing aFile.
*/
function uri(aFile)
{
return Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService).
newFileURI(aFile);
}
/**
* Checks to ensure a URI string is in download history or not.
*
* @param aURIString
* The string of the URI to check.
* @param aIsActive
* True if the URI should be actively downloaded, false otherwise.
*/
function check_active_download(aURIString, aIsActive)
{
let dm = Cc["@mozilla.org/download-manager;1"].
getService(Ci.nsIDownloadManager);
let enumerator = dm.activeDownloads;
let found = false;
while (enumerator.hasMoreElements()) {
let dl = enumerator.getNext().QueryInterface(Ci.nsIDownload);
if (dl.source.spec == aURIString)
found = true;
}
let checker = aIsActive ? do_check_true : do_check_false;
checker(found);
}
////////////////////////////////////////////////////////////////////////////////
//// Test Functions
let destFile = dirSvc.get("TmpD", Ci.nsIFile);
destFile.append("dm-test-file");
destFile = uri(destFile);
let data = [
{ source: "http://mozilla.org/direct_match",
target: destFile.spec,
removed: true
},
{ source: "http://www.mozilla.org/subdomain",
target: destFile.spec,
removed: true
},
{ source: "http://ilovemozilla.org/contains_domain",
target: destFile.spec,
removed: false
},
];
function do_test()
{
// We add this data to the database first, but we cannot instantiate the
// download manager service, otherwise these downloads will not be placed in
// the active downloads array.
// Copy the empty downloads database to our profile directory
let downloads = do_get_file("toolkit/components/downloads/test/downloads.empty.sqlite");
downloads.copyTo(dirSvc.get("ProfD", Ci.nsIFile), "downloads.sqlite");
// Open the database
let ss = Cc["@mozilla.org/storage/service;1"].
getService(Ci.mozIStorageService);
let file = dirSvc.get("ProfD", Ci.nsIFile);
file.append("downloads.sqlite");
let db = ss.openDatabase(file);
// Insert the data
let stmt = db.createStatement(
"INSERT INTO moz_downloads (source, target, state, autoResume, entityID) " +
"VALUES (:source, :target, :state, :autoResume, :entityID)"
);
for (let i = 0; i < data.length; i++) {
stmt.params.source = data[i].source;
stmt.params.target = data[i].target;
stmt.params.state = Ci.nsIDownloadManager.DOWNLOAD_PAUSED;
stmt.params.autoResume = 0; // DONT_RESUME is 0
stmt.params.entityID = "foo" // just has to be non-null for our test
stmt.execute();
stmt.reset();
}
stmt.finalize();
stmt = null;
db.close();
db = null;
// Check to make sure it's all there
for (let i = 0; i < data.length; i++)
check_active_download(data[i].source, true);
// Dispatch the remove call
pb.removeDataFromDomain("mozilla.org");
// And check our data
for (let i = 0; i < data.length; i++)
check_active_download(data[i].source, !data[i].removed);
}

Просмотреть файл

@ -109,3 +109,16 @@ function cleanUp()
}
}
cleanUp();
var PRIVATEBROWSING_CONTRACT_ID;
function run_test_on_all_services() {
var contractIDs = [
"@mozilla.org/privatebrowsing;1",
"@mozilla.org/privatebrowsing-wrapper;1"
];
for (var i = 0; i < contractIDs.length; ++i) {
PRIVATEBROWSING_CONTRACT_ID = contractIDs[i];
run_test_on_service();
cleanUp();
}
}

Просмотреть файл

@ -41,7 +41,7 @@
// This test should run before the rest of private browsing service unit tests,
// hence the naming used for this file.
function run_test() {
function run_test_on_service() {
// initialization
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
@ -50,14 +50,14 @@ function run_test() {
prefBranch.setBoolPref("browser.privatebrowsing.keep_current_session", true);
// the contract ID should be available
do_check_true("@mozilla.org/privatebrowsing;1" in Cc);
do_check_true(PRIVATEBROWSING_CONTRACT_ID in Cc);
// the interface should be available
do_check_true("nsIPrivateBrowsingService" in Ci);
// it should be possible to initialize the component
try {
var pb = Cc["@mozilla.org/privatebrowsing;1"].
var pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
getService(Ci.nsIPrivateBrowsingService);
} catch (ex) {
LOG("exception thrown when trying to get the service: " + ex);
@ -222,3 +222,8 @@ function run_test() {
prefBranch.clearUserPref("browser.privatebrowsing.keep_current_session");
}
// Support running tests on both the service itself and its wrapper
function run_test() {
run_test_on_all_services();
}

Просмотреть файл

@ -59,9 +59,9 @@ function is_about_privatebrowsing_available() {
return false;
}
function run_test() {
function run_test_on_service() {
// initialization
var pb = Cc["@mozilla.org/privatebrowsing;1"].
var pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
getService(Ci.nsIPrivateBrowsingService);
var prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
@ -86,3 +86,8 @@ function run_test() {
prefBranch.clearUserPref("browser.privatebrowsing.keep_current_session");
}
}
// Support running tests on both the service itself and its wrapper
function run_test() {
run_test_on_all_services();
}

Просмотреть файл

@ -38,12 +38,12 @@
// This test makes sure the HTTP authenticated sessions are correctly cleared
// when entering and leaving the private browsing mode.
function run_test() {
function run_test_on_service() {
var prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
prefBranch.setBoolPref("browser.privatebrowsing.keep_current_session", true);
var pb = Cc["@mozilla.org/privatebrowsing;1"].
var pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
getService(Ci.nsIPrivateBrowsingService);
var am = Cc["@mozilla.org/network/http-auth-manager;1"].
@ -112,3 +112,8 @@ function run_test() {
prefBranch.clearUserPref("browser.privatebrowsing.keep_current_session");
}
}
// Support running tests on both the service itself and its wrapper
function run_test() {
run_test_on_all_services();
}

Просмотреть файл

@ -38,31 +38,7 @@
// This test checks the browser.privatebrowsing.autostart preference.
function run_test() {
// initialization
var prefsService = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
prefsService.setBoolPref("browser.privatebrowsing.autostart", true);
do_check_true(prefsService.getBoolPref("browser.privatebrowsing.autostart"));
var pb = Cc["@mozilla.org/privatebrowsing;1"].
getService(Ci.nsIPrivateBrowsingService).
QueryInterface(Ci.nsIObserver);
// private browsing not auto-started yet
do_check_false(pb.autoStarted);
// simulate startup to make the PB service read the prefs
pb.observe(null, "profile-after-change", "");
// the private mode should be entered automatically
do_check_true(pb.privateBrowsingEnabled);
// private browsing is auto-started
do_check_true(pb.autoStarted);
// leave private browsing mode
pb.privateBrowsingEnabled = false;
// private browsing not auto-started
do_check_false(pb.autoStarted);
PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing;1";
do_import_script("browser/components/privatebrowsing/test/unit/do_test_privatebrowsing_autostart.js");
do_test();
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше