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