зеркало из https://github.com/mozilla/gecko-dev.git
Bug 543458 - Make the HTML5 tree op executor use nsContentSink code for deciding when to return to the event loop. r=bnewman.
--HG-- extra : rebase_source : fa41825753811279070d232c7d467600d7b2d523
This commit is contained in:
Родитель
385bd7f8b5
Коммит
ced01aa917
|
@ -1728,11 +1728,13 @@ nsContentSink::WillBuildModelImpl()
|
|||
void
|
||||
nsContentSink::ContinueInterruptedParsingIfEnabled()
|
||||
{
|
||||
// This shouldn't be called in the HTML5 case.
|
||||
if (mParser && mParser->IsParserEnabled()) {
|
||||
mParser->ContinueInterruptedParsing();
|
||||
}
|
||||
}
|
||||
|
||||
// Overridden in the HTML5 case
|
||||
void
|
||||
nsContentSink::ContinueInterruptedParsingAsync()
|
||||
{
|
||||
|
|
|
@ -300,7 +300,7 @@ private:
|
|||
|
||||
protected:
|
||||
|
||||
void ContinueInterruptedParsingAsync();
|
||||
virtual void ContinueInterruptedParsingAsync();
|
||||
void ContinueInterruptedParsingIfEnabled();
|
||||
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
|
|
|
@ -2880,18 +2880,10 @@ pref("html5.flushtimer.startdelay", 200);
|
|||
pref("html5.flushtimer.continuedelay", 150);
|
||||
// Time in milliseconds between timer firings once the timer has starting
|
||||
// firing.
|
||||
pref("html5.flushtimer.interval", 100);
|
||||
pref("html5.flushtimer.interval", 120);
|
||||
|
||||
// Push/Pop/Replace State prefs
|
||||
pref("browser.history.allowPushState", true);
|
||||
pref("browser.history.allowReplaceState", true);
|
||||
pref("browser.history.allowPopState", true);
|
||||
pref("browser.history.maxStateObjectSize", 655360);
|
||||
// Initial max length for number of tree ops in on flush.
|
||||
pref("html5.opqueue.initiallengthlimit", 200);
|
||||
// Maximum time in milliseconds to spend flushing the tree op queue when not forced to completion
|
||||
pref("html5.opqueue.maxtime", 100);
|
||||
// Minimun number of tree ops to flush regardless of time (takes precedence over the maxtime pref)
|
||||
pref("html5.opqueue.minlength", 100);
|
||||
// Maximum number of tree ops to flush regardless of time (takes precedence over the maxtime pref)
|
||||
pref("html5.opqueue.maxlength", 4500); // most top sites stay under this value
|
||||
|
|
|
@ -74,7 +74,7 @@ CPPSRCS = \
|
|||
nsHtml5TreeOpExecutor.cpp \
|
||||
nsHtml5StreamParser.cpp \
|
||||
nsHtml5Speculation.cpp \
|
||||
nsHtml5SpeculativeLoader.cpp \
|
||||
nsHtml5SpeculativeLoad.cpp \
|
||||
$(NULL)
|
||||
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include "nsAHtml5EncodingDeclarationHandler.h"
|
||||
|
||||
class nsHtml5StreamParser;
|
||||
class nsHtml5SpeculativeLoader;
|
||||
|
||||
class nsHtml5Tokenizer;
|
||||
class nsHtml5TreeBuilder;
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include "nsAHtml5EncodingDeclarationHandler.h"
|
||||
|
||||
class nsHtml5StreamParser;
|
||||
class nsHtml5SpeculativeLoader;
|
||||
|
||||
class nsHtml5Tokenizer;
|
||||
class nsHtml5TreeBuilder;
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
#include "nsAHtml5EncodingDeclarationHandler.h"
|
||||
|
||||
class nsHtml5StreamParser;
|
||||
class nsHtml5SpeculativeLoader;
|
||||
|
||||
class nsHtml5Tokenizer;
|
||||
class nsHtml5TreeBuilder;
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
#include "nsAHtml5EncodingDeclarationHandler.h"
|
||||
|
||||
class nsHtml5StreamParser;
|
||||
class nsHtml5SpeculativeLoader;
|
||||
|
||||
class nsHtml5Tokenizer;
|
||||
class nsHtml5TreeBuilder;
|
||||
|
|
|
@ -72,7 +72,6 @@ nsHtml5Module::InitializeStatics()
|
|||
nsHtml5TreeBuilder::initializeStatics();
|
||||
nsHtml5UTF16Buffer::initializeStatics();
|
||||
nsHtml5StreamParser::InitializeStatics();
|
||||
nsHtml5TreeOpExecutor::InitializeStatics();
|
||||
#ifdef DEBUG
|
||||
sNsHtml5ModuleInitialized = PR_TRUE;
|
||||
#endif
|
||||
|
|
|
@ -179,28 +179,8 @@ nsHtml5Parser::GetStreamListener(nsIStreamListener** aListener)
|
|||
NS_IMETHODIMP
|
||||
nsHtml5Parser::ContinueInterruptedParsing()
|
||||
{
|
||||
// If there are scripts executing, then the content sink is jumping the gun
|
||||
// (probably due to a synchronous XMLHttpRequest) and will re-enable us
|
||||
// later, see bug 460706.
|
||||
if (mExecutor->IsScriptExecuting()) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (mExecutor->IsFlushing()) {
|
||||
// A nested event loop dequeued the continue event and there aren't
|
||||
// scripts executing. What's currently causing the flush is running to
|
||||
// completion or there will be a script later and the script will cause
|
||||
// another continue event.
|
||||
return NS_OK;
|
||||
}
|
||||
// If the stream has already finished, there's a good chance
|
||||
// that we might start closing things down when the parser
|
||||
// is reenabled. To make sure that we're not deleted across
|
||||
// the reenabling process, hold a reference to ourselves.
|
||||
nsCOMPtr<nsIParser> kungFuDeathGrip(this);
|
||||
nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(mStreamParser);
|
||||
nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
|
||||
ParseUntilBlocked();
|
||||
return NS_OK;
|
||||
NS_NOTREACHED("Don't call. For interface compat only.");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
|
@ -360,7 +340,6 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
|
|||
}
|
||||
|
||||
if (!mBlocked) {
|
||||
// mExecutor->WillResume();
|
||||
while (buffer->hasMore()) {
|
||||
buffer->adjust(mLastWasCR);
|
||||
mLastWasCR = PR_FALSE;
|
||||
|
@ -387,10 +366,9 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
|
|||
if (mTreeBuilder->HasScript()) {
|
||||
// No need to flush characters, because an end tag was tokenized last
|
||||
mTreeBuilder->Flush(); // Move ops to the executor
|
||||
mExecutor->Flush(PR_TRUE); // run the ops
|
||||
mExecutor->FlushDocumentWrite(); // run the ops
|
||||
}
|
||||
if (mBlocked) {
|
||||
// mExecutor->WillInterrupt();
|
||||
break;
|
||||
}
|
||||
// Ignore suspension requests
|
||||
|
@ -403,7 +381,7 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
|
|||
// Scripting semantics require a forced tree builder flush here
|
||||
mTreeBuilder->flushCharacters(); // Flush trailing characters
|
||||
mTreeBuilder->Flush(); // Move ops to the executor
|
||||
mExecutor->Flush(PR_TRUE); // run the ops
|
||||
mExecutor->FlushDocumentWrite(); // run the ops
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -498,7 +476,7 @@ nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
|
|||
mTokenizer->eof();
|
||||
mTreeBuilder->StreamEnded();
|
||||
mTreeBuilder->Flush();
|
||||
mExecutor->Flush(PR_TRUE);
|
||||
mExecutor->FlushDocumentWrite();
|
||||
mTokenizer->end();
|
||||
mExecutor->DropParserAndPerfHint();
|
||||
mAtomTable.Clear();
|
||||
|
@ -592,7 +570,6 @@ nsHtml5Parser::ParseUntilBlocked()
|
|||
}
|
||||
NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
|
||||
|
||||
mExecutor->WillResume();
|
||||
for (;;) {
|
||||
if (!mFirstBuffer->hasMore()) {
|
||||
if (mFirstBuffer == mLastBuffer) {
|
||||
|
@ -606,7 +583,7 @@ nsHtml5Parser::ParseUntilBlocked()
|
|||
mTokenizer->eof();
|
||||
mTreeBuilder->StreamEnded();
|
||||
mTreeBuilder->Flush();
|
||||
mExecutor->Flush(PR_TRUE);
|
||||
mExecutor->FlushDocumentWrite();
|
||||
mTokenizer->end();
|
||||
return;
|
||||
} else {
|
||||
|
@ -651,10 +628,9 @@ nsHtml5Parser::ParseUntilBlocked()
|
|||
}
|
||||
if (mTreeBuilder->HasScript()) {
|
||||
mTreeBuilder->Flush();
|
||||
mExecutor->Flush(PR_TRUE);
|
||||
mExecutor->FlushDocumentWrite();
|
||||
}
|
||||
if (mBlocked) {
|
||||
// mExecutor->WillInterrupt();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,10 +143,9 @@ class nsHtml5Parser : public nsIParser,
|
|||
NS_IMETHOD GetStreamListener(nsIStreamListener** aListener);
|
||||
|
||||
/**
|
||||
* If scripts are not executing, maybe flushes tree builder and parses
|
||||
* until suspension.
|
||||
* Don't call. For interface compat only.
|
||||
*/
|
||||
NS_IMETHOD ContinueInterruptedParsing();
|
||||
NS_IMETHOD ContinueInterruptedParsing();
|
||||
|
||||
/**
|
||||
* Blocks the parser.
|
||||
|
@ -159,7 +158,7 @@ class nsHtml5Parser : public nsIParser,
|
|||
NS_IMETHOD_(void) UnblockParser();
|
||||
|
||||
/**
|
||||
* Query whether the parser is enabled or not.
|
||||
* Query whether the parser is enabled (i.e. not blocked) or not.
|
||||
*/
|
||||
NS_IMETHOD_(PRBool) IsParserEnabled();
|
||||
|
||||
|
@ -312,19 +311,17 @@ class nsHtml5Parser : public nsIParser,
|
|||
|
||||
void ContinueAfterFailedCharsetSwitch();
|
||||
|
||||
#ifdef DEBUG
|
||||
PRBool HasStreamParser() {
|
||||
return !!mStreamParser;
|
||||
nsHtml5StreamParser* GetStreamParser() {
|
||||
return mStreamParser;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Parse until pending data is exhausted or a script blocks the parser
|
||||
*/
|
||||
void ParseUntilBlocked();
|
||||
|
||||
private:
|
||||
|
||||
// State variables
|
||||
|
||||
/**
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include "nsAHtml5EncodingDeclarationHandler.h"
|
||||
|
||||
class nsHtml5StreamParser;
|
||||
class nsHtml5SpeculativeLoader;
|
||||
|
||||
class nsHtml5Tokenizer;
|
||||
class nsHtml5TreeBuilder;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
|
@ -35,54 +35,42 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsHtml5SpeculativeLoader_h__
|
||||
#define nsHtml5SpeculativeLoader_h__
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsHtml5SpeculativeLoad.h"
|
||||
#include "nsHtml5TreeOpExecutor.h"
|
||||
#include "nsHashSets.h"
|
||||
|
||||
class nsHtml5SpeculativeLoader
|
||||
nsHtml5SpeculativeLoad::nsHtml5SpeculativeLoad()
|
||||
#ifdef DEBUG
|
||||
: mOpCode(eSpeculativeLoadUninitialized)
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
nsHtml5SpeculativeLoader(nsHtml5TreeOpExecutor* aExecutor);
|
||||
~nsHtml5SpeculativeLoader();
|
||||
MOZ_COUNT_CTOR(nsHtml5SpeculativeLoad);
|
||||
}
|
||||
|
||||
NS_IMETHOD_(nsrefcnt) AddRef(void);
|
||||
NS_IMETHOD_(nsrefcnt) Release(void);
|
||||
|
||||
void PreloadScript(const nsAString& aURL,
|
||||
const nsAString& aCharset,
|
||||
const nsAString& aType);
|
||||
|
||||
void PreloadStyle(const nsAString& aURL, const nsAString& aCharset);
|
||||
|
||||
void PreloadImage(const nsAString& aURL);
|
||||
|
||||
void ProcessManifest(const nsAString& aURL);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Get a nsIURI for an nsString if the URL hasn't been preloaded yet.
|
||||
*/
|
||||
already_AddRefed<nsIURI> ConvertIfNotPreloadedYet(const nsAString& aURL);
|
||||
|
||||
nsAutoRefCnt mRefCnt;
|
||||
|
||||
/**
|
||||
* The executor to use as the context for preloading.
|
||||
*/
|
||||
nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
|
||||
|
||||
/**
|
||||
* URLs already preloaded/preloading.
|
||||
*/
|
||||
nsCStringHashSet mPreloadedURLs;
|
||||
};
|
||||
|
||||
#endif // nsHtml5SpeculativeLoader_h__
|
||||
nsHtml5SpeculativeLoad::~nsHtml5SpeculativeLoad()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsHtml5SpeculativeLoad);
|
||||
NS_ASSERTION(mOpCode != eSpeculativeLoadUninitialized,
|
||||
"Uninitialized speculative load.");
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor)
|
||||
{
|
||||
switch (mOpCode) {
|
||||
case eSpeculativeLoadImage:
|
||||
aExecutor->PreloadImage(mUrl);
|
||||
break;
|
||||
case eSpeculativeLoadScript:
|
||||
aExecutor->PreloadScript(mUrl, mCharset, mType);
|
||||
break;
|
||||
case eSpeculativeLoadStyle:
|
||||
aExecutor->PreloadStyle(mUrl, mCharset);
|
||||
break;
|
||||
case eSpeculativeLoadManifest:
|
||||
aExecutor->ProcessOfflineManifest(mUrl);
|
||||
break;
|
||||
default:
|
||||
NS_NOTREACHED("Bogus speculative load.");
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is HTML Parser C++ Translator code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Henri Sivonen <hsivonen@iki.fi>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsHtml5SpeculativeLoad_h_
|
||||
#define nsHtml5SpeculativeLoad_h_
|
||||
|
||||
#include "nsString.h"
|
||||
|
||||
class nsHtml5TreeOpExecutor;
|
||||
|
||||
enum eHtml5SpeculativeLoad {
|
||||
#ifdef DEBUG
|
||||
eSpeculativeLoadUninitialized,
|
||||
#endif
|
||||
eSpeculativeLoadImage,
|
||||
eSpeculativeLoadScript,
|
||||
eSpeculativeLoadStyle,
|
||||
eSpeculativeLoadManifest
|
||||
};
|
||||
|
||||
class nsHtml5SpeculativeLoad {
|
||||
public:
|
||||
nsHtml5SpeculativeLoad();
|
||||
~nsHtml5SpeculativeLoad();
|
||||
|
||||
inline void InitImage(const nsAString& aUrl) {
|
||||
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
|
||||
"Trying to reinitialize a speculative load!");
|
||||
mOpCode = eSpeculativeLoadImage;
|
||||
mUrl.Assign(aUrl);
|
||||
}
|
||||
|
||||
inline void InitScript(const nsAString& aUrl,
|
||||
const nsAString& aCharset,
|
||||
const nsAString& aType) {
|
||||
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
|
||||
"Trying to reinitialize a speculative load!");
|
||||
mOpCode = eSpeculativeLoadScript;
|
||||
mUrl.Assign(aUrl);
|
||||
mCharset.Assign(aCharset);
|
||||
mType.Assign(aType);
|
||||
}
|
||||
|
||||
inline void InitStyle(const nsAString& aUrl, const nsAString& aCharset) {
|
||||
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
|
||||
"Trying to reinitialize a speculative load!");
|
||||
mOpCode = eSpeculativeLoadStyle;
|
||||
mUrl.Assign(aUrl);
|
||||
mCharset.Assign(aCharset);
|
||||
}
|
||||
|
||||
/**
|
||||
* "Speculative" manifest loads aren't truly speculative--if a manifest
|
||||
* gets loaded, we are committed to it. There can never be a <script>
|
||||
* before the manifest, so the situation of having to undo a manifest due
|
||||
* to document.write() never arises. The reason why a parser
|
||||
* thread-discovered manifest gets loaded via the speculative load queue
|
||||
* as opposed to tree operation queue is that the manifest must get
|
||||
* processed before any actually speculative loads such as scripts. Thus,
|
||||
* manifests seen by the parser thread have to maintain the queue order
|
||||
* relative to true speculative loads. See bug 541079.
|
||||
*/
|
||||
inline void InitManifest(const nsAString& aUrl) {
|
||||
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
|
||||
"Trying to reinitialize a speculative load!");
|
||||
mOpCode = eSpeculativeLoadManifest;
|
||||
mUrl.Assign(aUrl);
|
||||
}
|
||||
|
||||
void Perform(nsHtml5TreeOpExecutor* aExecutor);
|
||||
|
||||
private:
|
||||
eHtml5SpeculativeLoad mOpCode;
|
||||
nsString mUrl;
|
||||
nsString mCharset;
|
||||
nsString mType;
|
||||
};
|
||||
|
||||
#endif // nsHtml5SpeculativeLoad_h_
|
|
@ -1,135 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=2 et tw=79: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Pierre Phaneuf <pp@ludusdesign.com>
|
||||
* Henri Sivonen <hsivonen@iki.fi>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsHtml5SpeculativeLoader.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsScriptLoader.h"
|
||||
#include "nsIDocument.h"
|
||||
|
||||
nsHtml5SpeculativeLoader::
|
||||
nsHtml5SpeculativeLoader(nsHtml5TreeOpExecutor* aExecutor)
|
||||
: mExecutor(aExecutor)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsHtml5SpeculativeLoader);
|
||||
mPreloadedURLs.Init(23); // Mean # of preloadable resources per page on dmoz
|
||||
}
|
||||
|
||||
nsHtml5SpeculativeLoader::~nsHtml5SpeculativeLoader()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsHtml5SpeculativeLoader);
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(nsHtml5SpeculativeLoader)
|
||||
|
||||
NS_IMPL_THREADSAFE_RELEASE(nsHtml5SpeculativeLoader)
|
||||
|
||||
already_AddRefed<nsIURI>
|
||||
nsHtml5SpeculativeLoader::ConvertIfNotPreloadedYet(const nsAString& aURL)
|
||||
{
|
||||
nsIDocument* doc = mExecutor->GetDocument();
|
||||
if (!doc) {
|
||||
return nsnull;
|
||||
}
|
||||
nsIURI* base = doc->GetBaseURI();
|
||||
const nsCString& charset = doc->GetDocumentCharacterSet();
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, charset.get(), base);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to create a URI");
|
||||
return nsnull;
|
||||
}
|
||||
nsCAutoString spec;
|
||||
uri->GetSpec(spec);
|
||||
if (mPreloadedURLs.Contains(spec)) {
|
||||
return nsnull;
|
||||
}
|
||||
mPreloadedURLs.Put(spec);
|
||||
nsIURI* retURI = uri;
|
||||
NS_ADDREF(retURI);
|
||||
return retURI;
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5SpeculativeLoader::PreloadScript(const nsAString& aURL,
|
||||
const nsAString& aCharset,
|
||||
const nsAString& aType)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
nsIDocument* doc = mExecutor->GetDocument();
|
||||
if (doc) {
|
||||
doc->ScriptLoader()->PreloadURI(uri, aCharset, aType);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5SpeculativeLoader::PreloadStyle(const nsAString& aURL,
|
||||
const nsAString& aCharset)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
nsIDocument* doc = mExecutor->GetDocument();
|
||||
if (doc) {
|
||||
doc->PreloadStyle(uri, aCharset);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5SpeculativeLoader::PreloadImage(const nsAString& aURL)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
nsIDocument* doc = mExecutor->GetDocument();
|
||||
if (doc) {
|
||||
doc->MaybePreLoadImage(uri);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5SpeculativeLoader::ProcessManifest(const nsAString& aURL)
|
||||
{
|
||||
mExecutor->ProcessOfflineManifest(aURL);
|
||||
}
|
|
@ -48,7 +48,6 @@
|
|||
#include "nsAHtml5EncodingDeclarationHandler.h"
|
||||
|
||||
class nsHtml5StreamParser;
|
||||
class nsHtml5SpeculativeLoader;
|
||||
|
||||
class nsHtml5Tokenizer;
|
||||
class nsHtml5TreeBuilder;
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include "nsAHtml5EncodingDeclarationHandler.h"
|
||||
|
||||
class nsHtml5StreamParser;
|
||||
class nsHtml5SpeculativeLoader;
|
||||
|
||||
class nsHtml5Tokenizer;
|
||||
class nsHtml5TreeBuilder;
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
#include "nsHtml5AtomTable.h"
|
||||
#include "nsHtml5Module.h"
|
||||
#include "nsHtml5RefPtr.h"
|
||||
#include "nsHtml5SpeculativeLoader.h"
|
||||
|
||||
static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID);
|
||||
|
||||
|
@ -92,9 +91,9 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5StreamParser)
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRequest)
|
||||
tmp->mOwner = nsnull;
|
||||
tmp->mExecutorFlusher = nsnull;
|
||||
tmp->mLoadFlusher = nsnull;
|
||||
tmp->mExecutor = nsnull;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChardet)
|
||||
tmp->mTreeBuilder->DropSpeculativeLoader();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5StreamParser)
|
||||
|
@ -109,18 +108,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5StreamParser)
|
|||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExecutorFlusher->mExecutor");
|
||||
cb.NoteXPCOMChild(static_cast<nsIContentSink*> (tmp->mExecutor));
|
||||
}
|
||||
// hack: count the strongly owned edge wrapped in the runnable
|
||||
if (tmp->mLoadFlusher) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mLoadFlusher->mExecutor");
|
||||
cb.NoteXPCOMChild(static_cast<nsIContentSink*> (tmp->mExecutor));
|
||||
}
|
||||
// hack: count self if held by mChardet
|
||||
if (tmp->mChardet) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mChardet->mObserver");
|
||||
cb.NoteXPCOMChild(static_cast<nsIStreamListener*>(tmp));
|
||||
}
|
||||
// hack: count the strongly owned edge wrapped in the speculative loader
|
||||
if (tmp->mTreeBuilder->HasSpeculativeLoader()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mTreeBuilder->mSpeculativeLoader->mExecutor");
|
||||
cb.NoteXPCOMChild(static_cast<nsIContentSink*> (tmp->mExecutor));
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
class nsHtml5ExecutorFlusher : public nsRunnable
|
||||
|
@ -133,7 +131,22 @@ class nsHtml5ExecutorFlusher : public nsRunnable
|
|||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mExecutor->Flush(PR_FALSE);
|
||||
mExecutor->RunFlushLoop();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class nsHtml5LoadFlusher : public nsRunnable
|
||||
{
|
||||
private:
|
||||
nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
|
||||
public:
|
||||
nsHtml5LoadFlusher(nsHtml5TreeOpExecutor* aExecutor)
|
||||
: mExecutor(aExecutor)
|
||||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mExecutor->FlushSpeculativeLoads();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
@ -143,8 +156,8 @@ nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
|
|||
: mFirstBuffer(new nsHtml5UTF16Buffer(NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE))
|
||||
, mLastBuffer(mFirstBuffer)
|
||||
, mExecutor(aExecutor)
|
||||
, mTreeBuilder(new nsHtml5TreeBuilder(mExecutor->GetStage(),
|
||||
new nsHtml5SpeculativeLoader(mExecutor)))
|
||||
, mTreeBuilder(new nsHtml5TreeBuilder(mExecutor->GetStage(),
|
||||
mExecutor->GetStage()))
|
||||
, mTokenizer(new nsHtml5Tokenizer(mTreeBuilder))
|
||||
, mTokenizerMutex("nsHtml5StreamParser mTokenizerMutex")
|
||||
, mOwner(aOwner)
|
||||
|
@ -152,6 +165,7 @@ nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
|
|||
, mTerminatedMutex("nsHtml5StreamParser mTerminatedMutex")
|
||||
, mThread(nsHtml5Module::GetStreamParserThread())
|
||||
, mExecutorFlusher(new nsHtml5ExecutorFlusher(aExecutor))
|
||||
, mLoadFlusher(new nsHtml5LoadFlusher(aExecutor))
|
||||
, mFlushTimer(do_CreateInstance("@mozilla.org/timer;1"))
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
@ -755,6 +769,13 @@ nsHtml5StreamParser::ParseAvailableData()
|
|||
mFirstBuffer->setStart(0);
|
||||
mFirstBuffer->setEnd(0);
|
||||
}
|
||||
mTreeBuilder->FlushLoads();
|
||||
// Dispatch this runnable unconditionally, because the loads
|
||||
// that need flushing may have been flushed earlier even if the
|
||||
// flush right above here did nothing.
|
||||
if (NS_FAILED(NS_DispatchToMainThread(mLoadFlusher))) {
|
||||
NS_WARNING("failed to dispatch load flush event");
|
||||
}
|
||||
return; // no more data for now but expecting more
|
||||
case STREAM_ENDED:
|
||||
if (mAtEOF) {
|
||||
|
@ -859,9 +880,12 @@ nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
|
|||
// the first speculation isn't the current speculation, so there's
|
||||
// no need to bother the parser thread.
|
||||
speculation->FlushToSink(mExecutor);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
|
||||
NS_WARNING("failed to dispatch executor flush event");
|
||||
}
|
||||
NS_ASSERTION(!mExecutor->IsScriptExecuting(),
|
||||
"ParseUntilBlocked() was supposed to ensure we don't come "
|
||||
"here when scripts are executing.");
|
||||
NS_ASSERTION(mExecutor->IsInFlushLoop(), "How are we here if "
|
||||
"RunFlushLoop() didn't call ParseUntilBlocked() which is the "
|
||||
"only caller of this method?");
|
||||
mSpeculations.RemoveElementAt(0);
|
||||
return;
|
||||
}
|
||||
|
@ -920,9 +944,12 @@ nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
|
|||
// We've got a successful speculation and at least a moment ago it was
|
||||
// the current speculation
|
||||
mSpeculations.ElementAt(0)->FlushToSink(mExecutor);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
|
||||
NS_WARNING("failed to dispatch executor flush event");
|
||||
}
|
||||
NS_ASSERTION(!mExecutor->IsScriptExecuting(),
|
||||
"ParseUntilBlocked() was supposed to ensure we don't come "
|
||||
"here when scripts are executing.");
|
||||
NS_ASSERTION(mExecutor->IsInFlushLoop(), "How are we here if "
|
||||
"RunFlushLoop() didn't call ParseUntilBlocked() which is the "
|
||||
"only caller of this method?");
|
||||
mSpeculations.RemoveElementAt(0);
|
||||
if (mSpeculations.IsEmpty()) {
|
||||
// yes, it was still the only speculation. Now stop speculating
|
||||
|
|
|
@ -453,6 +453,8 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
|
||||
nsCOMPtr<nsIRunnable> mExecutorFlusher;
|
||||
|
||||
nsCOMPtr<nsIRunnable> mLoadFlusher;
|
||||
|
||||
/**
|
||||
* The chardet instance if chardet is enabled.
|
||||
*/
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
#include "nsAHtml5EncodingDeclarationHandler.h"
|
||||
|
||||
class nsHtml5StreamParser;
|
||||
class nsHtml5SpeculativeLoader;
|
||||
|
||||
class nsHtml5TreeBuilder;
|
||||
class nsHtml5MetaScanner;
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
#include "nsAHtml5TreeBuilderState.h"
|
||||
|
||||
class nsHtml5StreamParser;
|
||||
class nsHtml5SpeculativeLoader;
|
||||
|
||||
class nsHtml5Tokenizer;
|
||||
class nsHtml5MetaScanner;
|
||||
|
|
|
@ -46,12 +46,11 @@
|
|||
#include "nsEventDispatcher.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsNodeUtils.h"
|
||||
#include "nsHtml5SpeculativeLoader.h"
|
||||
|
||||
// this really should be autogenerated...
|
||||
jArray<PRUnichar,PRInt32> nsHtml5TreeBuilder::ISINDEX_PROMPT = jArray<PRUnichar,PRInt32>();
|
||||
nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
|
||||
nsHtml5SpeculativeLoader* aSpeculativeLoader)
|
||||
nsHtml5TreeOpStage* aStage)
|
||||
: scriptingEnabled(PR_FALSE)
|
||||
, fragment(PR_FALSE)
|
||||
, contextNode(nsnull)
|
||||
|
@ -60,7 +59,7 @@ nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
|
|||
, mOpSink(aOpSink)
|
||||
, mHandles(new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH])
|
||||
, mHandlesUsed(0)
|
||||
, mSpeculativeLoader(aSpeculativeLoader)
|
||||
, mSpeculativeLoadStage(aStage)
|
||||
, mCurrentHtmlScriptIsAsyncOrDefer(PR_FALSE)
|
||||
#ifdef DEBUG
|
||||
, mActive(PR_FALSE)
|
||||
|
@ -76,87 +75,6 @@ nsHtml5TreeBuilder::~nsHtml5TreeBuilder()
|
|||
mOpQueue.Clear();
|
||||
}
|
||||
|
||||
class nsHtml5SpeculativeScript : public nsRunnable
|
||||
{
|
||||
private:
|
||||
nsRefPtr<nsHtml5SpeculativeLoader> mSpeculativeLoader;
|
||||
nsString mURL;
|
||||
nsString mCharset;
|
||||
nsString mType;
|
||||
public:
|
||||
nsHtml5SpeculativeScript(nsHtml5SpeculativeLoader* aSpeculativeLoader,
|
||||
const nsAString& aURL,
|
||||
const nsAString& aCharset,
|
||||
const nsAString& aType)
|
||||
: mSpeculativeLoader(aSpeculativeLoader)
|
||||
, mURL(aURL)
|
||||
, mCharset(aCharset)
|
||||
, mType(aType)
|
||||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mSpeculativeLoader->PreloadScript(mURL, mCharset, mType);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class nsHtml5SpeculativeStyle : public nsRunnable
|
||||
{
|
||||
private:
|
||||
nsRefPtr<nsHtml5SpeculativeLoader> mSpeculativeLoader;
|
||||
nsString mURL;
|
||||
nsString mCharset;
|
||||
public:
|
||||
nsHtml5SpeculativeStyle(nsHtml5SpeculativeLoader* aSpeculativeLoader,
|
||||
const nsAString& aURL,
|
||||
const nsAString& aCharset)
|
||||
: mSpeculativeLoader(aSpeculativeLoader)
|
||||
, mURL(aURL)
|
||||
, mCharset(aCharset)
|
||||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mSpeculativeLoader->PreloadStyle(mURL, mCharset);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class nsHtml5SpeculativeImage : public nsRunnable
|
||||
{
|
||||
private:
|
||||
nsRefPtr<nsHtml5SpeculativeLoader> mSpeculativeLoader;
|
||||
nsString mURL;
|
||||
public:
|
||||
nsHtml5SpeculativeImage(nsHtml5SpeculativeLoader* aSpeculativeLoader,
|
||||
const nsAString& aURL)
|
||||
: mSpeculativeLoader(aSpeculativeLoader)
|
||||
, mURL(aURL)
|
||||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mSpeculativeLoader->PreloadImage(mURL);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class nsHtml5SpeculativeManifest : public nsRunnable
|
||||
{
|
||||
private:
|
||||
nsRefPtr<nsHtml5SpeculativeLoader> mSpeculativeLoader;
|
||||
nsString mURL;
|
||||
public:
|
||||
nsHtml5SpeculativeManifest(nsHtml5SpeculativeLoader* aSpeculativeLoader,
|
||||
const nsAString& aURL)
|
||||
: mSpeculativeLoader(aSpeculativeLoader)
|
||||
, mURL(aURL)
|
||||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mSpeculativeLoader->ProcessManifest(mURL);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
nsIContent**
|
||||
nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes)
|
||||
{
|
||||
|
@ -174,13 +92,13 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
|
|||
|
||||
// Start wall of code for speculative loading and line numbers
|
||||
|
||||
if (mSpeculativeLoader) {
|
||||
if (mSpeculativeLoadStage) {
|
||||
switch (aNamespace) {
|
||||
case kNameSpaceID_XHTML:
|
||||
if (nsHtml5Atoms::img == aName) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC);
|
||||
if (url) {
|
||||
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url));
|
||||
mSpeculativeLoadQueue.AppendElement()->InitImage(*url);
|
||||
}
|
||||
} else if (nsHtml5Atoms::script == aName) {
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
|
@ -191,10 +109,9 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
|
|||
if (url) {
|
||||
nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
|
||||
nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
|
||||
Dispatch(new nsHtml5SpeculativeScript(mSpeculativeLoader,
|
||||
*url,
|
||||
(charset) ? *charset : EmptyString(),
|
||||
(type) ? *type : EmptyString()));
|
||||
mSpeculativeLoadQueue.AppendElement()->InitScript(*url,
|
||||
(charset) ? *charset : EmptyString(),
|
||||
(type) ? *type : EmptyString());
|
||||
mCurrentHtmlScriptIsAsyncOrDefer =
|
||||
aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) ||
|
||||
aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER);
|
||||
|
@ -207,15 +124,14 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
|
|||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
|
||||
if (url) {
|
||||
nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
|
||||
Dispatch(new nsHtml5SpeculativeStyle(mSpeculativeLoader,
|
||||
*url,
|
||||
(charset) ? *charset : EmptyString()));
|
||||
mSpeculativeLoadQueue.AppendElement()->InitStyle(*url,
|
||||
(charset) ? *charset : EmptyString());
|
||||
}
|
||||
}
|
||||
} else if (nsHtml5Atoms::video == aName) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_POSTER);
|
||||
if (url) {
|
||||
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url));
|
||||
mSpeculativeLoadQueue.AppendElement()->InitImage(*url);
|
||||
}
|
||||
} else if (nsHtml5Atoms::style == aName) {
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
|
@ -224,7 +140,7 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
|
|||
} else if (nsHtml5Atoms::html == aName) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST);
|
||||
if (url) {
|
||||
Dispatch(new nsHtml5SpeculativeManifest(mSpeculativeLoader, *url));
|
||||
mSpeculativeLoadQueue.AppendElement()->InitManifest(*url);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -232,7 +148,7 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
|
|||
if (nsHtml5Atoms::image == aName) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
|
||||
if (url) {
|
||||
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url));
|
||||
mSpeculativeLoadQueue.AppendElement()->InitImage(*url);
|
||||
}
|
||||
} else if (nsHtml5Atoms::script == aName) {
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
|
@ -242,10 +158,9 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
|
|||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
|
||||
if (url) {
|
||||
nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
|
||||
Dispatch(new nsHtml5SpeculativeScript(mSpeculativeLoader,
|
||||
*url,
|
||||
EmptyString(),
|
||||
(type) ? *type : EmptyString()));
|
||||
mSpeculativeLoadQueue.AppendElement()->InitScript(*url,
|
||||
EmptyString(),
|
||||
(type) ? *type : EmptyString());
|
||||
}
|
||||
} else if (nsHtml5Atoms::style == aName) {
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
|
@ -254,9 +169,7 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
|
|||
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
|
||||
if (url) {
|
||||
Dispatch(new nsHtml5SpeculativeStyle(mSpeculativeLoader,
|
||||
*url,
|
||||
EmptyString()));
|
||||
mSpeculativeLoadQueue.AppendElement()->InitStyle(*url, EmptyString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -636,6 +549,7 @@ nsHtml5TreeBuilder::HasScript()
|
|||
PRBool
|
||||
nsHtml5TreeBuilder::Flush()
|
||||
{
|
||||
FlushLoads();
|
||||
PRBool hasOps = !mOpQueue.IsEmpty();
|
||||
if (hasOps) {
|
||||
mOpSink->MoveOpsFrom(mOpQueue);
|
||||
|
@ -643,6 +557,14 @@ nsHtml5TreeBuilder::Flush()
|
|||
return hasOps;
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeBuilder::FlushLoads()
|
||||
{
|
||||
if (!mSpeculativeLoadQueue.IsEmpty()) {
|
||||
mSpeculativeLoadStage->MoveSpeculativeLoadsFrom(mSpeculativeLoadQueue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeBuilder::SetDocumentCharset(nsACString& aCharset,
|
||||
PRInt32 aCharsetSource)
|
||||
|
@ -683,11 +605,6 @@ nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, PRI
|
|||
mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot, aLine);
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeBuilder::DropSpeculativeLoader() {
|
||||
mSpeculativeLoader = nsnull;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHtml5TreeBuilder::IsDiscretionaryFlushSafe()
|
||||
{
|
||||
|
|
|
@ -40,11 +40,12 @@
|
|||
private:
|
||||
|
||||
nsTArray<nsHtml5TreeOperation> mOpQueue;
|
||||
nsTArray<nsHtml5SpeculativeLoad> mSpeculativeLoadQueue;
|
||||
nsAHtml5TreeOpSink* mOpSink;
|
||||
nsAutoArrayPtr<nsIContent*> mHandles;
|
||||
PRInt32 mHandlesUsed;
|
||||
nsTArray<nsAutoArrayPtr<nsIContent*> > mOldHandles;
|
||||
nsRefPtr<nsHtml5SpeculativeLoader> mSpeculativeLoader;
|
||||
nsHtml5TreeOpStage* mSpeculativeLoadStage;
|
||||
PRBool mCurrentHtmlScriptIsAsyncOrDefer;
|
||||
#ifdef DEBUG
|
||||
PRBool mActive;
|
||||
|
@ -60,8 +61,8 @@
|
|||
|
||||
public:
|
||||
|
||||
nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
|
||||
nsHtml5SpeculativeLoader* aSpeculativeLoader);
|
||||
nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
|
||||
nsHtml5TreeOpStage* aStage);
|
||||
|
||||
~nsHtml5TreeBuilder();
|
||||
|
||||
|
@ -77,14 +78,10 @@
|
|||
mOpQueue.Clear();
|
||||
}
|
||||
|
||||
PRBool HasSpeculativeLoader() {
|
||||
return !!mSpeculativeLoader;
|
||||
}
|
||||
|
||||
void DropSpeculativeLoader();
|
||||
|
||||
PRBool Flush();
|
||||
|
||||
void FlushLoads();
|
||||
|
||||
void SetDocumentCharset(nsACString& aCharset, PRInt32 aCharsetSource);
|
||||
|
||||
void StreamEnded();
|
||||
|
@ -92,9 +89,3 @@
|
|||
void NeedsCharsetSwitchTo(const nsACString& aEncoding);
|
||||
|
||||
void AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine);
|
||||
|
||||
inline void Dispatch(nsIRunnable* aEvent) {
|
||||
if (NS_FAILED(NS_DispatchToMainThread(aEvent))) {
|
||||
NS_WARNING("Failed to dispatch speculative load runnable.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,44 +77,25 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHtml5TreeOpExecutor, nsContent
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mOwnedElements)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueLengthLimit = 200;
|
||||
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueMaxTime = 100; // milliseconds
|
||||
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueMinLength = 100;
|
||||
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueMaxLength = 4500;
|
||||
|
||||
// static
|
||||
void
|
||||
nsHtml5TreeOpExecutor::InitializeStatics()
|
||||
class nsHtml5ExecutorReflusher : public nsRunnable
|
||||
{
|
||||
// Changes to the initial max length pref are intentionally allowed
|
||||
// to reset the run-time-calibrated value.
|
||||
nsContentUtils::AddIntPrefVarCache("html5.opqueue.initiallengthlimit",
|
||||
&sTreeOpQueueLengthLimit);
|
||||
nsContentUtils::AddIntPrefVarCache("html5.opqueue.maxtime",
|
||||
&sTreeOpQueueMaxTime);
|
||||
nsContentUtils::AddIntPrefVarCache("html5.opqueue.minlength",
|
||||
&sTreeOpQueueMinLength);
|
||||
nsContentUtils::AddIntPrefVarCache("html5.opqueue.maxlength",
|
||||
&sTreeOpQueueMaxLength);
|
||||
// Now do some sanity checking to prevent breaking the app via
|
||||
// about:config so badly that it can't be recovered via about:config.
|
||||
if (sTreeOpQueueMinLength <= 0) {
|
||||
sTreeOpQueueMinLength = 200;
|
||||
}
|
||||
if (sTreeOpQueueLengthLimit < sTreeOpQueueMinLength) {
|
||||
sTreeOpQueueLengthLimit = sTreeOpQueueMinLength;
|
||||
}
|
||||
if (sTreeOpQueueMaxLength < sTreeOpQueueMinLength) {
|
||||
sTreeOpQueueMaxLength = sTreeOpQueueMinLength;
|
||||
}
|
||||
if (sTreeOpQueueMaxTime <= 0) {
|
||||
sTreeOpQueueMaxTime = 200;
|
||||
}
|
||||
}
|
||||
private:
|
||||
nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
|
||||
public:
|
||||
nsHtml5ExecutorReflusher(nsHtml5TreeOpExecutor* aExecutor)
|
||||
: mExecutor(aExecutor)
|
||||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mExecutor->RunFlushLoop();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor()
|
||||
{
|
||||
// zeroing operator new for everything
|
||||
mPreloadedURLs.Init(23); // Mean # of preloadable resources per page on dmoz
|
||||
// zeroing operator new for everything else
|
||||
}
|
||||
|
||||
nsHtml5TreeOpExecutor::~nsHtml5TreeOpExecutor()
|
||||
|
@ -198,14 +179,15 @@ nsHtml5TreeOpExecutor::DidBuildModel(PRBool aTerminated)
|
|||
NS_IMETHODIMP
|
||||
nsHtml5TreeOpExecutor::WillInterrupt()
|
||||
{
|
||||
return WillInterruptImpl();
|
||||
NS_NOTREACHED("Don't call. For interface compat only.");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHtml5TreeOpExecutor::WillResume()
|
||||
{
|
||||
WillResumeImpl();
|
||||
return WillParseImpl();
|
||||
NS_NOTREACHED("Don't call. For interface compat only.");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -292,6 +274,15 @@ nsHtml5TreeOpExecutor::PostEvaluateScript(nsIScriptElement *aElement)
|
|||
htmlDocument->ScriptExecuted(aElement);
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> flusher = new nsHtml5ExecutorReflusher(this);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(flusher))) {
|
||||
NS_WARNING("failed to dispatch executor flush event");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpExecutor::UpdateStyleSheet(nsIContent* aElement)
|
||||
{
|
||||
|
@ -347,129 +338,267 @@ nsHtml5TreeOpExecutor::UpdateStyleSheet(nsIContent* aElement)
|
|||
BeginDocUpdate();
|
||||
}
|
||||
|
||||
class nsHtml5ExecutorReflusher : public nsRunnable
|
||||
void
|
||||
nsHtml5TreeOpExecutor::FlushSpeculativeLoads()
|
||||
{
|
||||
if (NS_UNLIKELY(!mParser)) {
|
||||
return;
|
||||
}
|
||||
nsTArray<nsHtml5SpeculativeLoad> speculativeLoadQueue;
|
||||
mStage.MoveSpeculativeLoadsTo(speculativeLoadQueue);
|
||||
const nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
|
||||
const nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
|
||||
for (nsHtml5SpeculativeLoad* iter = (nsHtml5SpeculativeLoad*)start;
|
||||
iter < end;
|
||||
++iter) {
|
||||
iter->Perform(this);
|
||||
}
|
||||
}
|
||||
|
||||
class nsHtml5FlushLoopGuard
|
||||
{
|
||||
private:
|
||||
nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
PRUint32 mStartTime;
|
||||
#endif
|
||||
public:
|
||||
nsHtml5ExecutorReflusher(nsHtml5TreeOpExecutor* aExecutor)
|
||||
nsHtml5FlushLoopGuard(nsHtml5TreeOpExecutor* aExecutor)
|
||||
: mExecutor(aExecutor)
|
||||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
, mStartTime(PR_IntervalToMilliseconds(PR_IntervalNow()))
|
||||
#endif
|
||||
{
|
||||
mExecutor->Flush(PR_FALSE);
|
||||
return NS_OK;
|
||||
mExecutor->mRunFlushLoopOnStack = PR_TRUE;
|
||||
}
|
||||
~nsHtml5FlushLoopGuard()
|
||||
{
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
PRUint32 timeOffTheEventLoop =
|
||||
PR_IntervalToMilliseconds(PR_IntervalNow()) - mStartTime;
|
||||
if (timeOffTheEventLoop >
|
||||
nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop) {
|
||||
nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop =
|
||||
timeOffTheEventLoop;
|
||||
}
|
||||
printf("Longest time off the event loop: %d\n",
|
||||
nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop);
|
||||
#endif
|
||||
|
||||
mExecutor->mRunFlushLoopOnStack = PR_FALSE;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The purpose of the loop here is to avoid returning to the main event loop
|
||||
*/
|
||||
void
|
||||
nsHtml5TreeOpExecutor::Flush(PRBool aForceWholeQueue)
|
||||
nsHtml5TreeOpExecutor::RunFlushLoop()
|
||||
{
|
||||
if (!mParser) {
|
||||
mOpQueue.Clear(); // clear in order to be able to assert in destructor
|
||||
return;
|
||||
}
|
||||
if (mFlushState != eNotFlushing) {
|
||||
if (mRunFlushLoopOnStack) {
|
||||
// There's already a RunFlushLoop() on the call stack.
|
||||
return;
|
||||
}
|
||||
|
||||
mFlushState = eInFlush;
|
||||
|
||||
nsRefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this); // avoid crashing near EOF
|
||||
nsHtml5FlushLoopGuard guard(this); // this is also the self-kungfu!
|
||||
|
||||
nsCOMPtr<nsIParser> parserKungFuDeathGrip(mParser);
|
||||
|
||||
if (mReadingFromStage) {
|
||||
mStage.MoveOpsTo(mOpQueue);
|
||||
// Remember the entry time
|
||||
(void) nsContentSink::WillParseImpl();
|
||||
|
||||
for (;;) {
|
||||
if (!mParser) {
|
||||
// Parse has terminated.
|
||||
mOpQueue.Clear(); // clear in order to be able to assert in destructor
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mParser->IsParserEnabled()) {
|
||||
// The parser is blocked.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFlushState != eNotFlushing) {
|
||||
// XXX Can this happen? In case it can, let's avoid crashing.
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are scripts executing, then the content sink is jumping the gun
|
||||
// (probably due to a synchronous XMLHttpRequest) and will re-enable us
|
||||
// later, see bug 460706.
|
||||
if (IsScriptExecuting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mReadingFromStage) {
|
||||
nsTArray<nsHtml5SpeculativeLoad> speculativeLoadQueue;
|
||||
mStage.MoveOpsAndSpeculativeLoadsTo(mOpQueue, speculativeLoadQueue);
|
||||
// Make sure speculative loads never start after the corresponding
|
||||
// normal loads for the same URLs.
|
||||
const nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
|
||||
const nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
|
||||
for (nsHtml5SpeculativeLoad* iter = (nsHtml5SpeculativeLoad*)start;
|
||||
iter < end;
|
||||
++iter) {
|
||||
iter->Perform(this);
|
||||
}
|
||||
} else {
|
||||
FlushSpeculativeLoads(); // Make sure speculative loads never start after
|
||||
// the corresponding normal loads for the same
|
||||
// URLs.
|
||||
// Not sure if this grip is still needed, but previously, the code
|
||||
// gripped before calling ParseUntilBlocked();
|
||||
nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip =
|
||||
static_cast<nsHtml5Parser*> (mParser.get())->GetStreamParser();
|
||||
// Now parse content left in the document.write() buffer queue if any.
|
||||
// This may generate tree ops on its own or dequeue a speculation.
|
||||
static_cast<nsHtml5Parser*> (mParser.get())->ParseUntilBlocked();
|
||||
}
|
||||
|
||||
if (mOpQueue.IsEmpty()) {
|
||||
// Avoid bothering the rest of the engine with a doc update if there's
|
||||
// nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
mFlushState = eInFlush;
|
||||
|
||||
nsIContent* scriptElement = nsnull;
|
||||
|
||||
BeginDocUpdate();
|
||||
|
||||
PRUint32 numberOfOpsToFlush = mOpQueue.Length();
|
||||
|
||||
mElementsSeenInThisAppendBatch.SetCapacity(numberOfOpsToFlush * 2);
|
||||
|
||||
const nsHtml5TreeOperation* first = mOpQueue.Elements();
|
||||
const nsHtml5TreeOperation* last = first + numberOfOpsToFlush - 1;
|
||||
for (nsHtml5TreeOperation* iter = (nsHtml5TreeOperation*)first;;) {
|
||||
if (NS_UNLIKELY(!mParser)) {
|
||||
// The previous tree op caused a call to nsIParser::Terminate().
|
||||
break;
|
||||
}
|
||||
NS_ASSERTION(mFlushState == eInDocUpdate,
|
||||
"Tried to perform tree op outside update batch.");
|
||||
iter->Perform(this, &scriptElement);
|
||||
|
||||
// Be sure not to check the deadline if the last op was just performed.
|
||||
if (NS_UNLIKELY(iter == last)) {
|
||||
break;
|
||||
} else if (NS_UNLIKELY(nsContentSink::DidProcessATokenImpl() ==
|
||||
NS_ERROR_HTMLPARSER_INTERRUPTED)) {
|
||||
mOpQueue.RemoveElementsAt(0, (iter - first) + 1);
|
||||
|
||||
EndDocUpdate();
|
||||
|
||||
mFlushState = eNotFlushing;
|
||||
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
printf("REFLUSH SCHEDULED (executing ops): %d\n",
|
||||
++sTimesFlushLoopInterrupted);
|
||||
#endif
|
||||
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
|
||||
return;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
||||
mOpQueue.Clear();
|
||||
|
||||
EndDocUpdate();
|
||||
|
||||
mFlushState = eNotFlushing;
|
||||
|
||||
if (NS_UNLIKELY(!mParser)) {
|
||||
// The parse ended already.
|
||||
return;
|
||||
}
|
||||
|
||||
if (scriptElement) {
|
||||
// must be tail call when mFlushState is eNotFlushing
|
||||
RunScript(scriptElement);
|
||||
|
||||
// The script execution machinery makes sure this doesn't get deflected.
|
||||
if (nsContentSink::DidProcessATokenImpl() ==
|
||||
NS_ERROR_HTMLPARSER_INTERRUPTED) {
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
printf("REFLUSH SCHEDULED (after script): %d\n",
|
||||
++sTimesFlushLoopInterrupted);
|
||||
#endif
|
||||
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpExecutor::FlushDocumentWrite()
|
||||
{
|
||||
if (!mParser) {
|
||||
// The parse has ended.
|
||||
mOpQueue.Clear(); // clear in order to be able to assert in destructor
|
||||
return;
|
||||
}
|
||||
|
||||
FlushSpeculativeLoads(); // Make sure speculative loads never start after the
|
||||
// corresponding normal loads for the same URLs.
|
||||
|
||||
if (mFlushState != eNotFlushing) {
|
||||
// XXX Can this happen? In case it can, let's avoid crashing.
|
||||
return;
|
||||
}
|
||||
|
||||
mFlushState = eInFlush;
|
||||
|
||||
// avoid crashing near EOF
|
||||
nsRefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this);
|
||||
nsCOMPtr<nsIParser> parserKungFuDeathGrip(mParser);
|
||||
|
||||
NS_ASSERTION(!mReadingFromStage,
|
||||
"Got doc write flush when reading from stage");
|
||||
|
||||
#ifdef DEBUG
|
||||
mStage.AssertEmpty();
|
||||
#endif
|
||||
|
||||
nsIContent* scriptElement = nsnull;
|
||||
|
||||
BeginDocUpdate();
|
||||
|
||||
PRIntervalTime flushStart = 0;
|
||||
PRUint32 numberOfOpsToFlush = mOpQueue.Length();
|
||||
PRBool reflushNeeded = PR_FALSE;
|
||||
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
if (numberOfOpsToFlush > sOpQueueMaxLength) {
|
||||
sOpQueueMaxLength = numberOfOpsToFlush;
|
||||
}
|
||||
printf("QUEUE LENGTH: %d\n", numberOfOpsToFlush);
|
||||
printf("MAX QUEUE LENGTH: %d\n", sOpQueueMaxLength);
|
||||
#endif
|
||||
|
||||
if (aForceWholeQueue) {
|
||||
if (numberOfOpsToFlush > (PRUint32)sTreeOpQueueMinLength) {
|
||||
flushStart = PR_IntervalNow(); // compute averages only if enough ops
|
||||
}
|
||||
} else {
|
||||
if (numberOfOpsToFlush > (PRUint32)sTreeOpQueueMinLength) {
|
||||
flushStart = PR_IntervalNow(); // compute averages only if enough ops
|
||||
if (numberOfOpsToFlush > (PRUint32)sTreeOpQueueLengthLimit) {
|
||||
numberOfOpsToFlush = (PRUint32)sTreeOpQueueLengthLimit;
|
||||
reflushNeeded = PR_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mElementsSeenInThisAppendBatch.SetCapacity(numberOfOpsToFlush * 2);
|
||||
|
||||
const nsHtml5TreeOperation* start = mOpQueue.Elements();
|
||||
const nsHtml5TreeOperation* end = start + numberOfOpsToFlush;
|
||||
for (nsHtml5TreeOperation* iter = (nsHtml5TreeOperation*)start; iter < end; ++iter) {
|
||||
for (nsHtml5TreeOperation* iter = (nsHtml5TreeOperation*)start;
|
||||
iter < end;
|
||||
++iter) {
|
||||
if (NS_UNLIKELY(!mParser)) {
|
||||
// The previous tree op caused a call to nsIParser::Terminate();
|
||||
// The previous tree op caused a call to nsIParser::Terminate().
|
||||
break;
|
||||
}
|
||||
NS_ASSERTION(mFlushState == eInDocUpdate, "Tried to perform tree op outside update batch.");
|
||||
NS_ASSERTION(mFlushState == eInDocUpdate,
|
||||
"Tried to perform tree op outside update batch.");
|
||||
iter->Perform(this, &scriptElement);
|
||||
}
|
||||
|
||||
if (NS_LIKELY(mParser)) {
|
||||
mOpQueue.RemoveElementsAt(0, numberOfOpsToFlush);
|
||||
} else {
|
||||
mOpQueue.Clear(); // only for diagnostics in the destructor of this class
|
||||
}
|
||||
mOpQueue.Clear();
|
||||
|
||||
if (flushStart) {
|
||||
PRUint32 delta = PR_IntervalToMilliseconds(PR_IntervalNow() - flushStart);
|
||||
sTreeOpQueueLengthLimit = delta ?
|
||||
(PRUint32)(((PRUint64)sTreeOpQueueMaxTime * (PRUint64)numberOfOpsToFlush)
|
||||
/ delta) :
|
||||
sTreeOpQueueMaxLength; // if the delta is less than one ms, use max
|
||||
if (sTreeOpQueueLengthLimit < sTreeOpQueueMinLength) {
|
||||
// both are signed and sTreeOpQueueMinLength is always positive, so this
|
||||
// also takes care of the theoretical overflow of sTreeOpQueueLengthLimit
|
||||
sTreeOpQueueLengthLimit = sTreeOpQueueMinLength;
|
||||
}
|
||||
if (sTreeOpQueueLengthLimit > sTreeOpQueueMaxLength) {
|
||||
sTreeOpQueueLengthLimit = sTreeOpQueueMaxLength;
|
||||
}
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
printf("FLUSH DURATION (millis): %d\n", delta);
|
||||
printf("QUEUE NEW MAX LENGTH: %d\n", sTreeOpQueueLengthLimit);
|
||||
#endif
|
||||
}
|
||||
|
||||
EndDocUpdate();
|
||||
|
||||
mFlushState = eNotFlushing;
|
||||
|
||||
if (NS_UNLIKELY(!mParser)) {
|
||||
// Ending the doc update caused a call to nsIParser::Terminate().
|
||||
return;
|
||||
}
|
||||
|
||||
if (scriptElement) {
|
||||
NS_ASSERTION(!reflushNeeded, "Got scriptElement when queue not fully flushed.");
|
||||
RunScript(scriptElement); // must be tail call when mFlushState is eNotFlushing
|
||||
} else if (reflushNeeded) {
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
printf("REFLUSH SCHEDULED.\n");
|
||||
#endif
|
||||
nsCOMPtr<nsIRunnable> flusher = new nsHtml5ExecutorReflusher(this);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(flusher))) {
|
||||
NS_WARNING("failed to dispatch executor flush event");
|
||||
}
|
||||
// must be tail call when mFlushState is eNotFlushing
|
||||
RunScript(scriptElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -631,8 +760,12 @@ nsHtml5TreeOpExecutor::RunScript(nsIContent* aScriptElement)
|
|||
// This may have already happened if the script executed, but in case
|
||||
// it didn't then remove the element so that it doesn't get stuck forever.
|
||||
htmlDocument->ScriptExecuted(sele);
|
||||
// mParser may have been nulled out by now, but nsContentSink deals
|
||||
ContinueInterruptedParsingAsync();
|
||||
|
||||
// mParser may have been nulled out by now, but the flusher deals
|
||||
|
||||
// If this event isn't needed, it doesn't do anything. It is sometimes
|
||||
// necessary for the parse to continue after complex situations.
|
||||
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -642,11 +775,7 @@ nsHtml5TreeOpExecutor::Init(nsIDocument* aDoc,
|
|||
nsISupports* aContainer,
|
||||
nsIChannel* aChannel)
|
||||
{
|
||||
nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mCanInterruptParser = PR_FALSE; // without this, nsContentSink calls
|
||||
// UnblockOnload from DropParserAndPerfHint
|
||||
return rv;
|
||||
return nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -701,6 +830,7 @@ nsHtml5TreeOpExecutor::Reset() {
|
|||
mOpQueue.Clear();
|
||||
mStarted = PR_FALSE;
|
||||
mFlushState = eNotFlushing;
|
||||
mRunFlushLoopOnStack = PR_FALSE;
|
||||
mFragmentMode = PR_FALSE;
|
||||
}
|
||||
|
||||
|
@ -721,9 +851,67 @@ nsHtml5TreeOpExecutor::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* a
|
|||
static_cast<nsHtml5Parser*> (mParser.get())->InitializeDocWriteParserState(aState, aLine);
|
||||
}
|
||||
|
||||
// Speculative loading
|
||||
|
||||
already_AddRefed<nsIURI>
|
||||
nsHtml5TreeOpExecutor::ConvertIfNotPreloadedYet(const nsAString& aURL)
|
||||
{
|
||||
nsIURI* base = mDocument->GetBaseURI();
|
||||
const nsCString& charset = mDocument->GetDocumentCharacterSet();
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, charset.get(), base);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to create a URI");
|
||||
return nsnull;
|
||||
}
|
||||
nsCAutoString spec;
|
||||
uri->GetSpec(spec);
|
||||
if (mPreloadedURLs.Contains(spec)) {
|
||||
return nsnull;
|
||||
}
|
||||
mPreloadedURLs.Put(spec);
|
||||
nsIURI* retURI = uri;
|
||||
NS_ADDREF(retURI);
|
||||
return retURI;
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpExecutor::PreloadScript(const nsAString& aURL,
|
||||
const nsAString& aCharset,
|
||||
const nsAString& aType)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
mDocument->ScriptLoader()->PreloadURI(uri, aCharset, aType);
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpExecutor::PreloadStyle(const nsAString& aURL,
|
||||
const nsAString& aCharset)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
mDocument->PreloadStyle(uri, aCharset);
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
mDocument->MaybePreLoadImage(uri);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
PRUint32 nsHtml5TreeOpExecutor::sOpQueueMaxLength = 0;
|
||||
PRUint32 nsHtml5TreeOpExecutor::sAppendBatchMaxSize = 0;
|
||||
PRUint32 nsHtml5TreeOpExecutor::sAppendBatchSlotsExamined = 0;
|
||||
PRUint32 nsHtml5TreeOpExecutor::sAppendBatchExaminations = 0;
|
||||
PRUint32 nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop = 0;
|
||||
PRUint32 nsHtml5TreeOpExecutor::sTimesFlushLoopInterrupted = 0;
|
||||
#endif
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "nsIDocument.h"
|
||||
#include "nsTraceRefcnt.h"
|
||||
#include "nsHtml5TreeOperation.h"
|
||||
#include "nsHtml5SpeculativeLoad.h"
|
||||
#include "nsHtml5PendingNotification.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsContentSink.h"
|
||||
|
@ -55,6 +56,8 @@
|
|||
#include "nsCOMArray.h"
|
||||
#include "nsAHtml5TreeOpSink.h"
|
||||
#include "nsHtml5TreeOpStage.h"
|
||||
#include "nsHashSets.h"
|
||||
#include "nsIURI.h"
|
||||
|
||||
class nsHtml5TreeBuilder;
|
||||
class nsHtml5Tokenizer;
|
||||
|
@ -73,24 +76,21 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
public nsIContentSink,
|
||||
public nsAHtml5TreeOpSink
|
||||
{
|
||||
friend class nsHtml5FlushLoopGuard;
|
||||
|
||||
public:
|
||||
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHtml5TreeOpExecutor, nsContentSink)
|
||||
|
||||
static void InitializeStatics();
|
||||
|
||||
private:
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
static PRUint32 sOpQueueMaxLength;
|
||||
static PRUint32 sAppendBatchMaxSize;
|
||||
static PRUint32 sAppendBatchSlotsExamined;
|
||||
static PRUint32 sAppendBatchExaminations;
|
||||
static PRUint32 sLongestTimeOffTheEventLoop;
|
||||
static PRUint32 sTimesFlushLoopInterrupted;
|
||||
#endif
|
||||
static PRInt32 sTreeOpQueueLengthLimit;
|
||||
static PRInt32 sTreeOpQueueMaxTime;
|
||||
static PRInt32 sTreeOpQueueMinLength;
|
||||
static PRInt32 sTreeOpQueueMaxLength;
|
||||
|
||||
/**
|
||||
* Whether EOF needs to be suppressed
|
||||
|
@ -105,6 +105,11 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
nsHtml5StreamParser* mStreamParser;
|
||||
nsCOMArray<nsIContent> mOwnedElements;
|
||||
|
||||
/**
|
||||
* URLs already preloaded/preloading.
|
||||
*/
|
||||
nsCStringHashSet mPreloadedURLs;
|
||||
|
||||
/**
|
||||
* Whether the parser has started
|
||||
*/
|
||||
|
@ -114,6 +119,10 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
|
||||
eHtml5FlushState mFlushState;
|
||||
|
||||
PRBool mRunFlushLoopOnStack;
|
||||
|
||||
PRBool mCallContinueInterruptedParsingIfEnabled;
|
||||
|
||||
PRBool mFragmentMode;
|
||||
|
||||
public:
|
||||
|
@ -183,6 +192,8 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
virtual void UpdateChildCounts();
|
||||
virtual nsresult FlushTags();
|
||||
virtual void PostEvaluateScript(nsIScriptElement *aElement);
|
||||
virtual void ContinueInterruptedParsingAsync();
|
||||
|
||||
/**
|
||||
* Sets up style sheet load / parse
|
||||
*/
|
||||
|
@ -226,6 +237,8 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
|
||||
void EnableFragmentMode() {
|
||||
mFragmentMode = PR_TRUE;
|
||||
mCanInterruptParser = PR_FALSE; // prevent DropParserAndPerfHint
|
||||
// from unblocking onload
|
||||
}
|
||||
|
||||
PRBool IsFragmentMode() {
|
||||
|
@ -316,8 +329,12 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
|
||||
nsresult Init(nsIDocument* aDoc, nsIURI* aURI,
|
||||
nsISupports* aContainer, nsIChannel* aChannel);
|
||||
|
||||
void FlushSpeculativeLoads();
|
||||
|
||||
void Flush(PRBool aForceWholeQueue);
|
||||
void RunFlushLoop();
|
||||
|
||||
void FlushDocumentWrite();
|
||||
|
||||
void MaybeSuspend();
|
||||
|
||||
|
@ -336,6 +353,12 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
PRBool IsFlushing() {
|
||||
return mFlushState >= eInFlush;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
PRBool IsInFlushLoop() {
|
||||
return mRunFlushLoopOnStack;
|
||||
}
|
||||
#endif
|
||||
|
||||
void RunScript(nsIContent* aScriptElement);
|
||||
|
||||
|
@ -345,15 +368,13 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
mOwnedElements.AppendObject(aContent);
|
||||
}
|
||||
|
||||
// The following two methods are for the main-thread case
|
||||
|
||||
/**
|
||||
* Flush the operations from the tree operations from the argument
|
||||
* queue unconditionally.
|
||||
* queue unconditionally. (This is for the main thread case.)
|
||||
*/
|
||||
virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
|
||||
nsAHtml5TreeOpSink* GetStage() {
|
||||
nsHtml5TreeOpStage* GetStage() {
|
||||
return &mStage;
|
||||
}
|
||||
|
||||
|
@ -369,10 +390,23 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
}
|
||||
#endif
|
||||
|
||||
void PreloadScript(const nsAString& aURL,
|
||||
const nsAString& aCharset,
|
||||
const nsAString& aType);
|
||||
|
||||
void PreloadStyle(const nsAString& aURL, const nsAString& aCharset);
|
||||
|
||||
void PreloadImage(const nsAString& aURL);
|
||||
|
||||
private:
|
||||
|
||||
nsHtml5Tokenizer* GetTokenizer();
|
||||
|
||||
|
||||
/**
|
||||
* Get a nsIURI for an nsString if the URL hasn't been preloaded yet.
|
||||
*/
|
||||
already_AddRefed<nsIURI> ConvertIfNotPreloadedYet(const nsAString& aURL);
|
||||
|
||||
};
|
||||
|
||||
#endif // nsHtml5TreeOpExecutor_h__
|
||||
|
|
|
@ -52,20 +52,48 @@ nsHtml5TreeOpStage::MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
|||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
if (mOpQueue.IsEmpty()) {
|
||||
mOpQueue.SwapElements(aOpQueue);
|
||||
return;
|
||||
} else {
|
||||
mOpQueue.MoveElementsFrom(aOpQueue);
|
||||
}
|
||||
mOpQueue.MoveElementsFrom(aOpQueue);
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpStage::MoveOpsTo(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
nsHtml5TreeOpStage::MoveOpsAndSpeculativeLoadsTo(nsTArray<nsHtml5TreeOperation>& aOpQueue,
|
||||
nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue)
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
if (aOpQueue.IsEmpty()) {
|
||||
mOpQueue.SwapElements(aOpQueue);
|
||||
return;
|
||||
} else {
|
||||
aOpQueue.MoveElementsFrom(mOpQueue);
|
||||
}
|
||||
if (aSpeculativeLoadQueue.IsEmpty()) {
|
||||
mSpeculativeLoadQueue.SwapElements(aSpeculativeLoadQueue);
|
||||
} else {
|
||||
aSpeculativeLoadQueue.MoveElementsFrom(mSpeculativeLoadQueue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpStage::MoveSpeculativeLoadsFrom(nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue)
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
if (mSpeculativeLoadQueue.IsEmpty()) {
|
||||
mSpeculativeLoadQueue.SwapElements(aSpeculativeLoadQueue);
|
||||
} else {
|
||||
mSpeculativeLoadQueue.MoveElementsFrom(aSpeculativeLoadQueue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpStage::MoveSpeculativeLoadsTo(nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue)
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
if (aSpeculativeLoadQueue.IsEmpty()) {
|
||||
mSpeculativeLoadQueue.SwapElements(aSpeculativeLoadQueue);
|
||||
} else {
|
||||
aSpeculativeLoadQueue.MoveElementsFrom(mSpeculativeLoadQueue);
|
||||
}
|
||||
aOpQueue.MoveElementsFrom(mOpQueue);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "nsHtml5TreeOperation.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsAHtml5TreeOpSink.h"
|
||||
#include "nsHtml5SpeculativeLoad.h"
|
||||
|
||||
class nsHtml5TreeOpStage : public nsAHtml5TreeOpSink {
|
||||
public:
|
||||
|
@ -57,9 +58,20 @@ class nsHtml5TreeOpStage : public nsAHtml5TreeOpSink {
|
|||
virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
|
||||
/**
|
||||
* Retrieve the staged operations into the argument.
|
||||
* Retrieve the staged operations and speculative loads into the arguments.
|
||||
*/
|
||||
void MoveOpsTo(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
void MoveOpsAndSpeculativeLoadsTo(nsTArray<nsHtml5TreeOperation>& aOpQueue,
|
||||
nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue);
|
||||
|
||||
/**
|
||||
* Move the speculative loads from the argument into the staging queue.
|
||||
*/
|
||||
void MoveSpeculativeLoadsFrom(nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue);
|
||||
|
||||
/**
|
||||
* Retrieve the staged speculative loads into the argument.
|
||||
*/
|
||||
void MoveSpeculativeLoadsTo(nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue);
|
||||
|
||||
#ifdef DEBUG
|
||||
void AssertEmpty();
|
||||
|
@ -67,7 +79,8 @@ class nsHtml5TreeOpStage : public nsAHtml5TreeOpSink {
|
|||
|
||||
private:
|
||||
nsTArray<nsHtml5TreeOperation> mOpQueue;
|
||||
mozilla::Mutex mMutex;
|
||||
nsTArray<nsHtml5SpeculativeLoad> mSpeculativeLoadQueue;
|
||||
mozilla::Mutex mMutex;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include "nsAHtml5EncodingDeclarationHandler.h"
|
||||
|
||||
class nsHtml5StreamParser;
|
||||
class nsHtml5SpeculativeLoader;
|
||||
|
||||
class nsHtml5Tokenizer;
|
||||
class nsHtml5TreeBuilder;
|
||||
|
|
Загрузка…
Ссылка в новой задаче