Bug 419416. Follow useful rules for handling ARIA properties on a frame, iframe and body elements. r=ginn.chen, a=dsicore

This commit is contained in:
aaronleventhal@moonset.net 2008-03-14 13:49:38 -07:00
parent 7b83e29004
commit 8b18471351
15 changed files with 232 additions and 88 deletions

View File

@ -117,5 +117,13 @@ interface nsPIAccessible : nsISupports
* nsnull if none.
*/
void setRoleMapEntry(in nsRoleMapEntryPtr aRoleMapEntry);
/**
* Maps ARIA state attributes to state of accessible. Note the given state
* argument should hold states for accessible before you pass it into this
* method.
* @param in/out where to fill the states into.
*/
void getARIAState(out unsigned long aState);
};

View File

@ -234,3 +234,8 @@ ACCESSIBILITY_ATOM(level, "level")
ACCESSIBILITY_ATOM(posinset, "posinset")
ACCESSIBILITY_ATOM(setsize, "setsize")
ACCESSIBILITY_ATOM(lineNumber, "line-number")
ACCESSIBILITY_ATOM(containerRelevant, "container-relevant")
ACCESSIBILITY_ATOM(containerLive, "container-live")
ACCESSIBILITY_ATOM(containerChannel, "container-channel")
ACCESSIBILITY_ATOM(containerAtomic, "container-atomic")
ACCESSIBILITY_ATOM(containerBusy, "container-busy")

View File

@ -453,6 +453,10 @@ nsAccessibilityService::CreateRootAccessible(nsIPresShell *aShell,
nsCOMPtr<nsPIAccessNode> privateAccessNode(do_QueryInterface(*aRootAcc));
privateAccessNode->Init();
nsRoleMapEntry *roleMapEntry = nsAccUtils::GetRoleMapEntry(rootNode);
nsCOMPtr<nsPIAccessible> privateAccessible =
do_QueryInterface(privateAccessNode);
privateAccessible->SetRoleMapEntry(roleMapEntry);
NS_ADDREF(*aRootAcc);

View File

@ -933,3 +933,33 @@ nsAccUtils::IsARIAPropForObjectAttr(nsIAtom *aAtom)
aAtom != nsAccessibilityAtoms::aria_valuenow &&
aAtom != nsAccessibilityAtoms::aria_valuetext;
}
void nsAccUtils::GetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
nsIContent *aStartContent, nsIContent *aTopContent)
{
nsAutoString atomic, live, relevant, channel, busy;
nsIContent *ancestor = aStartContent;
while (ancestor) {
if (relevant.IsEmpty() &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_relevant, relevant))
SetAccAttr(aAttributes, nsAccessibilityAtoms::containerRelevant, relevant);
if (live.IsEmpty() &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_live, live))
SetAccAttr(aAttributes, nsAccessibilityAtoms::containerLive, live);
if (channel.IsEmpty() &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_channel, channel))
SetAccAttr(aAttributes, nsAccessibilityAtoms::containerChannel, channel);
if (atomic.IsEmpty() &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_atomic, atomic))
SetAccAttr(aAttributes, nsAccessibilityAtoms::containerAtomic, atomic);
if (busy.IsEmpty() &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_busy, busy))
SetAccAttr(aAttributes, nsAccessibilityAtoms::containerBusy, busy);
if (ancestor == aTopContent)
break;
ancestor = ancestor->GetParent();
if (!ancestor) {
ancestor = aTopContent; // Use <body>/<frameset>
}
}
}

View File

@ -366,6 +366,16 @@ public:
// Return PR_TRUE if the ARIA property should always be exposed as an object attribute
static PRBool IsARIAPropForObjectAttr(nsIAtom *aAtom);
/**
* Get container-foo live region attributes for the given node
* @param aAttributes Where to store the attributes
* @param aStartContent Node to start from
* @param aTopContent Node to end at
*/
static void GetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
nsIContent *aStartContent, nsIContent *aTopContent);
};
#endif

View File

