Bug 676619 - Implementation of the download attribute for links. r=smaug

This commit is contained in:
Tom Schuster 2012-12-02 21:55:52 +01:00
parent 10f6d4b56d
commit 12e54d9bdb
14 changed files with 93 additions and 40 deletions

View File

@ -4663,7 +4663,6 @@ nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
if (!aClick) {
handler->OnOverLink(aContent, aLinkURI, aTargetSpec.get());
return;
}
@ -4681,9 +4680,25 @@ nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
}
// Only pass off the click event if the script security manager says it's ok.
// We need to rest aTargetSpec for forced downloads.
if (NS_SUCCEEDED(proceed)) {
handler->OnLinkClick(aContent, aLinkURI, aTargetSpec.get(), nullptr, nullptr,
aIsTrusted);
// A link/area element with a download attribute is allowed to set
// a pseudo Content-Disposition header.
// For security reasons we only allow websites to declare same-origin resources
// as downloadable. If this check fails we will just do the normal thing
// (i.e. navigate to the resource).
nsAutoString fileName;
if ((!aContent->IsHTML(nsGkAtoms::a) && !aContent->IsHTML(nsGkAtoms::area) &&
!aContent->IsSVG(nsGkAtoms::a)) ||
!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::download, fileName) ||
NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, false, true))) {
fileName.SetIsVoid(true); // No actionable download attribute was found.
}
handler->OnLinkClick(aContent, aLinkURI,
fileName.IsVoid() ? aTargetSpec.get() : EmptyString().get(),
fileName, nullptr, nullptr, aIsTrusted);
}
}

View File

@ -287,6 +287,7 @@ GK_ATOM(dl, "dl")
GK_ATOM(doctypePublic, "doctype-public")
GK_ATOM(doctypeSystem, "doctype-system")
GK_ATOM(document, "document")
GK_ATOM(download, "download")
GK_ATOM(DOMAttrModified, "DOMAttrModified")
GK_ATOM(DOMCharacterDataModified, "DOMCharacterDataModified")
GK_ATOM(DOMNodeInserted, "DOMNodeInserted")

View File

@ -160,6 +160,7 @@ NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Rel, rel)
NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Rev, rev)
NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Shape, shape)
NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Type, type)
NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Download, download)
int32_t
nsHTMLAnchorElement::TabIndexDefault()

View File

@ -120,6 +120,7 @@ NS_IMPL_STRING_ATTR(nsHTMLAreaElement, Coords, coords)
NS_IMPL_URI_ATTR(nsHTMLAreaElement, Href, href)
NS_IMPL_BOOL_ATTR(nsHTMLAreaElement, NoHref, nohref)
NS_IMPL_STRING_ATTR(nsHTMLAreaElement, Shape, shape)
NS_IMPL_STRING_ATTR(nsHTMLAreaElement, Download, download)
int32_t
nsHTMLAreaElement::TabIndexDefault()

View File

@ -883,6 +883,7 @@ nsHTMLFormElement::SubmitSubmission(nsFormSubmission* aFormSubmission)
rv = linkHandler->OnLinkClickSync(this, actionURI,
target.get(),
NullString(),
postDataStream, nullptr,
getter_AddRefs(docShell),
getter_AddRefs(mSubmittingRequest));

View File

@ -68,6 +68,7 @@ nsSVGAElement::GetHref(nsIDOMSVGAnimatedString * *aHref)
return mStringAttributes[HREF].ToDOMAnimatedString(aHref, this);
}
NS_IMPL_STRING_ATTR(nsSVGAElement, Download, download)
//----------------------------------------------------------------------
// nsINode methods

View File

