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