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