@ -489,10 +489,9 @@ NS_IMETHODIMP nsAccessible::SetNextSibling(nsIAccessible *aNextSibling)
nsIContent *nsAccessible::GetRoleContent(nsIDOMNode *aDOMNode)
{
// Given the DOM node for an acessible, return content node that
// we should look at role string from
// we should look for ARIA properties on.
// For non-document accessibles, this is the associated content node.
// For doc accessibles, first try the <body> if it's HTML and there is
// a role attribute used there.
// For doc accessibles, use the <body>/<frameset> if it's HTML.
// For any other doc accessible , this is the document element.
nsCOMPtr<nsIContent> content(do_QueryInterface(aDOMNode));
if (!content) {
@ -504,7 +503,7 @@ nsIContent *nsAccessible::GetRoleContent(nsIDOMNode *aDOMNode)
htmlDoc->GetBody(getter_AddRefs(bodyElement));
content = do_QueryInterface(bodyElement);
}
if (!content) {
else {
nsCOMPtr<nsIDOMElement> docElement;
domDoc->GetDocumentElement(getter_AddRefs(docElement));
content = do_QueryInterface(docElement);
@ -2003,19 +2002,20 @@ NS_IMETHODIMP nsAccessible::GetFinalRole(PRUint32 *aRole)
NS_IMETHODIMP
nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
{
NS_ENSURE_ARG_POINTER(aAttributes);
*aAttributes = nsnull;
NS_ENSURE_ARG_POINTER(aAttributes); // In/out param. Created if necessary.
nsCOMPtr<nsIContent> content = GetRoleContent(mDOMNode);
if (!content) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPersistentProperties> attributes =
do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
NS_ENSURE_TRUE(attributes, NS_ERROR_OUT_OF_MEMORY);
nsAccEvent::GetLastEventAttributes(mDOMNode, attributes);
nsCOMPtr<nsIPersistentProperties> attributes = *aAttributes;
if (!attributes) {
// Create only if an array wasn't already passed in
attributes = do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
NS_ENSURE_TRUE(attributes, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*aAttributes = attributes);
}
nsresult rv = GetAttributesInternal(attributes);
NS_ENSURE_SUCCESS(rv, rv);
@ -2023,13 +2023,11 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
nsAutoString id;
nsAutoString oldValueUnused;
if (nsAccUtils::GetID(content, id)) {
// Expose ID. If an <iframe id> exists override the one on the <body> of the source doc,
// because the specific instance is what makes the ID useful for scripts
attributes->SetStringProperty(NS_LITERAL_CSTRING("id"), id, oldValueUnused);
}
nsAutoString _class;
if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::_class, _class))
nsAccUtils::SetAccAttr(attributes, nsAccessibilityAtoms::_class, _class);
nsAutoString xmlRoles;
if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::role, xmlRoles)) {
attributes->SetStringProperty(NS_LITERAL_CSTRING("xml-roles"), xmlRoles, oldValueUnused);
@ -2046,29 +2044,6 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
}
// Get container-foo computed live region properties based on the closest container with
// the live region attribute
nsAutoString atomic, live, relevant, channel, busy;
nsIContent *ancestor = content;
while (ancestor) {
if (relevant.IsEmpty() &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_relevant, relevant))
attributes->SetStringProperty(NS_LITERAL_CSTRING("container-relevant"), relevant, oldValueUnused);
if (live.IsEmpty() &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_live, live))
attributes->SetStringProperty(NS_LITERAL_CSTRING("container-live"), live, oldValueUnused);
if (channel.IsEmpty() &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_channel, channel))
attributes->SetStringProperty(NS_LITERAL_CSTRING("container-channel"), channel, oldValueUnused);
if (atomic.IsEmpty() &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_atomic, atomic))
attributes->SetStringProperty(NS_LITERAL_CSTRING("container-atomic"), atomic, oldValueUnused);
if (busy.IsEmpty() &&
ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_busy, busy))
attributes->SetStringProperty(NS_LITERAL_CSTRING("container-busy"), busy, oldValueUnused);
ancestor = ancestor->GetParent();
}
PRUint32 role = Role(this);
if (role == nsIAccessibleRole::ROLE_CHECKBUTTON ||
role == nsIAccessibleRole::ROLE_PUSHBUTTON ||
@ -2168,15 +2143,16 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
}
}
attributes.swap(*aAttributes);
return NS_OK;
}
nsresult
nsAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
{
nsCOMPtr<nsIDOMElement> element(do_QueryInterface(GetRoleContent(mDOMNode)));
// Attributes set by this method will not be used to override attributes on a sub-document accessible
// when there is a <frame>/<iframe> element that spawned the sub-document
nsIContent *content = GetRoleContent(mDOMNode);
nsCOMPtr<nsIDOMElement> element(do_QueryInterface(content));
NS_ENSURE_TRUE(element, NS_ERROR_UNEXPECTED);
nsAutoString tagName;
@ -2187,6 +2163,43 @@ nsAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
oldValueUnused);
}
nsAccEvent::GetLastEventAttributes(mDOMNode, aAttributes);
// Expose class because it may have useful microformat information
// Let the class from an iframe's document be exposed, don't override from <iframe class>
nsAutoString _class;
if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::_class, _class))
nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::_class, _class);
// Get container-foo computed live region properties based on the closest container with
// the live region attribute.
// Inner nodes override outer nodes within the same document --
// The inner nodes can be used to override live region behavior on more general outer nodes
// However, nodes in outer documents override nodes in inner documents:
// Outer doc author may want to override properties on a widget they used in an iframe
nsCOMPtr<nsIDOMNode> startNode = mDOMNode;
nsIContent *startContent = content;
while (PR_TRUE) {
NS_ENSURE_STATE(startContent);
nsIDocument *doc = startContent->GetDocument();
nsCOMPtr<nsIDOMNode> docNode = do_QueryInterface(doc);
NS_ENSURE_STATE(docNode);
nsIContent *topContent = GetRoleContent(docNode);
NS_ENSURE_STATE(topContent);
nsAccUtils::GetLiveContainerAttributes(aAttributes, startContent, topContent);
// Allow ARIA live region markup from outer documents to override
nsCOMPtr<nsISupports> container = doc->GetContainer();
nsIDocShellTreeItem *docShellTreeItem = nsnull;
if (container)
CallQueryInterface(container, &docShellTreeItem);
nsIDocShellTreeItem *sameTypeParent = nsnull;
docShellTreeItem->GetSameTypeParent(&sameTypeParent);
if (!sameTypeParent || sameTypeParent == docShellTreeItem)
break;
nsIDocument *parentDoc = doc->GetParentDocument();
startContent = parentDoc->FindContentForSubDocument(doc);
}
return NS_OK;
}
@ -2266,7 +2279,7 @@ nsAccessible::GetFinalState(PRUint32 *aState, PRUint32 *aExtraState)
NS_ENSURE_SUCCESS(rv, rv);
// Apply ARIA states to be sure accessible states will be overriden.
*aState |= GetARIAState();
GetARIAState(aState);
if (mRoleMapEntry && mRoleMapEntry->role == nsIAccessibleRole::ROLE_PAGETAB) {
if (*aState & nsIAccessibleStates::STATE_FOCUSED) {
@ -2391,39 +2404,39 @@ nsAccessible::GetFinalState(PRUint32 *aState, PRUint32 *aExtraState)
return NS_OK;
}
PRUint32
nsAccessible::GetARIAState()
nsresult
nsAccessible::GetARIAState(PRUint32 *aState)
{
// Test for universal states first
nsIContent *content = GetRoleContent(mDOMNode);
if (!content) {
return 0;
return NS_OK;
}
PRUint32 ariaState = 0;
PRUint32 index = 0;
while (MappedAttrState(content, &ariaState, &nsARIAMap::gWAIUnivStateMap[index])) {
while (MappedAttrState(content, aState, &nsARIAMap::gWAIUnivStateMap[index])) {
++ index;
}
if (!mRoleMapEntry)
return ariaState;
return NS_OK;
// Once DHTML role is used, we're only readonly if DHTML readonly used
ariaState &= ~nsIAccessibleStates::STATE_READONLY;
ariaState |= mRoleMapEntry->state;
if (MappedAttrState(content, &ariaState, &mRoleMapEntry->attributeMap1) &&
MappedAttrState(content, &ariaState, &mRoleMapEntry->attributeMap2) &&
MappedAttrState(content, &ariaState, &mRoleMapEntry->attributeMap3) &&
MappedAttrState(content, &ariaState, &mRoleMapEntry->attributeMap4) &&
MappedAttrState(content, &ariaState, &mRoleMapEntry->attributeMap5) &&
MappedAttrState(content, &ariaState, &mRoleMapEntry->attributeMap6) &&
MappedAttrState(content, &ariaState, &mRoleMapEntry->attributeMap7)) {
MappedAttrState(content, &ariaState, &mRoleMapEntry->attributeMap8);
if (MappedAttrState(content, aState, &mRoleMapEntry->attributeMap1) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap2) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap3) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap4) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap5) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap6) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap7)) {
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap8);
}
return ariaState;
return NS_OK;
}
// Not implemented by this class

