Bug 1170837 - Make nsMultiMixedConv not return an error when served only a package's metadata from the cache r=honzab

Make the stream converter aware of the "application/package" mimetype.
This commit is contained in:
Valentin Gosu 2015-07-23 14:07:00 +02:00
parent fef1c0dbe0
commit b1ab85c4cb
6 changed files with 47 additions and 25 deletions

View File

@ -433,6 +433,7 @@ nsresult NS_NewStreamConv(nsStreamConverterService **aStreamConv);
#define INDEX_TO_HTML "?from=application/http-index-format&to=text/html"
#define MULTI_MIXED_X "?from=multipart/x-mixed-replace&to=*/*"
#define MULTI_MIXED "?from=multipart/mixed&to=*/*"
#define APPLICATION_PACKAGE_CONV "?from=" APPLICATION_PACKAGE "&to=*/*"
#define MULTI_BYTERANGES "?from=multipart/byteranges&to=*/*"
#define UNKNOWN_CONTENT "?from=" UNKNOWN_CONTENT_TYPE "&to=*/*"
#define GZIP_TO_UNCOMPRESSED "?from=gzip&to=uncompressed"
@ -451,6 +452,7 @@ static const mozilla::Module::CategoryEntry kNeckoCategories[] = {
{ NS_ISTREAMCONVERTER_KEY, INDEX_TO_HTML, "" },
{ NS_ISTREAMCONVERTER_KEY, MULTI_MIXED_X, "" },
{ NS_ISTREAMCONVERTER_KEY, MULTI_MIXED, "" },
{ NS_ISTREAMCONVERTER_KEY, APPLICATION_PACKAGE_CONV, "" },
{ NS_ISTREAMCONVERTER_KEY, MULTI_BYTERANGES, "" },
{ NS_ISTREAMCONVERTER_KEY, UNKNOWN_CONTENT, "" },
{ NS_ISTREAMCONVERTER_KEY, GZIP_TO_UNCOMPRESSED, "" },
@ -1032,6 +1034,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
{ NS_ISTREAMCONVERTER_KEY MULTI_MIXED_X, &kNS_MULTIMIXEDCONVERTER_CID },
{ NS_ISTREAMCONVERTER_KEY MULTI_BYTERANGES, &kNS_MULTIMIXEDCONVERTER_CID },
{ NS_ISTREAMCONVERTER_KEY MULTI_MIXED, &kNS_MULTIMIXEDCONVERTER_CID },
{ NS_ISTREAMCONVERTER_KEY APPLICATION_PACKAGE_CONV, &kNS_MULTIMIXEDCONVERTER_CID },
{ NS_ISTREAMCONVERTER_KEY UNKNOWN_CONTENT, &kNS_UNKNOWNDECODER_CID },
{ NS_GENERIC_CONTENT_SNIFFER, &kNS_UNKNOWNDECODER_CID },
{ NS_BINARYDETECTOR_CONTRACTID, &kNS_BINARYDETECTOR_CID },

View File

@ -72,6 +72,7 @@
#define APPLICATION_XSLT_XML "application/xslt+xml"
#define APPLICATION_MATHML_XML "application/mathml+xml"
#define APPLICATION_RDF_XML "application/rdf+xml"
#define APPLICATION_PACKAGE "application/package"
#define AUDIO_BASIC "audio/basic"
#define AUDIO_OGG "audio/ogg"

View File

@ -342,7 +342,9 @@ PackagedAppService::PackagedAppDownloader::OnStopRequest(nsIRequest *aRequest,
CallCallbacks(uri, entry, aStatusCode);
}
bool lastPart = false;
// lastPart will be true if this is the last part in the package,
// or if aRequest isn't a multipart channel
bool lastPart = true;
if (multiChannel) {
rv = multiChannel->GetIsLastPart(&lastPart);
if (NS_SUCCEEDED(rv) && !lastPart) {

View File

@ -13,7 +13,7 @@
#include "nsIHttpChannelInternal.h"
#include "nsURLHelper.h"
#include "nsIStreamConverterService.h"
#include "nsIPackagedAppService.h"
#include "nsICacheInfoChannel.h"
#include <algorithm>
#include "nsContentSecurityManager.h"
#include "nsHttp.h"
@ -492,8 +492,7 @@ nsMultiMixedConv::AsyncConvertData(const char *aFromType, const char *aToType,
// in the raw stream.
mFinalListener = aListener;
nsCOMPtr<nsIPackagedAppService> pas(do_QueryInterface(aCtxt));
if (pas) {
if (NS_LITERAL_CSTRING(APPLICATION_PACKAGE).Equals(aFromType)) {
mPackagedApp = true;
}
return NS_OK;
@ -753,7 +752,12 @@ nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsICacheInfoChannel> cacheChan = do_QueryInterface(request);
if (cacheChan) {
cacheChan->IsFromCache(&mIsFromCache);
}
// ask the HTTP channel for the content-type and extract the boundary from it.
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel, &rv);
if (NS_SUCCEEDED(rv)) {
@ -773,7 +777,7 @@ nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
// Although it is compatible with multipart/* this format does not require
// the boundary to be included in the header, as it can be ascertained from
// the content of the file.
if (delimiter.Find("application/package") != kNotFound) {
if (delimiter.Find(NS_LITERAL_CSTRING(APPLICATION_PACKAGE)) != kNotFound) {
mPackagedApp = true;
mHasAppContentType = true;
mToken.Truncate();
@ -829,7 +833,14 @@ nsMultiMixedConv::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
// We should definitely have found a token at this point. Not having one
// is clearly an error, so we need to pass it to the listener.
if (mToken.IsEmpty()) {
// However, since packaged apps usually have the boundary token at the
// begining of the content, if the package is served from the cache, and
// only metadata was saved for said package (meaning no content is available
// and `mFirstOnData` is true) then we wouldn't have a boundary even though
// no error has occured.
if (mToken.IsEmpty() &&
NS_SUCCEEDED(rv) && // don't hide channel error results
!(mPackagedApp && mIsFromCache && mFirstOnData)) {
aStatus = NS_ERROR_FAILURE;
rv = NS_ERROR_FAILURE;
}
@ -858,6 +869,12 @@ nsMultiMixedConv::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
// OnStartRequest! - This breaks necko's semantecs.
//(void) mFinalListener->OnStartRequest(request, ctxt);
(void) mFinalListener->OnStopRequest(request, ctxt, aStatus);
} else if (mIsFromCache && mFirstOnData) {
// `mFirstOnData` is true if the package's cache entry only holds
// metadata and no calls to OnDataAvailable are made.
// In this case we would not call OnStopRequest for any of the parts,
// so we need to call it here.
(void) mFinalListener->OnStopRequest(request, ctxt, aStatus);
}
@ -881,6 +898,7 @@ nsMultiMixedConv::nsMultiMixedConv() :
mIsByteRangeRequest = false;
mPackagedApp = false;
mHasAppContentType = false;
mIsFromCache = false;
}
nsMultiMixedConv::~nsMultiMixedConv() {
@ -1061,7 +1079,7 @@ nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr,
// It may already be initialized, from a previous call of ParseHeaders
// since the headers for a single part may come in more then one chunk
if (mPackagedApp && !mResponseHead) {
mResponseHead = new nsHttpResponseHead();
mResponseHead = new mozilla::net::nsHttpResponseHead();
}
mContentLength = UINT64_MAX; // XXX what if we were already called?

View File

@ -17,8 +17,6 @@
#include "nsIResponseHeadProvider.h"
#include "nsHttpResponseHead.h"
using mozilla::net::nsHttpResponseHead;
#define NS_MULTIMIXEDCONVERTER_CID \
{ /* 7584CE90-5B25-11d3-A175-0050041CAF44 */ \
0x7584ce90, \
@ -52,7 +50,7 @@ public:
/* SetContentDisposition expects the full value of the Content-Disposition
* header */
void SetContentDisposition(const nsACString& aContentDispositionHeader);
void SetResponseHead(nsHttpResponseHead * head) { mResponseHead = head; }
void SetResponseHead(mozilla::net::nsHttpResponseHead * head) { mResponseHead = head; }
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUEST
@ -67,7 +65,7 @@ protected:
protected:
nsCOMPtr<nsIChannel> mMultipartChannel;
nsCOMPtr<nsIStreamListener> mListener;
nsAutoPtr<nsHttpResponseHead> mResponseHead;
nsAutoPtr<mozilla::net::nsHttpResponseHead> mResponseHead;
nsresult mStatus;
nsLoadFlags mLoadFlags;
@ -184,7 +182,11 @@ protected:
// Streamable packages don't require the boundary in the header
// as it can be ascertained from the package file.
bool mPackagedApp;
nsAutoPtr<nsHttpResponseHead> mResponseHead;
nsAutoPtr<mozilla::net::nsHttpResponseHead> mResponseHead;
// It is necessary to know if the content is coming from the cache
// for packaged apps, in the case that only metadata is saved in the cache
// entry and OnDataAvailable never gets called.
bool mIsFromCache;
};
#endif /* __nsmultimixedconv__h__ */

View File

@ -24,8 +24,6 @@ Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/Services.jsm");
var httpserver = null;
var gPAS = Cc["@mozilla.org/network/packaged-app-service;1"]
.getService(Ci.nsIPackagedAppService);
XPCOMUtils.defineLazyGetter(this, "uri", function() {
return "http://localhost:" + httpserver.identity.primaryPort;
@ -178,10 +176,10 @@ headerListener.prototype.visitHeader = function(header, value) {
function test_multipart() {
var streamConv = Cc["@mozilla.org/streamConverters;1"]
.getService(Ci.nsIStreamConverterService);
var conv = streamConv.asyncConvertData("multipart/mixed",
var conv = streamConv.asyncConvertData("application/package",
"*/*",
new multipartListener(testData),
gPAS);
null);
var chan = make_channel(uri + "/multipart");
chan.asyncOpen(conv, null);
@ -190,10 +188,10 @@ function test_multipart() {
function test_multipart_with_boundary() {
var streamConv = Cc["@mozilla.org/streamConverters;1"]
.getService(Ci.nsIStreamConverterService);
var conv = streamConv.asyncConvertData("multipart/mixed",
var conv = streamConv.asyncConvertData("application/package",
"*/*",
new multipartListener(testData),
gPAS);
null);
var chan = make_channel(uri + "/multipart2");
chan.asyncOpen(conv, null);
@ -202,10 +200,10 @@ function test_multipart_with_boundary() {
function test_multipart_chunked_headers() {
var streamConv = Cc["@mozilla.org/streamConverters;1"]
.getService(Ci.nsIStreamConverterService);
var conv = streamConv.asyncConvertData("multipart/mixed",
var conv = streamConv.asyncConvertData("application/package",
"*/*",
new multipartListener(testData),
gPAS);
null);
var chan = make_channel(uri + "/multipart3");
chan.asyncOpen(conv, null);
@ -215,12 +213,10 @@ function test_multipart_content_type_other() {
var streamConv = Cc["@mozilla.org/streamConverters;1"]
.getService(Ci.nsIStreamConverterService);
// mime types other that multipart/mixed and application/package are only
// allowed if an nsIPackagedAppService is passed as context
var conv = streamConv.asyncConvertData("multipart/mixed",
var conv = streamConv.asyncConvertData("application/package",
"*/*",
new multipartListener(testData, true),
gPAS);
null);
var chan = make_channel(uri + "/multipart4");
chan.asyncOpen(conv, null);