@ -1501,6 +1501,7 @@ nsDocShell::LoadURI(nsIURI * aURI,
flags,
target.get(),
nullptr, // No type hint
NullString(), // No forced download
postStream,
headersStream,
loadType,
@ -4494,7 +4495,7 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
return InternalLoad(errorPageURI, nullptr, nullptr,
INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, nullptr,
nullptr, nullptr, LOAD_ERROR_PAGE,
NullString(), nullptr, nullptr, LOAD_ERROR_PAGE,
nullptr, true, nullptr, nullptr);
}
@ -4549,16 +4550,16 @@ nsDocShell::Reload(uint32_t aReloadFlags)
INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document
nullptr, // No window target
NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
NullString(), // No forced download
nullptr, // No post data
nullptr, // No headers data
loadType, // Load type
loadType, // Load type
nullptr, // No SHEntry
true,
nullptr, // No nsIDocShell
nullptr); // No nsIRequest
}
return rv;
}
@ -8322,8 +8323,9 @@ public:
NS_IMETHOD Run() {
return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags,
nullptr, mTypeHint.get(),
mPostData, mHeadersData, mLoadType,
mSHEntry, mFirstParty, nullptr, nullptr);
NullString(), mPostData, mHeadersData,
mLoadType, mSHEntry, mFirstParty,
nullptr, nullptr);
}
private:
@ -8368,6 +8370,7 @@ nsDocShell::InternalLoad(nsIURI * aURI,
uint32_t aFlags,
const PRUnichar *aWindowTarget,
const char* aTypeHint,
const nsAString& aFileName,
nsIInputStream * aPostData,
nsIInputStream * aHeadersData,
uint32_t aLoadType,
@ -8387,7 +8390,6 @@ nsDocShell::InternalLoad(nsIURI * aURI,
PR_LogPrint("DOCSHELL %p InternalLoad %s\n", this, spec.get());
}
#endif
// Initialize aDocShell/aRequest
if (aDocShell) {
*aDocShell = nullptr;
@ -8602,6 +8604,7 @@ nsDocShell::InternalLoad(nsIURI * aURI,
aFlags,
nullptr, // No window target
aTypeHint,
NullString(), // No forced download
aPostData,
aHeadersData,
aLoadType,
@ -9096,8 +9099,8 @@ nsDocShell::InternalLoad(nsIURI * aURI,
nsCOMPtr<nsIRequest> req;
rv = DoURILoad(aURI, aReferrer,
!(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
owner, aTypeHint, aPostData, aHeadersData, aFirstParty,
aDocShell, getter_AddRefs(req),
owner, aTypeHint, aFileName, aPostData, aHeadersData,
aFirstParty, aDocShell, getter_AddRefs(req),
(aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
(aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
(aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0);
@ -9170,6 +9173,7 @@ nsDocShell::DoURILoad(nsIURI * aURI,
bool aSendReferrer,
nsISupports * aOwner,
const char * aTypeHint,
const nsAString & aFileName,
nsIInputStream * aPostData,
nsIInputStream * aHeadersData,
bool aFirstParty,
@ -9269,11 +9273,19 @@ nsDocShell::DoURILoad(nsIURI * aURI,
if (aTypeHint && *aTypeHint) {
channel->SetContentType(nsDependentCString(aTypeHint));
mContentTypeHint = aTypeHint;
}
else {
} else {
mContentTypeHint.Truncate();
}
if (!aFileName.IsVoid()) {
rv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
NS_ENSURE_SUCCESS(rv, rv);
if (!aFileName.IsEmpty()) {
rv = channel->SetContentDispositionFilename(aFileName);
NS_ENSURE_SUCCESS(rv, rv);
}
}
//hack
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
@ -10586,11 +10598,12 @@ nsDocShell::LoadHistoryEntry(nsISHEntry * aEntry, uint32_t aLoadType)
owner,
INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document (security-critical!)
nullptr, // No window target
contentType.get(), // Type hint
postData, // Post data stream
contentType.get(), // Type hint
NullString(), // No forced file download
postData, // Post data stream
nullptr, // No headers stream
aLoadType, // Load type
aEntry, // SHEntry
aLoadType, // Load type
aEntry, // SHEntry
true,
nullptr, // No nsIDocShell
nullptr); // No nsIRequest
@ -11823,6 +11836,7 @@ public:
OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
nsIURI* aURI,
const PRUnichar* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
nsIInputStream* aHeadersDataStream,
bool aIsTrusted);
@ -11834,8 +11848,8 @@ public:
nsCxPusher pusher;
if (mIsTrusted || pusher.Push(mContent)) {
mHandler->OnLinkClickSync(mContent, mURI,
mTargetSpec.get(), mPostDataStream,
mHeadersDataStream,
mTargetSpec.get(), mFileName,
mPostDataStream, mHeadersDataStream,
nullptr, nullptr);
}
return NS_OK;
@ -11845,6 +11859,7 @@ private:
nsRefPtr<nsDocShell> mHandler;
nsCOMPtr<nsIURI> mURI;
nsString mTargetSpec;
nsString mFileName;
nsCOMPtr<nsIInputStream> mPostDataStream;
nsCOMPtr<nsIInputStream> mHeadersDataStream;
nsCOMPtr<nsIContent> mContent;
@ -11856,12 +11871,14 @@ OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
nsIContent *aContent,
nsIURI* aURI,
const PRUnichar* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
nsIInputStream* aHeadersDataStream,
bool aIsTrusted)
: mHandler(aHandler)
, mURI(aURI)
, mTargetSpec(aTargetSpec)
, mFileName(aFileName)
, mPostDataStream(aPostDataStream)
, mHeadersDataStream(aHeadersDataStream)
, mContent(aContent)
@ -11878,6 +11895,7 @@ NS_IMETHODIMP
nsDocShell::OnLinkClick(nsIContent* aContent,
nsIURI* aURI,
const PRUnichar* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
nsIInputStream* aHeadersDataStream,
bool aIsTrusted)
@ -11917,7 +11935,7 @@ nsDocShell::OnLinkClick(nsIContent* aContent,
target = aTargetSpec;
nsCOMPtr<nsIRunnable> ev =
new OnLinkClickEvent(this, aContent, aURI, target.get(),
new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
aPostDataStream, aHeadersDataStream, aIsTrusted);
return NS_DispatchToCurrentThread(ev);
}
@ -11926,6 +11944,7 @@ NS_IMETHODIMP
nsDocShell::OnLinkClickSync(nsIContent *aContent,
nsIURI* aURI,
const PRUnichar* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
nsIInputStream* aHeadersDataStream,
nsIDocShell** aDocShell,
@ -12026,11 +12045,12 @@ nsDocShell::OnLinkClickSync(nsIContent *aContent,
INTERNAL_LOAD_FLAGS_NONE,
target.get(), // Window target
NS_LossyConvertUTF16toASCII(typeHint).get(),
aFileName, // Download as file
aPostDataStream, // Post data stream
aHeadersDataStream, // Headers stream
LOAD_LINK, // Load type
nullptr, // No SHEntry
true, // first party site
nullptr, // No SHEntry
true, // first party site
aDocShell, // DocShell out-param
aRequest); // Request out-param
if (NS_SUCCEEDED(rv)) {

View File

@ -197,12 +197,14 @@ public:
NS_IMETHOD OnLinkClick(nsIContent* aContent,
nsIURI* aURI,
const PRUnichar* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
nsIInputStream* aHeadersDataStream,
bool aIsTrusted);
NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
nsIURI* aURI,
const PRUnichar* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream = 0,
nsIInputStream* aHeadersDataStream = 0,
nsIDocShell** aDocShell = 0,
@ -291,6 +293,7 @@ protected:
bool aSendReferrer,
nsISupports * aOwner,
const char * aTypeHint,
const nsAString & aFileName,
nsIInputStream * aPostData,
nsIInputStream * aHeadersData,
bool firstParty,

View File

@ -39,7 +39,7 @@ interface nsIWebBrowserPrint;
interface nsIVariant;
interface nsIPrivacyTransitionObserver;
[scriptable, builtinclass, uuid(318CE516-3F7A-41F6-8F3D-3661650F7A46)]
[scriptable, builtinclass, uuid(a106db7f-6449-4a6b-914f-834ba308c3e2)]
interface nsIDocShell : nsISupports
{
/**
@ -118,6 +118,8 @@ interface nsIDocShell : nsISupports
* @param aWindowTarget - Window target for the load.
* @param aTypeHint - A hint as to the content-type of the resulting
* data. May be null or empty if no hint.
* @param aFileName - Non-null when the link should be downloaded as
the given filename.
* @param aPostDataStream - Post data stream (if POSTing)
* @param aHeadersStream - Stream containing "extra" request headers...
* @param aLoadFlags - Flags to modify load behaviour. Flags are defined
@ -130,6 +132,7 @@ interface nsIDocShell : nsISupports
in uint32_t aFlags,
in wstring aWindowTarget,
in string aTypeHint,
in AString aFileName,
in nsIInputStream aPostDataStream,
in nsIInputStream aHeadersStream,
in unsigned long aLoadFlags,

View File

@ -16,8 +16,8 @@ class nsGUIEvent;
// Interface ID for nsILinkHandler
#define NS_ILINKHANDLER_IID \
{ 0xd85670a1, 0x224a, 0x4562, \
{ 0x87, 0xa9, 0x43, 0xa5, 0x24, 0xe7, 0xd0, 0x1b } }
{ 0xceb9aade, 0x43da, 0x4f1a, \
{ 0xac, 0x8a, 0xc7, 0x09, 0xfb, 0x22, 0x46, 0x64 } }
/**
* Interface used for handling clicks on links
@ -34,12 +34,14 @@ public:
* @param aTargetSpec indicates where the link is targeted (may be an empty
* string)
* @param aPostDataStream the POST data to send
* @param aFileName non-null when the link should be downloaded as the given file
* @param aHeadersDataStream ???
* @param aIsTrusted false if the triggerer is an untrusted DOM event.
*/
NS_IMETHOD OnLinkClick(nsIContent* aContent,
nsIURI* aURI,
const PRUnichar* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
nsIInputStream* aHeadersDataStream,
bool aIsTrusted) = 0;
@ -54,6 +56,7 @@ public:
* @param aURI a URI obect that defines the destination for the link
* @param aTargetSpec indicates where the link is targeted (may be an empty
* string)
* @param aFileName non-null when the link should be downloaded as the given file
* @param aPostDataStream the POST data to send
* @param aHeadersDataStream ???
* @param aDocShell (out-param) the DocShell that the request was opened on
@ -62,6 +65,7 @@ public:
NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
nsIURI* aURI,
const PRUnichar* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream = 0,
nsIInputStream* aHeadersDataStream = 0,
nsIDocShell** aDocShell = 0,

View File

@ -16,13 +16,14 @@
* http://www.whatwg.org/specs/web-apps/current-work/
*/
[scriptable, uuid(68F49F8F-5FFD-44EB-A59F-D2B3F4817299)]
[scriptable, uuid(1339c36e-23ad-4047-a04c-1702e27c7c83)]
interface nsIDOMHTMLAnchorElement : nsIDOMHTMLElement
{
attribute DOMString href;
attribute DOMString target;
attribute DOMString ping;
attribute DOMString download;
attribute DOMString rel;
attribute DOMString hreflang;

View File

@ -16,7 +16,7 @@
* http://www.whatwg.org/specs/web-apps/current-work/
*/
[scriptable, uuid(D3043539-158A-43EC-B845-175B5726AEB7)]
[scriptable, uuid(69c5ce45-108a-442e-91c5-8c874e384ed7)]
interface nsIDOMHTMLAreaElement : nsIDOMHTMLElement
{
attribute DOMString alt;
@ -26,6 +26,7 @@ interface nsIDOMHTMLAreaElement : nsIDOMHTMLElement
attribute DOMString target;
attribute DOMString ping;
attribute DOMString download;
// URL decomposition IDL attributes
attribute DOMString protocol;

View File

@ -12,7 +12,7 @@
interface nsIDOMSVGAnimatedString;
[scriptable, uuid(DBC9B56C-3DE3-4475-A934-EE88D3BCB03C)]
[scriptable, uuid(6e0eff6e-ce35-4c01-ab3c-ae81b79b40ca)]
interface nsIDOMSVGAElement
: nsIDOMSVGElement
/*
@ -33,4 +33,5 @@ interface nsIDOMSVGAElement
*/
{
readonly attribute nsIDOMSVGAnimatedString target;
attribute DOMString download;
};

View File

@ -563,7 +563,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL,
Preferences::GetInt("privacy.popups.disable_from_plugins");
nsAutoPopupStatePusher popupStatePusher((PopupControlState)blockPopups);
rv = lh->OnLinkClick(mContent, uri, unitarget.get(),
rv = lh->OnLinkClick(mContent, uri, unitarget.get(), NullString(),
aPostStream, headersDataStream, true);
return rv;