diff --git a/netwerk/build/nsNetModule.cpp b/netwerk/build/nsNetModule.cpp index 3a5881f7c005..d5aa6d99d648 100644 --- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -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 }, diff --git a/netwerk/mime/nsMimeTypes.h b/netwerk/mime/nsMimeTypes.h index bc1501c3484b..21ff19a50ec7 100644 --- a/netwerk/mime/nsMimeTypes.h +++ b/netwerk/mime/nsMimeTypes.h @@ -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" diff --git a/netwerk/protocol/http/PackagedAppService.cpp b/netwerk/protocol/http/PackagedAppService.cpp index f0662dbe0a9a..8f7e457739b6 100644 --- a/netwerk/protocol/http/PackagedAppService.cpp +++ b/netwerk/protocol/http/PackagedAppService.cpp @@ -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) { diff --git a/netwerk/streamconv/converters/nsMultiMixedConv.cpp b/netwerk/streamconv/converters/nsMultiMixedConv.cpp index e84e0bc2fc29..6b1763d07643 100644 --- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp +++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp @@ -13,7 +13,7 @@ #include "nsIHttpChannelInternal.h" #include "nsURLHelper.h" #include "nsIStreamConverterService.h" -#include "nsIPackagedAppService.h" +#include "nsICacheInfoChannel.h" #include #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 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 channel = do_QueryInterface(request, &rv); if (NS_FAILED(rv)) return rv; - + + nsCOMPtr cacheChan = do_QueryInterface(request); + if (cacheChan) { + cacheChan->IsFromCache(&mIsFromCache); + } + // ask the HTTP channel for the content-type and extract the boundary from it. nsCOMPtr 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? diff --git a/netwerk/streamconv/converters/nsMultiMixedConv.h b/netwerk/streamconv/converters/nsMultiMixedConv.h index dbc8c954d696..780201373070 100644 --- a/netwerk/streamconv/converters/nsMultiMixedConv.h +++ b/netwerk/streamconv/converters/nsMultiMixedConv.h @@ -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 mMultipartChannel; nsCOMPtr mListener; - nsAutoPtr mResponseHead; + nsAutoPtr 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 mResponseHead; + nsAutoPtr 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__ */ diff --git a/netwerk/test/unit/test_multipart_streamconv_application_package.js b/netwerk/test/unit/test_multipart_streamconv_application_package.js index 8f31854351b7..99570c822afd 100644 --- a/netwerk/test/unit/test_multipart_streamconv_application_package.js +++ b/netwerk/test/unit/test_multipart_streamconv_application_package.js @@ -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);