diff --git a/mozilla/docshell/base/nsWebShell.cpp b/mozilla/docshell/base/nsWebShell.cpp index 03969ee2b91..7a77949d76f 100644 --- a/mozilla/docshell/base/nsWebShell.cpp +++ b/mozilla/docshell/base/nsWebShell.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 cin et: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -100,15 +101,16 @@ #include "nsISHistoryInternal.h" #include "nsIHttpChannel.h" +#include "nsIHttpChannelInternal.h" #include "nsIUploadChannel.h" #include "nsISeekableStream.h" +#include "nsStreamUtils.h" #include "nsILocaleService.h" #include "nsIStringBundle.h" #include "nsICachingChannel.h" -//XXX for nsIPostData; this is wrong; we shouldn't see the nsIDocument type #include "nsIDocument.h" #include "nsITextToSubURI.h" @@ -117,6 +119,10 @@ #include "nsIIDNService.h" +#include "nsIPrefBranch.h" +#include "nsIPrefService.h" +#include "nsITimer.h" + #ifdef NS_DEBUG /** * Note: the log module is created during initialization which @@ -155,6 +161,189 @@ IsOffline() //---------------------------------------------------------------------- +// We wait this many milliseconds before killing the ping channel... +#define PING_TIMEOUT 10000 + +static void +OnPingTimeout(nsITimer *timer, void *closure) +{ + nsIRequest *request = NS_STATIC_CAST(nsIRequest *, closure); + request->Cancel(NS_BINDING_ABORTED); + request->Release(); +} + +//---------------------------------------------------------------------- + +class nsPingListener : public nsIStreamListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER +}; + +NS_IMPL_ISUPPORTS2(nsPingListener, nsIStreamListener, nsIRequestObserver) + +NS_IMETHODIMP +nsPingListener::OnStartRequest(nsIRequest *request, nsISupports *context) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsPingListener::OnDataAvailable(nsIRequest *request, nsISupports *context, + nsIInputStream *stream, PRUint32 offset, + PRUint32 count) +{ + PRUint32 result; + return stream->ReadSegments(NS_DiscardSegment, nsnull, count, &result); +} + +NS_IMETHODIMP +nsPingListener::OnStopRequest(nsIRequest *request, nsISupports *context, + nsresult status) +{ + return NS_OK; +} + +static void +SendPing(const nsSubstring &relSpec, nsIDocument *doc, nsIURI *referrer, + nsIIOService *ios) +{ + nsCOMPtr chan; + ios->NewChannel(NS_ConvertUTF16toUTF8(relSpec), + doc->GetDocumentCharacterSet().get(), + doc->GetBaseURI(), + getter_AddRefs(chan)); + if (!chan) + return; + + // Don't bother caching the result of this URI load. + chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING); + + nsCOMPtr httpChan = do_QueryInterface(chan); + if (httpChan) { + // This is needed in order for 3rd-party cookie blocking to work. + nsCOMPtr httpInternal = do_QueryInterface(httpChan); + if (httpInternal) + httpInternal->SetDocumentURI(doc->GetBaseURI()); + + if (referrer) + httpChan->SetReferrer(referrer); + + httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST")); + + // Remove extraneous request headers (to reduce request size) + httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"), + EmptyCString(), PR_FALSE); + httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"), + EmptyCString(), PR_FALSE); + httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-charset"), + EmptyCString(), PR_FALSE); + httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"), + EmptyCString(), PR_FALSE); + + nsCOMPtr uploadChan = do_QueryInterface(httpChan); + if (!uploadChan) + return; + + // To avoid sending an unnecessary Content-Type header, we encode the + // closing portion of the headers in the POST body. + NS_NAMED_LITERAL_CSTRING(uploadData, "Content-Length: 0\r\n\r\n"); + + nsCOMPtr uploadStream; + NS_NewPostDataStream(getter_AddRefs(uploadStream), PR_FALSE, + uploadData, 0); + if (!uploadStream) + return; + + uploadChan->SetUploadStream(uploadStream, EmptyCString(), -1); + } + + // Construct a listener that merely discards any response. If successful at + // opening the channel, then it is not necessary to hold a reference to the + // channel. The networking subsystem will take care of that for us. + nsCOMPtr listener = new nsPingListener(); + if (listener) { + chan->AsyncOpen(listener, nsnull); + + // Prevent ping requests from stalling and never being garbage collected... + nsCOMPtr timer = + do_CreateInstance(NS_TIMER_CONTRACTID); + if (timer) { + nsresult rv = timer->InitWithFuncCallback(OnPingTimeout, chan, + PING_TIMEOUT, + nsITimer::TYPE_ONE_SHOT); + if (NS_SUCCEEDED(rv)) { + // When the timer expires, the callback function will release this + // reference to the channel. + NS_STATIC_CAST(nsIChannel *, chan.get())->AddRef(); + } + } + } +} + +// Spec: http://whatwg.org/specs/web-apps/current-work/#ping +static void +DispatchPings(nsIContent *content, nsIURI *referrer) +{ + // Make sure we are dealing with either an or element in the HTML + // or XHTML namespace. + if (!content->IsContentOfType(nsIContent::eHTML)) + return; + nsIAtom *nameAtom = content->Tag(); + if (!nameAtom->EqualsUTF8(NS_LITERAL_CSTRING("a")) && + !nameAtom->EqualsUTF8(NS_LITERAL_CSTRING("area"))) + return; + + nsCOMPtr pingAtom = do_GetAtom("ping"); + if (!pingAtom) + return; + + nsAutoString value; + content->GetAttr(kNameSpaceID_None, pingAtom, value); + if (value.IsEmpty()) + return; + + // check prefs to see if pings are enabled + nsCOMPtr prefs = + do_GetService(NS_PREFSERVICE_CONTRACTID); + if (prefs) { + PRBool allow = PR_TRUE; + prefs->GetBoolPref("browser.send_pings", &allow); + if (!allow) + return; + } + + nsCOMPtr ios = do_GetIOService(); + if (!ios) + return; + + nsIDocument *doc = content->GetOwnerDoc(); + if (!doc) + return; + + // value contains relative URIs split on spaces (U+0020) + const PRUnichar *start = value.BeginReading(); + const PRUnichar *end = value.EndReading(); + const PRUnichar *iter = start; + for (;;) { + if (iter < end && *iter != ' ') { + ++iter; + } else { // iter is pointing at either end or a space + while (*start == ' ' && start < iter) + ++start; + if (iter != start) + SendPing(Substring(start, iter), doc, referrer, ios); + start = iter = iter + 1; + if (iter >= end) + break; + } + } +} + +//---------------------------------------------------------------------- + // Note: operator new zeros our memory nsWebShell::nsWebShell() : nsDocShell() { @@ -524,6 +713,7 @@ nsWebShell::OnLinkClickSync(nsIContent *aContent, *aRequest = nsnull; } + nsresult rv; switch(aVerb) { case eLinkVerb_New: target.AssignLiteral("_blank"); @@ -532,19 +722,22 @@ nsWebShell::OnLinkClickSync(nsIContent *aContent, // Fall through, this seems like the most reasonable action case eLinkVerb_Replace: { - return InternalLoad(aURI, // New URI - referer, // Referer URI - nsnull, // No onwer - INTERNAL_LOAD_FLAGS_INHERIT_OWNER, // Inherit owner from document - target.get(), // Window target - NS_LossyConvertUCS2toASCII(typeHint).get(), - aPostDataStream, // Post data stream - aHeadersDataStream, // Headers stream - LOAD_LINK, // Load type - nsnull, // No SHEntry - PR_TRUE, // first party site - aDocShell, // DocShell out-param - aRequest); // Request out-param + rv = InternalLoad(aURI, // New URI + referer, // Referer URI + nsnull, // No onwer + INTERNAL_LOAD_FLAGS_INHERIT_OWNER, // Inherit owner from document + target.get(), // Window target + NS_LossyConvertUCS2toASCII(typeHint).get(), + aPostDataStream, // Post data stream + aHeadersDataStream, // Headers stream + LOAD_LINK, // Load type + nsnull, // No SHEntry + PR_TRUE, // first party site + aDocShell, // DocShell out-param + aRequest); // Request out-param + if (NS_SUCCEEDED(rv)) { + DispatchPings(aContent, referer); + } } break; case eLinkVerb_Embed: @@ -552,8 +745,9 @@ nsWebShell::OnLinkClickSync(nsIContent *aContent, // in NS 4.x default: NS_ABORT_IF_FALSE(0,"unexpected link verb"); - return NS_ERROR_UNEXPECTED; + rv = NS_ERROR_UNEXPECTED; } + return rv; } NS_IMETHODIMP diff --git a/mozilla/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp b/mozilla/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp index 69c02647f22..c345ac873c6 100644 --- a/mozilla/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp +++ b/mozilla/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp @@ -60,6 +60,7 @@ #include "nsCRT.h" #include "nsSupportsArray.h" #include "nsInt64.h" +#include "nsStreamUtils.h" #include "nsCExternalHandlerService.h" @@ -791,17 +792,6 @@ NS_IMETHODIMP nsWebBrowserPersist::OnStopRequest( // nsWebBrowserPersist::nsIStreamListener //***************************************************************************** -static NS_METHOD DiscardSegments(nsIInputStream *input, - void *closure, - const char *buf, - PRUint32 offset, - PRUint32 count, - PRUint32 *countRead) -{ - *countRead = count; - return NS_OK; -} - NS_IMETHODIMP nsWebBrowserPersist::OnDataAvailable( nsIRequest* request, nsISupports *aContext, nsIInputStream *aIStream, PRUint32 aOffset, PRUint32 aLength) @@ -821,7 +811,7 @@ NS_IMETHODIMP nsWebBrowserPersist::OnDataAvailable( if (!data) { // might be uploadData; consume necko's buffer and bail... PRUint32 n; - return aIStream->ReadSegments(DiscardSegments, nsnull, aLength, &n); + return aIStream->ReadSegments(NS_DiscardSegment, nsnull, aLength, &n); } PRBool readError = PR_TRUE; diff --git a/mozilla/modules/libpref/src/init/all.js b/mozilla/modules/libpref/src/init/all.js index b2efd666098..718f25d9e39 100644 --- a/mozilla/modules/libpref/src/init/all.js +++ b/mozilla/modules/libpref/src/init/all.js @@ -84,6 +84,9 @@ pref("browser.underline_anchors", true); pref("browser.blink_allowed", true); pref("browser.enable_automatic_image_resizing", false); +// See http://whatwg.org/specs/web-apps/current-work/#ping +pref("browser.send_pings", true); + pref("browser.display.use_focus_colors", false); pref("browser.display.focus_background_color", "#117722"); pref("browser.display.focus_text_color", "#ffffff"); diff --git a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp index c5af08a4b45..f7baac64c83 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -71,6 +71,7 @@ #include "nsInt64.h" #include "nsIVariant.h" #include "nsChannelProperties.h" +#include "nsStreamUtils.h" // True if the local cache should be bypassed when processing a request. #define BYPASS_LOCAL_CACHE(loadFlags) \ @@ -79,17 +80,6 @@ static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID); -static NS_METHOD DiscardSegments(nsIInputStream *input, - void *closure, - const char *buf, - PRUint32 offset, - PRUint32 count, - PRUint32 *countRead) -{ - *countRead = count; - return NS_OK; -} - // // From section 2.2 of RFC 2616, a token is defined as: // @@ -4158,7 +4148,7 @@ nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, if (mAuthRetryPending || (request == mTransactionPump && mTransactionReplaced)) { PRUint32 n; - return input->ReadSegments(DiscardSegments, nsnull, count, &n); + return input->ReadSegments(NS_DiscardSegment, nsnull, count, &n); } if (mListener) { diff --git a/mozilla/netwerk/streamconv/converters/nsHTTPCompressConv.cpp b/mozilla/netwerk/streamconv/converters/nsHTTPCompressConv.cpp index 3b1333450c2..bae81b70aa3 100644 --- a/mozilla/netwerk/streamconv/converters/nsHTTPCompressConv.cpp +++ b/mozilla/netwerk/streamconv/converters/nsHTTPCompressConv.cpp @@ -45,21 +45,10 @@ #include "nsCOMPtr.h" #include "nsReadableUtils.h" #include "nsNetError.h" +#include "nsStreamUtils.h" #include "nsStringStream.h" #include "nsComponentManagerUtils.h" -static NS_METHOD -DiscardSegments(nsIInputStream *input, - void *closure, - const char *buf, - PRUint32 offset, - PRUint32 count, - PRUint32 *countRead) -{ - *countRead = count; - return NS_OK; -} - // nsISupports implementation NS_IMPL_ISUPPORTS2(nsHTTPCompressConv, nsIStreamConverter, nsIStreamListener) @@ -157,7 +146,7 @@ nsHTTPCompressConv::OnDataAvailable(nsIRequest* request, // what's left is either metadata or padding of some sort.... throwing // it out is probably the safe thing to do. PRUint32 n; - return iStr->ReadSegments(DiscardSegments, nsnull, streamLen, &n); + return iStr->ReadSegments(NS_DiscardSegment, nsnull, streamLen, &n); } switch (mMode) diff --git a/mozilla/xpcom/build/dlldeps.cpp b/mozilla/xpcom/build/dlldeps.cpp index 37bd2b78f78..362620e5f3d 100644 --- a/mozilla/xpcom/build/dlldeps.cpp +++ b/mozilla/xpcom/build/dlldeps.cpp @@ -125,6 +125,9 @@ void XXXNeverCalled() } NS_InputStreamIsBuffered(nsnull); NS_OutputStreamIsBuffered(nsnull); + NS_CopySegmentToStream(nsnull, nsnull, nsnull, 0, 0, nsnull); + NS_CopySegmentToBuffer(nsnull, nsnull, nsnull, 0, 0, nsnull); + NS_DiscardSegment(nsnull, nsnull, nsnull, 0, 0, nsnull); NS_NewByteInputStream(nsnull, nsnull, 0, NS_ASSIGNMENT_COPY); NS_NewCStringInputStream(nsnull, nsCString()); NS_NewStringInputStream(nsnull, nsString()); diff --git a/mozilla/xpcom/io/nsStreamUtils.cpp b/mozilla/xpcom/io/nsStreamUtils.cpp index c863574c782..b0f396a1b59 100644 --- a/mozilla/xpcom/io/nsStreamUtils.cpp +++ b/mozilla/xpcom/io/nsStreamUtils.cpp @@ -709,3 +709,15 @@ NS_CopySegmentToBuffer(nsIInputStream *inStr, *countWritten = count; return NS_OK; } + +NS_COM NS_METHOD +NS_DiscardSegment(nsIInputStream *inStr, + void *closure, + const char *buffer, + PRUint32 offset, + PRUint32 count, + PRUint32 *countWritten) +{ + *countWritten = count; + return NS_OK; +} diff --git a/mozilla/xpcom/io/nsStreamUtils.h b/mozilla/xpcom/io/nsStreamUtils.h index 2e98e7c24c9..0db75b12d69 100644 --- a/mozilla/xpcom/io/nsStreamUtils.h +++ b/mozilla/xpcom/io/nsStreamUtils.h @@ -192,4 +192,16 @@ NS_CopySegmentToBuffer(nsIInputStream *aInputStream, void *aClosure, const char *aFromSegment, PRUint32 aToOffset, PRUint32 aCount, PRUint32 *aWriteCount); +/** + * This function is intended to be passed to nsIInputStream::ReadSegments to + * discard data from the nsIInputStream. This can be used to efficiently read + * data from the stream without actually copying any bytes. + * + * @see nsIInputStream.idl for a description of this function's parameters. + */ +extern NS_COM NS_METHOD +NS_DiscardSegment(nsIInputStream *aInputStream, void *aClosure, + const char *aFromSegment, PRUint32 aToOffset, + PRUint32 aCount, PRUint32 *aWriteCount); + #endif // !nsStreamUtils_h__