1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsContentSink.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1552 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=78: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * Base class for the XML and HTML content sinks, which construct a 1.12 + * DOM based on information from the parser. 1.13 + */ 1.14 + 1.15 +#include "nsContentSink.h" 1.16 +#include "nsScriptLoader.h" 1.17 +#include "nsIDocument.h" 1.18 +#include "nsIDOMDocument.h" 1.19 +#include "mozilla/css/Loader.h" 1.20 +#include "nsStyleLinkElement.h" 1.21 +#include "nsIDocShell.h" 1.22 +#include "nsILoadContext.h" 1.23 +#include "nsCPrefetchService.h" 1.24 +#include "nsIURI.h" 1.25 +#include "nsNetUtil.h" 1.26 +#include "nsIHttpChannel.h" 1.27 +#include "nsIContent.h" 1.28 +#include "nsIPresShell.h" 1.29 +#include "nsPresContext.h" 1.30 +#include "nsViewManager.h" 1.31 +#include "nsIAtom.h" 1.32 +#include "nsGkAtoms.h" 1.33 +#include "nsNetCID.h" 1.34 +#include "nsIOfflineCacheUpdate.h" 1.35 +#include "nsIApplicationCache.h" 1.36 +#include "nsIApplicationCacheContainer.h" 1.37 +#include "nsIApplicationCacheChannel.h" 1.38 +#include "nsIScriptSecurityManager.h" 1.39 +#include "nsICookieService.h" 1.40 +#include "nsContentUtils.h" 1.41 +#include "nsNodeInfoManager.h" 1.42 +#include "nsIAppShell.h" 1.43 +#include "nsIWidget.h" 1.44 +#include "nsWidgetsCID.h" 1.45 +#include "nsIDOMNode.h" 1.46 +#include "mozAutoDocUpdate.h" 1.47 +#include "nsIWebNavigation.h" 1.48 +#include "nsGenericHTMLElement.h" 1.49 +#include "nsHTMLDNSPrefetch.h" 1.50 +#include "nsIObserverService.h" 1.51 +#include "mozilla/Preferences.h" 1.52 +#include "nsParserConstants.h" 1.53 + 1.54 +using namespace mozilla; 1.55 + 1.56 +PRLogModuleInfo* gContentSinkLogModuleInfo; 1.57 + 1.58 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink) 1.59 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink) 1.60 + 1.61 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSink) 1.62 + NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver) 1.63 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.64 + NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver) 1.65 + NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) 1.66 + NS_INTERFACE_MAP_ENTRY(nsITimerCallback) 1.67 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentObserver) 1.68 +NS_INTERFACE_MAP_END 1.69 + 1.70 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink) 1.71 + 1.72 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink) 1.73 + if (tmp->mDocument) { 1.74 + tmp->mDocument->RemoveObserver(tmp); 1.75 + } 1.76 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) 1.77 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser) 1.78 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager) 1.79 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader) 1.80 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.81 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink) 1.82 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) 1.83 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser) 1.84 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager) 1.85 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader) 1.86 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.87 + 1.88 + 1.89 +nsContentSink::nsContentSink() 1.90 +{ 1.91 + // We have a zeroing operator new 1.92 + NS_ASSERTION(!mLayoutStarted, "What?"); 1.93 + NS_ASSERTION(!mDynamicLowerValue, "What?"); 1.94 + NS_ASSERTION(!mParsing, "What?"); 1.95 + NS_ASSERTION(mLastSampledUserEventTime == 0, "What?"); 1.96 + NS_ASSERTION(mDeflectedCount == 0, "What?"); 1.97 + NS_ASSERTION(!mDroppedTimer, "What?"); 1.98 + NS_ASSERTION(mInMonolithicContainer == 0, "What?"); 1.99 + NS_ASSERTION(mInNotification == 0, "What?"); 1.100 + NS_ASSERTION(!mDeferredLayoutStart, "What?"); 1.101 + 1.102 +#ifdef DEBUG 1.103 + if (!gContentSinkLogModuleInfo) { 1.104 + gContentSinkLogModuleInfo = PR_NewLogModule("nscontentsink"); 1.105 + } 1.106 +#endif 1.107 +} 1.108 + 1.109 +nsContentSink::~nsContentSink() 1.110 +{ 1.111 + if (mDocument) { 1.112 + // Remove ourselves just to be safe, though we really should have 1.113 + // been removed in DidBuildModel if everything worked right. 1.114 + mDocument->RemoveObserver(this); 1.115 + } 1.116 +} 1.117 + 1.118 +bool nsContentSink::sNotifyOnTimer; 1.119 +int32_t nsContentSink::sBackoffCount; 1.120 +int32_t nsContentSink::sNotificationInterval; 1.121 +int32_t nsContentSink::sInteractiveDeflectCount; 1.122 +int32_t nsContentSink::sPerfDeflectCount; 1.123 +int32_t nsContentSink::sPendingEventMode; 1.124 +int32_t nsContentSink::sEventProbeRate; 1.125 +int32_t nsContentSink::sInteractiveParseTime; 1.126 +int32_t nsContentSink::sPerfParseTime; 1.127 +int32_t nsContentSink::sInteractiveTime; 1.128 +int32_t nsContentSink::sInitialPerfTime; 1.129 +int32_t nsContentSink::sEnablePerfMode; 1.130 + 1.131 +void 1.132 +nsContentSink::InitializeStatics() 1.133 +{ 1.134 + Preferences::AddBoolVarCache(&sNotifyOnTimer, 1.135 + "content.notify.ontimer", true); 1.136 + // -1 means never. 1.137 + Preferences::AddIntVarCache(&sBackoffCount, 1.138 + "content.notify.backoffcount", -1); 1.139 + // The gNotificationInterval has a dramatic effect on how long it 1.140 + // takes to initially display content for slow connections. 1.141 + // The current value provides good 1.142 + // incremental display of content without causing an increase 1.143 + // in page load time. If this value is set below 1/10 of second 1.144 + // it starts to impact page load performance. 1.145 + // see bugzilla bug 72138 for more info. 1.146 + Preferences::AddIntVarCache(&sNotificationInterval, 1.147 + "content.notify.interval", 120000); 1.148 + Preferences::AddIntVarCache(&sInteractiveDeflectCount, 1.149 + "content.sink.interactive_deflect_count", 0); 1.150 + Preferences::AddIntVarCache(&sPerfDeflectCount, 1.151 + "content.sink.perf_deflect_count", 200); 1.152 + Preferences::AddIntVarCache(&sPendingEventMode, 1.153 + "content.sink.pending_event_mode", 1); 1.154 + Preferences::AddIntVarCache(&sEventProbeRate, 1.155 + "content.sink.event_probe_rate", 1); 1.156 + Preferences::AddIntVarCache(&sInteractiveParseTime, 1.157 + "content.sink.interactive_parse_time", 3000); 1.158 + Preferences::AddIntVarCache(&sPerfParseTime, 1.159 + "content.sink.perf_parse_time", 360000); 1.160 + Preferences::AddIntVarCache(&sInteractiveTime, 1.161 + "content.sink.interactive_time", 750000); 1.162 + Preferences::AddIntVarCache(&sInitialPerfTime, 1.163 + "content.sink.initial_perf_time", 2000000); 1.164 + Preferences::AddIntVarCache(&sEnablePerfMode, 1.165 + "content.sink.enable_perf_mode", 0); 1.166 +} 1.167 + 1.168 +nsresult 1.169 +nsContentSink::Init(nsIDocument* aDoc, 1.170 + nsIURI* aURI, 1.171 + nsISupports* aContainer, 1.172 + nsIChannel* aChannel) 1.173 +{ 1.174 + NS_PRECONDITION(aDoc, "null ptr"); 1.175 + NS_PRECONDITION(aURI, "null ptr"); 1.176 + 1.177 + if (!aDoc || !aURI) { 1.178 + return NS_ERROR_NULL_POINTER; 1.179 + } 1.180 + 1.181 + mDocument = aDoc; 1.182 + 1.183 + mDocumentURI = aURI; 1.184 + mDocShell = do_QueryInterface(aContainer); 1.185 + mScriptLoader = mDocument->ScriptLoader(); 1.186 + 1.187 + if (!mRunsToCompletion) { 1.188 + if (mDocShell) { 1.189 + uint32_t loadType = 0; 1.190 + mDocShell->GetLoadType(&loadType); 1.191 + mDocument->SetChangeScrollPosWhenScrollingToRef( 1.192 + (loadType & nsIDocShell::LOAD_CMD_HISTORY) == 0); 1.193 + } 1.194 + 1.195 + ProcessHTTPHeaders(aChannel); 1.196 + } 1.197 + 1.198 + mCSSLoader = aDoc->CSSLoader(); 1.199 + 1.200 + mNodeInfoManager = aDoc->NodeInfoManager(); 1.201 + 1.202 + mBackoffCount = sBackoffCount; 1.203 + 1.204 + if (sEnablePerfMode != 0) { 1.205 + mDynamicLowerValue = sEnablePerfMode == 1; 1.206 + FavorPerformanceHint(!mDynamicLowerValue, 0); 1.207 + } 1.208 + 1.209 + return NS_OK; 1.210 +} 1.211 + 1.212 +NS_IMETHODIMP 1.213 +nsContentSink::StyleSheetLoaded(nsCSSStyleSheet* aSheet, 1.214 + bool aWasAlternate, 1.215 + nsresult aStatus) 1.216 +{ 1.217 + NS_ASSERTION(!mRunsToCompletion, "How come a fragment parser observed sheets?"); 1.218 + if (!aWasAlternate) { 1.219 + NS_ASSERTION(mPendingSheetCount > 0, "How'd that happen?"); 1.220 + --mPendingSheetCount; 1.221 + 1.222 + if (mPendingSheetCount == 0 && 1.223 + (mDeferredLayoutStart || mDeferredFlushTags)) { 1.224 + if (mDeferredFlushTags) { 1.225 + FlushTags(); 1.226 + } 1.227 + if (mDeferredLayoutStart) { 1.228 + // We might not have really started layout, since this sheet was still 1.229 + // loading. Do it now. Probably doesn't matter whether we do this 1.230 + // before or after we unblock scripts, but before feels saner. Note 1.231 + // that if mDeferredLayoutStart is true, that means any subclass 1.232 + // StartLayout() stuff that needs to happen has already happened, so we 1.233 + // don't need to worry about it. 1.234 + StartLayout(false); 1.235 + } 1.236 + 1.237 + // Go ahead and try to scroll to our ref if we have one 1.238 + ScrollToRef(); 1.239 + } 1.240 + 1.241 + mScriptLoader->RemoveExecuteBlocker(); 1.242 + } 1.243 + 1.244 + return NS_OK; 1.245 +} 1.246 + 1.247 +nsresult 1.248 +nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel) 1.249 +{ 1.250 + nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aChannel)); 1.251 + 1.252 + if (!httpchannel) { 1.253 + return NS_OK; 1.254 + } 1.255 + 1.256 + // Note that the only header we care about is the "link" header, since we 1.257 + // have all the infrastructure for kicking off stylesheet loads. 1.258 + 1.259 + nsAutoCString linkHeader; 1.260 + 1.261 + nsresult rv = httpchannel->GetResponseHeader(NS_LITERAL_CSTRING("link"), 1.262 + linkHeader); 1.263 + if (NS_SUCCEEDED(rv) && !linkHeader.IsEmpty()) { 1.264 + mDocument->SetHeaderData(nsGkAtoms::link, 1.265 + NS_ConvertASCIItoUTF16(linkHeader)); 1.266 + 1.267 + NS_ASSERTION(!mProcessLinkHeaderEvent.get(), 1.268 + "Already dispatched an event?"); 1.269 + 1.270 + mProcessLinkHeaderEvent = 1.271 + NS_NewNonOwningRunnableMethod(this, 1.272 + &nsContentSink::DoProcessLinkHeader); 1.273 + rv = NS_DispatchToCurrentThread(mProcessLinkHeaderEvent.get()); 1.274 + if (NS_FAILED(rv)) { 1.275 + mProcessLinkHeaderEvent.Forget(); 1.276 + } 1.277 + } 1.278 + 1.279 + return NS_OK; 1.280 +} 1.281 + 1.282 +nsresult 1.283 +nsContentSink::ProcessHeaderData(nsIAtom* aHeader, const nsAString& aValue, 1.284 + nsIContent* aContent) 1.285 +{ 1.286 + nsresult rv = NS_OK; 1.287 + // necko doesn't process headers coming in from the parser 1.288 + 1.289 + mDocument->SetHeaderData(aHeader, aValue); 1.290 + 1.291 + if (aHeader == nsGkAtoms::setcookie) { 1.292 + // Note: Necko already handles cookies set via the channel. We can't just 1.293 + // call SetCookie on the channel because we want to do some security checks 1.294 + // here. 1.295 + nsCOMPtr<nsICookieService> cookieServ = 1.296 + do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv); 1.297 + if (NS_FAILED(rv)) { 1.298 + return rv; 1.299 + } 1.300 + 1.301 + // Get a URI from the document principal 1.302 + 1.303 + // We use the original codebase in case the codebase was changed 1.304 + // by SetDomain 1.305 + 1.306 + // Note that a non-codebase principal (eg the system principal) will return 1.307 + // a null URI. 1.308 + nsCOMPtr<nsIURI> codebaseURI; 1.309 + rv = mDocument->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI)); 1.310 + NS_ENSURE_TRUE(codebaseURI, rv); 1.311 + 1.312 + nsCOMPtr<nsIChannel> channel; 1.313 + if (mParser) { 1.314 + mParser->GetChannel(getter_AddRefs(channel)); 1.315 + } 1.316 + 1.317 + rv = cookieServ->SetCookieString(codebaseURI, 1.318 + nullptr, 1.319 + NS_ConvertUTF16toUTF8(aValue).get(), 1.320 + channel); 1.321 + if (NS_FAILED(rv)) { 1.322 + return rv; 1.323 + } 1.324 + } 1.325 + else if (aHeader == nsGkAtoms::msthemecompatible) { 1.326 + // Disable theming for the presshell if the value is no. 1.327 + // XXXbz don't we want to support this as an HTTP header too? 1.328 + nsAutoString value(aValue); 1.329 + if (value.LowerCaseEqualsLiteral("no")) { 1.330 + nsIPresShell* shell = mDocument->GetShell(); 1.331 + if (shell) { 1.332 + shell->DisableThemeSupport(); 1.333 + } 1.334 + } 1.335 + } 1.336 + 1.337 + return rv; 1.338 +} 1.339 + 1.340 + 1.341 +void 1.342 +nsContentSink::DoProcessLinkHeader() 1.343 +{ 1.344 + nsAutoString value; 1.345 + mDocument->GetHeaderData(nsGkAtoms::link, value); 1.346 + ProcessLinkHeader(value); 1.347 +} 1.348 + 1.349 +// check whether the Link header field applies to the context resource 1.350 +// see <http://tools.ietf.org/html/rfc5988#section-5.2> 1.351 + 1.352 +bool 1.353 +nsContentSink::LinkContextIsOurDocument(const nsSubstring& aAnchor) 1.354 +{ 1.355 + if (aAnchor.IsEmpty()) { 1.356 + // anchor parameter not present or empty -> same document reference 1.357 + return true; 1.358 + } 1.359 + 1.360 + nsIURI* docUri = mDocument->GetDocumentURI(); 1.361 + 1.362 + // the document URI might contain a fragment identifier ("#...') 1.363 + // we want to ignore that because it's invisible to the server 1.364 + // and just affects the local interpretation in the recipient 1.365 + nsCOMPtr<nsIURI> contextUri; 1.366 + nsresult rv = docUri->CloneIgnoringRef(getter_AddRefs(contextUri)); 1.367 + 1.368 + if (NS_FAILED(rv)) { 1.369 + // copying failed 1.370 + return false; 1.371 + } 1.372 + 1.373 + // resolve anchor against context 1.374 + nsCOMPtr<nsIURI> resolvedUri; 1.375 + rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor, 1.376 + nullptr, contextUri); 1.377 + 1.378 + if (NS_FAILED(rv)) { 1.379 + // resolving failed 1.380 + return false; 1.381 + } 1.382 + 1.383 + bool same; 1.384 + rv = contextUri->Equals(resolvedUri, &same); 1.385 + if (NS_FAILED(rv)) { 1.386 + // comparison failed 1.387 + return false; 1.388 + } 1.389 + 1.390 + return same; 1.391 +} 1.392 + 1.393 +// Decode a parameter value using the encoding defined in RFC 5987 (in place) 1.394 +// 1.395 +// charset "'" [ language ] "'" value-chars 1.396 +// 1.397 +// returns true when decoding happened successfully (otherwise leaves 1.398 +// passed value alone) 1.399 +bool 1.400 +nsContentSink::Decode5987Format(nsAString& aEncoded) { 1.401 + 1.402 + nsresult rv; 1.403 + nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar = 1.404 + do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv); 1.405 + if (NS_FAILED(rv)) 1.406 + return false; 1.407 + 1.408 + nsAutoCString asciiValue; 1.409 + 1.410 + const char16_t* encstart = aEncoded.BeginReading(); 1.411 + const char16_t* encend = aEncoded.EndReading(); 1.412 + 1.413 + // create a plain ASCII string, aborting if we can't do that 1.414 + // converted form is always shorter than input 1.415 + while (encstart != encend) { 1.416 + if (*encstart > 0 && *encstart < 128) { 1.417 + asciiValue.Append((char)*encstart); 1.418 + } else { 1.419 + return false; 1.420 + } 1.421 + encstart++; 1.422 + } 1.423 + 1.424 + nsAutoString decoded; 1.425 + nsAutoCString language; 1.426 + 1.427 + rv = mimehdrpar->DecodeRFC5987Param(asciiValue, language, decoded); 1.428 + if (NS_FAILED(rv)) 1.429 + return false; 1.430 + 1.431 + aEncoded = decoded; 1.432 + return true; 1.433 +} 1.434 + 1.435 +nsresult 1.436 +nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) 1.437 +{ 1.438 + nsresult rv = NS_OK; 1.439 + 1.440 + // keep track where we are within the header field 1.441 + bool seenParameters = false; 1.442 + 1.443 + // parse link content and call process style link 1.444 + nsAutoString href; 1.445 + nsAutoString rel; 1.446 + nsAutoString title; 1.447 + nsAutoString titleStar; 1.448 + nsAutoString type; 1.449 + nsAutoString media; 1.450 + nsAutoString anchor; 1.451 + 1.452 + // copy to work buffer 1.453 + nsAutoString stringList(aLinkData); 1.454 + 1.455 + // put an extra null at the end 1.456 + stringList.Append(kNullCh); 1.457 + 1.458 + char16_t* start = stringList.BeginWriting(); 1.459 + char16_t* end = start; 1.460 + char16_t* last = start; 1.461 + char16_t endCh; 1.462 + 1.463 + while (*start != kNullCh) { 1.464 + // skip leading space 1.465 + while ((*start != kNullCh) && nsCRT::IsAsciiSpace(*start)) { 1.466 + ++start; 1.467 + } 1.468 + 1.469 + end = start; 1.470 + last = end - 1; 1.471 + 1.472 + bool wasQuotedString = false; 1.473 + 1.474 + // look for semicolon or comma 1.475 + while (*end != kNullCh && *end != kSemicolon && *end != kComma) { 1.476 + char16_t ch = *end; 1.477 + 1.478 + if (ch == kQuote || ch == kLessThan) { 1.479 + // quoted string 1.480 + 1.481 + char16_t quote = ch; 1.482 + if (quote == kLessThan) { 1.483 + quote = kGreaterThan; 1.484 + } 1.485 + 1.486 + wasQuotedString = (ch == kQuote); 1.487 + 1.488 + char16_t* closeQuote = (end + 1); 1.489 + 1.490 + // seek closing quote 1.491 + while (*closeQuote != kNullCh && quote != *closeQuote) { 1.492 + // in quoted-string, "\" is an escape character 1.493 + if (wasQuotedString && *closeQuote == kBackSlash && *(closeQuote + 1) != kNullCh) { 1.494 + ++closeQuote; 1.495 + } 1.496 + 1.497 + ++closeQuote; 1.498 + } 1.499 + 1.500 + if (quote == *closeQuote) { 1.501 + // found closer 1.502 + 1.503 + // skip to close quote 1.504 + end = closeQuote; 1.505 + 1.506 + last = end - 1; 1.507 + 1.508 + ch = *(end + 1); 1.509 + 1.510 + if (ch != kNullCh && ch != kSemicolon && ch != kComma) { 1.511 + // end string here 1.512 + *(++end) = kNullCh; 1.513 + 1.514 + ch = *(end + 1); 1.515 + 1.516 + // keep going until semi or comma 1.517 + while (ch != kNullCh && ch != kSemicolon && ch != kComma) { 1.518 + ++end; 1.519 + 1.520 + ch = *end; 1.521 + } 1.522 + } 1.523 + } 1.524 + } 1.525 + 1.526 + ++end; 1.527 + ++last; 1.528 + } 1.529 + 1.530 + endCh = *end; 1.531 + 1.532 + // end string here 1.533 + *end = kNullCh; 1.534 + 1.535 + if (start < end) { 1.536 + if ((*start == kLessThan) && (*last == kGreaterThan)) { 1.537 + *last = kNullCh; 1.538 + 1.539 + // first instance of <...> wins 1.540 + // also, do not allow hrefs after the first param was seen 1.541 + if (href.IsEmpty() && !seenParameters) { 1.542 + href = (start + 1); 1.543 + href.StripWhitespace(); 1.544 + } 1.545 + } else { 1.546 + char16_t* equals = start; 1.547 + seenParameters = true; 1.548 + 1.549 + while ((*equals != kNullCh) && (*equals != kEqual)) { 1.550 + equals++; 1.551 + } 1.552 + 1.553 + if (*equals != kNullCh) { 1.554 + *equals = kNullCh; 1.555 + nsAutoString attr(start); 1.556 + attr.StripWhitespace(); 1.557 + 1.558 + char16_t* value = ++equals; 1.559 + while (nsCRT::IsAsciiSpace(*value)) { 1.560 + value++; 1.561 + } 1.562 + 1.563 + if ((*value == kQuote) && (*value == *last)) { 1.564 + *last = kNullCh; 1.565 + value++; 1.566 + } 1.567 + 1.568 + if (wasQuotedString) { 1.569 + // unescape in-place 1.570 + char16_t* unescaped = value; 1.571 + char16_t *src = value; 1.572 + 1.573 + while (*src != kNullCh) { 1.574 + if (*src == kBackSlash && *(src + 1) != kNullCh) { 1.575 + src++; 1.576 + } 1.577 + *unescaped++ = *src++; 1.578 + } 1.579 + 1.580 + *unescaped = kNullCh; 1.581 + } 1.582 + 1.583 + if (attr.LowerCaseEqualsLiteral("rel")) { 1.584 + if (rel.IsEmpty()) { 1.585 + rel = value; 1.586 + rel.CompressWhitespace(); 1.587 + } 1.588 + } else if (attr.LowerCaseEqualsLiteral("title")) { 1.589 + if (title.IsEmpty()) { 1.590 + title = value; 1.591 + title.CompressWhitespace(); 1.592 + } 1.593 + } else if (attr.LowerCaseEqualsLiteral("title*")) { 1.594 + if (titleStar.IsEmpty() && !wasQuotedString) { 1.595 + // RFC 5987 encoding; uses token format only, so skip if we get 1.596 + // here with a quoted-string 1.597 + nsAutoString tmp; 1.598 + tmp = value; 1.599 + if (Decode5987Format(tmp)) { 1.600 + titleStar = tmp; 1.601 + titleStar.CompressWhitespace(); 1.602 + } else { 1.603 + // header value did not parse, throw it away 1.604 + titleStar.Truncate(); 1.605 + } 1.606 + } 1.607 + } else if (attr.LowerCaseEqualsLiteral("type")) { 1.608 + if (type.IsEmpty()) { 1.609 + type = value; 1.610 + type.StripWhitespace(); 1.611 + } 1.612 + } else if (attr.LowerCaseEqualsLiteral("media")) { 1.613 + if (media.IsEmpty()) { 1.614 + media = value; 1.615 + 1.616 + // The HTML5 spec is formulated in terms of the CSS3 spec, 1.617 + // which specifies that media queries are case insensitive. 1.618 + nsContentUtils::ASCIIToLower(media); 1.619 + } 1.620 + } else if (attr.LowerCaseEqualsLiteral("anchor")) { 1.621 + if (anchor.IsEmpty()) { 1.622 + anchor = value; 1.623 + anchor.StripWhitespace(); 1.624 + } 1.625 + } 1.626 + } 1.627 + } 1.628 + } 1.629 + 1.630 + if (endCh == kComma) { 1.631 + // hit a comma, process what we've got so far 1.632 + 1.633 + href.Trim(" \t\n\r\f"); // trim HTML5 whitespace 1.634 + if (!href.IsEmpty() && !rel.IsEmpty()) { 1.635 + rv = ProcessLink(anchor, href, rel, 1.636 + // prefer RFC 5987 variant over non-I18zed version 1.637 + titleStar.IsEmpty() ? title : titleStar, 1.638 + type, media); 1.639 + } 1.640 + 1.641 + href.Truncate(); 1.642 + rel.Truncate(); 1.643 + title.Truncate(); 1.644 + type.Truncate(); 1.645 + media.Truncate(); 1.646 + anchor.Truncate(); 1.647 + 1.648 + seenParameters = false; 1.649 + } 1.650 + 1.651 + start = ++end; 1.652 + } 1.653 + 1.654 + href.Trim(" \t\n\r\f"); // trim HTML5 whitespace 1.655 + if (!href.IsEmpty() && !rel.IsEmpty()) { 1.656 + rv = ProcessLink(anchor, href, rel, 1.657 + // prefer RFC 5987 variant over non-I18zed version 1.658 + titleStar.IsEmpty() ? title : titleStar, 1.659 + type, media); 1.660 + } 1.661 + 1.662 + return rv; 1.663 +} 1.664 + 1.665 + 1.666 +nsresult 1.667 +nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref, 1.668 + const nsSubstring& aRel, const nsSubstring& aTitle, 1.669 + const nsSubstring& aType, const nsSubstring& aMedia) 1.670 +{ 1.671 + uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(aRel); 1.672 + 1.673 + // The link relation may apply to a different resource, specified 1.674 + // in the anchor parameter. For the link relations supported so far, 1.675 + // we simply abort if the link applies to a resource different to the 1.676 + // one we've loaded 1.677 + if (!LinkContextIsOurDocument(aAnchor)) { 1.678 + return NS_OK; 1.679 + } 1.680 + 1.681 + bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH; 1.682 + // prefetch href if relation is "next" or "prefetch" 1.683 + if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) { 1.684 + PrefetchHref(aHref, mDocument, hasPrefetch); 1.685 + } 1.686 + 1.687 + if (!aHref.IsEmpty() && (linkTypes & nsStyleLinkElement::eDNS_PREFETCH)) { 1.688 + PrefetchDNS(aHref); 1.689 + } 1.690 + 1.691 + // is it a stylesheet link? 1.692 + if (!(linkTypes & nsStyleLinkElement::eSTYLESHEET)) { 1.693 + return NS_OK; 1.694 + } 1.695 + 1.696 + bool isAlternate = linkTypes & nsStyleLinkElement::eALTERNATE; 1.697 + return ProcessStyleLink(nullptr, aHref, isAlternate, aTitle, aType, 1.698 + aMedia); 1.699 +} 1.700 + 1.701 +nsresult 1.702 +nsContentSink::ProcessStyleLink(nsIContent* aElement, 1.703 + const nsSubstring& aHref, 1.704 + bool aAlternate, 1.705 + const nsSubstring& aTitle, 1.706 + const nsSubstring& aType, 1.707 + const nsSubstring& aMedia) 1.708 +{ 1.709 + if (aAlternate && aTitle.IsEmpty()) { 1.710 + // alternates must have title return without error, for now 1.711 + return NS_OK; 1.712 + } 1.713 + 1.714 + nsAutoString mimeType; 1.715 + nsAutoString params; 1.716 + nsContentUtils::SplitMimeType(aType, mimeType, params); 1.717 + 1.718 + // see bug 18817 1.719 + if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) { 1.720 + // Unknown stylesheet language 1.721 + return NS_OK; 1.722 + } 1.723 + 1.724 + nsCOMPtr<nsIURI> url; 1.725 + nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr, 1.726 + mDocument->GetDocBaseURI()); 1.727 + 1.728 + if (NS_FAILED(rv)) { 1.729 + // The URI is bad, move along, don't propagate the error (for now) 1.730 + return NS_OK; 1.731 + } 1.732 + 1.733 + NS_ASSERTION(!aElement || 1.734 + aElement->NodeType() == nsIDOMNode::PROCESSING_INSTRUCTION_NODE, 1.735 + "We only expect processing instructions here"); 1.736 + 1.737 + // If this is a fragment parser, we don't want to observe. 1.738 + // We don't support CORS for processing instructions 1.739 + bool isAlternate; 1.740 + rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia, aAlternate, 1.741 + CORS_NONE, 1.742 + mRunsToCompletion ? nullptr : this, &isAlternate); 1.743 + NS_ENSURE_SUCCESS(rv, rv); 1.744 + 1.745 + if (!isAlternate && !mRunsToCompletion) { 1.746 + ++mPendingSheetCount; 1.747 + mScriptLoader->AddExecuteBlocker(); 1.748 + } 1.749 + 1.750 + return NS_OK; 1.751 +} 1.752 + 1.753 + 1.754 +nsresult 1.755 +nsContentSink::ProcessMETATag(nsIContent* aContent) 1.756 +{ 1.757 + NS_ASSERTION(aContent, "missing meta-element"); 1.758 + 1.759 + nsresult rv = NS_OK; 1.760 + 1.761 + // set any HTTP-EQUIV data into document's header data as well as url 1.762 + nsAutoString header; 1.763 + aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header); 1.764 + if (!header.IsEmpty()) { 1.765 + nsAutoString result; 1.766 + aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result); 1.767 + if (!result.IsEmpty()) { 1.768 + nsContentUtils::ASCIIToLower(header); 1.769 + nsCOMPtr<nsIAtom> fieldAtom(do_GetAtom(header)); 1.770 + rv = ProcessHeaderData(fieldAtom, result, aContent); 1.771 + } 1.772 + } 1.773 + NS_ENSURE_SUCCESS(rv, rv); 1.774 + 1.775 + if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, 1.776 + nsGkAtoms::handheldFriendly, eIgnoreCase)) { 1.777 + nsAutoString result; 1.778 + aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result); 1.779 + if (!result.IsEmpty()) { 1.780 + nsContentUtils::ASCIIToLower(result); 1.781 + mDocument->SetHeaderData(nsGkAtoms::handheldFriendly, result); 1.782 + } 1.783 + } 1.784 + 1.785 + return rv; 1.786 +} 1.787 + 1.788 + 1.789 +void 1.790 +nsContentSink::PrefetchHref(const nsAString &aHref, 1.791 + nsINode *aSource, 1.792 + bool aExplicit) 1.793 +{ 1.794 + // 1.795 + // SECURITY CHECK: disable prefetching from mailnews! 1.796 + // 1.797 + // walk up the docshell tree to see if any containing 1.798 + // docshell are of type MAIL. 1.799 + // 1.800 + if (!mDocShell) 1.801 + return; 1.802 + 1.803 + nsCOMPtr<nsIDocShell> docshell = mDocShell; 1.804 + 1.805 + nsCOMPtr<nsIDocShellTreeItem> parentItem; 1.806 + do { 1.807 + uint32_t appType = 0; 1.808 + nsresult rv = docshell->GetAppType(&appType); 1.809 + if (NS_FAILED(rv) || appType == nsIDocShell::APP_TYPE_MAIL) 1.810 + return; // do not prefetch from mailnews 1.811 + docshell->GetParent(getter_AddRefs(parentItem)); 1.812 + if (parentItem) { 1.813 + docshell = do_QueryInterface(parentItem); 1.814 + if (!docshell) { 1.815 + NS_ERROR("cannot get a docshell from a treeItem!"); 1.816 + return; 1.817 + } 1.818 + } 1.819 + } while (parentItem); 1.820 + 1.821 + // OK, we passed the security check... 1.822 + 1.823 + nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID)); 1.824 + if (prefetchService) { 1.825 + // construct URI using document charset 1.826 + const nsACString &charset = mDocument->GetDocumentCharacterSet(); 1.827 + nsCOMPtr<nsIURI> uri; 1.828 + NS_NewURI(getter_AddRefs(uri), aHref, 1.829 + charset.IsEmpty() ? nullptr : PromiseFlatCString(charset).get(), 1.830 + mDocument->GetDocBaseURI()); 1.831 + if (uri) { 1.832 + nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aSource); 1.833 + prefetchService->PrefetchURI(uri, mDocumentURI, domNode, aExplicit); 1.834 + } 1.835 + } 1.836 +} 1.837 + 1.838 +void 1.839 +nsContentSink::PrefetchDNS(const nsAString &aHref) 1.840 +{ 1.841 + nsAutoString hostname; 1.842 + 1.843 + if (StringBeginsWith(aHref, NS_LITERAL_STRING("//"))) { 1.844 + hostname = Substring(aHref, 2); 1.845 + } 1.846 + else { 1.847 + nsCOMPtr<nsIURI> uri; 1.848 + NS_NewURI(getter_AddRefs(uri), aHref); 1.849 + if (!uri) { 1.850 + return; 1.851 + } 1.852 + nsAutoCString host; 1.853 + uri->GetHost(host); 1.854 + CopyUTF8toUTF16(host, hostname); 1.855 + } 1.856 + 1.857 + if (!hostname.IsEmpty() && nsHTMLDNSPrefetch::IsAllowed(mDocument)) { 1.858 + nsHTMLDNSPrefetch::PrefetchLow(hostname); 1.859 + } 1.860 +} 1.861 + 1.862 +nsresult 1.863 +nsContentSink::SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache, 1.864 + nsIURI *aManifestURI, 1.865 + bool aFetchedWithHTTPGetOrEquiv, 1.866 + CacheSelectionAction *aAction) 1.867 +{ 1.868 + nsresult rv; 1.869 + 1.870 + *aAction = CACHE_SELECTION_NONE; 1.871 + 1.872 + nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument = 1.873 + do_QueryInterface(mDocument); 1.874 + NS_ASSERTION(applicationCacheDocument, 1.875 + "mDocument must implement nsIApplicationCacheContainer."); 1.876 + 1.877 + if (aLoadApplicationCache) { 1.878 + nsCOMPtr<nsIURI> groupURI; 1.879 + rv = aLoadApplicationCache->GetManifestURI(getter_AddRefs(groupURI)); 1.880 + NS_ENSURE_SUCCESS(rv, rv); 1.881 + 1.882 + bool equal = false; 1.883 + rv = groupURI->Equals(aManifestURI, &equal); 1.884 + NS_ENSURE_SUCCESS(rv, rv); 1.885 + 1.886 + if (!equal) { 1.887 + // This is a foreign entry, force a reload to avoid loading the foreign 1.888 + // entry. The entry will be marked as foreign to avoid loading it again. 1.889 + 1.890 + *aAction = CACHE_SELECTION_RELOAD; 1.891 + } 1.892 + else { 1.893 + // The http manifest attribute URI is equal to the manifest URI of 1.894 + // the cache the document was loaded from - associate the document with 1.895 + // that cache and invoke the cache update process. 1.896 +#ifdef DEBUG 1.897 + nsAutoCString docURISpec, clientID; 1.898 + mDocumentURI->GetAsciiSpec(docURISpec); 1.899 + aLoadApplicationCache->GetClientID(clientID); 1.900 + SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS, 1.901 + ("Selection: assigning app cache %s to document %s", clientID.get(), docURISpec.get())); 1.902 +#endif 1.903 + 1.904 + rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache); 1.905 + NS_ENSURE_SUCCESS(rv, rv); 1.906 + 1.907 + // Document will be added as implicit entry to the cache as part of 1.908 + // the update process. 1.909 + *aAction = CACHE_SELECTION_UPDATE; 1.910 + } 1.911 + } 1.912 + else { 1.913 + // The document was not loaded from an application cache 1.914 + // Here we know the manifest has the same origin as the 1.915 + // document. There is call to CheckMayLoad() on it above. 1.916 + 1.917 + if (!aFetchedWithHTTPGetOrEquiv) { 1.918 + // The document was not loaded using HTTP GET or equivalent 1.919 + // method. The spec says to run the cache selection algorithm w/o 1.920 + // the manifest specified. 1.921 + *aAction = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST; 1.922 + } 1.923 + else { 1.924 + // Always do an update in this case 1.925 + *aAction = CACHE_SELECTION_UPDATE; 1.926 + } 1.927 + } 1.928 + 1.929 + return NS_OK; 1.930 +} 1.931 + 1.932 +nsresult 1.933 +nsContentSink::SelectDocAppCacheNoManifest(nsIApplicationCache *aLoadApplicationCache, 1.934 + nsIURI **aManifestURI, 1.935 + CacheSelectionAction *aAction) 1.936 +{ 1.937 + *aManifestURI = nullptr; 1.938 + *aAction = CACHE_SELECTION_NONE; 1.939 + 1.940 + nsresult rv; 1.941 + 1.942 + if (aLoadApplicationCache) { 1.943 + // The document was loaded from an application cache, use that 1.944 + // application cache as the document's application cache. 1.945 + nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument = 1.946 + do_QueryInterface(mDocument); 1.947 + NS_ASSERTION(applicationCacheDocument, 1.948 + "mDocument must implement nsIApplicationCacheContainer."); 1.949 + 1.950 +#ifdef DEBUG 1.951 + nsAutoCString docURISpec, clientID; 1.952 + mDocumentURI->GetAsciiSpec(docURISpec); 1.953 + aLoadApplicationCache->GetClientID(clientID); 1.954 + SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS, 1.955 + ("Selection, no manifest: assigning app cache %s to document %s", clientID.get(), docURISpec.get())); 1.956 +#endif 1.957 + 1.958 + rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache); 1.959 + NS_ENSURE_SUCCESS(rv, rv); 1.960 + 1.961 + // Return the uri and invoke the update process for the selected 1.962 + // application cache. 1.963 + rv = aLoadApplicationCache->GetManifestURI(aManifestURI); 1.964 + NS_ENSURE_SUCCESS(rv, rv); 1.965 + 1.966 + *aAction = CACHE_SELECTION_UPDATE; 1.967 + } 1.968 + 1.969 + return NS_OK; 1.970 +} 1.971 + 1.972 +void 1.973 +nsContentSink::ProcessOfflineManifest(nsIContent *aElement) 1.974 +{ 1.975 + // Only check the manifest for root document nodes. 1.976 + if (aElement != mDocument->GetRootElement()) { 1.977 + return; 1.978 + } 1.979 + 1.980 + // Don't bother processing offline manifest for documents 1.981 + // without a docshell 1.982 + if (!mDocShell) { 1.983 + return; 1.984 + } 1.985 + 1.986 + // Check for a manifest= attribute. 1.987 + nsAutoString manifestSpec; 1.988 + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec); 1.989 + ProcessOfflineManifest(manifestSpec); 1.990 +} 1.991 + 1.992 +void 1.993 +nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec) 1.994 +{ 1.995 + // Don't bother processing offline manifest for documents 1.996 + // without a docshell 1.997 + if (!mDocShell) { 1.998 + return; 1.999 + } 1.1000 + 1.1001 + // If the docshell's in private browsing mode, we don't want to do any 1.1002 + // manifest processing. 1.1003 + nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(mDocShell); 1.1004 + if (loadContext->UsePrivateBrowsing()) { 1.1005 + return; 1.1006 + } 1.1007 + 1.1008 + nsresult rv; 1.1009 + 1.1010 + // Grab the application cache the document was loaded from, if any. 1.1011 + nsCOMPtr<nsIApplicationCache> applicationCache; 1.1012 + 1.1013 + nsCOMPtr<nsIApplicationCacheChannel> applicationCacheChannel = 1.1014 + do_QueryInterface(mDocument->GetChannel()); 1.1015 + if (applicationCacheChannel) { 1.1016 + bool loadedFromApplicationCache; 1.1017 + rv = applicationCacheChannel->GetLoadedFromApplicationCache( 1.1018 + &loadedFromApplicationCache); 1.1019 + if (NS_FAILED(rv)) { 1.1020 + return; 1.1021 + } 1.1022 + 1.1023 + if (loadedFromApplicationCache) { 1.1024 + rv = applicationCacheChannel->GetApplicationCache( 1.1025 + getter_AddRefs(applicationCache)); 1.1026 + if (NS_FAILED(rv)) { 1.1027 + return; 1.1028 + } 1.1029 + } 1.1030 + } 1.1031 + 1.1032 + if (aManifestSpec.IsEmpty() && !applicationCache) { 1.1033 + // Not loaded from an application cache, and no manifest 1.1034 + // attribute. Nothing to do here. 1.1035 + return; 1.1036 + } 1.1037 + 1.1038 + CacheSelectionAction action = CACHE_SELECTION_NONE; 1.1039 + nsCOMPtr<nsIURI> manifestURI; 1.1040 + 1.1041 + if (aManifestSpec.IsEmpty()) { 1.1042 + action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST; 1.1043 + } 1.1044 + else { 1.1045 + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(manifestURI), 1.1046 + aManifestSpec, mDocument, 1.1047 + mDocumentURI); 1.1048 + if (!manifestURI) { 1.1049 + return; 1.1050 + } 1.1051 + 1.1052 + // Documents must list a manifest from the same origin 1.1053 + rv = mDocument->NodePrincipal()->CheckMayLoad(manifestURI, true, false); 1.1054 + if (NS_FAILED(rv)) { 1.1055 + action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST; 1.1056 + } 1.1057 + else { 1.1058 + // Only continue if the document has permission to use offline APIs or 1.1059 + // when preferences indicate to permit it automatically. 1.1060 + if (!nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal()) && 1.1061 + !nsContentUtils::MaybeAllowOfflineAppByDefault(mDocument->NodePrincipal(), mDocument->GetWindow()) && 1.1062 + !nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal())) { 1.1063 + return; 1.1064 + } 1.1065 + 1.1066 + bool fetchedWithHTTPGetOrEquiv = false; 1.1067 + nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mDocument->GetChannel())); 1.1068 + if (httpChannel) { 1.1069 + nsAutoCString method; 1.1070 + rv = httpChannel->GetRequestMethod(method); 1.1071 + if (NS_SUCCEEDED(rv)) 1.1072 + fetchedWithHTTPGetOrEquiv = method.Equals("GET"); 1.1073 + } 1.1074 + 1.1075 + rv = SelectDocAppCache(applicationCache, manifestURI, 1.1076 + fetchedWithHTTPGetOrEquiv, &action); 1.1077 + if (NS_FAILED(rv)) { 1.1078 + return; 1.1079 + } 1.1080 + } 1.1081 + } 1.1082 + 1.1083 + if (action == CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST) { 1.1084 + rv = SelectDocAppCacheNoManifest(applicationCache, 1.1085 + getter_AddRefs(manifestURI), 1.1086 + &action); 1.1087 + if (NS_FAILED(rv)) { 1.1088 + return; 1.1089 + } 1.1090 + } 1.1091 + 1.1092 + switch (action) 1.1093 + { 1.1094 + case CACHE_SELECTION_NONE: 1.1095 + break; 1.1096 + case CACHE_SELECTION_UPDATE: { 1.1097 + nsCOMPtr<nsIOfflineCacheUpdateService> updateService = 1.1098 + do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID); 1.1099 + 1.1100 + if (updateService) { 1.1101 + nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(mDocument); 1.1102 + updateService->ScheduleOnDocumentStop(manifestURI, mDocumentURI, domdoc); 1.1103 + } 1.1104 + break; 1.1105 + } 1.1106 + case CACHE_SELECTION_RELOAD: { 1.1107 + // This situation occurs only for toplevel documents, see bottom 1.1108 + // of SelectDocAppCache method. 1.1109 + // The document has been loaded from a different offline cache group than 1.1110 + // the manifest it refers to, i.e. this is a foreign entry, mark it as such 1.1111 + // and force a reload to avoid loading it. The next attempt will not 1.1112 + // choose it. 1.1113 + 1.1114 + applicationCacheChannel->MarkOfflineCacheEntryAsForeign(); 1.1115 + 1.1116 + nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(mDocShell); 1.1117 + 1.1118 + webNav->Stop(nsIWebNavigation::STOP_ALL); 1.1119 + webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE); 1.1120 + break; 1.1121 + } 1.1122 + default: 1.1123 + NS_ASSERTION(false, 1.1124 + "Cache selection algorithm didn't decide on proper action"); 1.1125 + break; 1.1126 + } 1.1127 +} 1.1128 + 1.1129 +void 1.1130 +nsContentSink::ScrollToRef() 1.1131 +{ 1.1132 + mDocument->ScrollToRef(); 1.1133 +} 1.1134 + 1.1135 +void 1.1136 +nsContentSink::StartLayout(bool aIgnorePendingSheets) 1.1137 +{ 1.1138 + if (mLayoutStarted) { 1.1139 + // Nothing to do here 1.1140 + return; 1.1141 + } 1.1142 + 1.1143 + mDeferredLayoutStart = true; 1.1144 + 1.1145 + if (!aIgnorePendingSheets && WaitForPendingSheets()) { 1.1146 + // Bail out; we'll start layout when the sheets load 1.1147 + return; 1.1148 + } 1.1149 + 1.1150 + mDeferredLayoutStart = false; 1.1151 + 1.1152 + // Notify on all our content. If none of our presshells have started layout 1.1153 + // yet it'll be a no-op except for updating our data structures, a la 1.1154 + // UpdateChildCounts() (because we don't want to double-notify on whatever we 1.1155 + // have right now). If some of them _have_ started layout, we want to make 1.1156 + // sure to flush tags instead of just calling UpdateChildCounts() after we 1.1157 + // loop over the shells. 1.1158 + FlushTags(); 1.1159 + 1.1160 + mLayoutStarted = true; 1.1161 + mLastNotificationTime = PR_Now(); 1.1162 + 1.1163 + mDocument->SetMayStartLayout(true); 1.1164 + nsCOMPtr<nsIPresShell> shell = mDocument->GetShell(); 1.1165 + // Make sure we don't call Initialize() for a shell that has 1.1166 + // already called it. This can happen when the layout frame for 1.1167 + // an iframe is constructed *between* the Embed() call for the 1.1168 + // docshell in the iframe, and the content sink's call to OpenBody(). 1.1169 + // (Bug 153815) 1.1170 + if (shell && !shell->DidInitialize()) { 1.1171 + nsRect r = shell->GetPresContext()->GetVisibleArea(); 1.1172 + nsCOMPtr<nsIPresShell> shellGrip = shell; 1.1173 + nsresult rv = shell->Initialize(r.width, r.height); 1.1174 + if (NS_FAILED(rv)) { 1.1175 + return; 1.1176 + } 1.1177 + } 1.1178 + 1.1179 + // If the document we are loading has a reference or it is a 1.1180 + // frameset document, disable the scroll bars on the views. 1.1181 + 1.1182 + mDocument->SetScrollToRef(mDocumentURI); 1.1183 +} 1.1184 + 1.1185 +void 1.1186 +nsContentSink::NotifyAppend(nsIContent* aContainer, uint32_t aStartIndex) 1.1187 +{ 1.1188 + if (aContainer->GetCurrentDoc() != mDocument) { 1.1189 + // aContainer is not actually in our document anymore.... Just bail out of 1.1190 + // here; notifying on our document for this append would be wrong. 1.1191 + return; 1.1192 + } 1.1193 + 1.1194 + mInNotification++; 1.1195 + 1.1196 + { 1.1197 + // Scope so we call EndUpdate before we decrease mInNotification 1.1198 + MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_CONTENT_MODEL, !mBeganUpdate); 1.1199 + nsNodeUtils::ContentAppended(aContainer, 1.1200 + aContainer->GetChildAt(aStartIndex), 1.1201 + aStartIndex); 1.1202 + mLastNotificationTime = PR_Now(); 1.1203 + } 1.1204 + 1.1205 + mInNotification--; 1.1206 +} 1.1207 + 1.1208 +NS_IMETHODIMP 1.1209 +nsContentSink::Notify(nsITimer *timer) 1.1210 +{ 1.1211 + if (mParsing) { 1.1212 + // We shouldn't interfere with our normal DidProcessAToken logic 1.1213 + mDroppedTimer = true; 1.1214 + return NS_OK; 1.1215 + } 1.1216 + 1.1217 + if (WaitForPendingSheets()) { 1.1218 + mDeferredFlushTags = true; 1.1219 + } else { 1.1220 + FlushTags(); 1.1221 + 1.1222 + // Now try and scroll to the reference 1.1223 + // XXX Should we scroll unconditionally for history loads?? 1.1224 + ScrollToRef(); 1.1225 + } 1.1226 + 1.1227 + mNotificationTimer = nullptr; 1.1228 + return NS_OK; 1.1229 +} 1.1230 + 1.1231 +bool 1.1232 +nsContentSink::IsTimeToNotify() 1.1233 +{ 1.1234 + if (!sNotifyOnTimer || !mLayoutStarted || !mBackoffCount || 1.1235 + mInMonolithicContainer) { 1.1236 + return false; 1.1237 + } 1.1238 + 1.1239 + if (WaitForPendingSheets()) { 1.1240 + mDeferredFlushTags = true; 1.1241 + return false; 1.1242 + } 1.1243 + 1.1244 + PRTime now = PR_Now(); 1.1245 + 1.1246 + int64_t interval = GetNotificationInterval(); 1.1247 + int64_t diff = now - mLastNotificationTime; 1.1248 + 1.1249 + if (diff > interval) { 1.1250 + mBackoffCount--; 1.1251 + return true; 1.1252 + } 1.1253 + 1.1254 + return false; 1.1255 +} 1.1256 + 1.1257 +nsresult 1.1258 +nsContentSink::WillInterruptImpl() 1.1259 +{ 1.1260 + nsresult result = NS_OK; 1.1261 + 1.1262 + SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS, 1.1263 + ("nsContentSink::WillInterrupt: this=%p", this)); 1.1264 +#ifndef SINK_NO_INCREMENTAL 1.1265 + if (WaitForPendingSheets()) { 1.1266 + mDeferredFlushTags = true; 1.1267 + } else if (sNotifyOnTimer && mLayoutStarted) { 1.1268 + if (mBackoffCount && !mInMonolithicContainer) { 1.1269 + int64_t now = PR_Now(); 1.1270 + int64_t interval = GetNotificationInterval(); 1.1271 + int64_t diff = now - mLastNotificationTime; 1.1272 + 1.1273 + // If it's already time for us to have a notification 1.1274 + if (diff > interval || mDroppedTimer) { 1.1275 + mBackoffCount--; 1.1276 + SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW, 1.1277 + ("nsContentSink::WillInterrupt: flushing tags since we've " 1.1278 + "run out time; backoff count: %d", mBackoffCount)); 1.1279 + result = FlushTags(); 1.1280 + if (mDroppedTimer) { 1.1281 + ScrollToRef(); 1.1282 + mDroppedTimer = false; 1.1283 + } 1.1284 + } else if (!mNotificationTimer) { 1.1285 + interval -= diff; 1.1286 + int32_t delay = interval; 1.1287 + 1.1288 + // Convert to milliseconds 1.1289 + delay /= PR_USEC_PER_MSEC; 1.1290 + 1.1291 + mNotificationTimer = do_CreateInstance("@mozilla.org/timer;1", 1.1292 + &result); 1.1293 + if (NS_SUCCEEDED(result)) { 1.1294 + SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW, 1.1295 + ("nsContentSink::WillInterrupt: setting up timer with " 1.1296 + "delay %d", delay)); 1.1297 + 1.1298 + result = 1.1299 + mNotificationTimer->InitWithCallback(this, delay, 1.1300 + nsITimer::TYPE_ONE_SHOT); 1.1301 + if (NS_FAILED(result)) { 1.1302 + mNotificationTimer = nullptr; 1.1303 + } 1.1304 + } 1.1305 + } 1.1306 + } 1.1307 + } else { 1.1308 + SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW, 1.1309 + ("nsContentSink::WillInterrupt: flushing tags " 1.1310 + "unconditionally")); 1.1311 + result = FlushTags(); 1.1312 + } 1.1313 +#endif 1.1314 + 1.1315 + mParsing = false; 1.1316 + 1.1317 + return result; 1.1318 +} 1.1319 + 1.1320 +nsresult 1.1321 +nsContentSink::WillResumeImpl() 1.1322 +{ 1.1323 + SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS, 1.1324 + ("nsContentSink::WillResume: this=%p", this)); 1.1325 + 1.1326 + mParsing = true; 1.1327 + 1.1328 + return NS_OK; 1.1329 +} 1.1330 + 1.1331 +nsresult 1.1332 +nsContentSink::DidProcessATokenImpl() 1.1333 +{ 1.1334 + if (mRunsToCompletion || !mParser) { 1.1335 + return NS_OK; 1.1336 + } 1.1337 + 1.1338 + // Get the current user event time 1.1339 + nsIPresShell *shell = mDocument->GetShell(); 1.1340 + if (!shell) { 1.1341 + // If there's no pres shell in the document, return early since 1.1342 + // we're not laying anything out here. 1.1343 + return NS_OK; 1.1344 + } 1.1345 + 1.1346 + // Increase before comparing to gEventProbeRate 1.1347 + ++mDeflectedCount; 1.1348 + 1.1349 + // Check if there's a pending event 1.1350 + if (sPendingEventMode != 0 && !mHasPendingEvent && 1.1351 + (mDeflectedCount % sEventProbeRate) == 0) { 1.1352 + nsViewManager* vm = shell->GetViewManager(); 1.1353 + NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE); 1.1354 + nsCOMPtr<nsIWidget> widget; 1.1355 + vm->GetRootWidget(getter_AddRefs(widget)); 1.1356 + mHasPendingEvent = widget && widget->HasPendingInputEvent(); 1.1357 + } 1.1358 + 1.1359 + if (mHasPendingEvent && sPendingEventMode == 2) { 1.1360 + return NS_ERROR_HTMLPARSER_INTERRUPTED; 1.1361 + } 1.1362 + 1.1363 + // Have we processed enough tokens to check time? 1.1364 + if (!mHasPendingEvent && 1.1365 + mDeflectedCount < uint32_t(mDynamicLowerValue ? sInteractiveDeflectCount : 1.1366 + sPerfDeflectCount)) { 1.1367 + return NS_OK; 1.1368 + } 1.1369 + 1.1370 + mDeflectedCount = 0; 1.1371 + 1.1372 + // Check if it's time to return to the main event loop 1.1373 + if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime) { 1.1374 + return NS_ERROR_HTMLPARSER_INTERRUPTED; 1.1375 + } 1.1376 + 1.1377 + return NS_OK; 1.1378 +} 1.1379 + 1.1380 +//---------------------------------------------------------------------- 1.1381 + 1.1382 +void 1.1383 +nsContentSink::FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay) 1.1384 +{ 1.1385 + static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); 1.1386 + nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID); 1.1387 + if (appShell) 1.1388 + appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay); 1.1389 +} 1.1390 + 1.1391 +void 1.1392 +nsContentSink::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType) 1.1393 +{ 1.1394 + // Remember nested updates from updates that we started. 1.1395 + if (mInNotification > 0 && mUpdatesInNotification < 2) { 1.1396 + ++mUpdatesInNotification; 1.1397 + } 1.1398 + 1.1399 + // If we're in a script and we didn't do the notification, 1.1400 + // something else in the script processing caused the 1.1401 + // notification to occur. Since this could result in frame 1.1402 + // creation, make sure we've flushed everything before we 1.1403 + // continue. 1.1404 + 1.1405 + if (!mInNotification++) { 1.1406 + FlushTags(); 1.1407 + } 1.1408 +} 1.1409 + 1.1410 +void 1.1411 +nsContentSink::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType) 1.1412 +{ 1.1413 + // If we're in a script and we didn't do the notification, 1.1414 + // something else in the script processing caused the 1.1415 + // notification to occur. Update our notion of how much 1.1416 + // has been flushed to include any new content if ending 1.1417 + // this update leaves us not inside a notification. 1.1418 + if (!--mInNotification) { 1.1419 + UpdateChildCounts(); 1.1420 + } 1.1421 +} 1.1422 + 1.1423 +void 1.1424 +nsContentSink::DidBuildModelImpl(bool aTerminated) 1.1425 +{ 1.1426 + if (mDocument) { 1.1427 + MOZ_ASSERT(aTerminated || 1.1428 + mDocument->GetReadyStateEnum() == 1.1429 + nsIDocument::READYSTATE_LOADING, "Bad readyState"); 1.1430 + mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE); 1.1431 + } 1.1432 + 1.1433 + if (mScriptLoader) { 1.1434 + mScriptLoader->ParsingComplete(aTerminated); 1.1435 + } 1.1436 + 1.1437 + if (!mDocument->HaveFiredDOMTitleChange()) { 1.1438 + mDocument->NotifyPossibleTitleChange(false); 1.1439 + } 1.1440 + 1.1441 + // Cancel a timer if we had one out there 1.1442 + if (mNotificationTimer) { 1.1443 + SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW, 1.1444 + ("nsContentSink::DidBuildModel: canceling notification " 1.1445 + "timeout")); 1.1446 + mNotificationTimer->Cancel(); 1.1447 + mNotificationTimer = 0; 1.1448 + } 1.1449 +} 1.1450 + 1.1451 +void 1.1452 +nsContentSink::DropParserAndPerfHint(void) 1.1453 +{ 1.1454 + if (!mParser) { 1.1455 + // Make sure we don't unblock unload too many times 1.1456 + return; 1.1457 + } 1.1458 + 1.1459 + // Ref. Bug 49115 1.1460 + // Do this hack to make sure that the parser 1.1461 + // doesn't get destroyed, accidently, before 1.1462 + // the circularity, between sink & parser, is 1.1463 + // actually broken. 1.1464 + // Drop our reference to the parser to get rid of a circular 1.1465 + // reference. 1.1466 + nsRefPtr<nsParserBase> kungFuDeathGrip(mParser.forget()); 1.1467 + 1.1468 + if (mDynamicLowerValue) { 1.1469 + // Reset the performance hint which was set to FALSE 1.1470 + // when mDynamicLowerValue was set. 1.1471 + FavorPerformanceHint(true, 0); 1.1472 + } 1.1473 + 1.1474 + if (!mRunsToCompletion) { 1.1475 + mDocument->UnblockOnload(true); 1.1476 + } 1.1477 +} 1.1478 + 1.1479 +bool 1.1480 +nsContentSink::IsScriptExecutingImpl() 1.1481 +{ 1.1482 + return !!mScriptLoader->GetCurrentScript(); 1.1483 +} 1.1484 + 1.1485 +nsresult 1.1486 +nsContentSink::WillParseImpl(void) 1.1487 +{ 1.1488 + if (mRunsToCompletion || !mDocument) { 1.1489 + return NS_OK; 1.1490 + } 1.1491 + 1.1492 + nsIPresShell *shell = mDocument->GetShell(); 1.1493 + if (!shell) { 1.1494 + return NS_OK; 1.1495 + } 1.1496 + 1.1497 + uint32_t currentTime = PR_IntervalToMicroseconds(PR_IntervalNow()); 1.1498 + 1.1499 + if (sEnablePerfMode == 0) { 1.1500 + nsViewManager* vm = shell->GetViewManager(); 1.1501 + NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE); 1.1502 + uint32_t lastEventTime; 1.1503 + vm->GetLastUserEventTime(lastEventTime); 1.1504 + 1.1505 + bool newDynLower = 1.1506 + mDocument->IsInBackgroundWindow() || 1.1507 + ((currentTime - mBeginLoadTime) > uint32_t(sInitialPerfTime) && 1.1508 + (currentTime - lastEventTime) < uint32_t(sInteractiveTime)); 1.1509 + 1.1510 + if (mDynamicLowerValue != newDynLower) { 1.1511 + FavorPerformanceHint(!newDynLower, 0); 1.1512 + mDynamicLowerValue = newDynLower; 1.1513 + } 1.1514 + } 1.1515 + 1.1516 + mDeflectedCount = 0; 1.1517 + mHasPendingEvent = false; 1.1518 + 1.1519 + mCurrentParseEndTime = currentTime + 1.1520 + (mDynamicLowerValue ? sInteractiveParseTime : sPerfParseTime); 1.1521 + 1.1522 + return NS_OK; 1.1523 +} 1.1524 + 1.1525 +void 1.1526 +nsContentSink::WillBuildModelImpl() 1.1527 +{ 1.1528 + if (!mRunsToCompletion) { 1.1529 + mDocument->BlockOnload(); 1.1530 + 1.1531 + mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow()); 1.1532 + } 1.1533 + 1.1534 + mDocument->ResetScrolledToRefAlready(); 1.1535 + 1.1536 + if (mProcessLinkHeaderEvent.get()) { 1.1537 + mProcessLinkHeaderEvent.Revoke(); 1.1538 + 1.1539 + DoProcessLinkHeader(); 1.1540 + } 1.1541 +} 1.1542 + 1.1543 +/* static */ 1.1544 +void 1.1545 +nsContentSink::NotifyDocElementCreated(nsIDocument* aDoc) 1.1546 +{ 1.1547 + nsCOMPtr<nsIObserverService> observerService = 1.1548 + mozilla::services::GetObserverService(); 1.1549 + if (observerService) { 1.1550 + nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc); 1.1551 + observerService-> 1.1552 + NotifyObservers(domDoc, "document-element-inserted", 1.1553 + EmptyString().get()); 1.1554 + } 1.1555 +}