Bug 427990 Gecko part - Make XLink href work on MathML element. r=jonas.

This commit is contained in:
Bill Gianopoulos 2011-06-24 14:54:28 +02:00
parent adebfbe49f
commit d69ab2c0ea
4 changed files with 269 additions and 86 deletions

View File

@ -62,6 +62,8 @@ NS_INTERFACE_TABLE_HEAD(nsMathMLElement)
NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsMathMLElement)
NS_INTERFACE_TABLE_ENTRY(nsMathMLElement, nsIDOMNode)
NS_INTERFACE_TABLE_ENTRY(nsMathMLElement, nsIDOMElement)
NS_INTERFACE_TABLE_ENTRY(nsMathMLElement, nsILink)
NS_INTERFACE_TABLE_ENTRY(nsMathMLElement, Link)
NS_OFFSET_AND_INTERFACE_TABLE_END
NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MathMLElement)
@ -77,6 +79,8 @@ nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
{
static const char kMathMLStyleSheetURI[] = "resource://gre-resources/mathml.css";
Link::ResetLinkState(false);
nsresult rv = nsMathMLElementBase::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
@ -101,6 +105,16 @@ nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
return rv;
}
void
nsMathMLElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
{
// If this link is ever reinserted into a document, it might
// be under a different xml:base, so forget the cached state now.
Link::ResetLinkState(false);
nsMathMLElementBase::UnbindFromTree(aDeep, aNullParent);
}
PRBool
nsMathMLElement::ParseAttribute(PRInt32 aNamespaceID,
nsIAtom* aAttribute,
@ -438,12 +452,27 @@ nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
}
}
nsresult
nsMathMLElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
{
nsresult rv = nsGenericElement::PreHandleEvent(aVisitor);
NS_ENSURE_SUCCESS(rv, rv);
return PreHandleEventForLinks(aVisitor);
}
nsresult
nsMathMLElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
{
return PostHandleEventForLinks(aVisitor);
}
NS_IMPL_ELEMENT_CLONE(nsMathMLElement)
nsEventStates
nsMathMLElement::IntrinsicState() const
{
return nsMathMLElementBase::IntrinsicState() |
return Link::LinkState() | nsMathMLElementBase::IntrinsicState() |
(mIncrementScriptLevel ? NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL : nsEventStates());
}
@ -465,3 +494,185 @@ nsMathMLElement::SetIncrementScriptLevel(PRBool aIncrementScriptLevel,
UpdateState(true);
}
PRBool
nsMathMLElement::IsFocusable(PRInt32 *aTabIndex, PRBool aWithMouse)
{
nsCOMPtr<nsIURI> uri;
if (IsLink(getter_AddRefs(uri))) {
if (aTabIndex) {
*aTabIndex = ((sTabFocusModel & eTabFocus_linksMask) == 0 ? -1 : 0);
}
return PR_TRUE;
}
if (aTabIndex) {
*aTabIndex = -1;
}
return PR_FALSE;
}
PRBool
nsMathMLElement::IsLink(nsIURI** aURI) const
{
// http://www.w3.org/TR/2010/REC-MathML3-20101021/chapter6.html#interf.link
// The REC says that the following elements should not be linking elements:
nsIAtom* tag = Tag();
if (tag == nsGkAtoms::mprescripts_ ||
tag == nsGkAtoms::none ||
tag == nsGkAtoms::malignmark_ ||
tag == nsGkAtoms::maligngroup_) {
*aURI = nsnull;
return PR_FALSE;
}
PRBool hasHref = PR_FALSE;
const nsAttrValue* href = mAttrsAndChildren.GetAttr(nsGkAtoms::href,
kNameSpaceID_None);
if (href) {
// MathML href
// The REC says: "When user agents encounter MathML elements with both href
// and xlink:href attributes, the href attribute should take precedence."
hasHref = PR_TRUE;
} else {
// To be a clickable XLink for styling and interaction purposes, we require:
//
// xlink:href - must be set
// xlink:type - must be unset or set to "" or set to "simple"
// xlink:show - must be unset or set to "", "new" or "replace"
// xlink:actuate - must be unset or set to "" or "onRequest"
//
// For any other values, we're either not a *clickable* XLink, or the end
// result is poorly specified. Either way, we return PR_FALSE.
static nsIContent::AttrValuesArray sTypeVals[] =
{ &nsGkAtoms::_empty, &nsGkAtoms::simple, nsnull };
static nsIContent::AttrValuesArray sShowVals[] =
{ &nsGkAtoms::_empty, &nsGkAtoms::_new, &nsGkAtoms::replace, nsnull };
static nsIContent::AttrValuesArray sActuateVals[] =
{ &nsGkAtoms::_empty, &nsGkAtoms::onRequest, nsnull };
// Optimization: check for href first for early return
href = mAttrsAndChildren.GetAttr(nsGkAtoms::href,
kNameSpaceID_XLink);
if (href &&
FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::type,
sTypeVals, eCaseMatters) !=
nsIContent::ATTR_VALUE_NO_MATCH &&
FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
sShowVals, eCaseMatters) !=
nsIContent::ATTR_VALUE_NO_MATCH &&
FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::actuate,
sActuateVals, eCaseMatters) !=
nsIContent::ATTR_VALUE_NO_MATCH) {
hasHref = PR_TRUE;
}
}
if (hasHref) {
nsCOMPtr<nsIURI> baseURI = GetBaseURI();
// Get absolute URI
nsAutoString hrefStr;
href->ToString(hrefStr);
nsContentUtils::NewURIWithDocumentCharset(aURI, hrefStr,
GetOwnerDoc(), baseURI);
// must promise out param is non-null if we return true
return !!*aURI;
}
*aURI = nsnull;
return PR_FALSE;
}
void
nsMathMLElement::GetLinkTarget(nsAString& aTarget)
{
const nsAttrValue* target = mAttrsAndChildren.GetAttr(nsGkAtoms::target,
kNameSpaceID_XLink);
if (target) {
target->ToString(aTarget);
}
if (aTarget.IsEmpty()) {
static nsIContent::AttrValuesArray sShowVals[] =
{ &nsGkAtoms::_new, &nsGkAtoms::replace, nsnull };
switch (FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
sShowVals, eCaseMatters)) {
case 0:
aTarget.AssignLiteral("_blank");
return;
case 1:
return;
}
nsIDocument* ownerDoc = GetOwnerDoc();
if (ownerDoc) {
ownerDoc->GetBaseTarget(aTarget);
}
}
}
nsLinkState
nsMathMLElement::GetLinkState() const
{
return Link::GetLinkState();
}
void
nsMathMLElement::RequestLinkStateUpdate()
{
UpdateLinkState(Link::LinkState());
}
already_AddRefed<nsIURI>
nsMathMLElement::GetHrefURI() const
{
nsCOMPtr<nsIURI> hrefURI;
return IsLink(getter_AddRefs(hrefURI)) ? hrefURI.forget() : nsnull;
}
nsresult
nsMathMLElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
PRBool aNotify)
{
nsresult rv = nsMathMLElementBase::SetAttr(aNameSpaceID, aName, aPrefix,
aValue, aNotify);
// The ordering of the parent class's SetAttr call and Link::ResetLinkState
// is important here! The attribute is not set until SetAttr returns, and
// we will need the updated attribute value because notifying the document
// that content states have changed will call IntrinsicState, which will try
// to get updated information about the visitedness from Link.
if (aName == nsGkAtoms::href &&
(aNameSpaceID == kNameSpaceID_None ||
aNameSpaceID == kNameSpaceID_XLink)) {
Link::ResetLinkState(!!aNotify);
}
return rv;
}
nsresult
nsMathMLElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttr,
PRBool aNotify)
{
nsresult rv = nsMathMLElementBase::UnsetAttr(aNameSpaceID, aAttr, aNotify);
// The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
// is important here! The attribute is not unset until UnsetAttr returns, and
// we will need the updated attribute value because notifying the document
// that content states have changed will call IntrinsicState, which will try
// to get updated information about the visitedness from Link.
if (aAttr == nsGkAtoms::href &&
(aNameSpaceID == kNameSpaceID_None ||
aNameSpaceID == kNameSpaceID_XLink)) {
Link::ResetLinkState(!!aNotify);
}
return rv;
}