View File

@ -130,13 +130,6 @@ public:
*/
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
/**
* Maps ARIA state attributes to state of accessible. Note the given state
* argument should hold states for accessible before you pass it into this
* method.
*/
PRUint32 GetARIAState();
#ifdef DEBUG_A11Y
static PRBool IsTextInterfaceSupportCorrect(nsIAccessible *aAccessible);
#endif

View File

@ -152,14 +152,14 @@ NS_IMETHODIMP nsDocAccessible::GetName(nsAString& aName)
{
nsresult rv = NS_OK;
aName.Truncate();
if (mRoleMapEntry) {
rv = nsAccessible::GetName(aName);
if (mParent) {
rv = mParent->GetName(aName); // Allow owning iframe to override the name
}
if (aName.IsEmpty()) {
rv = GetTitle(aName);
rv = nsAccessible::GetName(aName); // Allow name via aria-labelledby or title attribute
}
if (aName.IsEmpty() && mParent) {
rv = mParent->GetName(aName);
if (aName.IsEmpty()) {
rv = GetTitle(aName); // Finally try title element
}
return rv;
@ -202,17 +202,38 @@ NS_IMETHODIMP nsDocAccessible::GetRole(PRUint32 *aRole)
return NS_OK;
}
NS_IMETHODIMP nsDocAccessible::GetValue(nsAString& aValue)
NS_IMETHODIMP nsDocAccessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
{
return GetURL(aValue);
NS_ENSURE_STATE(mDocument);
mRoleMapEntry = aRoleMapEntry;
// Allow use of ARIA role from outer to override
nsIDocument *parentDoc = mDocument->GetParentDocument();
NS_ENSURE_TRUE(parentDoc, NS_ERROR_FAILURE);
nsIContent *ownerContent = parentDoc->FindContentForSubDocument(mDocument);
nsCOMPtr<nsIDOMNode> ownerNode(do_QueryInterface(ownerContent));
if (ownerNode) {
nsRoleMapEntry *roleMapEntry = nsAccUtils::GetRoleMapEntry(ownerNode);
if (roleMapEntry)
mRoleMapEntry = roleMapEntry; // Override
}
return NS_OK;
}
NS_IMETHODIMP
nsDocAccessible::GetDescription(nsAString& aDescription)
{
nsAutoString description;
GetTextFromRelationID(nsAccessibilityAtoms::aria_describedby, description);
aDescription = description;
if (mParent)
mParent->GetDescription(aDescription);
if (aDescription.IsEmpty()) {
nsAutoString description;
GetTextFromRelationID(nsAccessibilityAtoms::aria_describedby, description);
aDescription = description;
}
return NS_OK;
}
@ -265,6 +286,31 @@ nsDocAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
return NS_OK;
}
NS_IMETHODIMP
nsDocAccessible::GetARIAState(PRUint32 *aState)
{
// Combine with states from outer doc
NS_ENSURE_ARG_POINTER(aState);
nsresult rv = nsAccessible::GetARIAState(aState);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsPIAccessible> privateParentAccessible = do_QueryInterface(mParent);
if (privateParentAccessible) // Allow iframe/frame etc. to have final state override via ARIA
return privateParentAccessible->GetARIAState(aState);
return rv;
}
NS_IMETHODIMP
nsDocAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
{
nsAccessible::GetAttributes(aAttributes);
if (mParent) {
mParent->GetAttributes(aAttributes); // Add parent attributes (override inner)
}
return NS_OK;
}
NS_IMETHODIMP nsDocAccessible::GetFocusedChild(nsIAccessible **aFocusedChild)
{
if (!gLastFocusedNode) {
@ -502,18 +548,10 @@ NS_IMETHODIMP nsDocAccessible::Init()
AddEventListeners();
nsresult rv = nsHyperTextAccessibleWrap::Init();
nsCOMPtr<nsIAccessible> parentAccessible; // Ensure outer doc mParent accessible
GetParent(getter_AddRefs(parentAccessible));
if (mRoleMapEntry && mRoleMapEntry->role != nsIAccessibleRole::ROLE_DIALOG &&
mRoleMapEntry->role != nsIAccessibleRole::ROLE_APPLICATION &&
mRoleMapEntry->role != nsIAccessibleRole::ROLE_ALERT &&
mRoleMapEntry->role != nsIAccessibleRole::ROLE_DOCUMENT) {
// Document accessible can only have certain roles
// This was set in nsAccessible::Init() based on dynamic role attribute
mRoleMapEntry = nsnull; // role attribute is not valid for a document
}
return rv;
return nsHyperTextAccessibleWrap::Init();
}
NS_IMETHODIMP nsDocAccessible::Shutdown()

View File

@ -74,10 +74,12 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap,
virtual ~nsDocAccessible();
NS_IMETHOD GetRole(PRUint32 *aRole);
NS_IMETHOD SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry);
NS_IMETHOD GetName(nsAString& aName);
NS_IMETHOD GetValue(nsAString& aValue);
NS_IMETHOD GetDescription(nsAString& aDescription);
NS_IMETHOD GetARIAState(PRUint32 *aState);
NS_IMETHOD GetState(PRUint32 *aState, PRUint32 *aExtraState);
NS_IMETHOD GetAttributes(nsIPersistentProperties **aAttributes);
NS_IMETHOD GetFocusedChild(nsIAccessible **aFocusedChild);
NS_IMETHOD GetParent(nsIAccessible **aParent);
NS_IMETHOD TakeFocus(void);

View File

@ -155,3 +155,15 @@ void nsOuterDocAccessible::CacheChildren()
privateInnerAccessible->SetNextSibling(nsnull);
}
nsresult
nsOuterDocAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
{
nsAutoString tag;
aAttributes->GetStringProperty(NS_LITERAL_CSTRING("tag"), tag);
if (!tag.IsEmpty()) {
// We're overriding the ARIA attributes on an sub document, but we don't want to
// override the other attributes
return NS_OK;
}
return nsAccessible::GetAttributesInternal(aAttributes);
}

