1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=78: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Henri Sivonen <hsivonen@iki.fi>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /*
41 : * Base class for the XML and HTML content sinks, which construct a
42 : * DOM based on information from the parser.
43 : */
44 :
45 : #include "nsContentSink.h"
46 : #include "nsScriptLoader.h"
47 : #include "nsIDocument.h"
48 : #include "nsIDOMDocument.h"
49 : #include "mozilla/css/Loader.h"
50 : #include "nsStyleConsts.h"
51 : #include "nsStyleLinkElement.h"
52 : #include "nsINodeInfo.h"
53 : #include "nsIDocShell.h"
54 : #include "nsIDocShellTreeItem.h"
55 : #include "nsCPrefetchService.h"
56 : #include "nsIURI.h"
57 : #include "nsNetUtil.h"
58 : #include "nsIHttpChannel.h"
59 : #include "nsIContent.h"
60 : #include "nsIScriptElement.h"
61 : #include "nsIParser.h"
62 : #include "nsContentErrors.h"
63 : #include "nsIPresShell.h"
64 : #include "nsPresContext.h"
65 : #include "nsIViewManager.h"
66 : #include "nsIContentViewer.h"
67 : #include "nsIAtom.h"
68 : #include "nsGkAtoms.h"
69 : #include "nsIDOMWindow.h"
70 : #include "nsIPrincipal.h"
71 : #include "nsIScriptGlobalObject.h"
72 : #include "nsNetCID.h"
73 : #include "nsIOfflineCacheUpdate.h"
74 : #include "nsIApplicationCache.h"
75 : #include "nsIApplicationCacheContainer.h"
76 : #include "nsIApplicationCacheChannel.h"
77 : #include "nsIScriptSecurityManager.h"
78 : #include "nsIDOMLoadStatus.h"
79 : #include "nsICookieService.h"
80 : #include "nsIPrompt.h"
81 : #include "nsServiceManagerUtils.h"
82 : #include "nsContentUtils.h"
83 : #include "nsCRT.h"
84 : #include "nsEscape.h"
85 : #include "nsWeakReference.h"
86 : #include "nsUnicharUtils.h"
87 : #include "nsNodeInfoManager.h"
88 : #include "nsIAppShell.h"
89 : #include "nsIWidget.h"
90 : #include "nsWidgetsCID.h"
91 : #include "nsIRequest.h"
92 : #include "nsNodeUtils.h"
93 : #include "nsIDOMNode.h"
94 : #include "nsThreadUtils.h"
95 : #include "nsPIDOMWindow.h"
96 : #include "mozAutoDocUpdate.h"
97 : #include "nsIWebNavigation.h"
98 : #include "nsIDocumentLoader.h"
99 : #include "nsICachingChannel.h"
100 : #include "nsICacheEntryDescriptor.h"
101 : #include "nsGenericHTMLElement.h"
102 : #include "nsHTMLDNSPrefetch.h"
103 : #include "nsISupportsPrimitives.h"
104 : #include "mozilla/Preferences.h"
105 : #include "nsParserConstants.h"
106 :
107 : using namespace mozilla;
108 :
109 : PRLogModuleInfo* gContentSinkLogModuleInfo;
110 :
111 29670 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink)
112 29670 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink)
113 :
114 12183 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSink)
115 12142 : NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
116 12142 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
117 11104 : NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
118 2078 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
119 2078 : NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
120 2078 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentObserver)
121 2078 : NS_INTERFACE_MAP_END
122 :
123 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink)
124 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink)
125 0 : if (tmp->mDocument) {
126 0 : tmp->mDocument->RemoveObserver(tmp);
127 : }
128 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
129 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParser)
130 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNodeInfoManager)
131 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptLoader)
132 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
133 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink)
134 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
135 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParser)
136 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNodeInfoManager,
137 : nsNodeInfoManager)
138 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptLoader)
139 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
140 :
141 :
142 1051 : nsContentSink::nsContentSink()
143 : {
144 : // We have a zeroing operator new
145 1051 : NS_ASSERTION(!mLayoutStarted, "What?");
146 1051 : NS_ASSERTION(!mDynamicLowerValue, "What?");
147 1051 : NS_ASSERTION(!mParsing, "What?");
148 1051 : NS_ASSERTION(mLastSampledUserEventTime == 0, "What?");
149 1051 : NS_ASSERTION(mDeflectedCount == 0, "What?");
150 1051 : NS_ASSERTION(!mDroppedTimer, "What?");
151 1051 : NS_ASSERTION(mInMonolithicContainer == 0, "What?");
152 1051 : NS_ASSERTION(mInNotification == 0, "What?");
153 1051 : NS_ASSERTION(!mDeferredLayoutStart, "What?");
154 :
155 : #ifdef NS_DEBUG
156 1051 : if (!gContentSinkLogModuleInfo) {
157 249 : gContentSinkLogModuleInfo = PR_NewLogModule("nscontentsink");
158 : }
159 : #endif
160 1051 : }
161 :
162 2102 : nsContentSink::~nsContentSink()
163 : {
164 1051 : if (mDocument) {
165 : // Remove ourselves just to be safe, though we really should have
166 : // been removed in DidBuildModel if everything worked right.
167 1038 : mDocument->RemoveObserver(this);
168 : }
169 2102 : }
170 :
171 : bool nsContentSink::sNotifyOnTimer;
172 : PRInt32 nsContentSink::sBackoffCount;
173 : PRInt32 nsContentSink::sNotificationInterval;
174 : PRInt32 nsContentSink::sInteractiveDeflectCount;
175 : PRInt32 nsContentSink::sPerfDeflectCount;
176 : PRInt32 nsContentSink::sPendingEventMode;
177 : PRInt32 nsContentSink::sEventProbeRate;
178 : PRInt32 nsContentSink::sInteractiveParseTime;
179 : PRInt32 nsContentSink::sPerfParseTime;
180 : PRInt32 nsContentSink::sInteractiveTime;
181 : PRInt32 nsContentSink::sInitialPerfTime;
182 : PRInt32 nsContentSink::sEnablePerfMode;
183 :
184 : void
185 1404 : nsContentSink::InitializeStatics()
186 : {
187 : Preferences::AddBoolVarCache(&sNotifyOnTimer,
188 1404 : "content.notify.ontimer", true);
189 : // -1 means never.
190 : Preferences::AddIntVarCache(&sBackoffCount,
191 1404 : "content.notify.backoffcount", -1);
192 : // The gNotificationInterval has a dramatic effect on how long it
193 : // takes to initially display content for slow connections.
194 : // The current value provides good
195 : // incremental display of content without causing an increase
196 : // in page load time. If this value is set below 1/10 of second
197 : // it starts to impact page load performance.
198 : // see bugzilla bug 72138 for more info.
199 : Preferences::AddIntVarCache(&sNotificationInterval,
200 1404 : "content.notify.interval", 120000);
201 : Preferences::AddIntVarCache(&sInteractiveDeflectCount,
202 1404 : "content.sink.interactive_deflect_count", 0);
203 : Preferences::AddIntVarCache(&sPerfDeflectCount,
204 1404 : "content.sink.perf_deflect_count", 200);
205 : Preferences::AddIntVarCache(&sPendingEventMode,
206 1404 : "content.sink.pending_event_mode", 1);
207 : Preferences::AddIntVarCache(&sEventProbeRate,
208 1404 : "content.sink.event_probe_rate", 1);
209 : Preferences::AddIntVarCache(&sInteractiveParseTime,
210 1404 : "content.sink.interactive_parse_time", 3000);
211 : Preferences::AddIntVarCache(&sPerfParseTime,
212 1404 : "content.sink.perf_parse_time", 360000);
213 : Preferences::AddIntVarCache(&sInteractiveTime,
214 1404 : "content.sink.interactive_time", 750000);
215 : Preferences::AddIntVarCache(&sInitialPerfTime,
216 1404 : "content.sink.initial_perf_time", 2000000);
217 : Preferences::AddIntVarCache(&sEnablePerfMode,
218 1404 : "content.sink.enable_perf_mode", 0);
219 1404 : }
220 :
221 : nsresult
222 1272 : nsContentSink::Init(nsIDocument* aDoc,
223 : nsIURI* aURI,
224 : nsISupports* aContainer,
225 : nsIChannel* aChannel)
226 : {
227 1272 : NS_PRECONDITION(aDoc, "null ptr");
228 1272 : NS_PRECONDITION(aURI, "null ptr");
229 :
230 1272 : if (!aDoc || !aURI) {
231 0 : return NS_ERROR_NULL_POINTER;
232 : }
233 :
234 1272 : mDocument = aDoc;
235 :
236 1272 : mDocumentURI = aURI;
237 1272 : mDocShell = do_QueryInterface(aContainer);
238 1272 : mScriptLoader = mDocument->ScriptLoader();
239 :
240 1272 : if (!mRunsToCompletion) {
241 1038 : if (mDocShell) {
242 0 : PRUint32 loadType = 0;
243 0 : mDocShell->GetLoadType(&loadType);
244 0 : mDocument->SetChangeScrollPosWhenScrollingToRef(
245 0 : (loadType & nsIDocShell::LOAD_CMD_HISTORY) == 0);
246 : }
247 :
248 1038 : ProcessHTTPHeaders(aChannel);
249 : }
250 :
251 1272 : mCSSLoader = aDoc->CSSLoader();
252 :
253 1272 : mNodeInfoManager = aDoc->NodeInfoManager();
254 :
255 1272 : mBackoffCount = sBackoffCount;
256 :
257 1272 : if (sEnablePerfMode != 0) {
258 0 : mDynamicLowerValue = sEnablePerfMode == 1;
259 0 : FavorPerformanceHint(!mDynamicLowerValue, 0);
260 : }
261 :
262 1272 : return NS_OK;
263 : }
264 :
265 : NS_IMETHODIMP
266 0 : nsContentSink::StyleSheetLoaded(nsCSSStyleSheet* aSheet,
267 : bool aWasAlternate,
268 : nsresult aStatus)
269 : {
270 0 : NS_ASSERTION(!mRunsToCompletion, "How come a fragment parser observed sheets?");
271 0 : if (!aWasAlternate) {
272 0 : NS_ASSERTION(mPendingSheetCount > 0, "How'd that happen?");
273 0 : --mPendingSheetCount;
274 :
275 0 : if (mPendingSheetCount == 0 &&
276 : (mDeferredLayoutStart || mDeferredFlushTags)) {
277 0 : if (mDeferredFlushTags) {
278 0 : FlushTags();
279 : }
280 0 : if (mDeferredLayoutStart) {
281 : // We might not have really started layout, since this sheet was still
282 : // loading. Do it now. Probably doesn't matter whether we do this
283 : // before or after we unblock scripts, but before feels saner. Note
284 : // that if mDeferredLayoutStart is true, that means any subclass
285 : // StartLayout() stuff that needs to happen has already happened, so we
286 : // don't need to worry about it.
287 0 : StartLayout(false);
288 : }
289 :
290 : // Go ahead and try to scroll to our ref if we have one
291 0 : ScrollToRef();
292 : }
293 :
294 0 : mScriptLoader->RemoveExecuteBlocker();
295 : }
296 :
297 0 : return NS_OK;
298 : }
299 :
300 : nsresult
301 1038 : nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel)
302 : {
303 2076 : nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aChannel));
304 :
305 1038 : if (!httpchannel) {
306 571 : return NS_OK;
307 : }
308 :
309 : // Note that the only header we care about is the "link" header, since we
310 : // have all the infrastructure for kicking off stylesheet loads.
311 :
312 934 : nsCAutoString linkHeader;
313 :
314 934 : nsresult rv = httpchannel->GetResponseHeader(NS_LITERAL_CSTRING("link"),
315 467 : linkHeader);
316 467 : if (NS_SUCCEEDED(rv) && !linkHeader.IsEmpty()) {
317 0 : mDocument->SetHeaderData(nsGkAtoms::link,
318 0 : NS_ConvertASCIItoUTF16(linkHeader));
319 :
320 0 : NS_ASSERTION(!mProcessLinkHeaderEvent.get(),
321 : "Already dispatched an event?");
322 :
323 : mProcessLinkHeaderEvent =
324 : NS_NewNonOwningRunnableMethod(this,
325 0 : &nsContentSink::DoProcessLinkHeader);
326 0 : rv = NS_DispatchToCurrentThread(mProcessLinkHeaderEvent.get());
327 0 : if (NS_FAILED(rv)) {
328 0 : mProcessLinkHeaderEvent.Forget();
329 : }
330 : }
331 :
332 467 : return NS_OK;
333 : }
334 :
335 : nsresult
336 0 : nsContentSink::ProcessHeaderData(nsIAtom* aHeader, const nsAString& aValue,
337 : nsIContent* aContent)
338 : {
339 0 : nsresult rv = NS_OK;
340 : // necko doesn't process headers coming in from the parser
341 :
342 0 : mDocument->SetHeaderData(aHeader, aValue);
343 :
344 0 : if (aHeader == nsGkAtoms::setcookie) {
345 : // Note: Necko already handles cookies set via the channel. We can't just
346 : // call SetCookie on the channel because we want to do some security checks
347 : // here and want to use the prompt associated to our current window, not
348 : // the window where the channel was dispatched.
349 : nsCOMPtr<nsICookieService> cookieServ =
350 0 : do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
351 0 : if (NS_FAILED(rv)) {
352 0 : return rv;
353 : }
354 :
355 : // Get a URI from the document principal
356 :
357 : // We use the original codebase in case the codebase was changed
358 : // by SetDomain
359 :
360 : // Note that a non-codebase principal (eg the system principal) will return
361 : // a null URI.
362 0 : nsCOMPtr<nsIURI> codebaseURI;
363 0 : rv = mDocument->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
364 0 : NS_ENSURE_TRUE(codebaseURI, rv);
365 :
366 0 : nsCOMPtr<nsIPrompt> prompt;
367 0 : nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mDocument->GetScriptGlobalObject());
368 0 : if (window) {
369 0 : window->GetPrompter(getter_AddRefs(prompt));
370 : }
371 :
372 0 : nsCOMPtr<nsIChannel> channel;
373 0 : if (mParser) {
374 0 : mParser->GetChannel(getter_AddRefs(channel));
375 : }
376 :
377 0 : rv = cookieServ->SetCookieString(codebaseURI,
378 : prompt,
379 0 : NS_ConvertUTF16toUTF8(aValue).get(),
380 0 : channel);
381 0 : if (NS_FAILED(rv)) {
382 0 : return rv;
383 : }
384 : }
385 0 : else if (aHeader == nsGkAtoms::msthemecompatible) {
386 : // Disable theming for the presshell if the value is no.
387 : // XXXbz don't we want to support this as an HTTP header too?
388 0 : nsAutoString value(aValue);
389 0 : if (value.LowerCaseEqualsLiteral("no")) {
390 0 : nsIPresShell* shell = mDocument->GetShell();
391 0 : if (shell) {
392 0 : shell->DisableThemeSupport();
393 : }
394 : }
395 : }
396 :
397 0 : return rv;
398 : }
399 :
400 :
401 : void
402 0 : nsContentSink::DoProcessLinkHeader()
403 : {
404 0 : nsAutoString value;
405 0 : mDocument->GetHeaderData(nsGkAtoms::link, value);
406 0 : ProcessLinkHeader(nsnull, value);
407 0 : }
408 :
409 : // check whether the Link header field applies to the context resource
410 : // see <http://tools.ietf.org/html/rfc5988#section-5.2>
411 :
412 : bool
413 0 : nsContentSink::LinkContextIsOurDocument(const nsSubstring& aAnchor)
414 : {
415 0 : if (aAnchor.IsEmpty()) {
416 : // anchor parameter not present or empty -> same document reference
417 0 : return true;
418 : }
419 :
420 0 : nsIURI* docUri = mDocument->GetDocumentURI();
421 :
422 : // the document URI might contain a fragment identifier ("#...')
423 : // we want to ignore that because it's invisible to the server
424 : // and just affects the local interpretation in the recipient
425 0 : nsCOMPtr<nsIURI> contextUri;
426 0 : nsresult rv = docUri->CloneIgnoringRef(getter_AddRefs(contextUri));
427 :
428 0 : if (NS_FAILED(rv)) {
429 : // copying failed
430 0 : return false;
431 : }
432 :
433 : // resolve anchor against context
434 0 : nsCOMPtr<nsIURI> resolvedUri;
435 0 : rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor,
436 0 : nsnull, contextUri);
437 :
438 0 : if (NS_FAILED(rv)) {
439 : // resolving failed
440 0 : return false;
441 : }
442 :
443 : bool same;
444 0 : rv = contextUri->Equals(resolvedUri, &same);
445 0 : if (NS_FAILED(rv)) {
446 : // comparison failed
447 0 : return false;
448 : }
449 :
450 0 : return same;
451 : }
452 :
453 : nsresult
454 0 : nsContentSink::ProcessLinkHeader(nsIContent* aElement,
455 : const nsAString& aLinkData)
456 : {
457 0 : nsresult rv = NS_OK;
458 :
459 : // keep track where we are within the header field
460 0 : bool seenParameters = false;
461 :
462 : // parse link content and call process style link
463 0 : nsAutoString href;
464 0 : nsAutoString rel;
465 0 : nsAutoString title;
466 0 : nsAutoString type;
467 0 : nsAutoString media;
468 0 : nsAutoString anchor;
469 :
470 : // copy to work buffer
471 0 : nsAutoString stringList(aLinkData);
472 :
473 : // put an extra null at the end
474 0 : stringList.Append(kNullCh);
475 :
476 0 : PRUnichar* start = stringList.BeginWriting();
477 0 : PRUnichar* end = start;
478 0 : PRUnichar* last = start;
479 : PRUnichar endCh;
480 :
481 0 : while (*start != kNullCh) {
482 : // skip leading space
483 0 : while ((*start != kNullCh) && nsCRT::IsAsciiSpace(*start)) {
484 0 : ++start;
485 : }
486 :
487 0 : end = start;
488 0 : last = end - 1;
489 :
490 0 : bool needsUnescape = false;
491 :
492 : // look for semicolon or comma
493 0 : while (*end != kNullCh && *end != kSemicolon && *end != kComma) {
494 0 : PRUnichar ch = *end;
495 :
496 0 : if (ch == kQuote || ch == kLessThan) {
497 : // quoted string
498 :
499 0 : PRUnichar quote = ch;
500 0 : if (quote == kLessThan) {
501 0 : quote = kGreaterThan;
502 : }
503 :
504 0 : needsUnescape = (ch == kQuote);
505 :
506 0 : PRUnichar* closeQuote = (end + 1);
507 :
508 : // seek closing quote
509 0 : while (*closeQuote != kNullCh && quote != *closeQuote) {
510 : // in quoted-string, "\" is an escape character
511 0 : if (needsUnescape && *closeQuote == kBackSlash && *(closeQuote + 1) != kNullCh) {
512 0 : ++closeQuote;
513 : }
514 :
515 0 : ++closeQuote;
516 : }
517 :
518 0 : if (quote == *closeQuote) {
519 : // found closer
520 :
521 : // skip to close quote
522 0 : end = closeQuote;
523 :
524 0 : last = end - 1;
525 :
526 0 : ch = *(end + 1);
527 :
528 0 : if (ch != kNullCh && ch != kSemicolon && ch != kComma) {
529 : // end string here
530 0 : *(++end) = kNullCh;
531 :
532 0 : ch = *(end + 1);
533 :
534 : // keep going until semi or comma
535 0 : while (ch != kNullCh && ch != kSemicolon && ch != kComma) {
536 0 : ++end;
537 :
538 0 : ch = *end;
539 : }
540 : }
541 : }
542 : }
543 :
544 0 : ++end;
545 0 : ++last;
546 : }
547 :
548 0 : endCh = *end;
549 :
550 : // end string here
551 0 : *end = kNullCh;
552 :
553 0 : if (start < end) {
554 0 : if ((*start == kLessThan) && (*last == kGreaterThan)) {
555 0 : *last = kNullCh;
556 :
557 : // first instance of <...> wins
558 : // also, do not allow hrefs after the first param was seen
559 0 : if (href.IsEmpty() && !seenParameters) {
560 0 : href = (start + 1);
561 0 : href.StripWhitespace();
562 : }
563 : } else {
564 0 : PRUnichar* equals = start;
565 0 : seenParameters = true;
566 :
567 0 : while ((*equals != kNullCh) && (*equals != kEqual)) {
568 0 : equals++;
569 : }
570 :
571 0 : if (*equals != kNullCh) {
572 0 : *equals = kNullCh;
573 0 : nsAutoString attr(start);
574 0 : attr.StripWhitespace();
575 :
576 0 : PRUnichar* value = ++equals;
577 0 : while (nsCRT::IsAsciiSpace(*value)) {
578 0 : value++;
579 : }
580 :
581 0 : if ((*value == kQuote) && (*value == *last)) {
582 0 : *last = kNullCh;
583 0 : value++;
584 : }
585 :
586 0 : if (needsUnescape) {
587 : // unescape in-place
588 0 : PRUnichar* unescaped = value;
589 0 : PRUnichar *src = value;
590 :
591 0 : while (*src != kNullCh) {
592 0 : if (*src == kBackSlash && *(src + 1) != kNullCh) {
593 0 : src++;
594 : }
595 0 : *unescaped++ = *src++;
596 : }
597 :
598 0 : *unescaped = kNullCh;
599 : }
600 :
601 0 : if (attr.LowerCaseEqualsLiteral("rel")) {
602 0 : if (rel.IsEmpty()) {
603 0 : rel = value;
604 0 : rel.CompressWhitespace();
605 : }
606 0 : } else if (attr.LowerCaseEqualsLiteral("title")) {
607 0 : if (title.IsEmpty()) {
608 0 : title = value;
609 0 : title.CompressWhitespace();
610 : }
611 0 : } else if (attr.LowerCaseEqualsLiteral("type")) {
612 0 : if (type.IsEmpty()) {
613 0 : type = value;
614 0 : type.StripWhitespace();
615 : }
616 0 : } else if (attr.LowerCaseEqualsLiteral("media")) {
617 0 : if (media.IsEmpty()) {
618 0 : media = value;
619 :
620 : // The HTML5 spec is formulated in terms of the CSS3 spec,
621 : // which specifies that media queries are case insensitive.
622 0 : nsContentUtils::ASCIIToLower(media);
623 : }
624 0 : } else if (attr.LowerCaseEqualsLiteral("anchor")) {
625 0 : if (anchor.IsEmpty()) {
626 0 : anchor = value;
627 0 : anchor.StripWhitespace();
628 : }
629 : }
630 : }
631 : }
632 : }
633 :
634 0 : if (endCh == kComma) {
635 : // hit a comma, process what we've got so far
636 :
637 0 : href.Trim(" \t\n\r\f"); // trim HTML5 whitespace
638 0 : if (!href.IsEmpty() && !rel.IsEmpty()) {
639 0 : rv = ProcessLink(aElement, anchor, href, rel, title, type, media);
640 : }
641 :
642 0 : href.Truncate();
643 0 : rel.Truncate();
644 0 : title.Truncate();
645 0 : type.Truncate();
646 0 : media.Truncate();
647 0 : anchor.Truncate();
648 :
649 0 : seenParameters = false;
650 : }
651 :
652 0 : start = ++end;
653 : }
654 :
655 0 : href.Trim(" \t\n\r\f"); // trim HTML5 whitespace
656 0 : if (!href.IsEmpty() && !rel.IsEmpty()) {
657 0 : rv = ProcessLink(aElement, anchor, href, rel, title, type, media);
658 : }
659 :
660 0 : return rv;
661 : }
662 :
663 :
664 : nsresult
665 0 : nsContentSink::ProcessLink(nsIContent* aElement,
666 : const nsSubstring& aAnchor, const nsSubstring& aHref,
667 : const nsSubstring& aRel, const nsSubstring& aTitle,
668 : const nsSubstring& aType, const nsSubstring& aMedia)
669 : {
670 0 : PRUint32 linkTypes = nsStyleLinkElement::ParseLinkTypes(aRel);
671 :
672 : // The link relation may apply to a different resource, specified
673 : // in the anchor parameter. For the link relations supported so far,
674 : // we simply abort if the link applies to a resource different to the
675 : // one we've loaded
676 0 : if (!LinkContextIsOurDocument(aAnchor)) {
677 0 : return NS_OK;
678 : }
679 :
680 0 : bool hasPrefetch = linkTypes & PREFETCH;
681 : // prefetch href if relation is "next" or "prefetch"
682 0 : if (hasPrefetch || (linkTypes & NEXT)) {
683 0 : PrefetchHref(aHref, aElement, hasPrefetch);
684 : }
685 :
686 0 : if (!aHref.IsEmpty() && (linkTypes & DNS_PREFETCH)) {
687 0 : PrefetchDNS(aHref);
688 : }
689 :
690 : // is it a stylesheet link?
691 0 : if (!(linkTypes & STYLESHEET)) {
692 0 : return NS_OK;
693 : }
694 :
695 0 : bool isAlternate = linkTypes & ALTERNATE;
696 : return ProcessStyleLink(aElement, aHref, isAlternate, aTitle, aType,
697 0 : aMedia);
698 : }
699 :
700 : nsresult
701 0 : nsContentSink::ProcessStyleLink(nsIContent* aElement,
702 : const nsSubstring& aHref,
703 : bool aAlternate,
704 : const nsSubstring& aTitle,
705 : const nsSubstring& aType,
706 : const nsSubstring& aMedia)
707 : {
708 0 : if (aAlternate && aTitle.IsEmpty()) {
709 : // alternates must have title return without error, for now
710 0 : return NS_OK;
711 : }
712 :
713 0 : nsAutoString mimeType;
714 0 : nsAutoString params;
715 0 : nsContentUtils::SplitMimeType(aType, mimeType, params);
716 :
717 : // see bug 18817
718 0 : if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) {
719 : // Unknown stylesheet language
720 0 : return NS_OK;
721 : }
722 :
723 0 : nsCOMPtr<nsIURI> url;
724 0 : nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nsnull,
725 0 : mDocument->GetDocBaseURI());
726 :
727 0 : if (NS_FAILED(rv)) {
728 : // The URI is bad, move along, don't propagate the error (for now)
729 0 : return NS_OK;
730 : }
731 :
732 : // If this is a fragment parser, we don't want to observe.
733 : bool isAlternate;
734 : rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia, aAlternate,
735 0 : mRunsToCompletion ? nsnull : this, &isAlternate);
736 0 : NS_ENSURE_SUCCESS(rv, rv);
737 :
738 0 : if (!isAlternate && !mRunsToCompletion) {
739 0 : ++mPendingSheetCount;
740 0 : mScriptLoader->AddExecuteBlocker();
741 : }
742 :
743 0 : return NS_OK;
744 : }
745 :
746 :
747 : nsresult
748 0 : nsContentSink::ProcessMETATag(nsIContent* aContent)
749 : {
750 0 : NS_ASSERTION(aContent, "missing meta-element");
751 :
752 0 : nsresult rv = NS_OK;
753 :
754 : // set any HTTP-EQUIV data into document's header data as well as url
755 0 : nsAutoString header;
756 0 : aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
757 0 : if (!header.IsEmpty()) {
758 0 : nsAutoString result;
759 0 : aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result);
760 0 : if (!result.IsEmpty()) {
761 0 : nsContentUtils::ASCIIToLower(header);
762 0 : nsCOMPtr<nsIAtom> fieldAtom(do_GetAtom(header));
763 0 : rv = ProcessHeaderData(fieldAtom, result, aContent);
764 : }
765 : }
766 0 : NS_ENSURE_SUCCESS(rv, rv);
767 :
768 0 : if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
769 0 : nsGkAtoms::handheldFriendly, eIgnoreCase)) {
770 0 : nsAutoString result;
771 0 : aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result);
772 0 : if (!result.IsEmpty()) {
773 0 : nsContentUtils::ASCIIToLower(result);
774 0 : mDocument->SetHeaderData(nsGkAtoms::handheldFriendly, result);
775 : }
776 : }
777 :
778 0 : return rv;
779 : }
780 :
781 :
782 : void
783 0 : nsContentSink::PrefetchHref(const nsAString &aHref,
784 : nsIContent *aSource,
785 : bool aExplicit)
786 : {
787 : //
788 : // SECURITY CHECK: disable prefetching from mailnews!
789 : //
790 : // walk up the docshell tree to see if any containing
791 : // docshell are of type MAIL.
792 : //
793 0 : if (!mDocShell)
794 0 : return;
795 :
796 0 : nsCOMPtr<nsIDocShell> docshell = mDocShell;
797 :
798 0 : nsCOMPtr<nsIDocShellTreeItem> treeItem, parentItem;
799 0 : do {
800 0 : PRUint32 appType = 0;
801 0 : nsresult rv = docshell->GetAppType(&appType);
802 0 : if (NS_FAILED(rv) || appType == nsIDocShell::APP_TYPE_MAIL)
803 : return; // do not prefetch from mailnews
804 0 : treeItem = do_QueryInterface(docshell);
805 0 : if (treeItem) {
806 0 : treeItem->GetParent(getter_AddRefs(parentItem));
807 0 : if (parentItem) {
808 0 : treeItem = parentItem;
809 0 : docshell = do_QueryInterface(treeItem);
810 0 : if (!docshell) {
811 0 : NS_ERROR("cannot get a docshell from a treeItem!");
812 : return;
813 : }
814 : }
815 : }
816 0 : } while (parentItem);
817 :
818 : // OK, we passed the security check...
819 :
820 0 : nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
821 0 : if (prefetchService) {
822 : // construct URI using document charset
823 0 : const nsACString &charset = mDocument->GetDocumentCharacterSet();
824 0 : nsCOMPtr<nsIURI> uri;
825 0 : NS_NewURI(getter_AddRefs(uri), aHref,
826 0 : charset.IsEmpty() ? nsnull : PromiseFlatCString(charset).get(),
827 0 : mDocument->GetDocBaseURI());
828 0 : if (uri) {
829 0 : nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aSource);
830 0 : prefetchService->PrefetchURI(uri, mDocumentURI, domNode, aExplicit);
831 : }
832 : }
833 : }
834 :
835 : void
836 0 : nsContentSink::PrefetchDNS(const nsAString &aHref)
837 : {
838 0 : nsAutoString hostname;
839 :
840 0 : if (StringBeginsWith(aHref, NS_LITERAL_STRING("//"))) {
841 0 : hostname = Substring(aHref, 2);
842 : }
843 : else {
844 0 : nsCOMPtr<nsIURI> uri;
845 0 : NS_NewURI(getter_AddRefs(uri), aHref);
846 0 : if (!uri) {
847 : return;
848 : }
849 0 : nsCAutoString host;
850 0 : uri->GetHost(host);
851 0 : CopyUTF8toUTF16(host, hostname);
852 : }
853 :
854 0 : if (!hostname.IsEmpty() && nsHTMLDNSPrefetch::IsAllowed(mDocument)) {
855 0 : nsHTMLDNSPrefetch::PrefetchLow(hostname);
856 : }
857 : }
858 :
859 : nsresult
860 0 : nsContentSink::SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache,
861 : nsIURI *aManifestURI,
862 : bool aFetchedWithHTTPGetOrEquiv,
863 : CacheSelectionAction *aAction)
864 : {
865 : nsresult rv;
866 :
867 0 : *aAction = CACHE_SELECTION_NONE;
868 :
869 : nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument =
870 0 : do_QueryInterface(mDocument);
871 0 : NS_ASSERTION(applicationCacheDocument,
872 : "mDocument must implement nsIApplicationCacheContainer.");
873 :
874 0 : if (aLoadApplicationCache) {
875 0 : nsCAutoString groupID;
876 0 : rv = aLoadApplicationCache->GetGroupID(groupID);
877 0 : NS_ENSURE_SUCCESS(rv, rv);
878 :
879 0 : nsCOMPtr<nsIURI> groupURI;
880 0 : rv = NS_NewURI(getter_AddRefs(groupURI), groupID);
881 0 : NS_ENSURE_SUCCESS(rv, rv);
882 :
883 0 : bool equal = false;
884 0 : rv = groupURI->Equals(aManifestURI, &equal);
885 0 : NS_ENSURE_SUCCESS(rv, rv);
886 :
887 0 : if (!equal) {
888 : // This is a foreign entry, force a reload to avoid loading the foreign
889 : // entry. The entry will be marked as foreign to avoid loading it again.
890 :
891 0 : *aAction = CACHE_SELECTION_RELOAD;
892 : }
893 : else {
894 : // The http manifest attribute URI is equal to the manifest URI of
895 : // the cache the document was loaded from - associate the document with
896 : // that cache and invoke the cache update process.
897 : #ifdef NS_DEBUG
898 0 : nsCAutoString docURISpec, clientID;
899 0 : mDocumentURI->GetAsciiSpec(docURISpec);
900 0 : aLoadApplicationCache->GetClientID(clientID);
901 0 : SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
902 : ("Selection: assigning app cache %s to document %s", clientID.get(), docURISpec.get()));
903 : #endif
904 :
905 0 : rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache);
906 0 : NS_ENSURE_SUCCESS(rv, rv);
907 :
908 : // Document will be added as implicit entry to the cache as part of
909 : // the update process.
910 0 : *aAction = CACHE_SELECTION_UPDATE;
911 : }
912 : }
913 : else {
914 : // The document was not loaded from an application cache
915 : // Here we know the manifest has the same origin as the
916 : // document. There is call to CheckMayLoad() on it above.
917 :
918 0 : if (!aFetchedWithHTTPGetOrEquiv) {
919 : // The document was not loaded using HTTP GET or equivalent
920 : // method. The spec says to run the cache selection algorithm w/o
921 : // the manifest specified.
922 0 : *aAction = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
923 : }
924 : else {
925 : // Always do an update in this case
926 0 : *aAction = CACHE_SELECTION_UPDATE;
927 : }
928 : }
929 :
930 0 : return NS_OK;
931 : }
932 :
933 : nsresult
934 0 : nsContentSink::SelectDocAppCacheNoManifest(nsIApplicationCache *aLoadApplicationCache,
935 : nsIURI **aManifestURI,
936 : CacheSelectionAction *aAction)
937 : {
938 0 : *aManifestURI = nsnull;
939 0 : *aAction = CACHE_SELECTION_NONE;
940 :
941 : nsresult rv;
942 :
943 0 : if (aLoadApplicationCache) {
944 : // The document was loaded from an application cache, use that
945 : // application cache as the document's application cache.
946 : nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument =
947 0 : do_QueryInterface(mDocument);
948 0 : NS_ASSERTION(applicationCacheDocument,
949 : "mDocument must implement nsIApplicationCacheContainer.");
950 :
951 : #ifdef NS_DEBUG
952 0 : nsCAutoString docURISpec, clientID;
953 0 : mDocumentURI->GetAsciiSpec(docURISpec);
954 0 : aLoadApplicationCache->GetClientID(clientID);
955 0 : SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
956 : ("Selection, no manifest: assigning app cache %s to document %s", clientID.get(), docURISpec.get()));
957 : #endif
958 :
959 0 : rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache);
960 0 : NS_ENSURE_SUCCESS(rv, rv);
961 :
962 : // Return the uri and invoke the update process for the selected
963 : // application cache.
964 0 : nsCAutoString groupID;
965 0 : rv = aLoadApplicationCache->GetGroupID(groupID);
966 0 : NS_ENSURE_SUCCESS(rv, rv);
967 :
968 0 : rv = NS_NewURI(aManifestURI, groupID);
969 0 : NS_ENSURE_SUCCESS(rv, rv);
970 :
971 0 : *aAction = CACHE_SELECTION_UPDATE;
972 : }
973 :
974 0 : return NS_OK;
975 : }
976 :
977 : void
978 1 : nsContentSink::ProcessOfflineManifest(nsIContent *aElement)
979 : {
980 : // Only check the manifest for root document nodes.
981 1 : if (aElement != mDocument->GetRootElement()) {
982 0 : return;
983 : }
984 :
985 : // Don't bother processing offline manifest for documents
986 : // without a docshell
987 1 : if (!mDocShell) {
988 1 : return;
989 : }
990 :
991 : // Check for a manifest= attribute.
992 0 : nsAutoString manifestSpec;
993 0 : aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
994 0 : ProcessOfflineManifest(manifestSpec);
995 : }
996 :
997 : void
998 234 : nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec)
999 : {
1000 : // Don't bother processing offline manifest for documents
1001 : // without a docshell
1002 234 : if (!mDocShell) {
1003 234 : return;
1004 : }
1005 :
1006 : nsresult rv;
1007 :
1008 : // Grab the application cache the document was loaded from, if any.
1009 0 : nsCOMPtr<nsIApplicationCache> applicationCache;
1010 :
1011 : nsCOMPtr<nsIApplicationCacheChannel> applicationCacheChannel =
1012 0 : do_QueryInterface(mDocument->GetChannel());
1013 0 : if (applicationCacheChannel) {
1014 : bool loadedFromApplicationCache;
1015 0 : rv = applicationCacheChannel->GetLoadedFromApplicationCache(
1016 0 : &loadedFromApplicationCache);
1017 0 : if (NS_FAILED(rv)) {
1018 : return;
1019 : }
1020 :
1021 0 : if (loadedFromApplicationCache) {
1022 0 : rv = applicationCacheChannel->GetApplicationCache(
1023 0 : getter_AddRefs(applicationCache));
1024 0 : if (NS_FAILED(rv)) {
1025 : return;
1026 : }
1027 : }
1028 : }
1029 :
1030 0 : if (aManifestSpec.IsEmpty() && !applicationCache) {
1031 : // Not loaded from an application cache, and no manifest
1032 : // attribute. Nothing to do here.
1033 : return;
1034 : }
1035 :
1036 0 : CacheSelectionAction action = CACHE_SELECTION_NONE;
1037 0 : nsCOMPtr<nsIURI> manifestURI;
1038 :
1039 0 : if (aManifestSpec.IsEmpty()) {
1040 0 : action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
1041 : }
1042 : else {
1043 0 : nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(manifestURI),
1044 : aManifestSpec, mDocument,
1045 0 : mDocumentURI);
1046 0 : if (!manifestURI) {
1047 : return;
1048 : }
1049 :
1050 : // Documents must list a manifest from the same origin
1051 0 : rv = mDocument->NodePrincipal()->CheckMayLoad(manifestURI, true);
1052 0 : if (NS_FAILED(rv)) {
1053 0 : action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
1054 : }
1055 : else {
1056 : // Only continue if the document has permission to use offline APIs.
1057 0 : if (!nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal())) {
1058 : return;
1059 : }
1060 :
1061 0 : bool fetchedWithHTTPGetOrEquiv = false;
1062 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mDocument->GetChannel()));
1063 0 : if (httpChannel) {
1064 0 : nsCAutoString method;
1065 0 : rv = httpChannel->GetRequestMethod(method);
1066 0 : if (NS_SUCCEEDED(rv))
1067 0 : fetchedWithHTTPGetOrEquiv = method.Equals("GET");
1068 : }
1069 :
1070 : rv = SelectDocAppCache(applicationCache, manifestURI,
1071 0 : fetchedWithHTTPGetOrEquiv, &action);
1072 0 : if (NS_FAILED(rv)) {
1073 : return;
1074 : }
1075 : }
1076 : }
1077 :
1078 0 : if (action == CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST) {
1079 : rv = SelectDocAppCacheNoManifest(applicationCache,
1080 0 : getter_AddRefs(manifestURI),
1081 0 : &action);
1082 0 : if (NS_FAILED(rv)) {
1083 : return;
1084 : }
1085 : }
1086 :
1087 0 : switch (action)
1088 : {
1089 : case CACHE_SELECTION_NONE:
1090 0 : break;
1091 : case CACHE_SELECTION_UPDATE: {
1092 : nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1093 0 : do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
1094 :
1095 0 : if (updateService) {
1096 0 : nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(mDocument);
1097 0 : updateService->ScheduleOnDocumentStop(manifestURI, mDocumentURI, domdoc);
1098 : }
1099 : break;
1100 : }
1101 : case CACHE_SELECTION_RELOAD: {
1102 : // This situation occurs only for toplevel documents, see bottom
1103 : // of SelectDocAppCache method.
1104 : // The document has been loaded from a different offline cache group than
1105 : // the manifest it refers to, i.e. this is a foreign entry, mark it as such
1106 : // and force a reload to avoid loading it. The next attempt will not
1107 : // choose it.
1108 :
1109 0 : applicationCacheChannel->MarkOfflineCacheEntryAsForeign();
1110 :
1111 0 : nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(mDocShell);
1112 :
1113 0 : webNav->Stop(nsIWebNavigation::STOP_ALL);
1114 0 : webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE);
1115 : break;
1116 : }
1117 : default:
1118 0 : NS_ASSERTION(false,
1119 : "Cache selection algorithm didn't decide on proper action");
1120 0 : break;
1121 : }
1122 : }
1123 :
1124 : void
1125 1038 : nsContentSink::ScrollToRef()
1126 : {
1127 1038 : mDocument->ScrollToRef();
1128 1038 : }
1129 :
1130 : void
1131 2088 : nsContentSink::StartLayout(bool aIgnorePendingSheets)
1132 : {
1133 2088 : if (mLayoutStarted) {
1134 : // Nothing to do here
1135 1038 : return;
1136 : }
1137 :
1138 1050 : mDeferredLayoutStart = true;
1139 :
1140 1050 : if (!aIgnorePendingSheets && WaitForPendingSheets()) {
1141 : // Bail out; we'll start layout when the sheets load
1142 0 : return;
1143 : }
1144 :
1145 1050 : mDeferredLayoutStart = false;
1146 :
1147 : // Notify on all our content. If none of our presshells have started layout
1148 : // yet it'll be a no-op except for updating our data structures, a la
1149 : // UpdateChildCounts() (because we don't want to double-notify on whatever we
1150 : // have right now). If some of them _have_ started layout, we want to make
1151 : // sure to flush tags instead of just calling UpdateChildCounts() after we
1152 : // loop over the shells.
1153 1050 : FlushTags();
1154 :
1155 1050 : mLayoutStarted = true;
1156 1050 : mLastNotificationTime = PR_Now();
1157 :
1158 1050 : mDocument->SetMayStartLayout(true);
1159 2100 : nsCOMPtr<nsIPresShell> shell = mDocument->GetShell();
1160 : // Make sure we don't call InitialReflow() for a shell that has
1161 : // already called it. This can happen when the layout frame for
1162 : // an iframe is constructed *between* the Embed() call for the
1163 : // docshell in the iframe, and the content sink's call to OpenBody().
1164 : // (Bug 153815)
1165 1050 : if (shell && !shell->DidInitialReflow()) {
1166 0 : nsRect r = shell->GetPresContext()->GetVisibleArea();
1167 0 : nsCOMPtr<nsIPresShell> shellGrip = shell;
1168 0 : nsresult rv = shell->InitialReflow(r.width, r.height);
1169 0 : if (NS_FAILED(rv)) {
1170 : return;
1171 : }
1172 : }
1173 :
1174 : // If the document we are loading has a reference or it is a
1175 : // frameset document, disable the scroll bars on the views.
1176 :
1177 1050 : mDocument->SetScrollToRef(mDocumentURI);
1178 : }
1179 :
1180 : void
1181 1711 : nsContentSink::NotifyAppend(nsIContent* aContainer, PRUint32 aStartIndex)
1182 : {
1183 1711 : if (aContainer->GetCurrentDoc() != mDocument) {
1184 : // aContainer is not actually in our document anymore.... Just bail out of
1185 : // here; notifying on our document for this append would be wrong.
1186 0 : return;
1187 : }
1188 :
1189 1711 : mInNotification++;
1190 :
1191 : {
1192 : // Scope so we call EndUpdate before we decrease mInNotification
1193 3422 : MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_CONTENT_MODEL, !mBeganUpdate);
1194 : nsNodeUtils::ContentAppended(aContainer,
1195 1711 : aContainer->GetChildAt(aStartIndex),
1196 3422 : aStartIndex);
1197 1711 : mLastNotificationTime = PR_Now();
1198 : }
1199 :
1200 1711 : mInNotification--;
1201 : }
1202 :
1203 : NS_IMETHODIMP
1204 0 : nsContentSink::Notify(nsITimer *timer)
1205 : {
1206 0 : if (mParsing) {
1207 : // We shouldn't interfere with our normal DidProcessAToken logic
1208 0 : mDroppedTimer = true;
1209 0 : return NS_OK;
1210 : }
1211 :
1212 : #ifdef MOZ_DEBUG
1213 : {
1214 : PRTime now = PR_Now();
1215 : PRInt64 diff, interval;
1216 : PRInt32 delay;
1217 :
1218 : LL_I2L(interval, GetNotificationInterval());
1219 : LL_SUB(diff, now, mLastNotificationTime);
1220 :
1221 : LL_SUB(diff, diff, interval);
1222 : LL_L2I(delay, diff);
1223 : delay /= PR_USEC_PER_MSEC;
1224 :
1225 : mBackoffCount--;
1226 : SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1227 : ("nsContentSink::Notify: reflow on a timer: %d milliseconds "
1228 : "late, backoff count: %d", delay, mBackoffCount));
1229 : }
1230 : #endif
1231 :
1232 0 : if (WaitForPendingSheets()) {
1233 0 : mDeferredFlushTags = true;
1234 : } else {
1235 0 : FlushTags();
1236 :
1237 : // Now try and scroll to the reference
1238 : // XXX Should we scroll unconditionally for history loads??
1239 0 : ScrollToRef();
1240 : }
1241 :
1242 0 : mNotificationTimer = nsnull;
1243 0 : return NS_OK;
1244 : }
1245 :
1246 : bool
1247 75318 : nsContentSink::IsTimeToNotify()
1248 : {
1249 75318 : if (!sNotifyOnTimer || !mLayoutStarted || !mBackoffCount ||
1250 : mInMonolithicContainer) {
1251 1793 : return false;
1252 : }
1253 :
1254 73525 : if (WaitForPendingSheets()) {
1255 0 : mDeferredFlushTags = true;
1256 0 : return false;
1257 : }
1258 :
1259 73525 : PRTime now = PR_Now();
1260 : PRInt64 interval, diff;
1261 :
1262 73525 : LL_I2L(interval, GetNotificationInterval());
1263 73525 : LL_SUB(diff, now, mLastNotificationTime);
1264 :
1265 73525 : if (LL_CMP(diff, >, interval)) {
1266 0 : mBackoffCount--;
1267 0 : return true;
1268 : }
1269 :
1270 73525 : return false;
1271 : }
1272 :
1273 : nsresult
1274 1117 : nsContentSink::WillInterruptImpl()
1275 : {
1276 1117 : nsresult result = NS_OK;
1277 :
1278 1117 : SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
1279 : ("nsContentSink::WillInterrupt: this=%p", this));
1280 : #ifndef SINK_NO_INCREMENTAL
1281 1117 : if (WaitForPendingSheets()) {
1282 0 : mDeferredFlushTags = true;
1283 1117 : } else if (sNotifyOnTimer && mLayoutStarted) {
1284 1114 : if (mBackoffCount && !mInMonolithicContainer) {
1285 1114 : PRInt64 now = PR_Now();
1286 1114 : PRInt64 interval = GetNotificationInterval();
1287 1114 : PRInt64 diff = now - mLastNotificationTime;
1288 :
1289 : // If it's already time for us to have a notification
1290 1114 : if (diff > interval || mDroppedTimer) {
1291 0 : mBackoffCount--;
1292 0 : SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1293 : ("nsContentSink::WillInterrupt: flushing tags since we've "
1294 : "run out time; backoff count: %d", mBackoffCount));
1295 0 : result = FlushTags();
1296 0 : if (mDroppedTimer) {
1297 0 : ScrollToRef();
1298 0 : mDroppedTimer = false;
1299 : }
1300 1114 : } else if (!mNotificationTimer) {
1301 976 : interval -= diff;
1302 976 : PRInt32 delay = interval;
1303 :
1304 : // Convert to milliseconds
1305 976 : delay /= PR_USEC_PER_MSEC;
1306 :
1307 : mNotificationTimer = do_CreateInstance("@mozilla.org/timer;1",
1308 976 : &result);
1309 976 : if (NS_SUCCEEDED(result)) {
1310 976 : SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1311 : ("nsContentSink::WillInterrupt: setting up timer with "
1312 : "delay %d", delay));
1313 :
1314 : result =
1315 976 : mNotificationTimer->InitWithCallback(this, delay,
1316 976 : nsITimer::TYPE_ONE_SHOT);
1317 976 : if (NS_FAILED(result)) {
1318 0 : mNotificationTimer = nsnull;
1319 : }
1320 : }
1321 : }
1322 1114 : }
1323 : } else {
1324 3 : SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1325 : ("nsContentSink::WillInterrupt: flushing tags "
1326 : "unconditionally"));
1327 3 : result = FlushTags();
1328 : }
1329 : #endif
1330 :
1331 1117 : mParsing = false;
1332 :
1333 1117 : return result;
1334 : }
1335 :
1336 : nsresult
1337 2156 : nsContentSink::WillResumeImpl()
1338 : {
1339 2156 : SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
1340 : ("nsContentSink::WillResume: this=%p", this));
1341 :
1342 2156 : mParsing = true;
1343 :
1344 2156 : return NS_OK;
1345 : }
1346 :
1347 : nsresult
1348 207594 : nsContentSink::DidProcessATokenImpl()
1349 : {
1350 207594 : if (mRunsToCompletion || !mParser) {
1351 6 : return NS_OK;
1352 : }
1353 :
1354 : // Get the current user event time
1355 207588 : nsIPresShell *shell = mDocument->GetShell();
1356 207588 : if (!shell) {
1357 : // If there's no pres shell in the document, return early since
1358 : // we're not laying anything out here.
1359 207588 : return NS_OK;
1360 : }
1361 :
1362 : // Increase before comparing to gEventProbeRate
1363 0 : ++mDeflectedCount;
1364 :
1365 : // Check if there's a pending event
1366 0 : if (sPendingEventMode != 0 && !mHasPendingEvent &&
1367 : (mDeflectedCount % sEventProbeRate) == 0) {
1368 0 : nsIViewManager* vm = shell->GetViewManager();
1369 0 : NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
1370 0 : nsCOMPtr<nsIWidget> widget;
1371 0 : vm->GetRootWidget(getter_AddRefs(widget));
1372 0 : mHasPendingEvent = widget && widget->HasPendingInputEvent();
1373 : }
1374 :
1375 0 : if (mHasPendingEvent && sPendingEventMode == 2) {
1376 0 : return NS_ERROR_HTMLPARSER_INTERRUPTED;
1377 : }
1378 :
1379 : // Have we processed enough tokens to check time?
1380 0 : if (!mHasPendingEvent &&
1381 : mDeflectedCount < PRUint32(mDynamicLowerValue ? sInteractiveDeflectCount :
1382 : sPerfDeflectCount)) {
1383 0 : return NS_OK;
1384 : }
1385 :
1386 0 : mDeflectedCount = 0;
1387 :
1388 : // Check if it's time to return to the main event loop
1389 0 : if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime) {
1390 0 : return NS_ERROR_HTMLPARSER_INTERRUPTED;
1391 : }
1392 :
1393 0 : return NS_OK;
1394 : }
1395 :
1396 : //----------------------------------------------------------------------
1397 :
1398 : void
1399 0 : nsContentSink::FavorPerformanceHint(bool perfOverStarvation, PRUint32 starvationDelay)
1400 : {
1401 : static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
1402 0 : nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
1403 0 : if (appShell)
1404 0 : appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay);
1405 0 : }
1406 :
1407 : void
1408 3994 : nsContentSink::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
1409 : {
1410 : // Remember nested updates from updates that we started.
1411 3994 : if (mInNotification > 0 && mUpdatesInNotification < 2) {
1412 3008 : ++mUpdatesInNotification;
1413 : }
1414 :
1415 : // If we're in a script and we didn't do the notification,
1416 : // something else in the script processing caused the
1417 : // notification to occur. Since this could result in frame
1418 : // creation, make sure we've flushed everything before we
1419 : // continue.
1420 :
1421 3994 : if (!mInNotification++) {
1422 986 : FlushTags();
1423 : }
1424 3994 : }
1425 :
1426 : void
1427 3994 : nsContentSink::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
1428 : {
1429 : // If we're in a script and we didn't do the notification,
1430 : // something else in the script processing caused the
1431 : // notification to occur. Update our notion of how much
1432 : // has been flushed to include any new content if ending
1433 : // this update leaves us not inside a notification.
1434 3994 : if (!--mInNotification) {
1435 986 : UpdateChildCounts();
1436 : }
1437 3994 : }
1438 :
1439 : void
1440 1038 : nsContentSink::DidBuildModelImpl(bool aTerminated)
1441 : {
1442 1038 : if (mDocument && !aTerminated) {
1443 976 : mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
1444 : }
1445 :
1446 1038 : if (mScriptLoader) {
1447 1038 : mScriptLoader->ParsingComplete(aTerminated);
1448 : }
1449 :
1450 1038 : if (!mDocument->HaveFiredDOMTitleChange()) {
1451 1038 : mDocument->NotifyPossibleTitleChange(false);
1452 : }
1453 :
1454 : // Cancel a timer if we had one out there
1455 1038 : if (mNotificationTimer) {
1456 976 : SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1457 : ("nsContentSink::DidBuildModel: canceling notification "
1458 : "timeout"));
1459 976 : mNotificationTimer->Cancel();
1460 976 : mNotificationTimer = 0;
1461 : }
1462 1038 : }
1463 :
1464 : void
1465 1272 : nsContentSink::DropParserAndPerfHint(void)
1466 : {
1467 1272 : if (!mParser) {
1468 : // Make sure we don't unblock unload too many times
1469 0 : return;
1470 : }
1471 :
1472 : // Ref. Bug 49115
1473 : // Do this hack to make sure that the parser
1474 : // doesn't get destroyed, accidently, before
1475 : // the circularity, between sink & parser, is
1476 : // actually broken.
1477 : // Drop our reference to the parser to get rid of a circular
1478 : // reference.
1479 2544 : nsRefPtr<nsParserBase> kungFuDeathGrip(mParser.forget());
1480 :
1481 1272 : if (mDynamicLowerValue) {
1482 : // Reset the performance hint which was set to FALSE
1483 : // when mDynamicLowerValue was set.
1484 0 : FavorPerformanceHint(true, 0);
1485 : }
1486 :
1487 1272 : if (!mRunsToCompletion) {
1488 1038 : mDocument->UnblockOnload(true);
1489 : }
1490 : }
1491 :
1492 : bool
1493 2154 : nsContentSink::IsScriptExecutingImpl()
1494 : {
1495 2154 : return !!mScriptLoader->GetCurrentScript();
1496 : }
1497 :
1498 : nsresult
1499 2153 : nsContentSink::WillParseImpl(void)
1500 : {
1501 2153 : if (mRunsToCompletion) {
1502 0 : return NS_OK;
1503 : }
1504 :
1505 2153 : nsIPresShell *shell = mDocument->GetShell();
1506 2153 : if (!shell) {
1507 2153 : return NS_OK;
1508 : }
1509 :
1510 0 : PRUint32 currentTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1511 :
1512 0 : if (sEnablePerfMode == 0) {
1513 0 : nsIViewManager* vm = shell->GetViewManager();
1514 0 : NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
1515 : PRUint32 lastEventTime;
1516 0 : vm->GetLastUserEventTime(lastEventTime);
1517 :
1518 : bool newDynLower =
1519 : (currentTime - mBeginLoadTime) > PRUint32(sInitialPerfTime) &&
1520 0 : (currentTime - lastEventTime) < PRUint32(sInteractiveTime);
1521 :
1522 0 : if (mDynamicLowerValue != newDynLower) {
1523 0 : FavorPerformanceHint(!newDynLower, 0);
1524 0 : mDynamicLowerValue = newDynLower;
1525 : }
1526 : }
1527 :
1528 0 : mDeflectedCount = 0;
1529 0 : mHasPendingEvent = false;
1530 :
1531 : mCurrentParseEndTime = currentTime +
1532 0 : (mDynamicLowerValue ? sInteractiveParseTime : sPerfParseTime);
1533 :
1534 0 : return NS_OK;
1535 : }
1536 :
1537 : void
1538 1038 : nsContentSink::WillBuildModelImpl()
1539 : {
1540 1038 : if (!mRunsToCompletion) {
1541 1038 : mDocument->BlockOnload();
1542 :
1543 1038 : mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1544 : }
1545 :
1546 1038 : mDocument->ResetScrolledToRefAlready();
1547 :
1548 1038 : if (mProcessLinkHeaderEvent.get()) {
1549 0 : mProcessLinkHeaderEvent.Revoke();
1550 :
1551 0 : DoProcessLinkHeader();
1552 : }
1553 1038 : }
1554 :
1555 : /* static */
1556 : void
1557 1282 : nsContentSink::NotifyDocElementCreated(nsIDocument* aDoc)
1558 : {
1559 : nsCOMPtr<nsIObserverService> observerService =
1560 2564 : mozilla::services::GetObserverService();
1561 1282 : if (observerService) {
1562 2564 : nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
1563 1282 : observerService->
1564 : NotifyObservers(domDoc, "document-element-inserted",
1565 1282 : EmptyString().get());
1566 : }
1567 5674 : }
|