Bug 18333: Make the XML content sink incremental. Patch by Henri Sivonen<hsivonen@iki.fi>. r=peterv sr=sicking
git-svn-id: svn://10.0.0.236/trunk@219201 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
1172c6e14c
commit
dc89ea1f2d
@ -75,6 +75,7 @@ REQUIRES = xpcom \
|
||||
uriloader \
|
||||
rdf \
|
||||
xultmpl \
|
||||
util \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS = \
|
||||
|
||||
@ -100,6 +100,7 @@ public:
|
||||
nsAString& aStr);
|
||||
|
||||
// nsIContentSink
|
||||
NS_IMETHOD WillTokenize(void) { return NS_OK; }
|
||||
NS_IMETHOD WillBuildModel(void) { return NS_OK; }
|
||||
NS_IMETHOD DidBuildModel(void) { return NS_OK; }
|
||||
NS_IMETHOD WillInterrupt(void) { return NS_OK; }
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
* 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
|
||||
@ -78,6 +79,107 @@
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsNodeInfoManager.h"
|
||||
#include "nsTimer.h"
|
||||
#include "nsIAppShell.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "nsIDOMNSDocument.h"
|
||||
#include "nsIRequest.h"
|
||||
#include "nsNodeUtils.h"
|
||||
#include "nsIDOMNode.h"
|
||||
|
||||
PRLogModuleInfo* gContentSinkLogModuleInfo;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
// DummyParserRequest
|
||||
//
|
||||
// This is a dummy request implementation that we add to the document's load
|
||||
// group. It ensures that EndDocumentLoad() in the docshell doesn't fire
|
||||
// before we've finished all of parsing and tokenizing of the document.
|
||||
//
|
||||
|
||||
class DummyParserRequest : public nsIRequest
|
||||
{
|
||||
protected:
|
||||
nsContentSink* mSink; // Weak reference
|
||||
|
||||
public:
|
||||
DummyParserRequest(nsContentSink* aSink);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIRequest
|
||||
NS_IMETHOD GetName(nsACString &result)
|
||||
{
|
||||
result.AssignLiteral("about:layout-dummy-request");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD IsPending(PRBool *_retval)
|
||||
{
|
||||
*_retval = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetStatus(nsresult *status)
|
||||
{
|
||||
*status = NS_OK;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Cancel(nsresult status);
|
||||
NS_IMETHOD Suspend(void)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Resume(void)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetLoadGroup(nsILoadGroup **aLoadGroup)
|
||||
{
|
||||
*aLoadGroup = nsnull;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD SetLoadGroup(nsILoadGroup * aLoadGroup)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags)
|
||||
{
|
||||
*aLoadFlags = nsIRequest::LOAD_NORMAL;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(DummyParserRequest, nsIRequest)
|
||||
|
||||
|
||||
DummyParserRequest::DummyParserRequest(nsContentSink* aSink)
|
||||
: mSink(aSink)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DummyParserRequest::Cancel(nsresult status)
|
||||
{
|
||||
// Cancel parser
|
||||
if (mSink && mSink->mParser) {
|
||||
mSink->mParser->CancelParsingEvents();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ALLOW_ASYNCH_STYLE_SHEETS
|
||||
@ -86,7 +188,6 @@ const PRBool kBlockByDefault = PR_FALSE;
|
||||
const PRBool kBlockByDefault = PR_TRUE;
|
||||
#endif
|
||||
|
||||
|
||||
class nsScriptLoaderObserverProxy : public nsIScriptLoaderObserver
|
||||
{
|
||||
public:
|
||||
@ -146,7 +247,14 @@ NS_IMPL_ISUPPORTS3(nsContentSink,
|
||||
nsIScriptLoaderObserver)
|
||||
|
||||
nsContentSink::nsContentSink()
|
||||
: mLayoutStarted(PR_FALSE),
|
||||
mInMonolithicContainer(0)
|
||||
{
|
||||
#ifdef NS_DEBUG
|
||||
if (!gContentSinkLogModuleInfo) {
|
||||
gContentSinkLogModuleInfo = PR_NewLogModule("nscontentsink");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
nsContentSink::~nsContentSink()
|
||||
@ -186,6 +294,49 @@ nsContentSink::Init(nsIDocument* aDoc,
|
||||
ProcessHTTPHeaders(aChannel);
|
||||
|
||||
mNodeInfoManager = aDoc->NodeInfoManager();
|
||||
|
||||
mNotifyOnTimer =
|
||||
nsContentUtils::GetBoolPref("content.notify.ontimer", PR_TRUE);
|
||||
|
||||
// -1 means never
|
||||
mBackoffCount =
|
||||
nsContentUtils::GetIntPref("content.notify.backoffcount", -1);
|
||||
|
||||
// The mNotificationInterval has a dramatic effect on how long it
|
||||
// takes to initially display content for slow connections.
|
||||
// The current value provides good
|
||||
// incremental display of content without causing an increase
|
||||
// in page load time. If this value is set below 1/10 of second
|
||||
// it starts to impact page load performance.
|
||||
// see bugzilla bug 72138 for more info.
|
||||
mNotificationInterval =
|
||||
nsContentUtils::GetIntPref("content.notify.interval", 120000);
|
||||
|
||||
// The mMaxTokenProcessingTime controls how long we stay away from
|
||||
// the event loop when processing token. A lower value makes the app
|
||||
// more responsive, but may increase page load time. The content
|
||||
// sink mNotificationInterval gates how frequently the content is
|
||||
// processed so it will also affect how interactive the app is
|
||||
// during page load also. The mNotification prevents contents
|
||||
// flushes from happening too frequently. while
|
||||
// mMaxTokenProcessingTime prevents flushes from happening too
|
||||
// infrequently.
|
||||
|
||||
// The current ratio of 3 to 1 was determined to be the lowest
|
||||
// mMaxTokenProcessingTime which does not impact page load
|
||||
// performance. See bugzilla bug 76722 for details.
|
||||
|
||||
mMaxTokenProcessingTime =
|
||||
nsContentUtils::GetIntPref("content.max.tokenizing.time",
|
||||
mNotificationInterval * 3);
|
||||
|
||||
// 3/4 second (750000us) default for switching
|
||||
mDynamicIntervalSwitchThreshold =
|
||||
nsContentUtils::GetIntPref("content.switch.threshold", 750000);
|
||||
|
||||
mCanInterruptParser =
|
||||
nsContentUtils::GetBoolPref("content.interrupt.parsing", PR_TRUE);
|
||||
|
||||
return NS_OK;
|
||||
|
||||
}
|
||||
@ -808,6 +959,8 @@ nsContentSink::RefreshIfEnabled(nsIViewManager* vm)
|
||||
void
|
||||
nsContentSink::StartLayout(PRBool aIsFrameset)
|
||||
{
|
||||
mLayoutStarted = PR_TRUE;
|
||||
mLastNotificationTime = PR_Now();
|
||||
PRUint32 i, ns = mDocument->GetNumberOfShells();
|
||||
for (i = 0; i < ns; i++) {
|
||||
nsIPresShell *shell = mDocument->GetShellAt(i);
|
||||
@ -870,3 +1023,472 @@ nsContentSink::StartLayout(PRBool aIsFrameset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsContentSink::NotifyAppend(nsIContent* aContainer, PRUint32 aStartIndex)
|
||||
{
|
||||
if (aContainer->GetCurrentDoc() != mDocument) {
|
||||
// aContainer is not actually in our document anymore.... Just bail out of
|
||||
// here; notifying on our document for this append would be wrong.
|
||||
return;
|
||||
}
|
||||
|
||||
mInNotification++;
|
||||
|
||||
MOZ_TIMER_DEBUGLOG(("Save and stop: nsHTMLContentSink::NotifyAppend()\n"));
|
||||
MOZ_TIMER_SAVE(mWatch)
|
||||
MOZ_TIMER_STOP(mWatch);
|
||||
|
||||
nsNodeUtils::ContentAppended(aContainer, aStartIndex);
|
||||
mLastNotificationTime = PR_Now();
|
||||
|
||||
MOZ_TIMER_DEBUGLOG(("Restore: nsHTMLContentSink::NotifyAppend()\n"));
|
||||
MOZ_TIMER_RESTORE(mWatch);
|
||||
|
||||
mInNotification--;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentSink::Notify(nsITimer *timer)
|
||||
{
|
||||
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::Notify()\n"));
|
||||
MOZ_TIMER_START(mWatch);
|
||||
|
||||
if (mParsing) {
|
||||
// We shouldn't interfere with our normal DidProcessAToken logic
|
||||
mDroppedTimer = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_DEBUG
|
||||
{
|
||||
PRTime now = PR_Now();
|
||||
PRInt64 diff, interval;
|
||||
PRInt32 delay;
|
||||
|
||||
LL_I2L(interval, GetNotificationInterval());
|
||||
LL_SUB(diff, now, mLastNotificationTime);
|
||||
|
||||
LL_SUB(diff, diff, interval);
|
||||
LL_L2I(delay, diff);
|
||||
delay /= PR_USEC_PER_MSEC;
|
||||
|
||||
mBackoffCount--;
|
||||
SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
|
||||
("nsContentSink::Notify: reflow on a timer: %d milliseconds "
|
||||
"late, backoff count: %d", delay, mBackoffCount));
|
||||
}
|
||||
#endif
|
||||
|
||||
FlushTags();
|
||||
|
||||
// Now try and scroll to the reference
|
||||
// XXX Should we scroll unconditionally for history loads??
|
||||
TryToScrollToRef();
|
||||
mNotificationTimer = nsnull;
|
||||
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Notify()\n"));
|
||||
MOZ_TIMER_STOP(mWatch);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsContentSink::IsTimeToNotify()
|
||||
{
|
||||
if (!mNotifyOnTimer || !mLayoutStarted || !mBackoffCount ||
|
||||
mInMonolithicContainer) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRTime now = PR_Now();
|
||||
PRInt64 interval, diff;
|
||||
|
||||
LL_I2L(interval, GetNotificationInterval());
|
||||
LL_SUB(diff, now, mLastNotificationTime);
|
||||
|
||||
if (LL_CMP(diff, >, interval)) {
|
||||
mBackoffCount--;
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentSink::WillInterruptImpl()
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
|
||||
SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
|
||||
("nsContentSink::WillInterrupt: this=%p", this));
|
||||
#ifndef SINK_NO_INCREMENTAL
|
||||
if (mNotifyOnTimer && mLayoutStarted) {
|
||||
if (mBackoffCount && !mInMonolithicContainer) {
|
||||
PRInt64 now = PR_Now();
|
||||
PRInt64 interval = GetNotificationInterval();
|
||||
PRInt64 diff = now - mLastNotificationTime;
|
||||
|
||||
// If it's already time for us to have a notification
|
||||
if (diff > interval || mDroppedTimer) {
|
||||
mBackoffCount--;
|
||||
SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
|
||||
("nsContentSink::WillInterrupt: flushing tags since we've "
|
||||
"run out time; backoff count: %d", mBackoffCount));
|
||||
result = FlushTags();
|
||||
if (mDroppedTimer) {
|
||||
TryToScrollToRef();
|
||||
mDroppedTimer = PR_FALSE;
|
||||
}
|
||||
} else if (!mNotificationTimer) {
|
||||
interval -= diff;
|
||||
PRInt32 delay = interval;
|
||||
|
||||
// Convert to milliseconds
|
||||
delay /= PR_USEC_PER_MSEC;
|
||||
|
||||
mNotificationTimer = do_CreateInstance("@mozilla.org/timer;1",
|
||||
&result);
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
|
||||
("nsContentSink::WillInterrupt: setting up timer with "
|
||||
"delay %d", delay));
|
||||
|
||||
result =
|
||||
mNotificationTimer->InitWithCallback(this, delay,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
if (NS_FAILED(result)) {
|
||||
mNotificationTimer = nsnull;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
|
||||
("nsContentSink::WillInterrupt: flushing tags "
|
||||
"unconditionally"));
|
||||
result = FlushTags();
|
||||
}
|
||||
#endif
|
||||
|
||||
mParsing = PR_FALSE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentSink::WillResumeImpl()
|
||||
{
|
||||
SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
|
||||
("nsContentSink::WillResume: this=%p", this));
|
||||
|
||||
mParsing = PR_TRUE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentSink::DidProcessATokenImpl()
|
||||
{
|
||||
if (!mCanInterruptParser) {
|
||||
return NS_OK;
|
||||
}
|
||||
// There is both a high frequency interrupt mode and a low
|
||||
// frequency interupt mode controlled by the flag
|
||||
// mDynamicLowerValue The high frequency mode
|
||||
// interupts the parser frequently to provide UI responsiveness at
|
||||
// the expense of page load time. The low frequency mode
|
||||
// interrupts the parser and samples the system clock infrequently
|
||||
// to provide fast page load time. When the user moves the mouse,
|
||||
// clicks or types the mode switches to the high frequency
|
||||
// interrupt mode. If the user stops moving the mouse or typing
|
||||
// for a duration of time (mDynamicIntervalSwitchThreshold) it
|
||||
// switches to low frequency interrupt mode.
|
||||
|
||||
// Get the current user event time
|
||||
nsIPresShell *shell = mDocument->GetShellAt(0);
|
||||
|
||||
if (!shell) {
|
||||
// If there's no pres shell in the document, return early since
|
||||
// we're not laying anything out here.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIViewManager* vm = shell->GetViewManager();
|
||||
NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
|
||||
PRUint32 eventTime;
|
||||
nsCOMPtr<nsIWidget> widget;
|
||||
nsresult rv = vm->GetWidget(getter_AddRefs(widget));
|
||||
if (!widget || NS_FAILED(widget->GetLastInputEventTime(eventTime))) {
|
||||
// If we can't get the last input time from the widget
|
||||
// then we will get it from the viewmanager.
|
||||
rv = vm->GetLastUserEventTime(eventTime);
|
||||
NS_ENSURE_SUCCESS(rv , NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
|
||||
|
||||
if (!mDynamicLowerValue && mLastSampledUserEventTime == eventTime) {
|
||||
// The magic value of NS_MAX_TOKENS_DEFLECTED_IN_LOW_FREQ_MODE
|
||||
// was selected by empirical testing. It provides reasonable
|
||||
// user response and prevents us from sampling the clock too
|
||||
// frequently.
|
||||
if (mDeflectedCount < NS_MAX_TOKENS_DEFLECTED_IN_LOW_FREQ_MODE) {
|
||||
mDeflectedCount++;
|
||||
// return early to prevent sampling the clock. Note: This
|
||||
// prevents us from switching to higher frequency (better UI
|
||||
// responsive) mode, so limit ourselves to doing for no more
|
||||
// than NS_MAX_TOKENS_DEFLECTED_IN_LOW_FREQ_MODE tokens.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// reset count and drop through to the code which samples the
|
||||
// clock and does the dynamic switch between the high
|
||||
// frequency and low frequency interruption of the parser.
|
||||
mDeflectedCount = 0;
|
||||
}
|
||||
mLastSampledUserEventTime = eventTime;
|
||||
|
||||
PRUint32 currentTime = PR_IntervalToMicroseconds(PR_IntervalNow());
|
||||
|
||||
// Get the last user event time and compare it with the current
|
||||
// time to determine if the lower value for content notification
|
||||
// and max token processing should be used. But only consider
|
||||
// using the lower value if the document has already been loading
|
||||
// for 2 seconds. 2 seconds was chosen because it is greater than
|
||||
// the default 3/4 of second that is used to determine when to
|
||||
// switch between the modes and it gives the document a little
|
||||
// time to create windows. This is important because on some
|
||||
// systems (Windows, for example) when a window is created and the
|
||||
// mouse is over it, a mouse move event is sent, which will kick
|
||||
// us into interactive mode otherwise. It also suppresses reaction
|
||||
// to pressing the ENTER key in the URL bar...
|
||||
|
||||
PRUint32 delayBeforeLoweringThreshold =
|
||||
NS_STATIC_CAST(PRUint32, ((2 * mDynamicIntervalSwitchThreshold) +
|
||||
NS_DELAY_FOR_WINDOW_CREATION));
|
||||
|
||||
if ((currentTime - mBeginLoadTime) > delayBeforeLoweringThreshold) {
|
||||
if ((currentTime - eventTime) <
|
||||
NS_STATIC_CAST(PRUint32, mDynamicIntervalSwitchThreshold)) {
|
||||
|
||||
if (!mDynamicLowerValue) {
|
||||
// lower the dynamic values to favor application
|
||||
// responsiveness over page load time.
|
||||
mDynamicLowerValue = PR_TRUE;
|
||||
// Set the performance hint to prevent event starvation when
|
||||
// dispatching PLEvents. This improves application responsiveness
|
||||
// during page loads.
|
||||
FavorPerformanceHint(PR_FALSE, 0);
|
||||
}
|
||||
|
||||
}
|
||||
else if (mDynamicLowerValue) {
|
||||
// raise the content notification and MaxTokenProcessing time
|
||||
// to favor overall page load speed over responsiveness.
|
||||
mDynamicLowerValue = PR_FALSE;
|
||||
// Reset the hint that to favoring performance for PLEvent dispatch.
|
||||
FavorPerformanceHint(PR_TRUE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((currentTime - mDelayTimerStart) >
|
||||
NS_STATIC_CAST(PRUint32, GetMaxTokenProcessingTime())) {
|
||||
return NS_ERROR_HTMLPARSER_INTERRUPTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
nsContentSink::FavorPerformanceHint(PRBool perfOverStarvation, PRUint32 starvationDelay)
|
||||
{
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
if (appShell)
|
||||
appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay);
|
||||
}
|
||||
|
||||
void
|
||||
nsContentSink::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
|
||||
{
|
||||
// If we're in a script and we didn't do the notification,
|
||||
// something else in the script processing caused the
|
||||
// notification to occur. Since this could result in frame
|
||||
// creation, make sure we've flushed everything before we
|
||||
// continue.
|
||||
|
||||
// Note that UPDATE_CONTENT_STATE notifications never cause
|
||||
// synchronous frame construction, so we never have to worry about
|
||||
// them here. The code that handles the async event these
|
||||
// notifications post will flush us out if it needs to.
|
||||
|
||||
// Also, if this is not an UPDATE_CONTENT_STATE notification,
|
||||
// increment mInNotification to make sure we don't flush again until
|
||||
// the end of this update, even if nested updates or
|
||||
// FlushPendingNotifications calls happen during it.
|
||||
NS_ASSERTION(aUpdateType && (aUpdateType & UPDATE_ALL) == aUpdateType,
|
||||
"Weird update type bitmask");
|
||||
if (aUpdateType != UPDATE_CONTENT_STATE && !mInNotification++) {
|
||||
FlushTags();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsContentSink::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
|
||||
{
|
||||
// If we're in a script and we didn't do the notification,
|
||||
// something else in the script processing caused the
|
||||
// notification to occur. Update our notion of how much
|
||||
// has been flushed to include any new content if ending
|
||||
// this update leaves us not inside a notification. Note that we
|
||||
// exclude UPDATE_CONTENT_STATE notifications here, since those
|
||||
// never affect the frame model directly while inside the
|
||||
// notification.
|
||||
NS_ASSERTION(aUpdateType && (aUpdateType & UPDATE_ALL) == aUpdateType,
|
||||
"Weird update type bitmask");
|
||||
if (aUpdateType != UPDATE_CONTENT_STATE && !--mInNotification) {
|
||||
UpdateChildCounts();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentSink::DidBuildModelImpl(void)
|
||||
{
|
||||
if (mDocument && mDocument->GetDocumentTitle().IsVoid()) {
|
||||
nsCOMPtr<nsIDOMNSDocument> dom_doc(do_QueryInterface(mDocument));
|
||||
dom_doc->SetTitle(EmptyString());
|
||||
}
|
||||
|
||||
// Cancel a timer if we had one out there
|
||||
if (mNotificationTimer) {
|
||||
SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
|
||||
("nsContentSink::DidBuildModel: canceling notification "
|
||||
"timeout"));
|
||||
mNotificationTimer->Cancel();
|
||||
mNotificationTimer = 0;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentSink::DropParserAndPerfHint(void)
|
||||
{
|
||||
// Ref. Bug 49115
|
||||
// Do this hack to make sure that the parser
|
||||
// doesn't get destroyed, accidently, before
|
||||
// the circularity, between sink & parser, is
|
||||
// actually borken.
|
||||
nsCOMPtr<nsIParser> kungFuDeathGrip(mParser);
|
||||
|
||||
// Drop our reference to the parser to get rid of a circular
|
||||
// reference.
|
||||
mParser = nsnull;
|
||||
|
||||
if (mDynamicLowerValue) {
|
||||
// Reset the performance hint which was set to FALSE
|
||||
// when mDynamicLowerValue was set.
|
||||
FavorPerformanceHint(PR_TRUE, 0);
|
||||
}
|
||||
|
||||
if (mCanInterruptParser) {
|
||||
// Note: Don't return value from RemoveDummyParserRequest,
|
||||
// If RemoveDummyParserRequests fails it should not affect
|
||||
// DidBuildModel. The remove can fail if the parser request
|
||||
// was already removed by a DummyParserRequest::Cancel
|
||||
RemoveDummyParserRequest();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentSink::WillProcessTokensImpl(void)
|
||||
{
|
||||
if (mCanInterruptParser) {
|
||||
mDelayTimerStart = PR_IntervalToMicroseconds(PR_IntervalNow());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentSink::WillBuildModelImpl()
|
||||
{
|
||||
if (mCanInterruptParser) {
|
||||
nsresult rv = AddDummyParserRequest();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Adding dummy parser request failed");
|
||||
|
||||
// Don't return the error result, just reset flag which
|
||||
// indicates that it can interrupt parsing. If
|
||||
// AddDummyParserRequests fails it should not affect
|
||||
// WillBuildModel.
|
||||
mCanInterruptParser = PR_FALSE;
|
||||
}
|
||||
|
||||
mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow());
|
||||
}
|
||||
|
||||
mScrolledToRefAlready = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If the content sink can interrupt the parser (@see mCanInteruptParsing)
|
||||
// then it needs to schedule a dummy parser request to delay the document
|
||||
// from firing onload handlers and other document done actions until all of the
|
||||
// parsing has completed.
|
||||
|
||||
nsresult
|
||||
nsContentSink::AddDummyParserRequest(void)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
NS_ASSERTION(!mDummyParserRequest, "Already have a dummy parser request");
|
||||
|
||||
mDummyParserRequest = new DummyParserRequest(this);
|
||||
if (!mDummyParserRequest) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
if (mDocument) {
|
||||
loadGroup = mDocument->GetDocumentLoadGroup();
|
||||
}
|
||||
|
||||
if (loadGroup) {
|
||||
rv = mDummyParserRequest->SetLoadGroup(loadGroup);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = loadGroup->AddRequest(mDummyParserRequest, nsnull);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentSink::RemoveDummyParserRequest(void)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
if (mDocument) {
|
||||
loadGroup = mDocument->GetDocumentLoadGroup();
|
||||
}
|
||||
|
||||
if (loadGroup && mDummyParserRequest) {
|
||||
rv = loadGroup->RemoveRequest(mDummyParserRequest, nsnull, NS_OK);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mDummyParserRequest = nsnull;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@ -55,6 +55,13 @@
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsStubDocumentObserver.h"
|
||||
#include "nsIParserService.h"
|
||||
#include "nsIContentSink.h"
|
||||
#include "prlog.h"
|
||||
#include "nsIRequest.h"
|
||||
|
||||
|
||||
class nsIDocument;
|
||||
class nsIURI;
|
||||
@ -68,19 +75,75 @@ class nsIContent;
|
||||
class nsIViewManager;
|
||||
class nsNodeInfoManager;
|
||||
|
||||
#ifdef NS_DEBUG
|
||||
|
||||
extern PRLogModuleInfo* gContentSinkLogModuleInfo;
|
||||
|
||||
#define SINK_TRACE_CALLS 0x1
|
||||
#define SINK_TRACE_REFLOW 0x2
|
||||
#define SINK_ALWAYS_REFLOW 0x4
|
||||
|
||||
#define SINK_LOG_TEST(_lm, _bit) (PRIntn((_lm)->level) & (_bit))
|
||||
|
||||
#define SINK_TRACE(_lm, _bit, _args) \
|
||||
PR_BEGIN_MACRO \
|
||||
if (SINK_LOG_TEST(_lm, _bit)) { \
|
||||
PR_LogPrint _args; \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
#else
|
||||
#define SINK_TRACE(_lm, _bit, _args)
|
||||
#endif
|
||||
|
||||
#undef SINK_NO_INCREMENTAL
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// 1/2 second fudge factor for window creation
|
||||
#define NS_DELAY_FOR_WINDOW_CREATION 500000
|
||||
|
||||
// 200 determined empirically to provide good user response without
|
||||
// sampling the clock too often.
|
||||
#define NS_MAX_TOKENS_DEFLECTED_IN_LOW_FREQ_MODE 200
|
||||
|
||||
class nsContentSink : public nsICSSLoaderObserver,
|
||||
public nsIScriptLoaderObserver,
|
||||
public nsSupportsWeakReference
|
||||
public nsSupportsWeakReference,
|
||||
public nsStubDocumentObserver,
|
||||
public nsITimerCallback
|
||||
{
|
||||
friend class DummyParserRequest;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISCRIPTLOADEROBSERVER
|
||||
|
||||
|
||||
// nsITimerCallback
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
// nsICSSLoaderObserver
|
||||
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate,
|
||||
nsresult aStatus);
|
||||
|
||||
nsresult ProcessMETATag(nsIContent* aContent);
|
||||
|
||||
// nsIContentSink impl
|
||||
NS_IMETHOD WillInterruptImpl(void);
|
||||
NS_IMETHOD WillResumeImpl(void);
|
||||
NS_IMETHOD DidProcessATokenImpl(void);
|
||||
NS_IMETHOD WillBuildModelImpl(void);
|
||||
NS_IMETHOD DidBuildModelImpl(void);
|
||||
NS_IMETHOD DropParserAndPerfHint(void);
|
||||
NS_IMETHOD WillProcessTokensImpl(void);
|
||||
|
||||
void NotifyAppend(nsIContent* aContent, PRUint32 aStartIndex);
|
||||
|
||||
// nsIDocumentObserver
|
||||
virtual void BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType);
|
||||
virtual void EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType);
|
||||
|
||||
virtual void UpdateChildCounts() = 0;
|
||||
|
||||
protected:
|
||||
nsContentSink();
|
||||
virtual ~nsContentSink();
|
||||
@ -110,10 +173,43 @@ protected:
|
||||
nsresult RefreshIfEnabled(nsIViewManager* vm);
|
||||
void StartLayout(PRBool aIsFrameset);
|
||||
|
||||
PRBool IsTimeToNotify();
|
||||
|
||||
void
|
||||
FavorPerformanceHint(PRBool perfOverStarvation, PRUint32 starvationDelay);
|
||||
|
||||
inline PRInt32 GetNotificationInterval()
|
||||
{
|
||||
if (mDynamicLowerValue) {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
return mNotificationInterval;
|
||||
}
|
||||
|
||||
inline PRInt32 GetMaxTokenProcessingTime()
|
||||
{
|
||||
if (mDynamicLowerValue) {
|
||||
return 3000;
|
||||
}
|
||||
|
||||
return mMaxTokenProcessingTime;
|
||||
}
|
||||
|
||||
// Overridable hooks into script evaluation
|
||||
virtual void PreEvaluateScript() {return;}
|
||||
virtual void PostEvaluateScript(nsIScriptElement *aElement) {return;}
|
||||
|
||||
virtual nsresult FlushTags() = 0;
|
||||
|
||||
virtual void TryToScrollToRef()
|
||||
{
|
||||
}
|
||||
|
||||
// CanInterrupt parsing related routines
|
||||
nsresult AddDummyParserRequest(void);
|
||||
nsresult RemoveDummyParserRequest(void);
|
||||
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
nsCOMPtr<nsIParser> mParser;
|
||||
nsCOMPtr<nsIURI> mDocumentURI;
|
||||
@ -125,6 +221,63 @@ protected:
|
||||
nsCOMArray<nsIScriptElement> mScriptElements;
|
||||
|
||||
nsCString mRef; // ScrollTo #ref
|
||||
|
||||
// back off timer notification after count
|
||||
PRInt32 mBackoffCount;
|
||||
|
||||
// Notification interval in microseconds
|
||||
PRInt32 mNotificationInterval;
|
||||
|
||||
// Time of last notification
|
||||
PRTime mLastNotificationTime;
|
||||
|
||||
// Timer used for notification
|
||||
nsCOMPtr<nsITimer> mNotificationTimer;
|
||||
|
||||
// Do we notify based on time?
|
||||
PRPackedBool mNotifyOnTimer;
|
||||
|
||||
PRPackedBool mLayoutStarted;
|
||||
PRPackedBool mScrolledToRefAlready;
|
||||
|
||||
PRUint8 mScriptEnabled : 1;
|
||||
PRUint8 mFramesEnabled : 1;
|
||||
PRUint8 mCanInterruptParser : 1;
|
||||
PRUint8 mDynamicLowerValue : 1;
|
||||
PRUint8 mFormOnStack : 1;
|
||||
PRUint8 mParsing : 1;
|
||||
PRUint8 mDroppedTimer : 1;
|
||||
|
||||
// -- Can interrupt parsing members --
|
||||
PRUint32 mDelayTimerStart;
|
||||
|
||||
// Interrupt parsing during token procesing after # of microseconds
|
||||
PRInt32 mMaxTokenProcessingTime;
|
||||
|
||||
// Switch between intervals when time is exceeded
|
||||
PRInt32 mDynamicIntervalSwitchThreshold;
|
||||
|
||||
PRInt32 mBeginLoadTime;
|
||||
|
||||
// Last mouse event or keyboard event time sampled by the content
|
||||
// sink
|
||||
PRUint32 mLastSampledUserEventTime;
|
||||
|
||||
// The number of tokens that have been processed while in the low
|
||||
// frequency parser interrupt mode without falling through to the
|
||||
// logic which decides whether to switch to the high frequency
|
||||
// parser interrupt mode.
|
||||
PRUint8 mDeflectedCount;
|
||||
|
||||
// Boolean indicating whether we've notified insertion of our root content
|
||||
// yet. We want to make sure to only do this once.
|
||||
PRPackedBool mNotifiedRootInsertion;
|
||||
|
||||
PRInt32 mInMonolithicContainer;
|
||||
|
||||
PRInt32 mInNotification;
|
||||
|
||||
nsCOMPtr<nsIRequest> mDummyParserRequest;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -141,6 +141,7 @@ static NS_DEFINE_CID(kDOMEventGroupCID, NS_DOMEVENTGROUP_CID);
|
||||
|
||||
#include "nsICharsetAlias.h"
|
||||
#include "nsIParser.h"
|
||||
#include "nsIContentSink.h"
|
||||
|
||||
#include "nsDateTimeFormatCID.h"
|
||||
#include "nsIDateTimeFormat.h"
|
||||
@ -4586,6 +4587,16 @@ nsDocument::CreateEventGroup(nsIDOMEventGroup **aInstancePtrResult)
|
||||
void
|
||||
nsDocument::FlushPendingNotifications(mozFlushType aType)
|
||||
{
|
||||
// Determine if it is safe to flush the sink notifications
|
||||
// by determining if it safe to flush all the presshells.
|
||||
if ((aType & Flush_Content) && mParser &&
|
||||
(!(aType & Flush_SinkNotifications) || IsSafeToFlush())) {
|
||||
nsCOMPtr<nsIContentSink> sink = mParser->GetContentSink();
|
||||
if (sink) {
|
||||
sink->FlushPendingNotifications(aType);
|
||||
}
|
||||
}
|
||||
|
||||
nsPIDOMWindow *window = GetWindow();
|
||||
|
||||
if (aType == (aType & (Flush_Content | Flush_SinkNotifications)) ||
|
||||
|
||||
@ -95,6 +95,7 @@ public:
|
||||
nsAString& aStr);
|
||||
|
||||
// nsIContentSink
|
||||
NS_IMETHOD WillTokenize(void) { return NS_OK; }
|
||||
NS_IMETHOD WillBuildModel(void) { return NS_OK; }
|
||||
NS_IMETHOD DidBuildModel(void) { return NS_OK; }
|
||||
NS_IMETHOD WillInterrupt(void) { return NS_OK; }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1319,22 +1319,6 @@ nsHTMLDocument::AttributeChanged(nsIDocument* aDocument,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLDocument::FlushPendingNotifications(mozFlushType aType)
|
||||
{
|
||||
// Determine if it is safe to flush the sink notifications
|
||||
// by determining if it safe to flush all the presshells.
|
||||
if ((aType & Flush_Content) && mParser &&
|
||||
(!(aType & Flush_SinkNotifications) || IsSafeToFlush())) {
|
||||
nsCOMPtr<nsIContentSink> sink = mParser->GetContentSink();
|
||||
if (sink) {
|
||||
sink->FlushPendingNotifications(aType);
|
||||
}
|
||||
}
|
||||
|
||||
nsDocument::FlushPendingNotifications(aType);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHTMLDocument::IsCaseSensitive()
|
||||
{
|
||||
|
||||
@ -125,8 +125,6 @@ public:
|
||||
PRInt32 aNameSpaceID,
|
||||
nsIAtom* aAttribute);
|
||||
|
||||
virtual void FlushPendingNotifications(mozFlushType aType);
|
||||
|
||||
virtual PRBool IsCaseSensitive();
|
||||
|
||||
// nsIMutationObserver
|
||||
|
||||
@ -84,6 +84,7 @@ public:
|
||||
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
|
||||
|
||||
// nsIContentSink
|
||||
NS_IMETHOD WillTokenize(void) { return NS_OK; }
|
||||
NS_IMETHOD WillBuildModel(void);
|
||||
NS_IMETHOD DidBuildModel(void);
|
||||
NS_IMETHOD WillInterrupt(void);
|
||||
|
||||
@ -109,6 +109,12 @@ nsXBLContentSink::Init(nsIDocument* aDoc,
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLContentSink::MaybeStartLayout()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXBLContentSink::FlushText(PRBool aCreateTextNode,
|
||||
PRBool* aDidFlush)
|
||||
|
||||
@ -110,6 +110,8 @@ public:
|
||||
|
||||
protected:
|
||||
// nsXMLContentSink overrides
|
||||
void MaybeStartLayout();
|
||||
|
||||
PRBool OnOpenContainer(const PRUnichar **aAtts,
|
||||
PRUint32 aAttsCount,
|
||||
PRInt32 aNameSpaceID,
|
||||
|
||||
@ -70,6 +70,7 @@ REQUIRES = xpcom \
|
||||
unicharutil \
|
||||
windowwatcher \
|
||||
locale \
|
||||
util \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
*
|
||||
* 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"),
|
||||
@ -95,6 +96,8 @@
|
||||
#include "nsContentErrors.h"
|
||||
#include "nsIDOMProcessingInstruction.h"
|
||||
#include "nsNodeUtils.h"
|
||||
#include "nsTimer.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
|
||||
#ifdef MOZ_SVG
|
||||
#include "nsGUIEvent.h"
|
||||
@ -140,6 +143,7 @@ nsXMLContentSink::nsXMLContentSink()
|
||||
mText(nsnull),
|
||||
mTextLength(0),
|
||||
mTextSize(0),
|
||||
mNotifyLevel(0),
|
||||
mConstrainSize(PR_TRUE),
|
||||
mInTitle(PR_FALSE),
|
||||
mPrettyPrintXML(PR_TRUE),
|
||||
@ -152,6 +156,12 @@ nsXMLContentSink::nsXMLContentSink()
|
||||
|
||||
nsXMLContentSink::~nsXMLContentSink()
|
||||
{
|
||||
if (mDocument) {
|
||||
// Remove ourselves just to be safe, though we really should have
|
||||
// been removed in DidBuildModel if everything worked right.
|
||||
mDocument->RemoveObserver(this);
|
||||
}
|
||||
|
||||
NS_IF_RELEASE(mDocElement);
|
||||
if (mText) {
|
||||
PR_Free(mText); // Doesn't null out, unlike PR_FREEIF
|
||||
@ -164,29 +174,50 @@ nsXMLContentSink::Init(nsIDocument* aDoc,
|
||||
nsISupports* aContainer,
|
||||
nsIChannel* aChannel)
|
||||
{
|
||||
MOZ_TIMER_DEBUGLOG(("Reset and start: nsXMLContentSink::Init(), this=%p\n",
|
||||
this));
|
||||
MOZ_TIMER_RESET(mWatch);
|
||||
MOZ_TIMER_START(mWatch);
|
||||
|
||||
nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aDoc->AddObserver(this);
|
||||
|
||||
if (!mDocShell) {
|
||||
mPrettyPrintXML = PR_FALSE;
|
||||
}
|
||||
|
||||
mState = eXMLContentSinkState_InProlog;
|
||||
mDocElement = nsnull;
|
||||
|
||||
MOZ_TIMER_DEBUGLOG(("Stop: nsXMLContentSink::Init()\n"));
|
||||
MOZ_TIMER_STOP(mWatch);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED4(nsXMLContentSink,
|
||||
NS_IMPL_ISUPPORTS_INHERITED7(nsXMLContentSink,
|
||||
nsContentSink,
|
||||
nsIContentSink,
|
||||
nsIXMLContentSink,
|
||||
nsIExpatSink,
|
||||
nsITimerCallback,
|
||||
nsIDocumentObserver,
|
||||
nsIMutationObserver,
|
||||
nsITransformObserver)
|
||||
|
||||
// nsIContentSink
|
||||
NS_IMETHODIMP
|
||||
nsXMLContentSink::WillTokenize(void)
|
||||
{
|
||||
return WillProcessTokensImpl();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXMLContentSink::WillBuildModel(void)
|
||||
{
|
||||
WillBuildModelImpl();
|
||||
|
||||
// Notify document that the load is beginning
|
||||
mDocument->BeginLoad();
|
||||
|
||||
@ -202,16 +233,25 @@ nsXMLContentSink::WillBuildModel(void)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsXMLContentSink::CanStillPrettyPrint()
|
||||
{
|
||||
return mPrettyPrintXML &&
|
||||
(!mPrettyPrintHasFactoredElements || mPrettyPrintHasSpecialRoot);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXMLContentSink::MaybePrettyPrint()
|
||||
{
|
||||
if (!mPrettyPrintXML || (mPrettyPrintHasFactoredElements &&
|
||||
!mPrettyPrintHasSpecialRoot)) {
|
||||
if (!CanStillPrettyPrint()) {
|
||||
mPrettyPrintXML = PR_FALSE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// stop observing in order to avoid crashing when replacing content
|
||||
mDocument->RemoveObserver(this);
|
||||
|
||||
// Reenable the CSSLoader so that the prettyprinting stylesheets can load
|
||||
if (mCSSLoader) {
|
||||
mCSSLoader->SetEnabled(PR_TRUE);
|
||||
@ -269,12 +309,11 @@ CheckXSLTParamPI(nsIDOMProcessingInstruction* aPi,
|
||||
NS_IMETHODIMP
|
||||
nsXMLContentSink::DidBuildModel()
|
||||
{
|
||||
if (mDocument && mDocument->GetDocumentTitle().IsVoid()) {
|
||||
nsCOMPtr<nsIDOMNSDocument> dom_doc(do_QueryInterface(mDocument));
|
||||
dom_doc->SetTitle(EmptyString());
|
||||
}
|
||||
DidBuildModelImpl();
|
||||
|
||||
if (mXSLTProcessor) {
|
||||
// stop observing in order to avoid crashing when replacing content
|
||||
mDocument->RemoveObserver(this);
|
||||
|
||||
// Check for xslt-param and xslt-param-namespace PIs
|
||||
PRUint32 i;
|
||||
@ -310,10 +349,6 @@ nsXMLContentSink::DidBuildModel()
|
||||
// documentElement?
|
||||
NS_ASSERTION(mDocument->IndexOf(mDocElement) != -1,
|
||||
"mDocElement not in doc?");
|
||||
|
||||
mozAutoDocUpdate docUpdate(mDocument, UPDATE_CONTENT_MODEL, PR_TRUE);
|
||||
nsNodeUtils::ContentInserted(mDocument, mDocElement,
|
||||
mDocument->IndexOf(mDocElement));
|
||||
}
|
||||
|
||||
// Check if we want to prettyprint
|
||||
@ -332,12 +367,12 @@ nsXMLContentSink::DidBuildModel()
|
||||
ScrollToRef(PR_TRUE);
|
||||
#endif
|
||||
|
||||
mDocument->RemoveObserver(this);
|
||||
|
||||
mDocument->EndLoad();
|
||||
}
|
||||
|
||||
// Drop our reference to the parser to get rid of a circular
|
||||
// reference.
|
||||
mParser = nsnull;
|
||||
DropParserAndPerfHint();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -429,13 +464,13 @@ nsXMLContentSink::OnTransformDone(nsresult aResult,
|
||||
NS_IMETHODIMP
|
||||
nsXMLContentSink::WillInterrupt(void)
|
||||
{
|
||||
return NS_OK;
|
||||
return WillInterruptImpl();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXMLContentSink::WillResume(void)
|
||||
{
|
||||
return NS_OK;
|
||||
return WillResumeImpl();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -547,6 +582,10 @@ nsXMLContentSink::CloseElement(nsIContent* aContent)
|
||||
aContent->DoneAddingChildren(PR_FALSE);
|
||||
}
|
||||
|
||||
if (IsMonolithicContainer(nodeInfo)) {
|
||||
mInMonolithicContainer--;
|
||||
}
|
||||
|
||||
if (!nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) &&
|
||||
!nodeInfo->NamespaceEquals(kNameSpaceID_SVG)) {
|
||||
return NS_OK;
|
||||
@ -641,7 +680,6 @@ nsXMLContentSink::AddContentAsLeaf(nsIContent *aContent)
|
||||
result = parent->AppendChildTo(aContent, PR_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -822,47 +860,61 @@ nsXMLContentSink::FlushText(PRBool aCreateTextNode, PRBool* aDidFlush)
|
||||
nsIContent*
|
||||
nsXMLContentSink::GetCurrentContent()
|
||||
{
|
||||
PRInt32 count = mContentStack.Count();
|
||||
|
||||
if (count == 0) {
|
||||
if (mContentStack.Length() == 0) {
|
||||
return nsnull;
|
||||
}
|
||||
return GetCurrentStackNode().mContent;
|
||||
}
|
||||
|
||||
NS_ASSERTION(count > 0, "Bogus Count()");
|
||||
|
||||
StackNode &
|
||||
nsXMLContentSink::GetCurrentStackNode()
|
||||
{
|
||||
PRInt32 count = mContentStack.Length();
|
||||
NS_ASSERTION(count > 0, "Bogus Length()");
|
||||
return mContentStack[count-1];
|
||||
}
|
||||
|
||||
PRInt32
|
||||
|
||||
nsresult
|
||||
nsXMLContentSink::PushContent(nsIContent *aContent)
|
||||
{
|
||||
NS_PRECONDITION(aContent, "Null content being pushed!");
|
||||
mContentStack.AppendObject(aContent);
|
||||
return mContentStack.Count();
|
||||
StackNode *sn = mContentStack.AppendElement();
|
||||
NS_ENSURE_TRUE(sn, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
sn->mContent = aContent;
|
||||
sn->mNumFlushed = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIContent>
|
||||
void
|
||||
nsXMLContentSink::PopContent()
|
||||
{
|
||||
PRInt32 count = mContentStack.Count();
|
||||
{
|
||||
PRInt32 count = mContentStack.Length();
|
||||
|
||||
if (count == 0) {
|
||||
NS_WARNING("Popping empty stack");
|
||||
return nsnull;
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ASSERTION(count > 0, "Bogus Count()");
|
||||
mContentStack.RemoveElementAt(count - 1);
|
||||
}
|
||||
|
||||
nsIContent* content = mContentStack[count - 1];
|
||||
NS_IF_ADDREF(content);
|
||||
mContentStack.RemoveObjectAt(count - 1);
|
||||
|
||||
return content;
|
||||
void
|
||||
nsXMLContentSink::MaybeStartLayout()
|
||||
{
|
||||
if (mLayoutStarted || mXSLTProcessor || CanStillPrettyPrint()) {
|
||||
return;
|
||||
}
|
||||
StartLayout();
|
||||
}
|
||||
|
||||
void
|
||||
nsXMLContentSink::StartLayout()
|
||||
{
|
||||
if (mLayoutStarted) {
|
||||
return;
|
||||
}
|
||||
PRBool topLevelFrameset = PR_FALSE;
|
||||
nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(mDocShell));
|
||||
if (docShellAsItem) {
|
||||
@ -933,8 +985,7 @@ nsXMLContentSink::SetDocElement(PRInt32 aNameSpaceID,
|
||||
|
||||
mDocElement = aContent;
|
||||
NS_ADDREF(mDocElement);
|
||||
|
||||
nsresult rv = mDocument->AppendChildTo(mDocElement, PR_FALSE);
|
||||
nsresult rv = mDocument->AppendChildTo(mDocElement, PR_TRUE);
|
||||
if (NS_FAILED(rv)) {
|
||||
// If we return PR_FALSE here, the caller will bail out because it won't
|
||||
// find a parent content node to append to, which is fine.
|
||||
@ -965,6 +1016,7 @@ nsXMLContentSink::HandleStartElement(const PRUnichar *aName,
|
||||
PR_ASSERT(eXMLContentSinkState_InEpilog != mState);
|
||||
|
||||
FlushText();
|
||||
DidAddContent();
|
||||
|
||||
mState = eXMLContentSinkState_InDocumentElement;
|
||||
|
||||
@ -1014,7 +1066,8 @@ nsXMLContentSink::HandleStartElement(const PRUnichar *aName,
|
||||
parent->AppendChildTo(content, PR_FALSE);
|
||||
}
|
||||
|
||||
PushContent(content);
|
||||
result = PushContent(content);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
}
|
||||
|
||||
// Some HTML nodes need DoneCreatingElement() called to initialize
|
||||
@ -1025,7 +1078,13 @@ nsXMLContentSink::HandleStartElement(const PRUnichar *aName,
|
||||
content->DoneCreatingElement();
|
||||
}
|
||||
|
||||
return result;
|
||||
if (IsMonolithicContainer(nodeInfo)) {
|
||||
mInMonolithicContainer++;
|
||||
}
|
||||
|
||||
MaybeStartLayout();
|
||||
|
||||
return NS_SUCCEEDED(result) ? DidProcessATokenImpl() : result;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1040,7 +1099,13 @@ nsXMLContentSink::HandleEndElement(const PRUnichar *aName)
|
||||
|
||||
FlushText();
|
||||
|
||||
nsCOMPtr<nsIContent> content = PopContent();
|
||||
StackNode & sn = GetCurrentStackNode();
|
||||
|
||||
nsCOMPtr<nsIContent> content;
|
||||
sn.mContent.swap(content);
|
||||
PRUint32 numFlushed = sn.mNumFlushed;
|
||||
|
||||
PopContent();
|
||||
NS_ASSERTION(content, "failed to pop content");
|
||||
#ifdef DEBUG
|
||||
// Check that we're closing the right thing
|
||||
@ -1073,7 +1138,16 @@ nsXMLContentSink::HandleEndElement(const PRUnichar *aName)
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
PRInt32 stackLen = mContentStack.Length();
|
||||
if (mNotifyLevel >= stackLen) {
|
||||
if (numFlushed < content->GetChildCount()) {
|
||||
NotifyAppend(content, numFlushed);
|
||||
}
|
||||
mNotifyLevel = stackLen - 1;
|
||||
}
|
||||
DidAddContent();
|
||||
|
||||
return NS_SUCCEEDED(result) ? DidProcessATokenImpl() : result;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1086,9 +1160,10 @@ nsXMLContentSink::HandleComment(const PRUnichar *aName)
|
||||
if (comment) {
|
||||
comment->SetText(nsDependentString(aName), PR_FALSE);
|
||||
rv = AddContentAsLeaf(comment);
|
||||
DidAddContent();
|
||||
}
|
||||
|
||||
return rv;
|
||||
return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1112,9 +1187,10 @@ nsXMLContentSink::HandleCDataSection(const PRUnichar *aData,
|
||||
if (cdata) {
|
||||
cdata->SetText(aData, aLength, PR_FALSE);
|
||||
rv = AddContentAsLeaf(cdata);
|
||||
DidAddContent();
|
||||
}
|
||||
|
||||
return rv;
|
||||
return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1166,18 +1242,21 @@ nsXMLContentSink::HandleDoctypeDecl(const nsAString & aSubset,
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(docType);
|
||||
NS_ASSERTION(content, "doctype isn't content?");
|
||||
|
||||
return mDocument->AppendChildTo(content, PR_FALSE);
|
||||
rv = mDocument->AppendChildTo(content, PR_FALSE);
|
||||
DidAddContent();
|
||||
return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXMLContentSink::HandleCharacterData(const PRUnichar *aData,
|
||||
PRUint32 aLength)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
if (aData && mState != eXMLContentSinkState_InProlog &&
|
||||
mState != eXMLContentSinkState_InEpilog) {
|
||||
return AddText(aData, aLength);
|
||||
rv = AddText(aData, aLength);
|
||||
}
|
||||
return NS_OK;
|
||||
return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1204,6 +1283,7 @@ nsXMLContentSink::HandleProcessingInstruction(const PRUnichar *aTarget,
|
||||
|
||||
rv = AddContentAsLeaf(node);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
DidAddContent();
|
||||
|
||||
if (ssle) {
|
||||
ssle->SetEnableUpdates(PR_TRUE);
|
||||
@ -1224,7 +1304,7 @@ nsXMLContentSink::HandleProcessingInstruction(const PRUnichar *aTarget,
|
||||
if (mState != eXMLContentSinkState_InProlog ||
|
||||
!target.EqualsLiteral("xml-stylesheet") ||
|
||||
type.LowerCaseEqualsLiteral("text/css")) {
|
||||
return NS_OK;
|
||||
return DidProcessATokenImpl();
|
||||
}
|
||||
|
||||
nsAutoString href, title, media;
|
||||
@ -1233,10 +1313,11 @@ nsXMLContentSink::HandleProcessingInstruction(const PRUnichar *aTarget,
|
||||
|
||||
// If there was no href, we can't do anything with this PI
|
||||
if (href.IsEmpty()) {
|
||||
return NS_OK;
|
||||
return DidProcessATokenImpl();
|
||||
}
|
||||
|
||||
return ProcessStyleLink(node, href, isAlternate, title, type, media);
|
||||
rv = ProcessStyleLink(node, href, isAlternate, title, type, media);
|
||||
return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
|
||||
}
|
||||
|
||||
/* static */
|
||||
@ -1269,7 +1350,7 @@ nsXMLContentSink::HandleXMLDeclaration(const PRUnichar *aVersion,
|
||||
{
|
||||
mDocument->SetXMLDeclaration(aVersion, aEncoding, aStandalone);
|
||||
|
||||
return NS_OK;
|
||||
return DidProcessATokenImpl();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1288,6 +1369,11 @@ nsXMLContentSink::ReportError(const PRUnichar* aErrorText,
|
||||
|
||||
mState = eXMLContentSinkState_InProlog;
|
||||
|
||||
// XXX need to stop scripts here -- hsivonen
|
||||
|
||||
// stop observing in order to avoid crashing when removing content
|
||||
mDocument->RemoveObserver(this);
|
||||
|
||||
// Clear the current content and
|
||||
// prepare to set <parsererror> as the document root
|
||||
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mDocument));
|
||||
@ -1313,6 +1399,10 @@ nsXMLContentSink::ReportError(const PRUnichar* aErrorText,
|
||||
mXSLTProcessor = nsnull;
|
||||
}
|
||||
|
||||
// release the nodes on stack
|
||||
mContentStack.Clear();
|
||||
mNotifyLevel = 0;
|
||||
|
||||
const PRUnichar* noAtts[] = { 0, 0 };
|
||||
|
||||
NS_NAMED_LITERAL_STRING(errorNs,
|
||||
@ -1344,6 +1434,8 @@ nsXMLContentSink::ReportError(const PRUnichar* aErrorText,
|
||||
rv = HandleEndElement(parsererror.get());
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
FlushTags();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1427,6 +1519,7 @@ nsXMLContentSink::AddText(const PRUnichar* aText,
|
||||
}
|
||||
}
|
||||
}
|
||||
// Line breaks should not be normalized twice. See bug 343870.
|
||||
mTextLength +=
|
||||
nsContentUtils::CopyNewlineNormalizedUnicodeTo(str,
|
||||
offset,
|
||||
@ -1439,3 +1532,106 @@ nsXMLContentSink::AddText(const PRUnichar* aText,
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsXMLContentSink::FlushPendingNotifications(mozFlushType aType)
|
||||
{
|
||||
// Only flush tags if we're not doing the notification ourselves
|
||||
// (since we aren't reentrant)
|
||||
if (!mInNotification) {
|
||||
if (aType & Flush_SinkNotifications) {
|
||||
FlushTags();
|
||||
}
|
||||
else {
|
||||
FlushText();
|
||||
}
|
||||
if (aType & Flush_OnlyReflow) {
|
||||
// Make sure that layout has started so that the reflow flush
|
||||
// will actually happen.
|
||||
MaybeStartLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE!! Forked from SinkContext. Please keep in sync.
|
||||
*
|
||||
* Flush all elements that have been seen so far such that
|
||||
* they are visible in the tree. Specifically, make sure
|
||||
* that they are all added to their respective parents.
|
||||
* Also, do notification at the top for all content that
|
||||
* has been newly added so that the frame tree is complete.
|
||||
*/
|
||||
nsresult
|
||||
nsXMLContentSink::FlushTags()
|
||||
{
|
||||
// Don't release last text node in case we need to add to it again
|
||||
FlushText();
|
||||
|
||||
++mInNotification;
|
||||
mozAutoDocUpdate updateBatch(mDocument, UPDATE_CONTENT_MODEL, PR_TRUE);
|
||||
|
||||
// Start from the base of the stack (growing downward) and do
|
||||
// a notification from the node that is closest to the root of
|
||||
// tree for any content that has been added.
|
||||
|
||||
PRInt32 stackPos;
|
||||
PRInt32 stackLen = mContentStack.Length();
|
||||
PRBool flushed = PR_FALSE;
|
||||
PRUint32 childCount;
|
||||
nsIContent* content;
|
||||
|
||||
for (stackPos = 0; stackPos < stackLen; ++stackPos) {
|
||||
content = mContentStack[stackPos].mContent;
|
||||
childCount = content->GetChildCount();
|
||||
|
||||
if (!flushed && (mContentStack[stackPos].mNumFlushed < childCount)) {
|
||||
NotifyAppend(content, mContentStack[stackPos].mNumFlushed);
|
||||
flushed = PR_TRUE;
|
||||
}
|
||||
|
||||
mContentStack[stackPos].mNumFlushed = childCount;
|
||||
}
|
||||
mNotifyLevel = stackLen - 1;
|
||||
|
||||
--mInNotification;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE!! Forked from SinkContext. Please keep in sync.
|
||||
*/
|
||||
void
|
||||
nsXMLContentSink::UpdateChildCounts()
|
||||
{
|
||||
// Start from the top of the stack (growing upwards) and see if any
|
||||
// new content has been appended. If so, we recognize that reflows
|
||||
// have been generated for it and we should make sure that no
|
||||
// further reflows occur. Note that we have to include stackPos == 0
|
||||
// to properly notify on kids of <html>.
|
||||
PRInt32 stackLen = mContentStack.Length();
|
||||
PRInt32 stackPos = stackLen - 1;
|
||||
while (stackPos >= 0) {
|
||||
StackNode & node = mContentStack[stackPos];
|
||||
node.mNumFlushed = node.mContent->GetChildCount();
|
||||
|
||||
stackPos--;
|
||||
}
|
||||
mNotifyLevel = stackLen - 1;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsXMLContentSink::IsMonolithicContainer(nsINodeInfo* aNodeInfo)
|
||||
{
|
||||
return ((aNodeInfo->NamespaceID() == kNameSpaceID_XHTML &&
|
||||
(aNodeInfo->NameAtom() == nsGkAtoms::tr ||
|
||||
aNodeInfo->NameAtom() == nsGkAtoms::select ||
|
||||
aNodeInfo->NameAtom() == nsGkAtoms::object ||
|
||||
aNodeInfo->NameAtom() == nsGkAtoms::applet))
|
||||
#ifdef MOZ_MATHML
|
||||
|| (aNodeInfo->NamespaceID() == kNameSpaceID_MathML &&
|
||||
(aNodeInfo->NameAtom() == nsGkAtoms::math))
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
#include "nsIXMLContentSink.h"
|
||||
#include "nsIExpatSink.h"
|
||||
#include "nsIDocumentTransformer.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
|
||||
@ -59,6 +59,11 @@ typedef enum {
|
||||
eXMLContentSinkState_InEpilog
|
||||
} XMLContentSinkState;
|
||||
|
||||
struct StackNode {
|
||||
nsCOMPtr<nsIContent> mContent;
|
||||
PRUint32 mNumFlushed;
|
||||
};
|
||||
|
||||
class nsXMLContentSink : public nsContentSink,
|
||||
public nsIXMLContentSink,
|
||||
public nsITransformObserver,
|
||||
@ -79,12 +84,13 @@ public:
|
||||
NS_DECL_NSIEXPATSINK
|
||||
|
||||
// nsIContentSink
|
||||
NS_IMETHOD WillTokenize(void);
|
||||
NS_IMETHOD WillBuildModel(void);
|
||||
NS_IMETHOD DidBuildModel(void);
|
||||
NS_IMETHOD WillInterrupt(void);
|
||||
NS_IMETHOD WillResume(void);
|
||||
NS_IMETHOD SetParser(nsIParser* aParser);
|
||||
virtual void FlushPendingNotifications(mozFlushType aType) { }
|
||||
virtual void FlushPendingNotifications(mozFlushType aType);
|
||||
NS_IMETHOD SetDocumentCharset(nsACString& aCharset);
|
||||
virtual nsISupports *GetTarget();
|
||||
|
||||
@ -97,6 +103,7 @@ public:
|
||||
PRBool &aIsAlternate);
|
||||
|
||||
protected:
|
||||
virtual void MaybeStartLayout();
|
||||
void StartLayout();
|
||||
|
||||
virtual nsresult AddAttributes(const PRUnichar** aNode, nsIContent* aContent);
|
||||
@ -127,11 +134,22 @@ protected:
|
||||
nsresult AddContentAsLeaf(nsIContent *aContent);
|
||||
|
||||
nsIContent* GetCurrentContent();
|
||||
PRInt32 PushContent(nsIContent *aContent);
|
||||
already_AddRefed<nsIContent> PopContent();
|
||||
StackNode & GetCurrentStackNode();
|
||||
nsresult PushContent(nsIContent *aContent);
|
||||
void PopContent();
|
||||
|
||||
nsresult ProcessBASETag(nsIContent* aContent);
|
||||
|
||||
nsresult FlushTags();
|
||||
|
||||
void UpdateChildCounts();
|
||||
|
||||
void DidAddContent()
|
||||
{
|
||||
if (IsTimeToNotify()) {
|
||||
FlushTags();
|
||||
}
|
||||
}
|
||||
|
||||
// nsContentSink override
|
||||
virtual nsresult ProcessStyleLink(nsIContent* aElement,
|
||||
@ -143,8 +161,12 @@ protected:
|
||||
|
||||
nsresult LoadXSLStyleSheet(nsIURI* aUrl);
|
||||
|
||||
PRBool CanStillPrettyPrint();
|
||||
|
||||
nsresult MaybePrettyPrint();
|
||||
|
||||
PRBool IsMonolithicContainer(nsINodeInfo* aNodeInfo);
|
||||
|
||||
nsIContent* mDocElement;
|
||||
PRUnichar* mText;
|
||||
|
||||
@ -155,6 +177,8 @@ protected:
|
||||
PRInt32 mTextLength;
|
||||
PRInt32 mTextSize;
|
||||
|
||||
PRInt32 mNotifyLevel;
|
||||
|
||||
PRUint8 mConstrainSize : 1;
|
||||
PRUint8 mInTitle : 1;
|
||||
PRUint8 mPrettyPrintXML : 1;
|
||||
@ -164,7 +188,8 @@ protected:
|
||||
PRUint8 mAllowAutoXLinks : 1;
|
||||
PRUint8 unused : 1; // bit available if someone needs one
|
||||
|
||||
nsCOMArray<nsIContent> mContentStack;
|
||||
nsTArray<StackNode> mContentStack;
|
||||
|
||||
nsCOMPtr<nsIDocumentTransformer> mXSLTProcessor;
|
||||
};
|
||||
|
||||
|
||||
@ -92,6 +92,7 @@ public:
|
||||
NS_IMETHOD DidBuildModel();
|
||||
NS_IMETHOD SetDocumentCharset(nsACString& aCharset);
|
||||
virtual nsISupports *GetTarget();
|
||||
NS_IMETHOD DidProcessATokenImpl();
|
||||
|
||||
// nsIXMLContentSink
|
||||
|
||||
@ -111,6 +112,8 @@ protected:
|
||||
nsIContent** aResult, PRBool* aAppendContent);
|
||||
virtual nsresult CloseElement(nsIContent* aContent);
|
||||
|
||||
void MaybeStartLayout();
|
||||
|
||||
// nsContentSink overrides
|
||||
virtual nsresult ProcessStyleLink(nsIContent* aElement,
|
||||
const nsSubstring& aHref,
|
||||
@ -197,8 +200,7 @@ NS_IMETHODIMP
|
||||
nsXMLFragmentContentSink::DidBuildModel()
|
||||
{
|
||||
if (mAllContent) {
|
||||
// Need the nsCOMPtr to properly release
|
||||
nsCOMPtr<nsIContent> root = PopContent(); // remove mRoot pushed above
|
||||
PopContent(); // remove mRoot pushed above
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIParser> kungFuDeathGrip(mParser);
|
||||
@ -246,7 +248,7 @@ nsXMLFragmentContentSink::CreateElement(const PRUnichar** aAtts, PRUint32 aAttsC
|
||||
// When we aren't grabbing all of the content we, never open a doc
|
||||
// element, we run into trouble on the first element, so we don't append,
|
||||
// and simply push this onto the content stack.
|
||||
if (!mAllContent && mContentStack.Count() == 0) {
|
||||
if (!mAllContent && mContentStack.Length() == 0) {
|
||||
*aAppendContent = PR_FALSE;
|
||||
}
|
||||
|
||||
@ -260,6 +262,12 @@ nsXMLFragmentContentSink::CloseElement(nsIContent* aContent)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsXMLFragmentContentSink::MaybeStartLayout()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -422,13 +430,18 @@ nsXMLFragmentContentSink::DidBuildContent()
|
||||
if (!mParseError) {
|
||||
FlushText();
|
||||
}
|
||||
// Need the nsCOMPtr to properly release
|
||||
nsCOMPtr<nsIContent> root = PopContent();
|
||||
PopContent();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXMLFragmentContentSink::DidProcessATokenImpl()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXMLFragmentContentSink::IgnoreFirstContainer()
|
||||
{
|
||||
|
||||
@ -107,6 +107,7 @@ public:
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
// nsIContentSink
|
||||
NS_IMETHOD WillTokenize(void) { return NS_OK; }
|
||||
NS_IMETHOD WillBuildModel(void) { return NS_OK; }
|
||||
NS_IMETHOD DidBuildModel();
|
||||
NS_IMETHOD WillInterrupt(void) { return NS_OK; }
|
||||
|
||||
@ -123,6 +123,7 @@ public:
|
||||
NS_DECL_NSIEXPATSINK
|
||||
|
||||
// nsIContentSink
|
||||
NS_IMETHOD WillTokenize(void) { return NS_OK; }
|
||||
NS_IMETHOD WillBuildModel(void);
|
||||
NS_IMETHOD DidBuildModel(void);
|
||||
NS_IMETHOD WillInterrupt(void);
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
* 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 of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
@ -63,6 +64,17 @@ public:
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENT_SINK_IID)
|
||||
|
||||
/**
|
||||
* This method gets called before the nsParser calls tokenize.
|
||||
* This is needed because the XML side actually builds
|
||||
* the content model as part of the tokenization and
|
||||
* not on BuildModel(). The XML side can use this call
|
||||
* to do stuff that the HTML side does in WillProcessTokens().
|
||||
*
|
||||
* @update 2006-10-17 hsivonen
|
||||
*/
|
||||
NS_IMETHOD WillTokenize(void)=0;
|
||||
|
||||
/**
|
||||
* This method gets called when the parser begins the process
|
||||
* of building the content model via the content sink.
|
||||
|
||||
@ -85,6 +85,7 @@ public:
|
||||
NS_IMETHOD AddComment(const nsIParserNode& aNode);
|
||||
NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode);
|
||||
NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode);
|
||||
NS_IMETHOD WillTokenize(void) { return NS_OK; }
|
||||
NS_IMETHOD WillBuildModel(void) { return NS_OK; }
|
||||
NS_IMETHOD DidBuildModel(void) { return NS_OK; }
|
||||
NS_IMETHOD WillInterrupt(void) { return NS_OK; }
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
* 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 of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
@ -418,10 +419,11 @@ nsExpatDriver::HandleStartElement(const PRUnichar *aValue,
|
||||
}
|
||||
|
||||
if (mSink) {
|
||||
mSink->HandleStartElement(aValue, aAtts,
|
||||
attrArrayLength,
|
||||
XML_GetIdAttributeIndex(mExpatParser),
|
||||
XML_GetCurrentLineNumber(mExpatParser));
|
||||
mInternalState = mSink->
|
||||
HandleStartElement(aValue, aAtts, attrArrayLength,
|
||||
XML_GetIdAttributeIndex(mExpatParser),
|
||||
XML_GetCurrentLineNumber(mExpatParser));
|
||||
MaybeStopParser();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -432,9 +434,9 @@ nsExpatDriver::HandleEndElement(const PRUnichar *aValue)
|
||||
{
|
||||
NS_ASSERTION(mSink, "content sink not found!");
|
||||
|
||||
if (mSink &&
|
||||
mSink->HandleEndElement(aValue) == NS_ERROR_HTMLPARSER_BLOCK) {
|
||||
XML_StopParser(mExpatParser, XML_TRUE);
|
||||
if (mSink) {
|
||||
mInternalState = mSink->HandleEndElement(aValue);
|
||||
MaybeStopParser();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -451,6 +453,7 @@ nsExpatDriver::HandleCharacterData(const PRUnichar *aValue,
|
||||
}
|
||||
else if (mSink) {
|
||||
mInternalState = mSink->HandleCharacterData(aValue, aLength);
|
||||
MaybeStopParser();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -473,6 +476,7 @@ nsExpatDriver::HandleComment(const PRUnichar *aValue)
|
||||
}
|
||||
else if (mSink) {
|
||||
mInternalState = mSink->HandleComment(aValue);
|
||||
MaybeStopParser();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -497,10 +501,9 @@ nsExpatDriver::HandleProcessingInstruction(const PRUnichar *aTarget,
|
||||
mInternalSubset.Append(aData);
|
||||
mInternalSubset.AppendLiteral("?>");
|
||||
}
|
||||
else if (mSink &&
|
||||
mSink->HandleProcessingInstruction(aTarget, aData) ==
|
||||
NS_ERROR_HTMLPARSER_BLOCK) {
|
||||
XML_StopParser(mExpatParser, XML_TRUE);
|
||||
else if (mSink) {
|
||||
mInternalState = mSink->HandleProcessingInstruction(aTarget, aData);
|
||||
MaybeStopParser();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -511,7 +514,12 @@ nsExpatDriver::HandleXMLDeclaration(const PRUnichar *aVersion,
|
||||
const PRUnichar *aEncoding,
|
||||
PRInt32 aStandalone)
|
||||
{
|
||||
return mSink->HandleXMLDeclaration(aVersion, aEncoding, aStandalone);
|
||||
if (mSink) {
|
||||
mInternalState = mSink->HandleXMLDeclaration(aVersion, aEncoding, aStandalone);
|
||||
MaybeStopParser();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -536,6 +544,8 @@ nsExpatDriver::HandleDefault(const PRUnichar *aValue,
|
||||
mInternalState = mSink->HandleCharacterData(newline, 1);
|
||||
}
|
||||
}
|
||||
// The line breaks should not be normalized twice. See bug 343870.
|
||||
MaybeStopParser();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -558,6 +568,7 @@ nsExpatDriver::HandleEndCdataSection()
|
||||
if (mSink) {
|
||||
mInternalState = mSink->HandleCDataSection(mCDataText.get(),
|
||||
mCDataText.Length());
|
||||
MaybeStopParser();
|
||||
}
|
||||
mCDataText.Truncate();
|
||||
|
||||
@ -659,7 +670,7 @@ nsExpatDriver::HandleEndDoctypeDecl()
|
||||
// Note: mInternalSubset already doesn't include the [] around it.
|
||||
mInternalState = mSink->HandleDoctypeDecl(mInternalSubset, mDoctypeName,
|
||||
mSystemID, mPublicID, data);
|
||||
|
||||
MaybeStopParser();
|
||||
}
|
||||
|
||||
mInternalSubset.SetCapacity(0);
|
||||
@ -960,18 +971,17 @@ nsExpatDriver::ParseBuffer(const PRUnichar *aBuffer,
|
||||
NS_ASSERTION((aBuffer && aLength != 0) || (!aBuffer && aLength == 0), "?");
|
||||
NS_ASSERTION(mInternalState != NS_OK || aIsFinal || aBuffer,
|
||||
"Useless call, we won't call Expat");
|
||||
NS_PRECONDITION(mInternalState != NS_ERROR_HTMLPARSER_BLOCK || !aBuffer,
|
||||
NS_PRECONDITION(!BlockedOrInterrupted() || !aBuffer,
|
||||
"Non-null buffer when resuming");
|
||||
NS_PRECONDITION(XML_GetCurrentByteIndex(mExpatParser) % sizeof(PRUnichar) == 0,
|
||||
"Consumed part of a PRUnichar?");
|
||||
|
||||
if (mExpatParser && (mInternalState == NS_OK ||
|
||||
mInternalState == NS_ERROR_HTMLPARSER_BLOCK)) {
|
||||
if (mExpatParser && (mInternalState == NS_OK || BlockedOrInterrupted())) {
|
||||
PRInt32 parserBytesBefore = XML_GetCurrentByteIndex(mExpatParser);
|
||||
NS_ASSERTION(parserBytesBefore >= 0, "Unexpected value");
|
||||
|
||||
XML_Status status;
|
||||
if (mInternalState == NS_ERROR_HTMLPARSER_BLOCK) {
|
||||
if (BlockedOrInterrupted()) {
|
||||
mInternalState = NS_OK; // Resume in case we're blocked.
|
||||
status = XML_ResumeParser(mExpatParser);
|
||||
}
|
||||
@ -994,10 +1004,12 @@ nsExpatDriver::ParseBuffer(const PRUnichar *aBuffer,
|
||||
NS_ASSERTION(*aConsumed <= aLength + mExpatBuffered,
|
||||
"Too many bytes consumed?");
|
||||
|
||||
if (status == XML_STATUS_SUSPENDED) {
|
||||
mInternalState = NS_ERROR_HTMLPARSER_BLOCK;
|
||||
}
|
||||
else if (status == XML_STATUS_ERROR) {
|
||||
NS_ASSERTION(status != XML_STATUS_SUSPENDED ||
|
||||
(mInternalState == NS_ERROR_HTMLPARSER_BLOCK ||
|
||||
mInternalState == NS_ERROR_HTMLPARSER_INTERRUPTED),
|
||||
"Inconsistent expat suspension state.");
|
||||
|
||||
if (status == XML_STATUS_ERROR) {
|
||||
mInternalState = NS_ERROR_HTMLPARSER_STOPPARSING;
|
||||
}
|
||||
}
|
||||
@ -1033,9 +1045,9 @@ nsExpatDriver::ConsumeToken(nsScanner& aScanner, PRBool& aFlushTokens)
|
||||
// be more buffers (and so we want to flush the remaining data), or if we're
|
||||
// currently blocked and there's data in Expat's buffer.
|
||||
while (start != end || flush ||
|
||||
(mInternalState == NS_ERROR_HTMLPARSER_BLOCK && mExpatBuffered > 0)) {
|
||||
(BlockedOrInterrupted() && mExpatBuffered > 0)) {
|
||||
PRBool noMoreBuffers = start == end && mIsFinalChunk;
|
||||
PRBool blocked = mInternalState == NS_ERROR_HTMLPARSER_BLOCK;
|
||||
PRBool blocked = BlockedOrInterrupted();
|
||||
|
||||
// If we're resuming and we know there won't be more data we want to
|
||||
// flush the remaining data after we resumed the parser (so loop once
|
||||
@ -1110,10 +1122,10 @@ nsExpatDriver::ConsumeToken(nsScanner& aScanner, PRBool& aFlushTokens)
|
||||
|
||||
mExpatBuffered += length - consumed;
|
||||
|
||||
if (mInternalState == NS_ERROR_HTMLPARSER_BLOCK) {
|
||||
if (BlockedOrInterrupted()) {
|
||||
PR_LOG(gExpatDriverLog, PR_LOG_DEBUG,
|
||||
("Blocked parser (probably for loading linked stylesheets or "
|
||||
"scripts)."));
|
||||
("Blocked or interrupted parser (probably for loading linked "
|
||||
"stylesheets or scripts)."));
|
||||
|
||||
aScanner.SetPosition(currentExpatPosition, PR_TRUE);
|
||||
aScanner.Mark();
|
||||
@ -1387,3 +1399,11 @@ nsExpatDriver::CanContain(PRInt32 aParent,PRInt32 aChild) const
|
||||
{
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsExpatDriver::MaybeStopParser()
|
||||
{
|
||||
if (BlockedOrInterrupted()) {
|
||||
XML_StopParser(mExpatParser, XML_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@
|
||||
#include "nsIDTD.h"
|
||||
#include "nsITokenizer.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIParser.h"
|
||||
|
||||
class nsIExpatSink;
|
||||
class nsIExtendedExpatSink;
|
||||
@ -123,6 +124,14 @@ private:
|
||||
PRUint32 *aConsumed);
|
||||
nsresult HandleError();
|
||||
|
||||
void MaybeStopParser();
|
||||
|
||||
PRBool BlockedOrInterrupted()
|
||||
{
|
||||
return mInternalState == NS_ERROR_HTMLPARSER_BLOCK ||
|
||||
mInternalState == NS_ERROR_HTMLPARSER_INTERRUPTED;
|
||||
}
|
||||
|
||||
XML_Parser mExpatParser;
|
||||
nsString mLastLine;
|
||||
nsString mCDataText;
|
||||
|
||||
@ -93,6 +93,10 @@ void WriteTabs(PRFileDesc * out,int aTabCount) {
|
||||
PR_fprintf(out, " ");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLoggingSink::WillTokenize() {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLoggingSink::WillBuildModel() {
|
||||
|
||||
@ -60,6 +60,7 @@ public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIContentSink
|
||||
NS_IMETHOD WillTokenize();
|
||||
NS_IMETHOD WillBuildModel();
|
||||
NS_IMETHOD DidBuildModel();
|
||||
NS_IMETHOD WillInterrupt();
|
||||
|
||||
@ -2352,7 +2352,7 @@ nsParser::WillTokenize(PRBool aIsFinalChunk)
|
||||
NS_IPARSER_FLAG_HTML;
|
||||
nsresult result = mParserContext->GetTokenizer(type, mSink, theTokenizer);
|
||||
NS_ENSURE_SUCCESS(result, PR_FALSE);
|
||||
|
||||
mSink->WillTokenize();
|
||||
return NS_SUCCEEDED(theTokenizer->WillTokenize(aIsFinalChunk,
|
||||
&mTokenAllocator));
|
||||
}
|
||||
|
||||
@ -70,6 +70,11 @@ public:
|
||||
nsSAXXMLReader();
|
||||
|
||||
//nsIContentSink
|
||||
NS_IMETHOD WillTokenize()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD WillBuildModel();
|
||||
NS_IMETHOD DidBuildModel();
|
||||
NS_IMETHOD SetParser(nsIParser* aParser);
|
||||
|
||||
@ -159,6 +159,7 @@ public:
|
||||
NS_DECL_NSIEXPATSINK
|
||||
|
||||
// nsIContentSink
|
||||
NS_IMETHOD WillTokenize(void);
|
||||
NS_IMETHOD WillBuildModel(void);
|
||||
NS_IMETHOD DidBuildModel(void);
|
||||
NS_IMETHOD WillInterrupt(void);
|
||||
@ -606,6 +607,13 @@ RDFContentSinkImpl::ReportError(const PRUnichar* aErrorText,
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// nsIContentSink interface
|
||||
|
||||
NS_IMETHODIMP
|
||||
RDFContentSinkImpl::WillTokenize(void)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
RDFContentSinkImpl::WillBuildModel(void)
|
||||
{
|
||||
|
||||
@ -219,6 +219,7 @@ public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIContentSink (superclass of nsIHTMLContentSink)
|
||||
NS_IMETHOD WillTokenize() { return NS_OK; }
|
||||
NS_IMETHOD WillBuildModel() { return NS_OK; }
|
||||
NS_IMETHOD DidBuildModel() { return NS_OK; }
|
||||
NS_IMETHOD WillInterrupt() { return NS_OK; }
|
||||
|
||||
@ -735,6 +735,17 @@ NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
|
||||
#define NS_IMPL_THREADSAFE_QUERY_INTERFACE10 NS_IMPL_QUERY_INTERFACE10
|
||||
#define NS_IMPL_THREADSAFE_QUERY_INTERFACE11 NS_IMPL_QUERY_INTERFACE11
|
||||
|
||||
#define NS_IMPL_QUERY_INTERFACE_INHERITED7(Class,Super,i1,i2,i3,i4,i5,i6,i7) \
|
||||
NS_IMPL_QUERY_HEAD(Class) \
|
||||
NS_IMPL_QUERY_BODY(i1) \
|
||||
NS_IMPL_QUERY_BODY(i2) \
|
||||
NS_IMPL_QUERY_BODY(i3) \
|
||||
NS_IMPL_QUERY_BODY(i4) \
|
||||
NS_IMPL_QUERY_BODY(i5) \
|
||||
NS_IMPL_QUERY_BODY(i6) \
|
||||
NS_IMPL_QUERY_BODY(i7) \
|
||||
NS_IMPL_QUERY_TAIL_INHERITING(Super) \
|
||||
|
||||
/**
|
||||
* Declare that you're going to inherit from something that already
|
||||
* implements nsISupports, but also implements an additional interface, thus
|
||||
@ -943,6 +954,11 @@ NS_IMETHODIMP_(nsrefcnt) Class::Release(void) \
|
||||
NS_IMPL_ADDREF_INHERITED(Class, Super) \
|
||||
NS_IMPL_RELEASE_INHERITED(Class, Super) \
|
||||
|
||||
#define NS_IMPL_ISUPPORTS_INHERITED7(Class, Super, i1, i2, i3, i4, i5, i6, i7) \
|
||||
NS_IMPL_QUERY_INTERFACE_INHERITED7(Class, Super, i1, i2, i3, i4, i5, i6, i7) \
|
||||
NS_IMPL_ADDREF_INHERITED(Class, Super) \
|
||||
NS_IMPL_RELEASE_INHERITED(Class, Super) \
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user