--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -90,16 +90,18 @@ UNIFIED_SOURCES += [
'nsDefaultURIFixup.cpp',
'nsDocShell.cpp',
'nsDocShellEditorData.cpp',
'nsDocShellEnumerator.cpp',
'nsDocShellLoadInfo.cpp',
'nsDocShellTransferableHooks.cpp',
'nsDocShellTreeOwner.cpp',
'nsDSURIContentListener.cpp',
+ 'nsPingListener.cpp',
+ 'nsRefreshTimer.cpp',
'nsWebNavigationInfo.cpp',
'PendingGlobalHistoryEntry.cpp',
'SerializedLoadContext.cpp',
]
if not CONFIG['MOZ_PLACES']:
UNIFIED_SOURCES += ['nsDownloadHistory.cpp']
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -129,17 +129,16 @@
#include "nsIStructuredCloneContainer.h"
#include "nsISupportsPrimitives.h"
#include "nsITabChild.h"
#include "nsITextToSubURI.h"
#include "nsITimedChannel.h"
#include "nsITimer.h"
#include "nsITransportSecurityInfo.h"
#include "nsIUploadChannel.h"
-#include "nsIUploadChannel2.h"
#include "nsIURIFixup.h"
#include "nsIURILoader.h"
#include "nsIURL.h"
#include "nsIViewSourceChannel.h"
#include "nsIWebBrowserChrome.h"
#include "nsIWebBrowserChrome3.h"
#include "nsIWebBrowserChromeFocus.h"
#include "nsIWebBrowserFind.h"
@@ -181,23 +180,23 @@
#include "nsError.h"
#include "nsEscape.h"
#include "nsFocusManager.h"
#include "nsGlobalWindow.h"
#include "nsJSEnvironment.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsObjectLoadingContent.h"
+#include "nsPingListener.h"
#include "nsPoint.h"
#include "nsQueryObject.h"
#include "nsRect.h"
+#include "nsRefreshTimer.h"
#include "nsSandboxFlags.h"
#include "nsSHistory.h"
-#include "nsStreamUtils.h"
-#include "nsStringStream.h"
#include "nsStructuredCloneContainer.h"
#include "nsSubDocumentFrame.h"
#include "nsView.h"
#include "nsViewManager.h"
#include "nsViewSourceHandler.h"
#include "nsWhitespaceTokenizer.h"
#include "nsWidgetsCID.h"
#include "nsXULAppAPI.h"
@@ -278,395 +277,16 @@ FavorPerformanceHint(bool aPerfOverStarv
if (appShell) {
appShell->FavorPerformanceHint(
aPerfOverStarvation,
Preferences::GetUint("docshell.event_starvation_delay_hint",
NS_EVENT_STARVATION_DELAY_HINT));
}
}
-//*****************************************************************************
-// <a ping> support
-//*****************************************************************************
-
-#define PREF_PINGS_ENABLED "browser.send_pings"
-#define PREF_PINGS_MAX_PER_LINK "browser.send_pings.max_per_link"
-#define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host"
-
-// Check prefs to see if pings are enabled and if so what restrictions might
-// be applied.
-//
-// @param maxPerLink
-// This parameter returns the number of pings that are allowed per link click
-//
-// @param requireSameHost
-// This parameter returns true if pings are restricted to the same host as
-// the document in which the click occurs. If the same host restriction is
-// imposed, then we still allow for pings to cross over to different
-// protocols and ports for flexibility and because it is not possible to send
-// a ping via FTP.
-//
-// @returns
-// true if pings are enabled and false otherwise.
-//
-static bool
-PingsEnabled(int32_t* aMaxPerLink, bool* aRequireSameHost)
-{
- bool allow = Preferences::GetBool(PREF_PINGS_ENABLED, false);
-
- *aMaxPerLink = 1;
- *aRequireSameHost = true;
-
- if (allow) {
- Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, aMaxPerLink);
- Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, aRequireSameHost);
- }
-
- return allow;
-}
-
-typedef void (*ForEachPingCallback)(void* closure, nsIContent* content,
- nsIURI* uri, nsIIOService* ios);
-
-static bool
-IsElementAnchor(nsIContent* aContent)
-{
- // Make sure we are dealing with either an <A> or <AREA> element in the HTML
- // or XHTML namespace.
- return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area);
-}
-
-static void
-ForEachPing(nsIContent* aContent, ForEachPingCallback aCallback, void* aClosure)
-{
- // NOTE: Using nsIDOMHTMLAnchorElement::GetPing isn't really worth it here
- // since we'd still need to parse the resulting string. Instead, we
- // just parse the raw attribute. It might be nice if the content node
- // implemented an interface that exposed an enumeration of nsIURIs.
-
- // Make sure we are dealing with either an <A> or <AREA> element in the HTML
- // or XHTML namespace.
- if (!IsElementAnchor(aContent)) {
- return;
- }
-
- nsAutoString value;
- aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::ping, value);
- if (value.IsEmpty()) {
- return;
- }
-
- nsCOMPtr<nsIIOService> ios = do_GetIOService();
- if (!ios) {
- return;
- }
-
- nsIDocument* doc = aContent->OwnerDoc();
- nsAutoCString charset;
- doc->GetDocumentCharacterSet()->Name(charset);
-
- nsWhitespaceTokenizer tokenizer(value);
-
- while (tokenizer.hasMoreTokens()) {
- nsCOMPtr<nsIURI> uri, baseURI = aContent->GetBaseURI();
- ios->NewURI(NS_ConvertUTF16toUTF8(tokenizer.nextToken()),
- charset.get(), baseURI, getter_AddRefs(uri));
- // if we can't generate a valid URI, then there is nothing to do
- if (!uri) {
- continue;
- }
- // Explicitly not allow loading data: URIs
- bool isDataScheme =
- (NS_SUCCEEDED(uri->SchemeIs("data", &isDataScheme)) && isDataScheme);
-
- if (!isDataScheme) {
- aCallback(aClosure, aContent, uri, ios);
- }
- }
-}
-
-//----------------------------------------------------------------------
-
-// We wait this many milliseconds before killing the ping channel...
-#define PING_TIMEOUT 10000
-
-static void
-OnPingTimeout(nsITimer* aTimer, void* aClosure)
-{
- nsILoadGroup* loadGroup = static_cast<nsILoadGroup*>(aClosure);
- if (loadGroup) {
- loadGroup->Cancel(NS_ERROR_ABORT);
- }
-}
-
-class nsPingListener final
- : public nsIStreamListener
-{
-public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSIREQUESTOBSERVER
- NS_DECL_NSISTREAMLISTENER
-
- nsPingListener()
- {
- }
-
- void SetLoadGroup(nsILoadGroup* aLoadGroup) {
- mLoadGroup = aLoadGroup;
- }
-
- nsresult StartTimeout(DocGroup* aDocGroup);
-
-private:
- ~nsPingListener();
-
- nsCOMPtr<nsILoadGroup> mLoadGroup;
- nsCOMPtr<nsITimer> mTimer;
-};
-
-NS_IMPL_ISUPPORTS(nsPingListener, nsIStreamListener, nsIRequestObserver)
-
-nsPingListener::~nsPingListener()
-{
- if (mTimer) {
- mTimer->Cancel();
- mTimer = nullptr;
- }
-}
-
-nsresult
-nsPingListener::StartTimeout(DocGroup* aDocGroup)
-{
- NS_ENSURE_ARG(aDocGroup);
-
- return NS_NewTimerWithFuncCallback(getter_AddRefs(mTimer),
- OnPingTimeout,
- mLoadGroup,
- PING_TIMEOUT,
- nsITimer::TYPE_ONE_SHOT,
- "nsPingListener::StartTimeout",
- aDocGroup->EventTargetFor(TaskCategory::Network));
-}
-
-NS_IMETHODIMP
-nsPingListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
-{
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPingListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
- nsIInputStream* aStream, uint64_t aOffset,
- uint32_t aCount)
-{
- uint32_t result;
- return aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &result);
-}
-
-NS_IMETHODIMP
-nsPingListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
- nsresult aStatus)
-{
- mLoadGroup = nullptr;
-
- if (mTimer) {
- mTimer->Cancel();
- mTimer = nullptr;
- }
-
- return NS_OK;
-}
-
-struct MOZ_STACK_CLASS SendPingInfo
-{
- int32_t numPings;
- int32_t maxPings;
- bool requireSameHost;
- nsIURI* target;
- nsIURI* referrer;
- nsIDocShell* docShell;
- uint32_t referrerPolicy;
-};
-
-static void
-SendPing(void* aClosure, nsIContent* aContent, nsIURI* aURI,
- nsIIOService* aIOService)
-{
- SendPingInfo* info = static_cast<SendPingInfo*>(aClosure);
- if (info->maxPings > -1 && info->numPings >= info->maxPings) {
- return;
- }
-
- nsIDocument* doc = aContent->OwnerDoc();
-
- nsCOMPtr<nsIChannel> chan;
- NS_NewChannel(getter_AddRefs(chan),
- aURI,
- doc,
- info->requireSameHost
- ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
- : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
- nsIContentPolicy::TYPE_PING,
- nullptr, // aLoadGroup
- nullptr, // aCallbacks
- nsIRequest::LOAD_NORMAL, // aLoadFlags,
- aIOService);
-
- if (!chan) {
- return;
- }
-
- // Don't bother caching the result of this URI load, but do not exempt
- // it from Safe Browsing.
- chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING | nsIChannel::LOAD_CLASSIFY_URI);
-
- nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
- if (!httpChan) {
- return;
- }
-
- // This is needed in order for 3rd-party cookie blocking to work.
- nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(httpChan);
- nsresult rv;
- if (httpInternal) {
- rv = httpInternal->SetDocumentURI(doc->GetDocumentURI());
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- }
-
- rv = httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
- MOZ_ASSERT(NS_SUCCEEDED(rv));
-
- // Remove extraneous request headers (to reduce request size)
- rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
- EmptyCString(), false);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"),
- EmptyCString(), false);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"),
- EmptyCString(), false);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
-
- // Always send a Ping-To header.
- nsAutoCString pingTo;
- if (NS_SUCCEEDED(info->target->GetSpec(pingTo))) {
- rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-To"), pingTo, false);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- }
-
- nsCOMPtr<nsIScriptSecurityManager> sm =
- do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
-
- if (sm && info->referrer) {
- bool referrerIsSecure;
- uint32_t flags = nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
- rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure);
-
- // Default to sending less data if NS_URIChainHasFlags() fails.
- referrerIsSecure = NS_FAILED(rv) || referrerIsSecure;
-
- bool sameOrigin =
- NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, aURI, false));
-
- // If both the address of the document containing the hyperlink being
- // audited and "ping URL" have the same origin or the document containing
- // the hyperlink being audited was not retrieved over an encrypted
- // connection, send a Ping-From header.
- if (sameOrigin || !referrerIsSecure) {
- nsAutoCString pingFrom;
- if (NS_SUCCEEDED(info->referrer->GetSpec(pingFrom))) {
- rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-From"),
- pingFrom, false);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- }
- }
-
- // If the document containing the hyperlink being audited was not retrieved
- // over an encrypted connection and its address does not have the same
- // origin as "ping URL", send a referrer.
- if (!sameOrigin && !referrerIsSecure) {
- rv = httpChan->SetReferrerWithPolicy(info->referrer, info->referrerPolicy);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- }
- }
-
- nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(httpChan);
- if (!uploadChan) {
- return;
- }
-
- NS_NAMED_LITERAL_CSTRING(uploadData, "PING");
-
- nsCOMPtr<nsIInputStream> uploadStream;
- rv = NS_NewCStringInputStream(getter_AddRefs(uploadStream), uploadData);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
-
- uploadChan->ExplicitSetUploadStream(uploadStream,
- NS_LITERAL_CSTRING("text/ping"),
- uploadData.Length(),
- NS_LITERAL_CSTRING("POST"), false);
-
- // The channel needs to have a loadgroup associated with it, so that we can
- // cancel the channel and any redirected channels it may create.
- nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
- if (!loadGroup) {
- return;
- }
- nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(info->docShell);
- loadGroup->SetNotificationCallbacks(callbacks);
- chan->SetLoadGroup(loadGroup);
-
- RefPtr<nsPingListener> pingListener = new nsPingListener();
- chan->AsyncOpen2(pingListener);
-
- // Even if AsyncOpen failed, we still count this as a successful ping. It's
- // possible that AsyncOpen may have failed after triggering some background
- // process that may have written something to the network.
- info->numPings++;
-
- // Prevent ping requests from stalling and never being garbage collected...
- if (NS_FAILED(pingListener->StartTimeout(doc->GetDocGroup()))) {
- // If we failed to setup the timer, then we should just cancel the channel
- // because we won't be able to ensure that it goes away in a timely manner.
- chan->Cancel(NS_ERROR_ABORT);
- return;
- }
- // if the channel openend successfully, then make the pingListener hold
- // a strong reference to the loadgroup which is released in ::OnStopRequest
- pingListener->SetLoadGroup(loadGroup);
-}
-
-// Spec: http://whatwg.org/specs/web-apps/current-work/#ping
-static void
-DispatchPings(nsIDocShell* aDocShell,
- nsIContent* aContent,
- nsIURI* aTarget,
- nsIURI* aReferrer,
- uint32_t aReferrerPolicy)
-{
- SendPingInfo info;
-
- if (!PingsEnabled(&info.maxPings, &info.requireSameHost)) {
- return;
- }
- if (info.maxPings == 0) {
- return;
- }
-
- info.numPings = 0;
- info.target = aTarget;
- info.referrer = aReferrer;
- info.referrerPolicy = aReferrerPolicy;
- info.docShell = aDocShell;
-
- ForEachPing(aContent, SendPing, &info);
-}
-
static nsDOMNavigationTiming::Type
ConvertLoadTypeToNavigationType(uint32_t aLoadType)
{
// Not initialized, assume it's normal load.
if (aLoadType == 0) {
aLoadType = LOAD_NORMAL;
}
@@ -13891,60 +13511,16 @@ NS_IMETHODIMP
nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState)
{
if (mOSHE) {
mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
}
return NS_OK;
}
-nsRefreshTimer::nsRefreshTimer(nsDocShell* aDocShell,
- nsIURI* aURI,
- nsIPrincipal* aPrincipal,
- int32_t aDelay, bool aRepeat, bool aMetaRefresh)
- : mDocShell(aDocShell), mURI(aURI), mPrincipal(aPrincipal),
- mDelay(aDelay), mRepeat(aRepeat),
- mMetaRefresh(aMetaRefresh)
-{
-}
-
-nsRefreshTimer::~nsRefreshTimer()
-{
-}
-
-NS_IMPL_ADDREF(nsRefreshTimer)
-NS_IMPL_RELEASE(nsRefreshTimer)
-
-NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
- NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
- NS_INTERFACE_MAP_ENTRY(nsINamed)
-NS_INTERFACE_MAP_END_THREADSAFE
-
-NS_IMETHODIMP
-nsRefreshTimer::Notify(nsITimer* aTimer)
-{
- NS_ASSERTION(mDocShell, "DocShell is somehow null");
-
- if (mDocShell && aTimer) {
- // Get the delay count to determine load type
- uint32_t delay = 0;
- aTimer->GetDelay(&delay);
- mDocShell->ForceRefreshURIFromTimer(mURI, mPrincipal, delay, mMetaRefresh, aTimer);
- }
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsRefreshTimer::GetName(nsACString& aName)
-{
- aName.AssignLiteral("nsRefreshTimer");
- return NS_OK;
-}
-
nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
nsIInterfaceRequestor* aRequestor)
{
if (aRequestor) {
mWeakPtr = do_GetWeakReference(aRequestor);
}
}
@@ -14446,16 +14022,24 @@ nsDocShell::OnLinkClick(nsIContent* aCon
nsCOMPtr<nsIRunnable> ev =
new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
aPostDataStream, aPostDataStreamLength,
aHeadersDataStream, noOpenerImplied,
aIsTrusted, aTriggeringPrincipal);
return DispatchToTabGroup(TaskCategory::UI, ev.forget());
}
+static bool
+IsElementAnchorOrArea(nsIContent* aContent)
+{
+ // Make sure we are dealing with either an <A> or <AREA> element in the HTML
+ // or XHTML namespace.
+ return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area);
+}
+
NS_IMETHODIMP
nsDocShell::OnLinkClickSync(nsIContent* aContent,
nsIURI* aURI,
const char16_t* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
int64_t aPostDataStreamLength,
nsIInputStream* aHeadersDataStream,
@@ -14504,17 +14088,17 @@ nsDocShell::OnLinkClickSync(nsIContent*
if (NS_SUCCEEDED(rv) && !isExposed) {
return extProtService->LoadURI(aURI, this);
}
}
}
}
uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
- if (IsElementAnchor(aContent)) {
+ if (IsElementAnchorOrArea(aContent)) {
MOZ_ASSERT(aContent->IsHTMLElement());
nsAutoString referrer;
aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, referrer);
nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(referrer);
while (tok.hasMoreTokens()) {
const nsAString& token = tok.nextToken();
if (token.LowerCaseEqualsLiteral("noreferrer")) {
flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
@@ -14551,17 +14135,17 @@ nsDocShell::OnLinkClickSync(nsIContent*
}
nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
uint32_t refererPolicy = refererDoc->GetReferrerPolicy();
// get referrer attribute from clicked link and parse it
// if per element referrer is enabled, the element referrer overrules
// the document wide referrer
- if (IsElementAnchor(aContent)) {
+ if (IsElementAnchorOrArea(aContent)) {
net::ReferrerPolicy refPolEnum = aContent->AsElement()->GetReferrerPolicyAsEnum();
if (refPolEnum != net::RP_Unset) {
refererPolicy = refPolEnum;
}
}
// referer could be null here in some odd cases, but that's ok,
// we'll just load the link w/o sending a referer in those cases.
@@ -14620,17 +14204,17 @@ nsDocShell::OnLinkClickSync(nsIContent*
true, // first party site
VoidString(), // No srcdoc
this, // We are the source
nullptr, // baseURI not needed
true, // Check for prerendered doc
aDocShell, // DocShell out-param
aRequest); // Request out-param
if (NS_SUCCEEDED(rv)) {
- DispatchPings(this, aContent, aURI, referer, refererPolicy);
+ nsPingListener::DispatchPings(this, aContent, aURI, referer, refererPolicy);
}
return rv;
}
NS_IMETHODIMP
nsDocShell::OnOverLink(nsIContent* aContent,
nsIURI* aURI,
const char16_t* aTargetSpec)
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -26,23 +26,21 @@
#include "nsIDocShell.h"
#include "nsIDocShellLoadInfo.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDOMStorageManager.h"
#include "nsIInterfaceRequestor.h"
#include "nsILinkHandler.h"
#include "nsILoadContext.h"
#include "nsILoadURIDelegate.h"
-#include "nsINamed.h"
#include "nsINetworkInterceptController.h"
#include "nsIRefreshURI.h"
#include "nsIScrollable.h"
#include "nsITabParent.h"
#include "nsITextScroll.h"
-#include "nsITimer.h"
#include "nsIWebNavigation.h"
#include "nsIWebPageDescriptor.h"
#include "nsIWebProgressListener.h"
#include "nsIWebShellServices.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsContentPolicyUtils.h"
@@ -110,44 +108,16 @@ class OnLinkClickEvent;
/* internally used ViewMode types */
enum ViewMode
{
viewNormal = 0x0,
viewSource = 0x1
};
-class nsRefreshTimer : public nsITimerCallback
- , public nsINamed
-{
-public:
- nsRefreshTimer(nsDocShell* aDocShell,
- nsIURI* aURI,
- nsIPrincipal* aPrincipal,
- int32_t aDelay,
- bool aRepeat,
- bool aMetaRefresh);
-
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSITIMERCALLBACK
- NS_DECL_NSINAMED
-
- int32_t GetDelay() { return mDelay ;}
-
- RefPtr<nsDocShell> mDocShell;
- nsCOMPtr<nsIURI> mURI;
- nsCOMPtr<nsIPrincipal> mPrincipal;
- int32_t mDelay;
- bool mRepeat;
- bool mMetaRefresh;
-
-private:
- virtual ~nsRefreshTimer();
-};
-
enum eCharsetReloadState
{
eCharsetReloadInit,
eCharsetReloadRequested,
eCharsetReloadStopOrigional
};
class nsDocShell final
new file mode 100644
--- /dev/null
+++ b/docshell/base/nsPingListener.cpp
@@ -0,0 +1,370 @@
+/* -*- 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 "nsPingListener.h"
+
+#include "mozilla/Preferences.h"
+
+#include "mozilla/dom/DocGroup.h"
+
+#include "nsIDocument.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIInputStream.h"
+#include "nsIProtocolHandler.h"
+#include "nsIUploadChannel2.h"
+
+#include "nsDocument.h"
+#include "nsNetUtil.h"
+#include "nsStreamUtils.h"
+#include "nsStringStream.h"
+#include "nsWhitespaceTokenizer.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+NS_IMPL_ISUPPORTS(nsPingListener, nsIStreamListener, nsIRequestObserver)
+
+//*****************************************************************************
+// <a ping> support
+//*****************************************************************************
+
+#define PREF_PINGS_ENABLED "browser.send_pings"
+#define PREF_PINGS_MAX_PER_LINK "browser.send_pings.max_per_link"
+#define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host"
+
+// Check prefs to see if pings are enabled and if so what restrictions might
+// be applied.
+//
+// @param maxPerLink
+// This parameter returns the number of pings that are allowed per link click
+//
+// @param requireSameHost
+// This parameter returns true if pings are restricted to the same host as
+// the document in which the click occurs. If the same host restriction is
+// imposed, then we still allow for pings to cross over to different
+// protocols and ports for flexibility and because it is not possible to send
+// a ping via FTP.
+//
+// @returns
+// true if pings are enabled and false otherwise.
+//
+static bool
+PingsEnabled(int32_t* aMaxPerLink, bool* aRequireSameHost)
+{
+ bool allow = Preferences::GetBool(PREF_PINGS_ENABLED, false);
+
+ *aMaxPerLink = 1;
+ *aRequireSameHost = true;
+
+ if (allow) {
+ Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, aMaxPerLink);
+ Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, aRequireSameHost);
+ }
+
+ return allow;
+}
+
+// We wait this many milliseconds before killing the ping channel...
+#define PING_TIMEOUT 10000
+
+static void
+OnPingTimeout(nsITimer* aTimer, void* aClosure)
+{
+ nsILoadGroup* loadGroup = static_cast<nsILoadGroup*>(aClosure);
+ if (loadGroup) {
+ loadGroup->Cancel(NS_ERROR_ABORT);
+ }
+}
+
+struct MOZ_STACK_CLASS SendPingInfo
+{
+ int32_t numPings;
+ int32_t maxPings;
+ bool requireSameHost;
+ nsIURI* target;
+ nsIURI* referrer;
+ nsIDocShell* docShell;
+ uint32_t referrerPolicy;
+};
+
+static void
+SendPing(void* aClosure, nsIContent* aContent, nsIURI* aURI,
+ nsIIOService* aIOService)
+{
+ SendPingInfo* info = static_cast<SendPingInfo*>(aClosure);
+ if (info->maxPings > -1 && info->numPings >= info->maxPings) {
+ return;
+ }
+
+ nsIDocument* doc = aContent->OwnerDoc();
+
+ nsCOMPtr<nsIChannel> chan;
+ NS_NewChannel(getter_AddRefs(chan),
+ aURI,
+ doc,
+ info->requireSameHost
+ ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
+ : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_PING,
+ nullptr, // aLoadGroup
+ nullptr, // aCallbacks
+ nsIRequest::LOAD_NORMAL, // aLoadFlags,
+ aIOService);
+
+ if (!chan) {
+ return;
+ }
+
+ // Don't bother caching the result of this URI load, but do not exempt
+ // it from Safe Browsing.
+ chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING | nsIChannel::LOAD_CLASSIFY_URI);
+
+ nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
+ if (!httpChan) {
+ return;
+ }
+
+ // This is needed in order for 3rd-party cookie blocking to work.
+ nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(httpChan);
+ nsresult rv;
+ if (httpInternal) {
+ rv = httpInternal->SetDocumentURI(doc->GetDocumentURI());
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+
+ rv = httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ // Remove extraneous request headers (to reduce request size)
+ rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
+ EmptyCString(), false);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"),
+ EmptyCString(), false);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"),
+ EmptyCString(), false);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ // Always send a Ping-To header.
+ nsAutoCString pingTo;
+ if (NS_SUCCEEDED(info->target->GetSpec(pingTo))) {
+ rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-To"), pingTo, false);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+
+ nsCOMPtr<nsIScriptSecurityManager> sm =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+
+ if (sm && info->referrer) {
+ bool referrerIsSecure;
+ uint32_t flags = nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
+ rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure);
+
+ // Default to sending less data if NS_URIChainHasFlags() fails.
+ referrerIsSecure = NS_FAILED(rv) || referrerIsSecure;
+
+ bool sameOrigin =
+ NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, aURI, false));
+
+ // If both the address of the document containing the hyperlink being
+ // audited and "ping URL" have the same origin or the document containing
+ // the hyperlink being audited was not retrieved over an encrypted
+ // connection, send a Ping-From header.
+ if (sameOrigin || !referrerIsSecure) {
+ nsAutoCString pingFrom;
+ if (NS_SUCCEEDED(info->referrer->GetSpec(pingFrom))) {
+ rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-From"),
+ pingFrom, false);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+ }
+
+ // If the document containing the hyperlink being audited was not retrieved
+ // over an encrypted connection and its address does not have the same
+ // origin as "ping URL", send a referrer.
+ if (!sameOrigin && !referrerIsSecure) {
+ rv = httpChan->SetReferrerWithPolicy(info->referrer, info->referrerPolicy);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+ }
+
+ nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(httpChan);
+ if (!uploadChan) {
+ return;
+ }
+
+ NS_NAMED_LITERAL_CSTRING(uploadData, "PING");
+
+ nsCOMPtr<nsIInputStream> uploadStream;
+ rv = NS_NewCStringInputStream(getter_AddRefs(uploadStream), uploadData);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ uploadChan->ExplicitSetUploadStream(uploadStream,
+ NS_LITERAL_CSTRING("text/ping"),
+ uploadData.Length(),
+ NS_LITERAL_CSTRING("POST"), false);
+
+ // The channel needs to have a loadgroup associated with it, so that we can
+ // cancel the channel and any redirected channels it may create.
+ nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
+ if (!loadGroup) {
+ return;
+ }
+ nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(info->docShell);
+ loadGroup->SetNotificationCallbacks(callbacks);
+ chan->SetLoadGroup(loadGroup);
+
+ RefPtr<nsPingListener> pingListener = new nsPingListener();
+ chan->AsyncOpen2(pingListener);
+
+ // Even if AsyncOpen failed, we still count this as a successful ping. It's
+ // possible that AsyncOpen may have failed after triggering some background
+ // process that may have written something to the network.
+ info->numPings++;
+
+ // Prevent ping requests from stalling and never being garbage collected...
+ if (NS_FAILED(pingListener->StartTimeout(doc->GetDocGroup()))) {
+ // If we failed to setup the timer, then we should just cancel the channel
+ // because we won't be able to ensure that it goes away in a timely manner.
+ chan->Cancel(NS_ERROR_ABORT);
+ return;
+ }
+ // if the channel openend successfully, then make the pingListener hold
+ // a strong reference to the loadgroup which is released in ::OnStopRequest
+ pingListener->SetLoadGroup(loadGroup);
+}
+
+typedef void (*ForEachPingCallback)(void* closure, nsIContent* content,
+ nsIURI* uri, nsIIOService* ios);
+
+static void
+ForEachPing(nsIContent* aContent, ForEachPingCallback aCallback, void* aClosure)
+{
+ // NOTE: Using nsIDOMHTMLAnchorElement::GetPing isn't really worth it here
+ // since we'd still need to parse the resulting string. Instead, we
+ // just parse the raw attribute. It might be nice if the content node
+ // implemented an interface that exposed an enumeration of nsIURIs.
+
+ // Make sure we are dealing with either an <A> or <AREA> element in the HTML
+ // or XHTML namespace.
+ if (!aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area)) {
+ return;
+ }
+
+ nsAutoString value;
+ aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::ping, value);
+ if (value.IsEmpty()) {
+ return;
+ }
+
+ nsCOMPtr<nsIIOService> ios = do_GetIOService();
+ if (!ios) {
+ return;
+ }
+
+ nsIDocument* doc = aContent->OwnerDoc();
+ nsAutoCString charset;
+ doc->GetDocumentCharacterSet()->Name(charset);
+
+ nsWhitespaceTokenizer tokenizer(value);
+
+ while (tokenizer.hasMoreTokens()) {
+ nsCOMPtr<nsIURI> uri, baseURI = aContent->GetBaseURI();
+ ios->NewURI(NS_ConvertUTF16toUTF8(tokenizer.nextToken()),
+ charset.get(), baseURI, getter_AddRefs(uri));
+ // if we can't generate a valid URI, then there is nothing to do
+ if (!uri) {
+ continue;
+ }
+ // Explicitly not allow loading data: URIs
+ bool isDataScheme =
+ (NS_SUCCEEDED(uri->SchemeIs("data", &isDataScheme)) && isDataScheme);
+
+ if (!isDataScheme) {
+ aCallback(aClosure, aContent, uri, ios);
+ }
+ }
+}
+
+// Spec: http://whatwg.org/specs/web-apps/current-work/#ping
+/*static*/ void
+nsPingListener::DispatchPings(nsIDocShell* aDocShell,
+ nsIContent* aContent,
+ nsIURI* aTarget,
+ nsIURI* aReferrer,
+ uint32_t aReferrerPolicy)
+{
+ SendPingInfo info;
+
+ if (!PingsEnabled(&info.maxPings, &info.requireSameHost)) {
+ return;
+ }
+ if (info.maxPings == 0) {
+ return;
+ }
+
+ info.numPings = 0;
+ info.target = aTarget;
+ info.referrer = aReferrer;
+ info.referrerPolicy = aReferrerPolicy;
+ info.docShell = aDocShell;
+
+ ForEachPing(aContent, SendPing, &info);
+}
+
+nsPingListener::~nsPingListener()
+{
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+}
+
+nsresult
+nsPingListener::StartTimeout(DocGroup* aDocGroup)
+{
+ NS_ENSURE_ARG(aDocGroup);
+
+ return NS_NewTimerWithFuncCallback(getter_AddRefs(mTimer),
+ OnPingTimeout,
+ mLoadGroup,
+ PING_TIMEOUT,
+ nsITimer::TYPE_ONE_SHOT,
+ "nsPingListener::StartTimeout",
+ aDocGroup->EventTargetFor(TaskCategory::Network));
+}
+
+NS_IMETHODIMP
+nsPingListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPingListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
+ nsIInputStream* aStream, uint64_t aOffset,
+ uint32_t aCount)
+{
+ uint32_t result;
+ return aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &result);
+}
+
+NS_IMETHODIMP
+nsPingListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
+ nsresult aStatus)
+{
+ mLoadGroup = nullptr;
+
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+
+ return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/docshell/base/nsPingListener.h
@@ -0,0 +1,55 @@
+/* -*- 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/. */
+
+#ifndef nsPingListener_h__
+#define nsPingListener_h__
+
+#include "nsIStreamListener.h"
+
+#include "nsCOMPtr.h"
+
+namespace mozilla {
+namespace dom {
+class DocGroup;
+}
+}
+
+class nsIContent;
+class nsIDocShell;
+class nsILoadGroup;
+class nsITimer;
+class nsIURI;
+
+class nsPingListener final : public nsIStreamListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ nsPingListener()
+ {
+ }
+
+ void SetLoadGroup(nsILoadGroup* aLoadGroup) {
+ mLoadGroup = aLoadGroup;
+ }
+
+ nsresult StartTimeout(mozilla::dom::DocGroup* aDocGroup);
+
+ static void DispatchPings(nsIDocShell* aDocShell,
+ nsIContent* aContent,
+ nsIURI* aTarget,
+ nsIURI* aReferrer,
+ uint32_t aReferrerPolicy);
+private:
+ ~nsPingListener();
+
+ nsCOMPtr<nsILoadGroup> mLoadGroup;
+ nsCOMPtr<nsITimer> mTimer;
+};
+
+#endif /* nsPingListener_h__ */
new file mode 100644
--- /dev/null
+++ b/docshell/base/nsRefreshTimer.cpp
@@ -0,0 +1,56 @@
+/* -*- 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 "nsRefreshTimer.h"
+
+#include "nsIURI.h"
+#include "nsIPrincipal.h"
+
+#include "nsDocShell.h"
+
+NS_IMPL_ADDREF(nsRefreshTimer)
+NS_IMPL_RELEASE(nsRefreshTimer)
+
+NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
+ NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+ NS_INTERFACE_MAP_ENTRY(nsINamed)
+NS_INTERFACE_MAP_END_THREADSAFE
+
+nsRefreshTimer::nsRefreshTimer(nsDocShell* aDocShell,
+ nsIURI* aURI,
+ nsIPrincipal* aPrincipal,
+ int32_t aDelay, bool aRepeat, bool aMetaRefresh)
+ : mDocShell(aDocShell), mURI(aURI), mPrincipal(aPrincipal),
+ mDelay(aDelay), mRepeat(aRepeat),
+ mMetaRefresh(aMetaRefresh)
+{
+}
+
+nsRefreshTimer::~nsRefreshTimer()
+{
+}
+
+NS_IMETHODIMP
+nsRefreshTimer::Notify(nsITimer* aTimer)
+{
+ NS_ASSERTION(mDocShell, "DocShell is somehow null");
+
+ if (mDocShell && aTimer) {
+ // Get the delay count to determine load type
+ uint32_t delay = 0;
+ aTimer->GetDelay(&delay);
+ mDocShell->ForceRefreshURIFromTimer(mURI, mPrincipal, delay, mMetaRefresh, aTimer);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRefreshTimer::GetName(nsACString& aName)
+{
+ aName.AssignLiteral("nsRefreshTimer");
+ return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/docshell/base/nsRefreshTimer.h
@@ -0,0 +1,47 @@
+/* -*- 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/. */
+
+#ifndef nsRefreshTimer_h__
+#define nsRefreshTimer_h__
+
+#include "nsINamed.h"
+#include "nsITimer.h"
+
+#include "nsCOMPtr.h"
+
+class nsDocShell;
+class nsIURI;
+class nsIPrincipal;
+
+class nsRefreshTimer : public nsITimerCallback
+ , public nsINamed
+{
+public:
+ nsRefreshTimer(nsDocShell* aDocShell,
+ nsIURI* aURI,
+ nsIPrincipal* aPrincipal,
+ int32_t aDelay,
+ bool aRepeat,
+ bool aMetaRefresh);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSINAMED
+
+ int32_t GetDelay() { return mDelay ;}
+
+ RefPtr<nsDocShell> mDocShell;
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+ int32_t mDelay;
+ bool mRepeat;
+ bool mMetaRefresh;
+
+private:
+ virtual ~nsRefreshTimer();
+};
+
+#endif /* nsRefreshTimer_h__ */