View File

@ -42,6 +42,8 @@
#include "nsMappedAttributeElement.h"
#include "nsIDOMElement.h"
#include "nsILink.h"
#include "Link.h"
class nsCSSValue;
@ -50,12 +52,15 @@ typedef nsMappedAttributeElement nsMathMLElementBase;
/*
* The base class for MathML elements.
*/
class nsMathMLElement : public nsMathMLElementBase
, public nsIDOMElement
class nsMathMLElement : public nsMathMLElementBase,
public nsIDOMElement,
public nsILink,
public mozilla::dom::Link
{
public:
nsMathMLElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsMathMLElementBase(aNodeInfo), mIncrementScriptLevel(PR_FALSE)
: nsMathMLElementBase(aNodeInfo), Link(this),
mIncrementScriptLevel(PR_FALSE)
{}
// Implementation of nsISupports is inherited from nsMathMLElementBase
@ -69,6 +74,8 @@ public:
nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers);
virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
PRBool aNullParent = PR_TRUE);
virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
nsIAtom* aAttribute,
@ -89,6 +96,8 @@ public:
static void MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
nsRuleData* aRuleData);
virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
nsresult Clone(nsINodeInfo*, nsINode**) const;
virtual nsEventStates IntrinsicState() const;
virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
@ -100,6 +109,26 @@ public:
return mIncrementScriptLevel;
}
NS_IMETHOD LinkAdded() { return NS_OK; }
NS_IMETHOD LinkRemoved() { return NS_OK; }
virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull,
PRBool aWithMouse = PR_FALSE);
virtual PRBool IsLink(nsIURI** aURI) const;
virtual void GetLinkTarget(nsAString& aTarget);
virtual nsLinkState GetLinkState() const;
virtual void RequestLinkStateUpdate();
virtual already_AddRefed<nsIURI> GetHrefURI() const;
nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, PRBool aNotify)
{
return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
}
virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
PRBool aNotify);
virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
PRBool aNotify);
virtual nsXPCClassInfo* GetClassInfo();
private:
PRPackedBool mIncrementScriptLevel;

