bug 792438 part 1 - identify css and head-based-js as dedicated network resources r=bz

This commit is contained in:
Patrick McManus 2012-12-04 18:06:29 -05:00
parent 3e1b783f24
commit aef0638b1c
13 changed files with 122 additions and 17 deletions

View File

@ -24,6 +24,7 @@
#include "nsIPrincipal.h"
#include "nsContentPolicyUtils.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIScriptElement.h"
#include "nsIDOMHTMLScriptElement.h"
#include "nsIDocShell.h"
@ -257,7 +258,8 @@ nsScriptLoader::ShouldLoadScript(nsIDocument* aDocument,
}
nsresult
nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType)
nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
bool aScriptFromHead)
{
nsISupports *context = aRequest->mElement.get()
? static_cast<nsISupports *>(aRequest->mElement.get())
@ -302,6 +304,15 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType)
channelPolicy);
NS_ENSURE_SUCCESS(rv, rv);
nsIScriptElement *script = aRequest->mElement;
if (aScriptFromHead &&
!(script && (script->GetScriptAsync() || script->GetScriptDeferred()))) {
nsCOMPtr<nsIHttpChannelInternal>
internalHttpChannel(do_QueryInterface(channel));
if (internalHttpChannel)
internalHttpChannel->SetLoadAsBlocking(true);
}
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
if (httpChannel) {
// HTTP content negotation has little value in this context.
@ -518,7 +529,10 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
request->mURI = scriptURI;
request->mIsInline = false;
request->mLoading = true;
rv = StartLoad(request, type);
// set aScriptFromHead to false so we don't treat non preloaded scripts as
// blockers for full page load. See bug 792438.
rv = StartLoad(request, type, false);
if (NS_FAILED(rv)) {
// Asynchronously report the load failure
NS_DispatchToCurrentThread(
@ -1236,7 +1250,8 @@ nsScriptLoader::ParsingComplete(bool aTerminated)
void
nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
const nsAString &aType,
const nsAString &aCrossOrigin)
const nsAString &aCrossOrigin,
bool aScriptFromHead)
{
// Check to see if scripts has been turned off.
if (!mEnabled || !mDocument->IsScriptEnabled()) {
@ -1249,7 +1264,7 @@ nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
request->mURI = aURI;
request->mIsInline = false;
request->mLoading = true;
nsresult rv = StartLoad(request, aType);
nsresult rv = StartLoad(request, aType, aScriptFromHead);
if (NS_FAILED(rv)) {
return;
}

View File

@ -200,10 +200,12 @@ public:
* @param aType The type parameter for the script.
* @param aCrossOrigin The crossorigin attribute for the script.
* Void if not present.
* @param aScriptFromHead Whether or not the script was a child of head
*/
virtual void PreloadURI(nsIURI *aURI, const nsAString &aCharset,
const nsAString &aType,
const nsAString &aCrossOrigin);
const nsAString &aCrossOrigin,
bool aScriptFromHead);
private:
/**
@ -228,7 +230,8 @@ private:
/**
* Start a load for aRequest's URI.
*/
nsresult StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType);
nsresult StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
bool aScriptFromHead);
/**
* Process any pending requests asynchronously (i.e. off an event) if there

View File

@ -78,6 +78,7 @@
#include "mozilla/Attributes.h"
#include "nsIPermissionManager.h"
#include "nsMimeTypes.h"
#include "nsIHttpChannelInternal.h"
#include "nsWrapperCacheInlines.h"
#include "nsStreamListenerWrapper.h"
@ -2964,6 +2965,15 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
// the channel slow by default for pipeline purposes
AddLoadFlags(mChannel, nsIRequest::INHIBIT_PIPELINE);
nsCOMPtr<nsIHttpChannelInternal>
internalHttpChannel(do_QueryInterface(mChannel));
if (internalHttpChannel) {
// we never let XHR be blocked by head CSS/JS loads to avoid
// potential deadlock where server generation of CSS/JS requires
// an XHR signal.
internalHttpChannel->SetLoadUnblocked(true);
}
if (!IsSystemXHR()) {
// Always create a nsCORSListenerProxy here even if it's
// a same-origin request right now, since it could be redirected.

View File

@ -39,6 +39,7 @@
#include "nsIScriptSecurityManager.h"
#include "nsContentPolicyUtils.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIScriptError.h"
#include "nsMimeTypes.h"
#include "nsIAtom.h"
@ -1502,6 +1503,11 @@ Loader::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
return rv;
}
nsCOMPtr<nsIHttpChannelInternal>
internalHttpChannel(do_QueryInterface(channel));
if (internalHttpChannel)
internalHttpChannel->SetLoadAsBlocking(!aLoadData->mWasAlternate);
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
if (httpChannel) {
// send a minimal Accept header for text/css

View File

@ -49,6 +49,8 @@ HttpBaseChannel::HttpBaseChannel()
, mTracingEnabled(true)
, mTimingEnabled(false)
, mAllowSpdy(true)
, mLoadAsBlocking(false)
, mLoadUnblocked(false)
, mSuspendCount(0)
, mProxyResolveFlags(0)
, mContentDispositionHint(UINT32_MAX)
@ -1354,6 +1356,36 @@ HttpBaseChannel::SetAllowSpdy(bool aAllowSpdy)
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetLoadAsBlocking(bool *aLoadAsBlocking)
{
NS_ENSURE_ARG_POINTER(aLoadAsBlocking);
*aLoadAsBlocking = mLoadAsBlocking;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetLoadAsBlocking(bool aLoadAsBlocking)
{
mLoadAsBlocking = aLoadAsBlocking;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetLoadUnblocked(bool *aLoadUnblocked)
{
NS_ENSURE_ARG_POINTER(aLoadUnblocked);
*aLoadUnblocked = mLoadUnblocked;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetLoadUnblocked(bool aLoadUnblocked)
{
mLoadUnblocked = aLoadUnblocked;
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpBaseChannel::nsISupportsPriority
//-----------------------------------------------------------------------------

View File

@ -142,6 +142,10 @@ public:
NS_IMETHOD GetRemotePort(int32_t* port);
NS_IMETHOD GetAllowSpdy(bool *aAllowSpdy);
NS_IMETHOD SetAllowSpdy(bool aAllowSpdy);
NS_IMETHOD GetLoadAsBlocking(bool *aLoadAsBlocking);
NS_IMETHOD SetLoadAsBlocking(bool aLoadAsBlocking);
NS_IMETHOD GetLoadUnblocked(bool *aLoadUnblocked);
NS_IMETHOD SetLoadUnblocked(bool aLoadUnblocked);
inline void CleanRedirectCacheChainIfNecessary()
{
@ -274,6 +278,8 @@ protected:
// True if timing collection is enabled
uint32_t mTimingEnabled : 1;
uint32_t mAllowSpdy : 1;
uint32_t mLoadAsBlocking : 1;
uint32_t mLoadUnblocked : 1;
// Current suspension depth for this channel object
uint32_t mSuspendCount;

View File

@ -34,7 +34,7 @@ interface nsIHttpUpgradeListener : nsISupports
* using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned.
*/
[scriptable, uuid(9363fd96-af59-47e8-bddf-1d5e91acd336)]
[scriptable, uuid(74d13d41-85cd-490f-9942-300d0c01c726)]
interface nsIHttpChannelInternal : nsISupports
{
/**
@ -156,4 +156,19 @@ interface nsIHttpChannelInternal : nsISupports
*/
attribute boolean allowSpdy;
/**
* Set (e.g., by the docshell) to indicate whether or not the channel
* corresponds to content that should be given a degree of network exclusivity
* with respect to other members of its load group.
* Examples are js from the HTML head and css which are latency
* sensitive and should not compete with images for bandwidth. Default false.
*/
attribute boolean loadAsBlocking;
/**
* If set, this channel will load in parallel with the rest of the load
* group even if a blocking subset of the group would normally be given
* exclusivity. Default false.
*/
attribute boolean loadUnblocked;
};

