nsContentSink.cpp (31970B)
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 14 #include <stdint.h> 15 16 #include "HTMLLinkElement.h" 17 #include "Link.h" 18 #include "MediaList.h" 19 #include "mozAutoDocUpdate.h" 20 #include "mozilla/Components.h" 21 #include "mozilla/Preferences.h" 22 #include "mozilla/PresShell.h" 23 #include "mozilla/PresShellWidgetListener.h" 24 #include "mozilla/ProfilerLabels.h" 25 #include "mozilla/RefPtr.h" 26 #include "mozilla/StaticPrefs_browser.h" 27 #include "mozilla/StaticPrefs_content.h" 28 #include "mozilla/StaticPrefs_network.h" 29 #include "mozilla/StoragePrincipalHelper.h" 30 #include "mozilla/css/Loader.h" 31 #include "mozilla/dom/Document.h" 32 #include "mozilla/dom/HTMLDNSPrefetch.h" 33 #include "mozilla/dom/LinkStyle.h" 34 #include "mozilla/dom/MutationObservers.h" 35 #include "mozilla/dom/ReferrerInfo.h" 36 #include "mozilla/dom/SRILogHelper.h" 37 #include "mozilla/dom/ScriptLoader.h" 38 #include "mozilla/dom/ServiceWorkerDescriptor.h" 39 #include "mozilla/net/HttpBaseChannel.h" 40 #include "mozilla/net/NeckoChannelParams.h" 41 #include "nsAtom.h" 42 #include "nsCOMPtr.h" 43 #include "nsContentUtils.h" 44 #include "nsGenericHTMLElement.h" 45 #include "nsGkAtoms.h" 46 #include "nsGlobalWindowInner.h" 47 #include "nsIAppShell.h" 48 #include "nsIContent.h" 49 #include "nsIContentPolicy.h" 50 #include "nsICookieService.h" 51 #include "nsIDocShell.h" 52 #include "nsIHttpChannel.h" 53 #include "nsILoadContext.h" 54 #include "nsIMIMEHeaderParam.h" 55 #include "nsIObserverService.h" 56 #include "nsIPrefetchService.h" 57 #include "nsIProtocolHandler.h" 58 #include "nsIURI.h" 59 #include "nsIWebNavigation.h" 60 #include "nsIWidget.h" 61 #include "nsLiteralString.h" 62 #include "nsNetCID.h" 63 #include "nsNetUtil.h" 64 #include "nsNodeInfoManager.h" 65 #include "nsParserConstants.h" 66 #include "nsPresContext.h" 67 #include "nsSandboxFlags.h" 68 #include "nsString.h" 69 #include "nsStringFwd.h" 70 #include "nsWidgetsCID.h" 71 using namespace mozilla; 72 using namespace mozilla::css; 73 using namespace mozilla::dom; 74 75 LazyLogModule gContentSinkLogModuleInfo("nscontentsink"); 76 77 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink) 78 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink) 79 80 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSink) 81 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver) 82 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 83 NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver) 84 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) 85 NS_INTERFACE_MAP_ENTRY(nsITimerCallback) 86 NS_INTERFACE_MAP_ENTRY(nsINamed) 87 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentObserver) 88 NS_INTERFACE_MAP_END 89 90 NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink) 91 92 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink) 93 if (tmp->mDocument) { 94 tmp->mDocument->RemoveObserver(tmp); 95 } 96 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) 97 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser) 98 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell) 99 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager) 100 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader) 101 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE 102 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 103 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink) 104 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) 105 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser) 106 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell) 107 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager) 108 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader) 109 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 110 111 nsContentSink::nsContentSink() 112 : mBackoffCount(0), 113 mLastNotificationTime(0), 114 mLayoutStarted(0), 115 mDynamicLowerValue(0), 116 mParsing(0), 117 mDroppedTimer(0), 118 mDeferredLayoutStart(0), 119 mDeferredFlushTags(0), 120 mIsDocumentObserver(0), 121 mRunsToCompletion(0), 122 mIsBlockingOnload(false), 123 mDeflectedCount(0), 124 mHasPendingEvent(false), 125 mCurrentParseEndTime(0), 126 mBeginLoadTime(0), 127 mLastSampledUserEventTime(0), 128 mInMonolithicContainer(0), 129 mInNotification(0), 130 mUpdatesInNotification(0), 131 mPendingSheetCount(0) { 132 NS_ASSERTION(!mLayoutStarted, "What?"); 133 NS_ASSERTION(!mDynamicLowerValue, "What?"); 134 NS_ASSERTION(!mParsing, "What?"); 135 NS_ASSERTION(mLastSampledUserEventTime == 0, "What?"); 136 NS_ASSERTION(mDeflectedCount == 0, "What?"); 137 NS_ASSERTION(!mDroppedTimer, "What?"); 138 NS_ASSERTION(mInMonolithicContainer == 0, "What?"); 139 NS_ASSERTION(mInNotification == 0, "What?"); 140 NS_ASSERTION(!mDeferredLayoutStart, "What?"); 141 } 142 143 nsContentSink::~nsContentSink() { 144 if (mDocument) { 145 // Remove ourselves just to be safe, though we really should have 146 // been removed in DidBuildModel if everything worked right. 147 mDocument->RemoveObserver(this); 148 } 149 } 150 151 nsresult nsContentSink::Init(Document* aDoc, nsIURI* aURI, 152 nsISupports* aContainer, nsIChannel* aChannel) { 153 MOZ_ASSERT(aDoc, "null ptr"); 154 MOZ_ASSERT(aURI, "null ptr"); 155 156 if (!aDoc || !aURI) { 157 return NS_ERROR_NULL_POINTER; 158 } 159 160 mDocument = aDoc; 161 162 mDocumentURI = aURI; 163 mDocShell = do_QueryInterface(aContainer); 164 mScriptLoader = mDocument->GetScriptLoader(); 165 166 if (!mRunsToCompletion) { 167 if (mDocShell) { 168 uint32_t loadType = 0; 169 mDocShell->GetLoadType(&loadType); 170 mDocument->SetChangeScrollPosWhenScrollingToRef( 171 (loadType & nsIDocShell::LOAD_CMD_HISTORY) == 0); 172 } 173 174 ProcessHTTPHeaders(aChannel); 175 } 176 177 mNodeInfoManager = aDoc->NodeInfoManager(); 178 179 mBackoffCount = StaticPrefs::content_notify_backoffcount(); 180 181 if (StaticPrefs::content_sink_enable_perf_mode() != 0) { 182 mDynamicLowerValue = StaticPrefs::content_sink_enable_perf_mode() == 1; 183 } 184 185 return NS_OK; 186 } 187 188 NS_IMETHODIMP 189 nsContentSink::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred, 190 nsresult aStatus) { 191 MOZ_ASSERT(!mRunsToCompletion, "How come a fragment parser observed sheets?"); 192 if (aWasDeferred) { 193 return NS_OK; 194 } 195 MOZ_ASSERT(mPendingSheetCount > 0, "How'd that happen?"); 196 --mPendingSheetCount; 197 198 const bool loadedAllSheets = !mPendingSheetCount; 199 if (loadedAllSheets && (mDeferredLayoutStart || mDeferredFlushTags)) { 200 if (mDeferredFlushTags) { 201 FlushTags(); 202 } 203 if (mDeferredLayoutStart) { 204 // We might not have really started layout, since this sheet was still 205 // loading. Do it now. Probably doesn't matter whether we do this 206 // before or after we unblock scripts, but before feels saner. Note 207 // that if mDeferredLayoutStart is true, that means any subclass 208 // StartLayout() stuff that needs to happen has already happened, so 209 // we don't need to worry about it. 210 StartLayout(false); 211 } 212 213 // Go ahead and try to scroll to our ref if we have one 214 ScrollToRef(); 215 } 216 217 if (mScriptLoader) { 218 mScriptLoader->RemoveParserBlockingScriptExecutionBlocker(); 219 220 if (loadedAllSheets && 221 mDocument->GetReadyStateEnum() >= Document::READYSTATE_INTERACTIVE) { 222 mScriptLoader->DeferCheckpointReached(); 223 } 224 } 225 226 return NS_OK; 227 } 228 229 nsresult nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel) { 230 nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aChannel)); 231 232 if (!httpchannel) { 233 return NS_OK; 234 } 235 236 bool gotEarlyHints = false; 237 if (nsCOMPtr<mozilla::net::HttpBaseChannel> baseChannel = 238 do_QueryInterface(aChannel)) { 239 nsTArray<mozilla::net::EarlyHintConnectArgs> earlyHints = 240 baseChannel->TakeEarlyHints(); 241 gotEarlyHints = !earlyHints.IsEmpty(); 242 mDocument->SetEarlyHints(std::move(earlyHints)); 243 } 244 245 // Note that the only header we care about is the "link" header, since we 246 // have all the infrastructure for kicking off stylesheet loads. 247 248 nsAutoCString linkHeader; 249 250 nsresult rv = httpchannel->GetResponseHeader("link"_ns, linkHeader); 251 bool gotLinkHeader = NS_SUCCEEDED(rv) && !linkHeader.IsEmpty(); 252 if (gotLinkHeader) { 253 mDocument->SetHeaderData(nsGkAtoms::link, 254 NS_ConvertASCIItoUTF16(linkHeader)); 255 } 256 if (gotLinkHeader || gotEarlyHints) { 257 NS_ASSERTION(!mProcessLinkHeaderEvent.get(), 258 "Already dispatched an event?"); 259 260 mProcessLinkHeaderEvent = 261 NewNonOwningRunnableMethod("nsContentSink::DoProcessLinkHeader", this, 262 &nsContentSink::DoProcessLinkHeader); 263 rv = NS_DispatchToCurrentThread(mProcessLinkHeaderEvent.get()); 264 if (NS_FAILED(rv)) { 265 mProcessLinkHeaderEvent.Forget(); 266 } 267 } 268 269 return NS_OK; 270 } 271 272 void nsContentSink::DoProcessLinkHeader() { 273 for (const auto& earlyHint : mDocument->GetEarlyHints()) { 274 ProcessLinkFromHeader(earlyHint.link(), earlyHint.earlyHintPreloaderId()); 275 } 276 277 nsAutoString value; 278 279 // Getting the header data and parsing the link header together roughly 280 // implement <https://httpwg.org/specs/rfc8288.html#parse-set>. 281 mDocument->GetHeaderData(nsGkAtoms::link, value); 282 auto linkHeaders = net::ParseLinkHeader(value); 283 284 for (const auto& linkHeader : linkHeaders) { 285 ProcessLinkFromHeader(linkHeader, 0); 286 } 287 } 288 289 nsresult nsContentSink::ProcessLinkFromHeader(const net::LinkHeader& aHeader, 290 uint64_t aEarlyHintPreloaderId) { 291 uint32_t linkTypes = LinkStyle::ParseLinkTypes(aHeader.mRel); 292 293 // The link relation may apply to a different resource, specified 294 // in the anchor parameter. For the link relations supported so far, 295 // we simply abort if the link applies to a resource different to the 296 // one we've loaded 297 if (!nsContentUtils::LinkContextIsURI(aHeader.mAnchor, 298 mDocument->GetDocumentURI())) { 299 return NS_OK; 300 } 301 302 if (nsContentUtils::PrefetchPreloadEnabled(mDocShell)) { 303 // prefetch href if relation is "next" or "prefetch" 304 if ((linkTypes & LinkStyle::eNEXT) || (linkTypes & LinkStyle::ePREFETCH)) { 305 PrefetchHref(aHeader.mHref, aHeader.mAs, aHeader.mType, aHeader.mMedia); 306 } 307 308 if (!aHeader.mHref.IsEmpty() && (linkTypes & LinkStyle::eDNS_PREFETCH)) { 309 PrefetchDNS(aHeader.mHref); 310 } 311 312 if (!aHeader.mHref.IsEmpty() && (linkTypes & LinkStyle::ePRECONNECT)) { 313 Preconnect(aHeader.mHref, aHeader.mCrossOrigin); 314 } 315 316 if (linkTypes & LinkStyle::ePRELOAD) { 317 PreloadHref(aHeader.mHref, aHeader.mAs, aHeader.mRel, aHeader.mType, 318 aHeader.mMedia, aHeader.mNonce, aHeader.mIntegrity, 319 aHeader.mSrcset, aHeader.mSizes, aHeader.mCrossOrigin, 320 aHeader.mReferrerPolicy, aEarlyHintPreloaderId, 321 aHeader.mFetchPriority); 322 } 323 324 if (linkTypes & LinkStyle::eCOMPRESSION_DICTIONARY) { 325 PreloadHref(aHeader.mHref, u"fetch"_ns, aHeader.mRel, aHeader.mType, 326 aHeader.mMedia, aHeader.mNonce, aHeader.mIntegrity, 327 aHeader.mSrcset, aHeader.mSizes, aHeader.mCrossOrigin, 328 aHeader.mReferrerPolicy, aEarlyHintPreloaderId, 329 aHeader.mFetchPriority); 330 } 331 332 if ((linkTypes & LinkStyle::eMODULE_PRELOAD) && 333 mDocument->GetScriptLoader() && 334 mDocument->GetScriptLoader()->GetModuleLoader()) { 335 PreloadModule(aHeader.mHref, aHeader.mAs, aHeader.mMedia, aHeader.mNonce, 336 aHeader.mIntegrity, aHeader.mCrossOrigin, 337 aHeader.mReferrerPolicy, aEarlyHintPreloaderId, 338 aHeader.mFetchPriority); 339 } 340 } 341 342 // is it a stylesheet link? 343 if (!(linkTypes & LinkStyle::eSTYLESHEET)) { 344 return NS_OK; 345 } 346 347 bool isAlternate = linkTypes & LinkStyle::eALTERNATE; 348 return ProcessStyleLinkFromHeader(aHeader.mHref, isAlternate, aHeader.mTitle, 349 aHeader.mIntegrity, aHeader.mType, 350 aHeader.mMedia, aHeader.mReferrerPolicy, 351 aHeader.mFetchPriority); 352 } 353 354 nsresult nsContentSink::ProcessStyleLinkFromHeader( 355 const nsAString& aHref, bool aAlternate, const nsAString& aTitle, 356 const nsAString& aIntegrity, const nsAString& aType, 357 const nsAString& aMedia, const nsAString& aReferrerPolicy, 358 const nsAString& aFetchPriority) { 359 if (aAlternate && aTitle.IsEmpty()) { 360 // alternates must have title return without error, for now 361 return NS_OK; 362 } 363 364 nsAutoString mimeType; 365 nsAutoString params; 366 nsContentUtils::SplitMimeType(aType, mimeType, params); 367 368 // see bug 18817 369 if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) { 370 // Unknown stylesheet language 371 return NS_OK; 372 } 373 374 nsCOMPtr<nsIURI> url; 375 nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr, 376 mDocument->GetDocBaseURI()); 377 378 if (NS_FAILED(rv)) { 379 // The URI is bad, move along, don't propagate the error (for now) 380 return NS_OK; 381 } 382 383 // Link header is working like a <link> node, so referrerPolicy attr should 384 // have higher priority than referrer policy from document. 385 ReferrerPolicy policy = 386 ReferrerInfo::ReferrerPolicyAttributeFromString(aReferrerPolicy); 387 nsCOMPtr<nsIReferrerInfo> referrerInfo = 388 ReferrerInfo::CreateFromDocumentAndPolicyOverride(mDocument, policy); 389 390 const FetchPriority fetchPriority = 391 nsGenericHTMLElement::ToFetchPriority(aFetchPriority); 392 393 Loader::SheetInfo info{ 394 *mDocument, 395 nullptr, 396 url.forget(), 397 nullptr, 398 referrerInfo.forget(), 399 CORS_NONE, 400 aTitle, 401 aMedia, 402 aIntegrity, 403 /* nonce = */ u""_ns, 404 aAlternate ? Loader::HasAlternateRel::Yes : Loader::HasAlternateRel::No, 405 Loader::IsInline::No, 406 Loader::IsExplicitlyEnabled::No, 407 fetchPriority, 408 }; 409 410 auto loadResultOrErr = mDocument->EnsureCSSLoader().LoadStyleLink( 411 info, mRunsToCompletion ? nullptr : this); 412 if (loadResultOrErr.isErr()) { 413 return loadResultOrErr.unwrapErr(); 414 } 415 416 if (loadResultOrErr.inspect().ShouldBlock() && !mRunsToCompletion) { 417 ++mPendingSheetCount; 418 if (mScriptLoader) { 419 mScriptLoader->AddParserBlockingScriptExecutionBlocker(); 420 } 421 } 422 423 return NS_OK; 424 } 425 426 void nsContentSink::PrefetchHref(const nsAString& aHref, const nsAString& aAs, 427 const nsAString& aType, 428 const nsAString& aMedia) { 429 nsCOMPtr<nsIPrefetchService> prefetchService(components::Prefetch::Service()); 430 if (prefetchService) { 431 // construct URI using document charset 432 auto encoding = mDocument->GetDocumentCharacterSet(); 433 nsCOMPtr<nsIURI> uri; 434 NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI()); 435 if (uri) { 436 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*mDocument); 437 referrerInfo = referrerInfo->CloneWithNewOriginalReferrer(mDocumentURI); 438 439 prefetchService->PrefetchURI(uri, referrerInfo, mDocument, true); 440 } 441 } 442 } 443 444 void nsContentSink::PreloadHref( 445 const nsAString& aHref, const nsAString& aAs, const nsAString& aRel, 446 const nsAString& aType, const nsAString& aMedia, const nsAString& aNonce, 447 const nsAString& aIntegrity, const nsAString& aSrcset, 448 const nsAString& aSizes, const nsAString& aCORS, 449 const nsAString& aReferrerPolicy, uint64_t aEarlyHintPreloaderId, 450 const nsAString& aFetchPriority) { 451 auto encoding = mDocument->GetDocumentCharacterSet(); 452 nsCOMPtr<nsIURI> uri; 453 NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI()); 454 if (!uri) { 455 // URL parsing failed. 456 return; 457 } 458 459 nsAttrValue asAttr; 460 mozilla::net::ParseAsValue(aAs, asAttr); 461 462 nsAutoString mimeType; 463 nsAutoString notUsed; 464 nsContentUtils::SplitMimeType(aType, mimeType, notUsed); 465 466 auto policyType = mozilla::net::AsValueToContentPolicy(asAttr); 467 if (policyType == nsIContentPolicy::TYPE_INVALID || 468 !mozilla::net::CheckPreloadAttrs(asAttr, mimeType, aMedia, mDocument)) { 469 // Ignore preload wrong or empty attributes. 470 mozilla::net::WarnIgnoredPreload(*mDocument, *uri); 471 return; 472 } 473 474 mDocument->Preloads().PreloadLinkHeader( 475 uri, aHref, policyType, aAs, aRel, aType, aNonce, aIntegrity, aSrcset, 476 aSizes, aCORS, aReferrerPolicy, aEarlyHintPreloaderId, aFetchPriority); 477 } 478 479 void nsContentSink::PreloadModule( 480 const nsAString& aHref, const nsAString& aAs, const nsAString& aMedia, 481 const nsAString& aNonce, const nsAString& aIntegrity, 482 const nsAString& aCORS, const nsAString& aReferrerPolicy, 483 uint64_t aEarlyHintPreloaderId, const nsAString& aFetchPriority) { 484 dom::ScriptLoader* scriptLoader = mDocument->GetScriptLoader(); 485 if (!scriptLoader) { 486 return; 487 } 488 ModuleLoader* moduleLoader = scriptLoader->GetModuleLoader(); 489 490 if (!StaticPrefs::network_modulepreload()) { 491 // Keep behavior from https://phabricator.services.mozilla.com/D149371, 492 // prior to main implementation of modulepreload 493 moduleLoader->DisallowImportMaps(); 494 return; 495 } 496 497 RefPtr<mozilla::dom::MediaList> mediaList = 498 mozilla::dom::MediaList::Create(NS_ConvertUTF16toUTF8(aMedia)); 499 if (!mediaList->Matches(*mDocument)) { 500 return; 501 } 502 503 if (aHref.IsEmpty()) { 504 return; 505 } 506 507 if (!net::IsScriptLikeOrInvalid(aAs)) { 508 return; 509 } 510 511 auto encoding = mDocument->GetDocumentCharacterSet(); 512 nsCOMPtr<nsIURI> uri; 513 NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI()); 514 if (!uri) { 515 return; 516 } 517 518 moduleLoader->DisallowImportMaps(); 519 520 mDocument->Preloads().PreloadLinkHeader( 521 uri, aHref, nsIContentPolicy::TYPE_SCRIPT, u"script"_ns, 522 u"modulepreload"_ns, u"module"_ns, aNonce, aIntegrity, u""_ns, u""_ns, 523 aCORS, aReferrerPolicy, aEarlyHintPreloaderId, aFetchPriority); 524 } 525 526 void nsContentSink::PrefetchDNS(const nsAString& aHref) { 527 nsAutoString hostname; 528 bool isHttps = false; 529 530 if (StringBeginsWith(aHref, u"//"_ns)) { 531 hostname = Substring(aHref, 2); 532 } else { 533 nsCOMPtr<nsIURI> uri; 534 NS_NewURI(getter_AddRefs(uri), aHref); 535 if (!uri) { 536 return; 537 } 538 nsresult rv; 539 bool isLocalResource = false; 540 rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, 541 &isLocalResource); 542 if (NS_SUCCEEDED(rv) && !isLocalResource) { 543 nsAutoCString host; 544 uri->GetHost(host); 545 CopyUTF8toUTF16(host, hostname); 546 } 547 isHttps = uri->SchemeIs("https"); 548 } 549 550 if (!hostname.IsEmpty() && HTMLDNSPrefetch::IsAllowed(mDocument)) { 551 OriginAttributes oa; 552 StoragePrincipalHelper::GetOriginAttributesForNetworkState(mDocument, oa); 553 554 HTMLDNSPrefetch::Prefetch(hostname, isHttps, oa, 555 mDocument->GetChannel()->GetTRRMode(), 556 HTMLDNSPrefetch::Priority::Low); 557 } 558 } 559 560 void nsContentSink::Preconnect(const nsAString& aHref, 561 const nsAString& aCrossOrigin) { 562 // construct URI using document charset 563 auto encoding = mDocument->GetDocumentCharacterSet(); 564 nsCOMPtr<nsIURI> uri; 565 NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI()); 566 567 if (uri && mDocument) { 568 mDocument->MaybePreconnect(uri, 569 dom::Element::StringToCORSMode(aCrossOrigin)); 570 } 571 } 572 573 void nsContentSink::ScrollToRef() { 574 RefPtr<Document> document = mDocument; 575 document->ScrollToRef(); 576 } 577 578 void nsContentSink::StartLayout(bool aIgnorePendingSheets) { 579 if (mLayoutStarted) { 580 // Nothing to do here 581 return; 582 } 583 584 mDeferredLayoutStart = true; 585 586 if (!aIgnorePendingSheets && 587 (WaitForPendingSheets() || mDocument->HasPendingInitialTranslation())) { 588 // Bail out; we'll start layout when the sheets and l10n load 589 return; 590 } 591 592 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS( 593 "Layout", LAYOUT, mDocumentURI->GetSpecOrDefault()); 594 595 mDeferredLayoutStart = false; 596 597 if (aIgnorePendingSheets) { 598 nsContentUtils::ReportToConsole( 599 nsIScriptError::warningFlag, "Layout"_ns, mDocument, 600 nsContentUtils::eLAYOUT_PROPERTIES, "ForcedLayoutStart"); 601 } 602 603 // Notify on all our content. If none of our presshells have started layout 604 // yet it'll be a no-op except for updating our data structures, a la 605 // UpdateChildCounts() (because we don't want to double-notify on whatever we 606 // have right now). If some of them _have_ started layout, we want to make 607 // sure to flush tags instead of just calling UpdateChildCounts() after we 608 // loop over the shells. 609 FlushTags(); 610 611 mLayoutStarted = true; 612 mLastNotificationTime = PR_Now(); 613 614 mDocument->SetMayStartLayout(true); 615 RefPtr<PresShell> presShell = mDocument->GetPresShell(); 616 // Make sure we don't call Initialize() for a shell that has 617 // already called it. This can happen when the layout frame for 618 // an iframe is constructed *between* the Embed() call for the 619 // docshell in the iframe, and the content sink's call to OpenBody(). 620 // (Bug 153815) 621 if (presShell && !presShell->DidInitialize()) { 622 nsresult rv = presShell->Initialize(); 623 if (NS_FAILED(rv)) { 624 return; 625 } 626 } 627 628 // If the document we are loading has a reference or it is a 629 // frameset document, disable the scroll bars on the views. 630 631 mDocument->SetScrollToRef(mDocument->GetDocumentURI()); 632 } 633 634 void nsContentSink::NotifyAppend(nsIContent* aContainer, uint32_t aStartIndex) { 635 mInNotification++; 636 637 { 638 // Scope so we call EndUpdate before we decrease mInNotification 639 // 640 // Note that aContainer->OwnerDoc() may not be mDocument. 641 MOZ_AUTO_DOC_UPDATE(aContainer->OwnerDoc(), true); 642 MutationObservers::NotifyContentAppended( 643 aContainer, aContainer->GetChildAt_Deprecated(aStartIndex), {}); 644 mLastNotificationTime = PR_Now(); 645 } 646 647 mInNotification--; 648 } 649 650 NS_IMETHODIMP 651 nsContentSink::Notify(nsITimer* timer) { 652 if (mParsing) { 653 // We shouldn't interfere with our normal DidProcessAToken logic 654 mDroppedTimer = true; 655 return NS_OK; 656 } 657 658 if (WaitForPendingSheets()) { 659 mDeferredFlushTags = true; 660 } else { 661 FlushTags(); 662 663 // Now try and scroll to the reference 664 // XXX Should we scroll unconditionally for history loads?? 665 ScrollToRef(); 666 } 667 668 mNotificationTimer = nullptr; 669 return NS_OK; 670 } 671 672 bool nsContentSink::IsTimeToNotify() { 673 if (!StaticPrefs::content_notify_ontimer() || !mLayoutStarted || 674 !mBackoffCount || mInMonolithicContainer) { 675 return false; 676 } 677 678 if (WaitForPendingSheets()) { 679 mDeferredFlushTags = true; 680 return false; 681 } 682 683 PRTime now = PR_Now(); 684 685 int64_t interval = GetNotificationInterval(); 686 int64_t diff = now - mLastNotificationTime; 687 688 if (diff > interval) { 689 mBackoffCount--; 690 return true; 691 } 692 693 return false; 694 } 695 696 nsresult nsContentSink::WillInterruptImpl() { 697 nsresult result = NS_OK; 698 699 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), 700 SINK_TRACE_CALLS, ("nsContentSink::WillInterrupt: this=%p", this)); 701 #ifndef SINK_NO_INCREMENTAL 702 if (WaitForPendingSheets()) { 703 mDeferredFlushTags = true; 704 } else if (StaticPrefs::content_notify_ontimer() && mLayoutStarted) { 705 if (mBackoffCount && !mInMonolithicContainer) { 706 int64_t now = PR_Now(); 707 int64_t interval = GetNotificationInterval(); 708 int64_t diff = now - mLastNotificationTime; 709 710 // If it's already time for us to have a notification 711 if (diff > interval || mDroppedTimer) { 712 mBackoffCount--; 713 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), 714 SINK_TRACE_REFLOW, 715 ("nsContentSink::WillInterrupt: flushing tags since we've " 716 "run out time; backoff count: %d", 717 mBackoffCount)); 718 result = FlushTags(); 719 if (mDroppedTimer) { 720 ScrollToRef(); 721 mDroppedTimer = false; 722 } 723 } else if (!mNotificationTimer) { 724 interval -= diff; 725 int32_t delay = interval; 726 727 // Convert to milliseconds 728 delay /= PR_USEC_PER_MSEC; 729 730 NS_NewTimerWithCallback(getter_AddRefs(mNotificationTimer), this, delay, 731 nsITimer::TYPE_ONE_SHOT); 732 if (mNotificationTimer) { 733 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), 734 SINK_TRACE_REFLOW, 735 ("nsContentSink::WillInterrupt: setting up timer with " 736 "delay %d", 737 delay)); 738 } 739 } 740 } 741 } else { 742 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), 743 SINK_TRACE_REFLOW, 744 ("nsContentSink::WillInterrupt: flushing tags " 745 "unconditionally")); 746 result = FlushTags(); 747 } 748 #endif 749 750 mParsing = false; 751 752 return result; 753 } 754 755 void nsContentSink::WillResumeImpl() { 756 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), 757 SINK_TRACE_CALLS, ("nsContentSink::WillResume: this=%p", this)); 758 759 mParsing = true; 760 } 761 762 nsresult nsContentSink::DidProcessATokenImpl() { 763 if (mRunsToCompletion || !mParser) { 764 return NS_OK; 765 } 766 767 // Get the current user event time 768 PresShell* presShell = mDocument->GetPresShell(); 769 if (!presShell) { 770 // If there's no pres shell in the document, return early since 771 // we're not laying anything out here. 772 return NS_OK; 773 } 774 775 // Increase before comparing to gEventProbeRate 776 ++mDeflectedCount; 777 778 // Check if there's a pending event 779 if (StaticPrefs::content_sink_pending_event_mode() != 0 && 780 !mHasPendingEvent && 781 (mDeflectedCount % StaticPrefs::content_sink_event_probe_rate()) == 0) { 782 nsIWidget* widget = presShell->GetRootWidget(); 783 mHasPendingEvent = widget && widget->HasPendingInputEvent(); 784 } 785 786 if (mHasPendingEvent && StaticPrefs::content_sink_pending_event_mode() == 2) { 787 return NS_ERROR_HTMLPARSER_INTERRUPTED; 788 } 789 790 // Have we processed enough tokens to check time? 791 if (!mHasPendingEvent && 792 mDeflectedCount < 793 uint32_t(mDynamicLowerValue 794 ? StaticPrefs::content_sink_interactive_deflect_count() 795 : StaticPrefs::content_sink_perf_deflect_count())) { 796 return NS_OK; 797 } 798 799 mDeflectedCount = 0; 800 801 // Check if it's time to return to the main event loop 802 if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime) { 803 return NS_ERROR_HTMLPARSER_INTERRUPTED; 804 } 805 806 return NS_OK; 807 } 808 809 //---------------------------------------------------------------------- 810 811 void nsContentSink::BeginUpdate(Document* aDocument) { 812 // Remember nested updates from updates that we started. 813 if (mInNotification > 0 && mUpdatesInNotification < 2) { 814 ++mUpdatesInNotification; 815 } 816 817 // If we're in a script and we didn't do the notification, 818 // something else in the script processing caused the 819 // notification to occur. Since this could result in frame 820 // creation, make sure we've flushed everything before we 821 // continue. 822 823 if (!mInNotification++) { 824 FlushTags(); 825 } 826 } 827 828 void nsContentSink::EndUpdate(Document* aDocument) { 829 // If we're in a script and we didn't do the notification, 830 // something else in the script processing caused the 831 // notification to occur. Update our notion of how much 832 // has been flushed to include any new content if ending 833 // this update leaves us not inside a notification. 834 if (!--mInNotification) { 835 UpdateChildCounts(); 836 } 837 } 838 839 void nsContentSink::DidBuildModelImpl(bool aTerminated) { 840 MOZ_ASSERT(aTerminated || (mParser && mParser->IsParserClosed()) || 841 mDocument->GetReadyStateEnum() == Document::READYSTATE_LOADING, 842 "Bad readyState"); 843 mDocument->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE); 844 845 if (mScriptLoader) { 846 mScriptLoader->ParsingComplete(aTerminated); 847 if (!mPendingSheetCount) { 848 mScriptLoader->DeferCheckpointReached(); 849 } 850 } 851 852 if (!mDocument->HaveFiredDOMTitleChange()) { 853 mDocument->NotifyPossibleTitleChange(false); 854 } 855 856 // Cancel a timer if we had one out there 857 if (mNotificationTimer) { 858 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), 859 SINK_TRACE_REFLOW, 860 ("nsContentSink::DidBuildModel: canceling notification " 861 "timeout")); 862 mNotificationTimer->Cancel(); 863 mNotificationTimer = nullptr; 864 } 865 } 866 867 void nsContentSink::DropParserAndPerfHint(void) { 868 if (!mParser) { 869 // Make sure we don't unblock unload too many times 870 return; 871 } 872 873 // Ref. Bug 49115 874 // Do this hack to make sure that the parser 875 // doesn't get destroyed, accidently, before 876 // the circularity, between sink & parser, is 877 // actually broken. 878 // Drop our reference to the parser to get rid of a circular 879 // reference. 880 RefPtr<nsParserBase> kungFuDeathGrip = std::move(mParser); 881 (void)kungFuDeathGrip; 882 883 // Call UnblockOnload only if mRunsToComletion is false and if 884 // we have already started loading because it's possible that this function 885 // is called (i.e. the parser is terminated) before we start loading due to 886 // destroying the window inside unload event callbacks for the previous 887 // document. 888 if (!mRunsToCompletion && mIsBlockingOnload) { 889 mDocument->UnblockOnload(true); 890 mIsBlockingOnload = false; 891 } 892 } 893 894 bool nsContentSink::IsScriptExecutingImpl() { 895 return mScriptLoader && mScriptLoader->GetCurrentScript(); 896 } 897 898 void nsContentSink::ContinueParsingDocumentAfterCurrentScriptImpl() { 899 if (mScriptLoader) { 900 mScriptLoader->ContinueParsingDocumentAfterCurrentScript(); 901 } 902 } 903 904 nsresult nsContentSink::WillParseImpl(void) { 905 if (mRunsToCompletion || !mDocument) { 906 return NS_OK; 907 } 908 909 PresShell* presShell = mDocument->GetPresShell(); 910 if (!presShell) { 911 return NS_OK; 912 } 913 914 uint32_t currentTime = PR_IntervalToMicroseconds(PR_IntervalNow()); 915 916 if (StaticPrefs::content_sink_enable_perf_mode() == 0) { 917 uint32_t lastEventTime = PresShellWidgetListener::GetLastUserEventTime(); 918 bool newDynLower = mDocument->IsInBackgroundWindow() || 919 ((currentTime - mBeginLoadTime) > 920 StaticPrefs::content_sink_initial_perf_time() && 921 (currentTime - lastEventTime) < 922 StaticPrefs::content_sink_interactive_time()); 923 924 if (mDynamicLowerValue != newDynLower) { 925 mDynamicLowerValue = newDynLower; 926 } 927 } 928 929 mDeflectedCount = 0; 930 mHasPendingEvent = false; 931 932 mCurrentParseEndTime = 933 currentTime + (mDynamicLowerValue 934 ? StaticPrefs::content_sink_interactive_parse_time() 935 : StaticPrefs::content_sink_perf_parse_time()); 936 937 return NS_OK; 938 } 939 940 void nsContentSink::WillBuildModelImpl() { 941 if (!mRunsToCompletion) { 942 mDocument->BlockOnload(); 943 mIsBlockingOnload = true; 944 945 mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow()); 946 } 947 948 mDocument->ResetScrolledToRefAlready(); 949 950 if (mProcessLinkHeaderEvent.get()) { 951 mProcessLinkHeaderEvent.Revoke(); 952 953 DoProcessLinkHeader(); 954 } 955 } 956 957 /* static */ 958 void nsContentSink::NotifyDocElementCreated(Document* aDoc) { 959 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); 960 961 nsCOMPtr<nsIObserverService> observerService = 962 mozilla::services::GetObserverService(); 963 MOZ_ASSERT(observerService); 964 965 auto* win = nsGlobalWindowInner::Cast(aDoc->GetInnerWindow()); 966 bool fireInitialInsertion = !win || !win->DidFireDocElemInserted(); 967 if (win) { 968 win->SetDidFireDocElemInserted(); 969 } 970 if (fireInitialInsertion) { 971 observerService->NotifyObservers(ToSupports(aDoc), 972 "initial-document-element-inserted", u""); 973 } 974 observerService->NotifyObservers(ToSupports(aDoc), 975 "document-element-inserted", u""); 976 977 nsContentUtils::DispatchChromeEvent(aDoc, aDoc, u"DOMDocElementInserted"_ns, 978 CanBubble::eYes, Cancelable::eNo); 979 } 980 981 NS_IMETHODIMP 982 nsContentSink::GetName(nsACString& aName) { 983 aName.AssignLiteral("nsContentSink_timer"); 984 return NS_OK; 985 }