View File

@ -58,6 +58,7 @@ class nsOuterDocAccessible : public nsAccessibleWrap
NS_IMETHOD GetChildAtPoint(PRInt32 aX, PRInt32 aY,
nsIAccessible **aAccessible);
void CacheChildren();
nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
};
#endif

View File

@ -448,12 +448,15 @@ PRBool nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible,
// Check for aria-activedescendant, which changes which element has focus
nsCOMPtr<nsIDOMNode> finalFocusNode = aNode;
nsCOMPtr<nsIAccessible> finalFocusAccessible = aAccessible;
nsCOMPtr<nsIContent> finalFocusContent = do_QueryInterface(aNode);
nsCOMPtr<nsIContent> finalFocusContent = GetRoleContent(finalFocusNode);
if (finalFocusContent) {
nsAutoString id;
if (finalFocusContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_activedescendant, id)) {
nsCOMPtr<nsIDOMDocument> domDoc;
aNode->GetOwnerDocument(getter_AddRefs(domDoc));
if (!domDoc) { // Maybe the passed-in node actually is a doc
domDoc = do_QueryInterface(aNode);
}
if (!domDoc) {
return PR_FALSE;
}

View File

@ -318,7 +318,7 @@ __try {
GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
if (xpAccessible) {
nsAutoString value;
if (NS_FAILED(xpAccessible->GetValue(value)))
if (NS_FAILED(xpAccessible->GetValue(value)) || value.IsEmpty())
return S_FALSE;
*pszValue = ::SysAllocString(value.get());

View File

@ -272,3 +272,23 @@ STDMETHODIMP nsDocAccessibleWrap::put_alternateViewMediaTypes( /* [in] */ BSTR _
return E_NOTIMPL;
}
STDMETHODIMP nsDocAccessibleWrap::get_accValue(
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ BSTR __RPC_FAR *pszValue)
{
// For backwards-compat, we still support old MSAA hack to provide URL in accValue
*pszValue = NULL;
// Check for real value first
HRESULT hr = nsAccessibleWrap::get_accValue(varChild, pszValue);
if (FAILED(hr) || *pszValue || varChild.lVal != CHILDID_SELF)
return hr;
// If document is being used to create a widget, don't use the URL hack
PRUint32 role = Role(this);
if (role != nsIAccessibleRole::ROLE_DOCUMENT &&
role != nsIAccessibleRole::ROLE_APPLICATION &&
role != nsIAccessibleRole::ROLE_DIALOG &&
role != nsIAccessibleRole::ROLE_ALERT)
return hr;
return get_URL(pszValue);
}

View File

@ -87,6 +87,11 @@ public:
/* [in] */ VARIANT varChild,
/* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild);
// Override get_accValue to provide URL when no other value is available
virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accValue(
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ BSTR __RPC_FAR *pszValue);
NS_IMETHOD FireAnchorJumpEvent();
};