View File

@ -2079,6 +2079,12 @@ WebSocketChannel::SetupRequest()
nsIRequest::LOAD_BYPASS_CACHE);
NS_ENSURE_SUCCESS(rv, rv);
// we never let websockets be blocked by head CSS/JS loads to avoid
// potential deadlock where server generation of CSS/JS requires
// an XHR signal.
rv = mChannel->SetLoadUnblocked(true);
NS_ENSURE_SUCCESS(rv, rv);
// draft-ietf-hybi-thewebsocketprotocol-07 illustrates Upgrade: websocket
// in lower case, so go with that. It is technically case insensitive.
rv = mChannel->HTTPUpgrade(NS_LITERAL_CSTRING("websocket"), this);

View File

@ -32,7 +32,11 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor)
break;
case eSpeculativeLoadScript:
aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSource,
mCrossOrigin);
mCrossOrigin, false);
break;
case eSpeculativeLoadScriptFromHead:
aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSource,
mCrossOrigin, true);
break;
case eSpeculativeLoadStyle:
aExecutor->PreloadStyle(mUrl, mCharset, mCrossOrigin);

View File

@ -16,6 +16,7 @@ enum eHtml5SpeculativeLoad {
eSpeculativeLoadBase,
eSpeculativeLoadImage,
eSpeculativeLoadScript,
eSpeculativeLoadScriptFromHead,
eSpeculativeLoadStyle,
eSpeculativeLoadManifest,
eSpeculativeLoadSetDocumentCharset
@ -45,10 +46,12 @@ class nsHtml5SpeculativeLoad {
inline void InitScript(const nsAString& aUrl,
const nsAString& aCharset,
const nsAString& aType,
const nsAString& aCrossOrigin) {
const nsAString& aCrossOrigin,
bool aParserInHead) {
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
"Trying to reinitialize a speculative load!");
mOpCode = eSpeculativeLoadScript;
mOpCode = aParserInHead ?
eSpeculativeLoadScriptFromHead : eSpeculativeLoadScript;
mUrl.Assign(aUrl);
mCharset.Assign(aCharset);
mTypeOrCharsetSource.Assign(aType);
@ -108,7 +111,7 @@ class nsHtml5SpeculativeLoad {
eHtml5SpeculativeLoad mOpCode;
nsString mUrl;
/**
* If mOpCode is eSpeculativeLoadStyle or eSpeculativeLoadScript
* If mOpCode is eSpeculativeLoadStyle or eSpeculativeLoadScript[FromHead]
* then this is the value of the "charset" attribute. For
* eSpeculativeLoadSetDocumentCharset it is the charset that the
* document's charset is being set to. Otherwise it's empty.
@ -122,7 +125,7 @@ class nsHtml5SpeculativeLoad {
*/
nsString mTypeOrCharsetSource;
/**
* If mOpCode is eSpeculativeLoadImage or eSpeculativeLoadScript,
* If mOpCode is eSpeculativeLoadImage or eSpeculativeLoadScript[FromHead],
* this is the value of the "crossorigin" attribute. If the
* attribute is not set, this will be a void string.
*/

View File

@ -93,7 +93,8 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName, nsHtml5Htm
InitScript(*url,
(charset) ? *charset : EmptyString(),
(type) ? *type : EmptyString(),
(crossOrigin) ? *crossOrigin : NullString());
(crossOrigin) ? *crossOrigin : NullString(),
mode == NS_HTML5TREE_BUILDER_IN_HEAD);
mCurrentHtmlScriptIsAsyncOrDefer =
aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) ||
aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER);
@ -158,7 +159,8 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName, nsHtml5Htm
InitScript(*url,
EmptyString(),
(type) ? *type : EmptyString(),
(crossOrigin) ? *crossOrigin : NullString());
(crossOrigin) ? *crossOrigin : NullString(),
mode == NS_HTML5TREE_BUILDER_IN_HEAD);
}
} else if (nsHtml5Atoms::style == aName) {
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();

View File

@ -1043,13 +1043,15 @@ void
nsHtml5TreeOpExecutor::PreloadScript(const nsAString& aURL,
const nsAString& aCharset,
const nsAString& aType,
const nsAString& aCrossOrigin)
const nsAString& aCrossOrigin,
bool aScriptFromHead)
{
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
if (!uri) {
return;
}
mDocument->ScriptLoader()->PreloadURI(uri, aCharset, aType, aCrossOrigin);
mDocument->ScriptLoader()->PreloadURI(uri, aCharset, aType, aCrossOrigin,
aScriptFromHead);
}
void

View File

@ -393,7 +393,8 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
void PreloadScript(const nsAString& aURL,
const nsAString& aCharset,
const nsAString& aType,
const nsAString& aCrossOrigin);
const nsAString& aCrossOrigin,
bool aScriptFromHead);
void PreloadStyle(const nsAString& aURL, const nsAString& aCharset,
const nsAString& aCrossOrigin);