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