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