Coverage Report

Created: 2018-09-25 14:53

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