View File

@ -3410,37 +3410,20 @@ DocumentViewerImpl::GetPopupLinkNode(nsIDOMNode** aNode)
// find out if we have a link in our ancestry
while (node) {
// are we an anchor?
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(node));
nsCOMPtr<nsIDOMHTMLAreaElement> area;
nsCOMPtr<nsIDOMHTMLLinkElement> link;
nsAutoString xlinkType;
if (!anchor) {
// area?
area = do_QueryInterface(node);
if (!area) {
// link?
link = do_QueryInterface(node);
if (!link) {
// XLink?
nsCOMPtr<nsIDOMElement> element(do_QueryInterface(node));
if (element) {
element->GetAttributeNS(NS_LITERAL_STRING("http://www.w3.org/1999/xlink"),NS_LITERAL_STRING("type"),xlinkType);
}
}
nsCOMPtr<nsIContent> content(do_QueryInterface(node));
if (content) {
nsCOMPtr<nsIURI> hrefURI = content->GetHrefURI();
if (hrefURI) {
*aNode = node;
NS_IF_ADDREF(*aNode); // addref
return NS_OK;
}
}
if (anchor || area || link || xlinkType.EqualsLiteral("simple")) {
*aNode = node;
NS_IF_ADDREF(*aNode); // addref
return NS_OK;
}
else {
// if not, get our parent and keep trying...
nsCOMPtr<nsIDOMNode> parentNode;
node->GetParentNode(getter_AddRefs(parentNode));
node = parentNode;
}
// get our parent and keep trying...
nsCOMPtr<nsIDOMNode> parentNode;
node->GetParentNode(getter_AddRefs(parentNode));
node = parentNode;
}
// if we have no node, fail

View File

@ -4365,66 +4365,26 @@ nsresult PresShell::GetLinkLocation(nsIDOMNode* aNode, nsAString& aLocationStrin
#endif
NS_ENSURE_ARG_POINTER(aNode);
nsresult rv;
nsAutoString anchorText;
static const char strippedChars[] = "\t\r\n";
// are we an anchor?
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aNode));
nsCOMPtr<nsIDOMHTMLAreaElement> area;
nsCOMPtr<nsIDOMHTMLLinkElement> link;
nsAutoString xlinkType;
if (anchor) {
rv = anchor->GetHref(anchorText);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// area?
area = do_QueryInterface(aNode);
if (area) {
rv = area->GetHref(anchorText);
nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
if (content) {
nsCOMPtr<nsIURI> hrefURI = content->GetHrefURI();
if (hrefURI) {
nsCAutoString specUTF8;
nsresult rv = hrefURI->GetSpec(specUTF8);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// link?
link = do_QueryInterface(aNode);
if (link) {
rv = link->GetHref(anchorText);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// Xlink?
nsCOMPtr<nsIDOMElement> element(do_QueryInterface(aNode));
if (element) {
NS_NAMED_LITERAL_STRING(xlinkNS,"http://www.w3.org/1999/xlink");
element->GetAttributeNS(xlinkNS,NS_LITERAL_STRING("type"),xlinkType);
if (xlinkType.EqualsLiteral("simple")) {
element->GetAttributeNS(xlinkNS,NS_LITERAL_STRING("href"),anchorText);
if (!anchorText.IsEmpty()) {
// Resolve the full URI using baseURI property
nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIURI> baseURI = node->GetBaseURI();
nsAutoString anchorText;
CopyUTF8toUTF16(specUTF8, anchorText);
nsCAutoString spec;
rv = baseURI->Resolve(NS_ConvertUTF16toUTF8(anchorText),spec);
NS_ENSURE_SUCCESS(rv, rv);
CopyUTF8toUTF16(spec, anchorText);
}
}
}
}
// Remove all the '\t', '\r' and '\n' from 'anchorText'
static const char strippedChars[] = "\t\r\n";
anchorText.StripChars(strippedChars);
aLocationString = anchorText;
return NS_OK;
}
}
if (anchor || area || link || xlinkType.EqualsLiteral("simple")) {
//Remove all the '\t', '\r' and '\n' from 'anchorText'
anchorText.StripChars(strippedChars);
aLocationString = anchorText;
return NS_OK;
}
// if no link, fail.
return NS_ERROR_FAILURE;
}