The inclusions were removed with the following very crude script and the
resulting breakage was fixed up by hand. The manual fixups did either
revert the changes done by the script, replace a generic header with a more
specific one or replace a header with a forward declaration.
find . -name "*.idl" | grep -v web-platform | grep -v third_party | while read path; do
interfaces=$(grep "^\(class\|interface\).*:.*" "$path" | cut -d' ' -f2)
if [ -n "$interfaces" ]; then
if [[ "$interfaces" == *$'\n'* ]]; then
regexp="\("
for i in $interfaces; do regexp="$regexp$i\|"; done
regexp="${regexp%%\\\|}\)"
else
regexp="$interfaces"
fi
interface=$(basename "$path")
rg -l "#include.*${interface%%.idl}.h" . | while read path2; do
hits=$(grep -v "#include.*${interface%%.idl}.h" "$path2" | grep -c "$regexp" )
if [ $hits -eq 0 ]; then
echo "Removing ${interface} from ${path2}"
grep -v "#include.*${interface%%.idl}.h" "$path2" > "$path2".tmp
mv -f "$path2".tmp "$path2"
fi
done
fi
done
Differential Revision: https://phabricator.services.mozilla.com/D55442
596 lines
16 KiB
C++
596 lines
16 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 "Link.h"
|
|
|
|
#include "mozilla/EventStates.h"
|
|
#include "mozilla/MemoryReporting.h"
|
|
#include "mozilla/dom/Element.h"
|
|
#include "mozilla/IHistory.h"
|
|
#include "mozilla/StaticPrefs_layout.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include "nsIURL.h"
|
|
#include "nsIURIMutator.h"
|
|
#include "nsISizeOf.h"
|
|
#include "nsStyleLinkElement.h"
|
|
|
|
#include "nsEscape.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsHTMLDNSPrefetch.h"
|
|
#include "nsString.h"
|
|
#include "mozAutoDocUpdate.h"
|
|
|
|
#include "mozilla/Services.h"
|
|
#include "nsAttrValueInlines.h"
|
|
#include "HTMLLinkElement.h"
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
Link::Link(Element* aElement)
|
|
: mElement(aElement),
|
|
mLinkState(eLinkState_NotLink),
|
|
mNeedsRegistration(false),
|
|
mRegistered(false),
|
|
mHasPendingLinkUpdate(false),
|
|
mInDNSPrefetch(false),
|
|
mHistory(true) {
|
|
MOZ_ASSERT(mElement, "Must have an element");
|
|
}
|
|
|
|
Link::Link()
|
|
: mElement(nullptr),
|
|
mLinkState(eLinkState_NotLink),
|
|
mNeedsRegistration(false),
|
|
mRegistered(false),
|
|
mHasPendingLinkUpdate(false),
|
|
mInDNSPrefetch(false),
|
|
mHistory(false) {}
|
|
|
|
Link::~Link() {
|
|
// !mElement is for mock_Link.
|
|
MOZ_ASSERT(!mElement || !mElement->IsInComposedDoc());
|
|
if (IsInDNSPrefetch()) {
|
|
nsHTMLDNSPrefetch::LinkDestroyed(this);
|
|
}
|
|
UnregisterFromHistory();
|
|
}
|
|
|
|
bool Link::ElementHasHref() const {
|
|
return mElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
|
|
(!mElement->IsHTMLElement() &&
|
|
mElement->HasAttr(kNameSpaceID_XLink, nsGkAtoms::href));
|
|
}
|
|
|
|
void Link::TryDNSPrefetch() {
|
|
MOZ_ASSERT(mElement->IsInComposedDoc());
|
|
if (ElementHasHref() && nsHTMLDNSPrefetch::IsAllowed(mElement->OwnerDoc())) {
|
|
nsHTMLDNSPrefetch::PrefetchLow(this);
|
|
}
|
|
}
|
|
|
|
void Link::CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag,
|
|
nsWrapperCache::FlagsType aRequestedFlag) {
|
|
// If prefetch was deferred, clear flag and move on
|
|
if (mElement->HasFlag(aDeferredFlag)) {
|
|
mElement->UnsetFlags(aDeferredFlag);
|
|
// Else if prefetch was requested, clear flag and send cancellation
|
|
} else if (mElement->HasFlag(aRequestedFlag)) {
|
|
mElement->UnsetFlags(aRequestedFlag);
|
|
// Possible that hostname could have changed since binding, but since this
|
|
// covers common cases, most DNS prefetch requests will be canceled
|
|
nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT);
|
|
}
|
|
}
|
|
|
|
void Link::VisitedQueryFinished(bool aVisited) {
|
|
MOZ_ASSERT(mRegistered, "Setting the link state of an unregistered Link!");
|
|
MOZ_ASSERT(mLinkState == eLinkState_Unvisited,
|
|
"Why would we want to know our visited state otherwise?");
|
|
|
|
auto newState = aVisited ? eLinkState_Visited : eLinkState_Unvisited;
|
|
|
|
// Set our current state as appropriate.
|
|
mLinkState = newState;
|
|
|
|
// We will be no longer registered if we're visited, as it'd be pointless, we
|
|
// never transition from visited -> unvisited.
|
|
if (aVisited) {
|
|
mRegistered = false;
|
|
}
|
|
|
|
MOZ_ASSERT(LinkState() == NS_EVENT_STATE_VISITED ||
|
|
LinkState() == NS_EVENT_STATE_UNVISITED,
|
|
"Unexpected state obtained from LinkState()!");
|
|
|
|
// Tell the element to update its visited state
|
|
mElement->UpdateState(true);
|
|
|
|
if (StaticPrefs::layout_css_always_repaint_on_unvisited()) {
|
|
// Even if the state didn't actually change, we need to repaint in order for
|
|
// the visited state not to be observable.
|
|
nsLayoutUtils::PostRestyleEvent(GetElement(), RestyleHint::RestyleSubtree(),
|
|
nsChangeHint_RepaintFrame);
|
|
}
|
|
}
|
|
|
|
EventStates Link::LinkState() const {
|
|
// We are a constant method, but we are just lazily doing things and have to
|
|
// track that state. Cast away that constness!
|
|
//
|
|
// XXX(emilio): that's evil.
|
|
Link* self = const_cast<Link*>(this);
|
|
|
|
Element* element = self->mElement;
|
|
|
|
// If we have not yet registered for notifications and need to,
|
|
// due to our href changing, register now!
|
|
if (!mRegistered && mNeedsRegistration && element->IsInComposedDoc() &&
|
|
!HasPendingLinkUpdate()) {
|
|
// Only try and register once.
|
|
self->mNeedsRegistration = false;
|
|
|
|
nsCOMPtr<nsIURI> hrefURI(GetURI());
|
|
|
|
// Assume that we are not visited until we are told otherwise.
|
|
self->mLinkState = eLinkState_Unvisited;
|
|
|
|
// Make sure the href attribute has a valid link (bug 23209).
|
|
// If we have a good href, register with History if available.
|
|
if (mHistory && hrefURI) {
|
|
if (nsCOMPtr<IHistory> history = services::GetHistoryService()) {
|
|
nsresult rv = history->RegisterVisitedCallback(hrefURI, self);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
self->mRegistered = true;
|
|
|
|
// And make sure we are in the document's link map.
|
|
element->GetComposedDoc()->AddStyleRelevantLink(self);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise, return our known state.
|
|
if (mLinkState == eLinkState_Visited) {
|
|
return NS_EVENT_STATE_VISITED;
|
|
}
|
|
|
|
if (mLinkState == eLinkState_Unvisited) {
|
|
return NS_EVENT_STATE_UNVISITED;
|
|
}
|
|
|
|
return EventStates();
|
|
}
|
|
|
|
nsIURI* Link::GetURI() const {
|
|
// If we have this URI cached, use it.
|
|
if (mCachedURI) {
|
|
return mCachedURI;
|
|
}
|
|
|
|
// Otherwise obtain it.
|
|
Link* self = const_cast<Link*>(this);
|
|
Element* element = self->mElement;
|
|
mCachedURI = element->GetHrefURI();
|
|
|
|
return mCachedURI;
|
|
}
|
|
|
|
void Link::SetProtocol(const nsAString& aProtocol) {
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
// Ignore failures to be compatible with NS4.
|
|
return;
|
|
}
|
|
|
|
nsAString::const_iterator start, end;
|
|
aProtocol.BeginReading(start);
|
|
aProtocol.EndReading(end);
|
|
nsAString::const_iterator iter(start);
|
|
(void)FindCharInReadable(':', iter, end);
|
|
nsresult rv = NS_MutateURI(uri)
|
|
.SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)))
|
|
.Finalize(uri);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
SetHrefAttribute(uri);
|
|
}
|
|
|
|
void Link::SetPassword(const nsAString& aPassword) {
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
// Ignore failures to be compatible with NS4.
|
|
return;
|
|
}
|
|
|
|
nsresult rv = NS_MutateURI(uri)
|
|
.SetPassword(NS_ConvertUTF16toUTF8(aPassword))
|
|
.Finalize(uri);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
SetHrefAttribute(uri);
|
|
}
|
|
}
|
|
|
|
void Link::SetUsername(const nsAString& aUsername) {
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
// Ignore failures to be compatible with NS4.
|
|
return;
|
|
}
|
|
|
|
nsresult rv = NS_MutateURI(uri)
|
|
.SetUsername(NS_ConvertUTF16toUTF8(aUsername))
|
|
.Finalize(uri);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
SetHrefAttribute(uri);
|
|
}
|
|
}
|
|
|
|
void Link::SetHost(const nsAString& aHost) {
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
// Ignore failures to be compatible with NS4.
|
|
return;
|
|
}
|
|
|
|
nsresult rv =
|
|
NS_MutateURI(uri).SetHostPort(NS_ConvertUTF16toUTF8(aHost)).Finalize(uri);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
SetHrefAttribute(uri);
|
|
}
|
|
|
|
void Link::SetHostname(const nsAString& aHostname) {
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
// Ignore failures to be compatible with NS4.
|
|
return;
|
|
}
|
|
|
|
nsresult rv =
|
|
NS_MutateURI(uri).SetHost(NS_ConvertUTF16toUTF8(aHostname)).Finalize(uri);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
SetHrefAttribute(uri);
|
|
}
|
|
|
|
void Link::SetPathname(const nsAString& aPathname) {
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
|
|
if (!url) {
|
|
// Ignore failures to be compatible with NS4.
|
|
return;
|
|
}
|
|
|
|
nsresult rv = NS_MutateURI(uri)
|
|
.SetFilePath(NS_ConvertUTF16toUTF8(aPathname))
|
|
.Finalize(uri);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
SetHrefAttribute(uri);
|
|
}
|
|
|
|
void Link::SetSearch(const nsAString& aSearch) {
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
|
|
if (!url) {
|
|
// Ignore failures to be compatible with NS4.
|
|
return;
|
|
}
|
|
|
|
auto encoding = mElement->OwnerDoc()->GetDocumentCharacterSet();
|
|
nsresult rv =
|
|
NS_MutateURI(uri)
|
|
.SetQueryWithEncoding(NS_ConvertUTF16toUTF8(aSearch), encoding)
|
|
.Finalize(uri);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
SetHrefAttribute(uri);
|
|
}
|
|
|
|
void Link::SetPort(const nsAString& aPort) {
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
// Ignore failures to be compatible with NS4.
|
|
return;
|
|
}
|
|
|
|
nsresult rv;
|
|
nsAutoString portStr(aPort);
|
|
|
|
// nsIURI uses -1 as default value.
|
|
int32_t port = -1;
|
|
if (!aPort.IsEmpty()) {
|
|
port = portStr.ToInteger(&rv);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
rv = NS_MutateURI(uri).SetPort(port).Finalize(uri);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
SetHrefAttribute(uri);
|
|
}
|
|
|
|
void Link::SetHash(const nsAString& aHash) {
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
// Ignore failures to be compatible with NS4.
|
|
return;
|
|
}
|
|
|
|
nsresult rv =
|
|
NS_MutateURI(uri).SetRef(NS_ConvertUTF16toUTF8(aHash)).Finalize(uri);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
SetHrefAttribute(uri);
|
|
}
|
|
|
|
void Link::GetOrigin(nsAString& aOrigin) {
|
|
aOrigin.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
return;
|
|
}
|
|
|
|
nsString origin;
|
|
nsContentUtils::GetUTFOrigin(uri, origin);
|
|
aOrigin.Assign(origin);
|
|
}
|
|
|
|
void Link::GetProtocol(nsAString& _protocol) {
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
_protocol.AssignLiteral("http");
|
|
} else {
|
|
nsAutoCString scheme;
|
|
(void)uri->GetScheme(scheme);
|
|
CopyASCIItoUTF16(scheme, _protocol);
|
|
}
|
|
_protocol.Append(char16_t(':'));
|
|
}
|
|
|
|
void Link::GetUsername(nsAString& aUsername) {
|
|
aUsername.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
return;
|
|
}
|
|
|
|
nsAutoCString username;
|
|
uri->GetUsername(username);
|
|
CopyASCIItoUTF16(username, aUsername);
|
|
}
|
|
|
|
void Link::GetPassword(nsAString& aPassword) {
|
|
aPassword.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
return;
|
|
}
|
|
|
|
nsAutoCString password;
|
|
uri->GetPassword(password);
|
|
CopyASCIItoUTF16(password, aPassword);
|
|
}
|
|
|
|
void Link::GetHost(nsAString& _host) {
|
|
_host.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
// Do not throw! Not having a valid URI should result in an empty string.
|
|
return;
|
|
}
|
|
|
|
nsAutoCString hostport;
|
|
nsresult rv = uri->GetHostPort(hostport);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
CopyUTF8toUTF16(hostport, _host);
|
|
}
|
|
}
|
|
|
|
void Link::GetHostname(nsAString& _hostname) {
|
|
_hostname.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
// Do not throw! Not having a valid URI should result in an empty string.
|
|
return;
|
|
}
|
|
|
|
nsContentUtils::GetHostOrIPv6WithBrackets(uri, _hostname);
|
|
}
|
|
|
|
void Link::GetPathname(nsAString& _pathname) {
|
|
_pathname.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
|
|
if (!url) {
|
|
// Do not throw! Not having a valid URI or URL should result in an empty
|
|
// string.
|
|
return;
|
|
}
|
|
|
|
nsAutoCString file;
|
|
nsresult rv = url->GetFilePath(file);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
CopyUTF8toUTF16(file, _pathname);
|
|
}
|
|
}
|
|
|
|
void Link::GetSearch(nsAString& _search) {
|
|
_search.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
|
|
if (!url) {
|
|
// Do not throw! Not having a valid URI or URL should result in an empty
|
|
// string.
|
|
return;
|
|
}
|
|
|
|
nsAutoCString search;
|
|
nsresult rv = url->GetQuery(search);
|
|
if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
|
|
_search.Assign(u'?');
|
|
AppendUTF8toUTF16(search, _search);
|
|
}
|
|
}
|
|
|
|
void Link::GetPort(nsAString& _port) {
|
|
_port.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
// Do not throw! Not having a valid URI should result in an empty string.
|
|
return;
|
|
}
|
|
|
|
int32_t port;
|
|
nsresult rv = uri->GetPort(&port);
|
|
// Note that failure to get the port from the URI is not necessarily a bad
|
|
// thing. Some URIs do not have a port.
|
|
if (NS_SUCCEEDED(rv) && port != -1) {
|
|
nsAutoString portStr;
|
|
portStr.AppendInt(port, 10);
|
|
_port.Assign(portStr);
|
|
}
|
|
}
|
|
|
|
void Link::GetHash(nsAString& _hash) {
|
|
_hash.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri(GetURI());
|
|
if (!uri) {
|
|
// Do not throw! Not having a valid URI should result in an empty
|
|
// string.
|
|
return;
|
|
}
|
|
|
|
nsAutoCString ref;
|
|
nsresult rv = uri->GetRef(ref);
|
|
if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
|
|
_hash.Assign(char16_t('#'));
|
|
AppendUTF8toUTF16(ref, _hash);
|
|
}
|
|
}
|
|
|
|
void Link::ResetLinkState(bool aNotify, bool aHasHref) {
|
|
nsLinkState defaultState;
|
|
|
|
// The default state for links with an href is unvisited.
|
|
if (aHasHref) {
|
|
defaultState = eLinkState_Unvisited;
|
|
} else {
|
|
defaultState = eLinkState_NotLink;
|
|
}
|
|
|
|
// If !mNeedsRegstration, then either we've never registered, or we're
|
|
// currently registered; in either case, we should remove ourself
|
|
// from the doc and the history.
|
|
if (!mNeedsRegistration && mLinkState != eLinkState_NotLink) {
|
|
Document* doc = mElement->GetComposedDoc();
|
|
if (doc && (mRegistered || mLinkState == eLinkState_Visited)) {
|
|
// Tell the document to forget about this link if we've registered
|
|
// with it before.
|
|
doc->ForgetLink(this);
|
|
}
|
|
}
|
|
|
|
// If we have an href, and we're not a <link>, we should register with the
|
|
// history.
|
|
//
|
|
// FIXME(emilio): Do we really want to allow all MathML elements to be
|
|
// :visited? That seems not great.
|
|
mNeedsRegistration = aHasHref && !mElement->IsHTMLElement(nsGkAtoms::link);
|
|
|
|
// If we've cached the URI, reset always invalidates it.
|
|
UnregisterFromHistory();
|
|
mCachedURI = nullptr;
|
|
|
|
// Update our state back to the default.
|
|
mLinkState = defaultState;
|
|
|
|
// We have to be very careful here: if aNotify is false we do NOT
|
|
// want to call UpdateState, because that will call into LinkState()
|
|
// and try to start off loads, etc. But ResetLinkState is called
|
|
// with aNotify false when things are in inconsistent states, so
|
|
// we'll get confused in that situation. Instead, just silently
|
|
// update the link state on mElement. Since we might have set the
|
|
// link state to unvisited, make sure to update with that state if
|
|
// required.
|
|
if (aNotify) {
|
|
mElement->UpdateState(aNotify);
|
|
} else {
|
|
if (mLinkState == eLinkState_Unvisited) {
|
|
mElement->UpdateLinkState(NS_EVENT_STATE_UNVISITED);
|
|
} else {
|
|
mElement->UpdateLinkState(EventStates());
|
|
}
|
|
}
|
|
}
|
|
|
|
void Link::UnregisterFromHistory() {
|
|
// If we are not registered, we have nothing to do.
|
|
if (!mRegistered) {
|
|
return;
|
|
}
|
|
|
|
// And tell History to stop tracking us.
|
|
if (mHistory && mCachedURI) {
|
|
if (nsCOMPtr<IHistory> history = services::GetHistoryService()) {
|
|
history->UnregisterVisitedCallback(mCachedURI, this);
|
|
mRegistered = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Link::SetHrefAttribute(nsIURI* aURI) {
|
|
NS_ASSERTION(aURI, "Null URI is illegal!");
|
|
|
|
// if we change this code to not reserialize we need to do something smarter
|
|
// in SetProtocol because changing the protocol of an URI can change the
|
|
// "nature" of the nsIURL/nsIURI implementation.
|
|
nsAutoCString href;
|
|
(void)aURI->GetSpec(href);
|
|
(void)mElement->SetAttr(kNameSpaceID_None, nsGkAtoms::href,
|
|
NS_ConvertUTF8toUTF16(href), true);
|
|
}
|
|
|
|
size_t Link::SizeOfExcludingThis(mozilla::SizeOfState& aState) const {
|
|
size_t n = 0;
|
|
|
|
if (mCachedURI) {
|
|
nsCOMPtr<nsISizeOf> iface = do_QueryInterface(mCachedURI);
|
|
if (iface) {
|
|
n += iface->SizeOfIncludingThis(aState.mMallocSizeOf);
|
|
}
|
|
}
|
|
|
|
// The following members don't need to be measured:
|
|
// - mElement, because it is a pointer-to-self used to avoid QIs
|
|
|
|
return n;
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|