All the SizeOf{In,Ex}cludingThis() functions take a MallocSizeOf function
which measures memory blocks. This patch introduces a new type, SizeOfState,
which includes a MallocSizeOf function *and* a table of already-measured
pointers, called SeenPtrs. This gives us a general mechanism to measure
graph-like data structures, by recording which nodes have already been
measured. (This approach is used in a number of existing reporters, but not in
a uniform fashion.)
The patch also converts the window memory reporting to use SizeOfState in a lot
of places, all the way through to the measurement of Elements. This is a
precursor for bug 1383977 which will measure Stylo elements, which involve
Arcs.
The patch also converts the existing mAlreadyMeasuredOrphanTrees table in the
OrphanReporter to use the new mechanism.
417 lines
11 KiB
C++
417 lines
11 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/dom/HTMLAnchorElement.h"
|
|
|
|
#include "mozilla/dom/HTMLAnchorElementBinding.h"
|
|
#include "mozilla/EventDispatcher.h"
|
|
#include "mozilla/EventStates.h"
|
|
#include "mozilla/MemoryReporting.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsHTMLDNSPrefetch.h"
|
|
#include "nsAttrValueOrString.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsPresContext.h"
|
|
#include "nsIURI.h"
|
|
|
|
NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor)
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
#define ANCHOR_ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_))
|
|
|
|
// Anchor element specific bits
|
|
enum {
|
|
// Indicates that a DNS Prefetch has been requested from this Anchor elem
|
|
HTML_ANCHOR_DNS_PREFETCH_REQUESTED = ANCHOR_ELEMENT_FLAG_BIT(0),
|
|
|
|
// Indicates that a DNS Prefetch was added to the deferral queue
|
|
HTML_ANCHOR_DNS_PREFETCH_DEFERRED = ANCHOR_ELEMENT_FLAG_BIT(1)
|
|
};
|
|
|
|
ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
|
|
|
|
#undef ANCHOR_ELEMENT_FLAG_BIT
|
|
|
|
// static
|
|
const DOMTokenListSupportedToken HTMLAnchorElement::sSupportedRelValues[] = {
|
|
"noreferrer",
|
|
"noopener",
|
|
nullptr
|
|
};
|
|
|
|
HTMLAnchorElement::~HTMLAnchorElement()
|
|
{
|
|
}
|
|
|
|
bool
|
|
HTMLAnchorElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
|
|
{
|
|
return HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
|
|
nsGenericHTMLElement::IsInteractiveHTMLContent(aIgnoreTabindex);
|
|
}
|
|
|
|
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement)
|
|
NS_INTERFACE_TABLE_INHERITED(HTMLAnchorElement,
|
|
nsIDOMHTMLAnchorElement,
|
|
Link)
|
|
NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
|
|
|
|
NS_IMPL_ADDREF_INHERITED(HTMLAnchorElement, Element)
|
|
NS_IMPL_RELEASE_INHERITED(HTMLAnchorElement, Element)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLAnchorElement)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLAnchorElement,
|
|
nsGenericHTMLElement)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelList)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLAnchorElement,
|
|
nsGenericHTMLElement)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelList)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_ELEMENT_CLONE(HTMLAnchorElement)
|
|
|
|
JSObject*
|
|
HTMLAnchorElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return HTMLAnchorElementBinding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
NS_IMPL_STRING_ATTR(HTMLAnchorElement, Charset, charset)
|
|
NS_IMPL_STRING_ATTR(HTMLAnchorElement, Coords, coords)
|
|
NS_IMPL_URI_ATTR(HTMLAnchorElement, Href, href)
|
|
NS_IMPL_STRING_ATTR(HTMLAnchorElement, Hreflang, hreflang)
|
|
NS_IMPL_STRING_ATTR(HTMLAnchorElement, Name, name)
|
|
NS_IMPL_STRING_ATTR(HTMLAnchorElement, Rel, rel)
|
|
NS_IMPL_STRING_ATTR(HTMLAnchorElement, Rev, rev)
|
|
NS_IMPL_STRING_ATTR(HTMLAnchorElement, Shape, shape)
|
|
NS_IMPL_STRING_ATTR(HTMLAnchorElement, Type, type)
|
|
NS_IMPL_STRING_ATTR(HTMLAnchorElement, Download, download)
|
|
|
|
int32_t
|
|
HTMLAnchorElement::TabIndexDefault()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
HTMLAnchorElement::Draggable() const
|
|
{
|
|
// links can be dragged as long as there is an href and the
|
|
// draggable attribute isn't false
|
|
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
|
|
// no href, so just use the same behavior as other elements
|
|
return nsGenericHTMLElement::Draggable();
|
|
}
|
|
|
|
return !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
|
|
nsGkAtoms::_false, eIgnoreCase);
|
|
}
|
|
|
|
void
|
|
HTMLAnchorElement::OnDNSPrefetchRequested()
|
|
{
|
|
UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
|
|
SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
|
|
}
|
|
|
|
void
|
|
HTMLAnchorElement::OnDNSPrefetchDeferred()
|
|
{
|
|
UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
|
|
SetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
|
|
}
|
|
|
|
bool
|
|
HTMLAnchorElement::HasDeferredDNSPrefetchRequest()
|
|
{
|
|
return HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
|
|
}
|
|
|
|
nsresult
|
|
HTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|
nsIContent* aBindingParent,
|
|
bool aCompileEventHandlers)
|
|
{
|
|
Link::ResetLinkState(false, Link::ElementHasHref());
|
|
|
|
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
|
|
aBindingParent,
|
|
aCompileEventHandlers);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Prefetch links
|
|
nsIDocument* doc = GetComposedDoc();
|
|
if (doc) {
|
|
doc->RegisterPendingLinkUpdate(this);
|
|
TryDNSPrefetch();
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
HTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|
{
|
|
// Cancel any DNS prefetches
|
|
// Note: Must come before ResetLinkState. If called after, it will recreate
|
|
// mCachedURI based on data that is invalid - due to a call to GetHostname.
|
|
CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED,
|
|
HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
|
|
|
|
// 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, Link::ElementHasHref());
|
|
|
|
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
|
}
|
|
|
|
static bool
|
|
IsNodeInEditableRegion(nsINode* aNode)
|
|
{
|
|
while (aNode) {
|
|
if (aNode->IsEditable()) {
|
|
return true;
|
|
}
|
|
aNode = aNode->GetParent();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse,
|
|
bool *aIsFocusable, int32_t *aTabIndex)
|
|
{
|
|
if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
|
|
return true;
|
|
}
|
|
|
|
// cannot focus links if there is no link handler
|
|
nsIDocument* doc = GetComposedDoc();
|
|
if (doc) {
|
|
nsIPresShell* presShell = doc->GetShell();
|
|
if (presShell) {
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
if (presContext && !presContext->GetLinkHandler()) {
|
|
*aIsFocusable = false;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Links that are in an editable region should never be focusable, even if
|
|
// they are in a contenteditable="false" region.
|
|
if (IsNodeInEditableRegion(this)) {
|
|
if (aTabIndex) {
|
|
*aTabIndex = -1;
|
|
}
|
|
|
|
*aIsFocusable = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
|
|
// check whether we're actually a link
|
|
if (!Link::HasURI()) {
|
|
// Not tabbable or focusable without href (bug 17605), unless
|
|
// forced to be via presence of nonnegative tabindex attribute
|
|
if (aTabIndex) {
|
|
*aTabIndex = -1;
|
|
}
|
|
|
|
*aIsFocusable = false;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
|
|
*aTabIndex = -1;
|
|
}
|
|
|
|
*aIsFocusable = true;
|
|
|
|
return false;
|
|
}
|
|
|
|
nsresult
|
|
HTMLAnchorElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|
{
|
|
return GetEventTargetParentForAnchors(aVisitor);
|
|
}
|
|
|
|
nsresult
|
|
HTMLAnchorElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
|
|
{
|
|
return PostHandleEventForAnchors(aVisitor);
|
|
}
|
|
|
|
bool
|
|
HTMLAnchorElement::IsLink(nsIURI** aURI) const
|
|
{
|
|
return IsHTMLLink(aURI);
|
|
}
|
|
|
|
void
|
|
HTMLAnchorElement::GetLinkTarget(nsAString& aTarget)
|
|
{
|
|
GetAttr(kNameSpaceID_None, nsGkAtoms::target, aTarget);
|
|
if (aTarget.IsEmpty()) {
|
|
GetBaseTarget(aTarget);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLAnchorElement::GetTarget(nsAString& aValue)
|
|
{
|
|
if (!GetAttr(kNameSpaceID_None, nsGkAtoms::target, aValue)) {
|
|
GetBaseTarget(aValue);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLAnchorElement::SetTarget(const nsAString& aValue)
|
|
{
|
|
return SetAttr(kNameSpaceID_None, nsGkAtoms::target, aValue, true);
|
|
}
|
|
|
|
nsDOMTokenList*
|
|
HTMLAnchorElement::RelList()
|
|
{
|
|
if (!mRelList) {
|
|
mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, sSupportedRelValues);
|
|
}
|
|
return mRelList;
|
|
}
|
|
|
|
#define IMPL_URI_PART(_part) \
|
|
NS_IMETHODIMP \
|
|
HTMLAnchorElement::Get##_part(nsAString& a##_part) \
|
|
{ \
|
|
Link::Get##_part(a##_part); \
|
|
return NS_OK; \
|
|
} \
|
|
NS_IMETHODIMP \
|
|
HTMLAnchorElement::Set##_part(const nsAString& a##_part) \
|
|
{ \
|
|
Link::Set##_part(a##_part); \
|
|
return NS_OK; \
|
|
}
|
|
|
|
IMPL_URI_PART(Protocol)
|
|
IMPL_URI_PART(Host)
|
|
IMPL_URI_PART(Hostname)
|
|
IMPL_URI_PART(Pathname)
|
|
IMPL_URI_PART(Search)
|
|
IMPL_URI_PART(Port)
|
|
IMPL_URI_PART(Hash)
|
|
|
|
#undef IMPL_URI_PART
|
|
|
|
NS_IMETHODIMP
|
|
HTMLAnchorElement::GetText(nsAString& aText)
|
|
{
|
|
if(!nsContentUtils::GetNodeTextContent(this, true, aText, fallible)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLAnchorElement::SetText(const nsAString& aText)
|
|
{
|
|
return nsContentUtils::SetNodeTextContent(this, aText, false);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLAnchorElement::ToString(nsAString& aSource)
|
|
{
|
|
return GetHref(aSource);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLAnchorElement::GetPing(nsAString& aValue)
|
|
{
|
|
GetAttr(kNameSpaceID_None, nsGkAtoms::ping, aValue);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLAnchorElement::SetPing(const nsAString& aValue)
|
|
{
|
|
return SetAttr(kNameSpaceID_None, nsGkAtoms::ping, aValue, true);
|
|
}
|
|
|
|
already_AddRefed<nsIURI>
|
|
HTMLAnchorElement::GetHrefURI() const
|
|
{
|
|
nsCOMPtr<nsIURI> uri = Link::GetCachedURI();
|
|
if (uri) {
|
|
return uri.forget();
|
|
}
|
|
|
|
return GetHrefURIForAnchors();
|
|
}
|
|
|
|
nsresult
|
|
HTMLAnchorElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
|
|
const nsAttrValueOrString* aValue,
|
|
bool aNotify)
|
|
{
|
|
if (aNamespaceID == kNameSpaceID_None) {
|
|
if (aName == nsGkAtoms::href) {
|
|
CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED,
|
|
HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
|
|
}
|
|
}
|
|
|
|
return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue,
|
|
aNotify);
|
|
}
|
|
|
|
nsresult
|
|
HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
|
|
const nsAttrValue* aValue,
|
|
const nsAttrValue* aOldValue, bool aNotify)
|
|
{
|
|
if (aNamespaceID == kNameSpaceID_None) {
|
|
if (aName == nsGkAtoms::href) {
|
|
Link::ResetLinkState(aNotify, !!aValue);
|
|
if (aValue && IsInComposedDoc()) {
|
|
TryDNSPrefetch();
|
|
}
|
|
}
|
|
}
|
|
|
|
return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName,
|
|
aValue, aOldValue, aNotify);
|
|
}
|
|
|
|
EventStates
|
|
HTMLAnchorElement::IntrinsicState() const
|
|
{
|
|
return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
|
|
}
|
|
|
|
size_t
|
|
HTMLAnchorElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
|
|
{
|
|
return nsGenericHTMLElement::SizeOfExcludingThis(aState) +
|
|
Link::SizeOfExcludingThis(aState);
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|