content/base/src/nsContentSink.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial