mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 427990 Gecko part - Make XLink href work on MathML element. r=jonas.
This commit is contained in:
parent
adebfbe49f
commit
d69ab2c0ea
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user