/src/mozilla-central/dom/base/nsDocument.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 all our document implementations. |
9 | | */ |
10 | | |
11 | | #include "AudioChannelService.h" |
12 | | #include "nsDocument.h" |
13 | | #include "nsIDocumentInlines.h" |
14 | | #include "mozilla/AnimationComparator.h" |
15 | | #include "mozilla/AntiTrackingCommon.h" |
16 | | #include "mozilla/ArrayUtils.h" |
17 | | #include "mozilla/AutoRestore.h" |
18 | | #include "mozilla/BinarySearch.h" |
19 | | #include "mozilla/CSSEnabledState.h" |
20 | | #include "mozilla/DebugOnly.h" |
21 | | #include "mozilla/EffectSet.h" |
22 | | #include "mozilla/EnumSet.h" |
23 | | #include "mozilla/IntegerRange.h" |
24 | | #include "mozilla/MemoryReporting.h" |
25 | | #include "mozilla/Likely.h" |
26 | | #include "mozilla/PresShell.h" |
27 | | #include "mozilla/StaticPrefs.h" |
28 | | #include "mozilla/URLExtraData.h" |
29 | | #include <algorithm> |
30 | | |
31 | | #include "mozilla/Logging.h" |
32 | | #include "plstr.h" |
33 | | #include "mozilla/Sprintf.h" |
34 | | |
35 | | #include "mozilla/Telemetry.h" |
36 | | #include "nsIInterfaceRequestor.h" |
37 | | #include "nsIInterfaceRequestorUtils.h" |
38 | | #include "nsILoadContext.h" |
39 | | #include "nsITextControlFrame.h" |
40 | | #include "nsNumberControlFrame.h" |
41 | | #include "nsUnicharUtils.h" |
42 | | #include "nsContentList.h" |
43 | | #include "nsCSSPseudoElements.h" |
44 | | #include "nsIObserver.h" |
45 | | #include "nsIBaseWindow.h" |
46 | | #include "mozilla/css/Loader.h" |
47 | | #include "mozilla/css/ImageLoader.h" |
48 | | #include "nsDocShell.h" |
49 | | #include "nsDocShellLoadTypes.h" |
50 | | #include "nsIDocShellTreeItem.h" |
51 | | #include "nsCOMArray.h" |
52 | | #include "nsQueryObject.h" |
53 | | #include "mozilla/Services.h" |
54 | | #include "nsScreen.h" |
55 | | #include "ChildIterator.h" |
56 | | |
57 | | #include "mozilla/AsyncEventDispatcher.h" |
58 | | #include "mozilla/BasicEvents.h" |
59 | | #include "mozilla/EventListenerManager.h" |
60 | | #include "mozilla/EventStateManager.h" |
61 | | #include "mozilla/FullscreenChange.h" |
62 | | |
63 | | #include "mozilla/dom/Attr.h" |
64 | | #include "mozilla/dom/BindingDeclarations.h" |
65 | | #include "mozilla/dom/Element.h" |
66 | | #include "mozilla/dom/Event.h" |
67 | | #include "mozilla/dom/FramingChecker.h" |
68 | | #include "mozilla/dom/HTMLSharedElement.h" |
69 | | #include "mozilla/dom/SVGUseElement.h" |
70 | | #include "nsGenericHTMLElement.h" |
71 | | #include "mozilla/dom/CDATASection.h" |
72 | | #include "mozilla/dom/ProcessingInstruction.h" |
73 | | #include "nsDOMString.h" |
74 | | #include "nsNodeUtils.h" |
75 | | #include "nsLayoutUtils.h" // for GetFrameForPoint |
76 | | #include "nsIFrame.h" |
77 | | #include "nsITabChild.h" |
78 | | |
79 | | #include "nsRange.h" |
80 | | #include "mozilla/dom/DocumentType.h" |
81 | | #include "mozilla/dom/NodeIterator.h" |
82 | | #include "mozilla/dom/Promise.h" |
83 | | #include "mozilla/dom/PromiseNativeHandler.h" |
84 | | #include "mozilla/dom/TreeWalker.h" |
85 | | |
86 | | #include "nsIServiceManager.h" |
87 | | #include "mozilla/dom/ServiceWorkerManager.h" |
88 | | #include "imgLoader.h" |
89 | | |
90 | | #include "nsAboutProtocolUtils.h" |
91 | | #include "nsCanvasFrame.h" |
92 | | #include "nsContentCID.h" |
93 | | #include "nsError.h" |
94 | | #include "nsPresContext.h" |
95 | | #include "nsThreadUtils.h" |
96 | | #include "nsNodeInfoManager.h" |
97 | | #include "nsIFileChannel.h" |
98 | | #include "nsIMultiPartChannel.h" |
99 | | #include "nsIRefreshURI.h" |
100 | | #include "nsIWebNavigation.h" |
101 | | #include "nsIScriptError.h" |
102 | | #include "nsISimpleEnumerator.h" |
103 | | #include "nsIRequestContext.h" |
104 | | #include "nsStyleSheetService.h" |
105 | | |
106 | | #include "nsNetUtil.h" // for NS_NewURI |
107 | | #include "nsIInputStreamChannel.h" |
108 | | #include "nsIAuthPrompt.h" |
109 | | #include "nsIAuthPrompt2.h" |
110 | | |
111 | | #include "nsIScriptSecurityManager.h" |
112 | | #include "nsIPermissionManager.h" |
113 | | #include "nsIPrincipal.h" |
114 | | #include "ExpandedPrincipal.h" |
115 | | #include "mozilla/NullPrincipal.h" |
116 | | |
117 | | #include "nsIDOMWindow.h" |
118 | | #include "nsPIDOMWindow.h" |
119 | | #include "nsFocusManager.h" |
120 | | #include "nsICookieService.h" |
121 | | |
122 | | #include "nsBidiUtils.h" |
123 | | |
124 | | #include "nsContentCreatorFunctions.h" |
125 | | |
126 | | #include "nsIScriptContext.h" |
127 | | #include "nsBindingManager.h" |
128 | | #include "nsHTMLDocument.h" |
129 | | #include "nsIRequest.h" |
130 | | #include "mozilla/dom/BlobURLProtocolHandler.h" |
131 | | |
132 | | #include "nsCharsetSource.h" |
133 | | #include "nsIParser.h" |
134 | | #include "nsIContentSink.h" |
135 | | |
136 | | #include "mozilla/EventDispatcher.h" |
137 | | #include "mozilla/EventStates.h" |
138 | | #include "mozilla/InternalMutationEvent.h" |
139 | | #include "nsDOMCID.h" |
140 | | |
141 | | #include "jsapi.h" |
142 | | #include "nsIXPConnect.h" |
143 | | #include "xpcpublic.h" |
144 | | #include "nsCCUncollectableMarker.h" |
145 | | #include "nsIContentPolicy.h" |
146 | | #include "nsContentPolicyUtils.h" |
147 | | #include "nsICategoryManager.h" |
148 | | #include "nsIDocumentLoaderFactory.h" |
149 | | #include "nsIDocumentLoader.h" |
150 | | #include "nsIContentViewer.h" |
151 | | #include "nsIXMLContentSink.h" |
152 | | #include "nsIPrompt.h" |
153 | | #include "nsIPropertyBag2.h" |
154 | | #include "mozilla/dom/PageTransitionEvent.h" |
155 | | #include "mozilla/dom/StyleRuleChangeEvent.h" |
156 | | #include "mozilla/dom/StyleSheetChangeEvent.h" |
157 | | #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h" |
158 | | #include "nsJSUtils.h" |
159 | | #include "nsFrameLoader.h" |
160 | | #include "nsEscape.h" |
161 | | #include "nsObjectLoadingContent.h" |
162 | | #include "nsHtml5TreeOpExecutor.h" |
163 | | #include "mozilla/dom/HTMLFormElement.h" |
164 | | #include "mozilla/dom/HTMLLinkElement.h" |
165 | | #include "mozilla/dom/HTMLMediaElement.h" |
166 | | #include "mozilla/dom/HTMLIFrameElement.h" |
167 | | #include "mozilla/dom/HTMLImageElement.h" |
168 | | #include "mozilla/dom/HTMLTextAreaElement.h" |
169 | | #include "mozilla/dom/MediaSource.h" |
170 | | |
171 | | #include "mozAutoDocUpdate.h" |
172 | | #include "nsGlobalWindow.h" |
173 | | #include "mozilla/Encoding.h" |
174 | | #include "nsDOMNavigationTiming.h" |
175 | | |
176 | | #include "nsSMILAnimationController.h" |
177 | | #include "imgIContainer.h" |
178 | | #include "nsSVGUtils.h" |
179 | | |
180 | | #include "nsRefreshDriver.h" |
181 | | |
182 | | // FOR CSP (autogenerated by xpidl) |
183 | | #include "nsIContentSecurityPolicy.h" |
184 | | #include "mozilla/dom/nsCSPContext.h" |
185 | | #include "mozilla/dom/nsCSPService.h" |
186 | | #include "mozilla/dom/nsCSPUtils.h" |
187 | | #include "nsHTMLStyleSheet.h" |
188 | | #include "nsHTMLCSSStyleSheet.h" |
189 | | #include "mozilla/dom/DOMImplementation.h" |
190 | | #include "mozilla/dom/ShadowRoot.h" |
191 | | #include "mozilla/dom/Comment.h" |
192 | | #include "nsTextNode.h" |
193 | | #include "mozilla/dom/Link.h" |
194 | | #include "mozilla/dom/HTMLCollectionBinding.h" |
195 | | #include "mozilla/dom/HTMLElementBinding.h" |
196 | | #include "nsXULAppAPI.h" |
197 | | #include "mozilla/dom/Touch.h" |
198 | | #include "mozilla/dom/TouchEvent.h" |
199 | | |
200 | | #include "mozilla/Preferences.h" |
201 | | |
202 | | #include "imgILoader.h" |
203 | | #include "imgRequestProxy.h" |
204 | | #include "nsWrapperCacheInlines.h" |
205 | | #include "nsSandboxFlags.h" |
206 | | #include "mozilla/dom/AnimatableBinding.h" |
207 | | #include "mozilla/dom/AnonymousContent.h" |
208 | | #include "mozilla/dom/BindingUtils.h" |
209 | | #include "mozilla/dom/ClientInfo.h" |
210 | | #include "mozilla/dom/ClientState.h" |
211 | | #include "mozilla/dom/DocumentFragment.h" |
212 | | #include "mozilla/dom/DocumentL10n.h" |
213 | | #include "mozilla/dom/DocumentTimeline.h" |
214 | | #include "mozilla/dom/Event.h" |
215 | | #include "mozilla/dom/HTMLBodyElement.h" |
216 | | #include "mozilla/dom/HTMLInputElement.h" |
217 | | #include "mozilla/dom/ImageTracker.h" |
218 | | #include "mozilla/dom/MediaQueryList.h" |
219 | | #include "mozilla/dom/NodeFilterBinding.h" |
220 | | #include "mozilla/OwningNonNull.h" |
221 | | #include "mozilla/dom/TabChild.h" |
222 | | #include "mozilla/dom/WebComponentsBinding.h" |
223 | | #include "mozilla/dom/CustomElementRegistryBinding.h" |
224 | | #include "mozilla/dom/CustomElementRegistry.h" |
225 | | #include "mozilla/dom/ServiceWorkerDescriptor.h" |
226 | | #include "mozilla/dom/TimeoutManager.h" |
227 | | #include "mozilla/ExtensionPolicyService.h" |
228 | | #include "nsFrame.h" |
229 | | #include "nsDOMCaretPosition.h" |
230 | | #include "nsViewportInfo.h" |
231 | | #include "mozilla/StaticPtr.h" |
232 | | #include "nsITextControlElement.h" |
233 | | #include "nsIEditor.h" |
234 | | #include "nsIHttpChannelInternal.h" |
235 | | #include "nsISecurityConsoleMessage.h" |
236 | | #include "nsCharSeparatedTokenizer.h" |
237 | | #include "mozilla/dom/XPathEvaluator.h" |
238 | | #include "mozilla/dom/XPathNSResolverBinding.h" |
239 | | #include "mozilla/dom/XPathResult.h" |
240 | | #include "nsIDocumentEncoder.h" |
241 | | #include "nsIDocumentActivity.h" |
242 | | #include "nsIStructuredCloneContainer.h" |
243 | | #include "nsIMutableArray.h" |
244 | | #include "mozilla/dom/DOMStringList.h" |
245 | | #include "nsWindowSizes.h" |
246 | | #include "mozilla/dom/Location.h" |
247 | | #include "mozilla/dom/FontFaceSet.h" |
248 | | #include "gfxPrefs.h" |
249 | | #include "nsISupportsPrimitives.h" |
250 | | #include "mozilla/ServoStyleSet.h" |
251 | | #include "mozilla/StyleSheet.h" |
252 | | #include "mozilla/StyleSheetInlines.h" |
253 | | #include "mozilla/dom/SVGDocument.h" |
254 | | #include "mozilla/dom/SVGSVGElement.h" |
255 | | #include "mozilla/dom/DocGroup.h" |
256 | | #include "mozilla/dom/TabGroup.h" |
257 | | #ifdef MOZ_XUL |
258 | | #include "mozilla/dom/TreeBoxObject.h" |
259 | | #include "nsIXULWindow.h" |
260 | | #include "nsXULCommandDispatcher.h" |
261 | | #include "nsXULPopupManager.h" |
262 | | #include "nsIDocShellTreeOwner.h" |
263 | | #endif |
264 | | #include "nsIPresShellInlines.h" |
265 | | |
266 | | #include "mozilla/DocLoadingTimelineMarker.h" |
267 | | |
268 | | #include "nsISpeculativeConnect.h" |
269 | | |
270 | | #include "mozilla/MediaManager.h" |
271 | | |
272 | | #include "nsIURIClassifier.h" |
273 | | #include "nsIURIMutator.h" |
274 | | #include "mozilla/DocumentStyleRootIterator.h" |
275 | | #include "mozilla/PendingFullscreenEvent.h" |
276 | | #include "mozilla/RestyleManager.h" |
277 | | #include "mozilla/ClearOnShutdown.h" |
278 | | #include "nsHTMLTags.h" |
279 | | #include "NodeUbiReporting.h" |
280 | | #include "nsICookieService.h" |
281 | | |
282 | | using namespace mozilla; |
283 | | using namespace mozilla::dom; |
284 | | |
285 | | typedef nsTArray<Link*> LinkArray; |
286 | | |
287 | | static LazyLogModule gDocumentLeakPRLog("DocumentLeak"); |
288 | | static LazyLogModule gCspPRLog("CSP"); |
289 | | static LazyLogModule gUserInteractionPRLog("UserInteraction"); |
290 | | |
291 | | static nsresult |
292 | | GetHttpChannelHelper(nsIChannel* aChannel, nsIHttpChannel** aHttpChannel) |
293 | 0 | { |
294 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); |
295 | 0 | if (httpChannel) { |
296 | 0 | httpChannel.forget(aHttpChannel); |
297 | 0 | return NS_OK; |
298 | 0 | } |
299 | 0 | |
300 | 0 | nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel); |
301 | 0 | if (!multipart) { |
302 | 0 | *aHttpChannel = nullptr; |
303 | 0 | return NS_OK; |
304 | 0 | } |
305 | 0 | |
306 | 0 | nsCOMPtr<nsIChannel> baseChannel; |
307 | 0 | nsresult rv = multipart->GetBaseChannel(getter_AddRefs(baseChannel)); |
308 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
309 | 0 | return rv; |
310 | 0 | } |
311 | 0 | |
312 | 0 | httpChannel = do_QueryInterface(baseChannel); |
313 | 0 | httpChannel.forget(aHttpChannel); |
314 | 0 |
|
315 | 0 | return NS_OK; |
316 | 0 | } |
317 | | |
318 | | //////////////////////////////////////////////////////////////////// |
319 | | // PrincipalFlashClassifier |
320 | | |
321 | | // Classify the flash based on the document principal. |
322 | | // The usage of this class is as follows: |
323 | | // |
324 | | // 1) Call AsyncClassify() as early as possible to asynchronously do |
325 | | // classification against all the flash blocking related tables |
326 | | // via nsIURIClassifier.asyncClassifyLocalWithTables. |
327 | | // |
328 | | // 2) At any time you need the classification result, call Result() |
329 | | // and it is guaranteed to give you the result. Note that you have |
330 | | // to specify "aIsThirdParty" to the function so please make sure |
331 | | // you can already correctly decide if the document is third-party. |
332 | | // |
333 | | // Behind the scenes, the sync classification API |
334 | | // (nsIURIClassifier.classifyLocalWithTable) may be called as a fallback to |
335 | | // synchronously get the result if the asyncClassifyLocalWithTables hasn't |
336 | | // been done yet. |
337 | | // |
338 | | // 3) You can call Result() as many times as you want and only the first time |
339 | | // it may unfortunately call the blocking sync API. The subsequent call |
340 | | // will just return the result that came out at the first time. |
341 | | // |
342 | | class PrincipalFlashClassifier final : public nsIURIClassifierCallback |
343 | | { |
344 | | public: |
345 | | NS_DECL_THREADSAFE_ISUPPORTS |
346 | | NS_DECL_NSIURICLASSIFIERCALLBACK |
347 | | |
348 | | PrincipalFlashClassifier(); |
349 | | |
350 | | // Fire async classification based on the given principal. |
351 | | void AsyncClassify(nsIPrincipal* aPrincipal); |
352 | | |
353 | | // Would block if the result hasn't come out. |
354 | | mozilla::dom::FlashClassification ClassifyMaybeSync(nsIPrincipal* aPrincipal, |
355 | | bool aIsThirdParty); |
356 | | |
357 | | private: |
358 | 0 | ~PrincipalFlashClassifier() = default; |
359 | | |
360 | | void Reset(); |
361 | | bool EnsureUriClassifier(); |
362 | | mozilla::dom::FlashClassification CheckIfClassifyNeeded(nsIPrincipal* aPrincipal); |
363 | | mozilla::dom::FlashClassification Resolve(bool aIsThirdParty); |
364 | | mozilla::dom::FlashClassification AsyncClassifyInternal(nsIPrincipal* aPrincipal); |
365 | | void GetClassificationTables(bool aIsThirdParty, nsACString& aTables); |
366 | | |
367 | | // For the fallback sync classification. |
368 | | nsCOMPtr<nsIURI> mClassificationURI; |
369 | | |
370 | | nsCOMPtr<nsIURIClassifier> mUriClassifier; |
371 | | bool mAsyncClassified; |
372 | | nsTArray<nsCString> mMatchedTables; |
373 | | mozilla::dom::FlashClassification mResult; |
374 | | }; |
375 | | |
376 | | |
377 | | #define NAME_NOT_VALID ((nsSimpleContentList*)1) |
378 | | |
379 | | nsIdentifierMapEntry::nsIdentifierMapEntry(const nsIdentifierMapEntry::AtomOrString& aKey) |
380 | | : mKey(aKey) |
381 | 0 | {} |
382 | | |
383 | | nsIdentifierMapEntry::nsIdentifierMapEntry(const nsIdentifierMapEntry::AtomOrString* aKey) |
384 | | : mKey(aKey ? *aKey : nullptr) |
385 | 0 | {} |
386 | | |
387 | | nsIdentifierMapEntry::~nsIdentifierMapEntry() |
388 | 0 | {} |
389 | | |
390 | | nsIdentifierMapEntry::nsIdentifierMapEntry(nsIdentifierMapEntry&& aOther) |
391 | | : PLDHashEntryHdr(std::move(aOther)) |
392 | | , mKey(std::move(aOther.mKey)) |
393 | | , mIdContentList(std::move(aOther.mIdContentList)) |
394 | | , mNameContentList(std::move(aOther.mNameContentList)) |
395 | | , mChangeCallbacks(std::move(aOther.mChangeCallbacks)) |
396 | | , mImageElement(std::move(aOther.mImageElement)) |
397 | 0 | {} |
398 | | |
399 | | void |
400 | | nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback) |
401 | 0 | { |
402 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, |
403 | 0 | "mIdentifierMap mNameContentList"); |
404 | 0 | aCallback->NoteXPCOMChild(static_cast<nsINodeList*>(mNameContentList)); |
405 | 0 |
|
406 | 0 | if (mImageElement) { |
407 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, |
408 | 0 | "mIdentifierMap mImageElement element"); |
409 | 0 | nsIContent* imageElement = mImageElement; |
410 | 0 | aCallback->NoteXPCOMChild(imageElement); |
411 | 0 | } |
412 | 0 | } |
413 | | |
414 | | bool |
415 | | nsIdentifierMapEntry::IsEmpty() |
416 | 0 | { |
417 | 0 | return mIdContentList.IsEmpty() && !mNameContentList && |
418 | 0 | !mChangeCallbacks && !mImageElement; |
419 | 0 | } |
420 | | |
421 | | bool |
422 | | nsIdentifierMapEntry::HasNameElement() const |
423 | 0 | { |
424 | 0 | return mNameContentList && mNameContentList->Length() != 0; |
425 | 0 | } |
426 | | |
427 | | Element* |
428 | | nsIdentifierMapEntry::GetIdElement() |
429 | 0 | { |
430 | 0 | return mIdContentList.SafeElementAt(0); |
431 | 0 | } |
432 | | |
433 | | Element* |
434 | | nsIdentifierMapEntry::GetImageIdElement() |
435 | 0 | { |
436 | 0 | return mImageElement ? mImageElement.get() : GetIdElement(); |
437 | 0 | } |
438 | | |
439 | | void |
440 | | nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback, |
441 | | void* aData, bool aForImage) |
442 | 0 | { |
443 | 0 | if (!mChangeCallbacks) { |
444 | 0 | mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>; |
445 | 0 | } |
446 | 0 |
|
447 | 0 | ChangeCallback cc = { aCallback, aData, aForImage }; |
448 | 0 | mChangeCallbacks->PutEntry(cc); |
449 | 0 | } |
450 | | |
451 | | void |
452 | | nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback, |
453 | | void* aData, bool aForImage) |
454 | 0 | { |
455 | 0 | if (!mChangeCallbacks) |
456 | 0 | return; |
457 | 0 | ChangeCallback cc = { aCallback, aData, aForImage }; |
458 | 0 | mChangeCallbacks->RemoveEntry(cc); |
459 | 0 | if (mChangeCallbacks->Count() == 0) { |
460 | 0 | mChangeCallbacks = nullptr; |
461 | 0 | } |
462 | 0 | } |
463 | | |
464 | | void |
465 | | nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement, |
466 | | Element* aNewElement, |
467 | | bool aImageOnly) |
468 | 0 | { |
469 | 0 | if (!mChangeCallbacks) |
470 | 0 | return; |
471 | 0 | |
472 | 0 | for (auto iter = mChangeCallbacks->ConstIter(); !iter.Done(); iter.Next()) { |
473 | 0 | nsIdentifierMapEntry::ChangeCallbackEntry* entry = iter.Get(); |
474 | 0 | // Don't fire image changes for non-image observers, and don't fire element |
475 | 0 | // changes for image observers when an image override is active. |
476 | 0 | if (entry->mKey.mForImage ? (mImageElement && !aImageOnly) : aImageOnly) { |
477 | 0 | continue; |
478 | 0 | } |
479 | 0 | |
480 | 0 | if (!entry->mKey.mCallback(aOldElement, aNewElement, entry->mKey.mData)) { |
481 | 0 | iter.Remove(); |
482 | 0 | } |
483 | 0 | } |
484 | 0 | } |
485 | | |
486 | | namespace { |
487 | | |
488 | | struct PositionComparator |
489 | | { |
490 | | Element* const mElement; |
491 | 0 | explicit PositionComparator(Element* const aElement) : mElement(aElement) {} |
492 | | |
493 | 0 | int operator()(void* aElement) const { |
494 | 0 | Element* curElement = static_cast<Element*>(aElement); |
495 | 0 | if (mElement == curElement) { |
496 | 0 | return 0; |
497 | 0 | } |
498 | 0 | if (nsContentUtils::PositionIsBefore(mElement, curElement)) { |
499 | 0 | return -1; |
500 | 0 | } |
501 | 0 | return 1; |
502 | 0 | } |
503 | | }; |
504 | | |
505 | | } // namespace |
506 | | |
507 | | bool |
508 | | nsIdentifierMapEntry::AddIdElement(Element* aElement) |
509 | 0 | { |
510 | 0 | MOZ_ASSERT(aElement, "Must have element"); |
511 | 0 | MOZ_ASSERT(!mIdContentList.Contains(nullptr), |
512 | 0 | "Why is null in our list?"); |
513 | 0 |
|
514 | | #ifdef DEBUG |
515 | | Element* currentElement = mIdContentList.SafeElementAt(0); |
516 | | #endif |
517 | |
|
518 | 0 | // Common case |
519 | 0 | if (mIdContentList.IsEmpty()) { |
520 | 0 | if (!mIdContentList.AppendElement(aElement)) |
521 | 0 | return false; |
522 | 0 | NS_ASSERTION(currentElement == nullptr, "How did that happen?"); |
523 | 0 | FireChangeCallbacks(nullptr, aElement); |
524 | 0 | return true; |
525 | 0 | } |
526 | 0 | |
527 | 0 | // We seem to have multiple content nodes for the same id, or XUL is messing |
528 | 0 | // with us. Search for the right place to insert the content. |
529 | 0 | |
530 | 0 | size_t idx; |
531 | 0 | if (BinarySearchIf(mIdContentList, 0, mIdContentList.Length(), |
532 | 0 | PositionComparator(aElement), &idx)) { |
533 | 0 | // Already in the list, so already in the right spot. Get out of here. |
534 | 0 | // XXXbz this only happens because XUL does all sorts of random |
535 | 0 | // UpdateIdTableEntry calls. Hate, hate, hate! |
536 | 0 | return true; |
537 | 0 | } |
538 | 0 | |
539 | 0 | if (!mIdContentList.InsertElementAt(idx, aElement)) { |
540 | 0 | return false; |
541 | 0 | } |
542 | 0 | |
543 | 0 | if (idx == 0) { |
544 | 0 | Element* oldElement = mIdContentList.SafeElementAt(1); |
545 | 0 | NS_ASSERTION(currentElement == oldElement, "How did that happen?"); |
546 | 0 | FireChangeCallbacks(oldElement, aElement); |
547 | 0 | } |
548 | 0 | return true; |
549 | 0 | } |
550 | | |
551 | | void |
552 | | nsIdentifierMapEntry::RemoveIdElement(Element* aElement) |
553 | 0 | { |
554 | 0 | MOZ_ASSERT(aElement, "Missing element"); |
555 | 0 |
|
556 | 0 | // This should only be called while the document is in an update. |
557 | 0 | // Assertions near the call to this method guarantee this. |
558 | 0 |
|
559 | 0 | // This could fire in OOM situations |
560 | 0 | // Only assert this in HTML documents for now as XUL does all sorts of weird |
561 | 0 | // crap. |
562 | 0 | NS_ASSERTION(!aElement->OwnerDoc()->IsHTMLDocument() || |
563 | 0 | mIdContentList.Contains(aElement), |
564 | 0 | "Removing id entry that doesn't exist"); |
565 | 0 |
|
566 | 0 | // XXXbz should this ever Compact() I guess when all the content is gone |
567 | 0 | // we'll just get cleaned up in the natural order of things... |
568 | 0 | Element* currentElement = mIdContentList.SafeElementAt(0); |
569 | 0 | mIdContentList.RemoveElement(aElement); |
570 | 0 | if (currentElement == aElement) { |
571 | 0 | FireChangeCallbacks(currentElement, mIdContentList.SafeElementAt(0)); |
572 | 0 | } |
573 | 0 | } |
574 | | |
575 | | void |
576 | | nsIdentifierMapEntry::SetImageElement(Element* aElement) |
577 | 0 | { |
578 | 0 | Element* oldElement = GetImageIdElement(); |
579 | 0 | mImageElement = aElement; |
580 | 0 | Element* newElement = GetImageIdElement(); |
581 | 0 | if (oldElement != newElement) { |
582 | 0 | FireChangeCallbacks(oldElement, newElement, true); |
583 | 0 | } |
584 | 0 | } |
585 | | |
586 | | namespace mozilla { |
587 | | namespace dom { |
588 | | class SimpleHTMLCollection final : public nsSimpleContentList |
589 | | , public nsIHTMLCollection |
590 | | { |
591 | | public: |
592 | 0 | explicit SimpleHTMLCollection(nsINode* aRoot) : nsSimpleContentList(aRoot) {} |
593 | | |
594 | | NS_DECL_ISUPPORTS_INHERITED |
595 | | |
596 | | virtual nsINode* GetParentObject() override |
597 | 0 | { |
598 | 0 | return nsSimpleContentList::GetParentObject(); |
599 | 0 | } |
600 | | virtual uint32_t Length() override |
601 | 0 | { |
602 | 0 | return nsSimpleContentList::Length(); |
603 | 0 | } |
604 | | virtual Element* GetElementAt(uint32_t aIndex) override |
605 | 0 | { |
606 | 0 | return mElements.SafeElementAt(aIndex)->AsElement(); |
607 | 0 | } |
608 | | |
609 | | virtual Element* GetFirstNamedElement(const nsAString& aName, |
610 | | bool& aFound) override |
611 | 0 | { |
612 | 0 | aFound = false; |
613 | 0 | RefPtr<nsAtom> name = NS_Atomize(aName); |
614 | 0 | for (uint32_t i = 0; i < mElements.Length(); i++) { |
615 | 0 | MOZ_DIAGNOSTIC_ASSERT(mElements[i]); |
616 | 0 | Element* element = mElements[i]->AsElement(); |
617 | 0 | if (element->GetID() == name || |
618 | 0 | (element->HasName() && |
619 | 0 | element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == name)) { |
620 | 0 | aFound = true; |
621 | 0 | return element; |
622 | 0 | } |
623 | 0 | } |
624 | 0 | return nullptr; |
625 | 0 | } |
626 | | |
627 | | virtual void GetSupportedNames(nsTArray<nsString>& aNames) override |
628 | 0 | { |
629 | 0 | AutoTArray<nsAtom*, 8> atoms; |
630 | 0 | for (uint32_t i = 0; i < mElements.Length(); i++) { |
631 | 0 | MOZ_DIAGNOSTIC_ASSERT(mElements[i]); |
632 | 0 | Element* element = mElements[i]->AsElement(); |
633 | 0 |
|
634 | 0 | nsAtom* id = element->GetID(); |
635 | 0 | MOZ_ASSERT(id != nsGkAtoms::_empty); |
636 | 0 | if (id && !atoms.Contains(id)) { |
637 | 0 | atoms.AppendElement(id); |
638 | 0 | } |
639 | 0 |
|
640 | 0 | if (element->HasName()) { |
641 | 0 | nsAtom* name = element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue(); |
642 | 0 | MOZ_ASSERT(name && name != nsGkAtoms::_empty); |
643 | 0 | if (name && !atoms.Contains(name)) { |
644 | 0 | atoms.AppendElement(name); |
645 | 0 | } |
646 | 0 | } |
647 | 0 | } |
648 | 0 |
|
649 | 0 | nsString* names = aNames.AppendElements(atoms.Length()); |
650 | 0 | for (uint32_t i = 0; i < atoms.Length(); i++) { |
651 | 0 | atoms[i]->ToString(names[i]); |
652 | 0 | } |
653 | 0 | } |
654 | | |
655 | | virtual JSObject* GetWrapperPreserveColorInternal() override |
656 | 0 | { |
657 | 0 | return nsWrapperCache::GetWrapperPreserveColor(); |
658 | 0 | } |
659 | | virtual void PreserveWrapperInternal(nsISupports* aScriptObjectHolder) override |
660 | 0 | { |
661 | 0 | nsWrapperCache::PreserveWrapper(aScriptObjectHolder); |
662 | 0 | } |
663 | | virtual JSObject* WrapObject(JSContext *aCx, |
664 | | JS::Handle<JSObject*> aGivenProto) override |
665 | 0 | { |
666 | 0 | return HTMLCollection_Binding::Wrap(aCx, this, aGivenProto); |
667 | 0 | } |
668 | | |
669 | | using nsBaseContentList::Item; |
670 | | |
671 | | private: |
672 | 0 | virtual ~SimpleHTMLCollection() {} |
673 | | }; |
674 | | |
675 | | NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection, nsSimpleContentList, |
676 | | nsIHTMLCollection) |
677 | | |
678 | | } // namespace dom |
679 | | } // namespace mozilla |
680 | | |
681 | | void |
682 | | nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement) |
683 | 0 | { |
684 | 0 | if (!mNameContentList) { |
685 | 0 | mNameContentList = new SimpleHTMLCollection(aNode); |
686 | 0 | } |
687 | 0 |
|
688 | 0 | mNameContentList->AppendElement(aElement); |
689 | 0 | } |
690 | | |
691 | | void |
692 | | nsIdentifierMapEntry::RemoveNameElement(Element* aElement) |
693 | 0 | { |
694 | 0 | if (mNameContentList) { |
695 | 0 | mNameContentList->RemoveElement(aElement); |
696 | 0 | } |
697 | 0 | } |
698 | | |
699 | | bool |
700 | | nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() |
701 | 0 | { |
702 | 0 | Element* idElement = GetIdElement(); |
703 | 0 | return idElement && |
704 | 0 | nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement); |
705 | 0 | } |
706 | | |
707 | | size_t |
708 | | nsIdentifierMapEntry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
709 | 0 | { |
710 | 0 | return mKey.mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
711 | 0 | } |
712 | | |
713 | | // Helper structs for the content->subdoc map |
714 | | |
715 | | class SubDocMapEntry : public PLDHashEntryHdr |
716 | | { |
717 | | public: |
718 | | // Both of these are strong references |
719 | | Element *mKey; // must be first, to look like PLDHashEntryStub |
720 | | nsIDocument *mSubDocument; |
721 | | }; |
722 | | |
723 | | // nsOnloadBlocker implementation |
724 | | NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest) |
725 | | |
726 | | NS_IMETHODIMP |
727 | | nsOnloadBlocker::GetName(nsACString &aResult) |
728 | 0 | { |
729 | 0 | aResult.AssignLiteral("about:document-onload-blocker"); |
730 | 0 | return NS_OK; |
731 | 0 | } |
732 | | |
733 | | NS_IMETHODIMP |
734 | | nsOnloadBlocker::IsPending(bool *_retval) |
735 | 0 | { |
736 | 0 | *_retval = true; |
737 | 0 | return NS_OK; |
738 | 0 | } |
739 | | |
740 | | NS_IMETHODIMP |
741 | | nsOnloadBlocker::GetStatus(nsresult *status) |
742 | 0 | { |
743 | 0 | *status = NS_OK; |
744 | 0 | return NS_OK; |
745 | 0 | } |
746 | | |
747 | | NS_IMETHODIMP |
748 | | nsOnloadBlocker::Cancel(nsresult status) |
749 | 0 | { |
750 | 0 | return NS_OK; |
751 | 0 | } |
752 | | NS_IMETHODIMP |
753 | | nsOnloadBlocker::Suspend(void) |
754 | 0 | { |
755 | 0 | return NS_OK; |
756 | 0 | } |
757 | | NS_IMETHODIMP |
758 | | nsOnloadBlocker::Resume(void) |
759 | 0 | { |
760 | 0 | return NS_OK; |
761 | 0 | } |
762 | | |
763 | | NS_IMETHODIMP |
764 | | nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup) |
765 | 0 | { |
766 | 0 | *aLoadGroup = nullptr; |
767 | 0 | return NS_OK; |
768 | 0 | } |
769 | | |
770 | | NS_IMETHODIMP |
771 | | nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup) |
772 | 0 | { |
773 | 0 | return NS_OK; |
774 | 0 | } |
775 | | |
776 | | NS_IMETHODIMP |
777 | | nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags) |
778 | 0 | { |
779 | 0 | *aLoadFlags = nsIRequest::LOAD_NORMAL; |
780 | 0 | return NS_OK; |
781 | 0 | } |
782 | | |
783 | | NS_IMETHODIMP |
784 | | nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags) |
785 | 0 | { |
786 | 0 | return NS_OK; |
787 | 0 | } |
788 | | |
789 | | // ================================================================== |
790 | | |
791 | | nsExternalResourceMap::nsExternalResourceMap() |
792 | | : mHaveShutDown(false) |
793 | 0 | { |
794 | 0 | } |
795 | | |
796 | | nsIDocument* |
797 | | nsExternalResourceMap::RequestResource(nsIURI* aURI, |
798 | | nsIURI* aReferrer, |
799 | | uint32_t aReferrerPolicy, |
800 | | nsINode* aRequestingNode, |
801 | | nsIDocument* aDisplayDocument, |
802 | | ExternalResourceLoad** aPendingLoad) |
803 | 0 | { |
804 | 0 | // If we ever start allowing non-same-origin loads here, we might need to do |
805 | 0 | // something interesting with aRequestingPrincipal even for the hashtable |
806 | 0 | // gets. |
807 | 0 | MOZ_ASSERT(aURI, "Must have a URI"); |
808 | 0 | MOZ_ASSERT(aRequestingNode, "Must have a node"); |
809 | 0 | *aPendingLoad = nullptr; |
810 | 0 | if (mHaveShutDown) { |
811 | 0 | return nullptr; |
812 | 0 | } |
813 | 0 | |
814 | 0 | // First, make sure we strip the ref from aURI. |
815 | 0 | nsCOMPtr<nsIURI> clone; |
816 | 0 | nsresult rv = NS_GetURIWithoutRef(aURI, getter_AddRefs(clone)); |
817 | 0 | if (NS_FAILED(rv) || !clone) { |
818 | 0 | return nullptr; |
819 | 0 | } |
820 | 0 | |
821 | 0 | ExternalResource* resource; |
822 | 0 | mMap.Get(clone, &resource); |
823 | 0 | if (resource) { |
824 | 0 | return resource->mDocument; |
825 | 0 | } |
826 | 0 | |
827 | 0 | RefPtr<PendingLoad>& loadEntry = mPendingLoads.GetOrInsert(clone); |
828 | 0 | if (loadEntry) { |
829 | 0 | RefPtr<PendingLoad> load(loadEntry); |
830 | 0 | load.forget(aPendingLoad); |
831 | 0 | return nullptr; |
832 | 0 | } |
833 | 0 | |
834 | 0 | RefPtr<PendingLoad> load(new PendingLoad(aDisplayDocument)); |
835 | 0 | loadEntry = load; |
836 | 0 |
|
837 | 0 | if (NS_FAILED(load->StartLoad(clone, aReferrer, aReferrerPolicy, |
838 | 0 | aRequestingNode))) { |
839 | 0 | // Make sure we don't thrash things by trying this load again, since |
840 | 0 | // chances are it failed for good reasons (security check, etc). |
841 | 0 | AddExternalResource(clone, nullptr, nullptr, aDisplayDocument); |
842 | 0 | } else { |
843 | 0 | load.forget(aPendingLoad); |
844 | 0 | } |
845 | 0 |
|
846 | 0 | return nullptr; |
847 | 0 | } |
848 | | |
849 | | void |
850 | | nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback, |
851 | | void* aData) |
852 | 0 | { |
853 | 0 | for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) { |
854 | 0 | nsExternalResourceMap::ExternalResource* resource = iter.UserData(); |
855 | 0 | if (resource->mDocument && !aCallback(resource->mDocument, aData)) { |
856 | 0 | break; |
857 | 0 | } |
858 | 0 | } |
859 | 0 | } |
860 | | |
861 | | void |
862 | | nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const |
863 | 0 | { |
864 | 0 | // mPendingLoads will get cleared out as the requests complete, so |
865 | 0 | // no need to worry about those here. |
866 | 0 | for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) { |
867 | 0 | nsExternalResourceMap::ExternalResource* resource = iter.UserData(); |
868 | 0 |
|
869 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, |
870 | 0 | "mExternalResourceMap.mMap entry" |
871 | 0 | "->mDocument"); |
872 | 0 | aCallback->NoteXPCOMChild(resource->mDocument); |
873 | 0 |
|
874 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, |
875 | 0 | "mExternalResourceMap.mMap entry" |
876 | 0 | "->mViewer"); |
877 | 0 | aCallback->NoteXPCOMChild(resource->mViewer); |
878 | 0 |
|
879 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, |
880 | 0 | "mExternalResourceMap.mMap entry" |
881 | 0 | "->mLoadGroup"); |
882 | 0 | aCallback->NoteXPCOMChild(resource->mLoadGroup); |
883 | 0 | } |
884 | 0 | } |
885 | | |
886 | | void |
887 | | nsExternalResourceMap::HideViewers() |
888 | 0 | { |
889 | 0 | for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) { |
890 | 0 | nsCOMPtr<nsIContentViewer> viewer = iter.UserData()->mViewer; |
891 | 0 | if (viewer) { |
892 | 0 | viewer->Hide(); |
893 | 0 | } |
894 | 0 | } |
895 | 0 | } |
896 | | |
897 | | void |
898 | | nsExternalResourceMap::ShowViewers() |
899 | 0 | { |
900 | 0 | for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) { |
901 | 0 | nsCOMPtr<nsIContentViewer> viewer = iter.UserData()->mViewer; |
902 | 0 | if (viewer) { |
903 | 0 | viewer->Show(); |
904 | 0 | } |
905 | 0 | } |
906 | 0 | } |
907 | | |
908 | | void |
909 | | TransferZoomLevels(nsIDocument* aFromDoc, |
910 | | nsIDocument* aToDoc) |
911 | 0 | { |
912 | 0 | MOZ_ASSERT(aFromDoc && aToDoc, |
913 | 0 | "transferring zoom levels from/to null doc"); |
914 | 0 |
|
915 | 0 | nsPresContext* fromCtxt = aFromDoc->GetPresContext(); |
916 | 0 | if (!fromCtxt) |
917 | 0 | return; |
918 | 0 | |
919 | 0 | nsPresContext* toCtxt = aToDoc->GetPresContext(); |
920 | 0 | if (!toCtxt) |
921 | 0 | return; |
922 | 0 | |
923 | 0 | toCtxt->SetFullZoom(fromCtxt->GetFullZoom()); |
924 | 0 | toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize()); |
925 | 0 | toCtxt->SetTextZoom(fromCtxt->TextZoom()); |
926 | 0 | toCtxt->SetOverrideDPPX(fromCtxt->GetOverrideDPPX()); |
927 | 0 | } |
928 | | |
929 | | void |
930 | | TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc) |
931 | 0 | { |
932 | 0 | MOZ_ASSERT(aFromDoc && aToDoc, |
933 | 0 | "transferring showing state from/to null doc"); |
934 | 0 |
|
935 | 0 | if (aFromDoc->IsShowing()) { |
936 | 0 | aToDoc->OnPageShow(true, nullptr); |
937 | 0 | } |
938 | 0 | } |
939 | | |
940 | | nsresult |
941 | | nsExternalResourceMap::AddExternalResource(nsIURI* aURI, |
942 | | nsIContentViewer* aViewer, |
943 | | nsILoadGroup* aLoadGroup, |
944 | | nsIDocument* aDisplayDocument) |
945 | 0 | { |
946 | 0 | MOZ_ASSERT(aURI, "Unexpected call"); |
947 | 0 | MOZ_ASSERT((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup), |
948 | 0 | "Must have both or neither"); |
949 | 0 |
|
950 | 0 | RefPtr<PendingLoad> load; |
951 | 0 | mPendingLoads.Remove(aURI, getter_AddRefs(load)); |
952 | 0 |
|
953 | 0 | nsresult rv = NS_OK; |
954 | 0 |
|
955 | 0 | nsCOMPtr<nsIDocument> doc; |
956 | 0 | if (aViewer) { |
957 | 0 | doc = aViewer->GetDocument(); |
958 | 0 | NS_ASSERTION(doc, "Must have a document"); |
959 | 0 |
|
960 | 0 | if (doc->IsXULDocument()) { |
961 | 0 | // We don't handle XUL stuff here yet. |
962 | 0 | rv = NS_ERROR_NOT_AVAILABLE; |
963 | 0 | } else { |
964 | 0 | doc->SetDisplayDocument(aDisplayDocument); |
965 | 0 |
|
966 | 0 | // Make sure that hiding our viewer will tear down its presentation. |
967 | 0 | aViewer->SetSticky(false); |
968 | 0 |
|
969 | 0 | rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0)); |
970 | 0 | if (NS_SUCCEEDED(rv)) { |
971 | 0 | rv = aViewer->Open(nullptr, nullptr); |
972 | 0 | } |
973 | 0 | } |
974 | 0 |
|
975 | 0 | if (NS_FAILED(rv)) { |
976 | 0 | doc = nullptr; |
977 | 0 | aViewer = nullptr; |
978 | 0 | aLoadGroup = nullptr; |
979 | 0 | } |
980 | 0 | } |
981 | 0 |
|
982 | 0 | ExternalResource* newResource = new ExternalResource(); |
983 | 0 | mMap.Put(aURI, newResource); |
984 | 0 |
|
985 | 0 | newResource->mDocument = doc; |
986 | 0 | newResource->mViewer = aViewer; |
987 | 0 | newResource->mLoadGroup = aLoadGroup; |
988 | 0 | if (doc) { |
989 | 0 | TransferZoomLevels(aDisplayDocument, doc); |
990 | 0 | TransferShowingState(aDisplayDocument, doc); |
991 | 0 | } |
992 | 0 |
|
993 | 0 | const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers(); |
994 | 0 | for (uint32_t i = 0; i < obs.Length(); ++i) { |
995 | 0 | obs[i]->Observe(doc, "external-resource-document-created", nullptr); |
996 | 0 | } |
997 | 0 |
|
998 | 0 | return rv; |
999 | 0 | } |
1000 | | |
1001 | | NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad, |
1002 | | nsIStreamListener, |
1003 | | nsIRequestObserver) |
1004 | | |
1005 | | NS_IMETHODIMP |
1006 | | nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest, |
1007 | | nsISupports *aContext) |
1008 | 0 | { |
1009 | 0 | nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap(); |
1010 | 0 | if (map.HaveShutDown()) { |
1011 | 0 | return NS_BINDING_ABORTED; |
1012 | 0 | } |
1013 | 0 | |
1014 | 0 | nsCOMPtr<nsIContentViewer> viewer; |
1015 | 0 | nsCOMPtr<nsILoadGroup> loadGroup; |
1016 | 0 | nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer), |
1017 | 0 | getter_AddRefs(loadGroup)); |
1018 | 0 |
|
1019 | 0 | // Make sure to do this no matter what |
1020 | 0 | nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup, |
1021 | 0 | mDisplayDocument); |
1022 | 0 | if (NS_FAILED(rv)) { |
1023 | 0 | return rv; |
1024 | 0 | } |
1025 | 0 | if (NS_FAILED(rv2)) { |
1026 | 0 | mTargetListener = nullptr; |
1027 | 0 | return rv2; |
1028 | 0 | } |
1029 | 0 | |
1030 | 0 | return mTargetListener->OnStartRequest(aRequest, aContext); |
1031 | 0 | } |
1032 | | |
1033 | | nsresult |
1034 | | nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest, |
1035 | | nsIContentViewer** aViewer, |
1036 | | nsILoadGroup** aLoadGroup) |
1037 | 0 | { |
1038 | 0 | MOZ_ASSERT(!mTargetListener, "Unexpected call to OnStartRequest"); |
1039 | 0 | *aViewer = nullptr; |
1040 | 0 | *aLoadGroup = nullptr; |
1041 | 0 |
|
1042 | 0 | nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest)); |
1043 | 0 | NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED); |
1044 | 0 |
|
1045 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest)); |
1046 | 0 | if (httpChannel) { |
1047 | 0 | bool requestSucceeded; |
1048 | 0 | if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) || |
1049 | 0 | !requestSucceeded) { |
1050 | 0 | // Bail out on this load, since it looks like we have an HTTP error page |
1051 | 0 | return NS_BINDING_ABORTED; |
1052 | 0 | } |
1053 | 0 | } |
1054 | 0 | |
1055 | 0 | nsAutoCString type; |
1056 | 0 | chan->GetContentType(type); |
1057 | 0 |
|
1058 | 0 | nsCOMPtr<nsILoadGroup> loadGroup; |
1059 | 0 | chan->GetLoadGroup(getter_AddRefs(loadGroup)); |
1060 | 0 |
|
1061 | 0 | // Give this document its own loadgroup |
1062 | 0 | nsCOMPtr<nsILoadGroup> newLoadGroup = |
1063 | 0 | do_CreateInstance(NS_LOADGROUP_CONTRACTID); |
1064 | 0 | NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY); |
1065 | 0 | newLoadGroup->SetLoadGroup(loadGroup); |
1066 | 0 |
|
1067 | 0 | nsCOMPtr<nsIInterfaceRequestor> callbacks; |
1068 | 0 | loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks)); |
1069 | 0 |
|
1070 | 0 | nsCOMPtr<nsIInterfaceRequestor> newCallbacks = |
1071 | 0 | new LoadgroupCallbacks(callbacks); |
1072 | 0 | newLoadGroup->SetNotificationCallbacks(newCallbacks); |
1073 | 0 |
|
1074 | 0 | // This is some serious hackery cribbed from docshell |
1075 | 0 | nsCOMPtr<nsICategoryManager> catMan = |
1076 | 0 | do_GetService(NS_CATEGORYMANAGER_CONTRACTID); |
1077 | 0 | NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE); |
1078 | 0 | nsCString contractId; |
1079 | 0 | nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type, |
1080 | 0 | contractId); |
1081 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1082 | 0 | nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory = |
1083 | 0 | do_GetService(contractId.get()); |
1084 | 0 | NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE); |
1085 | 0 |
|
1086 | 0 | nsCOMPtr<nsIContentViewer> viewer; |
1087 | 0 | nsCOMPtr<nsIStreamListener> listener; |
1088 | 0 | rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup, |
1089 | 0 | type, nullptr, nullptr, |
1090 | 0 | getter_AddRefs(listener), |
1091 | 0 | getter_AddRefs(viewer)); |
1092 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1093 | 0 | NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED); |
1094 | 0 |
|
1095 | 0 | nsCOMPtr<nsIParser> parser = do_QueryInterface(listener); |
1096 | 0 | if (!parser) { |
1097 | 0 | /// We don't want to deal with the various fake documents yet |
1098 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1099 | 0 | } |
1100 | 0 | |
1101 | 0 | // We can't handle HTML and other weird things here yet. |
1102 | 0 | nsIContentSink* sink = parser->GetContentSink(); |
1103 | 0 | nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink); |
1104 | 0 | if (!xmlSink) { |
1105 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1106 | 0 | } |
1107 | 0 | |
1108 | 0 | listener.swap(mTargetListener); |
1109 | 0 | viewer.forget(aViewer); |
1110 | 0 | newLoadGroup.forget(aLoadGroup); |
1111 | 0 | return NS_OK; |
1112 | 0 | } |
1113 | | |
1114 | | NS_IMETHODIMP |
1115 | | nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest, |
1116 | | nsISupports* aContext, |
1117 | | nsIInputStream* aStream, |
1118 | | uint64_t aOffset, |
1119 | | uint32_t aCount) |
1120 | 0 | { |
1121 | 0 | // mTargetListener might be null if SetupViewer or AddExternalResource failed. |
1122 | 0 | NS_ENSURE_TRUE(mTargetListener, NS_ERROR_FAILURE); |
1123 | 0 | if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) { |
1124 | 0 | return NS_BINDING_ABORTED; |
1125 | 0 | } |
1126 | 0 | return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset, |
1127 | 0 | aCount); |
1128 | 0 | } |
1129 | | |
1130 | | NS_IMETHODIMP |
1131 | | nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest, |
1132 | | nsISupports* aContext, |
1133 | | nsresult aStatus) |
1134 | 0 | { |
1135 | 0 | // mTargetListener might be null if SetupViewer or AddExternalResource failed |
1136 | 0 | if (mTargetListener) { |
1137 | 0 | nsCOMPtr<nsIStreamListener> listener; |
1138 | 0 | mTargetListener.swap(listener); |
1139 | 0 | return listener->OnStopRequest(aRequest, aContext, aStatus); |
1140 | 0 | } |
1141 | 0 | |
1142 | 0 | return NS_OK; |
1143 | 0 | } |
1144 | | |
1145 | | nsresult |
1146 | | nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI, |
1147 | | nsIURI* aReferrer, |
1148 | | uint32_t aReferrerPolicy, |
1149 | | nsINode* aRequestingNode) |
1150 | 0 | { |
1151 | 0 | MOZ_ASSERT(aURI, "Must have a URI"); |
1152 | 0 | MOZ_ASSERT(aRequestingNode, "Must have a node"); |
1153 | 0 |
|
1154 | 0 | nsCOMPtr<nsILoadGroup> loadGroup = |
1155 | 0 | aRequestingNode->OwnerDoc()->GetDocumentLoadGroup(); |
1156 | 0 |
|
1157 | 0 | nsresult rv = NS_OK; |
1158 | 0 | nsCOMPtr<nsIChannel> channel; |
1159 | 0 | rv = NS_NewChannel(getter_AddRefs(channel), |
1160 | 0 | aURI, |
1161 | 0 | aRequestingNode, |
1162 | 0 | nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS, |
1163 | 0 | nsIContentPolicy::TYPE_OTHER, |
1164 | 0 | nullptr, // aPerformanceStorage |
1165 | 0 | loadGroup); |
1166 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1167 | 0 |
|
1168 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); |
1169 | 0 | if (httpChannel) { |
1170 | 0 | rv = httpChannel->SetReferrerWithPolicy(aReferrer, aReferrerPolicy); |
1171 | 0 | Unused << NS_WARN_IF(NS_FAILED(rv)); |
1172 | 0 | } |
1173 | 0 |
|
1174 | 0 | mURI = aURI; |
1175 | 0 |
|
1176 | 0 | return channel->AsyncOpen2(this); |
1177 | 0 | } |
1178 | | |
1179 | | NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks, |
1180 | | nsIInterfaceRequestor) |
1181 | | |
1182 | | #define IMPL_SHIM(_i) \ |
1183 | | NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i) |
1184 | | |
1185 | | IMPL_SHIM(nsILoadContext) |
1186 | | IMPL_SHIM(nsIProgressEventSink) |
1187 | | IMPL_SHIM(nsIChannelEventSink) |
1188 | | IMPL_SHIM(nsISecurityEventSink) |
1189 | | IMPL_SHIM(nsIApplicationCacheContainer) |
1190 | | |
1191 | | #undef IMPL_SHIM |
1192 | | |
1193 | 0 | #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i)) |
1194 | | |
1195 | | #define TRY_SHIM(_i) \ |
1196 | 0 | PR_BEGIN_MACRO \ |
1197 | 0 | if (IID_IS(_i)) { \ |
1198 | 0 | nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \ |
1199 | 0 | if (!real) { \ |
1200 | 0 | return NS_NOINTERFACE; \ |
1201 | 0 | } \ |
1202 | 0 | nsCOMPtr<_i> shim = new _i##Shim(this, real); \ |
1203 | 0 | shim.forget(aSink); \ |
1204 | 0 | return NS_OK; \ |
1205 | 0 | } \ |
1206 | 0 | PR_END_MACRO |
1207 | | |
1208 | | NS_IMETHODIMP |
1209 | | nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID, |
1210 | | void **aSink) |
1211 | 0 | { |
1212 | 0 | if (mCallbacks && |
1213 | 0 | (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2) || |
1214 | 0 | IID_IS(nsITabChild))) { |
1215 | 0 | return mCallbacks->GetInterface(aIID, aSink); |
1216 | 0 | } |
1217 | 0 | |
1218 | 0 | *aSink = nullptr; |
1219 | 0 |
|
1220 | 0 | TRY_SHIM(nsILoadContext); |
1221 | 0 | TRY_SHIM(nsIProgressEventSink); |
1222 | 0 | TRY_SHIM(nsIChannelEventSink); |
1223 | 0 | TRY_SHIM(nsISecurityEventSink); |
1224 | 0 | TRY_SHIM(nsIApplicationCacheContainer); |
1225 | 0 |
|
1226 | 0 | return NS_NOINTERFACE; |
1227 | 0 | } |
1228 | | |
1229 | | #undef TRY_SHIM |
1230 | | #undef IID_IS |
1231 | | |
1232 | | nsExternalResourceMap::ExternalResource::~ExternalResource() |
1233 | 0 | { |
1234 | 0 | if (mViewer) { |
1235 | 0 | mViewer->Close(nullptr); |
1236 | 0 | mViewer->Destroy(); |
1237 | 0 | } |
1238 | 0 | } |
1239 | | |
1240 | | // ================================================================== |
1241 | | // = |
1242 | | // ================================================================== |
1243 | | |
1244 | | // If we ever have an nsIDocumentObserver notification for stylesheet title |
1245 | | // changes we should update the list from that instead of overriding |
1246 | | // EnsureFresh. |
1247 | | class nsDOMStyleSheetSetList final : public DOMStringList |
1248 | | { |
1249 | | public: |
1250 | | explicit nsDOMStyleSheetSetList(nsIDocument* aDocument); |
1251 | | |
1252 | | void Disconnect() |
1253 | 0 | { |
1254 | 0 | mDocument = nullptr; |
1255 | 0 | } |
1256 | | |
1257 | | virtual void EnsureFresh() override; |
1258 | | |
1259 | | protected: |
1260 | | nsIDocument* mDocument; // Our document; weak ref. It'll let us know if it |
1261 | | // dies. |
1262 | | }; |
1263 | | |
1264 | | nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument) |
1265 | | : mDocument(aDocument) |
1266 | 0 | { |
1267 | 0 | NS_ASSERTION(mDocument, "Must have document!"); |
1268 | 0 | } |
1269 | | |
1270 | | void |
1271 | | nsDOMStyleSheetSetList::EnsureFresh() |
1272 | 0 | { |
1273 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1274 | 0 |
|
1275 | 0 | mNames.Clear(); |
1276 | 0 |
|
1277 | 0 | if (!mDocument) { |
1278 | 0 | return; // Spec says "no exceptions", and we have no style sets if we have |
1279 | 0 | // no document, for sure |
1280 | 0 | } |
1281 | 0 | |
1282 | 0 | size_t count = mDocument->SheetCount(); |
1283 | 0 | nsAutoString title; |
1284 | 0 | for (size_t index = 0; index < count; index++) { |
1285 | 0 | StyleSheet* sheet = mDocument->SheetAt(index); |
1286 | 0 | NS_ASSERTION(sheet, "Null sheet in sheet list!"); |
1287 | 0 | sheet->GetTitle(title); |
1288 | 0 | if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) { |
1289 | 0 | return; |
1290 | 0 | } |
1291 | 0 | } |
1292 | 0 | } |
1293 | | |
1294 | | // ================================================================== |
1295 | | nsIDocument::SelectorCache::SelectorCache(nsIEventTarget* aEventTarget) |
1296 | | : nsExpirationTracker<SelectorCacheKey, 4>( |
1297 | | 1000, "nsIDocument::SelectorCache", aEventTarget) |
1298 | 0 | { } |
1299 | | |
1300 | | nsIDocument::SelectorCache::~SelectorCache() |
1301 | 0 | { |
1302 | 0 | AgeAllGenerations(); |
1303 | 0 | } |
1304 | | |
1305 | | void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector) |
1306 | 0 | { |
1307 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1308 | 0 | MOZ_ASSERT(aSelector); |
1309 | 0 |
|
1310 | 0 | // There is no guarantee that this method won't be re-entered when selector |
1311 | 0 | // matching is ongoing because "memory-pressure" could be notified immediately |
1312 | 0 | // when OOM happens according to the design of nsExpirationTracker. |
1313 | 0 | // The perfect solution is to delete the |aSelector| and its |
1314 | 0 | // RawServoSelectorList in mTable asynchronously. |
1315 | 0 | // We remove these objects synchronously for now because NotifyExpired() will |
1316 | 0 | // never be triggered by "memory-pressure" which is not implemented yet in |
1317 | 0 | // the stage 2 of mozalloc_handle_oom(). |
1318 | 0 | // Once these objects are removed asynchronously, we should update the warning |
1319 | 0 | // added in mozalloc_handle_oom() as well. |
1320 | 0 | RemoveObject(aSelector); |
1321 | 0 | mTable.Remove(aSelector->mKey); |
1322 | 0 | delete aSelector; |
1323 | 0 | } |
1324 | | |
1325 | | struct nsIDocument::FrameRequest |
1326 | | { |
1327 | | FrameRequest(FrameRequestCallback& aCallback, |
1328 | | int32_t aHandle) : |
1329 | | mCallback(&aCallback), |
1330 | | mHandle(aHandle) |
1331 | 0 | {} |
1332 | | |
1333 | | // Conversion operator so that we can append these to a |
1334 | | // FrameRequestCallbackList |
1335 | 0 | operator const RefPtr<FrameRequestCallback>& () const { |
1336 | 0 | return mCallback; |
1337 | 0 | } |
1338 | | |
1339 | | // Comparator operators to allow RemoveElementSorted with an |
1340 | | // integer argument on arrays of FrameRequest |
1341 | 0 | bool operator==(int32_t aHandle) const { |
1342 | 0 | return mHandle == aHandle; |
1343 | 0 | } |
1344 | 0 | bool operator<(int32_t aHandle) const { |
1345 | 0 | return mHandle < aHandle; |
1346 | 0 | } |
1347 | | |
1348 | | RefPtr<FrameRequestCallback> mCallback; |
1349 | | int32_t mHandle; |
1350 | | }; |
1351 | | |
1352 | | // ================================================================== |
1353 | | // = |
1354 | | // ================================================================== |
1355 | | nsIDocument::nsIDocument() |
1356 | | : nsINode(nullptr), |
1357 | | DocumentOrShadowRoot(*this), |
1358 | | mReferrerPolicySet(false), |
1359 | | mReferrerPolicy(mozilla::net::RP_Unset), |
1360 | | mBlockAllMixedContent(false), |
1361 | | mBlockAllMixedContentPreloads(false), |
1362 | | mUpgradeInsecureRequests(false), |
1363 | | mUpgradeInsecurePreloads(false), |
1364 | | mCharacterSet(WINDOWS_1252_ENCODING), |
1365 | | mCharacterSetSource(0), |
1366 | | mParentDocument(nullptr), |
1367 | | mCachedRootElement(nullptr), |
1368 | | mNodeInfoManager(nullptr), |
1369 | | #ifdef DEBUG |
1370 | | mStyledLinksCleared(false), |
1371 | | #endif |
1372 | | mBidiEnabled(false), |
1373 | | mMathMLEnabled(false), |
1374 | | mIsInitialDocumentInWindow(false), |
1375 | | mIgnoreDocGroupMismatches(false), |
1376 | | mLoadedAsData(false), |
1377 | | mLoadedAsInteractiveData(false), |
1378 | | mMayStartLayout(true), |
1379 | | mHaveFiredTitleChange(false), |
1380 | | mIsShowing(false), |
1381 | | mVisible(true), |
1382 | | mRemovedFromDocShell(false), |
1383 | | // mAllowDNSPrefetch starts true, so that we can always reliably && it |
1384 | | // with various values that might disable it. Since we never prefetch |
1385 | | // unless we get a window, and in that case the docshell value will get |
1386 | | // &&-ed in, this is safe. |
1387 | | mAllowDNSPrefetch(true), |
1388 | | mIsStaticDocument(false), |
1389 | | mCreatingStaticClone(false), |
1390 | | mInUnlinkOrDeletion(false), |
1391 | | mHasHadScriptHandlingObject(false), |
1392 | | mIsBeingUsedAsImage(false), |
1393 | | mIsSyntheticDocument(false), |
1394 | | mHasLinksToUpdateRunnable(false), |
1395 | | mFlushingPendingLinkUpdates(false), |
1396 | | mMayHaveDOMMutationObservers(false), |
1397 | | mMayHaveAnimationObservers(false), |
1398 | | mHasMixedActiveContentLoaded(false), |
1399 | | mHasMixedActiveContentBlocked(false), |
1400 | | mHasMixedDisplayContentLoaded(false), |
1401 | | mHasMixedDisplayContentBlocked(false), |
1402 | | mHasMixedContentObjectSubrequest(false), |
1403 | | mHasCSP(false), |
1404 | | mHasUnsafeEvalCSP(false), |
1405 | | mHasUnsafeInlineCSP(false), |
1406 | | mHasTrackingContentBlocked(false), |
1407 | | mHasSlowTrackingContentBlocked(false), |
1408 | | mHasAllCookiesBlocked(false), |
1409 | | mHasTrackingCookiesBlocked(false), |
1410 | | mHasForeignCookiesBlocked(false), |
1411 | | mHasCookiesBlockedByPermission(false), |
1412 | | mHasTrackingContentLoaded(false), |
1413 | | mBFCacheDisallowed(false), |
1414 | | mHasHadDefaultView(false), |
1415 | | mStyleSheetChangeEventsEnabled(false), |
1416 | | mIsSrcdocDocument(false), |
1417 | | mDidDocumentOpen(false), |
1418 | | mHasDisplayDocument(false), |
1419 | | mFontFaceSetDirty(true), |
1420 | | mGetUserFontSetCalled(false), |
1421 | | mDidFireDOMContentLoaded(true), |
1422 | | mHasScrollLinkedEffect(false), |
1423 | | mFrameRequestCallbacksScheduled(false), |
1424 | | mIsTopLevelContentDocument(false), |
1425 | | mIsContentDocument(false), |
1426 | | mDidCallBeginLoad(false), |
1427 | | mAllowPaymentRequest(false), |
1428 | | mEncodingMenuDisabled(false), |
1429 | | mIsShadowDOMEnabled(false), |
1430 | | mIsSVGGlyphsDocument(false), |
1431 | | mInDestructor(false), |
1432 | | mIsGoingAway(false), |
1433 | | mInXBLUpdate(false), |
1434 | | mNeedsReleaseAfterStackRefCntRelease(false), |
1435 | | mStyleSetFilled(false), |
1436 | | mSSApplicableStateNotificationPending(false), |
1437 | | mMayHaveTitleElement(false), |
1438 | | mDOMLoadingSet(false), |
1439 | | mDOMInteractiveSet(false), |
1440 | | mDOMCompleteSet(false), |
1441 | | mAutoFocusFired(false), |
1442 | | mScrolledToRefAlready(false), |
1443 | | mChangeScrollPosWhenScrollingToRef(false), |
1444 | | mHasWarnedAboutBoxObjects(false), |
1445 | | mDelayFrameLoaderInitialization(false), |
1446 | | mSynchronousDOMContentLoaded(false), |
1447 | | mMaybeServiceWorkerControlled(false), |
1448 | | mValidWidth(false), |
1449 | | mValidHeight(false), |
1450 | | mAutoSize(false), |
1451 | | mAllowZoom(false), |
1452 | | mAllowDoubleTapZoom(false), |
1453 | | mValidScaleFloat(false), |
1454 | | mValidMaxScale(false), |
1455 | | mScaleStrEmpty(false), |
1456 | | mWidthStrEmpty(false), |
1457 | | mParserAborted(false), |
1458 | | mReportedUseCounters(false), |
1459 | | mHasReportedShadowDOMUsage(false), |
1460 | | mDocTreeHadAudibleMedia(false), |
1461 | | mDocTreeHadPlayRevoked(false), |
1462 | | #ifdef DEBUG |
1463 | | mWillReparent(false), |
1464 | | #endif |
1465 | | mPendingFullscreenRequests(0), |
1466 | | mXMLDeclarationBits(0), |
1467 | | mOnloadBlockCount(0), |
1468 | | mAsyncOnloadBlockCount(0), |
1469 | | mCompatMode(eCompatibility_FullStandards), |
1470 | | mReadyState(ReadyState::READYSTATE_UNINITIALIZED), |
1471 | | #ifdef MOZILLA_INTERNAL_API |
1472 | | mVisibilityState(dom::VisibilityState::Hidden), |
1473 | | #else |
1474 | | mDummy(0), |
1475 | | #endif |
1476 | | mType(eUnknown), |
1477 | | mDefaultElementType(0), |
1478 | | mAllowXULXBL(eTriUnset), |
1479 | | mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS), |
1480 | | mSandboxFlags(0), |
1481 | | mPartID(0), |
1482 | | mMarkedCCGeneration(0), |
1483 | | mPresShell(nullptr), |
1484 | | mSubtreeModifiedDepth(0), |
1485 | | mPreloadPictureDepth(0), |
1486 | | mEventsSuppressed(0), |
1487 | | mIgnoreDestructiveWritesCounter(0), |
1488 | | mFrameRequestCallbackCounter(0), |
1489 | | mStaticCloneCount(0), |
1490 | | mWindow(nullptr), |
1491 | | mBFCacheEntry(nullptr), |
1492 | | mInSyncOperationCount(0), |
1493 | | mBlockDOMContentLoaded(0), |
1494 | | mUseCounters(0), |
1495 | | mChildDocumentUseCounters(0), |
1496 | | mNotifiedPageForUseCounter(0), |
1497 | | mUserHasInteracted(false), |
1498 | | mHasUserInteractionTimerScheduled(false), |
1499 | | mUserGestureActivated(false), |
1500 | | mStackRefCnt(0), |
1501 | | mUpdateNestLevel(0), |
1502 | | mViewportType(Unknown), |
1503 | | mViewportOverflowType(ViewportOverflowType::NoOverflow), |
1504 | | mSubDocuments(nullptr), |
1505 | | mHeaderData(nullptr), |
1506 | | mFlashClassification(FlashClassification::Unclassified), |
1507 | | mBoxObjectTable(nullptr), |
1508 | | mCurrentOrientationAngle(0), |
1509 | | mCurrentOrientationType(OrientationType::Portrait_primary), |
1510 | | mServoRestyleRootDirtyBits(0), |
1511 | | mThrowOnDynamicMarkupInsertionCounter(0), |
1512 | | mIgnoreOpensDuringUnloadCounter(0), |
1513 | | mNumTrackersFound(0), |
1514 | | mNumTrackersBlocked(0), |
1515 | | mDocLWTheme(Doc_Theme_Uninitialized) |
1516 | 0 | { |
1517 | 0 | SetIsInDocument(); |
1518 | 0 | SetIsConnected(true); |
1519 | 0 |
|
1520 | 0 | if (StaticPrefs::layout_css_use_counters_enabled()) { |
1521 | 0 | mStyleUseCounters.reset(Servo_UseCounters_Create()); |
1522 | 0 | } |
1523 | 0 | } |
1524 | | |
1525 | | nsDocument::nsDocument(const char* aContentType) |
1526 | | : nsIDocument() |
1527 | 0 | { |
1528 | 0 | SetContentTypeInternal(nsDependentCString(aContentType)); |
1529 | 0 |
|
1530 | 0 | MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this)); |
1531 | 0 |
|
1532 | 0 | // Start out mLastStyleSheetSet as null, per spec |
1533 | 0 | SetDOMStringToNull(mLastStyleSheetSet); |
1534 | 0 |
|
1535 | 0 | // void state used to differentiate an empty source from an unselected source |
1536 | 0 | mPreloadPictureFoundSource.SetIsVoid(true); |
1537 | 0 | // For determining if this is a flash document which should be |
1538 | 0 | // blocked based on its principal. |
1539 | 0 | mPrincipalFlashClassifier = new PrincipalFlashClassifier(); |
1540 | 0 | } |
1541 | | |
1542 | | void |
1543 | | nsIDocument::ClearAllBoxObjects() |
1544 | 0 | { |
1545 | 0 | if (mBoxObjectTable) { |
1546 | 0 | for (auto iter = mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) { |
1547 | 0 | nsPIBoxObject* boxObject = iter.UserData(); |
1548 | 0 | if (boxObject) { |
1549 | 0 | boxObject->Clear(); |
1550 | 0 | } |
1551 | 0 | } |
1552 | 0 | delete mBoxObjectTable; |
1553 | 0 | mBoxObjectTable = nullptr; |
1554 | 0 | } |
1555 | 0 | } |
1556 | | |
1557 | | nsIDocument::~nsIDocument() |
1558 | 0 | { |
1559 | 0 | MOZ_ASSERT(mDOMMediaQueryLists.isEmpty(), |
1560 | 0 | "must not have media query lists left"); |
1561 | 0 |
|
1562 | 0 | if (mNodeInfoManager) { |
1563 | 0 | mNodeInfoManager->DropDocumentReference(); |
1564 | 0 | } |
1565 | 0 |
|
1566 | 0 | if (mDocGroup) { |
1567 | 0 | mDocGroup->RemoveDocument(this); |
1568 | 0 | } |
1569 | 0 |
|
1570 | 0 | UnlinkOriginalDocumentIfStatic(); |
1571 | 0 | } |
1572 | | |
1573 | | bool |
1574 | | nsIDocument::IsAboutPage() const |
1575 | 0 | { |
1576 | 0 | nsCOMPtr<nsIPrincipal> principal = NodePrincipal(); |
1577 | 0 | nsCOMPtr<nsIURI> uri; |
1578 | 0 | principal->GetURI(getter_AddRefs(uri)); |
1579 | 0 | bool isAboutScheme = true; |
1580 | 0 | if (uri) { |
1581 | 0 | uri->SchemeIs("about", &isAboutScheme); |
1582 | 0 | } |
1583 | 0 | return isAboutScheme; |
1584 | 0 | } |
1585 | | |
1586 | | void |
1587 | | nsIDocument::ConstructUbiNode(void* storage) |
1588 | 0 | { |
1589 | 0 | JS::ubi::Concrete<nsIDocument>::construct(storage, this); |
1590 | 0 | } |
1591 | | |
1592 | | |
1593 | | nsDocument::~nsDocument() |
1594 | 0 | { |
1595 | 0 | MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p destroyed", this)); |
1596 | 0 |
|
1597 | 0 | NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document"); |
1598 | 0 |
|
1599 | 0 | if (IsTopLevelContentDocument()) { |
1600 | 0 | //don't report for about: pages |
1601 | 0 | if (!IsAboutPage()) { |
1602 | 0 | // Record the page load |
1603 | 0 | uint32_t pageLoaded = 1; |
1604 | 0 | Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded); |
1605 | 0 | // Record the mixed content status of the docshell in Telemetry |
1606 | 0 | enum { |
1607 | 0 | NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page |
1608 | 0 | MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content |
1609 | 0 | MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content |
1610 | 0 | MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed Display & Mixed Active Content |
1611 | 0 | }; |
1612 | 0 |
|
1613 | 0 | bool mixedActiveLoaded = GetHasMixedActiveContentLoaded(); |
1614 | 0 | bool mixedActiveBlocked = GetHasMixedActiveContentBlocked(); |
1615 | 0 |
|
1616 | 0 | bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded(); |
1617 | 0 | bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked(); |
1618 | 0 |
|
1619 | 0 | bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded); |
1620 | 0 | bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded); |
1621 | 0 |
|
1622 | 0 | uint32_t mixedContentLevel = NO_MIXED_CONTENT; |
1623 | 0 | if (hasMixedDisplay && hasMixedActive) { |
1624 | 0 | mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT; |
1625 | 0 | } else if (hasMixedActive){ |
1626 | 0 | mixedContentLevel = MIXED_ACTIVE_CONTENT; |
1627 | 0 | } else if (hasMixedDisplay) { |
1628 | 0 | mixedContentLevel = MIXED_DISPLAY_CONTENT; |
1629 | 0 | } |
1630 | 0 | Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel); |
1631 | 0 |
|
1632 | 0 | // record mixed object subrequest telemetry |
1633 | 0 | if (mHasMixedContentObjectSubrequest) { |
1634 | 0 | /* mixed object subrequest loaded on page*/ |
1635 | 0 | Accumulate(Telemetry::MIXED_CONTENT_OBJECT_SUBREQUEST, 1); |
1636 | 0 | } else { |
1637 | 0 | /* no mixed object subrequests loaded on page*/ |
1638 | 0 | Accumulate(Telemetry::MIXED_CONTENT_OBJECT_SUBREQUEST, 0); |
1639 | 0 | } |
1640 | 0 |
|
1641 | 0 | // record CSP telemetry on this document |
1642 | 0 | if (mHasCSP) { |
1643 | 0 | Accumulate(Telemetry::CSP_DOCUMENTS_COUNT, 1); |
1644 | 0 | } |
1645 | 0 | if (mHasUnsafeInlineCSP) { |
1646 | 0 | Accumulate(Telemetry::CSP_UNSAFE_INLINE_DOCUMENTS_COUNT, 1); |
1647 | 0 | } |
1648 | 0 | if (mHasUnsafeEvalCSP) { |
1649 | 0 | Accumulate(Telemetry::CSP_UNSAFE_EVAL_DOCUMENTS_COUNT, 1); |
1650 | 0 | } |
1651 | 0 |
|
1652 | 0 | if (MOZ_UNLIKELY(mMathMLEnabled)) { |
1653 | 0 | ScalarAdd(Telemetry::ScalarID::MATHML_DOC_COUNT, 1); |
1654 | 0 | } |
1655 | 0 |
|
1656 | 0 | ScalarAdd(Telemetry::ScalarID::MEDIA_PAGE_COUNT, 1); |
1657 | 0 | if (mDocTreeHadAudibleMedia) { |
1658 | 0 | ScalarAdd(Telemetry::ScalarID::MEDIA_PAGE_HAD_MEDIA_COUNT, 1); |
1659 | 0 | } |
1660 | 0 | if (mDocTreeHadPlayRevoked) { |
1661 | 0 | ScalarAdd(Telemetry::ScalarID::MEDIA_PAGE_HAD_PLAY_REVOKED_COUNT, 1); |
1662 | 0 | } |
1663 | 0 | } |
1664 | 0 |
|
1665 | 0 | // Report the fastblock telemetry probes when the document is dying if |
1666 | 0 | // fastblock is enabled and we're not a private document. We always report |
1667 | 0 | // the all probe, and for the rest, report each category's probe depending |
1668 | 0 | // on whether the respective bit has been set in our enum set. |
1669 | 0 | if (StaticPrefs::browser_contentblocking_enabled() && |
1670 | 0 | StaticPrefs::browser_fastblock_enabled() && |
1671 | 0 | !nsContentUtils::IsInPrivateBrowsing(this)) { |
1672 | 0 | for (auto label : mTrackerBlockedReasons) { |
1673 | 0 | AccumulateCategorical(label); |
1674 | 0 | } |
1675 | 0 | // Always accumulate the "all" probe since we will use it as a baseline counter. |
1676 | 0 | AccumulateCategorical(Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all); |
1677 | 0 | } |
1678 | 0 | } |
1679 | 0 |
|
1680 | 0 | ReportUseCounters(); |
1681 | 0 |
|
1682 | 0 | mInDestructor = true; |
1683 | 0 | mInUnlinkOrDeletion = true; |
1684 | 0 |
|
1685 | 0 | mozilla::DropJSObjects(this); |
1686 | 0 |
|
1687 | 0 | // Clear mObservers to keep it in sync with the mutationobserver list |
1688 | 0 | mObservers.Clear(); |
1689 | 0 |
|
1690 | 0 | mIntersectionObservers.Clear(); |
1691 | 0 |
|
1692 | 0 | if (mStyleSheetSetList) { |
1693 | 0 | mStyleSheetSetList->Disconnect(); |
1694 | 0 | } |
1695 | 0 |
|
1696 | 0 | if (mAnimationController) { |
1697 | 0 | mAnimationController->Disconnect(); |
1698 | 0 | } |
1699 | 0 |
|
1700 | 0 | MOZ_ASSERT(mTimelines.isEmpty()); |
1701 | 0 |
|
1702 | 0 | mParentDocument = nullptr; |
1703 | 0 |
|
1704 | 0 | // Kill the subdocument map, doing this will release its strong |
1705 | 0 | // references, if any. |
1706 | 0 | delete mSubDocuments; |
1707 | 0 | mSubDocuments = nullptr; |
1708 | 0 |
|
1709 | 0 | // Destroy link map now so we don't waste time removing |
1710 | 0 | // links one by one |
1711 | 0 | DestroyElementMaps(); |
1712 | 0 |
|
1713 | 0 | nsAutoScriptBlocker scriptBlocker; |
1714 | 0 |
|
1715 | 0 | // Invalidate cached array of child nodes |
1716 | 0 | InvalidateChildNodes(); |
1717 | 0 |
|
1718 | 0 | // We should not have child nodes when destructor is called, |
1719 | 0 | // since child nodes keep their owner document alive. |
1720 | 0 | MOZ_ASSERT(!HasChildren()); |
1721 | 0 |
|
1722 | 0 | mCachedRootElement = nullptr; |
1723 | 0 |
|
1724 | 0 | for (auto& sheets : mAdditionalSheets) { |
1725 | 0 | for (StyleSheet* sheet : sheets) { |
1726 | 0 | sheet->ClearAssociatedDocumentOrShadowRoot(); |
1727 | 0 | } |
1728 | 0 | } |
1729 | 0 |
|
1730 | 0 | if (mAttrStyleSheet) { |
1731 | 0 | mAttrStyleSheet->SetOwningDocument(nullptr); |
1732 | 0 | } |
1733 | 0 |
|
1734 | 0 | if (mListenerManager) { |
1735 | 0 | mListenerManager->Disconnect(); |
1736 | 0 | UnsetFlags(NODE_HAS_LISTENERMANAGER); |
1737 | 0 | } |
1738 | 0 |
|
1739 | 0 | if (mScriptLoader) { |
1740 | 0 | mScriptLoader->DropDocumentReference(); |
1741 | 0 | } |
1742 | 0 |
|
1743 | 0 | if (mCSSLoader) { |
1744 | 0 | // Could be null here if Init() failed or if we have been unlinked. |
1745 | 0 | mCSSLoader->DropDocumentReference(); |
1746 | 0 | } |
1747 | 0 |
|
1748 | 0 | if (mStyleImageLoader) { |
1749 | 0 | mStyleImageLoader->DropDocumentReference(); |
1750 | 0 | } |
1751 | 0 |
|
1752 | 0 | delete mHeaderData; |
1753 | 0 |
|
1754 | 0 | ClearAllBoxObjects(); |
1755 | 0 |
|
1756 | 0 | mPendingTitleChangeEvent.Revoke(); |
1757 | 0 |
|
1758 | 0 | mPlugins.Clear(); |
1759 | 0 | } |
1760 | | |
1761 | 0 | NS_INTERFACE_TABLE_HEAD(nsDocument) |
1762 | 0 | NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY |
1763 | 0 | NS_INTERFACE_TABLE_BEGIN |
1764 | 0 | NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode) |
1765 | 0 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode) |
1766 | 0 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument) |
1767 | 0 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal) |
1768 | 0 | NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget) |
1769 | 0 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference) |
1770 | 0 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer) |
1771 | 0 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver) |
1772 | 0 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer) |
1773 | 0 | NS_INTERFACE_TABLE_END |
1774 | 0 | NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument) |
1775 | 0 | NS_INTERFACE_MAP_END |
1776 | | |
1777 | | |
1778 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument) |
1779 | | NS_IMETHODIMP_(MozExternalRefCountType) |
1780 | | nsDocument::Release() |
1781 | 0 | { |
1782 | 0 | MOZ_ASSERT(0 != mRefCnt, "dup release"); |
1783 | 0 | NS_ASSERT_OWNINGTHREAD(nsDocument); |
1784 | 0 | nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this); |
1785 | 0 | bool shouldDelete = false; |
1786 | 0 | nsrefcnt count = mRefCnt.decr(base, &shouldDelete); |
1787 | 0 | NS_LOG_RELEASE(this, count, "nsDocument"); |
1788 | 0 | if (count == 0) { |
1789 | 0 | if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) { |
1790 | 0 | mNeedsReleaseAfterStackRefCntRelease = true; |
1791 | 0 | NS_ADDREF_THIS(); |
1792 | 0 | return mRefCnt.get(); |
1793 | 0 | } |
1794 | 0 | mRefCnt.incr(base); |
1795 | 0 | nsNodeUtils::LastRelease(this); |
1796 | 0 | mRefCnt.decr(base); |
1797 | 0 | if (shouldDelete) { |
1798 | 0 | mRefCnt.stabilizeForDeletion(); |
1799 | 0 | DeleteCycleCollectable(); |
1800 | 0 | } |
1801 | 0 | } |
1802 | 0 | return count; |
1803 | 0 | } |
1804 | | |
1805 | | NS_IMETHODIMP_(void) |
1806 | | nsDocument::DeleteCycleCollectable() |
1807 | 0 | { |
1808 | 0 | delete this; |
1809 | 0 | } |
1810 | | |
1811 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument) |
1812 | 0 | if (Element::CanSkip(tmp, aRemovingAllowed)) { |
1813 | 0 | EventListenerManager* elm = tmp->GetExistingListenerManager(); |
1814 | 0 | if (elm) { |
1815 | 0 | elm->MarkForCC(); |
1816 | 0 | } |
1817 | 0 | return true; |
1818 | 0 | } |
1819 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END |
1820 | 0 |
|
1821 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument) |
1822 | 0 | return Element::CanSkipInCC(tmp); |
1823 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END |
1824 | | |
1825 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument) |
1826 | 0 | return Element::CanSkipThis(tmp); |
1827 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END |
1828 | | |
1829 | | static const char* kNSURIs[] = { |
1830 | | "([none])", |
1831 | | "(xmlns)", |
1832 | | "(xml)", |
1833 | | "(xhtml)", |
1834 | | "(XLink)", |
1835 | | "(XSLT)", |
1836 | | "(XBL)", |
1837 | | "(MathML)", |
1838 | | "(RDF)", |
1839 | | "(XUL)" |
1840 | | }; |
1841 | | |
1842 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument) |
1843 | 0 | if (MOZ_UNLIKELY(cb.WantDebugInfo())) { |
1844 | 0 | char name[512]; |
1845 | 0 | nsAutoCString loadedAsData; |
1846 | 0 | if (tmp->IsLoadedAsData()) { |
1847 | 0 | loadedAsData.AssignLiteral("data"); |
1848 | 0 | } else { |
1849 | 0 | loadedAsData.AssignLiteral("normal"); |
1850 | 0 | } |
1851 | 0 | uint32_t nsid = tmp->GetDefaultNamespaceID(); |
1852 | 0 | nsAutoCString uri; |
1853 | 0 | if (tmp->mDocumentURI) |
1854 | 0 | uri = tmp->mDocumentURI->GetSpecOrDefault(); |
1855 | 0 | if (nsid < ArrayLength(kNSURIs)) { |
1856 | 0 | SprintfLiteral(name, "nsDocument %s %s %s", |
1857 | 0 | loadedAsData.get(), kNSURIs[nsid], uri.get()); |
1858 | 0 | } |
1859 | 0 | else { |
1860 | 0 | SprintfLiteral(name, "nsDocument %s %s", |
1861 | 0 | loadedAsData.get(), uri.get()); |
1862 | 0 | } |
1863 | 0 | cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); |
1864 | 0 | } |
1865 | 0 | else { |
1866 | 0 | NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get()) |
1867 | 0 | } |
1868 | 0 |
|
1869 | 0 | if (!nsINode::Traverse(tmp, cb)) { |
1870 | 0 | return NS_SUCCESS_INTERRUPTED_TRAVERSE; |
1871 | 0 | } |
1872 | 0 | |
1873 | 0 | if (tmp->mMaybeEndOutermostXBLUpdateRunner) { |
1874 | 0 | // The cached runnable keeps a reference to the document object.. |
1875 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, |
1876 | 0 | "mMaybeEndOutermostXBLUpdateRunner.mObj"); |
1877 | 0 | cb.NoteXPCOMChild(ToSupports(tmp)); |
1878 | 0 | } |
1879 | 0 |
|
1880 | 0 | for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done(); |
1881 | 0 | iter.Next()) { |
1882 | 0 | iter.Get()->Traverse(&cb); |
1883 | 0 | } |
1884 | 0 |
|
1885 | 0 | tmp->mExternalResourceMap.Traverse(&cb); |
1886 | 0 |
|
1887 | 0 | // Traverse all nsIDocument pointer members. |
1888 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo) |
1889 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument) |
1890 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet) |
1891 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle) |
1892 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n) |
1893 | 0 |
|
1894 | 0 | // Traverse all nsDocument nsCOMPtrs. |
1895 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser) |
1896 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject) |
1897 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) |
1898 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets) |
1899 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList) |
1900 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader) |
1901 | 0 |
|
1902 | 0 | DocumentOrShadowRoot::Traverse(tmp, cb); |
1903 | 0 |
|
1904 | 0 | // The boxobject for an element will only exist as long as it's in the |
1905 | 0 | // document, so we'll traverse the table here instead of from the element. |
1906 | 0 | if (tmp->mBoxObjectTable) { |
1907 | 0 | for (auto iter = tmp->mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) { |
1908 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mBoxObjectTable entry"); |
1909 | 0 | cb.NoteXPCOMChild(iter.UserData()); |
1910 | 0 | } |
1911 | 0 | } |
1912 | 0 |
|
1913 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel) |
1914 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState) |
1915 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker) |
1916 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref) |
1917 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation) |
1918 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps) |
1919 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise) |
1920 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument) |
1921 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder) |
1922 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached) |
1923 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline) |
1924 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker) |
1925 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner) |
1926 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection) |
1927 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages); |
1928 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds); |
1929 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks); |
1930 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms); |
1931 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts); |
1932 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets); |
1933 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors); |
1934 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents) |
1935 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher) |
1936 | 0 |
|
1937 | 0 | // Traverse all our nsCOMArrays. |
1938 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets) |
1939 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages) |
1940 | 0 |
|
1941 | 0 | for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) { |
1942 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]"); |
1943 | 0 | cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback); |
1944 | 0 | } |
1945 | 0 |
|
1946 | 0 | // Traverse animation components |
1947 | 0 | if (tmp->mAnimationController) { |
1948 | 0 | tmp->mAnimationController->Traverse(&cb); |
1949 | 0 | } |
1950 | 0 |
|
1951 | 0 | if (tmp->mSubDocuments) { |
1952 | 0 | for (auto iter = tmp->mSubDocuments->Iter(); !iter.Done(); iter.Next()) { |
1953 | 0 | auto entry = static_cast<SubDocMapEntry*>(iter.Get()); |
1954 | 0 |
|
1955 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, |
1956 | 0 | "mSubDocuments entry->mKey"); |
1957 | 0 | cb.NoteXPCOMChild(entry->mKey); |
1958 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, |
1959 | 0 | "mSubDocuments entry->mSubDocument"); |
1960 | 0 | cb.NoteXPCOMChild(entry->mSubDocument); |
1961 | 0 | } |
1962 | 0 | } |
1963 | 0 |
|
1964 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader) |
1965 | 0 |
|
1966 | 0 | // We own only the items in mDOMMediaQueryLists that have listeners; |
1967 | 0 | // this reference is managed by their AddListener and RemoveListener |
1968 | 0 | // methods. |
1969 | 0 | for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql; |
1970 | 0 | mql = static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext()) { |
1971 | 0 | if (mql->HasListeners() && |
1972 | 0 | NS_SUCCEEDED(mql->CheckInnerWindowCorrectness())) { |
1973 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item"); |
1974 | 0 | cb.NoteXPCOMChild(mql); |
1975 | 0 | } |
1976 | 0 | } |
1977 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
1978 | | |
1979 | | NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument) |
1980 | | |
1981 | | NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDocument) |
1982 | | |
1983 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) |
1984 | 0 | tmp->mInUnlinkOrDeletion = true; |
1985 | 0 |
|
1986 | 0 | // Clear out our external resources |
1987 | 0 | tmp->mExternalResourceMap.Shutdown(); |
1988 | 0 |
|
1989 | 0 | nsAutoScriptBlocker scriptBlocker; |
1990 | 0 |
|
1991 | 0 | nsINode::Unlink(tmp); |
1992 | 0 |
|
1993 | 0 | while (tmp->HasChildren()) { |
1994 | 0 | // Hold a strong ref to the node when we remove it, because we may be |
1995 | 0 | // the last reference to it. |
1996 | 0 | // If this code changes, change the corresponding code in nsDocument's |
1997 | 0 | // unlink impl and ContentUnbinder::UnbindSubtree. |
1998 | 0 | nsCOMPtr<nsIContent> child = tmp->GetLastChild(); |
1999 | 0 | tmp->DisconnectChild(child); |
2000 | 0 | child->UnbindFromTree(); |
2001 | 0 | } |
2002 | 0 |
|
2003 | 0 | tmp->UnlinkOriginalDocumentIfStatic(); |
2004 | 0 |
|
2005 | 0 | tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer |
2006 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument) |
2007 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref) |
2008 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaybeEndOutermostXBLUpdateRunner) |
2009 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation) |
2010 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps) |
2011 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder) |
2012 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline) |
2013 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker) |
2014 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner) |
2015 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection) |
2016 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages); |
2017 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mEmbeds); |
2018 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks); |
2019 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms); |
2020 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts); |
2021 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets); |
2022 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors); |
2023 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise) |
2024 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet) |
2025 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle); |
2026 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher) |
2027 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n); |
2028 | 0 |
|
2029 | 0 | tmp->mParentDocument = nullptr; |
2030 | 0 |
|
2031 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages) |
2032 | 0 |
|
2033 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers) |
2034 | 0 |
|
2035 | 0 | tmp->ClearAllBoxObjects(); |
2036 | 0 |
|
2037 | 0 | if (tmp->mListenerManager) { |
2038 | 0 | tmp->mListenerManager->Disconnect(); |
2039 | 0 | tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER); |
2040 | 0 | tmp->mListenerManager = nullptr; |
2041 | 0 | } |
2042 | 0 |
|
2043 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets) |
2044 | 0 |
|
2045 | 0 | if (tmp->mStyleSheetSetList) { |
2046 | 0 | tmp->mStyleSheetSetList->Disconnect(); |
2047 | 0 | tmp->mStyleSheetSetList = nullptr; |
2048 | 0 | } |
2049 | 0 |
|
2050 | 0 | delete tmp->mSubDocuments; |
2051 | 0 | tmp->mSubDocuments = nullptr; |
2052 | 0 |
|
2053 | 0 | tmp->mFrameRequestCallbacks.Clear(); |
2054 | 0 | MOZ_RELEASE_ASSERT(!tmp->mFrameRequestCallbacksScheduled, |
2055 | 0 | "How did we get here without our presshell going away " |
2056 | 0 | "first?"); |
2057 | 0 |
|
2058 | 0 | DocumentOrShadowRoot::Unlink(tmp); |
2059 | 0 |
|
2060 | 0 | // nsDocument has a pretty complex destructor, so we're going to |
2061 | 0 | // assume that *most* cycles you actually want to break somewhere |
2062 | 0 | // else, and not unlink an awful lot here. |
2063 | 0 |
|
2064 | 0 | tmp->mIdentifierMap.Clear(); |
2065 | 0 | tmp->mExpandoAndGeneration.OwnerUnlinked(); |
2066 | 0 |
|
2067 | 0 | if (tmp->mAnimationController) { |
2068 | 0 | tmp->mAnimationController->Unlink(); |
2069 | 0 | } |
2070 | 0 |
|
2071 | 0 | tmp->mPendingTitleChangeEvent.Revoke(); |
2072 | 0 |
|
2073 | 0 | if (tmp->mCSSLoader) { |
2074 | 0 | tmp->mCSSLoader->DropDocumentReference(); |
2075 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader) |
2076 | 0 | } |
2077 | 0 |
|
2078 | 0 | // We own only the items in mDOMMediaQueryLists that have listeners; |
2079 | 0 | // this reference is managed by their AddListener and RemoveListener |
2080 | 0 | // methods. |
2081 | 0 | for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;) { |
2082 | 0 | MediaQueryList* next = |
2083 | 0 | static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext(); |
2084 | 0 | mql->Disconnect(); |
2085 | 0 | mql = next; |
2086 | 0 | } |
2087 | 0 |
|
2088 | 0 | tmp->mInUnlinkOrDeletion = false; |
2089 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
2090 | | |
2091 | | nsresult |
2092 | | nsDocument::Init() |
2093 | 0 | { |
2094 | 0 | if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) { |
2095 | 0 | return NS_ERROR_ALREADY_INITIALIZED; |
2096 | 0 | } |
2097 | 0 | |
2098 | 0 | // Force initialization. |
2099 | 0 | nsINode::nsSlots* slots = Slots(); |
2100 | 0 |
|
2101 | 0 | // Prepend self as mutation-observer whether we need it or not (some |
2102 | 0 | // subclasses currently do, other don't). This is because the code in |
2103 | 0 | // nsNodeUtils always notifies the first observer first, expecting the |
2104 | 0 | // first observer to be the document. |
2105 | 0 | NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)), |
2106 | 0 | NS_ERROR_OUT_OF_MEMORY); |
2107 | 0 |
|
2108 | 0 |
|
2109 | 0 | mOnloadBlocker = new nsOnloadBlocker(); |
2110 | 0 | mCSSLoader = new mozilla::css::Loader(this); |
2111 | 0 | // Assume we're not quirky, until we know otherwise |
2112 | 0 | mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards); |
2113 | 0 |
|
2114 | 0 | mStyleImageLoader = new mozilla::css::ImageLoader(this); |
2115 | 0 |
|
2116 | 0 | mNodeInfoManager = new nsNodeInfoManager(); |
2117 | 0 | nsresult rv = mNodeInfoManager->Init(this); |
2118 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2119 | 0 |
|
2120 | 0 | // mNodeInfo keeps NodeInfoManager alive! |
2121 | 0 | mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo(); |
2122 | 0 | NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY); |
2123 | 0 | MOZ_ASSERT(mNodeInfo->NodeType() == DOCUMENT_NODE, |
2124 | 0 | "Bad NodeType in aNodeInfo"); |
2125 | 0 |
|
2126 | 0 | NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!"); |
2127 | 0 |
|
2128 | 0 | // Set this when document is initialized and value stays the same for the |
2129 | 0 | // lifetime of the document. |
2130 | 0 | mIsShadowDOMEnabled = nsContentUtils::IsShadowDOMEnabled() || |
2131 | 0 | (XRE_IsParentProcess() && AllowXULXBL()); |
2132 | 0 |
|
2133 | 0 | // If after creation the owner js global is not set for a document |
2134 | 0 | // we use the default compartment for this document, instead of creating |
2135 | 0 | // wrapper in some random compartment when the document is exposed to js |
2136 | 0 | // via some events. |
2137 | 0 | nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(xpc::PrivilegedJunkScope()); |
2138 | 0 | NS_ENSURE_TRUE(global, NS_ERROR_FAILURE); |
2139 | 0 | mScopeObject = do_GetWeakReference(global); |
2140 | 0 | MOZ_ASSERT(mScopeObject); |
2141 | 0 |
|
2142 | 0 | mScriptLoader = new dom::ScriptLoader(this); |
2143 | 0 |
|
2144 | 0 | mozilla::HoldJSObjects(this); |
2145 | 0 |
|
2146 | 0 | return NS_OK; |
2147 | 0 | } |
2148 | | |
2149 | | void |
2150 | | nsIDocument::DeleteAllProperties() |
2151 | 0 | { |
2152 | 0 | PropertyTable().DeleteAllProperties(); |
2153 | 0 | } |
2154 | | |
2155 | | void |
2156 | | nsIDocument::DeleteAllPropertiesFor(nsINode* aNode) |
2157 | 0 | { |
2158 | 0 | PropertyTable().DeleteAllPropertiesFor(aNode); |
2159 | 0 | } |
2160 | | |
2161 | | bool |
2162 | | nsIDocument::IsVisibleConsideringAncestors() const |
2163 | 0 | { |
2164 | 0 | const nsIDocument *parent = this; |
2165 | 0 | do { |
2166 | 0 | if (!parent->IsVisible()) { |
2167 | 0 | return false; |
2168 | 0 | } |
2169 | 0 | } while ((parent = parent->GetParentDocument())); |
2170 | 0 |
|
2171 | 0 | return true; |
2172 | 0 | } |
2173 | | |
2174 | | void |
2175 | | nsIDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) |
2176 | 0 | { |
2177 | 0 | nsCOMPtr<nsIURI> uri; |
2178 | 0 | nsCOMPtr<nsIPrincipal> principal; |
2179 | 0 | if (aChannel) { |
2180 | 0 | // Note: this code is duplicated in XULDocument::StartDocumentLoad and |
2181 | 0 | // nsScriptSecurityManager::GetChannelResultPrincipal. |
2182 | 0 | // Note: this should match nsDocShell::OnLoadingSite |
2183 | 0 | NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); |
2184 | 0 |
|
2185 | 0 | bool isWyciwyg = false; |
2186 | 0 | uri->SchemeIs("wyciwyg", &isWyciwyg); |
2187 | 0 | if (isWyciwyg) { |
2188 | 0 | nsCOMPtr<nsIURI> cleanURI; |
2189 | 0 | nsresult rv = |
2190 | 0 | nsContentUtils::RemoveWyciwygScheme(uri, getter_AddRefs(cleanURI)); |
2191 | 0 | if (NS_SUCCEEDED(rv)) { |
2192 | 0 | uri = cleanURI; |
2193 | 0 | } |
2194 | 0 | } |
2195 | 0 |
|
2196 | 0 | nsIScriptSecurityManager *securityManager = |
2197 | 0 | nsContentUtils::GetSecurityManager(); |
2198 | 0 | if (securityManager) { |
2199 | 0 | securityManager->GetChannelResultPrincipal(aChannel, |
2200 | 0 | getter_AddRefs(principal)); |
2201 | 0 | } |
2202 | 0 | } |
2203 | 0 |
|
2204 | 0 | principal = MaybeDowngradePrincipal(principal); |
2205 | 0 |
|
2206 | 0 | ResetToURI(uri, aLoadGroup, principal); |
2207 | 0 |
|
2208 | 0 | // Note that, since mTiming does not change during a reset, the |
2209 | 0 | // navigationStart time remains unchanged and therefore any future new |
2210 | 0 | // timeline will have the same global clock time as the old one. |
2211 | 0 | mDocumentTimeline = nullptr; |
2212 | 0 |
|
2213 | 0 | nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel); |
2214 | 0 | if (bag) { |
2215 | 0 | nsCOMPtr<nsIURI> baseURI; |
2216 | 0 | bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"), |
2217 | 0 | NS_GET_IID(nsIURI), getter_AddRefs(baseURI)); |
2218 | 0 | if (baseURI) { |
2219 | 0 | mDocumentBaseURI = baseURI; |
2220 | 0 | mChromeXHRDocBaseURI = nullptr; |
2221 | 0 | } |
2222 | 0 | } |
2223 | 0 |
|
2224 | 0 | mChannel = aChannel; |
2225 | 0 | } |
2226 | | |
2227 | | /** |
2228 | | * DocumentL10n is currently allowed for system |
2229 | | * principal. |
2230 | | * |
2231 | | * In the future we'll want to expose it to non-web-exposed |
2232 | | * about:* pages. |
2233 | | */ |
2234 | | bool |
2235 | | PrincipalAllowsL10n(nsIPrincipal* principal) |
2236 | 0 | { |
2237 | 0 | if (nsContentUtils::IsSystemPrincipal(principal)) { |
2238 | 0 | return true; |
2239 | 0 | } |
2240 | 0 | |
2241 | 0 | return false; |
2242 | 0 | } |
2243 | | |
2244 | | void |
2245 | | nsIDocument::ResetToURI(nsIURI* aURI, |
2246 | | nsILoadGroup* aLoadGroup, |
2247 | | nsIPrincipal* aPrincipal) |
2248 | 0 | { |
2249 | 0 | MOZ_ASSERT(aURI, "Null URI passed to ResetToURI"); |
2250 | 0 |
|
2251 | 0 | MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, |
2252 | 0 | ("DOCUMENT %p ResetToURI %s", this, aURI->GetSpecOrDefault().get())); |
2253 | 0 |
|
2254 | 0 | mSecurityInfo = nullptr; |
2255 | 0 |
|
2256 | 0 | nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup); |
2257 | 0 | if (!aLoadGroup || group != aLoadGroup) { |
2258 | 0 | mDocumentLoadGroup = nullptr; |
2259 | 0 | } |
2260 | 0 |
|
2261 | 0 | // Delete references to sub-documents and kill the subdocument map, |
2262 | 0 | // if any. It holds strong references |
2263 | 0 | delete mSubDocuments; |
2264 | 0 | mSubDocuments = nullptr; |
2265 | 0 |
|
2266 | 0 | // Destroy link map now so we don't waste time removing |
2267 | 0 | // links one by one |
2268 | 0 | DestroyElementMaps(); |
2269 | 0 |
|
2270 | 0 | bool oldVal = mInUnlinkOrDeletion; |
2271 | 0 | mInUnlinkOrDeletion = true; |
2272 | 0 | { // Scope for update |
2273 | 0 | MOZ_AUTO_DOC_UPDATE(this, true); |
2274 | 0 |
|
2275 | 0 | // Invalidate cached array of child nodes |
2276 | 0 | InvalidateChildNodes(); |
2277 | 0 |
|
2278 | 0 | while (HasChildren()) { |
2279 | 0 | nsCOMPtr<nsIContent> content = GetLastChild(); |
2280 | 0 | nsIContent* previousSibling = content->GetPreviousSibling(); |
2281 | 0 | DisconnectChild(content); |
2282 | 0 | if (content == mCachedRootElement) { |
2283 | 0 | // Immediately clear mCachedRootElement, now that it's been removed |
2284 | 0 | // from mChildren, so that GetRootElement() will stop returning this |
2285 | 0 | // now-stale value. |
2286 | 0 | mCachedRootElement = nullptr; |
2287 | 0 | } |
2288 | 0 | nsNodeUtils::ContentRemoved(this, content, previousSibling); |
2289 | 0 | content->UnbindFromTree(); |
2290 | 0 | } |
2291 | 0 | MOZ_ASSERT(!mCachedRootElement, |
2292 | 0 | "After removing all children, there should be no root elem"); |
2293 | 0 | } |
2294 | 0 | mInUnlinkOrDeletion = oldVal; |
2295 | 0 |
|
2296 | 0 | // Reset our stylesheets |
2297 | 0 | ResetStylesheetsToURI(aURI); |
2298 | 0 |
|
2299 | 0 | // Release the listener manager |
2300 | 0 | if (mListenerManager) { |
2301 | 0 | mListenerManager->Disconnect(); |
2302 | 0 | mListenerManager = nullptr; |
2303 | 0 | } |
2304 | 0 |
|
2305 | 0 | // Release the stylesheets list. |
2306 | 0 | mDOMStyleSheets = nullptr; |
2307 | 0 |
|
2308 | 0 | // Release our principal after tearing down the document, rather than before. |
2309 | 0 | // This ensures that, during teardown, the document and the dying window (which |
2310 | 0 | // already nulled out its document pointer and cached the principal) have |
2311 | 0 | // matching principals. |
2312 | 0 | SetPrincipal(nullptr); |
2313 | 0 |
|
2314 | 0 | // Clear the original URI so SetDocumentURI sets it. |
2315 | 0 | mOriginalURI = nullptr; |
2316 | 0 |
|
2317 | 0 | SetDocumentURI(aURI); |
2318 | 0 | mChromeXHRDocURI = nullptr; |
2319 | 0 | // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns |
2320 | 0 | // mDocumentURI. |
2321 | 0 | mDocumentBaseURI = nullptr; |
2322 | 0 | mChromeXHRDocBaseURI = nullptr; |
2323 | 0 |
|
2324 | 0 | if (aLoadGroup) { |
2325 | 0 | mDocumentLoadGroup = do_GetWeakReference(aLoadGroup); |
2326 | 0 | // there was an assertion here that aLoadGroup was not null. This |
2327 | 0 | // is no longer valid: nsDocShell::SetDocument does not create a |
2328 | 0 | // load group, and it works just fine |
2329 | 0 |
|
2330 | 0 | // XXXbz what does "just fine" mean exactly? And given that there |
2331 | 0 | // is no nsDocShell::SetDocument, what is this talking about? |
2332 | 0 |
|
2333 | 0 | if (IsContentDocument()) { |
2334 | 0 | // Inform the associated request context about this load start so |
2335 | 0 | // any of its internal load progress flags gets reset. |
2336 | 0 | nsCOMPtr<nsIRequestContextService> rcsvc = |
2337 | 0 | do_GetService("@mozilla.org/network/request-context-service;1"); |
2338 | 0 | if (rcsvc) { |
2339 | 0 | nsCOMPtr<nsIRequestContext> rc; |
2340 | 0 | rcsvc->GetRequestContextFromLoadGroup(aLoadGroup, getter_AddRefs(rc)); |
2341 | 0 | if (rc) { |
2342 | 0 | rc->BeginLoad(); |
2343 | 0 | } |
2344 | 0 | } |
2345 | 0 | } |
2346 | 0 | } |
2347 | 0 |
|
2348 | 0 | mLastModified.Truncate(); |
2349 | 0 | // XXXbz I guess we're assuming that the caller will either pass in |
2350 | 0 | // a channel with a useful type or call SetContentType? |
2351 | 0 | SetContentTypeInternal(EmptyCString()); |
2352 | 0 | mContentLanguage.Truncate(); |
2353 | 0 | mBaseTarget.Truncate(); |
2354 | 0 | mReferrer.Truncate(); |
2355 | 0 |
|
2356 | 0 | mXMLDeclarationBits = 0; |
2357 | 0 |
|
2358 | 0 | // Now get our new principal |
2359 | 0 | if (aPrincipal) { |
2360 | 0 | SetPrincipal(aPrincipal); |
2361 | 0 | } else { |
2362 | 0 | nsIScriptSecurityManager *securityManager = |
2363 | 0 | nsContentUtils::GetSecurityManager(); |
2364 | 0 | if (securityManager) { |
2365 | 0 | nsCOMPtr<nsILoadContext> loadContext(mDocumentContainer); |
2366 | 0 |
|
2367 | 0 | if (!loadContext && aLoadGroup) { |
2368 | 0 | nsCOMPtr<nsIInterfaceRequestor> cbs; |
2369 | 0 | aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs)); |
2370 | 0 | loadContext = do_GetInterface(cbs); |
2371 | 0 | } |
2372 | 0 |
|
2373 | 0 | MOZ_ASSERT(loadContext, |
2374 | 0 | "must have a load context or pass in an explicit principal"); |
2375 | 0 |
|
2376 | 0 | nsCOMPtr<nsIPrincipal> principal; |
2377 | 0 | nsresult rv = securityManager-> |
2378 | 0 | GetLoadContextCodebasePrincipal(mDocumentURI, loadContext, |
2379 | 0 | getter_AddRefs(principal)); |
2380 | 0 | if (NS_SUCCEEDED(rv)) { |
2381 | 0 | SetPrincipal(principal); |
2382 | 0 | } |
2383 | 0 | } |
2384 | 0 | } |
2385 | 0 |
|
2386 | 0 | if (mFontFaceSet) { |
2387 | 0 | mFontFaceSet->RefreshStandardFontLoadPrincipal(); |
2388 | 0 | } |
2389 | 0 |
|
2390 | 0 | // Refresh the principal on the realm. |
2391 | 0 | if (nsPIDOMWindowInner* win = GetInnerWindow()) { |
2392 | 0 | nsGlobalWindowInner::Cast(win)->RefreshRealmPrincipal(); |
2393 | 0 | } |
2394 | 0 | } |
2395 | | |
2396 | | already_AddRefed<nsIPrincipal> |
2397 | | nsIDocument::MaybeDowngradePrincipal(nsIPrincipal* aPrincipal) |
2398 | 0 | { |
2399 | 0 | if (!aPrincipal) { |
2400 | 0 | return nullptr; |
2401 | 0 | } |
2402 | 0 | |
2403 | 0 | // We can't load a document with an expanded principal. If we're given one, |
2404 | 0 | // automatically downgrade it to the last principal it subsumes (which is the |
2405 | 0 | // extension principal, in the case of extension content scripts). |
2406 | 0 | auto* basePrin = BasePrincipal::Cast(aPrincipal); |
2407 | 0 | if (basePrin->Is<ExpandedPrincipal>()) { |
2408 | 0 | MOZ_DIAGNOSTIC_ASSERT(false, "Should never try to create a document with " |
2409 | 0 | "an expanded principal"); |
2410 | 0 |
|
2411 | 0 | auto* expanded = basePrin->As<ExpandedPrincipal>(); |
2412 | 0 | return do_AddRef(expanded->WhiteList().LastElement()); |
2413 | 0 | } |
2414 | 0 | |
2415 | 0 | if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
2416 | 0 | // We basically want the parent document here, but because this is very |
2417 | 0 | // early in the load, GetParentDocument() returns null, so we use the |
2418 | 0 | // docshell hierarchy to get this information instead. |
2419 | 0 | if (mDocumentContainer) { |
2420 | 0 | nsCOMPtr<nsIDocShellTreeItem> parentDocShellItem; |
2421 | 0 | mDocumentContainer->GetParent(getter_AddRefs(parentDocShellItem)); |
2422 | 0 | nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentDocShellItem); |
2423 | 0 | if (parentDocShell) { |
2424 | 0 | nsCOMPtr<nsIDocument> parentDoc; |
2425 | 0 | parentDoc = parentDocShell->GetDocument(); |
2426 | 0 | if (!parentDoc || |
2427 | 0 | !nsContentUtils::IsSystemPrincipal(parentDoc->NodePrincipal())) { |
2428 | 0 | nsCOMPtr<nsIPrincipal> nullPrincipal = |
2429 | 0 | do_CreateInstance("@mozilla.org/nullprincipal;1"); |
2430 | 0 | return nullPrincipal.forget(); |
2431 | 0 | } |
2432 | 0 | } |
2433 | 0 | } |
2434 | 0 | } |
2435 | 0 | nsCOMPtr<nsIPrincipal> principal(aPrincipal); |
2436 | 0 | return principal.forget(); |
2437 | 0 | } |
2438 | | |
2439 | | void |
2440 | | nsIDocument::RemoveDocStyleSheetsFromStyleSets() |
2441 | 0 | { |
2442 | 0 | // The stylesheets should forget us |
2443 | 0 | for (StyleSheet* sheet : Reversed(mStyleSheets)) { |
2444 | 0 | sheet->ClearAssociatedDocumentOrShadowRoot(); |
2445 | 0 |
|
2446 | 0 | if (sheet->IsApplicable()) { |
2447 | 0 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
2448 | 0 | if (shell) { |
2449 | 0 | shell->StyleSet()->RemoveDocStyleSheet(sheet); |
2450 | 0 | } |
2451 | 0 | } |
2452 | 0 | // XXX Tell observers? |
2453 | 0 | } |
2454 | 0 | } |
2455 | | |
2456 | | void |
2457 | | nsIDocument::RemoveStyleSheetsFromStyleSets( |
2458 | | const nsTArray<RefPtr<StyleSheet>>& aSheets, |
2459 | | SheetType aType) |
2460 | 0 | { |
2461 | 0 | // The stylesheets should forget us |
2462 | 0 | for (StyleSheet* sheet : Reversed(aSheets)) { |
2463 | 0 | sheet->ClearAssociatedDocumentOrShadowRoot(); |
2464 | 0 |
|
2465 | 0 | if (sheet->IsApplicable()) { |
2466 | 0 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
2467 | 0 | if (shell) { |
2468 | 0 | shell->StyleSet()->RemoveStyleSheet(aType, sheet); |
2469 | 0 | } |
2470 | 0 | } |
2471 | 0 | // XXX Tell observers? |
2472 | 0 | } |
2473 | 0 | } |
2474 | | |
2475 | | void |
2476 | | nsIDocument::ResetStylesheetsToURI(nsIURI* aURI) |
2477 | 0 | { |
2478 | 0 | MOZ_ASSERT(aURI); |
2479 | 0 |
|
2480 | 0 | if (mStyleSetFilled) { |
2481 | 0 | // Skip removing style sheets from the style set if we know we haven't |
2482 | 0 | // filled the style set. (This allows us to avoid calling |
2483 | 0 | // GetStyleBackendType() too early.) |
2484 | 0 | RemoveDocStyleSheetsFromStyleSets(); |
2485 | 0 | RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], SheetType::Agent); |
2486 | 0 | RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], SheetType::User); |
2487 | 0 | RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], SheetType::Doc); |
2488 | 0 |
|
2489 | 0 | if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) { |
2490 | 0 | RemoveStyleSheetsFromStyleSets( |
2491 | 0 | *sheetService->AuthorStyleSheets(), SheetType::Doc); |
2492 | 0 | } |
2493 | 0 |
|
2494 | 0 | mStyleSetFilled = false; |
2495 | 0 | } |
2496 | 0 |
|
2497 | 0 | // Release all the sheets |
2498 | 0 | mStyleSheets.Clear(); |
2499 | 0 | for (auto& sheets : mAdditionalSheets) { |
2500 | 0 | sheets.Clear(); |
2501 | 0 | } |
2502 | 0 |
|
2503 | 0 | // NOTE: We don't release the catalog sheets. It doesn't really matter |
2504 | 0 | // now, but it could in the future -- in which case not releasing them |
2505 | 0 | // is probably the right thing to do. |
2506 | 0 |
|
2507 | 0 | // Now reset our inline style and attribute sheets. |
2508 | 0 | if (mAttrStyleSheet) { |
2509 | 0 | mAttrStyleSheet->Reset(); |
2510 | 0 | mAttrStyleSheet->SetOwningDocument(this); |
2511 | 0 | } else { |
2512 | 0 | mAttrStyleSheet = new nsHTMLStyleSheet(this); |
2513 | 0 | } |
2514 | 0 |
|
2515 | 0 | if (!mStyleAttrStyleSheet) { |
2516 | 0 | mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet(); |
2517 | 0 | } |
2518 | 0 |
|
2519 | 0 | // Now set up our style sets |
2520 | 0 | if (nsIPresShell* shell = GetShell()) { |
2521 | 0 | FillStyleSet(shell->StyleSet()); |
2522 | 0 | if (shell->StyleSet()->StyleSheetsHaveChanged()) { |
2523 | 0 | shell->ApplicableStylesChanged(); |
2524 | 0 | } |
2525 | 0 | } |
2526 | 0 | } |
2527 | | |
2528 | | static void |
2529 | | AppendSheetsToStyleSet(ServoStyleSet* aStyleSet, |
2530 | | const nsTArray<RefPtr<StyleSheet>>& aSheets, |
2531 | | SheetType aType) |
2532 | 0 | { |
2533 | 0 | for (StyleSheet* sheet : Reversed(aSheets)) { |
2534 | 0 | aStyleSet->AppendStyleSheet(aType, sheet); |
2535 | 0 | } |
2536 | 0 | } |
2537 | | |
2538 | | |
2539 | | void |
2540 | | nsIDocument::FillStyleSet(ServoStyleSet* aStyleSet) |
2541 | 0 | { |
2542 | 0 | MOZ_ASSERT(aStyleSet, "Must have a style set"); |
2543 | 0 | MOZ_ASSERT(aStyleSet->SheetCount(SheetType::Doc) == 0, |
2544 | 0 | "Style set already has document sheets?"); |
2545 | 0 |
|
2546 | 0 | MOZ_ASSERT(!mStyleSetFilled); |
2547 | 0 |
|
2548 | 0 | for (StyleSheet* sheet : Reversed(mStyleSheets)) { |
2549 | 0 | if (sheet->IsApplicable()) { |
2550 | 0 | aStyleSet->AddDocStyleSheet(sheet, this); |
2551 | 0 | } |
2552 | 0 | } |
2553 | 0 |
|
2554 | 0 | if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) { |
2555 | 0 | nsTArray<RefPtr<StyleSheet>>& sheets = |
2556 | 0 | *sheetService->AuthorStyleSheets(); |
2557 | 0 | for (StyleSheet* sheet : sheets) { |
2558 | 0 | aStyleSet->AppendStyleSheet(SheetType::Doc, sheet); |
2559 | 0 | } |
2560 | 0 | } |
2561 | 0 |
|
2562 | 0 | AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet], |
2563 | 0 | SheetType::Agent); |
2564 | 0 | AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet], |
2565 | 0 | SheetType::User); |
2566 | 0 | AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet], |
2567 | 0 | SheetType::Doc); |
2568 | 0 |
|
2569 | 0 | mStyleSetFilled = true; |
2570 | 0 | } |
2571 | | |
2572 | | static void |
2573 | | WarnIfSandboxIneffective(nsIDocShell* aDocShell, |
2574 | | uint32_t aSandboxFlags, |
2575 | | nsIChannel* aChannel) |
2576 | 0 | { |
2577 | 0 | // If the document is sandboxed (via the HTML5 iframe sandbox |
2578 | 0 | // attribute) and both the allow-scripts and allow-same-origin |
2579 | 0 | // keywords are supplied, the sandboxed document can call into its |
2580 | 0 | // parent document and remove its sandboxing entirely - we print a |
2581 | 0 | // warning to the web console in this case. |
2582 | 0 | if (aSandboxFlags & SANDBOXED_NAVIGATION && |
2583 | 0 | !(aSandboxFlags & SANDBOXED_SCRIPTS) && |
2584 | 0 | !(aSandboxFlags & SANDBOXED_ORIGIN)) { |
2585 | 0 | nsCOMPtr<nsIDocShellTreeItem> parentAsItem; |
2586 | 0 | aDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem)); |
2587 | 0 | nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentAsItem); |
2588 | 0 | if (!parentDocShell) { |
2589 | 0 | return; |
2590 | 0 | } |
2591 | 0 | |
2592 | 0 | // Don't warn if our parent is not the top-level document. |
2593 | 0 | nsCOMPtr<nsIDocShellTreeItem> grandParentAsItem; |
2594 | 0 | parentDocShell->GetSameTypeParent(getter_AddRefs(grandParentAsItem)); |
2595 | 0 | if (grandParentAsItem) { |
2596 | 0 | return; |
2597 | 0 | } |
2598 | 0 | |
2599 | 0 | nsCOMPtr<nsIChannel> parentChannel; |
2600 | 0 | parentDocShell->GetCurrentDocumentChannel(getter_AddRefs(parentChannel)); |
2601 | 0 | if (!parentChannel) { |
2602 | 0 | return; |
2603 | 0 | } |
2604 | 0 | nsresult rv = nsContentUtils::CheckSameOrigin(aChannel, parentChannel); |
2605 | 0 | if (NS_FAILED(rv)) { |
2606 | 0 | return; |
2607 | 0 | } |
2608 | 0 | |
2609 | 0 | nsCOMPtr<nsIDocument> parentDocument = parentDocShell->GetDocument(); |
2610 | 0 | nsCOMPtr<nsIURI> iframeUri; |
2611 | 0 | parentChannel->GetURI(getter_AddRefs(iframeUri)); |
2612 | 0 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
2613 | 0 | NS_LITERAL_CSTRING("Iframe Sandbox"), |
2614 | 0 | parentDocument, |
2615 | 0 | nsContentUtils::eSECURITY_PROPERTIES, |
2616 | 0 | "BothAllowScriptsAndSameOriginPresent", |
2617 | 0 | nullptr, 0, iframeUri); |
2618 | 0 | } |
2619 | 0 | } |
2620 | | |
2621 | | bool |
2622 | 0 | nsIDocument::IsSynthesized() { |
2623 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->GetLoadInfo() : nullptr; |
2624 | 0 | return loadInfo && loadInfo->GetServiceWorkerTaintingSynthesized(); |
2625 | 0 | } |
2626 | | |
2627 | | bool |
2628 | | nsDocument::IsShadowDOMEnabled(JSContext* aCx, JSObject* aGlobal) |
2629 | 0 | { |
2630 | 0 | MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal)); |
2631 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = xpc::WindowOrNull(aGlobal); |
2632 | 0 |
|
2633 | 0 | nsIDocument* doc = window ? window->GetExtantDoc() : nullptr; |
2634 | 0 | if (!doc) { |
2635 | 0 | return false; |
2636 | 0 | } |
2637 | 0 | |
2638 | 0 | return doc->IsShadowDOMEnabled(); |
2639 | 0 | } |
2640 | | |
2641 | | // static |
2642 | | bool |
2643 | | nsDocument::IsShadowDOMEnabledAndCallerIsChromeOrAddon(JSContext* aCx, |
2644 | | JSObject* aObject) |
2645 | 0 | { |
2646 | 0 | if (IsShadowDOMEnabled(aCx, aObject)) { |
2647 | 0 | nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx); |
2648 | 0 | return principal && |
2649 | 0 | (nsContentUtils::IsSystemPrincipal(principal) || |
2650 | 0 | principal->GetIsAddonOrExpandedAddonPrincipal()); |
2651 | 0 | } |
2652 | 0 |
|
2653 | 0 | return false; |
2654 | 0 | } |
2655 | | |
2656 | | bool |
2657 | | nsDocument::IsShadowDOMEnabled(const nsINode* aNode) |
2658 | 0 | { |
2659 | 0 | return aNode->OwnerDoc()->IsShadowDOMEnabled(); |
2660 | 0 | } |
2661 | | |
2662 | | nsresult |
2663 | | nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, |
2664 | | nsILoadGroup* aLoadGroup, |
2665 | | nsISupports* aContainer, |
2666 | | nsIStreamListener **aDocListener, |
2667 | | bool aReset, nsIContentSink* aSink) |
2668 | 0 | { |
2669 | 0 | if (MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) { |
2670 | 0 | nsCOMPtr<nsIURI> uri; |
2671 | 0 | aChannel->GetURI(getter_AddRefs(uri)); |
2672 | 0 | MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, |
2673 | 0 | ("DOCUMENT %p StartDocumentLoad %s", |
2674 | 0 | this, uri ? uri->GetSpecOrDefault().get() : "")); |
2675 | 0 | } |
2676 | 0 |
|
2677 | 0 | MOZ_ASSERT(NodePrincipal()->GetAppId() != nsIScriptSecurityManager::UNKNOWN_APP_ID, |
2678 | 0 | "Document should never have UNKNOWN_APP_ID"); |
2679 | 0 |
|
2680 | 0 | MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED, |
2681 | 0 | "Bad readyState"); |
2682 | 0 | SetReadyStateInternal(READYSTATE_LOADING); |
2683 | 0 |
|
2684 | 0 | if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) { |
2685 | 0 | mLoadedAsData = true; |
2686 | 0 | // We need to disable script & style loading in this case. |
2687 | 0 | // We leave them disabled even in EndLoad(), and let anyone |
2688 | 0 | // who puts the document on display to worry about enabling. |
2689 | 0 |
|
2690 | 0 | // Do not load/process scripts when loading as data |
2691 | 0 | ScriptLoader()->SetEnabled(false); |
2692 | 0 |
|
2693 | 0 | // styles |
2694 | 0 | CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data |
2695 | 0 | } else if (nsCRT::strcmp("external-resource", aCommand) == 0) { |
2696 | 0 | // Allow CSS, but not scripts |
2697 | 0 | ScriptLoader()->SetEnabled(false); |
2698 | 0 | } |
2699 | 0 |
|
2700 | 0 | mMayStartLayout = false; |
2701 | 0 | MOZ_ASSERT(!mReadyForIdle, "We should never hit DOMContentLoaded before this point"); |
2702 | 0 |
|
2703 | 0 | if (aReset) { |
2704 | 0 | Reset(aChannel, aLoadGroup); |
2705 | 0 | } |
2706 | 0 |
|
2707 | 0 | nsAutoCString contentType; |
2708 | 0 | nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel); |
2709 | 0 | if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString( |
2710 | 0 | NS_LITERAL_STRING("contentType"), contentType))) || |
2711 | 0 | NS_SUCCEEDED(aChannel->GetContentType(contentType))) { |
2712 | 0 | // XXX this is only necessary for viewsource: |
2713 | 0 | nsACString::const_iterator start, end, semicolon; |
2714 | 0 | contentType.BeginReading(start); |
2715 | 0 | contentType.EndReading(end); |
2716 | 0 | semicolon = start; |
2717 | 0 | FindCharInReadable(';', semicolon, end); |
2718 | 0 | SetContentTypeInternal(Substring(start, semicolon)); |
2719 | 0 | } |
2720 | 0 |
|
2721 | 0 | RetrieveRelevantHeaders(aChannel); |
2722 | 0 |
|
2723 | 0 | mChannel = aChannel; |
2724 | 0 | nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel); |
2725 | 0 | if (inStrmChan) { |
2726 | 0 | bool isSrcdocChannel; |
2727 | 0 | inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel); |
2728 | 0 | if (isSrcdocChannel) { |
2729 | 0 | mIsSrcdocDocument = true; |
2730 | 0 | } |
2731 | 0 | } |
2732 | 0 |
|
2733 | 0 | if (mChannel) { |
2734 | 0 | nsLoadFlags loadFlags; |
2735 | 0 | mChannel->GetLoadFlags(&loadFlags); |
2736 | 0 | bool isDocument = false; |
2737 | 0 | mChannel->GetIsDocument(&isDocument); |
2738 | 0 | if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE && |
2739 | 0 | isDocument && |
2740 | 0 | IsSynthesized() && |
2741 | 0 | XRE_IsContentProcess()) { |
2742 | 0 | ContentChild::UpdateCookieStatus(mChannel); |
2743 | 0 | } |
2744 | 0 | } |
2745 | 0 |
|
2746 | 0 | // If this document is being loaded by a docshell, copy its sandbox flags |
2747 | 0 | // to the document, and store the fullscreen enabled flag. These are |
2748 | 0 | // immutable after being set here. |
2749 | 0 | nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer); |
2750 | 0 |
|
2751 | 0 | // If this is an error page, don't inherit sandbox flags from docshell |
2752 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo(); |
2753 | 0 | if (docShell && !(loadInfo && loadInfo->GetLoadErrorPage())) { |
2754 | 0 | nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags); |
2755 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2756 | 0 | WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel()); |
2757 | 0 | } |
2758 | 0 |
|
2759 | 0 | // The CSP directive upgrade-insecure-requests not only applies to the |
2760 | 0 | // toplevel document, but also to nested documents. Let's propagate that |
2761 | 0 | // flag from the parent to the nested document. |
2762 | 0 | nsCOMPtr<nsIDocShellTreeItem> treeItem = this->GetDocShell(); |
2763 | 0 | if (treeItem) { |
2764 | 0 | nsCOMPtr<nsIDocShellTreeItem> sameTypeParent; |
2765 | 0 | treeItem->GetSameTypeParent(getter_AddRefs(sameTypeParent)); |
2766 | 0 | if (sameTypeParent) { |
2767 | 0 | nsIDocument* doc = sameTypeParent->GetDocument(); |
2768 | 0 | mBlockAllMixedContent = doc->GetBlockAllMixedContent(false); |
2769 | 0 | // if the parent document makes use of block-all-mixed-content |
2770 | 0 | // then subdocument preloads should always be blocked. |
2771 | 0 | mBlockAllMixedContentPreloads = |
2772 | 0 | mBlockAllMixedContent || doc->GetBlockAllMixedContent(true); |
2773 | 0 |
|
2774 | 0 | mUpgradeInsecureRequests = doc->GetUpgradeInsecureRequests(false); |
2775 | 0 | // if the parent document makes use of upgrade-insecure-requests |
2776 | 0 | // then subdocument preloads should always be upgraded. |
2777 | 0 | mUpgradeInsecurePreloads = |
2778 | 0 | mUpgradeInsecureRequests || doc->GetUpgradeInsecureRequests(true); |
2779 | 0 | } |
2780 | 0 | } |
2781 | 0 |
|
2782 | 0 | // If this is not a data document, set CSP. |
2783 | 0 | if (!mLoadedAsData) { |
2784 | 0 | nsresult rv = InitCSP(aChannel); |
2785 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2786 | 0 | } |
2787 | 0 |
|
2788 | 0 | // XFO needs to be checked after CSP because it is ignored if |
2789 | 0 | // the CSP defines frame-ancestors. |
2790 | 0 | if (!FramingChecker::CheckFrameOptions(aChannel, docShell, NodePrincipal())) { |
2791 | 0 | MOZ_LOG(gCspPRLog, LogLevel::Debug, |
2792 | 0 | ("XFO doesn't like frame's ancestry, not loading.")); |
2793 | 0 | // stop! ERROR page! |
2794 | 0 | aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION); |
2795 | 0 | } |
2796 | 0 |
|
2797 | 0 | // Perform a async flash classification based on the doc principal |
2798 | 0 | // in an early stage to reduce the blocking time. |
2799 | 0 | mFlashClassification = FlashClassification::Unclassified; |
2800 | 0 | mPrincipalFlashClassifier->AsyncClassify(GetPrincipal()); |
2801 | 0 |
|
2802 | 0 | return NS_OK; |
2803 | 0 | } |
2804 | | |
2805 | | void |
2806 | | nsIDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) |
2807 | 0 | { |
2808 | 0 | for (uint32_t i = 0; i < aMessages.Length(); ++i) { |
2809 | 0 | nsAutoString messageTag; |
2810 | 0 | aMessages[i]->GetTag(messageTag); |
2811 | 0 |
|
2812 | 0 | nsAutoString category; |
2813 | 0 | aMessages[i]->GetCategory(category); |
2814 | 0 |
|
2815 | 0 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
2816 | 0 | NS_ConvertUTF16toUTF8(category), |
2817 | 0 | this, nsContentUtils::eSECURITY_PROPERTIES, |
2818 | 0 | NS_ConvertUTF16toUTF8(messageTag).get()); |
2819 | 0 | } |
2820 | 0 | } |
2821 | | |
2822 | | void |
2823 | | nsIDocument::ApplySettingsFromCSP(bool aSpeculative) |
2824 | 0 | { |
2825 | 0 | nsresult rv = NS_OK; |
2826 | 0 | if (!aSpeculative) { |
2827 | 0 | // 1) apply settings from regular CSP |
2828 | 0 | nsCOMPtr<nsIContentSecurityPolicy> csp; |
2829 | 0 | rv = NodePrincipal()->GetCsp(getter_AddRefs(csp)); |
2830 | 0 | NS_ENSURE_SUCCESS_VOID(rv); |
2831 | 0 | if (csp) { |
2832 | 0 | // Set up 'block-all-mixed-content' if not already inherited |
2833 | 0 | // from the parent context or set by any other CSP. |
2834 | 0 | if (!mBlockAllMixedContent) { |
2835 | 0 | rv = csp->GetBlockAllMixedContent(&mBlockAllMixedContent); |
2836 | 0 | NS_ENSURE_SUCCESS_VOID(rv); |
2837 | 0 | } |
2838 | 0 | if (!mBlockAllMixedContentPreloads) { |
2839 | 0 | mBlockAllMixedContentPreloads = mBlockAllMixedContent; |
2840 | 0 | } |
2841 | 0 |
|
2842 | 0 | // Set up 'upgrade-insecure-requests' if not already inherited |
2843 | 0 | // from the parent context or set by any other CSP. |
2844 | 0 | if (!mUpgradeInsecureRequests) { |
2845 | 0 | rv = csp->GetUpgradeInsecureRequests(&mUpgradeInsecureRequests); |
2846 | 0 | NS_ENSURE_SUCCESS_VOID(rv); |
2847 | 0 | } |
2848 | 0 | if (!mUpgradeInsecurePreloads) { |
2849 | 0 | mUpgradeInsecurePreloads = mUpgradeInsecureRequests; |
2850 | 0 | } |
2851 | 0 | } |
2852 | 0 | return; |
2853 | 0 | } |
2854 | 0 | |
2855 | 0 | // 2) apply settings from speculative csp |
2856 | 0 | nsCOMPtr<nsIContentSecurityPolicy> preloadCsp; |
2857 | 0 | rv = NodePrincipal()->GetPreloadCsp(getter_AddRefs(preloadCsp)); |
2858 | 0 | NS_ENSURE_SUCCESS_VOID(rv); |
2859 | 0 | if (preloadCsp) { |
2860 | 0 | if (!mBlockAllMixedContentPreloads) { |
2861 | 0 | rv = preloadCsp->GetBlockAllMixedContent(&mBlockAllMixedContentPreloads); |
2862 | 0 | NS_ENSURE_SUCCESS_VOID(rv); |
2863 | 0 | } |
2864 | 0 | if (!mUpgradeInsecurePreloads) { |
2865 | 0 | rv = preloadCsp->GetUpgradeInsecureRequests(&mUpgradeInsecurePreloads); |
2866 | 0 | NS_ENSURE_SUCCESS_VOID(rv); |
2867 | 0 | } |
2868 | 0 | } |
2869 | 0 | } |
2870 | | |
2871 | | nsresult |
2872 | | nsIDocument::InitCSP(nsIChannel* aChannel) |
2873 | 0 | { |
2874 | 0 | MOZ_ASSERT(!mScriptGlobalObject, |
2875 | 0 | "CSP must be initialized before mScriptGlobalObject is set!"); |
2876 | 0 | if (!StaticPrefs::security_csp_enable()) { |
2877 | 0 | MOZ_LOG(gCspPRLog, LogLevel::Debug, |
2878 | 0 | ("CSP is disabled, skipping CSP init for document %p", this)); |
2879 | 0 | return NS_OK; |
2880 | 0 | } |
2881 | 0 |
|
2882 | 0 | nsAutoCString tCspHeaderValue, tCspROHeaderValue; |
2883 | 0 |
|
2884 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel; |
2885 | 0 | nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel)); |
2886 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2887 | 0 | return rv; |
2888 | 0 | } |
2889 | 0 | |
2890 | 0 | if (httpChannel) { |
2891 | 0 | Unused << httpChannel->GetResponseHeader( |
2892 | 0 | NS_LITERAL_CSTRING("content-security-policy"), |
2893 | 0 | tCspHeaderValue); |
2894 | 0 |
|
2895 | 0 | Unused << httpChannel->GetResponseHeader( |
2896 | 0 | NS_LITERAL_CSTRING("content-security-policy-report-only"), |
2897 | 0 | tCspROHeaderValue); |
2898 | 0 | } |
2899 | 0 | NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue); |
2900 | 0 | NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue); |
2901 | 0 |
|
2902 | 0 | // Check if this is a document from a WebExtension. |
2903 | 0 | nsCOMPtr<nsIPrincipal> principal = NodePrincipal(); |
2904 | 0 | auto addonPolicy = BasePrincipal::Cast(principal)->AddonPolicy(); |
2905 | 0 |
|
2906 | 0 | // Check if this is a signed content to apply default CSP. |
2907 | 0 | bool applySignedContentCSP = false; |
2908 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo(); |
2909 | 0 | if (loadInfo && loadInfo->GetVerifySignedContent()) { |
2910 | 0 | applySignedContentCSP = true; |
2911 | 0 | } |
2912 | 0 |
|
2913 | 0 | // If there's no CSP to apply, go ahead and return early |
2914 | 0 | if (!addonPolicy && |
2915 | 0 | !applySignedContentCSP && |
2916 | 0 | cspHeaderValue.IsEmpty() && |
2917 | 0 | cspROHeaderValue.IsEmpty()) { |
2918 | 0 | if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) { |
2919 | 0 | nsCOMPtr<nsIURI> chanURI; |
2920 | 0 | aChannel->GetURI(getter_AddRefs(chanURI)); |
2921 | 0 | nsAutoCString aspec; |
2922 | 0 | chanURI->GetAsciiSpec(aspec); |
2923 | 0 | MOZ_LOG(gCspPRLog, LogLevel::Debug, |
2924 | 0 | ("no CSP for document, %s", |
2925 | 0 | aspec.get())); |
2926 | 0 | } |
2927 | 0 |
|
2928 | 0 | return NS_OK; |
2929 | 0 | } |
2930 | 0 |
|
2931 | 0 | MOZ_LOG(gCspPRLog, LogLevel::Debug, ("Document is an add-on or CSP header specified %p", this)); |
2932 | 0 |
|
2933 | 0 | nsCOMPtr<nsIContentSecurityPolicy> csp; |
2934 | 0 | rv = principal->EnsureCSP(static_cast<nsDocument*>(this), getter_AddRefs(csp)); |
2935 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2936 | 0 |
|
2937 | 0 | // ----- if the doc is an addon, apply its CSP. |
2938 | 0 | if (addonPolicy) { |
2939 | 0 | nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1"); |
2940 | 0 |
|
2941 | 0 | nsAutoString addonCSP; |
2942 | 0 | Unused << ExtensionPolicyService::GetSingleton().GetBaseCSP(addonCSP); |
2943 | 0 | csp->AppendPolicy(addonCSP, false, false); |
2944 | 0 |
|
2945 | 0 | csp->AppendPolicy(addonPolicy->ContentSecurityPolicy(), false, false); |
2946 | 0 | } |
2947 | 0 |
|
2948 | 0 | // ----- if the doc is a signed content, apply the default CSP. |
2949 | 0 | // Note that when the content signing becomes a standard, we might have |
2950 | 0 | // to restrict this enforcement to "remote content" only. |
2951 | 0 | if (applySignedContentCSP) { |
2952 | 0 | nsAutoString signedContentCSP; |
2953 | 0 | Preferences::GetString("security.signed_content.CSP.default", |
2954 | 0 | signedContentCSP); |
2955 | 0 | csp->AppendPolicy(signedContentCSP, false, false); |
2956 | 0 | } |
2957 | 0 |
|
2958 | 0 | // ----- if there's a full-strength CSP header, apply it. |
2959 | 0 | if (!cspHeaderValue.IsEmpty()) { |
2960 | 0 | rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false); |
2961 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2962 | 0 | } |
2963 | 0 |
|
2964 | 0 | // ----- if there's a report-only CSP header, apply it. |
2965 | 0 | if (!cspROHeaderValue.IsEmpty()) { |
2966 | 0 | rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true); |
2967 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2968 | 0 | } |
2969 | 0 |
|
2970 | 0 | // ----- Enforce sandbox policy if supplied in CSP header |
2971 | 0 | // The document may already have some sandbox flags set (e.g. if the document |
2972 | 0 | // is an iframe with the sandbox attribute set). If we have a CSP sandbox |
2973 | 0 | // directive, intersect the CSP sandbox flags with the existing flags. This |
2974 | 0 | // corresponds to the _least_ permissive policy. |
2975 | 0 | uint32_t cspSandboxFlags = SANDBOXED_NONE; |
2976 | 0 | rv = csp->GetCSPSandboxFlags(&cspSandboxFlags); |
2977 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2978 | 0 |
|
2979 | 0 | // Probably the iframe sandbox attribute already caused the creation of a |
2980 | 0 | // new NullPrincipal. Only create a new NullPrincipal if CSP requires so |
2981 | 0 | // and no one has been created yet. |
2982 | 0 | bool needNewNullPrincipal = |
2983 | 0 | (cspSandboxFlags & SANDBOXED_ORIGIN) && !(mSandboxFlags & SANDBOXED_ORIGIN); |
2984 | 0 |
|
2985 | 0 | mSandboxFlags |= cspSandboxFlags; |
2986 | 0 |
|
2987 | 0 | if (needNewNullPrincipal) { |
2988 | 0 | principal = NullPrincipal::CreateWithInheritedAttributes(principal); |
2989 | 0 | principal->SetCsp(csp); |
2990 | 0 | SetPrincipal(principal); |
2991 | 0 | } |
2992 | 0 |
|
2993 | 0 | // ----- Enforce frame-ancestor policy on any applied policies |
2994 | 0 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
2995 | 0 | if (docShell) { |
2996 | 0 | bool safeAncestry = false; |
2997 | 0 |
|
2998 | 0 | // PermitsAncestry sends violation reports when necessary |
2999 | 0 | rv = csp->PermitsAncestry(docShell, &safeAncestry); |
3000 | 0 |
|
3001 | 0 | if (NS_FAILED(rv) || !safeAncestry) { |
3002 | 0 | MOZ_LOG(gCspPRLog, LogLevel::Debug, |
3003 | 0 | ("CSP doesn't like frame's ancestry, not loading.")); |
3004 | 0 | // stop! ERROR page! |
3005 | 0 | aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION); |
3006 | 0 | } |
3007 | 0 | } |
3008 | 0 | ApplySettingsFromCSP(false); |
3009 | 0 | return NS_OK; |
3010 | 0 | } |
3011 | | |
3012 | | void |
3013 | | nsDocument::StopDocumentLoad() |
3014 | 0 | { |
3015 | 0 | if (mParser) { |
3016 | 0 | mParserAborted = true; |
3017 | 0 | mParser->Terminate(); |
3018 | 0 | } |
3019 | 0 | } |
3020 | | |
3021 | | void |
3022 | | nsIDocument::SetDocumentURI(nsIURI* aURI) |
3023 | 0 | { |
3024 | 0 | nsCOMPtr<nsIURI> oldBase = GetDocBaseURI(); |
3025 | 0 | mDocumentURI = aURI; |
3026 | 0 | nsIURI* newBase = GetDocBaseURI(); |
3027 | 0 |
|
3028 | 0 | bool equalBases = false; |
3029 | 0 | // Changing just the ref of a URI does not change how relative URIs would |
3030 | 0 | // resolve wrt to it, so we can treat the bases as equal as long as they're |
3031 | 0 | // equal ignoring the ref. |
3032 | 0 | if (oldBase && newBase) { |
3033 | 0 | oldBase->EqualsExceptRef(newBase, &equalBases); |
3034 | 0 | } |
3035 | 0 | else { |
3036 | 0 | equalBases = !oldBase && !newBase; |
3037 | 0 | } |
3038 | 0 |
|
3039 | 0 | // If this is the first time we're setting the document's URI, set the |
3040 | 0 | // document's original URI. |
3041 | 0 | if (!mOriginalURI) |
3042 | 0 | mOriginalURI = mDocumentURI; |
3043 | 0 |
|
3044 | 0 | // If changing the document's URI changed the base URI of the document, we |
3045 | 0 | // need to refresh the hrefs of all the links on the page. |
3046 | 0 | if (!equalBases) { |
3047 | 0 | RefreshLinkHrefs(); |
3048 | 0 | } |
3049 | 0 | } |
3050 | | |
3051 | | static void |
3052 | | GetFormattedTimeString(PRTime aTime, nsAString& aFormattedTimeString) |
3053 | 0 | { |
3054 | 0 | PRExplodedTime prtime; |
3055 | 0 | PR_ExplodeTime(aTime, PR_LocalTimeParameters, &prtime); |
3056 | 0 | // "MM/DD/YYYY hh:mm:ss" |
3057 | 0 | char formatedTime[24]; |
3058 | 0 | if (SprintfLiteral(formatedTime, "%02d/%02d/%04d %02d:%02d:%02d", |
3059 | 0 | prtime.tm_month + 1, prtime.tm_mday, int(prtime.tm_year), |
3060 | 0 | prtime.tm_hour , prtime.tm_min, prtime.tm_sec)) { |
3061 | 0 | CopyASCIItoUTF16(nsDependentCString(formatedTime), aFormattedTimeString); |
3062 | 0 | } else { |
3063 | 0 | // If we for whatever reason failed to find the last modified time |
3064 | 0 | // (or even the current time), fall back to what NS4.x returned. |
3065 | 0 | aFormattedTimeString.AssignLiteral(u"01/01/1970 00:00:00"); |
3066 | 0 | } |
3067 | 0 | } |
3068 | | |
3069 | | void |
3070 | | nsIDocument::GetLastModified(nsAString& aLastModified) const |
3071 | 0 | { |
3072 | 0 | if (!mLastModified.IsEmpty()) { |
3073 | 0 | aLastModified.Assign(mLastModified); |
3074 | 0 | } else { |
3075 | 0 | GetFormattedTimeString(PR_Now(), aLastModified); |
3076 | 0 | } |
3077 | 0 | } |
3078 | | |
3079 | | static void |
3080 | | IncrementExpandoGeneration(nsIDocument& aDoc) |
3081 | 0 | { |
3082 | 0 | ++static_cast<nsDocument&>(aDoc).mExpandoAndGeneration.generation; |
3083 | 0 | } |
3084 | | |
3085 | | void |
3086 | | nsIDocument::AddToNameTable(Element* aElement, nsAtom* aName) |
3087 | 0 | { |
3088 | 0 | MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement), |
3089 | 0 | "Only put elements that need to be exposed as document['name'] in " |
3090 | 0 | "the named table."); |
3091 | 0 |
|
3092 | 0 | nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName); |
3093 | 0 |
|
3094 | 0 | // Null for out-of-memory |
3095 | 0 | if (entry) { |
3096 | 0 | if (!entry->HasNameElement() && |
3097 | 0 | !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
3098 | 0 | IncrementExpandoGeneration(*this); |
3099 | 0 | } |
3100 | 0 | entry->AddNameElement(this, aElement); |
3101 | 0 | } |
3102 | 0 | } |
3103 | | |
3104 | | void |
3105 | | nsIDocument::RemoveFromNameTable(Element* aElement, nsAtom* aName) |
3106 | 0 | { |
3107 | 0 | // Speed up document teardown |
3108 | 0 | if (mIdentifierMap.Count() == 0) |
3109 | 0 | return; |
3110 | 0 | |
3111 | 0 | nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName); |
3112 | 0 | if (!entry) // Could be false if the element was anonymous, hence never added |
3113 | 0 | return; |
3114 | 0 | |
3115 | 0 | entry->RemoveNameElement(aElement); |
3116 | 0 | if (!entry->HasNameElement() && |
3117 | 0 | !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
3118 | 0 | IncrementExpandoGeneration(*this); |
3119 | 0 | } |
3120 | 0 | } |
3121 | | |
3122 | | void |
3123 | | nsIDocument::AddToIdTable(Element* aElement, nsAtom* aId) |
3124 | 0 | { |
3125 | 0 | nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId); |
3126 | 0 |
|
3127 | 0 | if (entry) { /* True except on OOM */ |
3128 | 0 | if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) && |
3129 | 0 | !entry->HasNameElement() && |
3130 | 0 | !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
3131 | 0 | IncrementExpandoGeneration(*this); |
3132 | 0 | } |
3133 | 0 | entry->AddIdElement(aElement); |
3134 | 0 | } |
3135 | 0 | } |
3136 | | |
3137 | | void |
3138 | | nsIDocument::RemoveFromIdTable(Element* aElement, nsAtom* aId) |
3139 | 0 | { |
3140 | 0 | NS_ASSERTION(aId, "huhwhatnow?"); |
3141 | 0 |
|
3142 | 0 | // Speed up document teardown |
3143 | 0 | if (mIdentifierMap.Count() == 0) { |
3144 | 0 | return; |
3145 | 0 | } |
3146 | 0 | |
3147 | 0 | nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId); |
3148 | 0 | if (!entry) // Can be null for XML elements with changing ids. |
3149 | 0 | return; |
3150 | 0 | |
3151 | 0 | entry->RemoveIdElement(aElement); |
3152 | 0 | if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) && |
3153 | 0 | !entry->HasNameElement() && |
3154 | 0 | !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
3155 | 0 | IncrementExpandoGeneration(*this); |
3156 | 0 | } |
3157 | 0 | if (entry->IsEmpty()) { |
3158 | 0 | mIdentifierMap.RemoveEntry(entry); |
3159 | 0 | } |
3160 | 0 | } |
3161 | | |
3162 | | nsIPrincipal* |
3163 | | nsDocument::GetPrincipal() |
3164 | 0 | { |
3165 | 0 | return NodePrincipal(); |
3166 | 0 | } |
3167 | | |
3168 | | extern bool sDisablePrefetchHTTPSPref; |
3169 | | |
3170 | | void |
3171 | | nsIDocument::SetPrincipal(nsIPrincipal *aNewPrincipal) |
3172 | 0 | { |
3173 | 0 | if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) { |
3174 | 0 | nsCOMPtr<nsIURI> uri; |
3175 | 0 | aNewPrincipal->GetURI(getter_AddRefs(uri)); |
3176 | 0 | bool isHTTPS; |
3177 | 0 | if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) || |
3178 | 0 | isHTTPS) { |
3179 | 0 | mAllowDNSPrefetch = false; |
3180 | 0 | } |
3181 | 0 | } |
3182 | 0 | mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal); |
3183 | 0 |
|
3184 | | #ifdef DEBUG |
3185 | | // Validate that the docgroup is set correctly by calling its getter and |
3186 | | // triggering its sanity check. |
3187 | | // |
3188 | | // If we're setting the principal to null, we don't want to perform the check, |
3189 | | // as the document is entering an intermediate state where it does not have a |
3190 | | // principal. It will be given another real principal shortly which we will |
3191 | | // check. It's not unsafe to have a document which has a null principal in the |
3192 | | // same docgroup as another document, so this should not be a problem. |
3193 | | if (aNewPrincipal) { |
3194 | | GetDocGroup(); |
3195 | | } |
3196 | | #endif |
3197 | | } |
3198 | | |
3199 | | mozilla::dom::DocGroup* |
3200 | | nsIDocument::GetDocGroup() const |
3201 | 0 | { |
3202 | | #ifdef DEBUG |
3203 | | // Sanity check that we have an up-to-date and accurate docgroup |
3204 | | if (mDocGroup) { |
3205 | | nsAutoCString docGroupKey; |
3206 | | |
3207 | | // GetKey() can fail, e.g. after the TLD service has shut down. |
3208 | | nsresult rv = mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey); |
3209 | | if (NS_SUCCEEDED(rv)) { |
3210 | | MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey)); |
3211 | | } |
3212 | | // XXX: Check that the TabGroup is correct as well! |
3213 | | } |
3214 | | #endif |
3215 | |
|
3216 | 0 | return mDocGroup; |
3217 | 0 | } |
3218 | | |
3219 | | nsresult |
3220 | | nsIDocument::Dispatch(TaskCategory aCategory, |
3221 | | already_AddRefed<nsIRunnable>&& aRunnable) |
3222 | 0 | { |
3223 | 0 | // Note that this method may be called off the main thread. |
3224 | 0 | if (mDocGroup) { |
3225 | 0 | return mDocGroup->Dispatch(aCategory, std::move(aRunnable)); |
3226 | 0 | } |
3227 | 0 | return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable)); |
3228 | 0 | } |
3229 | | |
3230 | | nsISerialEventTarget* |
3231 | | nsIDocument::EventTargetFor(TaskCategory aCategory) const |
3232 | 0 | { |
3233 | 0 | if (mDocGroup) { |
3234 | 0 | return mDocGroup->EventTargetFor(aCategory); |
3235 | 0 | } |
3236 | 0 | return DispatcherTrait::EventTargetFor(aCategory); |
3237 | 0 | } |
3238 | | |
3239 | | AbstractThread* |
3240 | | nsIDocument::AbstractMainThreadFor(mozilla::TaskCategory aCategory) |
3241 | 0 | { |
3242 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3243 | 0 | if (mDocGroup) { |
3244 | 0 | return mDocGroup->AbstractMainThreadFor(aCategory); |
3245 | 0 | } |
3246 | 0 | return DispatcherTrait::AbstractMainThreadFor(aCategory); |
3247 | 0 | } |
3248 | | |
3249 | | void |
3250 | | nsIDocument::NoteScriptTrackingStatus(const nsACString& aURL, bool aIsTracking) |
3251 | 0 | { |
3252 | 0 | if (aIsTracking) { |
3253 | 0 | mTrackingScripts.PutEntry(aURL); |
3254 | 0 | } else { |
3255 | 0 | MOZ_ASSERT(!mTrackingScripts.Contains(aURL)); |
3256 | 0 | } |
3257 | 0 | } |
3258 | | |
3259 | | bool |
3260 | | nsIDocument::IsScriptTracking(const nsACString& aURL) const |
3261 | 0 | { |
3262 | 0 | return mTrackingScripts.Contains(aURL); |
3263 | 0 | } |
3264 | | |
3265 | | NS_IMETHODIMP |
3266 | | nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache) |
3267 | 0 | { |
3268 | 0 | NS_IF_ADDREF(*aApplicationCache = mApplicationCache); |
3269 | 0 |
|
3270 | 0 | return NS_OK; |
3271 | 0 | } |
3272 | | |
3273 | | NS_IMETHODIMP |
3274 | | nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache) |
3275 | 0 | { |
3276 | 0 | mApplicationCache = aApplicationCache; |
3277 | 0 |
|
3278 | 0 | return NS_OK; |
3279 | 0 | } |
3280 | | |
3281 | | void |
3282 | | nsIDocument::GetContentType(nsAString& aContentType) |
3283 | 0 | { |
3284 | 0 | CopyUTF8toUTF16(GetContentTypeInternal(), aContentType); |
3285 | 0 | } |
3286 | | |
3287 | | void |
3288 | | nsIDocument::SetContentType(const nsAString& aContentType) |
3289 | 0 | { |
3290 | 0 | SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType)); |
3291 | 0 | } |
3292 | | |
3293 | | bool |
3294 | | nsIDocument::GetAllowPlugins() |
3295 | 0 | { |
3296 | 0 | // First, we ask our docshell if it allows plugins. |
3297 | 0 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
3298 | 0 |
|
3299 | 0 | if (docShell) { |
3300 | 0 | bool allowPlugins = false; |
3301 | 0 | docShell->GetAllowPlugins(&allowPlugins); |
3302 | 0 | if (!allowPlugins) { |
3303 | 0 | return false; |
3304 | 0 | } |
3305 | 0 | |
3306 | 0 | // If the docshell allows plugins, we check whether |
3307 | 0 | // we are sandboxed and plugins should not be allowed. |
3308 | 0 | if (mSandboxFlags & SANDBOXED_PLUGINS) { |
3309 | 0 | return false; |
3310 | 0 | } |
3311 | 0 | } |
3312 | 0 | |
3313 | 0 | FlashClassification classification = DocumentFlashClassification(); |
3314 | 0 | if (classification == FlashClassification::Denied) { |
3315 | 0 | return false; |
3316 | 0 | } |
3317 | 0 | |
3318 | 0 | return true; |
3319 | 0 | } |
3320 | | |
3321 | | void |
3322 | | nsIDocument::InitializeLocalization(nsTArray<nsString>& aResourceIds) |
3323 | 0 | { |
3324 | 0 | MOZ_ASSERT(!mDocumentL10n, "mDocumentL10n should not be initialized yet"); |
3325 | 0 |
|
3326 | 0 | DocumentL10n* l10n = new DocumentL10n(this); |
3327 | 0 | MOZ_ALWAYS_TRUE(l10n->Init(aResourceIds)); |
3328 | 0 | mDocumentL10n = l10n; |
3329 | 0 | } |
3330 | | |
3331 | | DocumentL10n* |
3332 | | nsIDocument::GetL10n() |
3333 | 0 | { |
3334 | 0 | return mDocumentL10n; |
3335 | 0 | } |
3336 | | |
3337 | | bool |
3338 | | nsDocument::DocumentSupportsL10n(JSContext* aCx, JSObject* aObject) |
3339 | 0 | { |
3340 | 0 | return PrincipalAllowsL10n(nsContentUtils::SubjectPrincipal(aCx)); |
3341 | 0 | } |
3342 | | |
3343 | | void |
3344 | | nsIDocument::LocalizationLinkAdded(Element* aLinkElement) |
3345 | 0 | { |
3346 | 0 | if (!PrincipalAllowsL10n(NodePrincipal())) { |
3347 | 0 | return; |
3348 | 0 | } |
3349 | 0 | |
3350 | 0 | nsAutoString href; |
3351 | 0 | aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href); |
3352 | 0 | // If the link is added after the DocumentL10n instance |
3353 | 0 | // has been initialized, just pass the resource ID to it. |
3354 | 0 | if (mDocumentL10n) { |
3355 | 0 | AutoTArray<nsString, 1> resourceIds; |
3356 | 0 | resourceIds.AppendElement(href); |
3357 | 0 | mDocumentL10n->AddResourceIds(resourceIds); |
3358 | 0 | } else if (mReadyState >= READYSTATE_INTERACTIVE) { |
3359 | 0 | // Otherwise, if the document has already been parsed |
3360 | 0 | // we need to lazily initialize the localization. |
3361 | 0 | AutoTArray<nsString, 1> resourceIds; |
3362 | 0 | resourceIds.AppendElement(href); |
3363 | 0 | InitializeLocalization(resourceIds); |
3364 | 0 | mDocumentL10n->TriggerInitialDocumentTranslation(); |
3365 | 0 | } else { |
3366 | 0 | // Otherwise, we're still parsing the document. |
3367 | 0 | // In that case, add it to the pending list. This list |
3368 | 0 | // will be resolved once the end of l10n resource |
3369 | 0 | // container is reached. |
3370 | 0 | mL10nResources.AppendElement(href); |
3371 | 0 | } |
3372 | 0 | } |
3373 | | |
3374 | | void |
3375 | | nsIDocument::LocalizationLinkRemoved(Element* aLinkElement) |
3376 | 0 | { |
3377 | 0 | if (!PrincipalAllowsL10n(NodePrincipal())) { |
3378 | 0 | return; |
3379 | 0 | } |
3380 | 0 | |
3381 | 0 | nsAutoString href; |
3382 | 0 | aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href); |
3383 | 0 | if (mDocumentL10n) { |
3384 | 0 | AutoTArray<nsString, 1> resourceIds; |
3385 | 0 | resourceIds.AppendElement(href); |
3386 | 0 | uint32_t remaining = mDocumentL10n->RemoveResourceIds(resourceIds); |
3387 | 0 | if (remaining == 0) { |
3388 | 0 | mDocumentL10n = nullptr; |
3389 | 0 | } |
3390 | 0 | } else { |
3391 | 0 | mL10nResources.RemoveElement(href); |
3392 | 0 | } |
3393 | 0 | } |
3394 | | |
3395 | | /** |
3396 | | * This method should be called once the end of the l10n |
3397 | | * resource container has been parsed. |
3398 | | * |
3399 | | * In XUL this is the end of the first </linkset>, |
3400 | | * In XHTML/HTML this is the end of </head>. |
3401 | | * |
3402 | | * This milestone is used to allow for batch |
3403 | | * localization context I/O and building done |
3404 | | * once when all resources in the document have been |
3405 | | * collected. |
3406 | | */ |
3407 | | void |
3408 | | nsIDocument::OnL10nResourceContainerParsed() |
3409 | 0 | { |
3410 | 0 | if (!mL10nResources.IsEmpty()) { |
3411 | 0 | InitializeLocalization(mL10nResources); |
3412 | 0 | mL10nResources.Clear(); |
3413 | 0 | } |
3414 | 0 | } |
3415 | | |
3416 | | void |
3417 | | nsIDocument::TriggerInitialDocumentTranslation() |
3418 | 0 | { |
3419 | 0 | if (mDocumentL10n) { |
3420 | 0 | mDocumentL10n->TriggerInitialDocumentTranslation(); |
3421 | 0 | } |
3422 | 0 | } |
3423 | | |
3424 | | bool |
3425 | | nsDocument::IsWebAnimationsEnabled(JSContext* aCx, JSObject* /*unused*/) |
3426 | 0 | { |
3427 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3428 | 0 |
|
3429 | 0 | return nsContentUtils::IsSystemCaller(aCx) || |
3430 | 0 | nsContentUtils::AnimationsAPICoreEnabled(); |
3431 | 0 | } |
3432 | | |
3433 | | bool |
3434 | | nsDocument::IsWebAnimationsEnabled(CallerType aCallerType) |
3435 | 0 | { |
3436 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3437 | 0 |
|
3438 | 0 | return aCallerType == dom::CallerType::System || |
3439 | 0 | nsContentUtils::AnimationsAPICoreEnabled(); |
3440 | 0 | } |
3441 | | |
3442 | | bool |
3443 | | nsDocument::IsWebAnimationsGetAnimationsEnabled(JSContext* aCx, |
3444 | | JSObject* /*unused*/ |
3445 | | ) |
3446 | 0 | { |
3447 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3448 | 0 |
|
3449 | 0 | return nsContentUtils::IsSystemCaller(aCx) || |
3450 | 0 | StaticPrefs::dom_animations_api_getAnimations_enabled(); |
3451 | 0 | } |
3452 | | |
3453 | | bool |
3454 | | nsDocument::AreWebAnimationsImplicitKeyframesEnabled(JSContext* aCx, |
3455 | | JSObject* /*unused*/ |
3456 | | ) |
3457 | 0 | { |
3458 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3459 | 0 |
|
3460 | 0 | return nsContentUtils::IsSystemCaller(aCx) || |
3461 | 0 | StaticPrefs::dom_animations_api_implicit_keyframes_enabled(); |
3462 | 0 | } |
3463 | | |
3464 | | bool |
3465 | | nsDocument::AreWebAnimationsTimelinesEnabled(JSContext* aCx, |
3466 | | JSObject* /*unused*/ |
3467 | | ) |
3468 | 0 | { |
3469 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3470 | 0 |
|
3471 | 0 | return nsContentUtils::IsSystemCaller(aCx) || |
3472 | 0 | StaticPrefs::dom_animations_api_timelines_enabled(); |
3473 | 0 | } |
3474 | | |
3475 | | DocumentTimeline* |
3476 | | nsIDocument::Timeline() |
3477 | 0 | { |
3478 | 0 | if (!mDocumentTimeline) { |
3479 | 0 | mDocumentTimeline = new DocumentTimeline(this, TimeDuration(0)); |
3480 | 0 | } |
3481 | 0 |
|
3482 | 0 | return mDocumentTimeline; |
3483 | 0 | } |
3484 | | |
3485 | | void |
3486 | | nsIDocument::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations) |
3487 | 0 | { |
3488 | 0 | // Hold a strong ref for the root element since Element::GetAnimations() calls |
3489 | 0 | // FlushPendingNotifications() which may destroy the element. |
3490 | 0 | RefPtr<Element> root = GetRootElement(); |
3491 | 0 | if (!root) { |
3492 | 0 | return; |
3493 | 0 | } |
3494 | 0 | AnimationFilter filter; |
3495 | 0 | filter.mSubtree = true; |
3496 | 0 | root->GetAnimations(filter, aAnimations); |
3497 | 0 | } |
3498 | | |
3499 | | SVGSVGElement* |
3500 | | nsIDocument::GetSVGRootElement() const |
3501 | 0 | { |
3502 | 0 | Element* root = GetRootElement(); |
3503 | 0 | if (!root || !root->IsSVGElement(nsGkAtoms::svg)) { |
3504 | 0 | return nullptr; |
3505 | 0 | } |
3506 | 0 | return static_cast<SVGSVGElement*>(root); |
3507 | 0 | } |
3508 | | |
3509 | | /* Return true if the document is in the focused top-level window, and is an |
3510 | | * ancestor of the focused DOMWindow. */ |
3511 | | bool |
3512 | | nsIDocument::HasFocus(ErrorResult& rv) const |
3513 | 0 | { |
3514 | 0 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
3515 | 0 | if (!fm) { |
3516 | 0 | rv.Throw(NS_ERROR_NOT_AVAILABLE); |
3517 | 0 | return false; |
3518 | 0 | } |
3519 | 0 | |
3520 | 0 | // Is there a focused DOMWindow? |
3521 | 0 | nsCOMPtr<mozIDOMWindowProxy> focusedWindow; |
3522 | 0 | fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); |
3523 | 0 | if (!focusedWindow) { |
3524 | 0 | return false; |
3525 | 0 | } |
3526 | 0 | |
3527 | 0 | nsPIDOMWindowOuter* piWindow = nsPIDOMWindowOuter::From(focusedWindow); |
3528 | 0 |
|
3529 | 0 | // Are we an ancestor of the focused DOMWindow? |
3530 | 0 | for (nsIDocument* currentDoc = piWindow->GetDoc(); currentDoc; |
3531 | 0 | currentDoc = currentDoc->GetParentDocument()) { |
3532 | 0 | if (currentDoc == this) { |
3533 | 0 | // Yes, we are an ancestor |
3534 | 0 | return true; |
3535 | 0 | } |
3536 | 0 | } |
3537 | 0 |
|
3538 | 0 | return false; |
3539 | 0 | } |
3540 | | |
3541 | | TimeStamp |
3542 | | nsIDocument::LastFocusTime() const |
3543 | 0 | { |
3544 | 0 | return mLastFocusTime; |
3545 | 0 | } |
3546 | | |
3547 | | void |
3548 | | nsIDocument::SetLastFocusTime(const TimeStamp& aFocusTime) |
3549 | 0 | { |
3550 | 0 | MOZ_DIAGNOSTIC_ASSERT(!aFocusTime.IsNull()); |
3551 | 0 | MOZ_DIAGNOSTIC_ASSERT(mLastFocusTime.IsNull() || |
3552 | 0 | aFocusTime >= mLastFocusTime); |
3553 | 0 | mLastFocusTime = aFocusTime; |
3554 | 0 | } |
3555 | | |
3556 | | void |
3557 | | nsIDocument::GetReferrer(nsAString& aReferrer) const |
3558 | 0 | { |
3559 | 0 | if (mIsSrcdocDocument && mParentDocument) |
3560 | 0 | mParentDocument->GetReferrer(aReferrer); |
3561 | 0 | else |
3562 | 0 | CopyUTF8toUTF16(mReferrer, aReferrer); |
3563 | 0 | } |
3564 | | |
3565 | | nsresult |
3566 | | nsIDocument::GetSrcdocData(nsAString &aSrcdocData) |
3567 | 0 | { |
3568 | 0 | if (mIsSrcdocDocument) { |
3569 | 0 | nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel); |
3570 | 0 | if (inStrmChan) { |
3571 | 0 | return inStrmChan->GetSrcdocData(aSrcdocData); |
3572 | 0 | } |
3573 | 0 | } |
3574 | 0 | aSrcdocData = VoidString(); |
3575 | 0 | return NS_OK; |
3576 | 0 | } |
3577 | | |
3578 | | Element* |
3579 | | nsIDocument::GetActiveElement() |
3580 | 0 | { |
3581 | 0 | // Get the focused element. |
3582 | 0 | Element* focusedElement = GetRetargetedFocusedElement(); |
3583 | 0 | if (focusedElement) { |
3584 | 0 | return focusedElement; |
3585 | 0 | } |
3586 | 0 | |
3587 | 0 | // No focused element anywhere in this document. Try to get the BODY. |
3588 | 0 | if (IsHTMLOrXHTML()) { |
3589 | 0 | // Because of IE compatibility, return null when html document doesn't have |
3590 | 0 | // a body. |
3591 | 0 | return AsHTMLDocument()->GetBody(); |
3592 | 0 | } |
3593 | 0 | |
3594 | 0 | // If we couldn't get a BODY, return the root element. |
3595 | 0 | return GetDocumentElement(); |
3596 | 0 | } |
3597 | | |
3598 | | Element* |
3599 | | nsIDocument::GetCurrentScript() |
3600 | 0 | { |
3601 | 0 | nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript())); |
3602 | 0 | return el; |
3603 | 0 | } |
3604 | | |
3605 | | nsresult |
3606 | | nsIDocument::NodesFromRectHelper(float aX, float aY, |
3607 | | float aTopSize, float aRightSize, |
3608 | | float aBottomSize, float aLeftSize, |
3609 | | bool aIgnoreRootScrollFrame, |
3610 | | bool aFlushLayout, |
3611 | | nsINodeList** aReturn) |
3612 | 0 | { |
3613 | 0 | NS_ENSURE_ARG_POINTER(aReturn); |
3614 | 0 |
|
3615 | 0 | nsSimpleContentList* elements = new nsSimpleContentList(this); |
3616 | 0 | NS_ADDREF(elements); |
3617 | 0 | *aReturn = elements; |
3618 | 0 |
|
3619 | 0 | // Following the same behavior of elementFromPoint, |
3620 | 0 | // we don't return anything if either coord is negative |
3621 | 0 | if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) |
3622 | 0 | return NS_OK; |
3623 | 0 | |
3624 | 0 | nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize); |
3625 | 0 | nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize); |
3626 | 0 | nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1; |
3627 | 0 | nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1; |
3628 | 0 |
|
3629 | 0 | nsRect rect(x, y, w, h); |
3630 | 0 |
|
3631 | 0 | // Make sure the layout information we get is up-to-date, and |
3632 | 0 | // ensure we get a root frame (for everything but XUL) |
3633 | 0 | if (aFlushLayout) { |
3634 | 0 | FlushPendingNotifications(FlushType::Layout); |
3635 | 0 | } |
3636 | 0 |
|
3637 | 0 | nsIPresShell *ps = GetShell(); |
3638 | 0 | NS_ENSURE_STATE(ps); |
3639 | 0 | nsIFrame *rootFrame = ps->GetRootFrame(); |
3640 | 0 |
|
3641 | 0 | // XUL docs, unlike HTML, have no frame tree until everything's done loading |
3642 | 0 | if (!rootFrame) |
3643 | 0 | return NS_OK; // return nothing to premature XUL callers as a reminder to wait |
3644 | 0 | |
3645 | 0 | AutoTArray<nsIFrame*,8> outFrames; |
3646 | 0 | nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames, |
3647 | 0 | nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC | |
3648 | 0 | (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); |
3649 | 0 |
|
3650 | 0 | // Used to filter out repeated elements in sequence. |
3651 | 0 | nsIContent* lastAdded = nullptr; |
3652 | 0 |
|
3653 | 0 | for (uint32_t i = 0; i < outFrames.Length(); i++) { |
3654 | 0 | nsIContent* node = GetContentInThisDocument(outFrames[i]); |
3655 | 0 |
|
3656 | 0 | if (node && !node->IsElement() && !node->IsText()) { |
3657 | 0 | // We have a node that isn't an element or a text node, |
3658 | 0 | // use its parent content instead. |
3659 | 0 | node = node->GetParent(); |
3660 | 0 | } |
3661 | 0 | if (node && node != lastAdded) { |
3662 | 0 | elements->AppendElement(node); |
3663 | 0 | lastAdded = node; |
3664 | 0 | } |
3665 | 0 | } |
3666 | 0 |
|
3667 | 0 | return NS_OK; |
3668 | 0 | } |
3669 | | |
3670 | | void |
3671 | | nsIDocument::ReleaseCapture() const |
3672 | 0 | { |
3673 | 0 | // only release the capture if the caller can access it. This prevents a |
3674 | 0 | // page from stopping a scrollbar grab for example. |
3675 | 0 | nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent(); |
3676 | 0 | if (node && nsContentUtils::CanCallerAccess(node)) { |
3677 | 0 | nsIPresShell::SetCapturingContent(nullptr, 0); |
3678 | 0 | } |
3679 | 0 | } |
3680 | | |
3681 | | already_AddRefed<nsIURI> |
3682 | | nsIDocument::GetBaseURI(bool aTryUseXHRDocBaseURI) const |
3683 | 0 | { |
3684 | 0 | nsCOMPtr<nsIURI> uri; |
3685 | 0 | if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) { |
3686 | 0 | uri = mChromeXHRDocBaseURI; |
3687 | 0 | } else { |
3688 | 0 | uri = GetDocBaseURI(); |
3689 | 0 | } |
3690 | 0 |
|
3691 | 0 | return uri.forget(); |
3692 | 0 | } |
3693 | | |
3694 | | void |
3695 | | nsIDocument::SetBaseURI(nsIURI* aURI) |
3696 | 0 | { |
3697 | 0 | if (!aURI && !mDocumentBaseURI) { |
3698 | 0 | return; |
3699 | 0 | } |
3700 | 0 | |
3701 | 0 | // Don't do anything if the URI wasn't actually changed. |
3702 | 0 | if (aURI && mDocumentBaseURI) { |
3703 | 0 | bool equalBases = false; |
3704 | 0 | mDocumentBaseURI->Equals(aURI, &equalBases); |
3705 | 0 | if (equalBases) { |
3706 | 0 | return; |
3707 | 0 | } |
3708 | 0 | } |
3709 | 0 | |
3710 | 0 | mDocumentBaseURI = aURI; |
3711 | 0 | RefreshLinkHrefs(); |
3712 | 0 | } |
3713 | | |
3714 | | URLExtraData* |
3715 | | nsIDocument::DefaultStyleAttrURLData() |
3716 | 0 | { |
3717 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3718 | 0 | nsIURI* baseURI = GetDocBaseURI(); |
3719 | 0 | nsIURI* docURI = GetDocumentURI(); |
3720 | 0 | nsIPrincipal* principal = NodePrincipal(); |
3721 | 0 | mozilla::net::ReferrerPolicy policy = GetReferrerPolicy(); |
3722 | 0 | if (!mCachedURLData || |
3723 | 0 | mCachedURLData->BaseURI() != baseURI || |
3724 | 0 | mCachedURLData->GetReferrer() != docURI || |
3725 | 0 | mCachedURLData->GetReferrerPolicy() != policy || |
3726 | 0 | mCachedURLData->GetPrincipal() != principal) { |
3727 | 0 | mCachedURLData = new URLExtraData(baseURI, docURI, principal, policy); |
3728 | 0 | } |
3729 | 0 | return mCachedURLData; |
3730 | 0 | } |
3731 | | |
3732 | | void |
3733 | | nsIDocument::SetDocumentCharacterSet(NotNull<const Encoding*> aEncoding) |
3734 | 0 | { |
3735 | 0 | if (mCharacterSet != aEncoding) { |
3736 | 0 | mCharacterSet = aEncoding; |
3737 | 0 | mEncodingMenuDisabled = aEncoding == UTF_8_ENCODING; |
3738 | 0 |
|
3739 | 0 | if (nsPresContext* context = GetPresContext()) { |
3740 | 0 | context->DispatchCharSetChange(aEncoding); |
3741 | 0 | } |
3742 | 0 | } |
3743 | 0 | } |
3744 | | |
3745 | | void |
3746 | | nsIDocument::GetSandboxFlagsAsString(nsAString& aFlags) |
3747 | 0 | { |
3748 | 0 | nsContentUtils::SandboxFlagsToString(mSandboxFlags, aFlags); |
3749 | 0 | } |
3750 | | |
3751 | | void |
3752 | | nsIDocument::GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const |
3753 | 0 | { |
3754 | 0 | aData.Truncate(); |
3755 | 0 | const nsDocHeaderData* data = mHeaderData; |
3756 | 0 | while (data) { |
3757 | 0 | if (data->mField == aHeaderField) { |
3758 | 0 | aData = data->mData; |
3759 | 0 |
|
3760 | 0 | break; |
3761 | 0 | } |
3762 | 0 | data = data->mNext; |
3763 | 0 | } |
3764 | 0 | } |
3765 | | |
3766 | | void |
3767 | | nsIDocument::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) |
3768 | 0 | { |
3769 | 0 | if (!aHeaderField) { |
3770 | 0 | NS_ERROR("null headerField"); |
3771 | 0 | return; |
3772 | 0 | } |
3773 | 0 |
|
3774 | 0 | if (!mHeaderData) { |
3775 | 0 | if (!aData.IsEmpty()) { // don't bother storing empty string |
3776 | 0 | mHeaderData = new nsDocHeaderData(aHeaderField, aData); |
3777 | 0 | } |
3778 | 0 | } |
3779 | 0 | else { |
3780 | 0 | nsDocHeaderData* data = mHeaderData; |
3781 | 0 | nsDocHeaderData** lastPtr = &mHeaderData; |
3782 | 0 | bool found = false; |
3783 | 0 | do { // look for existing and replace |
3784 | 0 | if (data->mField == aHeaderField) { |
3785 | 0 | if (!aData.IsEmpty()) { |
3786 | 0 | data->mData.Assign(aData); |
3787 | 0 | } |
3788 | 0 | else { // don't store empty string |
3789 | 0 | *lastPtr = data->mNext; |
3790 | 0 | data->mNext = nullptr; |
3791 | 0 | delete data; |
3792 | 0 | } |
3793 | 0 | found = true; |
3794 | 0 |
|
3795 | 0 | break; |
3796 | 0 | } |
3797 | 0 | lastPtr = &(data->mNext); |
3798 | 0 | data = *lastPtr; |
3799 | 0 | } while (data); |
3800 | 0 |
|
3801 | 0 | if (!aData.IsEmpty() && !found) { |
3802 | 0 | // didn't find, append |
3803 | 0 | *lastPtr = new nsDocHeaderData(aHeaderField, aData); |
3804 | 0 | } |
3805 | 0 | } |
3806 | 0 |
|
3807 | 0 | if (aHeaderField == nsGkAtoms::headerContentLanguage) { |
3808 | 0 | CopyUTF16toUTF8(aData, mContentLanguage); |
3809 | 0 | } |
3810 | 0 |
|
3811 | 0 | if (aHeaderField == nsGkAtoms::headerDefaultStyle) { |
3812 | 0 | SetPreferredStyleSheetSet(aData); |
3813 | 0 | } |
3814 | 0 |
|
3815 | 0 | if (aHeaderField == nsGkAtoms::refresh) { |
3816 | 0 | // We get into this code before we have a script global yet, so get to |
3817 | 0 | // our container via mDocumentContainer. |
3818 | 0 | nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer); |
3819 | 0 | if (refresher) { |
3820 | 0 | // Note: using mDocumentURI instead of mBaseURI here, for consistency |
3821 | 0 | // (used to just use the current URI of our webnavigation, but that |
3822 | 0 | // should really be the same thing). Note that this code can run |
3823 | 0 | // before the current URI of the webnavigation has been updated, so we |
3824 | 0 | // can't assert equality here. |
3825 | 0 | refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(), |
3826 | 0 | NS_ConvertUTF16toUTF8(aData)); |
3827 | 0 | } |
3828 | 0 | } |
3829 | 0 |
|
3830 | 0 | if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl && |
3831 | 0 | mAllowDNSPrefetch) { |
3832 | 0 | // Chromium treats any value other than 'on' (case insensitive) as 'off'. |
3833 | 0 | mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on"); |
3834 | 0 | } |
3835 | 0 |
|
3836 | 0 | if (aHeaderField == nsGkAtoms::viewport || |
3837 | 0 | aHeaderField == nsGkAtoms::handheldFriendly || |
3838 | 0 | aHeaderField == nsGkAtoms::viewport_minimum_scale || |
3839 | 0 | aHeaderField == nsGkAtoms::viewport_maximum_scale || |
3840 | 0 | aHeaderField == nsGkAtoms::viewport_initial_scale || |
3841 | 0 | aHeaderField == nsGkAtoms::viewport_height || |
3842 | 0 | aHeaderField == nsGkAtoms::viewport_width || |
3843 | 0 | aHeaderField == nsGkAtoms::viewport_user_scalable) { |
3844 | 0 | mViewportType = Unknown; |
3845 | 0 | mViewportOverflowType = ViewportOverflowType::NoOverflow; |
3846 | 0 | } |
3847 | 0 |
|
3848 | 0 | // Referrer policy spec says to ignore any empty referrer policies. |
3849 | 0 | if (aHeaderField == nsGkAtoms::referrer && !aData.IsEmpty()) { |
3850 | 0 | enum mozilla::net::ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aData); |
3851 | 0 | // If policy is not the empty string, then set element's node document's |
3852 | 0 | // referrer policy to policy |
3853 | 0 | if (policy != mozilla::net::RP_Unset) { |
3854 | 0 | // Referrer policy spec (section 6.1) says that we always use the newest |
3855 | 0 | // referrer policy we find |
3856 | 0 | mReferrerPolicy = policy; |
3857 | 0 | mReferrerPolicySet = true; |
3858 | 0 | } |
3859 | 0 | } |
3860 | 0 |
|
3861 | 0 | if (aHeaderField == nsGkAtoms::headerReferrerPolicy && !aData.IsEmpty()) { |
3862 | 0 | enum mozilla::net::ReferrerPolicy policy = nsContentUtils::GetReferrerPolicyFromHeader(aData); |
3863 | 0 | if (policy != mozilla::net::RP_Unset) { |
3864 | 0 | mReferrerPolicy = policy; |
3865 | 0 | mReferrerPolicySet = true; |
3866 | 0 | } |
3867 | 0 | } |
3868 | 0 |
|
3869 | 0 | } |
3870 | | void |
3871 | | nsDocument::TryChannelCharset(nsIChannel *aChannel, |
3872 | | int32_t& aCharsetSource, |
3873 | | NotNull<const Encoding*>& aEncoding, |
3874 | | nsHtml5TreeOpExecutor* aExecutor) |
3875 | 0 | { |
3876 | 0 | if (aChannel) { |
3877 | 0 | nsAutoCString charsetVal; |
3878 | 0 | nsresult rv = aChannel->GetContentCharset(charsetVal); |
3879 | 0 | if (NS_SUCCEEDED(rv)) { |
3880 | 0 | const Encoding* preferred = Encoding::ForLabel(charsetVal); |
3881 | 0 | if (preferred) { |
3882 | 0 | aEncoding = WrapNotNull(preferred); |
3883 | 0 | aCharsetSource = kCharsetFromChannel; |
3884 | 0 | return; |
3885 | 0 | } else if (aExecutor && !charsetVal.IsEmpty()) { |
3886 | 0 | aExecutor->ComplainAboutBogusProtocolCharset(this); |
3887 | 0 | } |
3888 | 0 | } |
3889 | 0 | } |
3890 | 0 | } |
3891 | | |
3892 | | static inline void |
3893 | | AssertNoStaleServoDataIn(const nsINode& aSubtreeRoot) |
3894 | 0 | { |
3895 | | #ifdef DEBUG |
3896 | | for (const nsINode* node = &aSubtreeRoot; |
3897 | | node; |
3898 | | node = node->GetNextNode(&aSubtreeRoot)) { |
3899 | | const Element* element = Element::FromNode(node); |
3900 | | if (!element) { |
3901 | | continue; |
3902 | | } |
3903 | | MOZ_ASSERT(!element->HasServoData()); |
3904 | | if (auto* shadow = element->GetShadowRoot()) { |
3905 | | AssertNoStaleServoDataIn(*shadow); |
3906 | | } |
3907 | | if (nsXBLBinding* binding = element->GetXBLBinding()) { |
3908 | | if (nsXBLBinding* bindingWithContent = binding->GetBindingWithContent()) { |
3909 | | nsIContent* content = bindingWithContent->GetAnonymousContent(); |
3910 | | MOZ_ASSERT(!content->AsElement()->HasServoData()); |
3911 | | for (nsINode* child = content->GetFirstChild(); |
3912 | | child; |
3913 | | child = child->GetNextSibling()) { |
3914 | | AssertNoStaleServoDataIn(*child); |
3915 | | } |
3916 | | } |
3917 | | } |
3918 | | } |
3919 | | #endif |
3920 | | } |
3921 | | |
3922 | | already_AddRefed<nsIPresShell> |
3923 | | nsIDocument::CreateShell(nsPresContext* aContext, |
3924 | | nsViewManager* aViewManager, |
3925 | | UniquePtr<ServoStyleSet> aStyleSet) |
3926 | 0 | { |
3927 | 0 | NS_ASSERTION(!mPresShell, "We have a presshell already!"); |
3928 | 0 |
|
3929 | 0 | NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr); |
3930 | 0 |
|
3931 | 0 | FillStyleSet(aStyleSet.get()); |
3932 | 0 | AssertNoStaleServoDataIn(static_cast<nsINode&>(*this)); |
3933 | 0 |
|
3934 | 0 | RefPtr<PresShell> shell = new PresShell; |
3935 | 0 | // Note: we don't hold a ref to the shell (it holds a ref to us) |
3936 | 0 | mPresShell = shell; |
3937 | 0 | shell->Init(this, aContext, aViewManager, std::move(aStyleSet)); |
3938 | 0 |
|
3939 | 0 | // Make sure to never paint if we belong to an invisible DocShell. |
3940 | 0 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
3941 | 0 | if (docShell && docShell->IsInvisible()) |
3942 | 0 | shell->SetNeverPainting(true); |
3943 | 0 |
|
3944 | 0 | MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p with PressShell %p and DocShell %p", |
3945 | 0 | this, shell.get(), docShell.get())); |
3946 | 0 |
|
3947 | 0 | mExternalResourceMap.ShowViewers(); |
3948 | 0 |
|
3949 | 0 | UpdateFrameRequestCallbackSchedulingState(); |
3950 | 0 |
|
3951 | 0 | // Now that we have a shell, we might have @font-face rules (the presence of a |
3952 | 0 | // shell may change which rules apply to us). We don't need to do anything |
3953 | 0 | // like EnsureStyleFlush or such, there's nothing to update yet and when stuff |
3954 | 0 | // is ready to update we'll flush the font set. |
3955 | 0 | MarkUserFontSetDirty(); |
3956 | 0 |
|
3957 | 0 | return shell.forget(); |
3958 | 0 | } |
3959 | | |
3960 | | void |
3961 | | nsIDocument::UpdateFrameRequestCallbackSchedulingState(nsIPresShell* aOldShell) |
3962 | 0 | { |
3963 | 0 | // If the condition for shouldBeScheduled changes to depend on some other |
3964 | 0 | // variable, add UpdateFrameRequestCallbackSchedulingState() calls to the |
3965 | 0 | // places where that variable can change. |
3966 | 0 | bool shouldBeScheduled = |
3967 | 0 | mPresShell && IsEventHandlingEnabled() && !mFrameRequestCallbacks.IsEmpty(); |
3968 | 0 | if (shouldBeScheduled == mFrameRequestCallbacksScheduled) { |
3969 | 0 | // nothing to do |
3970 | 0 | return; |
3971 | 0 | } |
3972 | 0 | |
3973 | 0 | nsIPresShell* presShell = aOldShell ? aOldShell : mPresShell; |
3974 | 0 | MOZ_RELEASE_ASSERT(presShell); |
3975 | 0 |
|
3976 | 0 | nsRefreshDriver* rd = presShell->GetPresContext()->RefreshDriver(); |
3977 | 0 | if (shouldBeScheduled) { |
3978 | 0 | rd->ScheduleFrameRequestCallbacks(this); |
3979 | 0 | } else { |
3980 | 0 | rd->RevokeFrameRequestCallbacks(this); |
3981 | 0 | } |
3982 | 0 |
|
3983 | 0 | mFrameRequestCallbacksScheduled = shouldBeScheduled; |
3984 | 0 | } |
3985 | | |
3986 | | void |
3987 | | nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks) |
3988 | 0 | { |
3989 | 0 | aCallbacks.AppendElements(mFrameRequestCallbacks); |
3990 | 0 | mFrameRequestCallbacks.Clear(); |
3991 | 0 | // No need to manually remove ourselves from the refresh driver; it will |
3992 | 0 | // handle that part. But we do have to update our state. |
3993 | 0 | mFrameRequestCallbacksScheduled = false; |
3994 | 0 | } |
3995 | | |
3996 | | bool |
3997 | | nsIDocument::ShouldThrottleFrameRequests() |
3998 | 0 | { |
3999 | 0 | if (mStaticCloneCount > 0) { |
4000 | 0 | // Even if we're not visible, a static clone may be, so run at full speed. |
4001 | 0 | return false; |
4002 | 0 | } |
4003 | 0 | |
4004 | 0 | if (Hidden()) { |
4005 | 0 | // We're not visible (probably in a background tab or the bf cache). |
4006 | 0 | return true; |
4007 | 0 | } |
4008 | 0 | |
4009 | 0 | if (!mPresShell) { |
4010 | 0 | return false; // Can't do anything smarter. |
4011 | 0 | } |
4012 | 0 | |
4013 | 0 | nsIFrame* frame = mPresShell->GetRootFrame(); |
4014 | 0 | if (!frame) { |
4015 | 0 | return false; // Can't do anything smarter. |
4016 | 0 | } |
4017 | 0 | |
4018 | 0 | nsIFrame* displayRootFrame = nsLayoutUtils::GetDisplayRootFrame(frame); |
4019 | 0 | if (!displayRootFrame) { |
4020 | 0 | return false; // Can't do anything smarter. |
4021 | 0 | } |
4022 | 0 | |
4023 | 0 | if (!displayRootFrame->DidPaintPresShell(mPresShell)) { |
4024 | 0 | // We didn't get painted during the last paint, so we're not visible. |
4025 | 0 | // Throttle. Note that because we have to paint this document at least |
4026 | 0 | // once to unthrottle it, we will drop one requestAnimationFrame frame |
4027 | 0 | // when a document that previously wasn't visible scrolls into view. This |
4028 | 0 | // is acceptable since it would happen outside the viewport on APZ |
4029 | 0 | // platforms and is unlikely to be human-perceivable on non-APZ platforms. |
4030 | 0 | return true; |
4031 | 0 | } |
4032 | 0 | |
4033 | 0 | // We got painted during the last paint, so run at full speed. |
4034 | 0 | return false; |
4035 | 0 | } |
4036 | | |
4037 | | void |
4038 | | nsIDocument::DeleteShell() |
4039 | 0 | { |
4040 | 0 | mExternalResourceMap.HideViewers(); |
4041 | 0 | if (nsPresContext* presContext = mPresShell->GetPresContext()) { |
4042 | 0 | presContext->RefreshDriver()->CancelPendingFullscreenEvents(this); |
4043 | 0 | } |
4044 | 0 |
|
4045 | 0 | // When our shell goes away, request that all our images be immediately |
4046 | 0 | // discarded, so we don't carry around decoded image data for a document we |
4047 | 0 | // no longer intend to paint. |
4048 | 0 | ImageTracker()->RequestDiscardAll(); |
4049 | 0 |
|
4050 | 0 | // Now that we no longer have a shell, we need to forget about any FontFace |
4051 | 0 | // objects for @font-face rules that came from the style set. There's no need |
4052 | 0 | // to call EnsureStyleFlush either, the shell is going away anyway, so there's |
4053 | 0 | // no point on it. |
4054 | 0 | MarkUserFontSetDirty(); |
4055 | 0 |
|
4056 | 0 | nsIPresShell* oldShell = mPresShell; |
4057 | 0 | mPresShell = nullptr; |
4058 | 0 | UpdateFrameRequestCallbackSchedulingState(oldShell); |
4059 | 0 | mStyleSetFilled = false; |
4060 | 0 |
|
4061 | 0 | ClearStaleServoData(); |
4062 | 0 | AssertNoStaleServoDataIn(static_cast<nsINode&>(*this)); |
4063 | 0 | } |
4064 | | |
4065 | | void |
4066 | | nsIDocument::SetBFCacheEntry(nsIBFCacheEntry* aEntry) |
4067 | 0 | { |
4068 | 0 | MOZ_ASSERT(IsBFCachingAllowed() || !aEntry, "You should have checked!"); |
4069 | 0 |
|
4070 | 0 | if (mPresShell) { |
4071 | 0 | if (aEntry) { |
4072 | 0 | mPresShell->StopObservingRefreshDriver(); |
4073 | 0 | } else if (mBFCacheEntry) { |
4074 | 0 | mPresShell->StartObservingRefreshDriver(); |
4075 | 0 | } |
4076 | 0 | } |
4077 | 0 | mBFCacheEntry = aEntry; |
4078 | 0 | } |
4079 | | |
4080 | | static void |
4081 | | SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) |
4082 | 0 | { |
4083 | 0 | SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry); |
4084 | 0 |
|
4085 | 0 | NS_RELEASE(e->mKey); |
4086 | 0 | if (e->mSubDocument) { |
4087 | 0 | e->mSubDocument->SetParentDocument(nullptr); |
4088 | 0 | NS_RELEASE(e->mSubDocument); |
4089 | 0 | } |
4090 | 0 | } |
4091 | | |
4092 | | static void |
4093 | | SubDocInitEntry(PLDHashEntryHdr *entry, const void *key) |
4094 | 0 | { |
4095 | 0 | SubDocMapEntry *e = |
4096 | 0 | const_cast<SubDocMapEntry *> |
4097 | 0 | (static_cast<const SubDocMapEntry *>(entry)); |
4098 | 0 |
|
4099 | 0 | e->mKey = const_cast<Element*>(static_cast<const Element*>(key)); |
4100 | 0 | NS_ADDREF(e->mKey); |
4101 | 0 |
|
4102 | 0 | e->mSubDocument = nullptr; |
4103 | 0 | } |
4104 | | |
4105 | | nsresult |
4106 | | nsIDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc) |
4107 | 0 | { |
4108 | 0 | NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED); |
4109 | 0 |
|
4110 | 0 | if (!aSubDoc) { |
4111 | 0 | // aSubDoc is nullptr, remove the mapping |
4112 | 0 |
|
4113 | 0 | if (mSubDocuments) { |
4114 | 0 | nsIDocument* subDoc = GetSubDocumentFor(aElement); |
4115 | 0 | if (subDoc) { |
4116 | 0 | subDoc->SetAllowPaymentRequest(false); |
4117 | 0 | } |
4118 | 0 | mSubDocuments->Remove(aElement); |
4119 | 0 | } |
4120 | 0 | } else { |
4121 | 0 | if (!mSubDocuments) { |
4122 | 0 | // Create a new hashtable |
4123 | 0 |
|
4124 | 0 | static const PLDHashTableOps hash_table_ops = |
4125 | 0 | { |
4126 | 0 | PLDHashTable::HashVoidPtrKeyStub, |
4127 | 0 | PLDHashTable::MatchEntryStub, |
4128 | 0 | PLDHashTable::MoveEntryStub, |
4129 | 0 | SubDocClearEntry, |
4130 | 0 | SubDocInitEntry |
4131 | 0 | }; |
4132 | 0 |
|
4133 | 0 | mSubDocuments = new PLDHashTable(&hash_table_ops, sizeof(SubDocMapEntry)); |
4134 | 0 | } |
4135 | 0 |
|
4136 | 0 | // Add a mapping to the hash table |
4137 | 0 | auto entry = |
4138 | 0 | static_cast<SubDocMapEntry*>(mSubDocuments->Add(aElement, fallible)); |
4139 | 0 |
|
4140 | 0 | if (!entry) { |
4141 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
4142 | 0 | } |
4143 | 0 | |
4144 | 0 | if (entry->mSubDocument) { |
4145 | 0 | entry->mSubDocument->SetAllowPaymentRequest(false); |
4146 | 0 | entry->mSubDocument->SetParentDocument(nullptr); |
4147 | 0 |
|
4148 | 0 | // Release the old sub document |
4149 | 0 | NS_RELEASE(entry->mSubDocument); |
4150 | 0 | } |
4151 | 0 |
|
4152 | 0 | entry->mSubDocument = aSubDoc; |
4153 | 0 | NS_ADDREF(entry->mSubDocument); |
4154 | 0 |
|
4155 | 0 | // set allowpaymentrequest for the binding subdocument |
4156 | 0 | if (!mAllowPaymentRequest) { |
4157 | 0 | aSubDoc->SetAllowPaymentRequest(false); |
4158 | 0 | } else { |
4159 | 0 | nsresult rv = nsContentUtils::CheckSameOrigin(aElement, aSubDoc); |
4160 | 0 | if (NS_SUCCEEDED(rv)) { |
4161 | 0 | aSubDoc->SetAllowPaymentRequest(true); |
4162 | 0 | } else { |
4163 | 0 | if (aElement->IsHTMLElement(nsGkAtoms::iframe) && |
4164 | 0 | aElement->GetBoolAttr(nsGkAtoms::allowpaymentrequest)) { |
4165 | 0 | aSubDoc->SetAllowPaymentRequest(true); |
4166 | 0 | } else { |
4167 | 0 | aSubDoc->SetAllowPaymentRequest(false); |
4168 | 0 | } |
4169 | 0 | } |
4170 | 0 | } |
4171 | 0 |
|
4172 | 0 | aSubDoc->SetParentDocument(this); |
4173 | 0 | } |
4174 | 0 |
|
4175 | 0 | return NS_OK; |
4176 | 0 | } |
4177 | | |
4178 | | nsIDocument* |
4179 | | nsIDocument::GetSubDocumentFor(nsIContent *aContent) const |
4180 | 0 | { |
4181 | 0 | if (mSubDocuments && aContent->IsElement()) { |
4182 | 0 | auto entry = static_cast<SubDocMapEntry*> |
4183 | 0 | (mSubDocuments->Search(aContent->AsElement())); |
4184 | 0 |
|
4185 | 0 | if (entry) { |
4186 | 0 | return entry->mSubDocument; |
4187 | 0 | } |
4188 | 0 | } |
4189 | 0 | |
4190 | 0 | return nullptr; |
4191 | 0 | } |
4192 | | |
4193 | | Element* |
4194 | | nsIDocument::FindContentForSubDocument(nsIDocument *aDocument) const |
4195 | 0 | { |
4196 | 0 | NS_ENSURE_TRUE(aDocument, nullptr); |
4197 | 0 |
|
4198 | 0 | if (!mSubDocuments) { |
4199 | 0 | return nullptr; |
4200 | 0 | } |
4201 | 0 | |
4202 | 0 | for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) { |
4203 | 0 | auto entry = static_cast<SubDocMapEntry*>(iter.Get()); |
4204 | 0 | if (entry->mSubDocument == aDocument) { |
4205 | 0 | return entry->mKey; |
4206 | 0 | } |
4207 | 0 | } |
4208 | 0 | return nullptr; |
4209 | 0 | } |
4210 | | |
4211 | | bool |
4212 | | nsIDocument::IsNodeOfType(uint32_t aFlags) const |
4213 | 0 | { |
4214 | 0 | return false; |
4215 | 0 | } |
4216 | | |
4217 | | Element* |
4218 | | nsIDocument::GetRootElement() const |
4219 | 0 | { |
4220 | 0 | return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ? |
4221 | 0 | mCachedRootElement : GetRootElementInternal(); |
4222 | 0 | } |
4223 | | |
4224 | | nsIContent* |
4225 | | nsIDocument::GetUnfocusedKeyEventTarget() |
4226 | 0 | { |
4227 | 0 | return GetRootElement(); |
4228 | 0 | } |
4229 | | |
4230 | | Element* |
4231 | | nsIDocument::GetRootElementInternal() const |
4232 | 0 | { |
4233 | 0 | // We invoke GetRootElement() immediately before the servo traversal, so we |
4234 | 0 | // should always have a cache hit from Servo. |
4235 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
4236 | 0 |
|
4237 | 0 | // Loop backwards because any non-elements, such as doctypes and PIs |
4238 | 0 | // are likely to appear before the root element. |
4239 | 0 | for (nsIContent* child = GetLastChild(); child; |
4240 | 0 | child = child->GetPreviousSibling()) { |
4241 | 0 | if (Element* element = Element::FromNode(child)) { |
4242 | 0 | const_cast<nsIDocument*>(this)->mCachedRootElement = element; |
4243 | 0 | return element; |
4244 | 0 | } |
4245 | 0 | } |
4246 | 0 |
|
4247 | 0 | const_cast<nsIDocument*>(this)->mCachedRootElement = nullptr; |
4248 | 0 | return nullptr; |
4249 | 0 | } |
4250 | | |
4251 | | nsresult |
4252 | | nsIDocument::InsertChildBefore(nsIContent* aKid, |
4253 | | nsIContent* aBeforeThis, |
4254 | | bool aNotify) |
4255 | 0 | { |
4256 | 0 | if (aKid->IsElement() && GetRootElement()) { |
4257 | 0 | NS_WARNING("Inserting root element when we already have one"); |
4258 | 0 | return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
4259 | 0 | } |
4260 | 0 |
|
4261 | 0 | return nsINode::InsertChildBefore(aKid, aBeforeThis, aNotify); |
4262 | 0 | } |
4263 | | |
4264 | | void |
4265 | | nsIDocument::RemoveChildNode(nsIContent* aKid, bool aNotify) |
4266 | 0 | { |
4267 | 0 | if (aKid->IsElement()) { |
4268 | 0 | // Destroy the link map up front before we mess with the child list. |
4269 | 0 | DestroyElementMaps(); |
4270 | 0 | } |
4271 | 0 |
|
4272 | 0 | // Preemptively clear mCachedRootElement, since we may be about to remove it |
4273 | 0 | // from our child list, and we don't want to return this maybe-obsolete value |
4274 | 0 | // from any GetRootElement() calls that happen inside of RemoveChildNode(). |
4275 | 0 | // (NOTE: for this to be useful, RemoveChildNode() must NOT trigger any |
4276 | 0 | // GetRootElement() calls until after it's removed the child from mChildren. |
4277 | 0 | // Any call before that point would restore this soon-to-be-obsolete cached |
4278 | 0 | // answer, and our clearing here would be fruitless.) |
4279 | 0 | mCachedRootElement = nullptr; |
4280 | 0 | nsINode::RemoveChildNode(aKid, aNotify); |
4281 | 0 | MOZ_ASSERT(mCachedRootElement != aKid, |
4282 | 0 | "Stale pointer in mCachedRootElement, after we tried to clear it " |
4283 | 0 | "(maybe somebody called GetRootElement() too early?)"); |
4284 | 0 | } |
4285 | | |
4286 | | void |
4287 | | nsIDocument::AddStyleSheetToStyleSets(StyleSheet* aSheet) |
4288 | 0 | { |
4289 | 0 | if (nsIPresShell* shell = GetShell()) { |
4290 | 0 | shell->StyleSet()->AddDocStyleSheet(aSheet, this); |
4291 | 0 | shell->ApplicableStylesChanged(); |
4292 | 0 | } |
4293 | 0 | } |
4294 | | |
4295 | | #define DO_STYLESHEET_NOTIFICATION(className, type, memberName, argName) \ |
4296 | 0 | do { \ |
4297 | 0 | className##Init init; \ |
4298 | 0 | init.mBubbles = true; \ |
4299 | 0 | init.mCancelable = true; \ |
4300 | 0 | init.mStylesheet = aSheet; \ |
4301 | 0 | init.memberName = argName; \ |
4302 | 0 | \ |
4303 | 0 | RefPtr<className> event = \ |
4304 | 0 | className::Constructor(this, NS_LITERAL_STRING(type), init); \ |
4305 | 0 | event->SetTrusted(true); \ |
4306 | 0 | event->SetTarget(this); \ |
4307 | 0 | RefPtr<AsyncEventDispatcher> asyncDispatcher = \ |
4308 | 0 | new AsyncEventDispatcher(this, event); \ |
4309 | 0 | asyncDispatcher->mOnlyChromeDispatch = ChromeOnlyDispatch::eYes; \ |
4310 | 0 | asyncDispatcher->PostDOMEvent(); \ |
4311 | 0 | } while (0); |
4312 | | |
4313 | | void |
4314 | | nsIDocument::NotifyStyleSheetAdded(StyleSheet* aSheet, bool aDocumentSheet) |
4315 | 0 | { |
4316 | 0 | if (StyleSheetChangeEventsEnabled()) { |
4317 | 0 | DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent, |
4318 | 0 | "StyleSheetAdded", |
4319 | 0 | mDocumentSheet, |
4320 | 0 | aDocumentSheet); |
4321 | 0 | } |
4322 | 0 | } |
4323 | | |
4324 | | void |
4325 | | nsIDocument::NotifyStyleSheetRemoved(StyleSheet* aSheet, bool aDocumentSheet) |
4326 | 0 | { |
4327 | 0 | if (StyleSheetChangeEventsEnabled()) { |
4328 | 0 | DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent, |
4329 | 0 | "StyleSheetRemoved", |
4330 | 0 | mDocumentSheet, |
4331 | 0 | aDocumentSheet); |
4332 | 0 | } |
4333 | 0 | } |
4334 | | |
4335 | | void |
4336 | | nsIDocument::RemoveStyleSheetFromStyleSets(StyleSheet* aSheet) |
4337 | 0 | { |
4338 | 0 | if (nsIPresShell* shell = GetShell()) { |
4339 | 0 | shell->StyleSet()->RemoveDocStyleSheet(aSheet); |
4340 | 0 | shell->ApplicableStylesChanged(); |
4341 | 0 | } |
4342 | 0 | } |
4343 | | |
4344 | | void |
4345 | | nsIDocument::RemoveStyleSheet(StyleSheet* aSheet) |
4346 | 0 | { |
4347 | 0 | MOZ_ASSERT(aSheet); |
4348 | 0 | RefPtr<StyleSheet> sheet = DocumentOrShadowRoot::RemoveSheet(*aSheet); |
4349 | 0 |
|
4350 | 0 | if (!sheet) { |
4351 | 0 | NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found"); |
4352 | 0 | return; |
4353 | 0 | } |
4354 | 0 |
|
4355 | 0 | if (!mIsGoingAway) { |
4356 | 0 | if (sheet->IsApplicable()) { |
4357 | 0 | RemoveStyleSheetFromStyleSets(sheet); |
4358 | 0 | } |
4359 | 0 |
|
4360 | 0 | NotifyStyleSheetRemoved(sheet, true); |
4361 | 0 | } |
4362 | 0 |
|
4363 | 0 | sheet->ClearAssociatedDocumentOrShadowRoot(); |
4364 | 0 | } |
4365 | | |
4366 | | void |
4367 | | nsIDocument::UpdateStyleSheets(nsTArray<RefPtr<StyleSheet>>& aOldSheets, |
4368 | | nsTArray<RefPtr<StyleSheet>>& aNewSheets) |
4369 | 0 | { |
4370 | 0 | // XXX Need to set the sheet on the ownernode, if any |
4371 | 0 | MOZ_ASSERT(aOldSheets.Length() == aNewSheets.Length(), |
4372 | 0 | "The lists must be the same length!"); |
4373 | 0 | int32_t count = aOldSheets.Length(); |
4374 | 0 |
|
4375 | 0 | RefPtr<StyleSheet> oldSheet; |
4376 | 0 | int32_t i; |
4377 | 0 | for (i = 0; i < count; ++i) { |
4378 | 0 | oldSheet = aOldSheets[i]; |
4379 | 0 |
|
4380 | 0 | // First remove the old sheet. |
4381 | 0 | NS_ASSERTION(oldSheet, "None of the old sheets should be null"); |
4382 | 0 | int32_t oldIndex = mStyleSheets.IndexOf(oldSheet); |
4383 | 0 | RemoveStyleSheet(oldSheet); // This does the right notifications |
4384 | 0 |
|
4385 | 0 | // Now put the new one in its place. If it's null, just ignore it. |
4386 | 0 | StyleSheet* newSheet = aNewSheets[i]; |
4387 | 0 | if (newSheet) { |
4388 | 0 | DocumentOrShadowRoot::InsertSheetAt(oldIndex, *newSheet); |
4389 | 0 | if (newSheet->IsApplicable()) { |
4390 | 0 | AddStyleSheetToStyleSets(newSheet); |
4391 | 0 | } |
4392 | 0 |
|
4393 | 0 | NotifyStyleSheetAdded(newSheet, true); |
4394 | 0 | } |
4395 | 0 | } |
4396 | 0 | } |
4397 | | |
4398 | | void |
4399 | | nsIDocument::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) |
4400 | 0 | { |
4401 | 0 | DocumentOrShadowRoot::InsertSheetAt(aIndex, aSheet); |
4402 | 0 |
|
4403 | 0 | if (aSheet.IsApplicable()) { |
4404 | 0 | AddStyleSheetToStyleSets(&aSheet); |
4405 | 0 | } |
4406 | 0 |
|
4407 | 0 | NotifyStyleSheetAdded(&aSheet, true); |
4408 | 0 | } |
4409 | | |
4410 | | |
4411 | | void |
4412 | | nsIDocument::SetStyleSheetApplicableState(StyleSheet* aSheet, bool aApplicable) |
4413 | 0 | { |
4414 | 0 | MOZ_ASSERT(aSheet, "null arg"); |
4415 | 0 |
|
4416 | 0 | // If we're actually in the document style sheet list |
4417 | 0 | if (mStyleSheets.IndexOf(aSheet) != mStyleSheets.NoIndex) { |
4418 | 0 | if (aApplicable) { |
4419 | 0 | AddStyleSheetToStyleSets(aSheet); |
4420 | 0 | } else { |
4421 | 0 | RemoveStyleSheetFromStyleSets(aSheet); |
4422 | 0 | } |
4423 | 0 | } |
4424 | 0 |
|
4425 | 0 | if (StyleSheetChangeEventsEnabled()) { |
4426 | 0 | DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent, |
4427 | 0 | "StyleSheetApplicableStateChanged", |
4428 | 0 | mApplicable, |
4429 | 0 | aApplicable); |
4430 | 0 | } |
4431 | 0 |
|
4432 | 0 | if (!mSSApplicableStateNotificationPending) { |
4433 | 0 | MOZ_RELEASE_ASSERT(NS_IsMainThread()); |
4434 | 0 | nsCOMPtr<nsIRunnable> notification = |
4435 | 0 | NewRunnableMethod("nsIDocument::NotifyStyleSheetApplicableStateChanged", |
4436 | 0 | this, |
4437 | 0 | &nsIDocument::NotifyStyleSheetApplicableStateChanged); |
4438 | 0 | mSSApplicableStateNotificationPending = |
4439 | 0 | NS_SUCCEEDED( |
4440 | 0 | Dispatch(TaskCategory::Other, notification.forget())); |
4441 | 0 | } |
4442 | 0 | } |
4443 | | |
4444 | | void |
4445 | | nsIDocument::NotifyStyleSheetApplicableStateChanged() |
4446 | 0 | { |
4447 | 0 | mSSApplicableStateNotificationPending = false; |
4448 | 0 | nsCOMPtr<nsIObserverService> observerService = |
4449 | 0 | mozilla::services::GetObserverService(); |
4450 | 0 | if (observerService) { |
4451 | 0 | observerService->NotifyObservers(this, |
4452 | 0 | "style-sheet-applicable-state-changed", |
4453 | 0 | nullptr); |
4454 | 0 | } |
4455 | 0 | } |
4456 | | |
4457 | | static SheetType |
4458 | | ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType) |
4459 | 0 | { |
4460 | 0 | switch(aType) { |
4461 | 0 | case nsIDocument::eAgentSheet: |
4462 | 0 | return SheetType::Agent; |
4463 | 0 | case nsIDocument::eUserSheet: |
4464 | 0 | return SheetType::User; |
4465 | 0 | case nsIDocument::eAuthorSheet: |
4466 | 0 | return SheetType::Doc; |
4467 | 0 | default: |
4468 | 0 | MOZ_ASSERT(false, "wrong type"); |
4469 | 0 | // we must return something although this should never happen |
4470 | 0 | return SheetType::Count; |
4471 | 0 | } |
4472 | 0 | } |
4473 | | |
4474 | | static int32_t |
4475 | | FindSheet(const nsTArray<RefPtr<StyleSheet>>& aSheets, nsIURI* aSheetURI) |
4476 | 0 | { |
4477 | 0 | for (int32_t i = aSheets.Length() - 1; i >= 0; i-- ) { |
4478 | 0 | bool bEqual; |
4479 | 0 | nsIURI* uri = aSheets[i]->GetSheetURI(); |
4480 | 0 |
|
4481 | 0 | if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual) |
4482 | 0 | return i; |
4483 | 0 | } |
4484 | 0 |
|
4485 | 0 | return -1; |
4486 | 0 | } |
4487 | | |
4488 | | nsresult |
4489 | | nsIDocument::LoadAdditionalStyleSheet(additionalSheetType aType, |
4490 | | nsIURI* aSheetURI) |
4491 | 0 | { |
4492 | 0 | MOZ_ASSERT(aSheetURI, "null arg"); |
4493 | 0 |
|
4494 | 0 | // Checking if we have loaded this one already. |
4495 | 0 | if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0) |
4496 | 0 | return NS_ERROR_INVALID_ARG; |
4497 | 0 | |
4498 | 0 | // Loading the sheet sync. |
4499 | 0 | RefPtr<css::Loader> loader = new css::Loader(GetDocGroup()); |
4500 | 0 |
|
4501 | 0 | css::SheetParsingMode parsingMode; |
4502 | 0 | switch (aType) { |
4503 | 0 | case nsIDocument::eAgentSheet: |
4504 | 0 | parsingMode = css::eAgentSheetFeatures; |
4505 | 0 | break; |
4506 | 0 |
|
4507 | 0 | case nsIDocument::eUserSheet: |
4508 | 0 | parsingMode = css::eUserSheetFeatures; |
4509 | 0 | break; |
4510 | 0 |
|
4511 | 0 | case nsIDocument::eAuthorSheet: |
4512 | 0 | parsingMode = css::eAuthorSheetFeatures; |
4513 | 0 | break; |
4514 | 0 |
|
4515 | 0 | default: |
4516 | 0 | MOZ_CRASH("impossible value for aType"); |
4517 | 0 | } |
4518 | 0 |
|
4519 | 0 | RefPtr<StyleSheet> sheet; |
4520 | 0 | nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true, &sheet); |
4521 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
4522 | 0 |
|
4523 | 0 | sheet->SetAssociatedDocumentOrShadowRoot( |
4524 | 0 | this, StyleSheet::OwnedByDocumentOrShadowRoot); |
4525 | 0 | MOZ_ASSERT(sheet->IsApplicable()); |
4526 | 0 |
|
4527 | 0 | return AddAdditionalStyleSheet(aType, sheet); |
4528 | 0 | } |
4529 | | |
4530 | | nsresult |
4531 | | nsIDocument::AddAdditionalStyleSheet(additionalSheetType aType, StyleSheet* aSheet) |
4532 | 0 | { |
4533 | 0 | if (mAdditionalSheets[aType].Contains(aSheet)) |
4534 | 0 | return NS_ERROR_INVALID_ARG; |
4535 | 0 | |
4536 | 0 | if (!aSheet->IsApplicable()) |
4537 | 0 | return NS_ERROR_INVALID_ARG; |
4538 | 0 | |
4539 | 0 | mAdditionalSheets[aType].AppendElement(aSheet); |
4540 | 0 |
|
4541 | 0 | if (nsIPresShell* shell = GetShell()) { |
4542 | 0 | SheetType type = ConvertAdditionalSheetType(aType); |
4543 | 0 | shell->StyleSet()->AppendStyleSheet(type, aSheet); |
4544 | 0 | shell->ApplicableStylesChanged(); |
4545 | 0 | } |
4546 | 0 |
|
4547 | 0 | // Passing false, so documet.styleSheets.length will not be affected by |
4548 | 0 | // these additional sheets. |
4549 | 0 | NotifyStyleSheetAdded(aSheet, false); |
4550 | 0 | return NS_OK; |
4551 | 0 | } |
4552 | | |
4553 | | void |
4554 | | nsIDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) |
4555 | 0 | { |
4556 | 0 | MOZ_ASSERT(aSheetURI); |
4557 | 0 |
|
4558 | 0 | nsTArray<RefPtr<StyleSheet>>& sheets = mAdditionalSheets[aType]; |
4559 | 0 |
|
4560 | 0 | int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI); |
4561 | 0 | if (i >= 0) { |
4562 | 0 | RefPtr<StyleSheet> sheetRef = sheets[i]; |
4563 | 0 | sheets.RemoveElementAt(i); |
4564 | 0 |
|
4565 | 0 | if (!mIsGoingAway) { |
4566 | 0 | MOZ_ASSERT(sheetRef->IsApplicable()); |
4567 | 0 | if (nsIPresShell* shell = GetShell()) { |
4568 | 0 | SheetType type = ConvertAdditionalSheetType(aType); |
4569 | 0 | shell->StyleSet()->RemoveStyleSheet(type, sheetRef); |
4570 | 0 | shell->ApplicableStylesChanged(); |
4571 | 0 | } |
4572 | 0 | } |
4573 | 0 |
|
4574 | 0 | // Passing false, so documet.styleSheets.length will not be affected by |
4575 | 0 | // these additional sheets. |
4576 | 0 | NotifyStyleSheetRemoved(sheetRef, false); |
4577 | 0 | sheetRef->ClearAssociatedDocumentOrShadowRoot(); |
4578 | 0 | } |
4579 | 0 | } |
4580 | | |
4581 | | nsIGlobalObject* |
4582 | | nsIDocument::GetScopeObject() const |
4583 | 0 | { |
4584 | 0 | nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject)); |
4585 | 0 | return scope; |
4586 | 0 | } |
4587 | | |
4588 | | void |
4589 | | nsIDocument::SetScopeObject(nsIGlobalObject* aGlobal) |
4590 | 0 | { |
4591 | 0 | mScopeObject = do_GetWeakReference(aGlobal); |
4592 | 0 | if (aGlobal) { |
4593 | 0 | mHasHadScriptHandlingObject = true; |
4594 | 0 |
|
4595 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal); |
4596 | 0 | if (window) { |
4597 | 0 | // We want to get the tabgroup unconditionally, such that we can make |
4598 | 0 | // certain that it is cached in the inner window early enough. |
4599 | 0 | mozilla::dom::TabGroup* tabgroup = window->TabGroup(); |
4600 | 0 | // We should already have the principal, and now that we have been added to a |
4601 | 0 | // window, we should be able to join a DocGroup! |
4602 | 0 | nsAutoCString docGroupKey; |
4603 | 0 | nsresult rv = |
4604 | 0 | mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey); |
4605 | 0 | if (mDocGroup) { |
4606 | 0 | if (NS_SUCCEEDED(rv)) { |
4607 | 0 | MOZ_RELEASE_ASSERT(mDocGroup->MatchesKey(docGroupKey)); |
4608 | 0 | } |
4609 | 0 | MOZ_RELEASE_ASSERT(mDocGroup->GetTabGroup() == tabgroup); |
4610 | 0 | } else { |
4611 | 0 | mDocGroup = tabgroup->AddDocument(docGroupKey, this); |
4612 | 0 | MOZ_ASSERT(mDocGroup); |
4613 | 0 | } |
4614 | 0 | } |
4615 | 0 | } |
4616 | 0 | } |
4617 | | |
4618 | | static void |
4619 | | CheckIfContainsEMEContent(nsISupports* aSupports, void* aContainsEME) |
4620 | 0 | { |
4621 | 0 | nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports)); |
4622 | 0 | if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) { |
4623 | 0 | bool* contains = static_cast<bool*>(aContainsEME); |
4624 | 0 | if (mediaElem->GetMediaKeys()) { |
4625 | 0 | *contains = true; |
4626 | 0 | } |
4627 | 0 | } |
4628 | 0 | } |
4629 | | |
4630 | | bool |
4631 | | nsIDocument::ContainsEMEContent() |
4632 | 0 | { |
4633 | 0 | bool containsEME = false; |
4634 | 0 | EnumerateActivityObservers(CheckIfContainsEMEContent, |
4635 | 0 | static_cast<void*>(&containsEME)); |
4636 | 0 | return containsEME; |
4637 | 0 | } |
4638 | | |
4639 | | static void |
4640 | | CheckIfContainsMSEContent(nsISupports* aSupports, void* aContainsMSE) |
4641 | 0 | { |
4642 | 0 | nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports)); |
4643 | 0 | if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) { |
4644 | 0 | bool* contains = static_cast<bool*>(aContainsMSE); |
4645 | 0 | RefPtr<MediaSource> ms = mediaElem->GetMozMediaSourceObject(); |
4646 | 0 | if (ms) { |
4647 | 0 | *contains = true; |
4648 | 0 | } |
4649 | 0 | } |
4650 | 0 | } |
4651 | | |
4652 | | bool |
4653 | | nsIDocument::ContainsMSEContent() |
4654 | 0 | { |
4655 | 0 | bool containsMSE = false; |
4656 | 0 | EnumerateActivityObservers(CheckIfContainsMSEContent, |
4657 | 0 | static_cast<void*>(&containsMSE)); |
4658 | 0 | return containsMSE; |
4659 | 0 | } |
4660 | | |
4661 | | static void |
4662 | | NotifyActivityChanged(nsISupports *aSupports, void *aUnused) |
4663 | 0 | { |
4664 | 0 | nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports)); |
4665 | 0 | if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) { |
4666 | 0 | mediaElem->NotifyOwnerDocumentActivityChanged(); |
4667 | 0 | } |
4668 | 0 | nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aSupports)); |
4669 | 0 | if (objectLoadingContent) { |
4670 | 0 | nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get()); |
4671 | 0 | olc->NotifyOwnerDocumentActivityChanged(); |
4672 | 0 | } |
4673 | 0 | nsCOMPtr<nsIDocumentActivity> objectDocumentActivity(do_QueryInterface(aSupports)); |
4674 | 0 | if (objectDocumentActivity) { |
4675 | 0 | objectDocumentActivity->NotifyOwnerDocumentActivityChanged(); |
4676 | 0 | } |
4677 | 0 | } |
4678 | | |
4679 | | bool |
4680 | | nsIDocument::IsTopLevelWindowInactive() const |
4681 | 0 | { |
4682 | 0 | nsCOMPtr<nsIDocShellTreeItem> treeItem = GetDocShell(); |
4683 | 0 | if (!treeItem) { |
4684 | 0 | return false; |
4685 | 0 | } |
4686 | 0 | |
4687 | 0 | nsCOMPtr<nsIDocShellTreeItem> rootItem; |
4688 | 0 | treeItem->GetRootTreeItem(getter_AddRefs(rootItem)); |
4689 | 0 | if (!rootItem) { |
4690 | 0 | return false; |
4691 | 0 | } |
4692 | 0 | |
4693 | 0 | nsCOMPtr<nsPIDOMWindowOuter> domWindow = rootItem->GetWindow(); |
4694 | 0 | return domWindow && !domWindow->IsActive(); |
4695 | 0 | } |
4696 | | |
4697 | | void |
4698 | | nsIDocument::SetContainer(nsDocShell* aContainer) |
4699 | 0 | { |
4700 | 0 | if (aContainer) { |
4701 | 0 | mDocumentContainer = aContainer; |
4702 | 0 | } else { |
4703 | 0 | mDocumentContainer = WeakPtr<nsDocShell>(); |
4704 | 0 | } |
4705 | 0 |
|
4706 | 0 | EnumerateActivityObservers(NotifyActivityChanged, nullptr); |
4707 | 0 |
|
4708 | 0 | // IsTopLevelWindowInactive depends on the docshell, so |
4709 | 0 | // update the cached value now that it's available. |
4710 | 0 | UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE); |
4711 | 0 | if (!aContainer) { |
4712 | 0 | return; |
4713 | 0 | } |
4714 | 0 | |
4715 | 0 | // Get the Docshell |
4716 | 0 | if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) { |
4717 | 0 | // check if same type root |
4718 | 0 | nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot; |
4719 | 0 | aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); |
4720 | 0 | NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!"); |
4721 | 0 |
|
4722 | 0 | if (sameTypeRoot == aContainer) { |
4723 | 0 | static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true); |
4724 | 0 | } |
4725 | 0 |
|
4726 | 0 | static_cast<nsDocument*>(this)->SetIsContentDocument(true); |
4727 | 0 | } |
4728 | 0 |
|
4729 | 0 | mAncestorPrincipals = aContainer->AncestorPrincipals(); |
4730 | 0 | mAncestorOuterWindowIDs = aContainer->AncestorOuterWindowIDs(); |
4731 | 0 | } |
4732 | | |
4733 | | nsISupports* |
4734 | | nsIDocument::GetContainer() const |
4735 | 0 | { |
4736 | 0 | return static_cast<nsIDocShell*>(mDocumentContainer); |
4737 | 0 | } |
4738 | | |
4739 | | void |
4740 | | nsIDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject) |
4741 | 0 | { |
4742 | 0 | MOZ_ASSERT(aScriptGlobalObject || !mAnimationController || |
4743 | 0 | mAnimationController->IsPausedByType( |
4744 | 0 | nsSMILTimeContainer::PAUSE_PAGEHIDE | |
4745 | 0 | nsSMILTimeContainer::PAUSE_BEGIN), |
4746 | 0 | "Clearing window pointer while animations are unpaused"); |
4747 | 0 |
|
4748 | 0 | if (mScriptGlobalObject && !aScriptGlobalObject) { |
4749 | 0 | // We're detaching from the window. We need to grab a pointer to |
4750 | 0 | // our layout history state now. |
4751 | 0 | mLayoutHistoryState = GetLayoutHistoryState(); |
4752 | 0 |
|
4753 | 0 | // Also make sure to remove our onload blocker now if we haven't done it yet |
4754 | 0 | if (mOnloadBlockCount != 0) { |
4755 | 0 | nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
4756 | 0 | if (loadGroup) { |
4757 | 0 | loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK); |
4758 | 0 | } |
4759 | 0 | } |
4760 | 0 |
|
4761 | 0 | ErrorResult error; |
4762 | 0 | if (GetController().isSome()) { |
4763 | 0 | imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this); |
4764 | 0 | if (loader) { |
4765 | 0 | loader->ClearCacheForControlledDocument(this); |
4766 | 0 | } |
4767 | 0 |
|
4768 | 0 | // We may become controlled again if this document comes back out |
4769 | 0 | // of bfcache. Clear our state to allow that to happen. Only |
4770 | 0 | // clear this flag if we are actually controlled, though, so pages |
4771 | 0 | // that were force reloaded don't become controlled when they |
4772 | 0 | // come out of bfcache. |
4773 | 0 | mMaybeServiceWorkerControlled = false; |
4774 | 0 | } |
4775 | 0 | } |
4776 | 0 |
|
4777 | 0 | // BlockOnload() might be called before mScriptGlobalObject is set. |
4778 | 0 | // We may need to add the blocker once mScriptGlobalObject is set. |
4779 | 0 | bool needOnloadBlocker = !mScriptGlobalObject && aScriptGlobalObject; |
4780 | 0 |
|
4781 | 0 | mScriptGlobalObject = aScriptGlobalObject; |
4782 | 0 |
|
4783 | 0 | if (needOnloadBlocker) { |
4784 | 0 | EnsureOnloadBlocker(); |
4785 | 0 | } |
4786 | 0 |
|
4787 | 0 | UpdateFrameRequestCallbackSchedulingState(); |
4788 | 0 |
|
4789 | 0 | if (aScriptGlobalObject) { |
4790 | 0 | // Go back to using the docshell for the layout history state |
4791 | 0 | mLayoutHistoryState = nullptr; |
4792 | 0 | SetScopeObject(aScriptGlobalObject); |
4793 | 0 | mHasHadDefaultView = true; |
4794 | | #ifdef DEBUG |
4795 | | if (!mWillReparent) { |
4796 | | // We really shouldn't have a wrapper here but if we do we need to make sure |
4797 | | // it has the correct parent. |
4798 | | JSObject *obj = GetWrapperPreserveColor(); |
4799 | | if (obj) { |
4800 | | JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject(); |
4801 | | NS_ASSERTION(JS::GetNonCCWObjectGlobal(obj) == newScope, |
4802 | | "Wrong scope, this is really bad!"); |
4803 | | } |
4804 | | } |
4805 | | #endif |
4806 | |
|
4807 | 0 | if (mAllowDNSPrefetch) { |
4808 | 0 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
4809 | 0 | if (docShell) { |
4810 | | #ifdef DEBUG |
4811 | | nsCOMPtr<nsIWebNavigation> webNav = |
4812 | | do_GetInterface(aScriptGlobalObject); |
4813 | | NS_ASSERTION(SameCOMIdentity(webNav, docShell), |
4814 | | "Unexpected container or script global?"); |
4815 | | #endif |
4816 | | bool allowDNSPrefetch; |
4817 | 0 | docShell->GetAllowDNSPrefetch(&allowDNSPrefetch); |
4818 | 0 | mAllowDNSPrefetch = allowDNSPrefetch; |
4819 | 0 | } |
4820 | 0 | } |
4821 | 0 |
|
4822 | 0 | // If we are set in a window that is already focused we should remember this |
4823 | 0 | // as the time the document gained focus. |
4824 | 0 | IgnoredErrorResult ignored; |
4825 | 0 | bool focused = HasFocus(ignored); |
4826 | 0 | if (focused) { |
4827 | 0 | SetLastFocusTime(TimeStamp::Now()); |
4828 | 0 | } |
4829 | 0 | } |
4830 | 0 |
|
4831 | 0 | // Remember the pointer to our window (or lack there of), to avoid |
4832 | 0 | // having to QI every time it's asked for. |
4833 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mScriptGlobalObject); |
4834 | 0 | mWindow = window; |
4835 | 0 |
|
4836 | 0 | // Now that we know what our window is, we can flush the CSP errors to the |
4837 | 0 | // Web Console. We are flushing all messages that occured and were stored |
4838 | 0 | // in the queue prior to this point. |
4839 | 0 | nsCOMPtr<nsIContentSecurityPolicy> csp; |
4840 | 0 | NodePrincipal()->GetCsp(getter_AddRefs(csp)); |
4841 | 0 | if (csp) { |
4842 | 0 | static_cast<nsCSPContext*>(csp.get())->flushConsoleMessages(); |
4843 | 0 | } |
4844 | 0 |
|
4845 | 0 | nsCOMPtr<nsIHttpChannelInternal> internalChannel = |
4846 | 0 | do_QueryInterface(GetChannel()); |
4847 | 0 | if (internalChannel) { |
4848 | 0 | nsCOMArray<nsISecurityConsoleMessage> messages; |
4849 | 0 | DebugOnly<nsresult> rv = internalChannel->TakeAllSecurityMessages(messages); |
4850 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
4851 | 0 | SendToConsole(messages); |
4852 | 0 | } |
4853 | 0 |
|
4854 | 0 | // Set our visibility state, but do not fire the event. This is correct |
4855 | 0 | // because either we're coming out of bfcache (in which case IsVisible() will |
4856 | 0 | // still test false at this point and no state change will happen) or we're |
4857 | 0 | // doing the initial document load and don't want to fire the event for this |
4858 | 0 | // change. |
4859 | 0 | dom::VisibilityState oldState = mVisibilityState; |
4860 | 0 | mVisibilityState = ComputeVisibilityState(); |
4861 | 0 | // When the visibility is changed, notify it to observers. |
4862 | 0 | // Some observers need the notification, for example HTMLMediaElement uses |
4863 | 0 | // it to update internal media resource allocation. |
4864 | 0 | // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder |
4865 | 0 | // creation are already done before nsDocument::SetScriptGlobalObject() call. |
4866 | 0 | // MediaDecoder decides whether starting decoding is decided based on |
4867 | 0 | // document's visibility. When the MediaDecoder is created, |
4868 | 0 | // nsDocument::SetScriptGlobalObject() is not yet called and document is |
4869 | 0 | // hidden state. Therefore the MediaDecoder decides that decoding is |
4870 | 0 | // not yet necessary. But soon after nsDocument::SetScriptGlobalObject() |
4871 | 0 | // call, the document becomes not hidden. At the time, MediaDecoder needs |
4872 | 0 | // to know it and needs to start updating decoding. |
4873 | 0 | if (oldState != mVisibilityState) { |
4874 | 0 | EnumerateActivityObservers(NotifyActivityChanged, nullptr); |
4875 | 0 | } |
4876 | 0 |
|
4877 | 0 | // The global in the template contents owner document should be the same. |
4878 | 0 | if (mTemplateContentsOwner && mTemplateContentsOwner != this) { |
4879 | 0 | mTemplateContentsOwner->SetScriptGlobalObject(aScriptGlobalObject); |
4880 | 0 | } |
4881 | 0 |
|
4882 | 0 | if (!mMaybeServiceWorkerControlled && mDocumentContainer && mScriptGlobalObject && GetChannel()) { |
4883 | 0 |
|
4884 | 0 | // If we are shift-reloaded, don't associate with a ServiceWorker. |
4885 | 0 | if (mDocumentContainer->IsForceReloading()) { |
4886 | 0 | NS_WARNING("Page was shift reloaded, skipping ServiceWorker control"); |
4887 | 0 | return; |
4888 | 0 | } |
4889 | 0 |
|
4890 | 0 | mMaybeServiceWorkerControlled = true; |
4891 | 0 | } |
4892 | 0 | } |
4893 | | |
4894 | | nsIScriptGlobalObject* |
4895 | | nsIDocument::GetScriptHandlingObjectInternal() const |
4896 | 0 | { |
4897 | 0 | MOZ_ASSERT(!mScriptGlobalObject, |
4898 | 0 | "Do not call this when mScriptGlobalObject is set!"); |
4899 | 0 | if (mHasHadDefaultView) { |
4900 | 0 | return nullptr; |
4901 | 0 | } |
4902 | 0 | |
4903 | 0 | nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject = |
4904 | 0 | do_QueryReferent(mScopeObject); |
4905 | 0 | nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(scriptHandlingObject); |
4906 | 0 | if (win) { |
4907 | 0 | nsPIDOMWindowOuter* outer = win->GetOuterWindow(); |
4908 | 0 | if (!outer || outer->GetCurrentInnerWindow() != win) { |
4909 | 0 | NS_WARNING("Wrong inner/outer window combination!"); |
4910 | 0 | return nullptr; |
4911 | 0 | } |
4912 | 0 | } |
4913 | 0 | return scriptHandlingObject; |
4914 | 0 | } |
4915 | | void |
4916 | | nsIDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject) |
4917 | 0 | { |
4918 | 0 | NS_ASSERTION(!mScriptGlobalObject || |
4919 | 0 | mScriptGlobalObject == aScriptObject, |
4920 | 0 | "Wrong script object!"); |
4921 | 0 | if (aScriptObject) { |
4922 | 0 | SetScopeObject(aScriptObject); |
4923 | 0 | mHasHadDefaultView = false; |
4924 | 0 | } |
4925 | 0 | } |
4926 | | |
4927 | | nsPIDOMWindowOuter* |
4928 | | nsIDocument::GetWindowInternal() const |
4929 | 0 | { |
4930 | 0 | MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!"); |
4931 | 0 | // Let's use mScriptGlobalObject. Even if the document is already removed from |
4932 | 0 | // the docshell, the outer window might be still obtainable from the it. |
4933 | 0 | nsCOMPtr<nsPIDOMWindowOuter> win; |
4934 | 0 | if (mRemovedFromDocShell) { |
4935 | 0 | // The docshell returns the outer window we are done. |
4936 | 0 | nsCOMPtr<nsIDocShell> kungFuDeathGrip(mDocumentContainer); |
4937 | 0 | if (kungFuDeathGrip) { |
4938 | 0 | win = kungFuDeathGrip->GetWindow(); |
4939 | 0 | } |
4940 | 0 | } else { |
4941 | 0 | if (nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(mScriptGlobalObject)) { |
4942 | 0 | // mScriptGlobalObject is always the inner window, let's get the outer. |
4943 | 0 | win = inner->GetOuterWindow(); |
4944 | 0 | } |
4945 | 0 | } |
4946 | 0 |
|
4947 | 0 | return win; |
4948 | 0 | } |
4949 | | |
4950 | | bool |
4951 | | nsIDocument::InternalAllowXULXBL() |
4952 | 0 | { |
4953 | 0 | if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) { |
4954 | 0 | mAllowXULXBL = eTriTrue; |
4955 | 0 | return true; |
4956 | 0 | } |
4957 | 0 | |
4958 | 0 | mAllowXULXBL = eTriFalse; |
4959 | 0 | return false; |
4960 | 0 | } |
4961 | | |
4962 | | // Note: We don't hold a reference to the document observer; we assume |
4963 | | // that it has a live reference to the document. |
4964 | | void |
4965 | | nsIDocument::AddObserver(nsIDocumentObserver* aObserver) |
4966 | 0 | { |
4967 | 0 | NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex, |
4968 | 0 | "Observer already in the list"); |
4969 | 0 | mObservers.AppendElement(aObserver); |
4970 | 0 | AddMutationObserver(aObserver); |
4971 | 0 | } |
4972 | | |
4973 | | bool |
4974 | | nsIDocument::RemoveObserver(nsIDocumentObserver* aObserver) |
4975 | 0 | { |
4976 | 0 | // If we're in the process of destroying the document (and we're |
4977 | 0 | // informing the observers of the destruction), don't remove the |
4978 | 0 | // observers from the list. This is not a big deal, since we |
4979 | 0 | // don't hold a live reference to the observers. |
4980 | 0 | if (!mInDestructor) { |
4981 | 0 | RemoveMutationObserver(aObserver); |
4982 | 0 | return mObservers.RemoveElement(aObserver); |
4983 | 0 | } |
4984 | 0 | |
4985 | 0 | return mObservers.Contains(aObserver); |
4986 | 0 | } |
4987 | | |
4988 | | void |
4989 | | nsIDocument::MaybeEndOutermostXBLUpdate() |
4990 | 0 | { |
4991 | 0 | // Only call BindingManager()->EndOutermostUpdate() when |
4992 | 0 | // we're not in an update and it is safe to run scripts. |
4993 | 0 | if (mUpdateNestLevel == 0 && mInXBLUpdate) { |
4994 | 0 | if (nsContentUtils::IsSafeToRunScript()) { |
4995 | 0 | mInXBLUpdate = false; |
4996 | 0 | BindingManager()->EndOutermostUpdate(); |
4997 | 0 | } else if (!mInDestructor) { |
4998 | 0 | if (!mMaybeEndOutermostXBLUpdateRunner) { |
4999 | 0 | mMaybeEndOutermostXBLUpdateRunner = |
5000 | 0 | NewRunnableMethod("nsDocument::MaybeEndOutermostXBLUpdate", |
5001 | 0 | this, |
5002 | 0 | &nsDocument::MaybeEndOutermostXBLUpdate); |
5003 | 0 | } |
5004 | 0 | nsContentUtils::AddScriptRunner(mMaybeEndOutermostXBLUpdateRunner); |
5005 | 0 | } |
5006 | 0 | } |
5007 | 0 | } |
5008 | | |
5009 | | void |
5010 | | nsIDocument::BeginUpdate() |
5011 | 0 | { |
5012 | 0 | // If the document is going away, then it's probably okay to do things to it |
5013 | 0 | // in the wrong DocGroup. We're unlikely to run JS or do anything else |
5014 | 0 | // observable at this point. We reach this point when cycle collecting a |
5015 | 0 | // <link> element and the unlink code removes a style sheet. |
5016 | 0 | // |
5017 | 0 | // TODO(emilio): Style updates are gone, can this happen now? |
5018 | 0 | if (mDocGroup && !mIsGoingAway && !mInUnlinkOrDeletion && !mIgnoreDocGroupMismatches) { |
5019 | 0 | mDocGroup->ValidateAccess(); |
5020 | 0 | } |
5021 | 0 |
|
5022 | 0 | if (mUpdateNestLevel == 0 && !mInXBLUpdate) { |
5023 | 0 | mInXBLUpdate = true; |
5024 | 0 | BindingManager()->BeginOutermostUpdate(); |
5025 | 0 | } |
5026 | 0 |
|
5027 | 0 | ++mUpdateNestLevel; |
5028 | 0 | nsContentUtils::AddScriptBlocker(); |
5029 | 0 | NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this)); |
5030 | 0 | } |
5031 | | |
5032 | | void |
5033 | | nsDocument::EndUpdate() |
5034 | 0 | { |
5035 | 0 | NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this)); |
5036 | 0 |
|
5037 | 0 | nsContentUtils::RemoveScriptBlocker(); |
5038 | 0 |
|
5039 | 0 | --mUpdateNestLevel; |
5040 | 0 |
|
5041 | 0 | // This set of updates may have created XBL bindings. Let the |
5042 | 0 | // binding manager know we're done. |
5043 | 0 | MaybeEndOutermostXBLUpdate(); |
5044 | 0 |
|
5045 | 0 | MaybeInitializeFinalizeFrameLoaders(); |
5046 | 0 | } |
5047 | | |
5048 | | void |
5049 | | nsDocument::BeginLoad() |
5050 | 0 | { |
5051 | 0 | MOZ_ASSERT(!mDidCallBeginLoad); |
5052 | 0 | mDidCallBeginLoad = true; |
5053 | 0 |
|
5054 | 0 | // Block onload here to prevent having to deal with blocking and |
5055 | 0 | // unblocking it while we know the document is loading. |
5056 | 0 | BlockOnload(); |
5057 | 0 | mDidFireDOMContentLoaded = false; |
5058 | 0 | BlockDOMContentLoaded(); |
5059 | 0 |
|
5060 | 0 | if (mScriptLoader) { |
5061 | 0 | mScriptLoader->BeginDeferringScripts(); |
5062 | 0 | } |
5063 | 0 |
|
5064 | 0 | NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this)); |
5065 | 0 | } |
5066 | | |
5067 | | void |
5068 | | nsIDocument::MozSetImageElement(const nsAString& aImageElementId, |
5069 | | Element* aElement) |
5070 | 0 | { |
5071 | 0 | if (aImageElementId.IsEmpty()) |
5072 | 0 | return; |
5073 | 0 | |
5074 | 0 | // Hold a script blocker while calling SetImageElement since that can call |
5075 | 0 | // out to id-observers |
5076 | 0 | nsAutoScriptBlocker scriptBlocker; |
5077 | 0 |
|
5078 | 0 | nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aImageElementId); |
5079 | 0 | if (entry) { |
5080 | 0 | entry->SetImageElement(aElement); |
5081 | 0 | if (entry->IsEmpty()) { |
5082 | 0 | mIdentifierMap.RemoveEntry(entry); |
5083 | 0 | } |
5084 | 0 | } |
5085 | 0 | } |
5086 | | |
5087 | | void |
5088 | | nsIDocument::DispatchContentLoadedEvents() |
5089 | 0 | { |
5090 | 0 | // If you add early returns from this method, make sure you're |
5091 | 0 | // calling UnblockOnload properly. |
5092 | 0 |
|
5093 | 0 | // Unpin references to preloaded images |
5094 | 0 | mPreloadingImages.Clear(); |
5095 | 0 |
|
5096 | 0 | // DOM manipulation after content loaded should not care if the element |
5097 | 0 | // came from the preloader. |
5098 | 0 | mPreloadedPreconnects.Clear(); |
5099 | 0 |
|
5100 | 0 | if (mTiming) { |
5101 | 0 | mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI()); |
5102 | 0 | } |
5103 | 0 |
|
5104 | 0 | // Dispatch observer notification to notify observers document is interactive. |
5105 | 0 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
5106 | 0 | if (os) { |
5107 | 0 | nsIPrincipal* principal = NodePrincipal(); |
5108 | 0 | os->NotifyObservers(this, |
5109 | 0 | nsContentUtils::IsSystemPrincipal(principal) ? |
5110 | 0 | "chrome-document-interactive" : |
5111 | 0 | "content-document-interactive", |
5112 | 0 | nullptr); |
5113 | 0 | } |
5114 | 0 |
|
5115 | 0 | // Fire a DOM event notifying listeners that this document has been |
5116 | 0 | // loaded (excluding images and other loads initiated by this |
5117 | 0 | // document). |
5118 | 0 | nsContentUtils::DispatchTrustedEvent(this, this, |
5119 | 0 | NS_LITERAL_STRING("DOMContentLoaded"), |
5120 | 0 | CanBubble::eYes, Cancelable::eNo); |
5121 | 0 |
|
5122 | 0 | if (MayStartLayout()) { |
5123 | 0 | MaybeResolveReadyForIdle(); |
5124 | 0 | } |
5125 | 0 |
|
5126 | 0 | RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get(); |
5127 | 0 | nsIDocShell* docShell = this->GetDocShell(); |
5128 | 0 |
|
5129 | 0 | if (timelines && timelines->HasConsumer(docShell)) { |
5130 | 0 | timelines->AddMarkerForDocShell(docShell, |
5131 | 0 | MakeUnique<DocLoadingTimelineMarker>("document::DOMContentLoaded")); |
5132 | 0 | } |
5133 | 0 |
|
5134 | 0 | if (mTiming) { |
5135 | 0 | mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI()); |
5136 | 0 | } |
5137 | 0 |
|
5138 | 0 | // If this document is a [i]frame, fire a DOMFrameContentLoaded |
5139 | 0 | // event on all parent documents notifying that the HTML (excluding |
5140 | 0 | // other external files such as images and stylesheets) in a frame |
5141 | 0 | // has finished loading. |
5142 | 0 |
|
5143 | 0 | // target_frame is the [i]frame element that will be used as the |
5144 | 0 | // target for the event. It's the [i]frame whose content is done |
5145 | 0 | // loading. |
5146 | 0 | nsCOMPtr<EventTarget> target_frame; |
5147 | 0 |
|
5148 | 0 | if (mParentDocument) { |
5149 | 0 | target_frame = mParentDocument->FindContentForSubDocument(this); |
5150 | 0 | } |
5151 | 0 |
|
5152 | 0 | if (target_frame) { |
5153 | 0 | nsCOMPtr<nsIDocument> parent = mParentDocument; |
5154 | 0 | do { |
5155 | 0 | RefPtr<Event> event; |
5156 | 0 | if (parent) { |
5157 | 0 | IgnoredErrorResult ignored; |
5158 | 0 | event = parent->CreateEvent(NS_LITERAL_STRING("Events"), |
5159 | 0 | CallerType::System, ignored); |
5160 | 0 |
|
5161 | 0 | } |
5162 | 0 |
|
5163 | 0 | if (event) { |
5164 | 0 | event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true, |
5165 | 0 | true); |
5166 | 0 |
|
5167 | 0 | event->SetTarget(target_frame); |
5168 | 0 | event->SetTrusted(true); |
5169 | 0 |
|
5170 | 0 | // To dispatch this event we must manually call |
5171 | 0 | // EventDispatcher::Dispatch() on the ancestor document since the |
5172 | 0 | // target is not in the same document, so the event would never reach |
5173 | 0 | // the ancestor document if we used the normal event |
5174 | 0 | // dispatching code. |
5175 | 0 |
|
5176 | 0 | WidgetEvent* innerEvent = event->WidgetEventPtr(); |
5177 | 0 | if (innerEvent) { |
5178 | 0 | nsEventStatus status = nsEventStatus_eIgnore; |
5179 | 0 |
|
5180 | 0 | RefPtr<nsPresContext> context = parent->GetPresContext(); |
5181 | 0 |
|
5182 | 0 | if (context) { |
5183 | 0 | EventDispatcher::Dispatch(parent, context, innerEvent, event, |
5184 | 0 | &status); |
5185 | 0 | } |
5186 | 0 | } |
5187 | 0 | } |
5188 | 0 |
|
5189 | 0 | parent = parent->GetParentDocument(); |
5190 | 0 | } while (parent); |
5191 | 0 | } |
5192 | 0 |
|
5193 | 0 | // If the document has a manifest attribute, fire a MozApplicationManifest |
5194 | 0 | // event. |
5195 | 0 | Element* root = GetRootElement(); |
5196 | 0 | if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) { |
5197 | 0 | nsContentUtils::DispatchChromeEvent(this, this, |
5198 | 0 | NS_LITERAL_STRING("MozApplicationManifest"), |
5199 | 0 | CanBubble::eYes, Cancelable::eYes); |
5200 | 0 | } |
5201 | 0 |
|
5202 | 0 | nsPIDOMWindowInner* inner = GetInnerWindow(); |
5203 | 0 | if (inner) { |
5204 | 0 | inner->NoteDOMContentLoaded(); |
5205 | 0 | } |
5206 | 0 |
|
5207 | 0 | // TODO |
5208 | 0 | if (mMaybeServiceWorkerControlled) { |
5209 | 0 | using mozilla::dom::ServiceWorkerManager; |
5210 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
5211 | 0 | if (swm) { |
5212 | 0 | Maybe<ClientInfo> clientInfo = GetClientInfo(); |
5213 | 0 | if (clientInfo.isSome()) { |
5214 | 0 | swm->MaybeCheckNavigationUpdate(clientInfo.ref()); |
5215 | 0 | } |
5216 | 0 | } |
5217 | 0 | } |
5218 | 0 |
|
5219 | 0 | UnblockOnload(true); |
5220 | 0 | } |
5221 | | |
5222 | | #if defined(DEBUG) && !defined(ANDROID) |
5223 | | // We want to get to a point where all about: pages ship with a CSP. This |
5224 | | // assertion ensures that we can not deploy new about: pages without a CSP. |
5225 | | // Initially we will whitelist legacy about: pages which not yet have a CSP |
5226 | | // attached, but ultimately that whitelist should disappear. |
5227 | | // Please note that any about: page should not use inline JS or inline CSS, |
5228 | | // and instead should load JS and CSS from an external file (*.js, *.css) |
5229 | | // which allows us to apply a strong CSP omitting 'unsafe-inline'. Ideally, |
5230 | | // the CSP allows precisely the resources that need to be loaded; but it |
5231 | | // should at least be as strong as: |
5232 | | // <meta http-equiv="Content-Security-Policy" content="default-src chrome:"/> |
5233 | | static void |
5234 | | AssertContentPrivilegedAboutPageHasCSP(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal) |
5235 | | { |
5236 | | // Curently we can't serialize the CSP, hence we only assert if |
5237 | | // running in the content process. |
5238 | | if (!XRE_IsContentProcess()) { |
5239 | | return; |
5240 | | } |
5241 | | |
5242 | | // Check if we are loading an about: URI at all |
5243 | | bool isAboutURI = |
5244 | | (NS_SUCCEEDED(aDocumentURI->SchemeIs("about", &isAboutURI)) && isAboutURI); |
5245 | | |
5246 | | if (!isAboutURI) { |
5247 | | return; |
5248 | | } |
5249 | | |
5250 | | // Check if we are loading a content-privileged about: URI |
5251 | | nsCOMPtr<nsIAboutModule> aboutModule; |
5252 | | nsresult rv = NS_GetAboutModule(aDocumentURI, getter_AddRefs(aboutModule)); |
5253 | | NS_ENSURE_SUCCESS_VOID(rv); |
5254 | | |
5255 | | uint32_t aboutModuleFlags = 0; |
5256 | | rv = aboutModule->GetURIFlags(aDocumentURI, &aboutModuleFlags); |
5257 | | NS_ENSURE_SUCCESS_VOID(rv); |
5258 | | |
5259 | | if (!(aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT)) { |
5260 | | return; |
5261 | | } |
5262 | | |
5263 | | // Potentially init the legacy whitelist of about URIs without a CSP. |
5264 | | static StaticAutoPtr<nsTArray<nsCString>> sLegacyAboutPagesWithNoCSP; |
5265 | | if (!sLegacyAboutPagesWithNoCSP || |
5266 | | Preferences::GetBool("csp.overrule_content_privileged_about_uris_without_csp_whitelist")) { |
5267 | | sLegacyAboutPagesWithNoCSP = new nsTArray<nsCString>(); |
5268 | | nsAutoCString legacyAboutPages; |
5269 | | Preferences::GetCString("csp.content_privileged_about_uris_without_csp", |
5270 | | legacyAboutPages); |
5271 | | for (const nsACString& hostString : legacyAboutPages.Split(',')) { |
5272 | | // please note that for the actual whitelist we only store the path of |
5273 | | // about: URI. Let's reassemble the full about URI here so we don't |
5274 | | // have to remove query arguments later. |
5275 | | nsCString aboutURI; |
5276 | | aboutURI.AppendLiteral("about:"); |
5277 | | aboutURI.Append(hostString); |
5278 | | sLegacyAboutPagesWithNoCSP->AppendElement(aboutURI); |
5279 | | } |
5280 | | ClearOnShutdown(&sLegacyAboutPagesWithNoCSP); |
5281 | | } |
5282 | | |
5283 | | // Check if the about URI is whitelisted |
5284 | | nsAutoCString aboutSpec; |
5285 | | aDocumentURI->GetSpec(aboutSpec); |
5286 | | for (auto& legacyPageEntry : *sLegacyAboutPagesWithNoCSP) { |
5287 | | // please note that we perform a substring match here on purpose, |
5288 | | // so we don't have to deal and parse out all the query arguments |
5289 | | // the various about pages rely on. |
5290 | | if (aboutSpec.Find(legacyPageEntry) == 0) { |
5291 | | return; |
5292 | | } |
5293 | | } |
5294 | | |
5295 | | nsCOMPtr<nsIContentSecurityPolicy> csp; |
5296 | | aPrincipal->GetCsp(getter_AddRefs(csp)); |
5297 | | nsAutoString parsedPolicyStr; |
5298 | | if (csp) { |
5299 | | uint32_t policyCount = 0; |
5300 | | csp->GetPolicyCount(&policyCount); |
5301 | | if (policyCount > 0) { |
5302 | | csp->GetPolicyString(0, parsedPolicyStr); |
5303 | | } |
5304 | | } |
5305 | | if (Preferences::GetBool("csp.overrule_content_privileged_about_uris_without_csp_whitelist")) { |
5306 | | NS_ASSERTION(parsedPolicyStr.Find("default-src") >= 0, "about: page must have a CSP"); |
5307 | | return; |
5308 | | } |
5309 | | MOZ_ASSERT(parsedPolicyStr.Find("default-src") >= 0, |
5310 | | "about: page must contain a CSP including default-src"); |
5311 | | } |
5312 | | #endif |
5313 | | |
5314 | | void |
5315 | | nsDocument::EndLoad() |
5316 | 0 | { |
5317 | | #if defined(DEBUG) && !defined(ANDROID) |
5318 | | // only assert if nothing stopped the load on purpose |
5319 | | if (!mParserAborted) { |
5320 | | AssertContentPrivilegedAboutPageHasCSP(mDocumentURI, NodePrincipal()); |
5321 | | } |
5322 | | #endif |
5323 | |
|
5324 | 0 | // EndLoad may have been called without a matching call to BeginLoad, in the |
5325 | 0 | // case of a failed parse (for example, due to timeout). In such a case, we |
5326 | 0 | // still want to execute part of this code to do appropriate cleanup, but we |
5327 | 0 | // gate part of it because it is intended to match 1-for-1 with calls to |
5328 | 0 | // BeginLoad. We have an explicit flag bit for this purpose, since it's |
5329 | 0 | // complicated and error prone to derive this condition from other related |
5330 | 0 | // flags that can be manipulated outside of a BeginLoad/EndLoad pair. |
5331 | 0 |
|
5332 | 0 | // Part 1: Code that always executes to cleanup end of parsing, whether |
5333 | 0 | // that parsing was successful or not. |
5334 | 0 |
|
5335 | 0 | // Drop the ref to our parser, if any, but keep hold of the sink so that we |
5336 | 0 | // can flush it from FlushPendingNotifications as needed. We might have to |
5337 | 0 | // do that to get a StartLayout() to happen. |
5338 | 0 | if (mParser) { |
5339 | 0 | mWeakSink = do_GetWeakReference(mParser->GetContentSink()); |
5340 | 0 | mParser = nullptr; |
5341 | 0 | } |
5342 | 0 |
|
5343 | 0 | NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this)); |
5344 | 0 |
|
5345 | 0 | // Part 2: Code that only executes when this EndLoad matches a BeginLoad. |
5346 | 0 |
|
5347 | 0 | if (!mDidCallBeginLoad) { |
5348 | 0 | return; |
5349 | 0 | } |
5350 | 0 | mDidCallBeginLoad = false; |
5351 | 0 |
|
5352 | 0 | UnblockDOMContentLoaded(); |
5353 | 0 | } |
5354 | | |
5355 | | void |
5356 | | nsIDocument::UnblockDOMContentLoaded() |
5357 | 0 | { |
5358 | 0 | MOZ_ASSERT(mBlockDOMContentLoaded); |
5359 | 0 | if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) { |
5360 | 0 | return; |
5361 | 0 | } |
5362 | 0 | |
5363 | 0 | MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p UnblockDOMContentLoaded", this)); |
5364 | 0 |
|
5365 | 0 | mDidFireDOMContentLoaded = true; |
5366 | 0 | if (nsIPresShell* shell = GetShell()) { |
5367 | 0 | shell->GetRefreshDriver()->NotifyDOMContentLoaded(); |
5368 | 0 | } |
5369 | 0 |
|
5370 | 0 |
|
5371 | 0 | MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE); |
5372 | 0 | if (!mSynchronousDOMContentLoaded) { |
5373 | 0 | MOZ_RELEASE_ASSERT(NS_IsMainThread()); |
5374 | 0 | nsCOMPtr<nsIRunnable> ev = |
5375 | 0 | NewRunnableMethod("nsIDocument::DispatchContentLoadedEvents", |
5376 | 0 | this, |
5377 | 0 | &nsIDocument::DispatchContentLoadedEvents); |
5378 | 0 | Dispatch(TaskCategory::Other, ev.forget()); |
5379 | 0 | } else { |
5380 | 0 | DispatchContentLoadedEvents(); |
5381 | 0 | } |
5382 | 0 | } |
5383 | | |
5384 | | void |
5385 | | nsIDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask) |
5386 | 0 | { |
5387 | 0 | MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), |
5388 | 0 | "Someone forgot a scriptblocker"); |
5389 | 0 | NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged, |
5390 | 0 | (this, aContent, aStateMask)); |
5391 | 0 | } |
5392 | | |
5393 | | void |
5394 | | nsIDocument::DocumentStatesChanged(EventStates aStateMask) |
5395 | 0 | { |
5396 | 0 | UpdateDocumentStates(aStateMask); |
5397 | 0 | NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask)); |
5398 | 0 | } |
5399 | | |
5400 | | void |
5401 | | nsIDocument::StyleRuleChanged(StyleSheet* aSheet, css::Rule* aStyleRule) |
5402 | 0 | { |
5403 | 0 | if (nsIPresShell* shell = GetShell()) { |
5404 | 0 | shell->ApplicableStylesChanged(); |
5405 | 0 | } |
5406 | 0 |
|
5407 | 0 | if (!StyleSheetChangeEventsEnabled()) { |
5408 | 0 | return; |
5409 | 0 | } |
5410 | 0 | |
5411 | 0 | DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, |
5412 | 0 | "StyleRuleChanged", |
5413 | 0 | mRule, |
5414 | 0 | aStyleRule); |
5415 | 0 | } |
5416 | | |
5417 | | void |
5418 | | nsIDocument::StyleRuleAdded(StyleSheet* aSheet, css::Rule* aStyleRule) |
5419 | 0 | { |
5420 | 0 | if (nsIPresShell* shell = GetShell()) { |
5421 | 0 | shell->ApplicableStylesChanged(); |
5422 | 0 | } |
5423 | 0 |
|
5424 | 0 | if (!StyleSheetChangeEventsEnabled()) { |
5425 | 0 | return; |
5426 | 0 | } |
5427 | 0 | |
5428 | 0 | DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, |
5429 | 0 | "StyleRuleAdded", |
5430 | 0 | mRule, |
5431 | 0 | aStyleRule); |
5432 | 0 | } |
5433 | | |
5434 | | void |
5435 | | nsIDocument::StyleRuleRemoved(StyleSheet* aSheet, css::Rule* aStyleRule) |
5436 | 0 | { |
5437 | 0 | if (nsIPresShell* shell = GetShell()) { |
5438 | 0 | shell->ApplicableStylesChanged(); |
5439 | 0 | } |
5440 | 0 |
|
5441 | 0 | if (!StyleSheetChangeEventsEnabled()) { |
5442 | 0 | return; |
5443 | 0 | } |
5444 | 0 | |
5445 | 0 | DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, |
5446 | 0 | "StyleRuleRemoved", |
5447 | 0 | mRule, |
5448 | 0 | aStyleRule); |
5449 | 0 | } |
5450 | | |
5451 | | #undef DO_STYLESHEET_NOTIFICATION |
5452 | | |
5453 | | static Element* |
5454 | | GetCustomContentContainer(nsIPresShell* aShell) |
5455 | 0 | { |
5456 | 0 | if (!aShell || !aShell->GetCanvasFrame()) { |
5457 | 0 | return nullptr; |
5458 | 0 | } |
5459 | 0 | |
5460 | 0 | return aShell->GetCanvasFrame()->GetCustomContentContainer(); |
5461 | 0 | } |
5462 | | |
5463 | | static void |
5464 | | InsertAnonContentIntoCanvas(AnonymousContent& aAnonContent, |
5465 | | nsIPresShell* aShell) |
5466 | 0 | { |
5467 | 0 | Element* container = GetCustomContentContainer(aShell); |
5468 | 0 | if (!container) { |
5469 | 0 | return; |
5470 | 0 | } |
5471 | 0 | |
5472 | 0 | nsresult rv = container->AppendChildTo(&aAnonContent.ContentNode(), true); |
5473 | 0 | if (NS_FAILED(rv)) { |
5474 | 0 | return; |
5475 | 0 | } |
5476 | 0 | |
5477 | 0 | aShell->GetCanvasFrame()->ShowCustomContentContainer(); |
5478 | 0 | } |
5479 | | |
5480 | | already_AddRefed<AnonymousContent> |
5481 | | nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv) |
5482 | 0 | { |
5483 | 0 | nsAutoScriptBlocker scriptBlocker; |
5484 | 0 |
|
5485 | 0 | // Clone the node to avoid returning a direct reference. |
5486 | 0 | nsCOMPtr<nsINode> clone = aElement.CloneNode(true, aRv); |
5487 | 0 | if (aRv.Failed()) { |
5488 | 0 | return nullptr; |
5489 | 0 | } |
5490 | 0 | |
5491 | 0 | auto anonContent = |
5492 | 0 | MakeRefPtr<AnonymousContent>(clone.forget().downcast<Element>()); |
5493 | 0 | mAnonymousContents.AppendElement(anonContent); |
5494 | 0 |
|
5495 | 0 | InsertAnonContentIntoCanvas(*anonContent, GetShell()); |
5496 | 0 |
|
5497 | 0 | return anonContent.forget(); |
5498 | 0 | } |
5499 | | |
5500 | | static void |
5501 | | RemoveAnonContentFromCanvas(AnonymousContent& aAnonContent, |
5502 | | nsIPresShell* aShell) |
5503 | 0 | { |
5504 | 0 | RefPtr<Element> container = GetCustomContentContainer(aShell); |
5505 | 0 | if (!container) { |
5506 | 0 | return; |
5507 | 0 | } |
5508 | 0 | container->RemoveChild(aAnonContent.ContentNode(), IgnoreErrors()); |
5509 | 0 | } |
5510 | | |
5511 | | void |
5512 | | nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent, |
5513 | | ErrorResult& aRv) |
5514 | 0 | { |
5515 | 0 | nsAutoScriptBlocker scriptBlocker; |
5516 | 0 |
|
5517 | 0 | auto index = mAnonymousContents.IndexOf(&aContent); |
5518 | 0 | if (index == mAnonymousContents.NoIndex) { |
5519 | 0 | return; |
5520 | 0 | } |
5521 | 0 | |
5522 | 0 | mAnonymousContents.RemoveElementAt(index); |
5523 | 0 | RemoveAnonContentFromCanvas(aContent, GetShell()); |
5524 | 0 |
|
5525 | 0 | if (mAnonymousContents.IsEmpty() && GetCustomContentContainer(GetShell())) { |
5526 | 0 | GetShell()->GetCanvasFrame()->HideCustomContentContainer(); |
5527 | 0 | } |
5528 | 0 | } |
5529 | | |
5530 | | Element* |
5531 | | nsIDocument::GetAnonRootIfInAnonymousContentContainer(nsINode* aNode) const |
5532 | 0 | { |
5533 | 0 | if (!aNode->IsInNativeAnonymousSubtree()) { |
5534 | 0 | return nullptr; |
5535 | 0 | } |
5536 | 0 | |
5537 | 0 | nsIPresShell* shell = GetShell(); |
5538 | 0 | if (!shell || !shell->GetCanvasFrame()) { |
5539 | 0 | return nullptr; |
5540 | 0 | } |
5541 | 0 | |
5542 | 0 | nsAutoScriptBlocker scriptBlocker; |
5543 | 0 | nsCOMPtr<Element> customContainer = shell->GetCanvasFrame() |
5544 | 0 | ->GetCustomContentContainer(); |
5545 | 0 | if (!customContainer) { |
5546 | 0 | return nullptr; |
5547 | 0 | } |
5548 | 0 | |
5549 | 0 | // An arbitrary number of elements can be inserted as children of the custom |
5550 | 0 | // container frame. We want the one that was added that contains aNode, so |
5551 | 0 | // we need to keep track of the last child separately using |child| here. |
5552 | 0 | nsINode* child = aNode; |
5553 | 0 | nsINode* parent = aNode->GetParentNode(); |
5554 | 0 | while (parent && parent->IsInNativeAnonymousSubtree()) { |
5555 | 0 | if (parent == customContainer) { |
5556 | 0 | return Element::FromNode(child); |
5557 | 0 | } |
5558 | 0 | child = parent; |
5559 | 0 | parent = child->GetParentNode(); |
5560 | 0 | } |
5561 | 0 | return nullptr; |
5562 | 0 | } |
5563 | | |
5564 | | Maybe<ClientInfo> |
5565 | | nsIDocument::GetClientInfo() const |
5566 | 0 | { |
5567 | 0 | nsPIDOMWindowInner* inner = GetInnerWindow(); |
5568 | 0 | if (inner) { |
5569 | 0 | return inner->GetClientInfo(); |
5570 | 0 | } |
5571 | 0 | return Maybe<ClientInfo>(); |
5572 | 0 | } |
5573 | | |
5574 | | Maybe<ClientState> |
5575 | | nsIDocument::GetClientState() const |
5576 | 0 | { |
5577 | 0 | nsPIDOMWindowInner* inner = GetInnerWindow(); |
5578 | 0 | if (inner) { |
5579 | 0 | return inner->GetClientState(); |
5580 | 0 | } |
5581 | 0 | return Maybe<ClientState>(); |
5582 | 0 | } |
5583 | | |
5584 | | Maybe<ServiceWorkerDescriptor> |
5585 | | nsIDocument::GetController() const |
5586 | 0 | { |
5587 | 0 | nsPIDOMWindowInner* inner = GetInnerWindow(); |
5588 | 0 | if (inner) { |
5589 | 0 | return inner->GetController(); |
5590 | 0 | } |
5591 | 0 | return Maybe<ServiceWorkerDescriptor>(); |
5592 | 0 | } |
5593 | | |
5594 | | // |
5595 | | // nsIDocument interface |
5596 | | // |
5597 | | DocumentType* |
5598 | | nsIDocument::GetDoctype() const |
5599 | 0 | { |
5600 | 0 | for (nsIContent* child = GetFirstChild(); |
5601 | 0 | child; |
5602 | 0 | child = child->GetNextSibling()) { |
5603 | 0 | if (child->NodeType() == DOCUMENT_TYPE_NODE) { |
5604 | 0 | return static_cast<DocumentType*>(child); |
5605 | 0 | } |
5606 | 0 | } |
5607 | 0 | return nullptr; |
5608 | 0 | } |
5609 | | |
5610 | | DOMImplementation* |
5611 | | nsIDocument::GetImplementation(ErrorResult& rv) |
5612 | 0 | { |
5613 | 0 | if (!mDOMImplementation) { |
5614 | 0 | nsCOMPtr<nsIURI> uri; |
5615 | 0 | NS_NewURI(getter_AddRefs(uri), "about:blank"); |
5616 | 0 | if (!uri) { |
5617 | 0 | rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
5618 | 0 | return nullptr; |
5619 | 0 | } |
5620 | 0 | bool hasHadScriptObject = true; |
5621 | 0 | nsIScriptGlobalObject* scriptObject = |
5622 | 0 | GetScriptHandlingObject(hasHadScriptObject); |
5623 | 0 | if (!scriptObject && hasHadScriptObject) { |
5624 | 0 | rv.Throw(NS_ERROR_UNEXPECTED); |
5625 | 0 | return nullptr; |
5626 | 0 | } |
5627 | 0 | mDOMImplementation = new DOMImplementation(this, |
5628 | 0 | scriptObject ? scriptObject : GetScopeObject(), uri, uri); |
5629 | 0 | } |
5630 | 0 |
|
5631 | 0 | return mDOMImplementation; |
5632 | 0 | } |
5633 | | |
5634 | | bool IsLowercaseASCII(const nsAString& aValue) |
5635 | 0 | { |
5636 | 0 | int32_t len = aValue.Length(); |
5637 | 0 | for (int32_t i = 0; i < len; ++i) { |
5638 | 0 | char16_t c = aValue[i]; |
5639 | 0 | if (!(0x0061 <= (c) && ((c) <= 0x007a))) { |
5640 | 0 | return false; |
5641 | 0 | } |
5642 | 0 | } |
5643 | 0 | return true; |
5644 | 0 | } |
5645 | | |
5646 | | // We only support pseudo-elements with two colons in this function. |
5647 | | static CSSPseudoElementType |
5648 | | GetPseudoElementType(const nsString& aString, ErrorResult& aRv) |
5649 | 0 | { |
5650 | 0 | MOZ_ASSERT(!aString.IsEmpty(), "GetPseudoElementType aString should be non-null"); |
5651 | 0 | if (aString.Length() <= 2 || aString[0] != ':' || aString[1] != ':') { |
5652 | 0 | aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
5653 | 0 | return CSSPseudoElementType::NotPseudo; |
5654 | 0 | } |
5655 | 0 | RefPtr<nsAtom> pseudo = NS_Atomize(Substring(aString, 1)); |
5656 | 0 | return nsCSSPseudoElements::GetPseudoType(pseudo, |
5657 | 0 | CSSEnabledState::eInUASheets); |
5658 | 0 | } |
5659 | | |
5660 | | already_AddRefed<Element> |
5661 | | nsIDocument::CreateElement(const nsAString& aTagName, |
5662 | | const ElementCreationOptionsOrString& aOptions, |
5663 | | ErrorResult& rv) |
5664 | 0 | { |
5665 | 0 | rv = nsContentUtils::CheckQName(aTagName, false); |
5666 | 0 | if (rv.Failed()) { |
5667 | 0 | return nullptr; |
5668 | 0 | } |
5669 | 0 | |
5670 | 0 | bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName); |
5671 | 0 | nsAutoString lcTagName; |
5672 | 0 | if (needsLowercase) { |
5673 | 0 | nsContentUtils::ASCIIToLower(aTagName, lcTagName); |
5674 | 0 | } |
5675 | 0 |
|
5676 | 0 | const nsString* is = nullptr; |
5677 | 0 | CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo; |
5678 | 0 | if (aOptions.IsElementCreationOptions()) { |
5679 | 0 | const ElementCreationOptions& options = |
5680 | 0 | aOptions.GetAsElementCreationOptions(); |
5681 | 0 |
|
5682 | 0 | if (CustomElementRegistry::IsCustomElementEnabled(this) && |
5683 | 0 | options.mIs.WasPassed()) { |
5684 | 0 | is = &options.mIs.Value(); |
5685 | 0 | } |
5686 | 0 |
|
5687 | 0 | // Check 'pseudo' and throw an exception if it's not one allowed |
5688 | 0 | // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC. |
5689 | 0 | if (options.mPseudo.WasPassed()) { |
5690 | 0 | pseudoType = GetPseudoElementType(options.mPseudo.Value(), rv); |
5691 | 0 | if (rv.Failed() || |
5692 | 0 | pseudoType == CSSPseudoElementType::NotPseudo || |
5693 | 0 | !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType)) { |
5694 | 0 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
5695 | 0 | return nullptr; |
5696 | 0 | } |
5697 | 0 | } |
5698 | 0 | } |
5699 | 0 | |
5700 | 0 | RefPtr<Element> elem = CreateElem( |
5701 | 0 | needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is); |
5702 | 0 |
|
5703 | 0 | if (pseudoType != CSSPseudoElementType::NotPseudo) { |
5704 | 0 | elem->SetPseudoElementType(pseudoType); |
5705 | 0 | } |
5706 | 0 |
|
5707 | 0 | return elem.forget(); |
5708 | 0 | } |
5709 | | |
5710 | | already_AddRefed<Element> |
5711 | | nsIDocument::CreateElementNS(const nsAString& aNamespaceURI, |
5712 | | const nsAString& aQualifiedName, |
5713 | | const ElementCreationOptionsOrString& aOptions, |
5714 | | ErrorResult& rv) |
5715 | 0 | { |
5716 | 0 | RefPtr<mozilla::dom::NodeInfo> nodeInfo; |
5717 | 0 | rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, |
5718 | 0 | aQualifiedName, |
5719 | 0 | mNodeInfoManager, |
5720 | 0 | ELEMENT_NODE, |
5721 | 0 | getter_AddRefs(nodeInfo)); |
5722 | 0 | if (rv.Failed()) { |
5723 | 0 | return nullptr; |
5724 | 0 | } |
5725 | 0 | |
5726 | 0 | const nsString* is = nullptr; |
5727 | 0 | if (CustomElementRegistry::IsCustomElementEnabled(this) && |
5728 | 0 | aOptions.IsElementCreationOptions()) { |
5729 | 0 | const ElementCreationOptions& options = aOptions.GetAsElementCreationOptions(); |
5730 | 0 | if (options.mIs.WasPassed()) { |
5731 | 0 | is = &options.mIs.Value(); |
5732 | 0 | } |
5733 | 0 | } |
5734 | 0 |
|
5735 | 0 | nsCOMPtr<Element> element; |
5736 | 0 | rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), |
5737 | 0 | NOT_FROM_PARSER, is); |
5738 | 0 | if (rv.Failed()) { |
5739 | 0 | return nullptr; |
5740 | 0 | } |
5741 | 0 | |
5742 | 0 | return element.forget(); |
5743 | 0 | } |
5744 | | |
5745 | | already_AddRefed<Element> |
5746 | | nsIDocument::CreateXULElement(const nsAString& aTagName, |
5747 | | const ElementCreationOptionsOrString& aOptions, |
5748 | | ErrorResult& aRv) |
5749 | 0 | { |
5750 | 0 | aRv = nsContentUtils::CheckQName(aTagName, false); |
5751 | 0 | if (aRv.Failed()) { |
5752 | 0 | return nullptr; |
5753 | 0 | } |
5754 | 0 | |
5755 | 0 | const nsString* is = nullptr; |
5756 | 0 | if (CustomElementRegistry::IsCustomElementEnabled(this) && |
5757 | 0 | aOptions.IsElementCreationOptions()) { |
5758 | 0 | const ElementCreationOptions& options = aOptions.GetAsElementCreationOptions(); |
5759 | 0 | if (options.mIs.WasPassed()) { |
5760 | 0 | is = &options.mIs.Value(); |
5761 | 0 | } |
5762 | 0 | } |
5763 | 0 |
|
5764 | 0 | RefPtr<Element> elem = CreateElem(aTagName, nullptr, kNameSpaceID_XUL, is); |
5765 | 0 | if (!elem) { |
5766 | 0 | aRv.Throw(NS_ERROR_NOT_AVAILABLE); |
5767 | 0 | return nullptr; |
5768 | 0 | } |
5769 | 0 | return elem.forget(); |
5770 | 0 | } |
5771 | | |
5772 | | already_AddRefed<nsTextNode> |
5773 | | nsIDocument::CreateEmptyTextNode() const |
5774 | 0 | { |
5775 | 0 | RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager); |
5776 | 0 | return text.forget(); |
5777 | 0 | } |
5778 | | |
5779 | | already_AddRefed<nsTextNode> |
5780 | | nsIDocument::CreateTextNode(const nsAString& aData) const |
5781 | 0 | { |
5782 | 0 | RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager); |
5783 | 0 | // Don't notify; this node is still being created. |
5784 | 0 | text->SetText(aData, false); |
5785 | 0 | return text.forget(); |
5786 | 0 | } |
5787 | | |
5788 | | already_AddRefed<DocumentFragment> |
5789 | | nsIDocument::CreateDocumentFragment() const |
5790 | 0 | { |
5791 | 0 | RefPtr<DocumentFragment> frag = new DocumentFragment(mNodeInfoManager); |
5792 | 0 | return frag.forget(); |
5793 | 0 | } |
5794 | | |
5795 | | // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers. |
5796 | | already_AddRefed<dom::Comment> |
5797 | | nsIDocument::CreateComment(const nsAString& aData) const |
5798 | 0 | { |
5799 | 0 | RefPtr<dom::Comment> comment = new dom::Comment(mNodeInfoManager); |
5800 | 0 |
|
5801 | 0 | // Don't notify; this node is still being created. |
5802 | 0 | comment->SetText(aData, false); |
5803 | 0 | return comment.forget(); |
5804 | 0 | } |
5805 | | |
5806 | | already_AddRefed<CDATASection> |
5807 | | nsIDocument::CreateCDATASection(const nsAString& aData, |
5808 | | ErrorResult& rv) |
5809 | 0 | { |
5810 | 0 | if (IsHTMLDocument()) { |
5811 | 0 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
5812 | 0 | return nullptr; |
5813 | 0 | } |
5814 | 0 | |
5815 | 0 | if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) { |
5816 | 0 | rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR); |
5817 | 0 | return nullptr; |
5818 | 0 | } |
5819 | 0 | |
5820 | 0 | RefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager); |
5821 | 0 |
|
5822 | 0 | // Don't notify; this node is still being created. |
5823 | 0 | cdata->SetText(aData, false); |
5824 | 0 |
|
5825 | 0 | return cdata.forget(); |
5826 | 0 | } |
5827 | | |
5828 | | already_AddRefed<ProcessingInstruction> |
5829 | | nsIDocument::CreateProcessingInstruction(const nsAString& aTarget, |
5830 | | const nsAString& aData, |
5831 | | ErrorResult& rv) const |
5832 | 0 | { |
5833 | 0 | nsresult res = nsContentUtils::CheckQName(aTarget, false); |
5834 | 0 | if (NS_FAILED(res)) { |
5835 | 0 | rv.Throw(res); |
5836 | 0 | return nullptr; |
5837 | 0 | } |
5838 | 0 | |
5839 | 0 | if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) { |
5840 | 0 | rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR); |
5841 | 0 | return nullptr; |
5842 | 0 | } |
5843 | 0 | |
5844 | 0 | RefPtr<ProcessingInstruction> pi = |
5845 | 0 | NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData); |
5846 | 0 |
|
5847 | 0 | return pi.forget(); |
5848 | 0 | } |
5849 | | |
5850 | | already_AddRefed<Attr> |
5851 | | nsIDocument::CreateAttribute(const nsAString& aName, ErrorResult& rv) |
5852 | 0 | { |
5853 | 0 | if (!mNodeInfoManager) { |
5854 | 0 | rv.Throw(NS_ERROR_NOT_INITIALIZED); |
5855 | 0 | return nullptr; |
5856 | 0 | } |
5857 | 0 | |
5858 | 0 | nsresult res = nsContentUtils::CheckQName(aName, false); |
5859 | 0 | if (NS_FAILED(res)) { |
5860 | 0 | rv.Throw(res); |
5861 | 0 | return nullptr; |
5862 | 0 | } |
5863 | 0 | |
5864 | 0 | nsAutoString name; |
5865 | 0 | if (IsHTMLDocument()) { |
5866 | 0 | nsContentUtils::ASCIIToLower(aName, name); |
5867 | 0 | } else { |
5868 | 0 | name = aName; |
5869 | 0 | } |
5870 | 0 |
|
5871 | 0 | RefPtr<mozilla::dom::NodeInfo> nodeInfo; |
5872 | 0 | res = mNodeInfoManager->GetNodeInfo(name, nullptr, kNameSpaceID_None, |
5873 | 0 | ATTRIBUTE_NODE, getter_AddRefs(nodeInfo)); |
5874 | 0 | if (NS_FAILED(res)) { |
5875 | 0 | rv.Throw(res); |
5876 | 0 | return nullptr; |
5877 | 0 | } |
5878 | 0 | |
5879 | 0 | RefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(), |
5880 | 0 | EmptyString()); |
5881 | 0 | return attribute.forget(); |
5882 | 0 | } |
5883 | | |
5884 | | already_AddRefed<Attr> |
5885 | | nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI, |
5886 | | const nsAString& aQualifiedName, |
5887 | | ErrorResult& rv) |
5888 | 0 | { |
5889 | 0 | RefPtr<mozilla::dom::NodeInfo> nodeInfo; |
5890 | 0 | rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, |
5891 | 0 | aQualifiedName, |
5892 | 0 | mNodeInfoManager, |
5893 | 0 | ATTRIBUTE_NODE, |
5894 | 0 | getter_AddRefs(nodeInfo)); |
5895 | 0 | if (rv.Failed()) { |
5896 | 0 | return nullptr; |
5897 | 0 | } |
5898 | 0 | |
5899 | 0 | RefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(), |
5900 | 0 | EmptyString()); |
5901 | 0 | return attribute.forget(); |
5902 | 0 | } |
5903 | | |
5904 | | void |
5905 | | nsIDocument::ResolveScheduledSVGPresAttrs() |
5906 | 0 | { |
5907 | 0 | for (auto iter = mLazySVGPresElements.Iter(); !iter.Done(); iter.Next()) { |
5908 | 0 | nsSVGElement* svg = iter.Get()->GetKey(); |
5909 | 0 | svg->UpdateContentDeclarationBlock(); |
5910 | 0 | } |
5911 | 0 | mLazySVGPresElements.Clear(); |
5912 | 0 | } |
5913 | | |
5914 | | already_AddRefed<nsSimpleContentList> |
5915 | | nsIDocument::BlockedTrackingNodes() const |
5916 | 0 | { |
5917 | 0 | RefPtr<nsSimpleContentList> list = new nsSimpleContentList(nullptr); |
5918 | 0 |
|
5919 | 0 | nsTArray<nsWeakPtr> blockedTrackingNodes; |
5920 | 0 | blockedTrackingNodes = mBlockedTrackingNodes; |
5921 | 0 |
|
5922 | 0 | for (unsigned long i = 0; i < blockedTrackingNodes.Length(); i++) { |
5923 | 0 | nsWeakPtr weakNode = blockedTrackingNodes[i]; |
5924 | 0 | nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode); |
5925 | 0 | // Consider only nodes to which we have managed to get strong references. |
5926 | 0 | // Coping with nullptrs since it's expected for nodes to disappear when |
5927 | 0 | // nobody else is referring to them. |
5928 | 0 | if (node) { |
5929 | 0 | list->AppendElement(node); |
5930 | 0 | } |
5931 | 0 | } |
5932 | 0 |
|
5933 | 0 | return list.forget(); |
5934 | 0 | } |
5935 | | |
5936 | | void |
5937 | | nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet) |
5938 | 0 | { |
5939 | 0 | aSheetSet.Truncate(); |
5940 | 0 |
|
5941 | 0 | // Look through our sheets, find the selected set title |
5942 | 0 | size_t count = SheetCount(); |
5943 | 0 | nsAutoString title; |
5944 | 0 | for (size_t index = 0; index < count; index++) { |
5945 | 0 | StyleSheet* sheet = SheetAt(index); |
5946 | 0 | NS_ASSERTION(sheet, "Null sheet in sheet list!"); |
5947 | 0 |
|
5948 | 0 | if (sheet->Disabled()) { |
5949 | 0 | // Disabled sheets don't affect the currently selected set |
5950 | 0 | continue; |
5951 | 0 | } |
5952 | 0 | |
5953 | 0 | sheet->GetTitle(title); |
5954 | 0 |
|
5955 | 0 | if (aSheetSet.IsEmpty()) { |
5956 | 0 | aSheetSet = title; |
5957 | 0 | } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) { |
5958 | 0 | // Sheets from multiple sets enabled; return null string, per spec. |
5959 | 0 | SetDOMStringToNull(aSheetSet); |
5960 | 0 | return; |
5961 | 0 | } |
5962 | 0 | } |
5963 | 0 | } |
5964 | | |
5965 | | void |
5966 | | nsIDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet) |
5967 | 0 | { |
5968 | 0 | if (DOMStringIsNull(aSheetSet)) { |
5969 | 0 | return; |
5970 | 0 | } |
5971 | 0 | |
5972 | 0 | // Must update mLastStyleSheetSet before doing anything else with stylesheets |
5973 | 0 | // or CSSLoaders. |
5974 | 0 | mLastStyleSheetSet = aSheetSet; |
5975 | 0 | EnableStyleSheetsForSetInternal(aSheetSet, true); |
5976 | 0 | } |
5977 | | |
5978 | | void |
5979 | | nsIDocument::SetPreferredStyleSheetSet(const nsAString& aSheetSet) |
5980 | 0 | { |
5981 | 0 | mPreferredStyleSheetSet = aSheetSet; |
5982 | 0 | // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per |
5983 | 0 | // spec. |
5984 | 0 | if (DOMStringIsNull(mLastStyleSheetSet)) { |
5985 | 0 | // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet, |
5986 | 0 | // per spec. The idea here is that we're changing our preferred set and |
5987 | 0 | // that shouldn't change the value of lastStyleSheetSet. Also, we're |
5988 | 0 | // using the Internal version so we can update the CSSLoader and not have |
5989 | 0 | // to worry about null strings. |
5990 | 0 | EnableStyleSheetsForSetInternal(aSheetSet, true); |
5991 | 0 | } |
5992 | 0 | } |
5993 | | |
5994 | | DOMStringList* |
5995 | | nsIDocument::StyleSheetSets() |
5996 | 0 | { |
5997 | 0 | if (!mStyleSheetSetList) { |
5998 | 0 | mStyleSheetSetList = new nsDOMStyleSheetSetList(this); |
5999 | 0 | } |
6000 | 0 | return mStyleSheetSetList; |
6001 | 0 | } |
6002 | | |
6003 | | void |
6004 | | nsIDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet) |
6005 | 0 | { |
6006 | 0 | // Per spec, passing in null is a no-op. |
6007 | 0 | if (!DOMStringIsNull(aSheetSet)) { |
6008 | 0 | // Note: must make sure to not change the CSSLoader's preferred sheet -- |
6009 | 0 | // that value should be equal to either our lastStyleSheetSet (if that's |
6010 | 0 | // non-null) or to our preferredStyleSheetSet. And this method doesn't |
6011 | 0 | // change either of those. |
6012 | 0 | EnableStyleSheetsForSetInternal(aSheetSet, false); |
6013 | 0 | } |
6014 | 0 | } |
6015 | | |
6016 | | void |
6017 | | nsIDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet, |
6018 | | bool aUpdateCSSLoader) |
6019 | 0 | { |
6020 | 0 | size_t count = SheetCount(); |
6021 | 0 | nsAutoString title; |
6022 | 0 | for (size_t index = 0; index < count; index++) { |
6023 | 0 | StyleSheet* sheet = SheetAt(index); |
6024 | 0 | NS_ASSERTION(sheet, "Null sheet in sheet list!"); |
6025 | 0 |
|
6026 | 0 | sheet->GetTitle(title); |
6027 | 0 | if (!title.IsEmpty()) { |
6028 | 0 | sheet->SetEnabled(title.Equals(aSheetSet)); |
6029 | 0 | } |
6030 | 0 | } |
6031 | 0 | if (aUpdateCSSLoader) { |
6032 | 0 | CSSLoader()->DocumentStyleSheetSetChanged(); |
6033 | 0 | } |
6034 | 0 | if (nsIPresShell* shell = GetShell()) { |
6035 | 0 | if (shell->StyleSet()->StyleSheetsHaveChanged()) { |
6036 | 0 | shell->ApplicableStylesChanged(); |
6037 | 0 | } |
6038 | 0 | } |
6039 | 0 | } |
6040 | | |
6041 | | void |
6042 | | nsIDocument::GetCharacterSet(nsAString& aCharacterSet) const |
6043 | 0 | { |
6044 | 0 | nsAutoCString charset; |
6045 | 0 | GetDocumentCharacterSet()->Name(charset); |
6046 | 0 | CopyASCIItoUTF16(charset, aCharacterSet); |
6047 | 0 | } |
6048 | | |
6049 | | already_AddRefed<nsINode> |
6050 | | nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const |
6051 | 0 | { |
6052 | 0 | nsINode* imported = &aNode; |
6053 | 0 |
|
6054 | 0 | switch (imported->NodeType()) { |
6055 | 0 | case DOCUMENT_NODE: |
6056 | 0 | { |
6057 | 0 | break; |
6058 | 0 | } |
6059 | 0 | case DOCUMENT_FRAGMENT_NODE: |
6060 | 0 | case ATTRIBUTE_NODE: |
6061 | 0 | case ELEMENT_NODE: |
6062 | 0 | case PROCESSING_INSTRUCTION_NODE: |
6063 | 0 | case TEXT_NODE: |
6064 | 0 | case CDATA_SECTION_NODE: |
6065 | 0 | case COMMENT_NODE: |
6066 | 0 | case DOCUMENT_TYPE_NODE: |
6067 | 0 | { |
6068 | 0 | return nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager, nullptr, rv); |
6069 | 0 | } |
6070 | 0 | default: |
6071 | 0 | { |
6072 | 0 | NS_WARNING("Don't know how to clone this nodetype for importNode."); |
6073 | 0 | } |
6074 | 0 | } |
6075 | 0 |
|
6076 | 0 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
6077 | 0 | return nullptr; |
6078 | 0 | } |
6079 | | |
6080 | | void |
6081 | | nsIDocument::LoadBindingDocument(const nsAString& aURI, |
6082 | | nsIPrincipal& aSubjectPrincipal, |
6083 | | ErrorResult& rv) |
6084 | 0 | { |
6085 | 0 | nsCOMPtr<nsIURI> uri; |
6086 | 0 | rv = NS_NewURI(getter_AddRefs(uri), aURI, mCharacterSet, GetDocBaseURI()); |
6087 | 0 | if (rv.Failed()) { |
6088 | 0 | return; |
6089 | 0 | } |
6090 | 0 | |
6091 | 0 | BindingManager()->LoadBindingDocument(this, uri, &aSubjectPrincipal); |
6092 | 0 | } |
6093 | | |
6094 | | Element* |
6095 | | nsIDocument::GetBindingParent(nsINode& aNode) |
6096 | 0 | { |
6097 | 0 | nsCOMPtr<nsIContent> content(do_QueryInterface(&aNode)); |
6098 | 0 | if (!content) |
6099 | 0 | return nullptr; |
6100 | 0 | |
6101 | 0 | nsIContent* bindingParent = content->GetBindingParent(); |
6102 | 0 | return bindingParent ? bindingParent->AsElement() : nullptr; |
6103 | 0 | } |
6104 | | |
6105 | | static Element* |
6106 | | GetElementByAttribute(Element* aElement, nsAtom* aAttrName, |
6107 | | const nsAString& aAttrValue, bool aUniversalMatch) |
6108 | 0 | { |
6109 | 0 | if (aUniversalMatch ? aElement->HasAttr(kNameSpaceID_None, aAttrName) : |
6110 | 0 | aElement->AttrValueIs(kNameSpaceID_None, aAttrName, |
6111 | 0 | aAttrValue, eCaseMatters)) { |
6112 | 0 | return aElement; |
6113 | 0 | } |
6114 | 0 | |
6115 | 0 | for (nsIContent* child = aElement->GetFirstChild(); |
6116 | 0 | child; |
6117 | 0 | child = child->GetNextSibling()) { |
6118 | 0 | if (!child->IsElement()) { |
6119 | 0 | continue; |
6120 | 0 | } |
6121 | 0 | |
6122 | 0 | Element* matchedElement = |
6123 | 0 | GetElementByAttribute(child->AsElement(), aAttrName, aAttrValue, |
6124 | 0 | aUniversalMatch); |
6125 | 0 | if (matchedElement) |
6126 | 0 | return matchedElement; |
6127 | 0 | } |
6128 | 0 |
|
6129 | 0 | return nullptr; |
6130 | 0 | } |
6131 | | |
6132 | | Element* |
6133 | | nsIDocument::GetAnonymousElementByAttribute(nsIContent* aElement, |
6134 | | nsAtom* aAttrName, |
6135 | | const nsAString& aAttrValue) const |
6136 | 0 | { |
6137 | 0 | nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement); |
6138 | 0 | if (!nodeList) |
6139 | 0 | return nullptr; |
6140 | 0 | |
6141 | 0 | uint32_t length = nodeList->Length(); |
6142 | 0 |
|
6143 | 0 | bool universalMatch = aAttrValue.EqualsLiteral("*"); |
6144 | 0 |
|
6145 | 0 | for (uint32_t i = 0; i < length; ++i) { |
6146 | 0 | Element* current = Element::FromNode(nodeList->Item(i)); |
6147 | 0 | if (!current) { |
6148 | 0 | continue; |
6149 | 0 | } |
6150 | 0 | |
6151 | 0 | Element* matchedElm = |
6152 | 0 | GetElementByAttribute(current, aAttrName, aAttrValue, universalMatch); |
6153 | 0 | if (matchedElm) |
6154 | 0 | return matchedElm; |
6155 | 0 | } |
6156 | 0 |
|
6157 | 0 | return nullptr; |
6158 | 0 | } |
6159 | | |
6160 | | Element* |
6161 | | nsIDocument::GetAnonymousElementByAttribute(Element& aElement, |
6162 | | const nsAString& aAttrName, |
6163 | | const nsAString& aAttrValue) |
6164 | 0 | { |
6165 | 0 | RefPtr<nsAtom> attribute = NS_Atomize(aAttrName); |
6166 | 0 |
|
6167 | 0 | return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue); |
6168 | 0 | } |
6169 | | |
6170 | | nsINodeList* |
6171 | | nsIDocument::GetAnonymousNodes(Element& aElement) |
6172 | 0 | { |
6173 | 0 | return BindingManager()->GetAnonymousNodesFor(&aElement); |
6174 | 0 | } |
6175 | | |
6176 | | already_AddRefed<nsRange> |
6177 | | nsIDocument::CreateRange(ErrorResult& rv) |
6178 | 0 | { |
6179 | 0 | RefPtr<nsRange> range = new nsRange(this); |
6180 | 0 | nsresult res = range->CollapseTo(this, 0); |
6181 | 0 | if (NS_FAILED(res)) { |
6182 | 0 | rv.Throw(res); |
6183 | 0 | return nullptr; |
6184 | 0 | } |
6185 | 0 | |
6186 | 0 | return range.forget(); |
6187 | 0 | } |
6188 | | |
6189 | | already_AddRefed<NodeIterator> |
6190 | | nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow, |
6191 | | NodeFilter* aFilter, |
6192 | | ErrorResult& rv) const |
6193 | 0 | { |
6194 | 0 | RefPtr<NodeIterator> iterator = new NodeIterator(&aRoot, aWhatToShow, |
6195 | 0 | aFilter); |
6196 | 0 | return iterator.forget(); |
6197 | 0 | } |
6198 | | |
6199 | | already_AddRefed<TreeWalker> |
6200 | | nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow, |
6201 | | NodeFilter* aFilter, |
6202 | | ErrorResult& rv) const |
6203 | 0 | { |
6204 | 0 | RefPtr<TreeWalker> walker = new TreeWalker(&aRoot, aWhatToShow, aFilter); |
6205 | 0 | return walker.forget(); |
6206 | 0 | } |
6207 | | |
6208 | | |
6209 | | already_AddRefed<Location> |
6210 | | nsIDocument::GetLocation() const |
6211 | 0 | { |
6212 | 0 | nsCOMPtr<nsPIDOMWindowInner> w = do_QueryInterface(mScriptGlobalObject); |
6213 | 0 |
|
6214 | 0 | if (!w) { |
6215 | 0 | return nullptr; |
6216 | 0 | } |
6217 | 0 | |
6218 | 0 | nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(w); |
6219 | 0 | RefPtr<Location> loc = window->GetLocation(); |
6220 | 0 | return loc.forget(); |
6221 | 0 | } |
6222 | | |
6223 | | Element* |
6224 | | nsIDocument::GetHtmlElement() const |
6225 | 0 | { |
6226 | 0 | Element* rootElement = GetRootElement(); |
6227 | 0 | if (rootElement && rootElement->IsHTMLElement(nsGkAtoms::html)) |
6228 | 0 | return rootElement; |
6229 | 0 | return nullptr; |
6230 | 0 | } |
6231 | | |
6232 | | Element* |
6233 | | nsIDocument::GetHtmlChildElement(nsAtom* aTag) |
6234 | 0 | { |
6235 | 0 | Element* html = GetHtmlElement(); |
6236 | 0 | if (!html) |
6237 | 0 | return nullptr; |
6238 | 0 | |
6239 | 0 | // Look for the element with aTag inside html. This needs to run |
6240 | 0 | // forwards to find the first such element. |
6241 | 0 | for (nsIContent* child = html->GetFirstChild(); |
6242 | 0 | child; |
6243 | 0 | child = child->GetNextSibling()) { |
6244 | 0 | if (child->IsHTMLElement(aTag)) |
6245 | 0 | return child->AsElement(); |
6246 | 0 | } |
6247 | 0 | return nullptr; |
6248 | 0 | } |
6249 | | |
6250 | | nsGenericHTMLElement* |
6251 | | nsIDocument::GetBody() |
6252 | 0 | { |
6253 | 0 | Element* html = GetHtmlElement(); |
6254 | 0 | if (!html) { |
6255 | 0 | return nullptr; |
6256 | 0 | } |
6257 | 0 | |
6258 | 0 | for (nsIContent* child = html->GetFirstChild(); |
6259 | 0 | child; |
6260 | 0 | child = child->GetNextSibling()) { |
6261 | 0 | if (child->IsHTMLElement(nsGkAtoms::body) || |
6262 | 0 | child->IsHTMLElement(nsGkAtoms::frameset)) { |
6263 | 0 | return static_cast<nsGenericHTMLElement*>(child); |
6264 | 0 | } |
6265 | 0 | } |
6266 | 0 |
|
6267 | 0 | return nullptr; |
6268 | 0 | } |
6269 | | |
6270 | | void |
6271 | | nsIDocument::SetBody(nsGenericHTMLElement* newBody, ErrorResult& rv) |
6272 | 0 | { |
6273 | 0 | nsCOMPtr<Element> root = GetRootElement(); |
6274 | 0 |
|
6275 | 0 | // The body element must be either a body tag or a frameset tag. And we must |
6276 | 0 | // have a root element to be able to add kids to it. |
6277 | 0 | if (!newBody || |
6278 | 0 | !newBody->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset) || |
6279 | 0 | !root) { |
6280 | 0 | rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
6281 | 0 | return; |
6282 | 0 | } |
6283 | 0 | |
6284 | 0 | // Use DOM methods so that we pass through the appropriate security checks. |
6285 | 0 | nsCOMPtr<Element> currentBody = GetBody(); |
6286 | 0 | if (currentBody) { |
6287 | 0 | root->ReplaceChild(*newBody, *currentBody, rv); |
6288 | 0 | } else { |
6289 | 0 | root->AppendChild(*newBody, rv); |
6290 | 0 | } |
6291 | 0 | } |
6292 | | |
6293 | | HTMLSharedElement* |
6294 | | nsIDocument::GetHead() |
6295 | 0 | { |
6296 | 0 | return static_cast<HTMLSharedElement*>(GetHeadElement()); |
6297 | 0 | } |
6298 | | |
6299 | | Element* |
6300 | | nsIDocument::GetTitleElement() |
6301 | 0 | { |
6302 | 0 | // mMayHaveTitleElement will have been set to true if any HTML or SVG |
6303 | 0 | // <title> element has been bound to this document. So if it's false, |
6304 | 0 | // we know there is nothing to do here. This avoids us having to search |
6305 | 0 | // the whole DOM if someone calls document.title on a large document |
6306 | 0 | // without a title. |
6307 | 0 | if (!mMayHaveTitleElement) |
6308 | 0 | return nullptr; |
6309 | 0 | |
6310 | 0 | Element* root = GetRootElement(); |
6311 | 0 | if (root && root->IsSVGElement(nsGkAtoms::svg)) { |
6312 | 0 | // In SVG, the document's title must be a child |
6313 | 0 | for (nsIContent* child = root->GetFirstChild(); |
6314 | 0 | child; child = child->GetNextSibling()) { |
6315 | 0 | if (child->IsSVGElement(nsGkAtoms::title)) { |
6316 | 0 | return child->AsElement(); |
6317 | 0 | } |
6318 | 0 | } |
6319 | 0 | return nullptr; |
6320 | 0 | } |
6321 | 0 | |
6322 | 0 | // We check the HTML namespace even for non-HTML documents, except SVG. This |
6323 | 0 | // matches the spec and the behavior of all tested browsers. |
6324 | 0 | // We avoid creating a live nsContentList since we don't need to watch for DOM |
6325 | 0 | // tree mutations. |
6326 | 0 | RefPtr<nsContentList> list = new nsContentList(this, kNameSpaceID_XHTML, |
6327 | 0 | nsGkAtoms::title, nsGkAtoms::title, |
6328 | 0 | /* aDeep = */ true, |
6329 | 0 | /* aLiveList = */ false); |
6330 | 0 |
|
6331 | 0 | nsIContent* first = list->Item(0, false); |
6332 | 0 |
|
6333 | 0 | return first ? first->AsElement() : nullptr; |
6334 | 0 | } |
6335 | | |
6336 | | void |
6337 | | nsIDocument::GetTitle(nsAString& aTitle) |
6338 | 0 | { |
6339 | 0 | aTitle.Truncate(); |
6340 | 0 |
|
6341 | 0 | Element* rootElement = GetRootElement(); |
6342 | 0 | if (!rootElement) { |
6343 | 0 | return; |
6344 | 0 | } |
6345 | 0 | |
6346 | 0 | nsAutoString tmp; |
6347 | 0 |
|
6348 | 0 | #ifdef MOZ_XUL |
6349 | 0 | if (rootElement->IsXULElement()) { |
6350 | 0 | rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp); |
6351 | 0 | } else |
6352 | 0 | #endif |
6353 | 0 | { |
6354 | 0 | Element* title = GetTitleElement(); |
6355 | 0 | if (!title) { |
6356 | 0 | return; |
6357 | 0 | } |
6358 | 0 | nsContentUtils::GetNodeTextContent(title, false, tmp); |
6359 | 0 | } |
6360 | 0 |
|
6361 | 0 | tmp.CompressWhitespace(); |
6362 | 0 | aTitle = tmp; |
6363 | 0 | } |
6364 | | |
6365 | | void |
6366 | | nsIDocument::SetTitle(const nsAString& aTitle, ErrorResult& aRv) |
6367 | 0 | { |
6368 | 0 | Element* rootElement = GetRootElement(); |
6369 | 0 | if (!rootElement) { |
6370 | 0 | return; |
6371 | 0 | } |
6372 | 0 | |
6373 | 0 | #ifdef MOZ_XUL |
6374 | 0 | if (rootElement->IsXULElement()) { |
6375 | 0 | aRv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title, |
6376 | 0 | aTitle, true); |
6377 | 0 | return; |
6378 | 0 | } |
6379 | 0 | #endif |
6380 | 0 | |
6381 | 0 | Maybe<mozAutoDocUpdate> updateBatch; |
6382 | 0 | nsCOMPtr<Element> title = GetTitleElement(); |
6383 | 0 | if (rootElement->IsSVGElement(nsGkAtoms::svg)) { |
6384 | 0 | if (!title) { |
6385 | 0 | // Batch updates so that mutation events don't change "the title |
6386 | 0 | // element" under us |
6387 | 0 | updateBatch.emplace(this, true); |
6388 | 0 | RefPtr<mozilla::dom::NodeInfo> titleInfo = |
6389 | 0 | mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr, |
6390 | 0 | kNameSpaceID_SVG, |
6391 | 0 | ELEMENT_NODE); |
6392 | 0 | NS_NewSVGElement(getter_AddRefs(title), titleInfo.forget(), |
6393 | 0 | NOT_FROM_PARSER); |
6394 | 0 | if (!title) { |
6395 | 0 | return; |
6396 | 0 | } |
6397 | 0 | rootElement->InsertChildBefore(title, rootElement->GetFirstChild(), true); |
6398 | 0 | } |
6399 | 0 | } else if (rootElement->IsHTMLElement()) { |
6400 | 0 | if (!title) { |
6401 | 0 | // Batch updates so that mutation events don't change "the title |
6402 | 0 | // element" under us |
6403 | 0 | updateBatch.emplace(this, true); |
6404 | 0 | Element* head = GetHeadElement(); |
6405 | 0 | if (!head) { |
6406 | 0 | return; |
6407 | 0 | } |
6408 | 0 | |
6409 | 0 | RefPtr<mozilla::dom::NodeInfo> titleInfo; |
6410 | 0 | titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr, |
6411 | 0 | kNameSpaceID_XHTML, ELEMENT_NODE); |
6412 | 0 | title = NS_NewHTMLTitleElement(titleInfo.forget()); |
6413 | 0 | if (!title) { |
6414 | 0 | return; |
6415 | 0 | } |
6416 | 0 | |
6417 | 0 | head->AppendChildTo(title, true); |
6418 | 0 | } |
6419 | 0 | } else { |
6420 | 0 | return; |
6421 | 0 | } |
6422 | 0 | |
6423 | 0 | aRv = nsContentUtils::SetNodeTextContent(title, aTitle, false); |
6424 | 0 | } |
6425 | | |
6426 | | void |
6427 | | nsIDocument::NotifyPossibleTitleChange(bool aBoundTitleElement) |
6428 | 0 | { |
6429 | 0 | NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement, |
6430 | 0 | "Setting a title while unlinking or destroying the element?"); |
6431 | 0 | if (mInUnlinkOrDeletion) { |
6432 | 0 | return; |
6433 | 0 | } |
6434 | 0 | |
6435 | 0 | if (aBoundTitleElement) { |
6436 | 0 | mMayHaveTitleElement = true; |
6437 | 0 | } |
6438 | 0 | if (mPendingTitleChangeEvent.IsPending()) |
6439 | 0 | return; |
6440 | 0 | |
6441 | 0 | MOZ_RELEASE_ASSERT(NS_IsMainThread()); |
6442 | 0 | RefPtr<nsRunnableMethod<nsIDocument, void, false>> event = |
6443 | 0 | NewNonOwningRunnableMethod("nsIDocument::DoNotifyPossibleTitleChange", |
6444 | 0 | this, |
6445 | 0 | &nsIDocument::DoNotifyPossibleTitleChange); |
6446 | 0 | nsresult rv = Dispatch(TaskCategory::Other, do_AddRef(event)); |
6447 | 0 | if (NS_SUCCEEDED(rv)) { |
6448 | 0 | mPendingTitleChangeEvent = std::move(event); |
6449 | 0 | } |
6450 | 0 | } |
6451 | | |
6452 | | void |
6453 | | nsIDocument::DoNotifyPossibleTitleChange() |
6454 | 0 | { |
6455 | 0 | mPendingTitleChangeEvent.Forget(); |
6456 | 0 | mHaveFiredTitleChange = true; |
6457 | 0 |
|
6458 | 0 | nsAutoString title; |
6459 | 0 | GetTitle(title); |
6460 | 0 |
|
6461 | 0 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
6462 | 0 | if (shell) { |
6463 | 0 | nsCOMPtr<nsISupports> container = |
6464 | 0 | shell->GetPresContext()->GetContainerWeak(); |
6465 | 0 | if (container) { |
6466 | 0 | nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container); |
6467 | 0 | if (docShellWin) { |
6468 | 0 | docShellWin->SetTitle(title); |
6469 | 0 | } |
6470 | 0 | } |
6471 | 0 | } |
6472 | 0 |
|
6473 | 0 | // Fire a DOM event for the title change. |
6474 | 0 | nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this), |
6475 | 0 | NS_LITERAL_STRING("DOMTitleChanged"), |
6476 | 0 | CanBubble::eYes, Cancelable::eYes); |
6477 | 0 | } |
6478 | | |
6479 | | already_AddRefed<BoxObject> |
6480 | | nsIDocument::GetBoxObjectFor(Element* aElement, ErrorResult& aRv) |
6481 | 0 | { |
6482 | 0 | if (!aElement) { |
6483 | 0 | aRv.Throw(NS_ERROR_UNEXPECTED); |
6484 | 0 | return nullptr; |
6485 | 0 | } |
6486 | 0 | |
6487 | 0 | nsIDocument* doc = aElement->OwnerDoc(); |
6488 | 0 | if (doc != this) { |
6489 | 0 | aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR); |
6490 | 0 | return nullptr; |
6491 | 0 | } |
6492 | 0 | |
6493 | 0 | if (!mHasWarnedAboutBoxObjects && !aElement->IsXULElement()) { |
6494 | 0 | mHasWarnedAboutBoxObjects = true; |
6495 | 0 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
6496 | 0 | NS_LITERAL_CSTRING("BoxObjects"), this, |
6497 | 0 | nsContentUtils::eDOM_PROPERTIES, |
6498 | 0 | "UseOfGetBoxObjectForWarning"); |
6499 | 0 | } |
6500 | 0 |
|
6501 | 0 | if (!mBoxObjectTable) { |
6502 | 0 | mBoxObjectTable = new nsRefPtrHashtable<nsPtrHashKey<nsIContent>, BoxObject>(6); |
6503 | 0 | } |
6504 | 0 |
|
6505 | 0 | RefPtr<BoxObject> boxObject; |
6506 | 0 | auto entry = mBoxObjectTable->LookupForAdd(aElement); |
6507 | 0 | if (entry) { |
6508 | 0 | boxObject = entry.Data(); |
6509 | 0 | return boxObject.forget(); |
6510 | 0 | } |
6511 | 0 | |
6512 | 0 | int32_t namespaceID; |
6513 | 0 | RefPtr<nsAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID); |
6514 | 0 | #ifdef MOZ_XUL |
6515 | 0 | if (namespaceID == kNameSpaceID_XUL) { |
6516 | 0 | if (tag == nsGkAtoms::tree) { |
6517 | 0 | boxObject = new TreeBoxObject(); |
6518 | 0 | } else { |
6519 | 0 | boxObject = new BoxObject(); |
6520 | 0 | } |
6521 | 0 | } else |
6522 | 0 | #endif // MOZ_XUL |
6523 | 0 | { |
6524 | 0 | boxObject = new BoxObject(); |
6525 | 0 | } |
6526 | 0 |
|
6527 | 0 | boxObject->Init(aElement); |
6528 | 0 | entry.OrInsert([&boxObject]() { return boxObject; }); |
6529 | 0 |
|
6530 | 0 | return boxObject.forget(); |
6531 | 0 | } |
6532 | | |
6533 | | void |
6534 | | nsIDocument::ClearBoxObjectFor(nsIContent* aContent) |
6535 | 0 | { |
6536 | 0 | if (mBoxObjectTable) { |
6537 | 0 | if (auto entry = mBoxObjectTable->Lookup(aContent)) { |
6538 | 0 | nsPIBoxObject* boxObject = entry.Data(); |
6539 | 0 | boxObject->Clear(); |
6540 | 0 | entry.Remove(); |
6541 | 0 | } |
6542 | 0 | } |
6543 | 0 | } |
6544 | | |
6545 | | already_AddRefed<MediaQueryList> |
6546 | | nsIDocument::MatchMedia(const nsAString& aMediaQueryList, |
6547 | | CallerType aCallerType) |
6548 | 0 | { |
6549 | 0 | RefPtr<MediaQueryList> result = |
6550 | 0 | new MediaQueryList(this, aMediaQueryList, aCallerType); |
6551 | 0 |
|
6552 | 0 | mDOMMediaQueryLists.insertBack(result); |
6553 | 0 |
|
6554 | 0 | return result.forget(); |
6555 | 0 | } |
6556 | | |
6557 | | void |
6558 | | nsIDocument::FlushSkinBindings() |
6559 | 0 | { |
6560 | 0 | BindingManager()->FlushSkinBindings(); |
6561 | 0 | } |
6562 | | |
6563 | | void |
6564 | | nsIDocument::SetMayStartLayout(bool aMayStartLayout) |
6565 | 0 | { |
6566 | 0 | mMayStartLayout = aMayStartLayout; |
6567 | 0 | if (MayStartLayout()) { |
6568 | 0 | // Before starting layout, check whether we're a toplevel chrome |
6569 | 0 | // window. If we are, setup some state so that we don't have to restyle |
6570 | 0 | // the whole tree after StartLayout. |
6571 | 0 | if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) { |
6572 | 0 | // We're the chrome document! |
6573 | 0 | win->BeforeStartLayout(); |
6574 | 0 | } |
6575 | 0 | ReadyState state = GetReadyStateEnum(); |
6576 | 0 | if (state >= READYSTATE_INTERACTIVE) { |
6577 | 0 | // DOMContentLoaded has fired already. |
6578 | 0 | MaybeResolveReadyForIdle(); |
6579 | 0 | } |
6580 | 0 | } |
6581 | 0 | } |
6582 | | |
6583 | | nsresult |
6584 | | nsIDocument::InitializeFrameLoader(nsFrameLoader* aLoader) |
6585 | 0 | { |
6586 | 0 | mInitializableFrameLoaders.RemoveElement(aLoader); |
6587 | 0 | // Don't even try to initialize. |
6588 | 0 | if (mInDestructor) { |
6589 | 0 | NS_WARNING("Trying to initialize a frame loader while" |
6590 | 0 | "document is being deleted"); |
6591 | 0 | return NS_ERROR_FAILURE; |
6592 | 0 | } |
6593 | 0 |
|
6594 | 0 | mInitializableFrameLoaders.AppendElement(aLoader); |
6595 | 0 | if (!mFrameLoaderRunner) { |
6596 | 0 | mFrameLoaderRunner = |
6597 | 0 | NewRunnableMethod("nsIDocument::MaybeInitializeFinalizeFrameLoaders", |
6598 | 0 | this, |
6599 | 0 | &nsIDocument::MaybeInitializeFinalizeFrameLoaders); |
6600 | 0 | NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY); |
6601 | 0 | nsContentUtils::AddScriptRunner(mFrameLoaderRunner); |
6602 | 0 | } |
6603 | 0 | return NS_OK; |
6604 | 0 | } |
6605 | | |
6606 | | nsresult |
6607 | | nsIDocument::FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer) |
6608 | 0 | { |
6609 | 0 | mInitializableFrameLoaders.RemoveElement(aLoader); |
6610 | 0 | if (mInDestructor) { |
6611 | 0 | return NS_ERROR_FAILURE; |
6612 | 0 | } |
6613 | 0 | |
6614 | 0 | mFrameLoaderFinalizers.AppendElement(aFinalizer); |
6615 | 0 | if (!mFrameLoaderRunner) { |
6616 | 0 | mFrameLoaderRunner = |
6617 | 0 | NewRunnableMethod("nsIDocument::MaybeInitializeFinalizeFrameLoaders", |
6618 | 0 | this, |
6619 | 0 | &nsIDocument::MaybeInitializeFinalizeFrameLoaders); |
6620 | 0 | NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY); |
6621 | 0 | nsContentUtils::AddScriptRunner(mFrameLoaderRunner); |
6622 | 0 | } |
6623 | 0 | return NS_OK; |
6624 | 0 | } |
6625 | | |
6626 | | void |
6627 | | nsIDocument::MaybeInitializeFinalizeFrameLoaders() |
6628 | 0 | { |
6629 | 0 | if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) { |
6630 | 0 | // This method will be recalled when mUpdateNestLevel drops to 0, |
6631 | 0 | // or when !mDelayFrameLoaderInitialization. |
6632 | 0 | mFrameLoaderRunner = nullptr; |
6633 | 0 | return; |
6634 | 0 | } |
6635 | 0 | |
6636 | 0 | // We're not in an update, but it is not safe to run scripts, so |
6637 | 0 | // postpone frameloader initialization and finalization. |
6638 | 0 | if (!nsContentUtils::IsSafeToRunScript()) { |
6639 | 0 | if (!mInDestructor && !mFrameLoaderRunner && |
6640 | 0 | (mInitializableFrameLoaders.Length() || |
6641 | 0 | mFrameLoaderFinalizers.Length())) { |
6642 | 0 | mFrameLoaderRunner = |
6643 | 0 | NewRunnableMethod("nsIDocument::MaybeInitializeFinalizeFrameLoaders", |
6644 | 0 | this, |
6645 | 0 | &nsIDocument::MaybeInitializeFinalizeFrameLoaders); |
6646 | 0 | nsContentUtils::AddScriptRunner(mFrameLoaderRunner); |
6647 | 0 | } |
6648 | 0 | return; |
6649 | 0 | } |
6650 | 0 | mFrameLoaderRunner = nullptr; |
6651 | 0 |
|
6652 | 0 | // Don't use a temporary array for mInitializableFrameLoaders, because |
6653 | 0 | // loading a frame may cause some other frameloader to be removed from the |
6654 | 0 | // array. But be careful to keep the loader alive when starting the load! |
6655 | 0 | while (mInitializableFrameLoaders.Length()) { |
6656 | 0 | RefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0]; |
6657 | 0 | mInitializableFrameLoaders.RemoveElementAt(0); |
6658 | 0 | NS_ASSERTION(loader, "null frameloader in the array?"); |
6659 | 0 | loader->ReallyStartLoading(); |
6660 | 0 | } |
6661 | 0 |
|
6662 | 0 | uint32_t length = mFrameLoaderFinalizers.Length(); |
6663 | 0 | if (length > 0) { |
6664 | 0 | nsTArray<nsCOMPtr<nsIRunnable> > finalizers; |
6665 | 0 | mFrameLoaderFinalizers.SwapElements(finalizers); |
6666 | 0 | for (uint32_t i = 0; i < length; ++i) { |
6667 | 0 | finalizers[i]->Run(); |
6668 | 0 | } |
6669 | 0 | } |
6670 | 0 | } |
6671 | | |
6672 | | void |
6673 | | nsIDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell) |
6674 | 0 | { |
6675 | 0 | uint32_t length = mInitializableFrameLoaders.Length(); |
6676 | 0 | for (uint32_t i = 0; i < length; ++i) { |
6677 | 0 | if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) { |
6678 | 0 | mInitializableFrameLoaders.RemoveElementAt(i); |
6679 | 0 | return; |
6680 | 0 | } |
6681 | 0 | } |
6682 | 0 | } |
6683 | | |
6684 | | nsIDocument* |
6685 | | nsIDocument::RequestExternalResource(nsIURI* aURI, |
6686 | | nsIURI* aReferrer, |
6687 | | uint32_t aReferrerPolicy, |
6688 | | nsINode* aRequestingNode, |
6689 | | ExternalResourceLoad** aPendingLoad) |
6690 | 0 | { |
6691 | 0 | MOZ_ASSERT(aURI, "Must have a URI"); |
6692 | 0 | MOZ_ASSERT(aRequestingNode, "Must have a node"); |
6693 | 0 | if (mDisplayDocument) { |
6694 | 0 | return mDisplayDocument->RequestExternalResource(aURI, |
6695 | 0 | aReferrer, |
6696 | 0 | aReferrerPolicy, |
6697 | 0 | aRequestingNode, |
6698 | 0 | aPendingLoad); |
6699 | 0 | } |
6700 | 0 | |
6701 | 0 | return mExternalResourceMap.RequestResource(aURI, aReferrer, aReferrerPolicy, |
6702 | 0 | aRequestingNode, this, |
6703 | 0 | aPendingLoad); |
6704 | 0 | } |
6705 | | |
6706 | | void |
6707 | | nsIDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData) |
6708 | 0 | { |
6709 | 0 | mExternalResourceMap.EnumerateResources(aCallback, aData); |
6710 | 0 | } |
6711 | | |
6712 | | nsSMILAnimationController* |
6713 | | nsIDocument::GetAnimationController() |
6714 | 0 | { |
6715 | 0 | // We create the animation controller lazily because most documents won't want |
6716 | 0 | // one and only SVG documents and the like will call this |
6717 | 0 | if (mAnimationController) |
6718 | 0 | return mAnimationController; |
6719 | 0 | // Refuse to create an Animation Controller for data documents. |
6720 | 0 | if (mLoadedAsData || mLoadedAsInteractiveData) |
6721 | 0 | return nullptr; |
6722 | 0 | |
6723 | 0 | mAnimationController = new nsSMILAnimationController(this); |
6724 | 0 |
|
6725 | 0 | // If there's a presContext then check the animation mode and pause if |
6726 | 0 | // necessary. |
6727 | 0 | nsPresContext* context = GetPresContext(); |
6728 | 0 | if (mAnimationController && context && |
6729 | 0 | context->ImageAnimationMode() == imgIContainer::kDontAnimMode) { |
6730 | 0 | mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF); |
6731 | 0 | } |
6732 | 0 |
|
6733 | 0 | // If we're hidden (or being hidden), notify the newly-created animation |
6734 | 0 | // controller. (Skip this check for SVG-as-an-image documents, though, |
6735 | 0 | // because they don't get OnPageShow / OnPageHide calls). |
6736 | 0 | if (!mIsShowing && !mIsBeingUsedAsImage) { |
6737 | 0 | mAnimationController->OnPageHide(); |
6738 | 0 | } |
6739 | 0 |
|
6740 | 0 | return mAnimationController; |
6741 | 0 | } |
6742 | | |
6743 | | PendingAnimationTracker* |
6744 | | nsIDocument::GetOrCreatePendingAnimationTracker() |
6745 | 0 | { |
6746 | 0 | if (!mPendingAnimationTracker) { |
6747 | 0 | mPendingAnimationTracker = new PendingAnimationTracker(this); |
6748 | 0 | } |
6749 | 0 |
|
6750 | 0 | return mPendingAnimationTracker; |
6751 | 0 | } |
6752 | | |
6753 | | /** |
6754 | | * Retrieve the "direction" property of the document. |
6755 | | * |
6756 | | * @lina 01/09/2001 |
6757 | | */ |
6758 | | void |
6759 | | nsIDocument::GetDir(nsAString& aDirection) const |
6760 | 0 | { |
6761 | 0 | aDirection.Truncate(); |
6762 | 0 | Element* rootElement = GetHtmlElement(); |
6763 | 0 | if (rootElement) { |
6764 | 0 | static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection); |
6765 | 0 | } |
6766 | 0 | } |
6767 | | |
6768 | | /** |
6769 | | * Set the "direction" property of the document. |
6770 | | * |
6771 | | * @lina 01/09/2001 |
6772 | | */ |
6773 | | void |
6774 | | nsIDocument::SetDir(const nsAString& aDirection) |
6775 | 0 | { |
6776 | 0 | Element* rootElement = GetHtmlElement(); |
6777 | 0 | if (rootElement) { |
6778 | 0 | rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, |
6779 | 0 | aDirection, true); |
6780 | 0 | } |
6781 | 0 | } |
6782 | | |
6783 | | nsIHTMLCollection* |
6784 | | nsIDocument::Images() |
6785 | 0 | { |
6786 | 0 | if (!mImages) { |
6787 | 0 | mImages = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::img, nsGkAtoms::img); |
6788 | 0 | } |
6789 | 0 | return mImages; |
6790 | 0 | } |
6791 | | |
6792 | | nsIHTMLCollection* |
6793 | | nsIDocument::Embeds() |
6794 | 0 | { |
6795 | 0 | if (!mEmbeds) { |
6796 | 0 | mEmbeds = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::embed, nsGkAtoms::embed); |
6797 | 0 | } |
6798 | 0 | return mEmbeds; |
6799 | 0 | } |
6800 | | |
6801 | | static bool |
6802 | | MatchLinks(Element* aElement, int32_t aNamespaceID, |
6803 | | nsAtom* aAtom, void* aData) |
6804 | 0 | { |
6805 | 0 | return aElement->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area) && |
6806 | 0 | aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href); |
6807 | 0 | } |
6808 | | |
6809 | | nsIHTMLCollection* |
6810 | | nsIDocument::Links() |
6811 | 0 | { |
6812 | 0 | if (!mLinks) { |
6813 | 0 | mLinks = new nsContentList(this, MatchLinks, nullptr, nullptr); |
6814 | 0 | } |
6815 | 0 | return mLinks; |
6816 | 0 | } |
6817 | | |
6818 | | nsIHTMLCollection* |
6819 | | nsIDocument::Forms() |
6820 | 0 | { |
6821 | 0 | if (!mForms) { |
6822 | 0 | // Please keep this in sync with nsHTMLDocument::GetFormsAndFormControls. |
6823 | 0 | mForms = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::form, nsGkAtoms::form); |
6824 | 0 | } |
6825 | 0 |
|
6826 | 0 | return mForms; |
6827 | 0 | } |
6828 | | |
6829 | | nsIHTMLCollection* |
6830 | | nsIDocument::Scripts() |
6831 | 0 | { |
6832 | 0 | if (!mScripts) { |
6833 | 0 | mScripts = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::script, nsGkAtoms::script); |
6834 | 0 | } |
6835 | 0 | return mScripts; |
6836 | 0 | } |
6837 | | |
6838 | | nsIHTMLCollection* |
6839 | | nsIDocument::Applets() |
6840 | 0 | { |
6841 | 0 | if (!mApplets) { |
6842 | 0 | mApplets = new nsEmptyContentList(this); |
6843 | 0 | } |
6844 | 0 | return mApplets; |
6845 | 0 | } |
6846 | | |
6847 | | static bool |
6848 | | MatchAnchors(Element* aElement, int32_t aNamespaceID, |
6849 | | nsAtom* aAtom, void* aData) |
6850 | 0 | { |
6851 | 0 | return aElement->IsHTMLElement(nsGkAtoms::a) && |
6852 | 0 | aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::name); |
6853 | 0 | } |
6854 | | |
6855 | | nsIHTMLCollection* |
6856 | | nsIDocument::Anchors() |
6857 | 0 | { |
6858 | 0 | if (!mAnchors) { |
6859 | 0 | mAnchors = new nsContentList(this, MatchAnchors, nullptr, nullptr); |
6860 | 0 | } |
6861 | 0 | return mAnchors; |
6862 | 0 | } |
6863 | | |
6864 | | /* static */ |
6865 | | bool |
6866 | | nsIDocument::MatchNameAttribute(Element* aElement, int32_t aNamespaceID, |
6867 | | nsAtom* aAtom, void* aData) |
6868 | 0 | { |
6869 | 0 | MOZ_ASSERT(aElement, "Must have element to work with!"); |
6870 | 0 |
|
6871 | 0 | if (!aElement->HasName()) { |
6872 | 0 | return false; |
6873 | 0 | } |
6874 | 0 | |
6875 | 0 | nsString* elementName = static_cast<nsString*>(aData); |
6876 | 0 | return |
6877 | 0 | aElement->GetNameSpaceID() == kNameSpaceID_XHTML && |
6878 | 0 | aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, |
6879 | 0 | *elementName, eCaseMatters); |
6880 | 0 | } |
6881 | | |
6882 | | /* static */ |
6883 | | void* |
6884 | | nsIDocument::UseExistingNameString(nsINode* aRootNode, const nsString* aName) |
6885 | 0 | { |
6886 | 0 | return const_cast<nsString*>(aName); |
6887 | 0 | } |
6888 | | |
6889 | | nsresult |
6890 | | nsIDocument::GetDocumentURI(nsString& aDocumentURI) const |
6891 | 0 | { |
6892 | 0 | if (mDocumentURI) { |
6893 | 0 | nsAutoCString uri; |
6894 | 0 | nsresult rv = mDocumentURI->GetSpec(uri); |
6895 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
6896 | 0 |
|
6897 | 0 | CopyUTF8toUTF16(uri, aDocumentURI); |
6898 | 0 | } else { |
6899 | 0 | aDocumentURI.Truncate(); |
6900 | 0 | } |
6901 | 0 |
|
6902 | 0 | return NS_OK; |
6903 | 0 | } |
6904 | | |
6905 | | // Alias of above |
6906 | | nsresult |
6907 | | nsIDocument::GetURL(nsString& aURL) const |
6908 | 0 | { |
6909 | 0 | return GetDocumentURI(aURL); |
6910 | 0 | } |
6911 | | |
6912 | | void |
6913 | | nsIDocument::GetDocumentURIFromJS(nsString& aDocumentURI, CallerType aCallerType, |
6914 | | ErrorResult& aRv) const |
6915 | 0 | { |
6916 | 0 | if (!mChromeXHRDocURI || aCallerType != CallerType::System) { |
6917 | 0 | aRv = GetDocumentURI(aDocumentURI); |
6918 | 0 | return; |
6919 | 0 | } |
6920 | 0 | |
6921 | 0 | nsAutoCString uri; |
6922 | 0 | nsresult res = mChromeXHRDocURI->GetSpec(uri); |
6923 | 0 | if (NS_FAILED(res)) { |
6924 | 0 | aRv.Throw(res); |
6925 | 0 | return; |
6926 | 0 | } |
6927 | 0 | CopyUTF8toUTF16(uri, aDocumentURI); |
6928 | 0 | } |
6929 | | |
6930 | | nsIURI* |
6931 | | nsIDocument::GetDocumentURIObject() const |
6932 | 0 | { |
6933 | 0 | if (!mChromeXHRDocURI) { |
6934 | 0 | return GetDocumentURI(); |
6935 | 0 | } |
6936 | 0 | |
6937 | 0 | return mChromeXHRDocURI; |
6938 | 0 | } |
6939 | | |
6940 | | void |
6941 | | nsIDocument::GetCompatMode(nsString& aCompatMode) const |
6942 | 0 | { |
6943 | 0 | NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks || |
6944 | 0 | mCompatMode == eCompatibility_AlmostStandards || |
6945 | 0 | mCompatMode == eCompatibility_FullStandards, |
6946 | 0 | "mCompatMode is neither quirks nor strict for this document"); |
6947 | 0 |
|
6948 | 0 | if (mCompatMode == eCompatibility_NavQuirks) { |
6949 | 0 | aCompatMode.AssignLiteral("BackCompat"); |
6950 | 0 | } else { |
6951 | 0 | aCompatMode.AssignLiteral("CSS1Compat"); |
6952 | 0 | } |
6953 | 0 | } |
6954 | | |
6955 | | void |
6956 | | nsDOMAttributeMap::BlastSubtreeToPieces(nsINode* aNode) |
6957 | 0 | { |
6958 | 0 | if (Element* element = Element::FromNode(aNode)) { |
6959 | 0 | if (const nsDOMAttributeMap* map = element->GetAttributeMap()) { |
6960 | 0 | while (true) { |
6961 | 0 | RefPtr<Attr> attr; |
6962 | 0 | { |
6963 | 0 | // Use an iterator to get an arbitrary attribute from the |
6964 | 0 | // cache. The iterator must be destroyed before any other |
6965 | 0 | // operations on mAttributeCache, to avoid hash table |
6966 | 0 | // assertions. |
6967 | 0 | auto iter = map->mAttributeCache.ConstIter(); |
6968 | 0 | if (iter.Done()) { |
6969 | 0 | break; |
6970 | 0 | } |
6971 | 0 | attr = iter.UserData(); |
6972 | 0 | } |
6973 | 0 |
|
6974 | 0 | BlastSubtreeToPieces(attr); |
6975 | 0 |
|
6976 | 0 | DebugOnly<nsresult> rv = |
6977 | 0 | element->UnsetAttr(attr->NodeInfo()->NamespaceID(), |
6978 | 0 | attr->NodeInfo()->NameAtom(), |
6979 | 0 | false); |
6980 | 0 |
|
6981 | 0 | // XXX Should we abort here? |
6982 | 0 | NS_ASSERTION(NS_SUCCEEDED(rv), "Uh-oh, UnsetAttr shouldn't fail!"); |
6983 | 0 | } |
6984 | 0 | } |
6985 | 0 | } |
6986 | 0 |
|
6987 | 0 | while (aNode->HasChildren()) { |
6988 | 0 | nsIContent* node = aNode->GetFirstChild(); |
6989 | 0 | BlastSubtreeToPieces(node); |
6990 | 0 | aNode->RemoveChildNode(node, false); |
6991 | 0 | } |
6992 | 0 | } |
6993 | | |
6994 | | nsINode* |
6995 | | nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv) |
6996 | 0 | { |
6997 | 0 | nsINode* adoptedNode = &aAdoptedNode; |
6998 | 0 |
|
6999 | 0 | // Scope firing mutation events so that we don't carry any state that |
7000 | 0 | // might be stale |
7001 | 0 | { |
7002 | 0 | nsINode* parent = adoptedNode->GetParentNode(); |
7003 | 0 | if (parent) { |
7004 | 0 | nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent); |
7005 | 0 | } |
7006 | 0 | } |
7007 | 0 |
|
7008 | 0 | nsAutoScriptBlocker scriptBlocker; |
7009 | 0 |
|
7010 | 0 | switch (adoptedNode->NodeType()) { |
7011 | 0 | case ATTRIBUTE_NODE: |
7012 | 0 | { |
7013 | 0 | // Remove from ownerElement. |
7014 | 0 | RefPtr<Attr> adoptedAttr = static_cast<Attr*>(adoptedNode); |
7015 | 0 |
|
7016 | 0 | nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv); |
7017 | 0 | if (rv.Failed()) { |
7018 | 0 | return nullptr; |
7019 | 0 | } |
7020 | 0 | |
7021 | 0 | if (ownerElement) { |
7022 | 0 | RefPtr<Attr> newAttr = |
7023 | 0 | ownerElement->RemoveAttributeNode(*adoptedAttr, rv); |
7024 | 0 | if (rv.Failed()) { |
7025 | 0 | return nullptr; |
7026 | 0 | } |
7027 | 0 | |
7028 | 0 | newAttr.swap(adoptedAttr); |
7029 | 0 | } |
7030 | 0 |
|
7031 | 0 | break; |
7032 | 0 | } |
7033 | 0 | case DOCUMENT_FRAGMENT_NODE: |
7034 | 0 | { |
7035 | 0 | if (adoptedNode->IsShadowRoot()) { |
7036 | 0 | rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
7037 | 0 | return nullptr; |
7038 | 0 | } |
7039 | 0 | MOZ_FALLTHROUGH; |
7040 | 0 | } |
7041 | 0 | case ELEMENT_NODE: |
7042 | 0 | case PROCESSING_INSTRUCTION_NODE: |
7043 | 0 | case TEXT_NODE: |
7044 | 0 | case CDATA_SECTION_NODE: |
7045 | 0 | case COMMENT_NODE: |
7046 | 0 | case DOCUMENT_TYPE_NODE: |
7047 | 0 | { |
7048 | 0 | // Don't allow adopting a node's anonymous subtree out from under it. |
7049 | 0 | if (adoptedNode->AsContent()->IsRootOfAnonymousSubtree()) { |
7050 | 0 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
7051 | 0 | return nullptr; |
7052 | 0 | } |
7053 | 0 | |
7054 | 0 | // We don't want to adopt an element into its own contentDocument or into |
7055 | 0 | // a descendant contentDocument, so we check if the frameElement of this |
7056 | 0 | // document or any of its parents is the adopted node or one of its |
7057 | 0 | // descendants. |
7058 | 0 | nsIDocument *doc = this; |
7059 | 0 | do { |
7060 | 0 | if (nsPIDOMWindowOuter *win = doc->GetWindow()) { |
7061 | 0 | nsCOMPtr<nsINode> node = win->GetFrameElementInternal(); |
7062 | 0 | if (node && |
7063 | 0 | nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) { |
7064 | 0 | rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
7065 | 0 | return nullptr; |
7066 | 0 | } |
7067 | 0 | } |
7068 | 0 | } while ((doc = doc->GetParentDocument())); |
7069 | 0 |
|
7070 | 0 | // Remove from parent. |
7071 | 0 | nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode(); |
7072 | 0 | if (parent) { |
7073 | 0 | parent->RemoveChildNode(adoptedNode->AsContent(), true); |
7074 | 0 | } else { |
7075 | 0 | MOZ_ASSERT(!adoptedNode->IsInUncomposedDoc()); |
7076 | 0 |
|
7077 | 0 | // If we're adopting a node that's not in a document, it might still |
7078 | 0 | // have a binding applied. Remove the binding from the element now |
7079 | 0 | // that it's getting adopted into a new document. |
7080 | 0 | // TODO Fully tear down the binding. |
7081 | 0 | if (Element* element = Element::FromNode(adoptedNode)) { |
7082 | 0 | element->SetXBLBinding(nullptr); |
7083 | 0 | } |
7084 | 0 | } |
7085 | 0 |
|
7086 | 0 | break; |
7087 | 0 | } |
7088 | 0 | case DOCUMENT_NODE: |
7089 | 0 | { |
7090 | 0 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
7091 | 0 | return nullptr; |
7092 | 0 | } |
7093 | 0 | default: |
7094 | 0 | { |
7095 | 0 | NS_WARNING("Don't know how to adopt this nodetype for adoptNode."); |
7096 | 0 |
|
7097 | 0 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
7098 | 0 | return nullptr; |
7099 | 0 | } |
7100 | 0 | } |
7101 | 0 |
|
7102 | 0 | nsCOMPtr<nsIDocument> oldDocument = adoptedNode->OwnerDoc(); |
7103 | 0 | bool sameDocument = oldDocument == this; |
7104 | 0 |
|
7105 | 0 | AutoJSContext cx; |
7106 | 0 | JS::Rooted<JSObject*> newScope(cx, nullptr); |
7107 | 0 | if (!sameDocument) { |
7108 | 0 | newScope = GetWrapper(); |
7109 | 0 | if (!newScope && GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) { |
7110 | 0 | // Make sure cx is in a semi-sane compartment before we call WrapNative. |
7111 | 0 | // It's kind of irrelevant, given that we're passing aAllowWrapping = |
7112 | 0 | // false, and documents should always insist on being wrapped in an |
7113 | 0 | // canonical scope. But we try to pass something sane anyway. |
7114 | 0 | JSAutoRealm ar(cx, GetScopeObject()->GetGlobalJSObject()); |
7115 | 0 | JS::Rooted<JS::Value> v(cx); |
7116 | 0 | rv = nsContentUtils::WrapNative(cx, this, this, &v, |
7117 | 0 | /* aAllowWrapping = */ false); |
7118 | 0 | if (rv.Failed()) |
7119 | 0 | return nullptr; |
7120 | 0 | newScope = &v.toObject(); |
7121 | 0 | } |
7122 | 0 | } |
7123 | 0 |
|
7124 | 0 | nsCOMArray<nsINode> nodesWithProperties; |
7125 | 0 | nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager, |
7126 | 0 | newScope, nodesWithProperties, rv); |
7127 | 0 | if (rv.Failed()) { |
7128 | 0 | // Disconnect all nodes from their parents, since some have the old document |
7129 | 0 | // as their ownerDocument and some have this as their ownerDocument. |
7130 | 0 | nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode); |
7131 | 0 |
|
7132 | 0 | if (!sameDocument && oldDocument) { |
7133 | 0 | for (nsINode* node : nodesWithProperties) { |
7134 | 0 | // Remove all properties. |
7135 | 0 | oldDocument->PropertyTable().DeleteAllPropertiesFor(node); |
7136 | 0 | } |
7137 | 0 | } |
7138 | 0 |
|
7139 | 0 | return nullptr; |
7140 | 0 | } |
7141 | 0 |
|
7142 | 0 | if (!sameDocument && oldDocument) { |
7143 | 0 | nsPropertyTable& oldTable = oldDocument->PropertyTable(); |
7144 | 0 | nsPropertyTable& newTable = PropertyTable(); |
7145 | 0 | for (nsINode* node : nodesWithProperties) { |
7146 | 0 | rv = oldTable.TransferOrDeleteAllPropertiesFor(node, newTable); |
7147 | 0 | } |
7148 | 0 |
|
7149 | 0 | if (rv.Failed()) { |
7150 | 0 | // Disconnect all nodes from their parents. |
7151 | 0 | nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode); |
7152 | 0 |
|
7153 | 0 | return nullptr; |
7154 | 0 | } |
7155 | 0 | } |
7156 | 0 | |
7157 | 0 | NS_ASSERTION(adoptedNode->OwnerDoc() == this, |
7158 | 0 | "Should still be in the document we just got adopted into"); |
7159 | 0 |
|
7160 | 0 | return adoptedNode; |
7161 | 0 | } |
7162 | | |
7163 | | nsViewportInfo |
7164 | | nsIDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize) |
7165 | 0 | { |
7166 | 0 | MOZ_ASSERT(mPresShell); |
7167 | 0 |
|
7168 | 0 | // Compute the CSS-to-LayoutDevice pixel scale as the product of the |
7169 | 0 | // widget scale and the full zoom. |
7170 | 0 | nsPresContext* context = mPresShell->GetPresContext(); |
7171 | 0 | // When querying the full zoom, get it from the device context rather than |
7172 | 0 | // directly from the pres context, because the device context's value can |
7173 | 0 | // include an adjustment necessay to keep the number of app units per device |
7174 | 0 | // pixel an integer, and we want the adjusted value. |
7175 | 0 | float fullZoom = context ? context->DeviceContext()->GetFullZoom() : 1.0; |
7176 | 0 | fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom; |
7177 | 0 | CSSToLayoutDeviceScale layoutDeviceScale = context ? context->CSSToDevPixelScale() : CSSToLayoutDeviceScale(1); |
7178 | 0 |
|
7179 | 0 | CSSToScreenScale defaultScale = layoutDeviceScale |
7180 | 0 | * LayoutDeviceToScreenScale(1.0); |
7181 | 0 |
|
7182 | 0 | // Special behaviour for desktop mode, provided we are not on an about: page |
7183 | 0 | nsPIDOMWindowOuter* win = GetWindow(); |
7184 | 0 | if (win && win->IsDesktopModeViewport() && !IsAboutPage()) { |
7185 | 0 | CSSCoord viewportWidth = gfxPrefs::DesktopViewportWidth() / fullZoom; |
7186 | 0 | CSSToScreenScale scaleToFit(aDisplaySize.width / viewportWidth); |
7187 | 0 | float aspectRatio = (float)aDisplaySize.height / aDisplaySize.width; |
7188 | 0 | CSSSize viewportSize(viewportWidth, viewportWidth * aspectRatio); |
7189 | 0 | ScreenIntSize fakeDesktopSize = RoundedToInt(viewportSize * scaleToFit); |
7190 | 0 | return nsViewportInfo(fakeDesktopSize, |
7191 | 0 | scaleToFit, |
7192 | 0 | /*allowZoom*/ true); |
7193 | 0 | } |
7194 | 0 | |
7195 | 0 | if (!gfxPrefs::MetaViewportEnabled()) { |
7196 | 0 | return nsViewportInfo(aDisplaySize, |
7197 | 0 | defaultScale, |
7198 | 0 | /*allowZoom*/ false); |
7199 | 0 | } |
7200 | 0 | |
7201 | 0 | // In cases where the width of the CSS viewport is less than or equal to the width |
7202 | 0 | // of the display (i.e. width <= device-width) then we disable double-tap-to-zoom |
7203 | 0 | // behaviour. See bug 941995 for details. |
7204 | 0 | |
7205 | 0 | switch (mViewportType) { |
7206 | 0 | case DisplayWidthHeight: |
7207 | 0 | return nsViewportInfo(aDisplaySize, |
7208 | 0 | defaultScale, |
7209 | 0 | /*allowZoom*/ true); |
7210 | 0 | case Unknown: |
7211 | 0 | { |
7212 | 0 | nsAutoString viewport; |
7213 | 0 | GetHeaderData(nsGkAtoms::viewport, viewport); |
7214 | 0 | if (viewport.IsEmpty()) { |
7215 | 0 | // If the docType specifies that we are on a site optimized for mobile, |
7216 | 0 | // then we want to return specially crafted defaults for the viewport info. |
7217 | 0 | RefPtr<DocumentType> docType = GetDoctype(); |
7218 | 0 | if (docType) { |
7219 | 0 | nsAutoString docId; |
7220 | 0 | docType->GetPublicId(docId); |
7221 | 0 | if ((docId.Find("WAP") != -1) || |
7222 | 0 | (docId.Find("Mobile") != -1) || |
7223 | 0 | (docId.Find("WML") != -1)) |
7224 | 0 | { |
7225 | 0 | // We're making an assumption that the docType can't change here |
7226 | 0 | mViewportType = DisplayWidthHeight; |
7227 | 0 | return nsViewportInfo(aDisplaySize, |
7228 | 0 | defaultScale, |
7229 | 0 | /*allowZoom*/true); |
7230 | 0 | } |
7231 | 0 | } |
7232 | 0 | |
7233 | 0 | nsAutoString handheldFriendly; |
7234 | 0 | GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly); |
7235 | 0 | if (handheldFriendly.EqualsLiteral("true")) { |
7236 | 0 | mViewportType = DisplayWidthHeight; |
7237 | 0 | return nsViewportInfo(aDisplaySize, defaultScale, |
7238 | 0 | /*allowZoom*/true); |
7239 | 0 | } |
7240 | 0 | } |
7241 | 0 | |
7242 | 0 | nsAutoString minScaleStr; |
7243 | 0 | GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr); |
7244 | 0 |
|
7245 | 0 | nsresult errorCode; |
7246 | 0 | mScaleMinFloat = LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode)); |
7247 | 0 |
|
7248 | 0 | if (NS_FAILED(errorCode)) { |
7249 | 0 | mScaleMinFloat = kViewportMinScale; |
7250 | 0 | } |
7251 | 0 |
|
7252 | 0 | mScaleMinFloat = mozilla::clamped( |
7253 | 0 | mScaleMinFloat, kViewportMinScale, kViewportMaxScale); |
7254 | 0 |
|
7255 | 0 | nsAutoString maxScaleStr; |
7256 | 0 | GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr); |
7257 | 0 |
|
7258 | 0 | // We define a special error code variable for the scale and max scale, |
7259 | 0 | // because they are used later (see the width calculations). |
7260 | 0 | nsresult scaleMaxErrorCode; |
7261 | 0 | mScaleMaxFloat = LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode)); |
7262 | 0 |
|
7263 | 0 | if (NS_FAILED(scaleMaxErrorCode)) { |
7264 | 0 | mScaleMaxFloat = kViewportMaxScale; |
7265 | 0 | } |
7266 | 0 |
|
7267 | 0 | mScaleMaxFloat = mozilla::clamped( |
7268 | 0 | mScaleMaxFloat, kViewportMinScale, kViewportMaxScale); |
7269 | 0 |
|
7270 | 0 | nsAutoString scaleStr; |
7271 | 0 | GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr); |
7272 | 0 |
|
7273 | 0 | nsresult scaleErrorCode; |
7274 | 0 | mScaleFloat = LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode)); |
7275 | 0 |
|
7276 | 0 | nsAutoString widthStr, heightStr; |
7277 | 0 |
|
7278 | 0 | GetHeaderData(nsGkAtoms::viewport_height, heightStr); |
7279 | 0 | GetHeaderData(nsGkAtoms::viewport_width, widthStr); |
7280 | 0 |
|
7281 | 0 | mAutoSize = false; |
7282 | 0 |
|
7283 | 0 | if (widthStr.EqualsLiteral("device-width")) { |
7284 | 0 | mAutoSize = true; |
7285 | 0 | } |
7286 | 0 |
|
7287 | 0 | if (widthStr.IsEmpty() && |
7288 | 0 | (heightStr.EqualsLiteral("device-height") || |
7289 | 0 | (mScaleFloat.scale == 1.0))) |
7290 | 0 | { |
7291 | 0 | mAutoSize = true; |
7292 | 0 | } |
7293 | 0 |
|
7294 | 0 | nsresult widthErrorCode, heightErrorCode; |
7295 | 0 | mViewportSize.width = widthStr.ToInteger(&widthErrorCode); |
7296 | 0 | mViewportSize.height = heightStr.ToInteger(&heightErrorCode); |
7297 | 0 |
|
7298 | 0 | // If width or height has not been set to a valid number by this point, |
7299 | 0 | // fall back to a default value. |
7300 | 0 | mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportSize.width > 0); |
7301 | 0 | mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportSize.height > 0); |
7302 | 0 |
|
7303 | 0 | // If the width is set to some unrecognized value, and there is no |
7304 | 0 | // height set, treat it as if device-width were specified. |
7305 | 0 | if ((!mValidWidth && !widthStr.IsEmpty()) && !mValidHeight) { |
7306 | 0 | mAutoSize = true; |
7307 | 0 | } |
7308 | 0 |
|
7309 | 0 | mAllowZoom = true; |
7310 | 0 | nsAutoString userScalable; |
7311 | 0 | GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable); |
7312 | 0 |
|
7313 | 0 | if ((userScalable.EqualsLiteral("0")) || |
7314 | 0 | (userScalable.EqualsLiteral("no")) || |
7315 | 0 | (userScalable.EqualsLiteral("false"))) { |
7316 | 0 | mAllowZoom = false; |
7317 | 0 | } |
7318 | 0 |
|
7319 | 0 | mScaleStrEmpty = scaleStr.IsEmpty(); |
7320 | 0 | mWidthStrEmpty = widthStr.IsEmpty(); |
7321 | 0 | mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode); |
7322 | 0 | mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode); |
7323 | 0 |
|
7324 | 0 | mViewportType = Specified; |
7325 | 0 | mViewportOverflowType = ViewportOverflowType::NoOverflow; |
7326 | 0 | MOZ_FALLTHROUGH; |
7327 | 0 | } |
7328 | 0 | case Specified: |
7329 | 0 | default: |
7330 | 0 | LayoutDeviceToScreenScale effectiveMinScale = mScaleMinFloat; |
7331 | 0 | LayoutDeviceToScreenScale effectiveMaxScale = mScaleMaxFloat; |
7332 | 0 | bool effectiveValidMaxScale = mValidMaxScale; |
7333 | 0 | bool effectiveAllowZoom = mAllowZoom; |
7334 | 0 | if (gfxPrefs::ForceUserScalable()) { |
7335 | 0 | // If the pref to force user-scalable is enabled, we ignore the values |
7336 | 0 | // from the meta-viewport tag for these properties and just assume they |
7337 | 0 | // allow the page to be scalable. Note in particular that this code is |
7338 | 0 | // in the "Specified" branch of the enclosing switch statement, so that |
7339 | 0 | // calls to GetViewportInfo always use the latest value of the |
7340 | 0 | // ForceUserScalable pref. Other codepaths that return nsViewportInfo |
7341 | 0 | // instances are all consistent with ForceUserScalable() already. |
7342 | 0 | effectiveMinScale = kViewportMinScale; |
7343 | 0 | effectiveMaxScale = kViewportMaxScale; |
7344 | 0 | effectiveValidMaxScale = true; |
7345 | 0 | effectiveAllowZoom = true; |
7346 | 0 | } |
7347 | 0 |
|
7348 | 0 | CSSSize size = mViewportSize; |
7349 | 0 |
|
7350 | 0 | if (!mValidWidth) { |
7351 | 0 | if (mValidHeight && !aDisplaySize.IsEmpty()) { |
7352 | 0 | size.width = size.height * aDisplaySize.width / aDisplaySize.height; |
7353 | 0 | } else { |
7354 | 0 | // Stretch CSS pixel size of viewport to keep device pixel size |
7355 | 0 | // unchanged after full zoom applied. |
7356 | 0 | // See bug 974242. |
7357 | 0 | size.width = gfxPrefs::DesktopViewportWidth() / fullZoom; |
7358 | 0 | } |
7359 | 0 | } |
7360 | 0 |
|
7361 | 0 | if (!mValidHeight) { |
7362 | 0 | if (!aDisplaySize.IsEmpty()) { |
7363 | 0 | size.height = size.width * aDisplaySize.height / aDisplaySize.width; |
7364 | 0 | } else { |
7365 | 0 | size.height = size.width; |
7366 | 0 | } |
7367 | 0 | } |
7368 | 0 |
|
7369 | 0 | CSSToScreenScale scaleFloat = mScaleFloat * layoutDeviceScale; |
7370 | 0 | CSSToScreenScale scaleMinFloat = effectiveMinScale * layoutDeviceScale; |
7371 | 0 | CSSToScreenScale scaleMaxFloat = effectiveMaxScale * layoutDeviceScale; |
7372 | 0 |
|
7373 | 0 | if (mAutoSize) { |
7374 | 0 | // aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size. |
7375 | 0 | CSSToScreenScale defaultPixelScale = layoutDeviceScale * LayoutDeviceToScreenScale(1.0f); |
7376 | 0 | size = ScreenSize(aDisplaySize) / defaultPixelScale; |
7377 | 0 | } |
7378 | 0 |
|
7379 | 0 | size.width = clamped(size.width, float(kViewportMinSize.width), float(kViewportMaxSize.width)); |
7380 | 0 |
|
7381 | 0 | // Also recalculate the default zoom, if it wasn't specified in the metadata, |
7382 | 0 | // and the width is specified. |
7383 | 0 | if (mScaleStrEmpty && !mWidthStrEmpty) { |
7384 | 0 | CSSToScreenScale defaultScale(float(aDisplaySize.width) / size.width); |
7385 | 0 | scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale; |
7386 | 0 | } |
7387 | 0 |
|
7388 | 0 | size.height = clamped(size.height, float(kViewportMinSize.height), float(kViewportMaxSize.height)); |
7389 | 0 |
|
7390 | 0 | // We need to perform a conversion, but only if the initial or maximum |
7391 | 0 | // scale were set explicitly by the user. |
7392 | 0 | if (mValidScaleFloat && scaleFloat >= scaleMinFloat && scaleFloat <= scaleMaxFloat) { |
7393 | 0 | CSSSize displaySize = ScreenSize(aDisplaySize) / scaleFloat; |
7394 | 0 | size.width = std::max(size.width, displaySize.width); |
7395 | 0 | size.height = std::max(size.height, displaySize.height); |
7396 | 0 | } else if (effectiveValidMaxScale) { |
7397 | 0 | CSSSize displaySize = ScreenSize(aDisplaySize) / scaleMaxFloat; |
7398 | 0 | size.width = std::max(size.width, displaySize.width); |
7399 | 0 | size.height = std::max(size.height, displaySize.height); |
7400 | 0 | } |
7401 | 0 |
|
7402 | 0 | return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size, |
7403 | 0 | mAutoSize, effectiveAllowZoom); |
7404 | 0 | } |
7405 | 0 | } |
7406 | | |
7407 | | void |
7408 | | nsIDocument::UpdateViewportOverflowType(nscoord aScrolledWidth, |
7409 | | nscoord aScrollportWidth) |
7410 | 0 | { |
7411 | | #ifdef DEBUG |
7412 | | MOZ_ASSERT(mPresShell); |
7413 | | nsPresContext* pc = GetPresContext(); |
7414 | | MOZ_ASSERT(pc->GetViewportScrollStylesOverride().mHorizontal == |
7415 | | NS_STYLE_OVERFLOW_HIDDEN, |
7416 | | "Should only be called when viewport has overflow-x: hidden"); |
7417 | | MOZ_ASSERT(aScrolledWidth > aScrollportWidth, |
7418 | | "Should only be called when viewport is overflowed"); |
7419 | | MOZ_ASSERT(IsTopLevelContentDocument(), |
7420 | | "Should only be called for top-level content document"); |
7421 | | #endif // DEBUG |
7422 | |
|
7423 | 0 | if (!gfxPrefs::MetaViewportEnabled() || |
7424 | 0 | (GetWindow() && GetWindow()->IsDesktopModeViewport())) { |
7425 | 0 | mViewportOverflowType = ViewportOverflowType::Desktop; |
7426 | 0 | return; |
7427 | 0 | } |
7428 | 0 | |
7429 | 0 | if (mViewportType == Unknown) { |
7430 | 0 | // The viewport info hasn't been initialized yet. Suppose we would |
7431 | 0 | // get here again at some point after it's initialized. |
7432 | 0 | return; |
7433 | 0 | } |
7434 | 0 | |
7435 | 0 | static const LayoutDeviceToScreenScale |
7436 | 0 | kBlinkDefaultMinScale = LayoutDeviceToScreenScale(0.25f); |
7437 | 0 | LayoutDeviceToScreenScale minScale; |
7438 | 0 | if (mViewportType == DisplayWidthHeight) { |
7439 | 0 | minScale = kBlinkDefaultMinScale; |
7440 | 0 | } else { |
7441 | 0 | if (mScaleMinFloat == kViewportMinScale) { |
7442 | 0 | minScale = kBlinkDefaultMinScale; |
7443 | 0 | } else { |
7444 | 0 | minScale = mScaleMinFloat; |
7445 | 0 | } |
7446 | 0 | } |
7447 | 0 |
|
7448 | 0 | // If the content has overflowed with minimum scale applied, don't |
7449 | 0 | // change it, otherwise update the overflow type. |
7450 | 0 | if (mViewportOverflowType != ViewportOverflowType::MinScaleSize) { |
7451 | 0 | if (aScrolledWidth * minScale.scale < aScrollportWidth) { |
7452 | 0 | mViewportOverflowType = ViewportOverflowType::ButNotMinScaleSize; |
7453 | 0 | } else { |
7454 | 0 | mViewportOverflowType = ViewportOverflowType::MinScaleSize; |
7455 | 0 | } |
7456 | 0 | } |
7457 | 0 | } |
7458 | | |
7459 | | EventListenerManager* |
7460 | | nsDocument::GetOrCreateListenerManager() |
7461 | 0 | { |
7462 | 0 | if (!mListenerManager) { |
7463 | 0 | mListenerManager = |
7464 | 0 | new EventListenerManager(static_cast<EventTarget*>(this)); |
7465 | 0 | SetFlags(NODE_HAS_LISTENERMANAGER); |
7466 | 0 | } |
7467 | 0 |
|
7468 | 0 | return mListenerManager; |
7469 | 0 | } |
7470 | | |
7471 | | EventListenerManager* |
7472 | | nsDocument::GetExistingListenerManager() const |
7473 | 0 | { |
7474 | 0 | return mListenerManager; |
7475 | 0 | } |
7476 | | |
7477 | | void |
7478 | | nsDocument::GetEventTargetParent(EventChainPreVisitor& aVisitor) |
7479 | 0 | { |
7480 | 0 | if (mDocGroup && aVisitor.mEvent->mMessage != eVoidEvent && |
7481 | 0 | !mIgnoreDocGroupMismatches) { |
7482 | 0 | mDocGroup->ValidateAccess(); |
7483 | 0 | } |
7484 | 0 |
|
7485 | 0 | aVisitor.mCanHandle = true; |
7486 | 0 | // FIXME! This is a hack to make middle mouse paste working also in Editor. |
7487 | 0 | // Bug 329119 |
7488 | 0 | aVisitor.mForceContentDispatch = true; |
7489 | 0 |
|
7490 | 0 | // Load events must not propagate to |window| object, see bug 335251. |
7491 | 0 | if (aVisitor.mEvent->mMessage != eLoad) { |
7492 | 0 | nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(GetWindow()); |
7493 | 0 | aVisitor.SetParentTarget( |
7494 | 0 | window ? window->GetTargetForEventTargetChain() : nullptr, false); |
7495 | 0 | } |
7496 | 0 | } |
7497 | | |
7498 | | already_AddRefed<Event> |
7499 | | nsIDocument::CreateEvent(const nsAString& aEventType, CallerType aCallerType, |
7500 | | ErrorResult& rv) const |
7501 | 0 | { |
7502 | 0 | nsPresContext* presContext = GetPresContext(); |
7503 | 0 |
|
7504 | 0 | // Create event even without presContext. |
7505 | 0 | RefPtr<Event> ev = |
7506 | 0 | EventDispatcher::CreateEvent(const_cast<nsIDocument*>(this), presContext, |
7507 | 0 | nullptr, aEventType, aCallerType); |
7508 | 0 | if (!ev) { |
7509 | 0 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
7510 | 0 | return nullptr; |
7511 | 0 | } |
7512 | 0 | WidgetEvent* e = ev->WidgetEventPtr(); |
7513 | 0 | e->mFlags.mBubbles = false; |
7514 | 0 | e->mFlags.mCancelable = false; |
7515 | 0 | return ev.forget(); |
7516 | 0 | } |
7517 | | |
7518 | | void |
7519 | | nsIDocument::FlushPendingNotifications(FlushType aType) |
7520 | 0 | { |
7521 | 0 | mozilla::ChangesToFlush flush(aType, aType >= FlushType::Style); |
7522 | 0 | FlushPendingNotifications(flush); |
7523 | 0 | } |
7524 | | |
7525 | | void |
7526 | | nsIDocument::FlushPendingNotifications(mozilla::ChangesToFlush aFlush) |
7527 | 0 | { |
7528 | 0 | FlushType flushType = aFlush.mFlushType; |
7529 | 0 |
|
7530 | 0 | nsDocumentOnStack dos(this); |
7531 | 0 |
|
7532 | 0 | // We need to flush the sink for non-HTML documents (because the XML |
7533 | 0 | // parser still does insertion with deferred notifications). We |
7534 | 0 | // also need to flush the sink if this is a layout-related flush, to |
7535 | 0 | // make sure that layout is started as needed. But we can skip that |
7536 | 0 | // part if we have no presshell or if it's already done an initial |
7537 | 0 | // reflow. |
7538 | 0 | if ((!IsHTMLDocument() || |
7539 | 0 | (flushType > FlushType::ContentAndNotify && mPresShell && |
7540 | 0 | !mPresShell->DidInitialize())) && |
7541 | 0 | (mParser || mWeakSink)) { |
7542 | 0 | nsCOMPtr<nsIContentSink> sink; |
7543 | 0 | if (mParser) { |
7544 | 0 | sink = mParser->GetContentSink(); |
7545 | 0 | } else { |
7546 | 0 | sink = do_QueryReferent(mWeakSink); |
7547 | 0 | if (!sink) { |
7548 | 0 | mWeakSink = nullptr; |
7549 | 0 | } |
7550 | 0 | } |
7551 | 0 | // Determine if it is safe to flush the sink notifications |
7552 | 0 | // by determining if it safe to flush all the presshells. |
7553 | 0 | if (sink && (flushType == FlushType::Content || IsSafeToFlush())) { |
7554 | 0 | sink->FlushPendingNotifications(flushType); |
7555 | 0 | } |
7556 | 0 | } |
7557 | 0 |
|
7558 | 0 | // Should we be flushing pending binding constructors in here? |
7559 | 0 |
|
7560 | 0 | if (flushType <= FlushType::ContentAndNotify) { |
7561 | 0 | // Nothing to do here |
7562 | 0 | return; |
7563 | 0 | } |
7564 | 0 | |
7565 | 0 | // If we have a parent we must flush the parent too to ensure that our |
7566 | 0 | // container is reflowed if its size was changed. But if it's not safe to |
7567 | 0 | // flush ourselves, then don't flush the parent, since that can cause things |
7568 | 0 | // like resizes of our frame's widget, which we can't handle while flushing |
7569 | 0 | // is unsafe. |
7570 | 0 | // Since media queries mean that a size change of our container can |
7571 | 0 | // affect style, we need to promote a style flush on ourself to a |
7572 | 0 | // layout flush on our parent, since we need our container to be the |
7573 | 0 | // correct size to determine the correct style. |
7574 | 0 | if (mParentDocument && IsSafeToFlush()) { |
7575 | 0 | mozilla::ChangesToFlush parentFlush = aFlush; |
7576 | 0 | if (flushType >= FlushType::Style) { |
7577 | 0 | parentFlush.mFlushType = std::max(FlushType::Layout, flushType); |
7578 | 0 | } |
7579 | 0 | mParentDocument->FlushPendingNotifications(parentFlush); |
7580 | 0 | } |
7581 | 0 |
|
7582 | 0 | if (nsIPresShell* shell = GetShell()) { |
7583 | 0 | shell->FlushPendingNotifications(aFlush); |
7584 | 0 | } |
7585 | 0 | } |
7586 | | |
7587 | | static bool |
7588 | | Copy(nsIDocument* aDocument, void* aData) |
7589 | 0 | { |
7590 | 0 | auto* resources = static_cast<nsTArray<nsCOMPtr<nsIDocument>>*>(aData); |
7591 | 0 | resources->AppendElement(aDocument); |
7592 | 0 | return true; |
7593 | 0 | } |
7594 | | |
7595 | | void |
7596 | | nsIDocument::FlushExternalResources(FlushType aType) |
7597 | 0 | { |
7598 | 0 | NS_ASSERTION(aType >= FlushType::Style, |
7599 | 0 | "should only need to flush for style or higher in external resources"); |
7600 | 0 | if (GetDisplayDocument()) { |
7601 | 0 | return; |
7602 | 0 | } |
7603 | 0 | |
7604 | 0 | nsTArray<nsCOMPtr<nsIDocument>> resources; |
7605 | 0 | EnumerateExternalResources(Copy, &resources); |
7606 | 0 |
|
7607 | 0 | for (uint32_t i = 0; i < resources.Length(); i++) { |
7608 | 0 | resources[i]->FlushPendingNotifications(aType); |
7609 | 0 | } |
7610 | 0 | } |
7611 | | |
7612 | | void |
7613 | | nsIDocument::SetXMLDeclaration(const char16_t* aVersion, |
7614 | | const char16_t* aEncoding, |
7615 | | const int32_t aStandalone) |
7616 | 0 | { |
7617 | 0 | if (!aVersion || *aVersion == '\0') { |
7618 | 0 | mXMLDeclarationBits = 0; |
7619 | 0 | return; |
7620 | 0 | } |
7621 | 0 | |
7622 | 0 | mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS; |
7623 | 0 |
|
7624 | 0 | if (aEncoding && *aEncoding != '\0') { |
7625 | 0 | mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS; |
7626 | 0 | } |
7627 | 0 |
|
7628 | 0 | if (aStandalone == 1) { |
7629 | 0 | mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS | |
7630 | 0 | XML_DECLARATION_BITS_STANDALONE_YES; |
7631 | 0 | } |
7632 | 0 | else if (aStandalone == 0) { |
7633 | 0 | mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS; |
7634 | 0 | } |
7635 | 0 | } |
7636 | | |
7637 | | void |
7638 | | nsIDocument::GetXMLDeclaration(nsAString& aVersion, |
7639 | | nsAString& aEncoding, |
7640 | | nsAString& aStandalone) |
7641 | 0 | { |
7642 | 0 | aVersion.Truncate(); |
7643 | 0 | aEncoding.Truncate(); |
7644 | 0 | aStandalone.Truncate(); |
7645 | 0 |
|
7646 | 0 | if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) { |
7647 | 0 | return; |
7648 | 0 | } |
7649 | 0 | |
7650 | 0 | // always until we start supporting 1.1 etc. |
7651 | 0 | aVersion.AssignLiteral("1.0"); |
7652 | 0 |
|
7653 | 0 | if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) { |
7654 | 0 | // This is what we have stored, not necessarily what was written |
7655 | 0 | // in the original |
7656 | 0 | GetCharacterSet(aEncoding); |
7657 | 0 | } |
7658 | 0 |
|
7659 | 0 | if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) { |
7660 | 0 | if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) { |
7661 | 0 | aStandalone.AssignLiteral("yes"); |
7662 | 0 | } else { |
7663 | 0 | aStandalone.AssignLiteral("no"); |
7664 | 0 | } |
7665 | 0 | } |
7666 | 0 | } |
7667 | | |
7668 | | bool |
7669 | | nsIDocument::IsScriptEnabled() |
7670 | 0 | { |
7671 | 0 | // If this document is sandboxed without 'allow-scripts' |
7672 | 0 | // script is not enabled |
7673 | 0 | if (HasScriptsBlockedBySandbox()) { |
7674 | 0 | return false; |
7675 | 0 | } |
7676 | 0 | |
7677 | 0 | nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow()); |
7678 | 0 | if (!globalObject || !globalObject->GetGlobalJSObject()) { |
7679 | 0 | return false; |
7680 | 0 | } |
7681 | 0 | |
7682 | 0 | return xpc::Scriptability::Get(globalObject->GetGlobalJSObject()).Allowed(); |
7683 | 0 | } |
7684 | | |
7685 | | void |
7686 | | nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel) |
7687 | 0 | { |
7688 | 0 | PRTime modDate = 0; |
7689 | 0 | nsresult rv; |
7690 | 0 |
|
7691 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel; |
7692 | 0 | rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel)); |
7693 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
7694 | 0 | return; |
7695 | 0 | } |
7696 | 0 | |
7697 | 0 | if (httpChannel) { |
7698 | 0 | nsAutoCString tmp; |
7699 | 0 | rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"), |
7700 | 0 | tmp); |
7701 | 0 |
|
7702 | 0 | if (NS_SUCCEEDED(rv)) { |
7703 | 0 | PRTime time; |
7704 | 0 | PRStatus st = PR_ParseTimeString(tmp.get(), true, &time); |
7705 | 0 | if (st == PR_SUCCESS) { |
7706 | 0 | modDate = time; |
7707 | 0 | } |
7708 | 0 | } |
7709 | 0 |
|
7710 | 0 | // The misspelled key 'referer' is as per the HTTP spec |
7711 | 0 | rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"), |
7712 | 0 | mReferrer); |
7713 | 0 |
|
7714 | 0 | static const char *const headers[] = { |
7715 | 0 | "default-style", |
7716 | 0 | "content-style-type", |
7717 | 0 | "content-language", |
7718 | 0 | "content-disposition", |
7719 | 0 | "refresh", |
7720 | 0 | "x-dns-prefetch-control", |
7721 | 0 | "x-frame-options", |
7722 | 0 | "referrer-policy", |
7723 | 0 | // add more http headers if you need |
7724 | 0 | // XXXbz don't add content-location support without reading bug |
7725 | 0 | // 238654 and its dependencies/dups first. |
7726 | 0 | 0 |
7727 | 0 | }; |
7728 | 0 |
|
7729 | 0 | nsAutoCString headerVal; |
7730 | 0 | const char *const *name = headers; |
7731 | 0 | while (*name) { |
7732 | 0 | rv = |
7733 | 0 | httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal); |
7734 | 0 | if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) { |
7735 | 0 | RefPtr<nsAtom> key = NS_Atomize(*name); |
7736 | 0 | SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal)); |
7737 | 0 | } |
7738 | 0 | ++name; |
7739 | 0 | } |
7740 | 0 | } else { |
7741 | 0 | nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel); |
7742 | 0 | if (fileChannel) { |
7743 | 0 | nsCOMPtr<nsIFile> file; |
7744 | 0 | fileChannel->GetFile(getter_AddRefs(file)); |
7745 | 0 | if (file) { |
7746 | 0 | PRTime msecs; |
7747 | 0 | rv = file->GetLastModifiedTime(&msecs); |
7748 | 0 |
|
7749 | 0 | if (NS_SUCCEEDED(rv)) { |
7750 | 0 | modDate = msecs * int64_t(PR_USEC_PER_MSEC); |
7751 | 0 | } |
7752 | 0 | } |
7753 | 0 | } else { |
7754 | 0 | nsAutoCString contentDisp; |
7755 | 0 | rv = aChannel->GetContentDispositionHeader(contentDisp); |
7756 | 0 | if (NS_SUCCEEDED(rv)) { |
7757 | 0 | SetHeaderData(nsGkAtoms::headerContentDisposition, |
7758 | 0 | NS_ConvertASCIItoUTF16(contentDisp)); |
7759 | 0 | } |
7760 | 0 | } |
7761 | 0 | } |
7762 | 0 |
|
7763 | 0 | mLastModified.Truncate(); |
7764 | 0 | if (modDate != 0) { |
7765 | 0 | GetFormattedTimeString(modDate, mLastModified); |
7766 | 0 | } |
7767 | 0 | } |
7768 | | |
7769 | | already_AddRefed<Element> |
7770 | | nsIDocument::CreateElem(const nsAString& aName, nsAtom *aPrefix, |
7771 | | int32_t aNamespaceID, const nsAString* aIs) |
7772 | 0 | { |
7773 | | #ifdef DEBUG |
7774 | | nsAutoString qName; |
7775 | | if (aPrefix) { |
7776 | | aPrefix->ToString(qName); |
7777 | | qName.Append(':'); |
7778 | | } |
7779 | | qName.Append(aName); |
7780 | | |
7781 | | // Note: "a:b:c" is a valid name in non-namespaces XML, and |
7782 | | // nsIDocument::CreateElement can call us with such a name and no prefix, |
7783 | | // which would cause an error if we just used true here. |
7784 | | bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID(); |
7785 | | NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)), |
7786 | | "Don't pass invalid prefixes to nsDocument::CreateElem, " |
7787 | | "check caller."); |
7788 | | #endif |
7789 | |
|
7790 | 0 | RefPtr<mozilla::dom::NodeInfo> nodeInfo; |
7791 | 0 | mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID, |
7792 | 0 | ELEMENT_NODE, getter_AddRefs(nodeInfo)); |
7793 | 0 | NS_ENSURE_TRUE(nodeInfo, nullptr); |
7794 | 0 |
|
7795 | 0 | nsCOMPtr<Element> element; |
7796 | 0 | nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), |
7797 | 0 | NOT_FROM_PARSER, aIs); |
7798 | 0 | return NS_SUCCEEDED(rv) ? element.forget() : nullptr; |
7799 | 0 | } |
7800 | | |
7801 | | bool |
7802 | | nsIDocument::IsSafeToFlush() const |
7803 | 0 | { |
7804 | 0 | nsIPresShell* shell = GetShell(); |
7805 | 0 | if (!shell) |
7806 | 0 | return true; |
7807 | 0 | |
7808 | 0 | return shell->IsSafeToFlush(); |
7809 | 0 | } |
7810 | | |
7811 | | void |
7812 | | nsIDocument::Sanitize() |
7813 | 0 | { |
7814 | 0 | // Sanitize the document by resetting all password fields and any form |
7815 | 0 | // fields with autocomplete=off to their default values. We do this now, |
7816 | 0 | // instead of when the presentation is restored, to offer some protection |
7817 | 0 | // in case there is ever an exploit that allows a cached document to be |
7818 | 0 | // accessed from a different document. |
7819 | 0 |
|
7820 | 0 | // First locate all input elements, regardless of whether they are |
7821 | 0 | // in a form, and reset the password and autocomplete=off elements. |
7822 | 0 |
|
7823 | 0 | RefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input")); |
7824 | 0 |
|
7825 | 0 | nsAutoString value; |
7826 | 0 |
|
7827 | 0 | uint32_t length = nodes->Length(true); |
7828 | 0 | for (uint32_t i = 0; i < length; ++i) { |
7829 | 0 | NS_ASSERTION(nodes->Item(i), "null item in node list!"); |
7830 | 0 |
|
7831 | 0 | RefPtr<HTMLInputElement> input = HTMLInputElement::FromNodeOrNull(nodes->Item(i)); |
7832 | 0 | if (!input) |
7833 | 0 | continue; |
7834 | 0 | |
7835 | 0 | bool resetValue = false; |
7836 | 0 |
|
7837 | 0 | input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value); |
7838 | 0 | if (value.LowerCaseEqualsLiteral("off")) { |
7839 | 0 | resetValue = true; |
7840 | 0 | } else { |
7841 | 0 | input->GetType(value); |
7842 | 0 | if (value.LowerCaseEqualsLiteral("password")) |
7843 | 0 | resetValue = true; |
7844 | 0 | } |
7845 | 0 |
|
7846 | 0 | if (resetValue) { |
7847 | 0 | input->Reset(); |
7848 | 0 | } |
7849 | 0 | } |
7850 | 0 |
|
7851 | 0 | // Now locate all _form_ elements that have autocomplete=off and reset them |
7852 | 0 | nodes = GetElementsByTagName(NS_LITERAL_STRING("form")); |
7853 | 0 |
|
7854 | 0 | length = nodes->Length(true); |
7855 | 0 | for (uint32_t i = 0; i < length; ++i) { |
7856 | 0 | NS_ASSERTION(nodes->Item(i), "null item in nodelist"); |
7857 | 0 |
|
7858 | 0 | HTMLFormElement* form = HTMLFormElement::FromNode(nodes->Item(i)); |
7859 | 0 | if (!form) |
7860 | 0 | continue; |
7861 | 0 | |
7862 | 0 | form->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete, value); |
7863 | 0 | if (value.LowerCaseEqualsLiteral("off")) |
7864 | 0 | form->Reset(); |
7865 | 0 | } |
7866 | 0 | } |
7867 | | |
7868 | | void |
7869 | | nsIDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData) |
7870 | 0 | { |
7871 | 0 | if (!mSubDocuments) { |
7872 | 0 | return; |
7873 | 0 | } |
7874 | 0 | |
7875 | 0 | // PLDHashTable::Iterator can't handle modifications while iterating so we |
7876 | 0 | // copy all entries to an array first before calling any callbacks. |
7877 | 0 | AutoTArray<nsCOMPtr<nsIDocument>, 8> subdocs; |
7878 | 0 | for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) { |
7879 | 0 | auto entry = static_cast<SubDocMapEntry*>(iter.Get()); |
7880 | 0 | nsIDocument* subdoc = entry->mSubDocument; |
7881 | 0 | if (subdoc) { |
7882 | 0 | subdocs.AppendElement(subdoc); |
7883 | 0 | } |
7884 | 0 | } |
7885 | 0 | for (auto subdoc : subdocs) { |
7886 | 0 | if (!aCallback(subdoc, aData)) { |
7887 | 0 | break; |
7888 | 0 | } |
7889 | 0 | } |
7890 | 0 | } |
7891 | | |
7892 | | void |
7893 | | nsIDocument::CollectDescendantDocuments( |
7894 | | nsTArray<nsCOMPtr<nsIDocument>>& aDescendants, |
7895 | | nsDocTestFunc aCallback) const |
7896 | 0 | { |
7897 | 0 | if (!mSubDocuments) { |
7898 | 0 | return; |
7899 | 0 | } |
7900 | 0 | |
7901 | 0 | for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) { |
7902 | 0 | auto entry = static_cast<SubDocMapEntry*>(iter.Get()); |
7903 | 0 | const nsIDocument* subdoc = entry->mSubDocument; |
7904 | 0 | if (subdoc) { |
7905 | 0 | if (aCallback(subdoc)) { |
7906 | 0 | aDescendants.AppendElement(entry->mSubDocument); |
7907 | 0 | } |
7908 | 0 | subdoc->CollectDescendantDocuments(aDescendants, aCallback); |
7909 | 0 | } |
7910 | 0 | } |
7911 | 0 | } |
7912 | | |
7913 | | #ifdef DEBUG_bryner |
7914 | | #define DEBUG_PAGE_CACHE |
7915 | | #endif |
7916 | | |
7917 | | bool |
7918 | | nsIDocument::CanSavePresentation(nsIRequest *aNewRequest) |
7919 | 0 | { |
7920 | 0 | if (!IsBFCachingAllowed()) { |
7921 | 0 | return false; |
7922 | 0 | } |
7923 | 0 | |
7924 | 0 | if (EventHandlingSuppressed()) { |
7925 | 0 | return false; |
7926 | 0 | } |
7927 | 0 | |
7928 | 0 | // Do not allow suspended windows to be placed in the |
7929 | 0 | // bfcache. This method is also used to verify a document |
7930 | 0 | // coming out of the bfcache is ok to restore, though. So |
7931 | 0 | // we only want to block suspend windows that aren't also |
7932 | 0 | // frozen. |
7933 | 0 | nsPIDOMWindowInner* win = GetInnerWindow(); |
7934 | 0 | if (win && win->IsSuspended() && !win->IsFrozen()) { |
7935 | 0 | return false; |
7936 | 0 | } |
7937 | 0 | |
7938 | 0 | // Check our event listener manager for unload/beforeunload listeners. |
7939 | 0 | nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject); |
7940 | 0 | if (piTarget) { |
7941 | 0 | EventListenerManager* manager = piTarget->GetExistingListenerManager(); |
7942 | 0 | if (manager && manager->HasUnloadListeners()) { |
7943 | 0 | return false; |
7944 | 0 | } |
7945 | 0 | } |
7946 | 0 | |
7947 | 0 | // Check if we have pending network requests |
7948 | 0 | nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
7949 | 0 | if (loadGroup) { |
7950 | 0 | nsCOMPtr<nsISimpleEnumerator> requests; |
7951 | 0 | loadGroup->GetRequests(getter_AddRefs(requests)); |
7952 | 0 |
|
7953 | 0 | bool hasMore = false; |
7954 | 0 |
|
7955 | 0 | // We want to bail out if we have any requests other than aNewRequest (or |
7956 | 0 | // in the case when aNewRequest is a part of a multipart response the base |
7957 | 0 | // channel the multipart response is coming in on). |
7958 | 0 | nsCOMPtr<nsIChannel> baseChannel; |
7959 | 0 | nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest)); |
7960 | 0 | if (part) { |
7961 | 0 | part->GetBaseChannel(getter_AddRefs(baseChannel)); |
7962 | 0 | } |
7963 | 0 |
|
7964 | 0 | while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) { |
7965 | 0 | nsCOMPtr<nsISupports> elem; |
7966 | 0 | requests->GetNext(getter_AddRefs(elem)); |
7967 | 0 |
|
7968 | 0 | nsCOMPtr<nsIRequest> request = do_QueryInterface(elem); |
7969 | 0 | if (request && request != aNewRequest && request != baseChannel) { |
7970 | 0 | // Favicon loads don't need to block caching. |
7971 | 0 | nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); |
7972 | 0 | if (channel) { |
7973 | 0 | nsCOMPtr<nsILoadInfo> li; |
7974 | 0 | channel->GetLoadInfo(getter_AddRefs(li)); |
7975 | 0 | if (li) { |
7976 | 0 | if (li->InternalContentPolicyType() == nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON) { |
7977 | 0 | continue; |
7978 | 0 | } |
7979 | 0 | } |
7980 | 0 | } |
7981 | | #ifdef DEBUG_PAGE_CACHE |
7982 | | nsAutoCString requestName, docSpec; |
7983 | | request->GetName(requestName); |
7984 | | if (mDocumentURI) |
7985 | | mDocumentURI->GetSpec(docSpec); |
7986 | | |
7987 | | printf("document %s has request %s\n", |
7988 | | docSpec.get(), requestName.get()); |
7989 | | #endif |
7990 | 0 | return false; |
7991 | 0 | } |
7992 | 0 | } |
7993 | 0 | } |
7994 | 0 |
|
7995 | 0 | // Check if we have active GetUserMedia use |
7996 | 0 | if (MediaManager::Exists() && win && |
7997 | 0 | MediaManager::Get()->IsWindowStillActive(win->WindowID())) { |
7998 | 0 | return false; |
7999 | 0 | } |
8000 | 0 | |
8001 | 0 | #ifdef MOZ_WEBRTC |
8002 | 0 | // Check if we have active PeerConnections |
8003 | 0 | if (win && win->HasActivePeerConnections()) { |
8004 | 0 | return false; |
8005 | 0 | } |
8006 | 0 | #endif // MOZ_WEBRTC |
8007 | 0 | |
8008 | 0 | // Don't save presentations for documents containing EME content, so that |
8009 | 0 | // CDMs reliably shutdown upon user navigation. |
8010 | 0 | if (ContainsEMEContent()) { |
8011 | 0 | return false; |
8012 | 0 | } |
8013 | 0 | |
8014 | 0 | // Don't save presentations for documents containing MSE content, to |
8015 | 0 | // reduce memory usage. |
8016 | 0 | if (ContainsMSEContent()) { |
8017 | 0 | return false; |
8018 | 0 | } |
8019 | 0 | |
8020 | 0 | if (mSubDocuments) { |
8021 | 0 | for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) { |
8022 | 0 | auto entry = static_cast<SubDocMapEntry*>(iter.Get()); |
8023 | 0 | nsIDocument* subdoc = entry->mSubDocument; |
8024 | 0 |
|
8025 | 0 | // The aIgnoreRequest we were passed is only for us, so don't pass it on. |
8026 | 0 | bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false; |
8027 | 0 | if (!canCache) { |
8028 | 0 | return false; |
8029 | 0 | } |
8030 | 0 | } |
8031 | 0 | } |
8032 | 0 |
|
8033 | 0 |
|
8034 | 0 | if (win) { |
8035 | 0 | auto* globalWindow = nsGlobalWindowInner::Cast(win); |
8036 | 0 | #ifdef MOZ_WEBSPEECH |
8037 | 0 | if (globalWindow->HasActiveSpeechSynthesis()) { |
8038 | 0 | return false; |
8039 | 0 | } |
8040 | 0 | #endif |
8041 | 0 | if (globalWindow->HasUsedVR()) { |
8042 | 0 | return false; |
8043 | 0 | } |
8044 | 0 | } |
8045 | 0 | |
8046 | 0 | return true; |
8047 | 0 | } |
8048 | | |
8049 | | void |
8050 | | nsDocument::Destroy() |
8051 | 0 | { |
8052 | 0 | // The ContentViewer wants to release the document now. So, tell our content |
8053 | 0 | // to drop any references to the document so that it can be destroyed. |
8054 | 0 | if (mIsGoingAway) |
8055 | 0 | return; |
8056 | 0 | |
8057 | 0 | mIsGoingAway = true; |
8058 | 0 |
|
8059 | 0 | ScriptLoader()->Destroy(); |
8060 | 0 | SetScriptGlobalObject(nullptr); |
8061 | 0 | RemovedFromDocShell(); |
8062 | 0 |
|
8063 | 0 | bool oldVal = mInUnlinkOrDeletion; |
8064 | 0 | mInUnlinkOrDeletion = true; |
8065 | 0 |
|
8066 | | #ifdef DEBUG |
8067 | | uint32_t oldChildCount = GetChildCount(); |
8068 | | #endif |
8069 | |
|
8070 | 0 | for (nsIContent* child = GetFirstChild(); child; |
8071 | 0 | child = child->GetNextSibling()) { |
8072 | 0 | child->DestroyContent(); |
8073 | 0 | MOZ_ASSERT(child->GetParentNode() == this); |
8074 | 0 | } |
8075 | 0 | MOZ_ASSERT(oldChildCount == GetChildCount()); |
8076 | 0 |
|
8077 | 0 | mInUnlinkOrDeletion = oldVal; |
8078 | 0 |
|
8079 | 0 | mLayoutHistoryState = nullptr; |
8080 | 0 |
|
8081 | 0 | // Shut down our external resource map. We might not need this for |
8082 | 0 | // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but |
8083 | 0 | // tearing down all those frame trees right now is the right thing to do. |
8084 | 0 | mExternalResourceMap.Shutdown(); |
8085 | 0 | } |
8086 | | |
8087 | | void |
8088 | | nsDocument::RemovedFromDocShell() |
8089 | 0 | { |
8090 | 0 | if (mRemovedFromDocShell) |
8091 | 0 | return; |
8092 | 0 | |
8093 | 0 | mRemovedFromDocShell = true; |
8094 | 0 | EnumerateActivityObservers(NotifyActivityChanged, nullptr); |
8095 | 0 |
|
8096 | 0 | for (nsIContent* child = GetFirstChild(); child; |
8097 | 0 | child = child->GetNextSibling()) { |
8098 | 0 | child->SaveSubtreeState(); |
8099 | 0 | } |
8100 | 0 | } |
8101 | | |
8102 | | already_AddRefed<nsILayoutHistoryState> |
8103 | | nsIDocument::GetLayoutHistoryState() const |
8104 | 0 | { |
8105 | 0 | nsCOMPtr<nsILayoutHistoryState> state; |
8106 | 0 | if (!mScriptGlobalObject) { |
8107 | 0 | state = mLayoutHistoryState; |
8108 | 0 | } else { |
8109 | 0 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
8110 | 0 | if (docShell) { |
8111 | 0 | docShell->GetLayoutHistoryState(getter_AddRefs(state)); |
8112 | 0 | } |
8113 | 0 | } |
8114 | 0 |
|
8115 | 0 | return state.forget(); |
8116 | 0 | } |
8117 | | |
8118 | | void |
8119 | | nsIDocument::EnsureOnloadBlocker() |
8120 | 0 | { |
8121 | 0 | // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup |
8122 | 0 | // -- it's not ours. |
8123 | 0 | if (mOnloadBlockCount != 0 && mScriptGlobalObject) { |
8124 | 0 | nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
8125 | 0 | if (loadGroup) { |
8126 | 0 | // Check first to see if mOnloadBlocker is in the loadgroup. |
8127 | 0 | nsCOMPtr<nsISimpleEnumerator> requests; |
8128 | 0 | loadGroup->GetRequests(getter_AddRefs(requests)); |
8129 | 0 |
|
8130 | 0 | bool hasMore = false; |
8131 | 0 | while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) { |
8132 | 0 | nsCOMPtr<nsISupports> elem; |
8133 | 0 | requests->GetNext(getter_AddRefs(elem)); |
8134 | 0 | nsCOMPtr<nsIRequest> request = do_QueryInterface(elem); |
8135 | 0 | if (request && request == mOnloadBlocker) { |
8136 | 0 | return; |
8137 | 0 | } |
8138 | 0 | } |
8139 | 0 |
|
8140 | 0 | // Not in the loadgroup, so add it. |
8141 | 0 | loadGroup->AddRequest(mOnloadBlocker, nullptr); |
8142 | 0 | } |
8143 | 0 | } |
8144 | 0 | } |
8145 | | |
8146 | | void |
8147 | | nsDocument::AsyncBlockOnload() |
8148 | 0 | { |
8149 | 0 | while (mAsyncOnloadBlockCount) { |
8150 | 0 | --mAsyncOnloadBlockCount; |
8151 | 0 | BlockOnload(); |
8152 | 0 | } |
8153 | 0 | } |
8154 | | |
8155 | | void |
8156 | | nsDocument::BlockOnload() |
8157 | 0 | { |
8158 | 0 | if (mDisplayDocument) { |
8159 | 0 | mDisplayDocument->BlockOnload(); |
8160 | 0 | return; |
8161 | 0 | } |
8162 | 0 | |
8163 | 0 | // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup |
8164 | 0 | // -- it's not ours. |
8165 | 0 | if (mOnloadBlockCount == 0 && mScriptGlobalObject) { |
8166 | 0 | if (!nsContentUtils::IsSafeToRunScript()) { |
8167 | 0 | // Because AddRequest may lead to OnStateChange calls in chrome, |
8168 | 0 | // block onload only when there are no script blockers. |
8169 | 0 | ++mAsyncOnloadBlockCount; |
8170 | 0 | if (mAsyncOnloadBlockCount == 1) { |
8171 | 0 | nsContentUtils::AddScriptRunner(NewRunnableMethod( |
8172 | 0 | "nsDocument::AsyncBlockOnload", this, &nsDocument::AsyncBlockOnload)); |
8173 | 0 | } |
8174 | 0 | return; |
8175 | 0 | } |
8176 | 0 | nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
8177 | 0 | if (loadGroup) { |
8178 | 0 | loadGroup->AddRequest(mOnloadBlocker, nullptr); |
8179 | 0 | } |
8180 | 0 | } |
8181 | 0 | ++mOnloadBlockCount; |
8182 | 0 | } |
8183 | | |
8184 | | void |
8185 | | nsDocument::UnblockOnload(bool aFireSync) |
8186 | 0 | { |
8187 | 0 | if (mDisplayDocument) { |
8188 | 0 | mDisplayDocument->UnblockOnload(aFireSync); |
8189 | 0 | return; |
8190 | 0 | } |
8191 | 0 | |
8192 | 0 | if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) { |
8193 | 0 | MOZ_ASSERT_UNREACHABLE("More UnblockOnload() calls than BlockOnload() " |
8194 | 0 | "calls; dropping call"); |
8195 | 0 | return; |
8196 | 0 | } |
8197 | 0 |
|
8198 | 0 | --mOnloadBlockCount; |
8199 | 0 |
|
8200 | 0 | if (mOnloadBlockCount == 0) { |
8201 | 0 | if (mScriptGlobalObject) { |
8202 | 0 | // Only manipulate the loadgroup in this case, because if mScriptGlobalObject |
8203 | 0 | // is null, it's not ours. |
8204 | 0 | if (aFireSync && mAsyncOnloadBlockCount == 0) { |
8205 | 0 | // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it |
8206 | 0 | ++mOnloadBlockCount; |
8207 | 0 | DoUnblockOnload(); |
8208 | 0 | } else { |
8209 | 0 | PostUnblockOnloadEvent(); |
8210 | 0 | } |
8211 | 0 | } else if (mIsBeingUsedAsImage) { |
8212 | 0 | // To correctly unblock onload for a document that contains an SVG |
8213 | 0 | // image, we need to know when all of the SVG document's resources are |
8214 | 0 | // done loading, in a way comparable to |window.onload|. We fire this |
8215 | 0 | // event to indicate that the SVG should be considered fully loaded. |
8216 | 0 | // Because scripting is disabled on SVG-as-image documents, this event |
8217 | 0 | // is not accessible to content authors. (See bug 837315.) |
8218 | 0 | RefPtr<AsyncEventDispatcher> asyncDispatcher = |
8219 | 0 | new AsyncEventDispatcher(this, |
8220 | 0 | NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), |
8221 | 0 | CanBubble::eNo, |
8222 | 0 | ChromeOnlyDispatch::eNo); |
8223 | 0 | asyncDispatcher->PostDOMEvent(); |
8224 | 0 | } |
8225 | 0 | } |
8226 | 0 | } |
8227 | | |
8228 | | class nsUnblockOnloadEvent : public Runnable { |
8229 | | public: |
8230 | | explicit nsUnblockOnloadEvent(nsIDocument* aDoc) |
8231 | | : mozilla::Runnable("nsUnblockOnloadEvent") |
8232 | | , mDoc(aDoc) |
8233 | 0 | { |
8234 | 0 | } |
8235 | 0 | NS_IMETHOD Run() override { |
8236 | 0 | mDoc->DoUnblockOnload(); |
8237 | 0 | return NS_OK; |
8238 | 0 | } |
8239 | | private: |
8240 | | RefPtr<nsIDocument> mDoc; |
8241 | | }; |
8242 | | |
8243 | | void |
8244 | | nsIDocument::PostUnblockOnloadEvent() |
8245 | 0 | { |
8246 | 0 | MOZ_RELEASE_ASSERT(NS_IsMainThread()); |
8247 | 0 | nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this); |
8248 | 0 | nsresult rv = |
8249 | 0 | Dispatch(TaskCategory::Other, evt.forget()); |
8250 | 0 | if (NS_SUCCEEDED(rv)) { |
8251 | 0 | // Stabilize block count so we don't post more events while this one is up |
8252 | 0 | ++mOnloadBlockCount; |
8253 | 0 | } else { |
8254 | 0 | NS_WARNING("failed to dispatch nsUnblockOnloadEvent"); |
8255 | 0 | } |
8256 | 0 | } |
8257 | | |
8258 | | void |
8259 | | nsIDocument::DoUnblockOnload() |
8260 | 0 | { |
8261 | 0 | MOZ_ASSERT(!mDisplayDocument, |
8262 | 0 | "Shouldn't get here for resource document"); |
8263 | 0 | MOZ_ASSERT(mOnloadBlockCount != 0, |
8264 | 0 | "Shouldn't have a count of zero here, since we stabilized in " |
8265 | 0 | "PostUnblockOnloadEvent"); |
8266 | 0 |
|
8267 | 0 | --mOnloadBlockCount; |
8268 | 0 |
|
8269 | 0 | if (mOnloadBlockCount != 0) { |
8270 | 0 | // We blocked again after the last unblock. Nothing to do here. We'll |
8271 | 0 | // post a new event when we unblock again. |
8272 | 0 | return; |
8273 | 0 | } |
8274 | 0 | |
8275 | 0 | if (mAsyncOnloadBlockCount != 0) { |
8276 | 0 | // We need to wait until the async onload block has been handled. |
8277 | 0 | PostUnblockOnloadEvent(); |
8278 | 0 | } |
8279 | 0 |
|
8280 | 0 | // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup |
8281 | 0 | // -- it's not ours. |
8282 | 0 | if (mScriptGlobalObject) { |
8283 | 0 | nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
8284 | 0 | if (loadGroup) { |
8285 | 0 | loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK); |
8286 | 0 | } |
8287 | 0 | } |
8288 | 0 | } |
8289 | | |
8290 | | nsIContent* |
8291 | | nsIDocument::GetContentInThisDocument(nsIFrame* aFrame) const |
8292 | 0 | { |
8293 | 0 | for (nsIFrame* f = aFrame; f; |
8294 | 0 | f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) { |
8295 | 0 | nsIContent* content = f->GetContent(); |
8296 | 0 | if (!content || content->IsInAnonymousSubtree()) |
8297 | 0 | continue; |
8298 | 0 | |
8299 | 0 | if (content->OwnerDoc() == this) { |
8300 | 0 | return content; |
8301 | 0 | } |
8302 | 0 | // We must be in a subdocument so jump directly to the root frame. |
8303 | 0 | // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to |
8304 | 0 | // the containing document. |
8305 | 0 | f = f->PresContext()->GetPresShell()->GetRootFrame(); |
8306 | 0 | } |
8307 | 0 |
|
8308 | 0 | return nullptr; |
8309 | 0 | } |
8310 | | |
8311 | | void |
8312 | | nsIDocument::DispatchPageTransition(EventTarget* aDispatchTarget, |
8313 | | const nsAString& aType, |
8314 | | bool aPersisted, |
8315 | | bool aOnlySystemGroup) |
8316 | 0 | { |
8317 | 0 | if (!aDispatchTarget) { |
8318 | 0 | return; |
8319 | 0 | } |
8320 | 0 | |
8321 | 0 | PageTransitionEventInit init; |
8322 | 0 | init.mBubbles = true; |
8323 | 0 | init.mCancelable = true; |
8324 | 0 | init.mPersisted = aPersisted; |
8325 | 0 |
|
8326 | 0 | nsDocShell* docShell = mDocumentContainer.get(); |
8327 | 0 | init.mInFrameSwap = docShell && docShell->InFrameSwap(); |
8328 | 0 |
|
8329 | 0 | RefPtr<PageTransitionEvent> event = |
8330 | 0 | PageTransitionEvent::Constructor(this, aType, init); |
8331 | 0 |
|
8332 | 0 | event->SetTrusted(true); |
8333 | 0 | event->SetTarget(this); |
8334 | 0 | if (aOnlySystemGroup) { |
8335 | 0 | event->WidgetEventPtr()->mFlags.mOnlySystemGroupDispatchInContent = true; |
8336 | 0 | } |
8337 | 0 | EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event, |
8338 | 0 | nullptr, nullptr); |
8339 | 0 | } |
8340 | | |
8341 | | static bool |
8342 | | NotifyPageShow(nsIDocument* aDocument, void* aData) |
8343 | 0 | { |
8344 | 0 | const bool* aPersistedPtr = static_cast<const bool*>(aData); |
8345 | 0 | aDocument->OnPageShow(*aPersistedPtr, nullptr); |
8346 | 0 | return true; |
8347 | 0 | } |
8348 | | |
8349 | | void |
8350 | | nsIDocument::OnPageShow(bool aPersisted, EventTarget* aDispatchStartTarget, |
8351 | | bool aOnlySystemGroup) |
8352 | 0 | { |
8353 | 0 | mVisible = true; |
8354 | 0 |
|
8355 | 0 | EnumerateActivityObservers(NotifyActivityChanged, nullptr); |
8356 | 0 | EnumerateExternalResources(NotifyPageShow, &aPersisted); |
8357 | 0 |
|
8358 | 0 | Element* root = GetRootElement(); |
8359 | 0 | if (aPersisted && root) { |
8360 | 0 | // Send out notifications that our <link> elements are attached. |
8361 | 0 | RefPtr<nsContentList> links = NS_GetContentList(root, |
8362 | 0 | kNameSpaceID_XHTML, |
8363 | 0 | NS_LITERAL_STRING("link")); |
8364 | 0 |
|
8365 | 0 | uint32_t linkCount = links->Length(true); |
8366 | 0 | for (uint32_t i = 0; i < linkCount; ++i) { |
8367 | 0 | static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded(); |
8368 | 0 | } |
8369 | 0 | } |
8370 | 0 |
|
8371 | 0 | // See nsIDocument |
8372 | 0 | if (!aDispatchStartTarget) { |
8373 | 0 | // Set mIsShowing before firing events, in case those event handlers |
8374 | 0 | // move us around. |
8375 | 0 | mIsShowing = true; |
8376 | 0 | } |
8377 | 0 |
|
8378 | 0 | if (mAnimationController) { |
8379 | 0 | mAnimationController->OnPageShow(); |
8380 | 0 | } |
8381 | 0 |
|
8382 | 0 | if (aPersisted) { |
8383 | 0 | ImageTracker()->SetAnimatingState(true); |
8384 | 0 | } |
8385 | 0 |
|
8386 | 0 | UpdateVisibilityState(); |
8387 | 0 |
|
8388 | 0 | if (!mIsBeingUsedAsImage) { |
8389 | 0 | // Dispatch observer notification to notify observers page is shown. |
8390 | 0 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
8391 | 0 | if (os) { |
8392 | 0 | nsIPrincipal* principal = NodePrincipal(); |
8393 | 0 | os->NotifyObservers(this, |
8394 | 0 | nsContentUtils::IsSystemPrincipal(principal) ? |
8395 | 0 | "chrome-page-shown" : |
8396 | 0 | "content-page-shown", |
8397 | 0 | nullptr); |
8398 | 0 | } |
8399 | 0 |
|
8400 | 0 | nsCOMPtr<EventTarget> target = aDispatchStartTarget; |
8401 | 0 | if (!target) { |
8402 | 0 | target = do_QueryInterface(GetWindow()); |
8403 | 0 | } |
8404 | 0 | DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted, |
8405 | 0 | aOnlySystemGroup); |
8406 | 0 | } |
8407 | 0 | } |
8408 | | |
8409 | | static bool |
8410 | | NotifyPageHide(nsIDocument* aDocument, void* aData) |
8411 | 0 | { |
8412 | 0 | const bool* aPersistedPtr = static_cast<const bool*>(aData); |
8413 | 0 | aDocument->OnPageHide(*aPersistedPtr, nullptr); |
8414 | 0 | return true; |
8415 | 0 | } |
8416 | | |
8417 | | static void |
8418 | | DispatchFullscreenChange(nsIDocument* aDocument, nsINode* aTarget) |
8419 | 0 | { |
8420 | 0 | if (nsPresContext* presContext = aDocument->GetPresContext()) { |
8421 | 0 | auto pendingEvent = MakeUnique<PendingFullscreenEvent>( |
8422 | 0 | FullscreenEventType::Change, aDocument, aTarget); |
8423 | 0 | presContext->RefreshDriver()-> |
8424 | 0 | ScheduleFullscreenEvent(std::move(pendingEvent)); |
8425 | 0 | } |
8426 | 0 | } |
8427 | | |
8428 | | static void ClearPendingFullscreenRequests(nsIDocument* aDoc); |
8429 | | |
8430 | | static bool |
8431 | | HasHttpScheme(nsIURI* aURI) |
8432 | 0 | { |
8433 | 0 | bool isHttpish = false; |
8434 | 0 | return aURI && |
8435 | 0 | ((NS_SUCCEEDED(aURI->SchemeIs("http", &isHttpish)) && isHttpish) || |
8436 | 0 | (NS_SUCCEEDED(aURI->SchemeIs("https", &isHttpish)) && isHttpish)); |
8437 | 0 | } |
8438 | | |
8439 | | void |
8440 | | nsIDocument::OnPageHide(bool aPersisted, EventTarget* aDispatchStartTarget, |
8441 | | bool aOnlySystemGroup) |
8442 | 0 | { |
8443 | 0 | if (IsTopLevelContentDocument() && GetDocGroup() && |
8444 | 0 | Telemetry::CanRecordExtended()) { |
8445 | 0 | TabGroup* tabGroup = mDocGroup->GetTabGroup(); |
8446 | 0 |
|
8447 | 0 | if (tabGroup) { |
8448 | 0 | uint32_t active = tabGroup->Count(true /* aActiveOnly */); |
8449 | 0 | uint32_t total = tabGroup->Count(); |
8450 | 0 |
|
8451 | 0 | if (HasHttpScheme(GetDocumentURI())) { |
8452 | 0 | Telemetry::Accumulate(Telemetry::ACTIVE_HTTP_DOCGROUPS_PER_TABGROUP, |
8453 | 0 | active); |
8454 | 0 | Telemetry::Accumulate(Telemetry::TOTAL_HTTP_DOCGROUPS_PER_TABGROUP, |
8455 | 0 | total); |
8456 | 0 | } |
8457 | 0 | } |
8458 | 0 | } |
8459 | 0 |
|
8460 | 0 | // Send out notifications that our <link> elements are detached, |
8461 | 0 | // but only if this is not a full unload. |
8462 | 0 | Element* root = GetRootElement(); |
8463 | 0 | if (aPersisted && root) { |
8464 | 0 | RefPtr<nsContentList> links = NS_GetContentList(root, |
8465 | 0 | kNameSpaceID_XHTML, |
8466 | 0 | NS_LITERAL_STRING("link")); |
8467 | 0 |
|
8468 | 0 | uint32_t linkCount = links->Length(true); |
8469 | 0 | for (uint32_t i = 0; i < linkCount; ++i) { |
8470 | 0 | static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved(); |
8471 | 0 | } |
8472 | 0 | } |
8473 | 0 |
|
8474 | 0 | // See nsIDocument |
8475 | 0 | if (!aDispatchStartTarget) { |
8476 | 0 | // Set mIsShowing before firing events, in case those event handlers |
8477 | 0 | // move us around. |
8478 | 0 | mIsShowing = false; |
8479 | 0 | } |
8480 | 0 |
|
8481 | 0 | if (mAnimationController) { |
8482 | 0 | mAnimationController->OnPageHide(); |
8483 | 0 | } |
8484 | 0 |
|
8485 | 0 | // We do not stop the animations (bug 1024343) |
8486 | 0 | // when the page is refreshing while being dragged out |
8487 | 0 | nsDocShell* docShell = mDocumentContainer.get(); |
8488 | 0 | if (aPersisted && !(docShell && docShell->InFrameSwap())) { |
8489 | 0 | ImageTracker()->SetAnimatingState(false); |
8490 | 0 | } |
8491 | 0 |
|
8492 | 0 | ExitPointerLock(); |
8493 | 0 |
|
8494 | 0 | if (!mIsBeingUsedAsImage) { |
8495 | 0 | // Dispatch observer notification to notify observers page is hidden. |
8496 | 0 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
8497 | 0 | if (os) { |
8498 | 0 | nsIPrincipal* principal = NodePrincipal(); |
8499 | 0 | os->NotifyObservers(this, |
8500 | 0 | nsContentUtils::IsSystemPrincipal(principal) ? |
8501 | 0 | "chrome-page-hidden" : |
8502 | 0 | "content-page-hidden", |
8503 | 0 | nullptr); |
8504 | 0 | } |
8505 | 0 |
|
8506 | 0 | // Now send out a PageHide event. |
8507 | 0 | nsCOMPtr<EventTarget> target = aDispatchStartTarget; |
8508 | 0 | if (!target) { |
8509 | 0 | target = do_QueryInterface(GetWindow()); |
8510 | 0 | } |
8511 | 0 | { |
8512 | 0 | PageUnloadingEventTimeStamp timeStamp(this); |
8513 | 0 | DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted, |
8514 | 0 | aOnlySystemGroup); |
8515 | 0 | } |
8516 | 0 | } |
8517 | 0 |
|
8518 | 0 | mVisible = false; |
8519 | 0 |
|
8520 | 0 | UpdateVisibilityState(); |
8521 | 0 |
|
8522 | 0 | EnumerateExternalResources(NotifyPageHide, &aPersisted); |
8523 | 0 | EnumerateActivityObservers(NotifyActivityChanged, nullptr); |
8524 | 0 |
|
8525 | 0 | ClearPendingFullscreenRequests(this); |
8526 | 0 | if (FullscreenStackTop()) { |
8527 | 0 | // If this document was fullscreen, we should exit fullscreen in this |
8528 | 0 | // doctree branch. This ensures that if the user navigates while in |
8529 | 0 | // fullscreen mode we don't leave its still visible ancestor documents |
8530 | 0 | // in fullscreen mode. So exit fullscreen in the document's fullscreen |
8531 | 0 | // root document, as this will exit fullscreen in all the root's |
8532 | 0 | // descendant documents. Note that documents are removed from the |
8533 | 0 | // doctree by the time OnPageHide() is called, so we must store a |
8534 | 0 | // reference to the root (in nsDocument::mFullscreenRoot) since we can't |
8535 | 0 | // just traverse the doctree to get the root. |
8536 | 0 | nsIDocument::ExitFullscreenInDocTree(this); |
8537 | 0 |
|
8538 | 0 | // Since the document is removed from the doctree before OnPageHide() is |
8539 | 0 | // called, ExitFullscreen() can't traverse from the root down to *this* |
8540 | 0 | // document, so we must manually call CleanupFullscreenState() below too. |
8541 | 0 | // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot, |
8542 | 0 | // so we *must* call it after ExitFullscreen(), not before. |
8543 | 0 | // OnPageHide() is called in every hidden (i.e. descendant) document, |
8544 | 0 | // so calling CleanupFullscreenState() here will ensure all hidden |
8545 | 0 | // documents have their fullscreen state reset. |
8546 | 0 | CleanupFullscreenState(); |
8547 | 0 |
|
8548 | 0 | // The fullscreenchange event is to be queued in the refresh driver, |
8549 | 0 | // however a hidden page wouldn't trigger that again, so it makes no |
8550 | 0 | // sense to dispatch such event here. |
8551 | 0 | } |
8552 | 0 | } |
8553 | | |
8554 | | void |
8555 | | nsIDocument::WillDispatchMutationEvent(nsINode* aTarget) |
8556 | 0 | { |
8557 | 0 | NS_ASSERTION(mSubtreeModifiedDepth != 0 || |
8558 | 0 | mSubtreeModifiedTargets.Count() == 0, |
8559 | 0 | "mSubtreeModifiedTargets not cleared after dispatching?"); |
8560 | 0 | ++mSubtreeModifiedDepth; |
8561 | 0 | if (aTarget) { |
8562 | 0 | // MayDispatchMutationEvent is often called just before this method, |
8563 | 0 | // so it has already appended the node to mSubtreeModifiedTargets. |
8564 | 0 | int32_t count = mSubtreeModifiedTargets.Count(); |
8565 | 0 | if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) { |
8566 | 0 | mSubtreeModifiedTargets.AppendObject(aTarget); |
8567 | 0 | } |
8568 | 0 | } |
8569 | 0 | } |
8570 | | |
8571 | | void |
8572 | | nsIDocument::MutationEventDispatched(nsINode* aTarget) |
8573 | 0 | { |
8574 | 0 | --mSubtreeModifiedDepth; |
8575 | 0 | if (mSubtreeModifiedDepth == 0) { |
8576 | 0 | int32_t count = mSubtreeModifiedTargets.Count(); |
8577 | 0 | if (!count) { |
8578 | 0 | return; |
8579 | 0 | } |
8580 | 0 | |
8581 | 0 | nsPIDOMWindowInner* window = GetInnerWindow(); |
8582 | 0 | if (window && |
8583 | 0 | !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) { |
8584 | 0 | mSubtreeModifiedTargets.Clear(); |
8585 | 0 | return; |
8586 | 0 | } |
8587 | 0 | |
8588 | 0 | nsCOMArray<nsINode> realTargets; |
8589 | 0 | for (int32_t i = 0; i < count; ++i) { |
8590 | 0 | nsINode* possibleTarget = mSubtreeModifiedTargets[i]; |
8591 | 0 | nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget); |
8592 | 0 | if (content && content->ChromeOnlyAccess()) { |
8593 | 0 | continue; |
8594 | 0 | } |
8595 | 0 | |
8596 | 0 | nsINode* commonAncestor = nullptr; |
8597 | 0 | int32_t realTargetCount = realTargets.Count(); |
8598 | 0 | for (int32_t j = 0; j < realTargetCount; ++j) { |
8599 | 0 | commonAncestor = |
8600 | 0 | nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]); |
8601 | 0 | if (commonAncestor) { |
8602 | 0 | realTargets.ReplaceObjectAt(commonAncestor, j); |
8603 | 0 | break; |
8604 | 0 | } |
8605 | 0 | } |
8606 | 0 | if (!commonAncestor) { |
8607 | 0 | realTargets.AppendObject(possibleTarget); |
8608 | 0 | } |
8609 | 0 | } |
8610 | 0 |
|
8611 | 0 | mSubtreeModifiedTargets.Clear(); |
8612 | 0 |
|
8613 | 0 | int32_t realTargetCount = realTargets.Count(); |
8614 | 0 | for (int32_t k = 0; k < realTargetCount; ++k) { |
8615 | 0 | InternalMutationEvent mutation(true, eLegacySubtreeModified); |
8616 | 0 | (new AsyncEventDispatcher(realTargets[k], mutation))-> |
8617 | 0 | RunDOMEventWhenSafe(); |
8618 | 0 | } |
8619 | 0 | } |
8620 | 0 | } |
8621 | | |
8622 | | void |
8623 | | nsIDocument::DestroyElementMaps() |
8624 | 0 | { |
8625 | | #ifdef DEBUG |
8626 | | mStyledLinksCleared = true; |
8627 | | #endif |
8628 | | mStyledLinks.Clear(); |
8629 | 0 | mIdentifierMap.Clear(); |
8630 | 0 | mComposedShadowRoots.Clear(); |
8631 | 0 | mResponsiveContent.Clear(); |
8632 | 0 | IncrementExpandoGeneration(*this); |
8633 | 0 | } |
8634 | | |
8635 | | void |
8636 | | nsIDocument::RefreshLinkHrefs() |
8637 | 0 | { |
8638 | 0 | // Get a list of all links we know about. We will reset them, which will |
8639 | 0 | // remove them from the document, so we need a copy of what is in the |
8640 | 0 | // hashtable. |
8641 | 0 | LinkArray linksToNotify(mStyledLinks.Count()); |
8642 | 0 | for (auto iter = mStyledLinks.ConstIter(); !iter.Done(); iter.Next()) { |
8643 | 0 | linksToNotify.AppendElement(iter.Get()->GetKey()); |
8644 | 0 | } |
8645 | 0 |
|
8646 | 0 | // Reset all of our styled links. |
8647 | 0 | nsAutoScriptBlocker scriptBlocker; |
8648 | 0 | for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) { |
8649 | 0 | linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref()); |
8650 | 0 | } |
8651 | 0 | } |
8652 | | |
8653 | | nsresult |
8654 | | nsDocument::CloneDocHelper(nsDocument* clone) const |
8655 | 0 | { |
8656 | 0 | clone->mIsStaticDocument = mCreatingStaticClone; |
8657 | 0 |
|
8658 | 0 | // Init document |
8659 | 0 | nsresult rv = clone->Init(); |
8660 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
8661 | 0 |
|
8662 | 0 | if (mCreatingStaticClone) { |
8663 | 0 | nsCOMPtr<nsILoadGroup> loadGroup; |
8664 | 0 |
|
8665 | 0 | // |mDocumentContainer| is the container of the document that is being |
8666 | 0 | // created and not the original container. See CreateStaticClone function(). |
8667 | 0 | nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer); |
8668 | 0 | if (docLoader) { |
8669 | 0 | docLoader->GetLoadGroup(getter_AddRefs(loadGroup)); |
8670 | 0 | } |
8671 | 0 | nsCOMPtr<nsIChannel> channel = GetChannel(); |
8672 | 0 | nsCOMPtr<nsIURI> uri; |
8673 | 0 | if (channel) { |
8674 | 0 | NS_GetFinalChannelURI(channel, getter_AddRefs(uri)); |
8675 | 0 | } else { |
8676 | 0 | uri = nsIDocument::GetDocumentURI(); |
8677 | 0 | } |
8678 | 0 | clone->mChannel = channel; |
8679 | 0 | if (uri) { |
8680 | 0 | clone->ResetToURI(uri, loadGroup, NodePrincipal()); |
8681 | 0 | } |
8682 | 0 |
|
8683 | 0 | clone->SetContainer(mDocumentContainer); |
8684 | 0 | } |
8685 | 0 |
|
8686 | 0 | // Now ensure that our clone has the same URI, base URI, and principal as us. |
8687 | 0 | // We do this after the mCreatingStaticClone block above, because that block |
8688 | 0 | // can set the base URI to an incorrect value in cases when base URI |
8689 | 0 | // information came from the channel. So we override explicitly, and do it |
8690 | 0 | // for all these properties, in case ResetToURI messes with any of the rest of |
8691 | 0 | // them. |
8692 | 0 | clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI()); |
8693 | 0 | clone->SetChromeXHRDocURI(mChromeXHRDocURI); |
8694 | 0 | clone->SetPrincipal(NodePrincipal()); |
8695 | 0 | clone->mDocumentBaseURI = mDocumentBaseURI; |
8696 | 0 | clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI); |
8697 | 0 |
|
8698 | 0 | bool hasHadScriptObject = true; |
8699 | 0 | nsIScriptGlobalObject* scriptObject = |
8700 | 0 | GetScriptHandlingObject(hasHadScriptObject); |
8701 | 0 | NS_ENSURE_STATE(scriptObject || !hasHadScriptObject); |
8702 | 0 | if (mCreatingStaticClone) { |
8703 | 0 | // If we're doing a static clone (print, print preview), then we're going to |
8704 | 0 | // be setting a scope object after the clone. It's better to set it only |
8705 | 0 | // once, so we don't do that here. However, we do want to act as if there is |
8706 | 0 | // a script handling object. So we set mHasHadScriptHandlingObject. |
8707 | 0 | clone->mHasHadScriptHandlingObject = true; |
8708 | 0 | } else if (scriptObject) { |
8709 | 0 | clone->SetScriptHandlingObject(scriptObject); |
8710 | 0 | } else { |
8711 | 0 | clone->SetScopeObject(GetScopeObject()); |
8712 | 0 | } |
8713 | 0 | // Make the clone a data document |
8714 | 0 | clone->SetLoadedAsData(true); |
8715 | 0 |
|
8716 | 0 | // Misc state |
8717 | 0 |
|
8718 | 0 | // State from nsIDocument |
8719 | 0 | clone->mCharacterSet = mCharacterSet; |
8720 | 0 | clone->mCharacterSetSource = mCharacterSetSource; |
8721 | 0 | clone->mCompatMode = mCompatMode; |
8722 | 0 | clone->mBidiOptions = mBidiOptions; |
8723 | 0 | clone->mContentLanguage = mContentLanguage; |
8724 | 0 | clone->SetContentTypeInternal(GetContentTypeInternal()); |
8725 | 0 | clone->mSecurityInfo = mSecurityInfo; |
8726 | 0 |
|
8727 | 0 | // State from nsDocument |
8728 | 0 | clone->mType = mType; |
8729 | 0 | clone->mXMLDeclarationBits = mXMLDeclarationBits; |
8730 | 0 | clone->mBaseTarget = mBaseTarget; |
8731 | 0 |
|
8732 | 0 | return NS_OK; |
8733 | 0 | } |
8734 | | |
8735 | | void |
8736 | | nsIDocument::SetReadyStateInternal(ReadyState rs) |
8737 | 0 | { |
8738 | 0 | mReadyState = rs; |
8739 | 0 | if (rs == READYSTATE_UNINITIALIZED) { |
8740 | 0 | // Transition back to uninitialized happens only to keep assertions happy |
8741 | 0 | // right before readyState transitions to something else. Make this |
8742 | 0 | // transition undetectable by Web content. |
8743 | 0 | return; |
8744 | 0 | } |
8745 | 0 | if (mTiming) { |
8746 | 0 | switch (rs) { |
8747 | 0 | case READYSTATE_LOADING: |
8748 | 0 | mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI()); |
8749 | 0 | break; |
8750 | 0 | case READYSTATE_INTERACTIVE: |
8751 | 0 | mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI()); |
8752 | 0 | break; |
8753 | 0 | case READYSTATE_COMPLETE: |
8754 | 0 | mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI()); |
8755 | 0 | break; |
8756 | 0 | default: |
8757 | 0 | NS_WARNING("Unexpected ReadyState value"); |
8758 | 0 | break; |
8759 | 0 | } |
8760 | 0 | } |
8761 | 0 | // At the time of loading start, we don't have timing object, record time. |
8762 | 0 | if (READYSTATE_LOADING == rs) { |
8763 | 0 | mLoadingTimeStamp = mozilla::TimeStamp::Now(); |
8764 | 0 | } |
8765 | 0 |
|
8766 | 0 | if (READYSTATE_INTERACTIVE == rs) { |
8767 | 0 | TriggerInitialDocumentTranslation(); |
8768 | 0 | } |
8769 | 0 |
|
8770 | 0 | RecordNavigationTiming(rs); |
8771 | 0 |
|
8772 | 0 | RefPtr<AsyncEventDispatcher> asyncDispatcher = |
8773 | 0 | new AsyncEventDispatcher(this, |
8774 | 0 | NS_LITERAL_STRING("readystatechange"), |
8775 | 0 | CanBubble::eNo, |
8776 | 0 | ChromeOnlyDispatch::eNo); |
8777 | 0 | asyncDispatcher->RunDOMEventWhenSafe(); |
8778 | 0 | } |
8779 | | |
8780 | | void |
8781 | | nsIDocument::GetReadyState(nsAString& aReadyState) const |
8782 | | { |
8783 | | switch(mReadyState) { |
8784 | | case READYSTATE_LOADING : |
8785 | | aReadyState.AssignLiteral(u"loading"); |
8786 | | break; |
8787 | | case READYSTATE_INTERACTIVE : |
8788 | | aReadyState.AssignLiteral(u"interactive"); |
8789 | | break; |
8790 | | case READYSTATE_COMPLETE : |
8791 | | aReadyState.AssignLiteral(u"complete"); |
8792 | | break; |
8793 | | default: |
8794 | | aReadyState.AssignLiteral(u"uninitialized"); |
8795 | | } |
8796 | | } |
8797 | | |
8798 | | static bool |
8799 | | SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData) |
8800 | 0 | { |
8801 | 0 | aDocument->SuppressEventHandling(*static_cast<uint32_t*>(aData)); |
8802 | 0 |
|
8803 | 0 | return true; |
8804 | 0 | } |
8805 | | |
8806 | | void |
8807 | | nsIDocument::SuppressEventHandling(uint32_t aIncrease) |
8808 | 0 | { |
8809 | 0 | mEventsSuppressed += aIncrease; |
8810 | 0 | UpdateFrameRequestCallbackSchedulingState(); |
8811 | 0 | for (uint32_t i = 0; i < aIncrease; ++i) { |
8812 | 0 | ScriptLoader()->AddExecuteBlocker(); |
8813 | 0 | } |
8814 | 0 |
|
8815 | 0 | EnumerateSubDocuments(SuppressEventHandlingInDocument, &aIncrease); |
8816 | 0 | } |
8817 | | |
8818 | | static void |
8819 | | FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument>>& aDocuments, |
8820 | | bool aFireEvents) |
8821 | 0 | { |
8822 | 0 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
8823 | 0 | if (!fm) |
8824 | 0 | return; |
8825 | 0 | |
8826 | 0 | for (uint32_t i = 0; i < aDocuments.Length(); ++i) { |
8827 | 0 | // NB: Don't bother trying to fire delayed events on documents that were |
8828 | 0 | // closed before this event ran. |
8829 | 0 | if (!aDocuments[i]->EventHandlingSuppressed()) { |
8830 | 0 | fm->FireDelayedEvents(aDocuments[i]); |
8831 | 0 | nsCOMPtr<nsIPresShell> shell = aDocuments[i]->GetShell(); |
8832 | 0 | if (shell) { |
8833 | 0 | // Only fire events for active documents. |
8834 | 0 | bool fire = aFireEvents && |
8835 | 0 | aDocuments[i]->GetInnerWindow() && |
8836 | 0 | aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow(); |
8837 | 0 | shell->FireOrClearDelayedEvents(fire); |
8838 | 0 | } |
8839 | 0 | } |
8840 | 0 | } |
8841 | 0 | } |
8842 | | |
8843 | | void |
8844 | | nsIDocument::PreloadPictureClosed() |
8845 | 0 | { |
8846 | 0 | MOZ_ASSERT(mPreloadPictureDepth > 0); |
8847 | 0 | mPreloadPictureDepth--; |
8848 | 0 | if (mPreloadPictureDepth == 0) { |
8849 | 0 | mPreloadPictureFoundSource.SetIsVoid(true); |
8850 | 0 | } |
8851 | 0 | } |
8852 | | |
8853 | | void |
8854 | | nsIDocument::PreloadPictureImageSource(const nsAString& aSrcsetAttr, |
8855 | | const nsAString& aSizesAttr, |
8856 | | const nsAString& aTypeAttr, |
8857 | | const nsAString& aMediaAttr) |
8858 | 0 | { |
8859 | 0 | // Nested pictures are not valid syntax, so while we'll eventually load them, |
8860 | 0 | // it's not worth tracking sources mixed between nesting levels to preload |
8861 | 0 | // them effectively. |
8862 | 0 | if (mPreloadPictureDepth == 1 && mPreloadPictureFoundSource.IsVoid()) { |
8863 | 0 | // <picture> selects the first matching source, so if this returns a URI we |
8864 | 0 | // needn't consider new sources until a new <picture> is encountered. |
8865 | 0 | bool found = |
8866 | 0 | HTMLImageElement::SelectSourceForTagWithAttrs(this, true, VoidString(), |
8867 | 0 | aSrcsetAttr, aSizesAttr, |
8868 | 0 | aTypeAttr, aMediaAttr, |
8869 | 0 | mPreloadPictureFoundSource); |
8870 | 0 | if (found && mPreloadPictureFoundSource.IsVoid()) { |
8871 | 0 | // Found an empty source, which counts |
8872 | 0 | mPreloadPictureFoundSource.SetIsVoid(false); |
8873 | 0 | } |
8874 | 0 | } |
8875 | 0 | } |
8876 | | |
8877 | | already_AddRefed<nsIURI> |
8878 | | nsIDocument::ResolvePreloadImage(nsIURI *aBaseURI, |
8879 | | const nsAString& aSrcAttr, |
8880 | | const nsAString& aSrcsetAttr, |
8881 | | const nsAString& aSizesAttr, |
8882 | | bool *aIsImgSet) |
8883 | 0 | { |
8884 | 0 | nsString sourceURL; |
8885 | 0 | bool isImgSet; |
8886 | 0 | if (mPreloadPictureDepth == 1 && !mPreloadPictureFoundSource.IsVoid()) { |
8887 | 0 | // We're in a <picture> element and found a URI from a source previous to |
8888 | 0 | // this image, use it. |
8889 | 0 | sourceURL = mPreloadPictureFoundSource; |
8890 | 0 | isImgSet = true; |
8891 | 0 | } else { |
8892 | 0 | // Otherwise try to use this <img> as a source |
8893 | 0 | HTMLImageElement::SelectSourceForTagWithAttrs(this, false, aSrcAttr, |
8894 | 0 | aSrcsetAttr, aSizesAttr, |
8895 | 0 | VoidString(), VoidString(), |
8896 | 0 | sourceURL); |
8897 | 0 | isImgSet = !aSrcsetAttr.IsEmpty(); |
8898 | 0 | } |
8899 | 0 |
|
8900 | 0 | // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI) |
8901 | 0 | if (sourceURL.IsEmpty()) { |
8902 | 0 | return nullptr; |
8903 | 0 | } |
8904 | 0 | |
8905 | 0 | // Construct into URI using passed baseURI (the parser may know of base URI |
8906 | 0 | // changes that have not reached us) |
8907 | 0 | nsresult rv; |
8908 | 0 | nsCOMPtr<nsIURI> uri; |
8909 | 0 | rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), sourceURL, |
8910 | 0 | this, aBaseURI); |
8911 | 0 | if (NS_FAILED(rv)) { |
8912 | 0 | return nullptr; |
8913 | 0 | } |
8914 | 0 | |
8915 | 0 | *aIsImgSet = isImgSet; |
8916 | 0 |
|
8917 | 0 | // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in |
8918 | 0 | // this this <picture> share the same <sources> (though this is not valid per |
8919 | 0 | // spec) |
8920 | 0 | return uri.forget(); |
8921 | 0 | } |
8922 | | |
8923 | | void |
8924 | | nsIDocument::MaybePreLoadImage(nsIURI* uri, |
8925 | | const nsAString &aCrossOriginAttr, |
8926 | | enum mozilla::net::ReferrerPolicy aReferrerPolicy, |
8927 | | bool aIsImgSet) |
8928 | 0 | { |
8929 | 0 | // Early exit if the img is already present in the img-cache |
8930 | 0 | // which indicates that the "real" load has already started and |
8931 | 0 | // that we shouldn't preload it. |
8932 | 0 | if (nsContentUtils::IsImageInCache(uri, this)) { |
8933 | 0 | return; |
8934 | 0 | } |
8935 | 0 | |
8936 | 0 | nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | |
8937 | 0 | nsContentUtils::CORSModeToLoadImageFlags( |
8938 | 0 | Element::StringToCORSMode(aCrossOriginAttr)); |
8939 | 0 |
|
8940 | 0 | nsContentPolicyType policyType = |
8941 | 0 | aIsImgSet ? nsIContentPolicy::TYPE_IMAGESET : |
8942 | 0 | nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD; |
8943 | 0 |
|
8944 | 0 | // Image not in cache - trigger preload |
8945 | 0 | RefPtr<imgRequestProxy> request; |
8946 | 0 | nsresult rv = |
8947 | 0 | nsContentUtils::LoadImage(uri, |
8948 | 0 | static_cast<nsINode*>(this), |
8949 | 0 | this, |
8950 | 0 | NodePrincipal(), |
8951 | 0 | 0, |
8952 | 0 | mDocumentURI, // uri of document used as referrer |
8953 | 0 | aReferrerPolicy, |
8954 | 0 | nullptr, // no observer |
8955 | 0 | loadFlags, |
8956 | 0 | NS_LITERAL_STRING("img"), |
8957 | 0 | getter_AddRefs(request), |
8958 | 0 | policyType); |
8959 | 0 |
|
8960 | 0 | // Pin image-reference to avoid evicting it from the img-cache before |
8961 | 0 | // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and |
8962 | 0 | // unlink |
8963 | 0 | if (NS_SUCCEEDED(rv)) { |
8964 | 0 | mPreloadingImages.Put(uri, request.forget()); |
8965 | 0 | } |
8966 | 0 | } |
8967 | | |
8968 | | void |
8969 | | nsIDocument::MaybePreconnect(nsIURI* aOrigURI, mozilla::CORSMode aCORSMode) |
8970 | 0 | { |
8971 | 0 | NS_MutateURI mutator(aOrigURI); |
8972 | 0 | if (NS_FAILED(mutator.GetStatus())) { |
8973 | 0 | return; |
8974 | 0 | } |
8975 | 0 | |
8976 | 0 | // The URI created here is used in 2 contexts. One is nsISpeculativeConnect |
8977 | 0 | // which ignores the path and uses only the origin. The other is for the |
8978 | 0 | // document mPreloadedPreconnects de-duplication hash. Anonymous vs |
8979 | 0 | // non-Anonymous preconnects create different connections on the wire and |
8980 | 0 | // therefore should not be considred duplicates of each other and we |
8981 | 0 | // normalize the path before putting it in the hash to accomplish that. |
8982 | 0 | |
8983 | 0 | if (aCORSMode == CORS_ANONYMOUS) { |
8984 | 0 | mutator.SetPathQueryRef(NS_LITERAL_CSTRING("/anonymous")); |
8985 | 0 | } else { |
8986 | 0 | mutator.SetPathQueryRef(NS_LITERAL_CSTRING("/")); |
8987 | 0 | } |
8988 | 0 |
|
8989 | 0 | nsCOMPtr<nsIURI> uri; |
8990 | 0 | nsresult rv = mutator.Finalize(uri); |
8991 | 0 | if (NS_FAILED(rv)) { |
8992 | 0 | return; |
8993 | 0 | } |
8994 | 0 | |
8995 | 0 | auto entry = mPreloadedPreconnects.LookupForAdd(uri); |
8996 | 0 | if (entry) { |
8997 | 0 | return; // we found an existing entry |
8998 | 0 | } |
8999 | 0 | entry.OrInsert([] () { return true; }); |
9000 | 0 |
|
9001 | 0 | nsCOMPtr<nsISpeculativeConnect> |
9002 | 0 | speculator(do_QueryInterface(nsContentUtils::GetIOService())); |
9003 | 0 | if (!speculator) { |
9004 | 0 | return; |
9005 | 0 | } |
9006 | 0 | |
9007 | 0 | if (aCORSMode == CORS_ANONYMOUS) { |
9008 | 0 | speculator->SpeculativeAnonymousConnect2(uri, NodePrincipal(), nullptr); |
9009 | 0 | } else { |
9010 | 0 | speculator->SpeculativeConnect2(uri, NodePrincipal(), nullptr); |
9011 | 0 | } |
9012 | 0 | } |
9013 | | |
9014 | | void |
9015 | | nsIDocument::ForgetImagePreload(nsIURI* aURI) |
9016 | 0 | { |
9017 | 0 | // Checking count is faster than hashing the URI in the common |
9018 | 0 | // case of empty table. |
9019 | 0 | if (mPreloadingImages.Count() != 0) { |
9020 | 0 | nsCOMPtr<imgIRequest> req; |
9021 | 0 | mPreloadingImages.Remove(aURI, getter_AddRefs(req)); |
9022 | 0 | if (req) { |
9023 | 0 | // Make sure to cancel the request so imagelib knows it's gone. |
9024 | 0 | req->CancelAndForgetObserver(NS_BINDING_ABORTED); |
9025 | 0 | } |
9026 | 0 | } |
9027 | 0 | } |
9028 | | |
9029 | | void |
9030 | | nsIDocument::UpdateDocumentStates(EventStates aChangedStates) |
9031 | 0 | { |
9032 | 0 | if (aChangedStates.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) { |
9033 | 0 | if (IsDocumentRightToLeft()) { |
9034 | 0 | mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE; |
9035 | 0 | } else { |
9036 | 0 | mDocumentState &= ~NS_DOCUMENT_STATE_RTL_LOCALE; |
9037 | 0 | } |
9038 | 0 | } |
9039 | 0 |
|
9040 | 0 | if (aChangedStates.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) { |
9041 | 0 | if (IsTopLevelWindowInactive()) { |
9042 | 0 | mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE; |
9043 | 0 | } else { |
9044 | 0 | mDocumentState &= ~NS_DOCUMENT_STATE_WINDOW_INACTIVE; |
9045 | 0 | } |
9046 | 0 | } |
9047 | 0 | } |
9048 | | |
9049 | | namespace { |
9050 | | |
9051 | | /** |
9052 | | * Stub for LoadSheet(), since all we want is to get the sheet into |
9053 | | * the CSSLoader's style cache |
9054 | | */ |
9055 | | class StubCSSLoaderObserver final : public nsICSSLoaderObserver { |
9056 | 0 | ~StubCSSLoaderObserver() {} |
9057 | | public: |
9058 | | NS_IMETHOD |
9059 | | StyleSheetLoaded(StyleSheet*, bool, nsresult) override |
9060 | 0 | { |
9061 | 0 | return NS_OK; |
9062 | 0 | } |
9063 | | NS_DECL_ISUPPORTS |
9064 | | }; |
9065 | | NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver) |
9066 | | |
9067 | | } // namespace |
9068 | | |
9069 | | void |
9070 | | nsIDocument::PreloadStyle(nsIURI* uri, |
9071 | | const Encoding* aEncoding, |
9072 | | const nsAString& aCrossOriginAttr, |
9073 | | const enum mozilla::net::ReferrerPolicy aReferrerPolicy, |
9074 | | const nsAString& aIntegrity) |
9075 | 0 | { |
9076 | 0 | // The CSSLoader will retain this object after we return. |
9077 | 0 | nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver(); |
9078 | 0 |
|
9079 | 0 | // Charset names are always ASCII. |
9080 | 0 | CSSLoader()->LoadSheet(uri, |
9081 | 0 | true, |
9082 | 0 | NodePrincipal(), |
9083 | 0 | aEncoding, |
9084 | 0 | obs, |
9085 | 0 | Element::StringToCORSMode(aCrossOriginAttr), |
9086 | 0 | aReferrerPolicy, |
9087 | 0 | aIntegrity); |
9088 | 0 | } |
9089 | | |
9090 | | nsresult |
9091 | | nsIDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet, |
9092 | | RefPtr<mozilla::StyleSheet>* aSheet) |
9093 | 0 | { |
9094 | 0 | css::SheetParsingMode mode = |
9095 | 0 | isAgentSheet ? css::eAgentSheetFeatures |
9096 | 0 | : css::eAuthorSheetFeatures; |
9097 | 0 | return CSSLoader()->LoadSheetSync(uri, mode, isAgentSheet, aSheet); |
9098 | 0 | } |
9099 | | |
9100 | | class nsDelayedEventDispatcher : public Runnable |
9101 | | { |
9102 | | public: |
9103 | | explicit nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument>>& aDocuments) |
9104 | | : mozilla::Runnable("nsDelayedEventDispatcher") |
9105 | 0 | { |
9106 | 0 | mDocuments.SwapElements(aDocuments); |
9107 | 0 | } |
9108 | 0 | virtual ~nsDelayedEventDispatcher() {} |
9109 | | |
9110 | | NS_IMETHOD Run() override |
9111 | 0 | { |
9112 | 0 | FireOrClearDelayedEvents(mDocuments, true); |
9113 | 0 | return NS_OK; |
9114 | 0 | } |
9115 | | |
9116 | | private: |
9117 | | nsTArray<nsCOMPtr<nsIDocument>> mDocuments; |
9118 | | }; |
9119 | | |
9120 | | static bool |
9121 | | GetAndUnsuppressSubDocuments(nsIDocument* aDocument, void* aData) |
9122 | 0 | { |
9123 | 0 | if (aDocument->EventHandlingSuppressed() > 0) { |
9124 | 0 | aDocument->DecreaseEventSuppression(); |
9125 | 0 | aDocument->ScriptLoader()->RemoveExecuteBlocker(); |
9126 | 0 | } |
9127 | 0 |
|
9128 | 0 | auto* docs = static_cast<nsTArray<nsCOMPtr<nsIDocument>>*>(aData); |
9129 | 0 |
|
9130 | 0 | docs->AppendElement(aDocument); |
9131 | 0 | aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData); |
9132 | 0 | return true; |
9133 | 0 | } |
9134 | | |
9135 | | void |
9136 | | nsIDocument::UnsuppressEventHandlingAndFireEvents(bool aFireEvents) |
9137 | 0 | { |
9138 | 0 | nsTArray<nsCOMPtr<nsIDocument>> documents; |
9139 | 0 | GetAndUnsuppressSubDocuments(this, &documents); |
9140 | 0 |
|
9141 | 0 | if (aFireEvents) { |
9142 | 0 | MOZ_RELEASE_ASSERT(NS_IsMainThread()); |
9143 | 0 | nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(documents); |
9144 | 0 | Dispatch(TaskCategory::Other, ded.forget()); |
9145 | 0 | } else { |
9146 | 0 | FireOrClearDelayedEvents(documents, false); |
9147 | 0 | } |
9148 | 0 | } |
9149 | | |
9150 | | nsISupports* |
9151 | | nsIDocument::GetCurrentContentSink() |
9152 | 0 | { |
9153 | 0 | return mParser ? mParser->GetContentSink() : nullptr; |
9154 | 0 | } |
9155 | | |
9156 | | nsIDocument* |
9157 | | nsIDocument::GetTemplateContentsOwner() |
9158 | 0 | { |
9159 | 0 | if (!mTemplateContentsOwner) { |
9160 | 0 | bool hasHadScriptObject = true; |
9161 | 0 | nsIScriptGlobalObject* scriptObject = |
9162 | 0 | GetScriptHandlingObject(hasHadScriptObject); |
9163 | 0 |
|
9164 | 0 | nsCOMPtr<nsIDocument> document; |
9165 | 0 | nsresult rv = NS_NewDOMDocument(getter_AddRefs(document), |
9166 | 0 | EmptyString(), // aNamespaceURI |
9167 | 0 | EmptyString(), // aQualifiedName |
9168 | 0 | nullptr, // aDoctype |
9169 | 0 | nsIDocument::GetDocumentURI(), |
9170 | 0 | nsIDocument::GetDocBaseURI(), |
9171 | 0 | NodePrincipal(), |
9172 | 0 | true, // aLoadedAsData |
9173 | 0 | scriptObject, // aEventObject |
9174 | 0 | DocumentFlavorHTML); |
9175 | 0 | NS_ENSURE_SUCCESS(rv, nullptr); |
9176 | 0 |
|
9177 | 0 | mTemplateContentsOwner = document; |
9178 | 0 | NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr); |
9179 | 0 |
|
9180 | 0 | nsDocument* doc = static_cast<nsDocument*>(mTemplateContentsOwner.get()); |
9181 | 0 |
|
9182 | 0 | if (!scriptObject) { |
9183 | 0 | mTemplateContentsOwner->SetScopeObject(GetScopeObject()); |
9184 | 0 | } |
9185 | 0 |
|
9186 | 0 | doc->mHasHadScriptHandlingObject = hasHadScriptObject; |
9187 | 0 |
|
9188 | 0 | // Set |doc| as the template contents owner of itself so that |
9189 | 0 | // |doc| is the template contents owner of template elements created |
9190 | 0 | // by |doc|. |
9191 | 0 | doc->mTemplateContentsOwner = doc; |
9192 | 0 | } |
9193 | 0 |
|
9194 | 0 | return mTemplateContentsOwner; |
9195 | 0 | } |
9196 | | |
9197 | | static already_AddRefed<nsPIDOMWindowOuter> |
9198 | | FindTopWindowForElement(Element* element) |
9199 | 0 | { |
9200 | 0 | nsIDocument* document = element->OwnerDoc(); |
9201 | 0 | if (!document) { |
9202 | 0 | return nullptr; |
9203 | 0 | } |
9204 | 0 | |
9205 | 0 | nsCOMPtr<nsPIDOMWindowOuter> window = document->GetWindow(); |
9206 | 0 | if (!window) { |
9207 | 0 | return nullptr; |
9208 | 0 | } |
9209 | 0 | |
9210 | 0 | // Trying to find the top window (equivalent to window.top). |
9211 | 0 | if (nsCOMPtr<nsPIDOMWindowOuter> top = window->GetTop()) { |
9212 | 0 | window = top.forget(); |
9213 | 0 | } |
9214 | 0 | return window.forget(); |
9215 | 0 | } |
9216 | | |
9217 | | /** |
9218 | | * nsAutoFocusEvent is used to dispatch a focus event for an |
9219 | | * nsGenericHTMLFormElement with the autofocus attribute enabled. |
9220 | | */ |
9221 | | class nsAutoFocusEvent : public Runnable |
9222 | | { |
9223 | | public: |
9224 | | explicit nsAutoFocusEvent(already_AddRefed<Element>&& aElement, |
9225 | | already_AddRefed<nsPIDOMWindowOuter>&& aTopWindow) |
9226 | | : mozilla::Runnable("nsAutoFocusEvent") |
9227 | | , mElement(aElement) |
9228 | | , mTopWindow(aTopWindow) |
9229 | 0 | { |
9230 | 0 | } |
9231 | | |
9232 | | NS_IMETHOD Run() override |
9233 | 0 | { |
9234 | 0 | nsCOMPtr<nsPIDOMWindowOuter> currentTopWindow = |
9235 | 0 | FindTopWindowForElement(mElement); |
9236 | 0 | if (currentTopWindow != mTopWindow) { |
9237 | 0 | // The element's top window changed from when the event was queued. |
9238 | 0 | // Don't take away focus from an unrelated window. |
9239 | 0 | return NS_OK; |
9240 | 0 | } |
9241 | 0 | |
9242 | 0 | // Don't steal focus from the user. |
9243 | 0 | if (mTopWindow->GetFocusedElement()) { |
9244 | 0 | return NS_OK; |
9245 | 0 | } |
9246 | 0 | |
9247 | 0 | mozilla::ErrorResult rv; |
9248 | 0 | mElement->Focus(rv); |
9249 | 0 | return rv.StealNSResult(); |
9250 | 0 | } |
9251 | | private: |
9252 | | nsCOMPtr<Element> mElement; |
9253 | | nsCOMPtr<nsPIDOMWindowOuter> mTopWindow; |
9254 | | }; |
9255 | | |
9256 | | void |
9257 | | nsIDocument::SetAutoFocusElement(Element* aAutoFocusElement) |
9258 | 0 | { |
9259 | 0 | if (mAutoFocusFired) { |
9260 | 0 | // Too late. |
9261 | 0 | return; |
9262 | 0 | } |
9263 | 0 | |
9264 | 0 | if (mAutoFocusElement) { |
9265 | 0 | // The spec disallows multiple autofocus elements, so we consider only the |
9266 | 0 | // first one to preserve the old behavior. |
9267 | 0 | return; |
9268 | 0 | } |
9269 | 0 | |
9270 | 0 | mAutoFocusElement = do_GetWeakReference(aAutoFocusElement); |
9271 | 0 | TriggerAutoFocus(); |
9272 | 0 | } |
9273 | | |
9274 | | void |
9275 | | nsIDocument::TriggerAutoFocus() |
9276 | 0 | { |
9277 | 0 | if (mAutoFocusFired) { |
9278 | 0 | return; |
9279 | 0 | } |
9280 | 0 | |
9281 | 0 | if (!mPresShell || !mPresShell->DidInitialize()) { |
9282 | 0 | // Delay autofocus until frames are constructed so that we don't thrash |
9283 | 0 | // style and layout calculations. |
9284 | 0 | return; |
9285 | 0 | } |
9286 | 0 | |
9287 | 0 | nsCOMPtr<Element> autoFocusElement = do_QueryReferent(mAutoFocusElement); |
9288 | 0 | if (autoFocusElement && autoFocusElement->OwnerDoc() == this) { |
9289 | 0 | mAutoFocusFired = true; |
9290 | 0 |
|
9291 | 0 | nsCOMPtr<nsPIDOMWindowOuter> topWindow = |
9292 | 0 | FindTopWindowForElement(autoFocusElement); |
9293 | 0 | if (!topWindow) { |
9294 | 0 | return; |
9295 | 0 | } |
9296 | 0 | |
9297 | 0 | // NOTE: This may be removed in the future since the spec technically |
9298 | 0 | // allows autofocus after load. |
9299 | 0 | nsCOMPtr<nsIDocument> topDoc = topWindow->GetExtantDoc(); |
9300 | 0 | if (topDoc && topDoc->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE) { |
9301 | 0 | return; |
9302 | 0 | } |
9303 | 0 | |
9304 | 0 | nsCOMPtr<nsIRunnable> event = |
9305 | 0 | new nsAutoFocusEvent(autoFocusElement.forget(), topWindow.forget()); |
9306 | 0 | nsresult rv = NS_DispatchToCurrentThread(event.forget()); |
9307 | 0 | NS_ENSURE_SUCCESS_VOID(rv); |
9308 | 0 | } |
9309 | 0 | } |
9310 | | |
9311 | | void |
9312 | | nsIDocument::SetScrollToRef(nsIURI* aDocumentURI) |
9313 | 0 | { |
9314 | 0 | if (!aDocumentURI) { |
9315 | 0 | return; |
9316 | 0 | } |
9317 | 0 | |
9318 | 0 | nsAutoCString ref; |
9319 | 0 |
|
9320 | 0 | // Since all URI's that pass through here aren't URL's we can't |
9321 | 0 | // rely on the nsIURI implementation for providing a way for |
9322 | 0 | // finding the 'ref' part of the URI, we'll haveto revert to |
9323 | 0 | // string routines for finding the data past '#' |
9324 | 0 |
|
9325 | 0 | nsresult rv = aDocumentURI->GetSpec(ref); |
9326 | 0 | if (NS_FAILED(rv)) { |
9327 | 0 | Unused << aDocumentURI->GetRef(mScrollToRef); |
9328 | 0 | return; |
9329 | 0 | } |
9330 | 0 | |
9331 | 0 | nsReadingIterator<char> start, end; |
9332 | 0 |
|
9333 | 0 | ref.BeginReading(start); |
9334 | 0 | ref.EndReading(end); |
9335 | 0 |
|
9336 | 0 | if (FindCharInReadable('#', start, end)) { |
9337 | 0 | ++start; // Skip over the '#' |
9338 | 0 |
|
9339 | 0 | mScrollToRef = Substring(start, end); |
9340 | 0 | } |
9341 | 0 | } |
9342 | | |
9343 | | void |
9344 | | nsIDocument::ScrollToRef() |
9345 | 0 | { |
9346 | 0 | if (mScrolledToRefAlready) { |
9347 | 0 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
9348 | 0 | if (shell) { |
9349 | 0 | shell->ScrollToAnchor(); |
9350 | 0 | } |
9351 | 0 | return; |
9352 | 0 | } |
9353 | 0 |
|
9354 | 0 | if (mScrollToRef.IsEmpty()) { |
9355 | 0 | return; |
9356 | 0 | } |
9357 | 0 | |
9358 | 0 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
9359 | 0 | if (shell) { |
9360 | 0 | nsresult rv = NS_ERROR_FAILURE; |
9361 | 0 | // We assume that the bytes are in UTF-8, as it says in the spec: |
9362 | 0 | // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1 |
9363 | 0 | NS_ConvertUTF8toUTF16 ref(mScrollToRef); |
9364 | 0 | // Check an empty string which might be caused by the UTF-8 conversion |
9365 | 0 | if (!ref.IsEmpty()) { |
9366 | 0 | // Note that GoToAnchor will handle flushing layout as needed. |
9367 | 0 | rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef); |
9368 | 0 | } else { |
9369 | 0 | rv = NS_ERROR_FAILURE; |
9370 | 0 | } |
9371 | 0 |
|
9372 | 0 | if (NS_FAILED(rv)) { |
9373 | 0 | char* tmpstr = ToNewCString(mScrollToRef); |
9374 | 0 | if (!tmpstr) { |
9375 | 0 | return; |
9376 | 0 | } |
9377 | 0 | nsUnescape(tmpstr); |
9378 | 0 | nsAutoCString unescapedRef; |
9379 | 0 | unescapedRef.Assign(tmpstr); |
9380 | 0 | free(tmpstr); |
9381 | 0 |
|
9382 | 0 | NS_ConvertUTF8toUTF16 utf16Str(unescapedRef); |
9383 | 0 | if (!utf16Str.IsEmpty()) { |
9384 | 0 | rv = shell->GoToAnchor(utf16Str, mChangeScrollPosWhenScrollingToRef); |
9385 | 0 | } |
9386 | 0 |
|
9387 | 0 | // If UTF-8 URI failed then try to assume the string as a |
9388 | 0 | // document's charset. |
9389 | 0 | if (NS_FAILED(rv)) { |
9390 | 0 | const Encoding* encoding = GetDocumentCharacterSet(); |
9391 | 0 | rv = encoding->DecodeWithoutBOMHandling(unescapedRef, ref); |
9392 | 0 | if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) { |
9393 | 0 | rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef); |
9394 | 0 | } |
9395 | 0 | } |
9396 | 0 | } |
9397 | 0 | if (NS_SUCCEEDED(rv)) { |
9398 | 0 | mScrolledToRefAlready = true; |
9399 | 0 | } |
9400 | 0 | } |
9401 | 0 | } |
9402 | | |
9403 | | void |
9404 | | nsIDocument::RegisterActivityObserver(nsISupports* aSupports) |
9405 | 0 | { |
9406 | 0 | if (!mActivityObservers) { |
9407 | 0 | mActivityObservers = new nsTHashtable<nsPtrHashKey<nsISupports> >(); |
9408 | 0 | } |
9409 | 0 | mActivityObservers->PutEntry(aSupports); |
9410 | 0 | } |
9411 | | |
9412 | | bool |
9413 | | nsIDocument::UnregisterActivityObserver(nsISupports* aSupports) |
9414 | 0 | { |
9415 | 0 | if (!mActivityObservers) { |
9416 | 0 | return false; |
9417 | 0 | } |
9418 | 0 | nsPtrHashKey<nsISupports>* entry = mActivityObservers->GetEntry(aSupports); |
9419 | 0 | if (!entry) { |
9420 | 0 | return false; |
9421 | 0 | } |
9422 | 0 | mActivityObservers->RemoveEntry(entry); |
9423 | 0 | return true; |
9424 | 0 | } |
9425 | | |
9426 | | void |
9427 | | nsIDocument::EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator, |
9428 | | void* aData) |
9429 | 0 | { |
9430 | 0 | if (!mActivityObservers) |
9431 | 0 | return; |
9432 | 0 | |
9433 | 0 | for (auto iter = mActivityObservers->ConstIter(); !iter.Done(); |
9434 | 0 | iter.Next()) { |
9435 | 0 | aEnumerator(iter.Get()->GetKey(), aData); |
9436 | 0 | } |
9437 | 0 | } |
9438 | | |
9439 | | void |
9440 | | nsIDocument::RegisterPendingLinkUpdate(Link* aLink) |
9441 | 0 | { |
9442 | 0 | if (aLink->HasPendingLinkUpdate()) { |
9443 | 0 | return; |
9444 | 0 | } |
9445 | 0 | |
9446 | 0 | aLink->SetHasPendingLinkUpdate(); |
9447 | 0 |
|
9448 | 0 | if (!mHasLinksToUpdateRunnable && !mFlushingPendingLinkUpdates) { |
9449 | 0 | nsCOMPtr<nsIRunnable> event = |
9450 | 0 | NewRunnableMethod("nsIDocument::FlushPendingLinkUpdatesFromRunnable", |
9451 | 0 | this, |
9452 | 0 | &nsIDocument::FlushPendingLinkUpdatesFromRunnable); |
9453 | 0 | // Do this work in a second in the worst case. |
9454 | 0 | nsresult rv = |
9455 | 0 | NS_IdleDispatchToCurrentThread(event.forget(), 1000); |
9456 | 0 | if (NS_FAILED(rv)) { |
9457 | 0 | // If during shutdown posting a runnable doesn't succeed, we probably |
9458 | 0 | // don't need to update link states. |
9459 | 0 | return; |
9460 | 0 | } |
9461 | 0 | mHasLinksToUpdateRunnable = true; |
9462 | 0 | } |
9463 | 0 |
|
9464 | 0 | mLinksToUpdate.InfallibleAppend(aLink); |
9465 | 0 | } |
9466 | | |
9467 | | void |
9468 | | nsIDocument::FlushPendingLinkUpdatesFromRunnable() |
9469 | 0 | { |
9470 | 0 | MOZ_ASSERT(mHasLinksToUpdateRunnable); |
9471 | 0 | mHasLinksToUpdateRunnable = false; |
9472 | 0 | FlushPendingLinkUpdates(); |
9473 | 0 | } |
9474 | | |
9475 | | void |
9476 | | nsIDocument::FlushPendingLinkUpdates() |
9477 | 0 | { |
9478 | 0 | if (mFlushingPendingLinkUpdates) { |
9479 | 0 | return; |
9480 | 0 | } |
9481 | 0 | |
9482 | 0 | auto restore = MakeScopeExit([&] { mFlushingPendingLinkUpdates = false; }); |
9483 | 0 | mFlushingPendingLinkUpdates = true; |
9484 | 0 |
|
9485 | 0 | while (!mLinksToUpdate.IsEmpty()) { |
9486 | 0 | LinksToUpdateList links(std::move(mLinksToUpdate)); |
9487 | 0 | for (auto iter = links.Iter(); !iter.Done(); iter.Next()) { |
9488 | 0 | Link* link = iter.Get(); |
9489 | 0 | Element* element = link->GetElement(); |
9490 | 0 | if (element->OwnerDoc() == this) { |
9491 | 0 | link->ClearHasPendingLinkUpdate(); |
9492 | 0 | if (element->IsInComposedDoc()) { |
9493 | 0 | element->UpdateLinkState(link->LinkState()); |
9494 | 0 | } |
9495 | 0 | } |
9496 | 0 | } |
9497 | 0 | } |
9498 | 0 | } |
9499 | | |
9500 | | already_AddRefed<nsIDocument> |
9501 | | nsIDocument::CreateStaticClone(nsIDocShell* aCloneContainer) |
9502 | 0 | { |
9503 | 0 | nsDocument* thisAsDoc = static_cast<nsDocument*>(this); |
9504 | 0 | mCreatingStaticClone = true; |
9505 | 0 |
|
9506 | 0 | // Make document use different container during cloning. |
9507 | 0 | RefPtr<nsDocShell> originalShell = mDocumentContainer.get(); |
9508 | 0 | SetContainer(static_cast<nsDocShell*>(aCloneContainer)); |
9509 | 0 | ErrorResult rv; |
9510 | 0 | nsCOMPtr<nsINode> clonedNode = thisAsDoc->CloneNode(true, rv); |
9511 | 0 | SetContainer(originalShell); |
9512 | 0 |
|
9513 | 0 | RefPtr<nsDocument> clonedDoc; |
9514 | 0 | if (rv.Failed()) { |
9515 | 0 | // Don't return yet; we need to reset mCreatingStaticClone |
9516 | 0 | rv.SuppressException(); |
9517 | 0 | } else { |
9518 | 0 | nsCOMPtr<nsIDocument> tmp = do_QueryInterface(clonedNode); |
9519 | 0 | if (tmp) { |
9520 | 0 | clonedDoc = static_cast<nsDocument*>(tmp.get()); |
9521 | 0 | if (IsStaticDocument()) { |
9522 | 0 | clonedDoc->mOriginalDocument = mOriginalDocument; |
9523 | 0 | } else { |
9524 | 0 | clonedDoc->mOriginalDocument = this; |
9525 | 0 | } |
9526 | 0 |
|
9527 | 0 | clonedDoc->mOriginalDocument->mStaticCloneCount++; |
9528 | 0 |
|
9529 | 0 | size_t sheetsCount = SheetCount(); |
9530 | 0 | for (size_t i = 0; i < sheetsCount; ++i) { |
9531 | 0 | RefPtr<StyleSheet> sheet = SheetAt(i); |
9532 | 0 | if (sheet) { |
9533 | 0 | if (sheet->IsApplicable()) { |
9534 | 0 | RefPtr<StyleSheet> clonedSheet = |
9535 | 0 | sheet->Clone(nullptr, nullptr, clonedDoc, nullptr); |
9536 | 0 | NS_WARNING_ASSERTION(clonedSheet, |
9537 | 0 | "Cloning a stylesheet didn't work!"); |
9538 | 0 | if (clonedSheet) { |
9539 | 0 | clonedDoc->AddStyleSheet(clonedSheet); |
9540 | 0 | } |
9541 | 0 | } |
9542 | 0 | } |
9543 | 0 | } |
9544 | 0 |
|
9545 | 0 | for (int t = 0; t < AdditionalSheetTypeCount; ++t) { |
9546 | 0 | auto& sheets = mAdditionalSheets[additionalSheetType(t)]; |
9547 | 0 | for (StyleSheet* sheet : sheets) { |
9548 | 0 | if (sheet->IsApplicable()) { |
9549 | 0 | RefPtr<StyleSheet> clonedSheet = |
9550 | 0 | sheet->Clone(nullptr, nullptr, clonedDoc, nullptr); |
9551 | 0 | NS_WARNING_ASSERTION(clonedSheet, |
9552 | 0 | "Cloning a stylesheet didn't work!"); |
9553 | 0 | if (clonedSheet) { |
9554 | 0 | clonedDoc->AddAdditionalStyleSheet(additionalSheetType(t), clonedSheet); |
9555 | 0 | } |
9556 | 0 | } |
9557 | 0 | } |
9558 | 0 | } |
9559 | 0 |
|
9560 | 0 | // Font faces created with the JS API will not be reflected in the |
9561 | 0 | // stylesheets and need to be copied over to the cloned document. |
9562 | 0 | if (const FontFaceSet* set = GetFonts()) { |
9563 | 0 | set->CopyNonRuleFacesTo(clonedDoc->Fonts()); |
9564 | 0 | } |
9565 | 0 |
|
9566 | 0 | } |
9567 | 0 | } |
9568 | 0 | mCreatingStaticClone = false; |
9569 | 0 | return clonedDoc.forget(); |
9570 | 0 | } |
9571 | | |
9572 | | void |
9573 | | nsIDocument::UnlinkOriginalDocumentIfStatic() |
9574 | 0 | { |
9575 | 0 | if (IsStaticDocument() && mOriginalDocument) { |
9576 | 0 | MOZ_ASSERT(mOriginalDocument->mStaticCloneCount > 0); |
9577 | 0 | mOriginalDocument->mStaticCloneCount--; |
9578 | 0 | mOriginalDocument = nullptr; |
9579 | 0 | } |
9580 | 0 | MOZ_ASSERT(!mOriginalDocument); |
9581 | 0 | } |
9582 | | |
9583 | | nsresult |
9584 | | nsIDocument::ScheduleFrameRequestCallback(FrameRequestCallback& aCallback, |
9585 | | int32_t *aHandle) |
9586 | 0 | { |
9587 | 0 | if (mFrameRequestCallbackCounter == INT32_MAX) { |
9588 | 0 | // Can't increment without overflowing; bail out |
9589 | 0 | return NS_ERROR_NOT_AVAILABLE; |
9590 | 0 | } |
9591 | 0 | int32_t newHandle = ++mFrameRequestCallbackCounter; |
9592 | 0 |
|
9593 | 0 | DebugOnly<FrameRequest*> request = |
9594 | 0 | mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle)); |
9595 | 0 | NS_ASSERTION(request, "This is supposed to be infallible!"); |
9596 | 0 | UpdateFrameRequestCallbackSchedulingState(); |
9597 | 0 |
|
9598 | 0 | *aHandle = newHandle; |
9599 | 0 | return NS_OK; |
9600 | 0 | } |
9601 | | |
9602 | | void |
9603 | | nsIDocument::CancelFrameRequestCallback(int32_t aHandle) |
9604 | 0 | { |
9605 | 0 | // mFrameRequestCallbacks is stored sorted by handle |
9606 | 0 | if (mFrameRequestCallbacks.RemoveElementSorted(aHandle)) { |
9607 | 0 | UpdateFrameRequestCallbackSchedulingState(); |
9608 | 0 | } |
9609 | 0 | } |
9610 | | |
9611 | | nsresult |
9612 | | nsIDocument::GetStateObject(nsIVariant** aState) |
9613 | 0 | { |
9614 | 0 | // Get the document's current state object. This is the object backing both |
9615 | 0 | // history.state and popStateEvent.state. |
9616 | 0 | // |
9617 | 0 | // mStateObjectContainer may be null; this just means that there's no |
9618 | 0 | // current state object. |
9619 | 0 |
|
9620 | 0 | if (!mStateObjectCached && mStateObjectContainer) { |
9621 | 0 | AutoJSContext cx; |
9622 | 0 | nsIGlobalObject* sgo = GetScopeObject(); |
9623 | 0 | NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED); |
9624 | 0 | JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject()); |
9625 | 0 | NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED); |
9626 | 0 | JSAutoRealm ar(cx, global); |
9627 | 0 |
|
9628 | 0 | mStateObjectContainer-> |
9629 | 0 | DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached)); |
9630 | 0 | } |
9631 | 0 |
|
9632 | 0 | NS_IF_ADDREF(*aState = mStateObjectCached); |
9633 | 0 | return NS_OK; |
9634 | 0 | } |
9635 | | |
9636 | | void |
9637 | | nsIDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming) |
9638 | 0 | { |
9639 | 0 | mTiming = aTiming; |
9640 | 0 | if (!mLoadingTimeStamp.IsNull() && mTiming) { |
9641 | 0 | mTiming->SetDOMLoadingTimeStamp(GetDocumentURI(), mLoadingTimeStamp); |
9642 | 0 | } |
9643 | 0 | } |
9644 | | |
9645 | | nsContentList* |
9646 | | nsIDocument::ImageMapList() |
9647 | 0 | { |
9648 | 0 | if (!mImageMaps) { |
9649 | 0 | mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, |
9650 | 0 | nsGkAtoms::map, nsGkAtoms::map); |
9651 | 0 | } |
9652 | 0 |
|
9653 | 0 | return mImageMaps; |
9654 | 0 | } |
9655 | | |
9656 | | #define DEPRECATED_OPERATION(_op) #_op "Warning", |
9657 | | static const char* kDeprecationWarnings[] = { |
9658 | | #include "nsDeprecatedOperationList.h" |
9659 | | nullptr |
9660 | | }; |
9661 | | #undef DEPRECATED_OPERATION |
9662 | | |
9663 | | #define DOCUMENT_WARNING(_op) #_op "Warning", |
9664 | | static const char* kDocumentWarnings[] = { |
9665 | | #include "nsDocumentWarningList.h" |
9666 | | nullptr |
9667 | | }; |
9668 | | #undef DOCUMENT_WARNING |
9669 | | |
9670 | | static UseCounter |
9671 | | OperationToUseCounter(nsIDocument::DeprecatedOperations aOperation) |
9672 | 0 | { |
9673 | 0 | switch(aOperation) { |
9674 | 0 | #define DEPRECATED_OPERATION(_op) \ |
9675 | 0 | case nsIDocument::e##_op: return eUseCounter_##_op; |
9676 | 0 | #include "nsDeprecatedOperationList.h" |
9677 | 0 | #undef DEPRECATED_OPERATION |
9678 | 0 | default: |
9679 | 0 | MOZ_CRASH(); |
9680 | 0 | } |
9681 | 0 | } |
9682 | | |
9683 | | bool |
9684 | | nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation) const |
9685 | 0 | { |
9686 | 0 | return mDeprecationWarnedAbout[aOperation]; |
9687 | 0 | } |
9688 | | |
9689 | | void |
9690 | | nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation, |
9691 | | bool asError /* = false */) const |
9692 | 0 | { |
9693 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
9694 | 0 | if (HasWarnedAbout(aOperation)) { |
9695 | 0 | return; |
9696 | 0 | } |
9697 | 0 | mDeprecationWarnedAbout[aOperation] = true; |
9698 | 0 | // Don't count deprecated operations for about pages since those pages |
9699 | 0 | // are almost in our control, and we always need to remove uses there |
9700 | 0 | // before we remove the operation itself anyway. |
9701 | 0 | if (!IsAboutPage()) { |
9702 | 0 | const_cast<nsIDocument*>(this)-> |
9703 | 0 | SetDocumentAndPageUseCounter(OperationToUseCounter(aOperation)); |
9704 | 0 | } |
9705 | 0 | uint32_t flags = asError ? nsIScriptError::errorFlag |
9706 | 0 | : nsIScriptError::warningFlag; |
9707 | 0 | nsContentUtils::ReportToConsole(flags, |
9708 | 0 | NS_LITERAL_CSTRING("DOM Core"), this, |
9709 | 0 | nsContentUtils::eDOM_PROPERTIES, |
9710 | 0 | kDeprecationWarnings[aOperation]); |
9711 | 0 | } |
9712 | | |
9713 | | bool |
9714 | | nsIDocument::HasWarnedAbout(DocumentWarnings aWarning) const |
9715 | 0 | { |
9716 | 0 | return mDocWarningWarnedAbout[aWarning]; |
9717 | 0 | } |
9718 | | |
9719 | | void |
9720 | | nsIDocument::WarnOnceAbout(DocumentWarnings aWarning, |
9721 | | bool asError /* = false */, |
9722 | | const char16_t **aParams /* = nullptr */, |
9723 | | uint32_t aParamsLength /* = 0 */) const |
9724 | 0 | { |
9725 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
9726 | 0 | if (HasWarnedAbout(aWarning)) { |
9727 | 0 | return; |
9728 | 0 | } |
9729 | 0 | mDocWarningWarnedAbout[aWarning] = true; |
9730 | 0 | uint32_t flags = asError ? nsIScriptError::errorFlag |
9731 | 0 | : nsIScriptError::warningFlag; |
9732 | 0 | nsContentUtils::ReportToConsole(flags, |
9733 | 0 | NS_LITERAL_CSTRING("DOM Core"), this, |
9734 | 0 | nsContentUtils::eDOM_PROPERTIES, |
9735 | 0 | kDocumentWarnings[aWarning], |
9736 | 0 | aParams, |
9737 | 0 | aParamsLength); |
9738 | 0 | } |
9739 | | |
9740 | | mozilla::dom::ImageTracker* |
9741 | | nsIDocument::ImageTracker() |
9742 | 0 | { |
9743 | 0 | if (!mImageTracker) { |
9744 | 0 | mImageTracker = new mozilla::dom::ImageTracker; |
9745 | 0 | } |
9746 | 0 | return mImageTracker; |
9747 | 0 | } |
9748 | | |
9749 | | static bool |
9750 | | AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg) |
9751 | 0 | { |
9752 | 0 | nsTArray<nsIObjectLoadingContent*>* plugins = |
9753 | 0 | reinterpret_cast<nsTArray<nsIObjectLoadingContent*>*>(userArg); |
9754 | 0 | MOZ_ASSERT(plugins); |
9755 | 0 | aDocument->GetPlugins(*plugins); |
9756 | 0 | return true; |
9757 | 0 | } |
9758 | | |
9759 | | void |
9760 | | nsIDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins) |
9761 | 0 | { |
9762 | 0 | aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count()); |
9763 | 0 | for (auto iter = mPlugins.ConstIter(); !iter.Done(); iter.Next()) { |
9764 | 0 | aPlugins.AppendElement(iter.Get()->GetKey()); |
9765 | 0 | } |
9766 | 0 | EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins); |
9767 | 0 | } |
9768 | | |
9769 | | void |
9770 | | nsIDocument::ScheduleSVGUseElementShadowTreeUpdate(SVGUseElement& aUseElement) |
9771 | 0 | { |
9772 | 0 | MOZ_ASSERT(aUseElement.IsInComposedDoc()); |
9773 | 0 |
|
9774 | 0 | mSVGUseElementsNeedingShadowTreeUpdate.PutEntry(&aUseElement); |
9775 | 0 |
|
9776 | 0 | if (nsIPresShell* shell = GetShell()) { |
9777 | 0 | shell->EnsureStyleFlush(); |
9778 | 0 | } |
9779 | 0 | } |
9780 | | |
9781 | | void |
9782 | | nsIDocument::DoUpdateSVGUseElementShadowTrees() |
9783 | 0 | { |
9784 | 0 | MOZ_ASSERT(!mSVGUseElementsNeedingShadowTreeUpdate.IsEmpty()); |
9785 | 0 | nsTArray<RefPtr<SVGUseElement>> useElementsToUpdate; |
9786 | 0 |
|
9787 | 0 | do { |
9788 | 0 | useElementsToUpdate.Clear(); |
9789 | 0 | useElementsToUpdate.SetCapacity(mSVGUseElementsNeedingShadowTreeUpdate.Count()); |
9790 | 0 |
|
9791 | 0 | { |
9792 | 0 | for (auto iter = mSVGUseElementsNeedingShadowTreeUpdate.ConstIter(); |
9793 | 0 | !iter.Done(); |
9794 | 0 | iter.Next()) { |
9795 | 0 | useElementsToUpdate.AppendElement(iter.Get()->GetKey()); |
9796 | 0 | } |
9797 | 0 | mSVGUseElementsNeedingShadowTreeUpdate.Clear(); |
9798 | 0 | } |
9799 | 0 |
|
9800 | 0 | for (auto& useElement : useElementsToUpdate) { |
9801 | 0 | if (MOZ_UNLIKELY(!useElement->IsInComposedDoc())) { |
9802 | 0 | // The element was in another <use> shadow tree which we processed |
9803 | 0 | // already and also needed an update, and is removed from the document |
9804 | 0 | // now, so nothing to do here. |
9805 | 0 | MOZ_ASSERT(useElementsToUpdate.Length() > 1); |
9806 | 0 | continue; |
9807 | 0 | } |
9808 | 0 | useElement->UpdateShadowTree(); |
9809 | 0 | } |
9810 | 0 | } while (!mSVGUseElementsNeedingShadowTreeUpdate.IsEmpty()); |
9811 | 0 | } |
9812 | | |
9813 | | void |
9814 | | nsIDocument::NotifyMediaFeatureValuesChanged() |
9815 | 0 | { |
9816 | 0 | for (auto iter = mResponsiveContent.ConstIter(); !iter.Done(); |
9817 | 0 | iter.Next()) { |
9818 | 0 | RefPtr<HTMLImageElement> imageElement = iter.Get()->GetKey(); |
9819 | 0 | imageElement->MediaFeatureValuesChanged(); |
9820 | 0 | } |
9821 | 0 | } |
9822 | | |
9823 | | already_AddRefed<Touch> |
9824 | | nsIDocument::CreateTouch(nsGlobalWindowInner* aView, |
9825 | | EventTarget* aTarget, |
9826 | | int32_t aIdentifier, |
9827 | | int32_t aPageX, int32_t aPageY, |
9828 | | int32_t aScreenX, int32_t aScreenY, |
9829 | | int32_t aClientX, int32_t aClientY, |
9830 | | int32_t aRadiusX, int32_t aRadiusY, |
9831 | | float aRotationAngle, |
9832 | | float aForce) |
9833 | 0 | { |
9834 | 0 | RefPtr<Touch> touch = new Touch(aTarget, |
9835 | 0 | aIdentifier, |
9836 | 0 | aPageX, aPageY, |
9837 | 0 | aScreenX, aScreenY, |
9838 | 0 | aClientX, aClientY, |
9839 | 0 | aRadiusX, aRadiusY, |
9840 | 0 | aRotationAngle, |
9841 | 0 | aForce); |
9842 | 0 | return touch.forget(); |
9843 | 0 | } |
9844 | | |
9845 | | already_AddRefed<TouchList> |
9846 | | nsIDocument::CreateTouchList() |
9847 | 0 | { |
9848 | 0 | RefPtr<TouchList> retval = new TouchList(ToSupports(this)); |
9849 | 0 | return retval.forget(); |
9850 | 0 | } |
9851 | | |
9852 | | already_AddRefed<TouchList> |
9853 | | nsIDocument::CreateTouchList(Touch& aTouch, |
9854 | | const Sequence<OwningNonNull<Touch> >& aTouches) |
9855 | 0 | { |
9856 | 0 | RefPtr<TouchList> retval = new TouchList(ToSupports(this)); |
9857 | 0 | retval->Append(&aTouch); |
9858 | 0 | for (uint32_t i = 0; i < aTouches.Length(); ++i) { |
9859 | 0 | retval->Append(aTouches[i].get()); |
9860 | 0 | } |
9861 | 0 | return retval.forget(); |
9862 | 0 | } |
9863 | | |
9864 | | already_AddRefed<TouchList> |
9865 | | nsIDocument::CreateTouchList(const Sequence<OwningNonNull<Touch> >& aTouches) |
9866 | 0 | { |
9867 | 0 | RefPtr<TouchList> retval = new TouchList(ToSupports(this)); |
9868 | 0 | for (uint32_t i = 0; i < aTouches.Length(); ++i) { |
9869 | 0 | retval->Append(aTouches[i].get()); |
9870 | 0 | } |
9871 | 0 | return retval.forget(); |
9872 | 0 | } |
9873 | | |
9874 | | already_AddRefed<nsDOMCaretPosition> |
9875 | | nsIDocument::CaretPositionFromPoint(float aX, float aY) |
9876 | 0 | { |
9877 | 0 | nscoord x = nsPresContext::CSSPixelsToAppUnits(aX); |
9878 | 0 | nscoord y = nsPresContext::CSSPixelsToAppUnits(aY); |
9879 | 0 | nsPoint pt(x, y); |
9880 | 0 |
|
9881 | 0 | FlushPendingNotifications(FlushType::Layout); |
9882 | 0 |
|
9883 | 0 | nsIPresShell *ps = GetShell(); |
9884 | 0 | if (!ps) { |
9885 | 0 | return nullptr; |
9886 | 0 | } |
9887 | 0 | |
9888 | 0 | nsIFrame *rootFrame = ps->GetRootFrame(); |
9889 | 0 |
|
9890 | 0 | // XUL docs, unlike HTML, have no frame tree until everything's done loading |
9891 | 0 | if (!rootFrame) { |
9892 | 0 | return nullptr; |
9893 | 0 | } |
9894 | 0 | |
9895 | 0 | nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, |
9896 | 0 | nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC); |
9897 | 0 | if (!ptFrame) { |
9898 | 0 | return nullptr; |
9899 | 0 | } |
9900 | 0 | |
9901 | 0 | // We require frame-relative coordinates for GetContentOffsetsFromPoint. |
9902 | 0 | nsPoint aOffset; |
9903 | 0 | nsCOMPtr<nsIWidget> widget = nsContentUtils::GetWidget(ps, &aOffset); |
9904 | 0 | LayoutDeviceIntPoint refPoint = |
9905 | 0 | nsContentUtils::ToWidgetPoint(CSSPoint(aX, aY), aOffset, GetPresContext()); |
9906 | 0 | nsPoint adjustedPoint = |
9907 | 0 | nsLayoutUtils::GetEventCoordinatesRelativeTo(widget, refPoint, ptFrame); |
9908 | 0 |
|
9909 | 0 | nsFrame::ContentOffsets offsets = |
9910 | 0 | ptFrame->GetContentOffsetsFromPoint(adjustedPoint); |
9911 | 0 |
|
9912 | 0 | nsCOMPtr<nsIContent> node = offsets.content; |
9913 | 0 | uint32_t offset = offsets.offset; |
9914 | 0 | nsCOMPtr<nsIContent> anonNode = node; |
9915 | 0 | bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree(); |
9916 | 0 | if (nodeIsAnonymous) { |
9917 | 0 | node = ptFrame->GetContent(); |
9918 | 0 | nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent(); |
9919 | 0 | HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromNode(nonanon); |
9920 | 0 | nsITextControlFrame* textFrame = do_QueryFrame(nonanon->GetPrimaryFrame()); |
9921 | 0 | nsNumberControlFrame* numberFrame = do_QueryFrame(nonanon->GetPrimaryFrame()); |
9922 | 0 | if (textFrame || numberFrame) { |
9923 | 0 | // If the anonymous content node has a child, then we need to make sure |
9924 | 0 | // that we get the appropriate child, as otherwise the offset may not be |
9925 | 0 | // correct when we construct a range for it. |
9926 | 0 | nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild(); |
9927 | 0 | if (firstChild) { |
9928 | 0 | anonNode = firstChild; |
9929 | 0 | } |
9930 | 0 |
|
9931 | 0 | if (textArea) { |
9932 | 0 | offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset); |
9933 | 0 | } |
9934 | 0 |
|
9935 | 0 | node = nonanon; |
9936 | 0 | } else { |
9937 | 0 | node = nullptr; |
9938 | 0 | offset = 0; |
9939 | 0 | } |
9940 | 0 | } |
9941 | 0 |
|
9942 | 0 | RefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset); |
9943 | 0 | if (nodeIsAnonymous) { |
9944 | 0 | aCaretPos->SetAnonymousContentNode(anonNode); |
9945 | 0 | } |
9946 | 0 | return aCaretPos.forget(); |
9947 | 0 | } |
9948 | | |
9949 | | bool |
9950 | | nsIDocument::IsPotentiallyScrollable(HTMLBodyElement* aBody) |
9951 | 0 | { |
9952 | 0 | // We rely on correct frame information here, so need to flush frames. |
9953 | 0 | FlushPendingNotifications(FlushType::Frames); |
9954 | 0 |
|
9955 | 0 | // An element is potentially scrollable if all of the following conditions are |
9956 | 0 | // true: |
9957 | 0 |
|
9958 | 0 | // The element has an associated CSS layout box. |
9959 | 0 | nsIFrame* bodyFrame = aBody->GetPrimaryFrame(); |
9960 | 0 | if (!bodyFrame) { |
9961 | 0 | return false; |
9962 | 0 | } |
9963 | 0 | |
9964 | 0 | // The element is not the HTML body element, or it is and the root element's |
9965 | 0 | // used value of the overflow-x or overflow-y properties is not visible. |
9966 | 0 | MOZ_ASSERT(aBody->GetParent() == aBody->OwnerDoc()->GetRootElement()); |
9967 | 0 | nsIFrame* parentFrame = aBody->GetParent()->GetPrimaryFrame(); |
9968 | 0 | if (parentFrame && |
9969 | 0 | parentFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE && |
9970 | 0 | parentFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) { |
9971 | 0 | return false; |
9972 | 0 | } |
9973 | 0 | |
9974 | 0 | // The element's used value of the overflow-x or overflow-y properties is not |
9975 | 0 | // visible. |
9976 | 0 | if (bodyFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE && |
9977 | 0 | bodyFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) { |
9978 | 0 | return false; |
9979 | 0 | } |
9980 | 0 | |
9981 | 0 | return true; |
9982 | 0 | } |
9983 | | |
9984 | | Element* |
9985 | | nsIDocument::GetScrollingElement() |
9986 | 0 | { |
9987 | 0 | // Keep this in sync with IsScrollingElement. |
9988 | 0 | if (GetCompatibilityMode() == eCompatibility_NavQuirks) { |
9989 | 0 | RefPtr<HTMLBodyElement> body = GetBodyElement(); |
9990 | 0 | if (body && !IsPotentiallyScrollable(body)) { |
9991 | 0 | return body; |
9992 | 0 | } |
9993 | 0 | |
9994 | 0 | return nullptr; |
9995 | 0 | } |
9996 | 0 | |
9997 | 0 | return GetRootElement(); |
9998 | 0 | } |
9999 | | |
10000 | | bool |
10001 | | nsIDocument::IsScrollingElement(Element* aElement) |
10002 | 0 | { |
10003 | 0 | // Keep this in sync with GetScrollingElement. |
10004 | 0 | MOZ_ASSERT(aElement); |
10005 | 0 |
|
10006 | 0 | if (GetCompatibilityMode() != eCompatibility_NavQuirks) { |
10007 | 0 | return aElement == GetRootElement(); |
10008 | 0 | } |
10009 | 0 | |
10010 | 0 | // In the common case when aElement != body, avoid refcounting. |
10011 | 0 | HTMLBodyElement* body = GetBodyElement(); |
10012 | 0 | if (aElement != body) { |
10013 | 0 | return false; |
10014 | 0 | } |
10015 | 0 | |
10016 | 0 | // Now we know body is non-null, since aElement is not null. It's the |
10017 | 0 | // scrolling element for the document if it itself is not potentially |
10018 | 0 | // scrollable. |
10019 | 0 | RefPtr<HTMLBodyElement> strongBody(body); |
10020 | 0 | return !IsPotentiallyScrollable(strongBody); |
10021 | 0 | } |
10022 | | |
10023 | | class UnblockParsingPromiseHandler final : public PromiseNativeHandler |
10024 | | { |
10025 | | public: |
10026 | | NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
10027 | | NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler) |
10028 | | |
10029 | | explicit UnblockParsingPromiseHandler(nsIDocument* aDocument, Promise* aPromise, |
10030 | | const BlockParsingOptions& aOptions) |
10031 | | : mPromise(aPromise) |
10032 | 0 | { |
10033 | 0 | nsCOMPtr<nsIParser> parser = aDocument->CreatorParserOrNull(); |
10034 | 0 | if (parser && (aOptions.mBlockScriptCreated || !parser->IsScriptCreated())) { |
10035 | 0 | parser->BlockParser(); |
10036 | 0 | mParser = do_GetWeakReference(parser); |
10037 | 0 | mDocument = aDocument; |
10038 | 0 | mDocument->BlockOnload(); |
10039 | 0 | } |
10040 | 0 | } |
10041 | | |
10042 | | void |
10043 | | ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override |
10044 | 0 | { |
10045 | 0 | MaybeUnblockParser(); |
10046 | 0 |
|
10047 | 0 | mPromise->MaybeResolve(aCx, aValue); |
10048 | 0 | } |
10049 | | |
10050 | | void |
10051 | | RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override |
10052 | 0 | { |
10053 | 0 | MaybeUnblockParser(); |
10054 | 0 |
|
10055 | 0 | mPromise->MaybeReject(aCx, aValue); |
10056 | 0 | } |
10057 | | |
10058 | | protected: |
10059 | | virtual ~UnblockParsingPromiseHandler() |
10060 | 0 | { |
10061 | 0 | // If we're being cleaned up by the cycle collector, our mDocument reference |
10062 | 0 | // may have been unlinked while our mParser weak reference is still alive. |
10063 | 0 | if (mDocument) { |
10064 | 0 | MaybeUnblockParser(); |
10065 | 0 | } |
10066 | 0 | } |
10067 | | |
10068 | | private: |
10069 | 0 | void MaybeUnblockParser() { |
10070 | 0 | nsCOMPtr<nsIParser> parser = do_QueryReferent(mParser); |
10071 | 0 | if (parser) { |
10072 | 0 | MOZ_DIAGNOSTIC_ASSERT(mDocument); |
10073 | 0 | nsCOMPtr<nsIParser> docParser = mDocument->CreatorParserOrNull(); |
10074 | 0 | if (parser == docParser) { |
10075 | 0 | parser->UnblockParser(); |
10076 | 0 | parser->ContinueInterruptedParsingAsync(); |
10077 | 0 | mDocument->UnblockOnload(false); |
10078 | 0 | } |
10079 | 0 | } |
10080 | 0 | mParser = nullptr; |
10081 | 0 | mDocument = nullptr; |
10082 | 0 | } |
10083 | | |
10084 | | nsWeakPtr mParser; |
10085 | | RefPtr<Promise> mPromise; |
10086 | | RefPtr<nsIDocument> mDocument; |
10087 | | }; |
10088 | | |
10089 | | NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler, mDocument, mPromise) |
10090 | | |
10091 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler) |
10092 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
10093 | 0 | NS_INTERFACE_MAP_END |
10094 | | |
10095 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(UnblockParsingPromiseHandler) |
10096 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(UnblockParsingPromiseHandler) |
10097 | | |
10098 | | already_AddRefed<Promise> |
10099 | | nsIDocument::BlockParsing(Promise& aPromise, const BlockParsingOptions& aOptions, ErrorResult& aRv) |
10100 | 0 | { |
10101 | 0 | RefPtr<Promise> resultPromise = Promise::Create(aPromise.GetParentObject(), aRv); |
10102 | 0 | if (aRv.Failed()) { |
10103 | 0 | return nullptr; |
10104 | 0 | } |
10105 | 0 | |
10106 | 0 | RefPtr<PromiseNativeHandler> promiseHandler = new UnblockParsingPromiseHandler(this, resultPromise, |
10107 | 0 | aOptions); |
10108 | 0 | aPromise.AppendNativeHandler(promiseHandler); |
10109 | 0 |
|
10110 | 0 | return resultPromise.forget(); |
10111 | 0 | } |
10112 | | |
10113 | | already_AddRefed<nsIURI> |
10114 | | nsIDocument::GetMozDocumentURIIfNotForErrorPages() |
10115 | 0 | { |
10116 | 0 | if (mFailedChannel) { |
10117 | 0 | nsCOMPtr<nsIURI> failedURI; |
10118 | 0 | if (NS_SUCCEEDED(mFailedChannel->GetURI(getter_AddRefs(failedURI)))) { |
10119 | 0 | return failedURI.forget(); |
10120 | 0 | } |
10121 | 0 | } |
10122 | 0 | |
10123 | 0 | nsCOMPtr<nsIURI> uri = GetDocumentURIObject(); |
10124 | 0 | if (!uri) { |
10125 | 0 | return nullptr; |
10126 | 0 | } |
10127 | 0 | |
10128 | 0 | return uri.forget(); |
10129 | 0 | } |
10130 | | |
10131 | | Promise* |
10132 | | nsIDocument::GetDocumentReadyForIdle(ErrorResult& aRv) |
10133 | 0 | { |
10134 | 0 | if (!mReadyForIdle) { |
10135 | 0 | nsIGlobalObject* global = GetScopeObject(); |
10136 | 0 | if (!global) { |
10137 | 0 | aRv.Throw(NS_ERROR_NOT_AVAILABLE); |
10138 | 0 | return nullptr; |
10139 | 0 | } |
10140 | 0 | |
10141 | 0 | mReadyForIdle = Promise::Create(global, aRv); |
10142 | 0 | if (aRv.Failed()) { |
10143 | 0 | return nullptr; |
10144 | 0 | } |
10145 | 0 | } |
10146 | 0 | |
10147 | 0 | return mReadyForIdle; |
10148 | 0 | } |
10149 | | |
10150 | | void |
10151 | | nsIDocument::MaybeResolveReadyForIdle() |
10152 | 0 | { |
10153 | 0 | IgnoredErrorResult rv; |
10154 | 0 | Promise* readyPromise = GetDocumentReadyForIdle(rv); |
10155 | 0 | if (readyPromise) { |
10156 | 0 | readyPromise->MaybeResolve(this); |
10157 | 0 | } |
10158 | 0 | } |
10159 | | |
10160 | | nsIDOMXULCommandDispatcher* |
10161 | | nsIDocument::GetCommandDispatcher() |
10162 | 0 | { |
10163 | 0 | // Only chrome documents are allowed to use command dispatcher. |
10164 | 0 | if (!nsContentUtils::IsChromeDoc(this)) { |
10165 | 0 | return nullptr; |
10166 | 0 | } |
10167 | 0 | if (!mCommandDispatcher) { |
10168 | 0 | // Create our command dispatcher and hook it up. |
10169 | 0 | mCommandDispatcher = new nsXULCommandDispatcher(this); |
10170 | 0 | } |
10171 | 0 | return mCommandDispatcher; |
10172 | 0 | } |
10173 | | |
10174 | | static JSObject* |
10175 | | GetScopeObjectOfNode(nsINode* node) |
10176 | 0 | { |
10177 | 0 | MOZ_ASSERT(node, "Must not be called with null."); |
10178 | 0 |
|
10179 | 0 | // Window root occasionally keeps alive a node of a document whose |
10180 | 0 | // window is already dead. If in this brief period someone calls |
10181 | 0 | // GetPopupNode and we return that node, we can end up creating a |
10182 | 0 | // reflector for the node in the wrong global (the current global, |
10183 | 0 | // not the document global, because we won't know what the document |
10184 | 0 | // global is). Returning an orphan node like that to JS would be a |
10185 | 0 | // bug anyway, so to avoid this, let's do the same check as fetching |
10186 | 0 | // GetParentObjet() on the document does to determine the scope and |
10187 | 0 | // if it returns null let's just return null in XULDocument::GetPopupNode. |
10188 | 0 | nsIDocument* doc = node->OwnerDoc(); |
10189 | 0 | MOZ_ASSERT(doc, "This should never happen."); |
10190 | 0 |
|
10191 | 0 | nsIGlobalObject* global = doc->GetScopeObject(); |
10192 | 0 | return global ? global->GetGlobalJSObject() : nullptr; |
10193 | 0 | } |
10194 | | |
10195 | | |
10196 | | already_AddRefed<nsPIWindowRoot> |
10197 | | nsIDocument::GetWindowRoot() |
10198 | 0 | { |
10199 | 0 | if (!mDocumentContainer) { |
10200 | 0 | return nullptr; |
10201 | 0 | } |
10202 | 0 | // XXX It's unclear why this can't just use GetWindow(). |
10203 | 0 | nsCOMPtr<nsPIDOMWindowOuter> piWin = mDocumentContainer->GetWindow(); |
10204 | 0 | return piWin ? piWin->GetTopWindowRoot() : nullptr; |
10205 | 0 | } |
10206 | | |
10207 | | already_AddRefed<nsINode> |
10208 | | nsIDocument::GetPopupNode() |
10209 | 0 | { |
10210 | 0 | nsCOMPtr<nsINode> node; |
10211 | 0 | nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot(); |
10212 | 0 | if (rootWin) { |
10213 | 0 | node = rootWin->GetPopupNode(); // addref happens here |
10214 | 0 | } |
10215 | 0 |
|
10216 | 0 | if (!node) { |
10217 | 0 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
10218 | 0 | if (pm) { |
10219 | 0 | node = pm->GetLastTriggerPopupNode(this); |
10220 | 0 | } |
10221 | 0 | } |
10222 | 0 |
|
10223 | 0 | if (node && GetScopeObjectOfNode(node)) { |
10224 | 0 | return node.forget(); |
10225 | 0 | } |
10226 | 0 | |
10227 | 0 | return nullptr; |
10228 | 0 | } |
10229 | | |
10230 | | void |
10231 | | nsIDocument::SetPopupNode(nsINode* aNode) |
10232 | 0 | { |
10233 | 0 | nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot(); |
10234 | 0 | if (rootWin) { |
10235 | 0 | rootWin->SetPopupNode(aNode); |
10236 | 0 | } |
10237 | 0 | } |
10238 | | |
10239 | | // Returns the rangeOffset element from the XUL Popup Manager. This is for |
10240 | | // chrome callers only. |
10241 | | nsINode* |
10242 | | nsIDocument::GetPopupRangeParent(ErrorResult& aRv) |
10243 | 0 | { |
10244 | 0 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
10245 | 0 | if (!pm) { |
10246 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
10247 | 0 | return nullptr; |
10248 | 0 | } |
10249 | 0 | |
10250 | 0 | return pm->GetMouseLocationParent(); |
10251 | 0 | } |
10252 | | |
10253 | | // Returns the rangeOffset element from the XUL Popup Manager. |
10254 | | int32_t |
10255 | | nsIDocument::GetPopupRangeOffset(ErrorResult& aRv) |
10256 | 0 | { |
10257 | 0 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
10258 | 0 | if (!pm) { |
10259 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
10260 | 0 | return 0; |
10261 | 0 | } |
10262 | 0 | |
10263 | 0 | return pm->MouseLocationOffset(); |
10264 | 0 | } |
10265 | | |
10266 | | already_AddRefed<nsINode> |
10267 | | nsIDocument::GetTooltipNode() |
10268 | 0 | { |
10269 | 0 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
10270 | 0 | if (pm) { |
10271 | 0 | nsCOMPtr<nsINode> node = pm->GetLastTriggerTooltipNode(this); |
10272 | 0 | if (node) { |
10273 | 0 | return node.forget(); |
10274 | 0 | } |
10275 | 0 | } |
10276 | 0 | |
10277 | 0 | return nullptr; |
10278 | 0 | } |
10279 | | |
10280 | | nsIHTMLCollection* |
10281 | | nsIDocument::Children() |
10282 | 0 | { |
10283 | 0 | if (!mChildrenCollection) { |
10284 | 0 | mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard, |
10285 | 0 | nsGkAtoms::_asterisk, |
10286 | 0 | nsGkAtoms::_asterisk, |
10287 | 0 | false); |
10288 | 0 | } |
10289 | 0 |
|
10290 | 0 | return mChildrenCollection; |
10291 | 0 | } |
10292 | | |
10293 | | uint32_t |
10294 | | nsIDocument::ChildElementCount() |
10295 | 0 | { |
10296 | 0 | return Children()->Length(); |
10297 | 0 | } |
10298 | | |
10299 | | namespace mozilla { |
10300 | | |
10301 | | // Singleton class to manage the list of fullscreen documents which are the |
10302 | | // root of a branch which contains fullscreen documents. We maintain this list |
10303 | | // so that we can easily exit all windows from fullscreen when the user |
10304 | | // presses the escape key. |
10305 | | class FullscreenRoots { |
10306 | | public: |
10307 | | // Adds the root of given document to the manager. Calling this method |
10308 | | // with a document whose root is already contained has no effect. |
10309 | | static void Add(nsIDocument* aDoc); |
10310 | | |
10311 | | // Iterates over every root in the root list, and calls aFunction, passing |
10312 | | // each root once to aFunction. It is safe to call Add() and Remove() while |
10313 | | // iterating over the list (i.e. in aFunction). Documents that are removed |
10314 | | // from the manager during traversal are not traversed, and documents that |
10315 | | // are added to the manager during traversal are also not traversed. |
10316 | | static void ForEach(void(*aFunction)(nsIDocument* aDoc)); |
10317 | | |
10318 | | // Removes the root of a specific document from the manager. |
10319 | | static void Remove(nsIDocument* aDoc); |
10320 | | |
10321 | | // Returns true if all roots added to the list have been removed. |
10322 | | static bool IsEmpty(); |
10323 | | |
10324 | | private: |
10325 | | |
10326 | 0 | FullscreenRoots() { |
10327 | 0 | MOZ_COUNT_CTOR(FullscreenRoots); |
10328 | 0 | } |
10329 | 0 | ~FullscreenRoots() { |
10330 | 0 | MOZ_COUNT_DTOR(FullscreenRoots); |
10331 | 0 | } |
10332 | | |
10333 | | enum { |
10334 | | NotFound = uint32_t(-1) |
10335 | | }; |
10336 | | // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound. |
10337 | | static uint32_t Find(nsIDocument* aRoot); |
10338 | | |
10339 | | // Returns true if aRoot is in the list of fullscreen roots. |
10340 | | static bool Contains(nsIDocument* aRoot); |
10341 | | |
10342 | | // Singleton instance of the FullscreenRoots. This is instantiated when a |
10343 | | // root is added, and it is deleted when the last root is removed. |
10344 | | static FullscreenRoots* sInstance; |
10345 | | |
10346 | | // List of weak pointers to roots. |
10347 | | nsTArray<nsWeakPtr> mRoots; |
10348 | | }; |
10349 | | |
10350 | | FullscreenRoots* FullscreenRoots::sInstance = nullptr; |
10351 | | |
10352 | | /* static */ |
10353 | | void |
10354 | | FullscreenRoots::ForEach(void(*aFunction)(nsIDocument* aDoc)) |
10355 | 0 | { |
10356 | 0 | if (!sInstance) { |
10357 | 0 | return; |
10358 | 0 | } |
10359 | 0 | // Create a copy of the roots array, and iterate over the copy. This is so |
10360 | 0 | // that if an element is removed from mRoots we don't mess up our iteration. |
10361 | 0 | nsTArray<nsWeakPtr> roots(sInstance->mRoots); |
10362 | 0 | // Call aFunction on all entries. |
10363 | 0 | for (uint32_t i = 0; i < roots.Length(); i++) { |
10364 | 0 | nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]); |
10365 | 0 | // Check that the root isn't in the manager. This is so that new additions |
10366 | 0 | // while we were running don't get traversed. |
10367 | 0 | if (root && FullscreenRoots::Contains(root)) { |
10368 | 0 | aFunction(root); |
10369 | 0 | } |
10370 | 0 | } |
10371 | 0 | } |
10372 | | |
10373 | | /* static */ |
10374 | | bool |
10375 | | FullscreenRoots::Contains(nsIDocument* aRoot) |
10376 | 0 | { |
10377 | 0 | return FullscreenRoots::Find(aRoot) != NotFound; |
10378 | 0 | } |
10379 | | |
10380 | | /* static */ |
10381 | | void |
10382 | | FullscreenRoots::Add(nsIDocument* aDoc) |
10383 | 0 | { |
10384 | 0 | nsCOMPtr<nsIDocument> root = nsContentUtils::GetRootDocument(aDoc); |
10385 | 0 | if (!FullscreenRoots::Contains(root)) { |
10386 | 0 | if (!sInstance) { |
10387 | 0 | sInstance = new FullscreenRoots(); |
10388 | 0 | } |
10389 | 0 | sInstance->mRoots.AppendElement(do_GetWeakReference(root)); |
10390 | 0 | } |
10391 | 0 | } |
10392 | | |
10393 | | /* static */ |
10394 | | uint32_t |
10395 | | FullscreenRoots::Find(nsIDocument* aRoot) |
10396 | 0 | { |
10397 | 0 | if (!sInstance) { |
10398 | 0 | return NotFound; |
10399 | 0 | } |
10400 | 0 | nsTArray<nsWeakPtr>& roots = sInstance->mRoots; |
10401 | 0 | for (uint32_t i = 0; i < roots.Length(); i++) { |
10402 | 0 | nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i])); |
10403 | 0 | if (otherRoot == aRoot) { |
10404 | 0 | return i; |
10405 | 0 | } |
10406 | 0 | } |
10407 | 0 | return NotFound; |
10408 | 0 | } |
10409 | | |
10410 | | /* static */ |
10411 | | void |
10412 | | FullscreenRoots::Remove(nsIDocument* aDoc) |
10413 | 0 | { |
10414 | 0 | nsCOMPtr<nsIDocument> root = nsContentUtils::GetRootDocument(aDoc); |
10415 | 0 | uint32_t index = Find(root); |
10416 | 0 | NS_ASSERTION(index != NotFound, |
10417 | 0 | "Should only try to remove roots which are still added!"); |
10418 | 0 | if (index == NotFound || !sInstance) { |
10419 | 0 | return; |
10420 | 0 | } |
10421 | 0 | sInstance->mRoots.RemoveElementAt(index); |
10422 | 0 | if (sInstance->mRoots.IsEmpty()) { |
10423 | 0 | delete sInstance; |
10424 | 0 | sInstance = nullptr; |
10425 | 0 | } |
10426 | 0 | } |
10427 | | |
10428 | | /* static */ |
10429 | | bool |
10430 | | FullscreenRoots::IsEmpty() |
10431 | 0 | { |
10432 | 0 | return !sInstance; |
10433 | 0 | } |
10434 | | |
10435 | | // Any fullscreen change waiting for the widget to finish transition |
10436 | | // is queued here. This is declared static instead of a member of |
10437 | | // nsDocument because in the majority of time, there would be at most |
10438 | | // one document requesting or exiting fullscreen. We shouldn't waste |
10439 | | // the space to hold for it in every document. |
10440 | | class PendingFullscreenChangeList |
10441 | | { |
10442 | | public: |
10443 | | PendingFullscreenChangeList() = delete; |
10444 | | |
10445 | | template<typename T> |
10446 | | static void Add(UniquePtr<T> aChange) |
10447 | 0 | { |
10448 | 0 | sList.insertBack(aChange.release()); |
10449 | 0 | } Unexecuted instantiation: void mozilla::PendingFullscreenChangeList::Add<mozilla::FullscreenExit>(mozilla::UniquePtr<mozilla::FullscreenExit, mozilla::DefaultDelete<mozilla::FullscreenExit> >) Unexecuted instantiation: void mozilla::PendingFullscreenChangeList::Add<mozilla::FullscreenRequest>(mozilla::UniquePtr<mozilla::FullscreenRequest, mozilla::DefaultDelete<mozilla::FullscreenRequest> >) |
10450 | | |
10451 | | static const FullscreenChange* GetLast() |
10452 | 0 | { |
10453 | 0 | return sList.getLast(); |
10454 | 0 | } |
10455 | | |
10456 | | enum IteratorOption |
10457 | | { |
10458 | | // When we are committing fullscreen changes or preparing for |
10459 | | // that, we generally want to iterate all requests in the same |
10460 | | // window with eDocumentsWithSameRoot option. |
10461 | | eDocumentsWithSameRoot, |
10462 | | // If we are removing a document from the tree, we would only |
10463 | | // want to remove the requests from the given document and its |
10464 | | // descendants. For that case, use eInclusiveDescendants. |
10465 | | eInclusiveDescendants |
10466 | | }; |
10467 | | |
10468 | | template<typename T> |
10469 | | class Iterator |
10470 | | { |
10471 | | public: |
10472 | | explicit Iterator(nsIDocument* aDoc, IteratorOption aOption) |
10473 | | : mCurrent(PendingFullscreenChangeList::sList.getFirst()) |
10474 | | , mRootShellForIteration(aDoc->GetDocShell()) |
10475 | 0 | { |
10476 | 0 | if (mCurrent) { |
10477 | 0 | if (mRootShellForIteration && aOption == eDocumentsWithSameRoot) { |
10478 | 0 | mRootShellForIteration-> |
10479 | 0 | GetRootTreeItem(getter_AddRefs(mRootShellForIteration)); |
10480 | 0 | } |
10481 | 0 | SkipToNextMatch(); |
10482 | 0 | } |
10483 | 0 | } Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenExit>::Iterator(nsIDocument*, mozilla::PendingFullscreenChangeList::IteratorOption) Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenRequest>::Iterator(nsIDocument*, mozilla::PendingFullscreenChangeList::IteratorOption) |
10484 | | |
10485 | | UniquePtr<T> TakeAndNext() |
10486 | 0 | { |
10487 | 0 | auto thisChange = TakeAndNextInternal(); |
10488 | 0 | SkipToNextMatch(); |
10489 | 0 | return thisChange; |
10490 | 0 | } Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenExit>::TakeAndNext() Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenRequest>::TakeAndNext() |
10491 | 0 | bool AtEnd() const { return mCurrent == nullptr; } Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenExit>::AtEnd() const Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenRequest>::AtEnd() const |
10492 | | |
10493 | | private: |
10494 | | UniquePtr<T> TakeAndNextInternal() |
10495 | 0 | { |
10496 | 0 | FullscreenChange* thisChange = mCurrent; |
10497 | 0 | MOZ_ASSERT(thisChange->Type() == T::kType); |
10498 | 0 | mCurrent = mCurrent->removeAndGetNext(); |
10499 | 0 | return WrapUnique(static_cast<T*>(thisChange)); |
10500 | 0 | } Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenExit>::TakeAndNextInternal() Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenRequest>::TakeAndNextInternal() |
10501 | | void SkipToNextMatch() |
10502 | 0 | { |
10503 | 0 | while (mCurrent) { |
10504 | 0 | if (mCurrent->Type() == T::kType) { |
10505 | 0 | nsCOMPtr<nsIDocShellTreeItem> |
10506 | 0 | docShell = mCurrent->Document()->GetDocShell(); |
10507 | 0 | if (!docShell) { |
10508 | 0 | // Always automatically drop fullscreen changes which are |
10509 | 0 | // from a document detached from the doc shell. |
10510 | 0 | UniquePtr<T> change = TakeAndNextInternal(); |
10511 | 0 | change->MayRejectPromise(); |
10512 | 0 | continue; |
10513 | 0 | } |
10514 | 0 | while (docShell && docShell != mRootShellForIteration) { |
10515 | 0 | docShell->GetParent(getter_AddRefs(docShell)); |
10516 | 0 | } |
10517 | 0 | if (docShell) { |
10518 | 0 | break; |
10519 | 0 | } |
10520 | 0 | } |
10521 | 0 | // The current one either don't have matched type, or isn't |
10522 | 0 | // inside the given subtree, so skip this item. |
10523 | 0 | mCurrent = mCurrent->getNext(); |
10524 | 0 | } |
10525 | 0 | } Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenExit>::SkipToNextMatch() Unexecuted instantiation: mozilla::PendingFullscreenChangeList::Iterator<mozilla::FullscreenRequest>::SkipToNextMatch() |
10526 | | |
10527 | | FullscreenChange* mCurrent; |
10528 | | nsCOMPtr<nsIDocShellTreeItem> mRootShellForIteration; |
10529 | | }; |
10530 | | |
10531 | | private: |
10532 | | static LinkedList<FullscreenChange> sList; |
10533 | | }; |
10534 | | |
10535 | | /* static */ LinkedList<FullscreenChange> PendingFullscreenChangeList::sList; |
10536 | | |
10537 | | } // end namespace mozilla. |
10538 | | |
10539 | | nsIDocument* |
10540 | | nsIDocument::GetFullscreenRoot() |
10541 | 0 | { |
10542 | 0 | nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot); |
10543 | 0 | return root; |
10544 | 0 | } |
10545 | | |
10546 | | void |
10547 | | nsIDocument::SetFullscreenRoot(nsIDocument* aRoot) |
10548 | 0 | { |
10549 | 0 | mFullscreenRoot = do_GetWeakReference(aRoot); |
10550 | 0 | } |
10551 | | |
10552 | | already_AddRefed<Promise> |
10553 | | nsIDocument::ExitFullscreen(ErrorResult& aRv) |
10554 | 0 | { |
10555 | 0 | UniquePtr<FullscreenExit> exit = FullscreenExit::Create(this, aRv); |
10556 | 0 | RefPtr<Promise> promise = exit->GetPromise(); |
10557 | 0 | RestorePreviousFullscreenState(std::move(exit)); |
10558 | 0 | return promise.forget(); |
10559 | 0 | } |
10560 | | |
10561 | | static void |
10562 | | AskWindowToExitFullscreen(nsIDocument* aDoc) |
10563 | 0 | { |
10564 | 0 | if (XRE_GetProcessType() == GeckoProcessType_Content) { |
10565 | 0 | nsContentUtils::DispatchEventOnlyToChrome( |
10566 | 0 | aDoc, ToSupports(aDoc), NS_LITERAL_STRING("MozDOMFullscreen:Exit"), |
10567 | 0 | CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr); |
10568 | 0 | } else { |
10569 | 0 | if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) { |
10570 | 0 | win->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, false); |
10571 | 0 | } |
10572 | 0 | } |
10573 | 0 | } |
10574 | | |
10575 | | class nsCallExitFullscreen : public Runnable |
10576 | | { |
10577 | | public: |
10578 | | explicit nsCallExitFullscreen(nsIDocument* aDoc) |
10579 | | : mozilla::Runnable("nsCallExitFullscreen") |
10580 | | , mDoc(aDoc) |
10581 | 0 | { |
10582 | 0 | } |
10583 | | |
10584 | | NS_IMETHOD Run() final |
10585 | 0 | { |
10586 | 0 | if (!mDoc) { |
10587 | 0 | FullscreenRoots::ForEach(&AskWindowToExitFullscreen); |
10588 | 0 | } else { |
10589 | 0 | AskWindowToExitFullscreen(mDoc); |
10590 | 0 | } |
10591 | 0 | return NS_OK; |
10592 | 0 | } |
10593 | | |
10594 | | private: |
10595 | | nsCOMPtr<nsIDocument> mDoc; |
10596 | | }; |
10597 | | |
10598 | | /* static */ void |
10599 | | nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc) |
10600 | 0 | { |
10601 | 0 | MOZ_RELEASE_ASSERT(NS_IsMainThread()); |
10602 | 0 | nsCOMPtr<nsIRunnable> exit = new nsCallExitFullscreen(aDoc); |
10603 | 0 | if (aDoc) { |
10604 | 0 | aDoc->Dispatch(TaskCategory::Other, exit.forget()); |
10605 | 0 | } else { |
10606 | 0 | NS_DispatchToCurrentThread(exit.forget()); |
10607 | 0 | } |
10608 | 0 | } |
10609 | | |
10610 | | static bool |
10611 | | CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData) |
10612 | 0 | { |
10613 | 0 | if (aDoc->FullscreenStackTop()) { |
10614 | 0 | uint32_t* count = static_cast<uint32_t*>(aData); |
10615 | 0 | (*count)++; |
10616 | 0 | } |
10617 | 0 | return true; |
10618 | 0 | } |
10619 | | |
10620 | | static uint32_t |
10621 | | CountFullscreenSubDocuments(nsIDocument* aDoc) |
10622 | 0 | { |
10623 | 0 | uint32_t count = 0; |
10624 | 0 | aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count); |
10625 | 0 | return count; |
10626 | 0 | } |
10627 | | |
10628 | | bool |
10629 | | nsIDocument::IsFullscreenLeaf() |
10630 | 0 | { |
10631 | 0 | // A fullscreen leaf document is fullscreen, and has no fullscreen |
10632 | 0 | // subdocuments. |
10633 | 0 | if (!FullscreenStackTop()) { |
10634 | 0 | return false; |
10635 | 0 | } |
10636 | 0 | return CountFullscreenSubDocuments(this) == 0; |
10637 | 0 | } |
10638 | | |
10639 | | bool |
10640 | | GetFullscreenLeaf(nsIDocument* aDoc, void* aData) |
10641 | 0 | { |
10642 | 0 | if (aDoc->IsFullscreenLeaf()) { |
10643 | 0 | nsIDocument** result = static_cast<nsIDocument**>(aData); |
10644 | 0 | *result = aDoc; |
10645 | 0 | return false; |
10646 | 0 | } |
10647 | 0 | if (aDoc->FullscreenStackTop()) { |
10648 | 0 | aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData); |
10649 | 0 | } |
10650 | 0 | return true; |
10651 | 0 | } |
10652 | | |
10653 | | static nsIDocument* |
10654 | | GetFullscreenLeaf(nsIDocument* aDoc) |
10655 | 0 | { |
10656 | 0 | nsIDocument* leaf = nullptr; |
10657 | 0 | GetFullscreenLeaf(aDoc, &leaf); |
10658 | 0 | if (leaf) { |
10659 | 0 | return leaf; |
10660 | 0 | } |
10661 | 0 | // Otherwise we could be either in a non-fullscreen doc tree, or we're |
10662 | 0 | // below the fullscreen doc. Start the search from the root. |
10663 | 0 | nsIDocument* root = nsContentUtils::GetRootDocument(aDoc); |
10664 | 0 | // Check that the root is actually fullscreen so we don't waste time walking |
10665 | 0 | // around its descendants. |
10666 | 0 | if (!root->FullscreenStackTop()) { |
10667 | 0 | return nullptr; |
10668 | 0 | } |
10669 | 0 | GetFullscreenLeaf(root, &leaf); |
10670 | 0 | return leaf; |
10671 | 0 | } |
10672 | | |
10673 | | static bool |
10674 | | ResetFullscreen(nsIDocument* aDocument, void* aData) |
10675 | 0 | { |
10676 | 0 | if (Element* fsElement = aDocument->FullscreenStackTop()) { |
10677 | 0 | NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1, |
10678 | 0 | "Should have at most 1 fullscreen subdocument."); |
10679 | 0 | aDocument->CleanupFullscreenState(); |
10680 | 0 | NS_ASSERTION(!aDocument->FullscreenStackTop(), |
10681 | 0 | "Should reset fullscreen"); |
10682 | 0 | DispatchFullscreenChange(aDocument, fsElement); |
10683 | 0 | aDocument->EnumerateSubDocuments(ResetFullscreen, nullptr); |
10684 | 0 | } |
10685 | 0 | return true; |
10686 | 0 | } |
10687 | | |
10688 | | // Since nsIDocument::ExitFullscreenInDocTree() could be called from |
10689 | | // Element::UnbindFromTree() where it is not safe to synchronously run |
10690 | | // script. This runnable is the script part of that function. |
10691 | | class ExitFullscreenScriptRunnable : public Runnable |
10692 | | { |
10693 | | public: |
10694 | | explicit ExitFullscreenScriptRunnable(nsIDocument* aRoot, nsIDocument* aLeaf) |
10695 | | : mozilla::Runnable("ExitFullscreenScriptRunnable") |
10696 | | , mRoot(aRoot) |
10697 | | , mLeaf(aLeaf) |
10698 | 0 | { |
10699 | 0 | } |
10700 | | |
10701 | | NS_IMETHOD Run() override |
10702 | 0 | { |
10703 | 0 | // Dispatch MozDOMFullscreen:Exited to the original fullscreen leaf |
10704 | 0 | // document since we want this event to follow the same path that |
10705 | 0 | // MozDOMFullscreen:Entered was dispatched. |
10706 | 0 | nsContentUtils::DispatchEventOnlyToChrome( |
10707 | 0 | mLeaf, ToSupports(mLeaf), |
10708 | 0 | NS_LITERAL_STRING("MozDOMFullscreen:Exited"), |
10709 | 0 | CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr); |
10710 | 0 | // Ensure the window exits fullscreen. |
10711 | 0 | if (nsPIDOMWindowOuter* win = mRoot->GetWindow()) { |
10712 | 0 | win->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen, false); |
10713 | 0 | } |
10714 | 0 | return NS_OK; |
10715 | 0 | } |
10716 | | |
10717 | | private: |
10718 | | nsCOMPtr<nsIDocument> mRoot; |
10719 | | nsCOMPtr<nsIDocument> mLeaf; |
10720 | | }; |
10721 | | |
10722 | | /* static */ void |
10723 | | nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc) |
10724 | 0 | { |
10725 | 0 | MOZ_ASSERT(aMaybeNotARootDoc); |
10726 | 0 |
|
10727 | 0 | // Unlock the pointer |
10728 | 0 | UnlockPointer(); |
10729 | 0 |
|
10730 | 0 | // Resolve all promises which waiting for exit fullscreen. |
10731 | 0 | PendingFullscreenChangeList::Iterator<FullscreenExit> iter( |
10732 | 0 | aMaybeNotARootDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot); |
10733 | 0 | while (!iter.AtEnd()) { |
10734 | 0 | UniquePtr<FullscreenExit> exit = iter.TakeAndNext(); |
10735 | 0 | exit->MayResolvePromise(); |
10736 | 0 | } |
10737 | 0 |
|
10738 | 0 | nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot(); |
10739 | 0 | if (!root || !root->FullscreenStackTop()) { |
10740 | 0 | // If a document was detached before exiting from fullscreen, it is |
10741 | 0 | // possible that the root had left fullscreen state. In this case, |
10742 | 0 | // we would not get anything from the ResetFullscreen() call. Root's |
10743 | 0 | // not being a fullscreen doc also means the widget should have |
10744 | 0 | // exited fullscreen state. It means even if we do not return here, |
10745 | 0 | // we would actually do nothing below except crashing ourselves via |
10746 | 0 | // dispatching the "MozDOMFullscreen:Exited" event to an nonexistent |
10747 | 0 | // document. |
10748 | 0 | return; |
10749 | 0 | } |
10750 | 0 | |
10751 | 0 | // Record the fullscreen leaf document for MozDOMFullscreen:Exited. |
10752 | 0 | // See ExitFullscreenScriptRunnable::Run for details. We have to |
10753 | 0 | // record it here because we don't have such information after we |
10754 | 0 | // reset the fullscreen state below. |
10755 | 0 | nsIDocument* fullscreenLeaf = GetFullscreenLeaf(root); |
10756 | 0 |
|
10757 | 0 | // Walk the tree of fullscreen documents, and reset their fullscreen state. |
10758 | 0 | ResetFullscreen(root, nullptr); |
10759 | 0 |
|
10760 | 0 | NS_ASSERTION(!root->FullscreenStackTop(), |
10761 | 0 | "Fullscreen root should no longer be a fullscreen doc..."); |
10762 | 0 |
|
10763 | 0 | // Move the top-level window out of fullscreen mode. |
10764 | 0 | FullscreenRoots::Remove(root); |
10765 | 0 |
|
10766 | 0 | nsContentUtils::AddScriptRunner( |
10767 | 0 | new ExitFullscreenScriptRunnable(root, fullscreenLeaf)); |
10768 | 0 | } |
10769 | | |
10770 | | static void |
10771 | | DispatchFullscreenNewOriginEvent(nsIDocument* aDoc) |
10772 | 0 | { |
10773 | 0 | RefPtr<AsyncEventDispatcher> asyncDispatcher = |
10774 | 0 | new AsyncEventDispatcher( |
10775 | 0 | aDoc, NS_LITERAL_STRING("MozDOMFullscreen:NewOrigin"), |
10776 | 0 | CanBubble::eYes, ChromeOnlyDispatch::eYes); |
10777 | 0 | asyncDispatcher->PostDOMEvent(); |
10778 | 0 | } |
10779 | | |
10780 | | void |
10781 | | nsIDocument::RestorePreviousFullscreenState(UniquePtr<FullscreenExit> aExit) |
10782 | 0 | { |
10783 | 0 | NS_ASSERTION(!FullscreenStackTop() || !FullscreenRoots::IsEmpty(), |
10784 | 0 | "Should have at least 1 fullscreen root when fullscreen!"); |
10785 | 0 |
|
10786 | 0 | if (!FullscreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) { |
10787 | 0 | aExit->MayRejectPromise(); |
10788 | 0 | return; |
10789 | 0 | } |
10790 | 0 | |
10791 | 0 | nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this); |
10792 | 0 | AutoTArray<Element*, 8> exitElements; |
10793 | 0 |
|
10794 | 0 | nsIDocument* doc = fullScreenDoc; |
10795 | 0 | // Collect all subdocuments. |
10796 | 0 | for (; doc != this; doc = doc->GetParentDocument()) { |
10797 | 0 | Element* fsElement = doc->FullscreenStackTop(); |
10798 | 0 | MOZ_ASSERT(fsElement, "Parent document of " |
10799 | 0 | "a fullscreen document without fullscreen element?"); |
10800 | 0 | exitElements.AppendElement(fsElement); |
10801 | 0 | } |
10802 | 0 | MOZ_ASSERT(doc == this, "Must have reached this doc"); |
10803 | 0 | // Collect all ancestor documents which we are going to change. |
10804 | 0 | for (; doc; doc = doc->GetParentDocument()) { |
10805 | 0 | MOZ_ASSERT(!doc->mFullscreenStack.IsEmpty(), |
10806 | 0 | "Ancestor of fullscreen document must also be in fullscreen"); |
10807 | 0 | Element* fsElement = doc->FullscreenStackTop(); |
10808 | 0 | if (doc != this) { |
10809 | 0 | if (auto* iframe = HTMLIFrameElement::FromNode(fsElement)) { |
10810 | 0 | if (iframe->FullscreenFlag()) { |
10811 | 0 | // If this is an iframe, and it explicitly requested |
10812 | 0 | // fullscreen, don't rollback it automatically. |
10813 | 0 | break; |
10814 | 0 | } |
10815 | 0 | } |
10816 | 0 | } |
10817 | 0 | exitElements.AppendElement(fsElement); |
10818 | 0 | if (doc->mFullscreenStack.Length() > 1) { |
10819 | 0 | break; |
10820 | 0 | } |
10821 | 0 | } |
10822 | 0 |
|
10823 | 0 | nsIDocument* lastDoc = exitElements.LastElement()->OwnerDoc(); |
10824 | 0 | if (!lastDoc->GetParentDocument() && |
10825 | 0 | lastDoc->mFullscreenStack.Length() == 1) { |
10826 | 0 | // If we are fully exiting fullscreen, don't touch anything here, |
10827 | 0 | // just wait for the window to get out from fullscreen first. |
10828 | 0 | PendingFullscreenChangeList::Add(std::move(aExit)); |
10829 | 0 | AskWindowToExitFullscreen(this); |
10830 | 0 | return; |
10831 | 0 | } |
10832 | 0 | |
10833 | 0 | // If fullscreen mode is updated the pointer should be unlocked |
10834 | 0 | UnlockPointer(); |
10835 | 0 | // All documents listed in the array except the last one are going to |
10836 | 0 | // completely exit from the fullscreen state. |
10837 | 0 | for (auto i : IntegerRange(exitElements.Length() - 1)) { |
10838 | 0 | exitElements[i]->OwnerDoc()->CleanupFullscreenState(); |
10839 | 0 | } |
10840 | 0 | // The last document will either rollback one fullscreen element, or |
10841 | 0 | // completely exit from the fullscreen state as well. |
10842 | 0 | nsIDocument* newFullscreenDoc; |
10843 | 0 | if (lastDoc->mFullscreenStack.Length() > 1) { |
10844 | 0 | lastDoc->FullscreenStackPop(); |
10845 | 0 | newFullscreenDoc = lastDoc; |
10846 | 0 | } else { |
10847 | 0 | lastDoc->CleanupFullscreenState(); |
10848 | 0 | newFullscreenDoc = lastDoc->GetParentDocument(); |
10849 | 0 | } |
10850 | 0 | // Dispatch the fullscreenchange event to all document listed. Note |
10851 | 0 | // that the loop order is reversed so that events are dispatched in |
10852 | 0 | // the tree order as indicated in the spec. |
10853 | 0 | for (Element* e : Reversed(exitElements)) { |
10854 | 0 | DispatchFullscreenChange(e->OwnerDoc(), e); |
10855 | 0 | } |
10856 | 0 | aExit->MayResolvePromise(); |
10857 | 0 |
|
10858 | 0 | MOZ_ASSERT(newFullscreenDoc, "If we were going to exit from fullscreen on " |
10859 | 0 | "all documents in this doctree, we should've asked the window to " |
10860 | 0 | "exit first instead of reaching here."); |
10861 | 0 | if (fullScreenDoc != newFullscreenDoc && |
10862 | 0 | !nsContentUtils::HaveEqualPrincipals(fullScreenDoc, newFullscreenDoc)) { |
10863 | 0 | // We've popped so enough off the stack that we've rolled back to |
10864 | 0 | // a fullscreen element in a parent document. If this document is |
10865 | 0 | // cross origin, dispatch an event to chrome so it knows to show |
10866 | 0 | // the warning UI. |
10867 | 0 | DispatchFullscreenNewOriginEvent(newFullscreenDoc); |
10868 | 0 | } |
10869 | 0 | } |
10870 | | |
10871 | | class nsCallRequestFullscreen : public Runnable |
10872 | | { |
10873 | | public: |
10874 | | explicit nsCallRequestFullscreen(UniquePtr<FullscreenRequest> aRequest) |
10875 | | : mozilla::Runnable("nsCallRequestFullscreen") |
10876 | | , mRequest(std::move(aRequest)) |
10877 | 0 | { |
10878 | 0 | } |
10879 | | |
10880 | | NS_IMETHOD Run() override |
10881 | 0 | { |
10882 | 0 | nsIDocument* doc = mRequest->Document(); |
10883 | 0 | doc->RequestFullscreen(std::move(mRequest)); |
10884 | 0 | return NS_OK; |
10885 | 0 | } |
10886 | | |
10887 | | UniquePtr<FullscreenRequest> mRequest; |
10888 | | }; |
10889 | | |
10890 | | void |
10891 | | nsIDocument::AsyncRequestFullscreen(UniquePtr<FullscreenRequest> aRequest) |
10892 | 0 | { |
10893 | 0 | // Request fullscreen asynchronously. |
10894 | 0 | MOZ_RELEASE_ASSERT(NS_IsMainThread()); |
10895 | 0 | nsCOMPtr<nsIRunnable> event = new nsCallRequestFullscreen(std::move(aRequest)); |
10896 | 0 | Dispatch(TaskCategory::Other, event.forget()); |
10897 | 0 | } |
10898 | | |
10899 | | static void |
10900 | | UpdateViewportScrollbarOverrideForFullscreen(nsIDocument* aDoc) |
10901 | 0 | { |
10902 | 0 | if (nsPresContext* presContext = aDoc->GetPresContext()) { |
10903 | 0 | presContext->UpdateViewportScrollStylesOverride(); |
10904 | 0 | } |
10905 | 0 | } |
10906 | | |
10907 | | static void |
10908 | | ClearFullscreenStateOnElement(Element* aElement) |
10909 | 0 | { |
10910 | 0 | // Remove styles from existing top element. |
10911 | 0 | EventStateManager::SetFullscreenState(aElement, false); |
10912 | 0 | // Reset iframe fullscreen flag. |
10913 | 0 | if (aElement->IsHTMLElement(nsGkAtoms::iframe)) { |
10914 | 0 | static_cast<HTMLIFrameElement*>(aElement)->SetFullscreenFlag(false); |
10915 | 0 | } |
10916 | 0 | } |
10917 | | |
10918 | | void |
10919 | | nsIDocument::CleanupFullscreenState() |
10920 | 0 | { |
10921 | 0 | // Iterate the fullscreen stack and clear the fullscreen states. |
10922 | 0 | // Since we also need to clear the fullscreen-ancestor state, and |
10923 | 0 | // currently fullscreen elements can only be placed in hierarchy |
10924 | 0 | // order in the stack, reversely iterating the stack could be more |
10925 | 0 | // efficient. NOTE that fullscreen-ancestor state would be removed |
10926 | 0 | // in bug 1199529, and the elements may not in hierarchy order |
10927 | 0 | // after bug 1195213. |
10928 | 0 | for (nsWeakPtr& weakPtr : Reversed(mFullscreenStack)) { |
10929 | 0 | if (nsCOMPtr<Element> element = do_QueryReferent(weakPtr)) { |
10930 | 0 | ClearFullscreenStateOnElement(element); |
10931 | 0 | } |
10932 | 0 | } |
10933 | 0 | mFullscreenStack.Clear(); |
10934 | 0 | mFullscreenRoot = nullptr; |
10935 | 0 | UpdateViewportScrollbarOverrideForFullscreen(this); |
10936 | 0 | } |
10937 | | |
10938 | | bool |
10939 | | nsIDocument::FullscreenStackPush(Element* aElement) |
10940 | 0 | { |
10941 | 0 | NS_ASSERTION(aElement, "Must pass non-null to FullscreenStackPush()"); |
10942 | 0 | Element* top = FullscreenStackTop(); |
10943 | 0 | if (top == aElement || !aElement) { |
10944 | 0 | return false; |
10945 | 0 | } |
10946 | 0 | EventStateManager::SetFullscreenState(aElement, true); |
10947 | 0 | mFullscreenStack.AppendElement(do_GetWeakReference(aElement)); |
10948 | 0 | NS_ASSERTION(FullscreenStackTop() == aElement, "Should match"); |
10949 | 0 | UpdateViewportScrollbarOverrideForFullscreen(this); |
10950 | 0 | return true; |
10951 | 0 | } |
10952 | | |
10953 | | void |
10954 | | nsIDocument::FullscreenStackPop() |
10955 | 0 | { |
10956 | 0 | if (mFullscreenStack.IsEmpty()) { |
10957 | 0 | return; |
10958 | 0 | } |
10959 | 0 | |
10960 | 0 | ClearFullscreenStateOnElement(FullscreenStackTop()); |
10961 | 0 |
|
10962 | 0 | // Remove top element. Note the remaining top element in the stack |
10963 | 0 | // will not have fullscreen style bits set, so we will need to restore |
10964 | 0 | // them on the new top element before returning. |
10965 | 0 | uint32_t last = mFullscreenStack.Length() - 1; |
10966 | 0 | mFullscreenStack.RemoveElementAt(last); |
10967 | 0 |
|
10968 | 0 | // Pop from the stack null elements (references to elements which have |
10969 | 0 | // been GC'd since they were added to the stack) and elements which are |
10970 | 0 | // no longer in this document. |
10971 | 0 | while (!mFullscreenStack.IsEmpty()) { |
10972 | 0 | Element* element = FullscreenStackTop(); |
10973 | 0 | if (!element || !element->IsInUncomposedDoc() || element->OwnerDoc() != this) { |
10974 | 0 | NS_ASSERTION(!element->State().HasState(NS_EVENT_STATE_FULLSCREEN), |
10975 | 0 | "Should have already removed fullscreen styles"); |
10976 | 0 | uint32_t last = mFullscreenStack.Length() - 1; |
10977 | 0 | mFullscreenStack.RemoveElementAt(last); |
10978 | 0 | } else { |
10979 | 0 | // The top element of the stack is now an in-doc element. Return here. |
10980 | 0 | break; |
10981 | 0 | } |
10982 | 0 | } |
10983 | 0 |
|
10984 | 0 | UpdateViewportScrollbarOverrideForFullscreen(this); |
10985 | 0 | } |
10986 | | |
10987 | | Element* |
10988 | | nsIDocument::FullscreenStackTop() |
10989 | 0 | { |
10990 | 0 | if (mFullscreenStack.IsEmpty()) { |
10991 | 0 | return nullptr; |
10992 | 0 | } |
10993 | 0 | uint32_t last = mFullscreenStack.Length() - 1; |
10994 | 0 | nsCOMPtr<Element> element(do_QueryReferent(mFullscreenStack[last])); |
10995 | 0 | NS_ASSERTION(element, "Should have fullscreen element!"); |
10996 | 0 | NS_ASSERTION(element->IsInComposedDoc(), "Fullscreen element should be in doc"); |
10997 | 0 | NS_ASSERTION(element->OwnerDoc() == this, "Fullscreen element should be in this doc"); |
10998 | 0 | return element; |
10999 | 0 | } |
11000 | | |
11001 | | nsTArray<Element*> |
11002 | | nsIDocument::GetFullscreenStack() const |
11003 | 0 | { |
11004 | 0 | nsTArray<Element*> elements; |
11005 | 0 | for (const nsWeakPtr& ptr : mFullscreenStack) { |
11006 | 0 | if (nsCOMPtr<Element> elem = do_QueryReferent(ptr)) { |
11007 | 0 | MOZ_ASSERT(elem->State().HasState(NS_EVENT_STATE_FULLSCREEN)); |
11008 | 0 | elements.AppendElement(elem); |
11009 | 0 | } |
11010 | 0 | } |
11011 | 0 | return elements; |
11012 | 0 | } |
11013 | | |
11014 | | // Returns true if aDoc is in the focused tab in the active window. |
11015 | | static bool |
11016 | | IsInActiveTab(nsIDocument* aDoc) |
11017 | 0 | { |
11018 | 0 | nsCOMPtr<nsIDocShell> docshell = aDoc->GetDocShell(); |
11019 | 0 | if (!docshell) { |
11020 | 0 | return false; |
11021 | 0 | } |
11022 | 0 | |
11023 | 0 | bool isActive = false; |
11024 | 0 | docshell->GetIsActive(&isActive); |
11025 | 0 | if (!isActive) { |
11026 | 0 | return false; |
11027 | 0 | } |
11028 | 0 | |
11029 | 0 | nsCOMPtr<nsIDocShellTreeItem> rootItem; |
11030 | 0 | docshell->GetRootTreeItem(getter_AddRefs(rootItem)); |
11031 | 0 | if (!rootItem) { |
11032 | 0 | return false; |
11033 | 0 | } |
11034 | 0 | nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow(); |
11035 | 0 | if (!rootWin) { |
11036 | 0 | return false; |
11037 | 0 | } |
11038 | 0 | |
11039 | 0 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
11040 | 0 | if (!fm) { |
11041 | 0 | return false; |
11042 | 0 | } |
11043 | 0 | |
11044 | 0 | nsCOMPtr<mozIDOMWindowProxy> activeWindow; |
11045 | 0 | fm->GetActiveWindow(getter_AddRefs(activeWindow)); |
11046 | 0 | if (!activeWindow) { |
11047 | 0 | return false; |
11048 | 0 | } |
11049 | 0 | |
11050 | 0 | return activeWindow == rootWin; |
11051 | 0 | } |
11052 | | |
11053 | | nsresult nsIDocument::RemoteFrameFullscreenChanged(Element* aFrameElement) |
11054 | 0 | { |
11055 | 0 | // Ensure the frame element is the fullscreen element in this document. |
11056 | 0 | // If the frame element is already the fullscreen element in this document, |
11057 | 0 | // this has no effect. |
11058 | 0 | auto request = FullscreenRequest::CreateForRemote(aFrameElement); |
11059 | 0 | RequestFullscreen(std::move(request)); |
11060 | 0 | return NS_OK; |
11061 | 0 | } |
11062 | | |
11063 | | nsresult nsIDocument::RemoteFrameFullscreenReverted() |
11064 | 0 | { |
11065 | 0 | UniquePtr<FullscreenExit> exit = FullscreenExit::CreateForRemote(this); |
11066 | 0 | RestorePreviousFullscreenState(std::move(exit)); |
11067 | 0 | return NS_OK; |
11068 | 0 | } |
11069 | | |
11070 | | /* static */ bool |
11071 | | nsIDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject) |
11072 | 0 | { |
11073 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
11074 | 0 | return nsContentUtils::IsSystemCaller(aCx) || |
11075 | 0 | nsContentUtils::IsUnprefixedFullscreenApiEnabled(); |
11076 | 0 | } |
11077 | | |
11078 | | static bool |
11079 | | HasFullscreenSubDocument(nsIDocument* aDoc) |
11080 | 0 | { |
11081 | 0 | uint32_t count = CountFullscreenSubDocuments(aDoc); |
11082 | 0 | NS_ASSERTION(count <= 1, "Fullscreen docs should have at most 1 fullscreen child!"); |
11083 | 0 | return count >= 1; |
11084 | 0 | } |
11085 | | |
11086 | | // Returns nullptr if a request for Fullscreen API is currently enabled |
11087 | | // in the given document. Returns a static string indicates the reason |
11088 | | // why it is not enabled otherwise. |
11089 | | static const char* |
11090 | | GetFullscreenError(nsIDocument* aDoc, CallerType aCallerType) |
11091 | 0 | { |
11092 | 0 | bool apiEnabled = nsContentUtils::IsFullscreenApiEnabled(); |
11093 | 0 | if (apiEnabled && aCallerType == CallerType::System) { |
11094 | 0 | // Chrome code can always use the fullscreen API, provided it's not |
11095 | 0 | // explicitly disabled. |
11096 | 0 | return nullptr; |
11097 | 0 | } |
11098 | 0 | |
11099 | 0 | if (!apiEnabled) { |
11100 | 0 | return "FullscreenDeniedDisabled"; |
11101 | 0 | } |
11102 | 0 | |
11103 | 0 | if (!aDoc->IsVisible()) { |
11104 | 0 | return "FullscreenDeniedHidden"; |
11105 | 0 | } |
11106 | 0 | |
11107 | 0 | // Ensure that all containing elements are <iframe> and have |
11108 | 0 | // allowfullscreen attribute set. |
11109 | 0 | nsCOMPtr<nsIDocShell> docShell(aDoc->GetDocShell()); |
11110 | 0 | if (!docShell || !docShell->GetFullscreenAllowed()) { |
11111 | 0 | return "FullscreenDeniedContainerNotAllowed"; |
11112 | 0 | } |
11113 | 0 | return nullptr; |
11114 | 0 | } |
11115 | | |
11116 | | bool |
11117 | | nsIDocument::FullscreenElementReadyCheck(const FullscreenRequest& aRequest) |
11118 | 0 | { |
11119 | 0 | Element* elem = aRequest.Element(); |
11120 | 0 | // Strictly speaking, this isn't part of the fullscreen element ready |
11121 | 0 | // check in the spec, but per steps in the spec, when an element which |
11122 | 0 | // is already the fullscreen element requests fullscreen, nothing |
11123 | 0 | // should change and no event should be dispatched, but we still need |
11124 | 0 | // to resolve the returned promise. |
11125 | 0 | if (elem == FullscreenStackTop()) { |
11126 | 0 | aRequest.MayResolvePromise(); |
11127 | 0 | return false; |
11128 | 0 | } |
11129 | 0 | if (!elem->IsInComposedDoc()) { |
11130 | 0 | aRequest.Reject("FullscreenDeniedNotInDocument"); |
11131 | 0 | return false; |
11132 | 0 | } |
11133 | 0 | if (elem->OwnerDoc() != this) { |
11134 | 0 | aRequest.Reject("FullscreenDeniedMovedDocument"); |
11135 | 0 | return false; |
11136 | 0 | } |
11137 | 0 | if (!GetWindow()) { |
11138 | 0 | aRequest.Reject("FullscreenDeniedLostWindow"); |
11139 | 0 | return false; |
11140 | 0 | } |
11141 | 0 | if (const char* msg = GetFullscreenError(this, aRequest.mCallerType)) { |
11142 | 0 | aRequest.Reject(msg); |
11143 | 0 | return false; |
11144 | 0 | } |
11145 | 0 | if (HasFullscreenSubDocument(this)) { |
11146 | 0 | aRequest.Reject("FullscreenDeniedSubDocFullScreen"); |
11147 | 0 | return false; |
11148 | 0 | } |
11149 | 0 | //XXXsmaug Note, we don't follow the latest fullscreen spec here. |
11150 | 0 | // This whole check could be probably removed. |
11151 | 0 | if (FullscreenStackTop() && |
11152 | 0 | !nsContentUtils::ContentIsHostIncludingDescendantOf(elem, |
11153 | 0 | FullscreenStackTop())) { |
11154 | 0 | // If this document is fullscreen, only grant fullscreen requests from |
11155 | 0 | // a descendant of the current fullscreen element. |
11156 | 0 | aRequest.Reject("FullscreenDeniedNotDescendant"); |
11157 | 0 | return false; |
11158 | 0 | } |
11159 | 0 | if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) { |
11160 | 0 | aRequest.Reject("FullscreenDeniedNotFocusedTab"); |
11161 | 0 | return false; |
11162 | 0 | } |
11163 | 0 | // Deny requests when a windowed plugin is focused. |
11164 | 0 | nsFocusManager* fm = nsFocusManager::GetFocusManager(); |
11165 | 0 | if (!fm) { |
11166 | 0 | NS_WARNING("Failed to retrieve focus manager in fullscreen request."); |
11167 | 0 | aRequest.MayRejectPromise(); |
11168 | 0 | return false; |
11169 | 0 | } |
11170 | 0 | if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(fm->GetFocusedElement())) { |
11171 | 0 | aRequest.Reject("FullscreenDeniedFocusedPlugin"); |
11172 | 0 | return false; |
11173 | 0 | } |
11174 | 0 | return true; |
11175 | 0 | } |
11176 | | |
11177 | | static nsCOMPtr<nsPIDOMWindowOuter> |
11178 | | GetRootWindow(nsIDocument* aDoc) |
11179 | 0 | { |
11180 | 0 | nsIDocShell* docShell = aDoc->GetDocShell(); |
11181 | 0 | if (!docShell) { |
11182 | 0 | return nullptr; |
11183 | 0 | } |
11184 | 0 | nsCOMPtr<nsIDocShellTreeItem> rootItem; |
11185 | 0 | docShell->GetRootTreeItem(getter_AddRefs(rootItem)); |
11186 | 0 | return rootItem ? rootItem->GetWindow() : nullptr; |
11187 | 0 | } |
11188 | | |
11189 | | static bool |
11190 | | ShouldApplyFullscreenDirectly(nsIDocument* aDoc, |
11191 | | nsPIDOMWindowOuter* aRootWin) |
11192 | 0 | { |
11193 | 0 | if (XRE_GetProcessType() == GeckoProcessType_Content) { |
11194 | 0 | // If we are in the content process, we can apply the fullscreen |
11195 | 0 | // state directly only if we have been in DOM fullscreen, because |
11196 | 0 | // otherwise we always need to notify the chrome. |
11197 | 0 | return !!nsContentUtils::GetRootDocument(aDoc)->GetFullscreenElement(); |
11198 | 0 | } else { |
11199 | 0 | // If we are in the chrome process, and the window has not been in |
11200 | 0 | // fullscreen, we certainly need to make that fullscreen first. |
11201 | 0 | if (!aRootWin->GetFullScreen()) { |
11202 | 0 | return false; |
11203 | 0 | } |
11204 | 0 | // The iterator not being at end indicates there is still some |
11205 | 0 | // pending fullscreen request relates to this document. We have to |
11206 | 0 | // push the request to the pending queue so requests are handled |
11207 | 0 | // in the correct order. |
11208 | 0 | PendingFullscreenChangeList::Iterator<FullscreenRequest> iter( |
11209 | 0 | aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot); |
11210 | 0 | if (!iter.AtEnd()) { |
11211 | 0 | return false; |
11212 | 0 | } |
11213 | 0 | // We have to apply the fullscreen state directly in this case, |
11214 | 0 | // because nsGlobalWindow::SetFullscreenInternal() will do nothing |
11215 | 0 | // if it is already in fullscreen. If we do not apply the state but |
11216 | 0 | // instead add it to the queue and wait for the window as normal, |
11217 | 0 | // we would get stuck. |
11218 | 0 | return true; |
11219 | 0 | } |
11220 | 0 | } |
11221 | | |
11222 | | void |
11223 | | nsIDocument::RequestFullscreen(UniquePtr<FullscreenRequest> aRequest) |
11224 | 0 | { |
11225 | 0 | nsCOMPtr<nsPIDOMWindowOuter> rootWin = GetRootWindow(this); |
11226 | 0 | if (!rootWin) { |
11227 | 0 | aRequest->MayRejectPromise(); |
11228 | 0 | return; |
11229 | 0 | } |
11230 | 0 | |
11231 | 0 | if (ShouldApplyFullscreenDirectly(this, rootWin)) { |
11232 | 0 | ApplyFullscreen(std::move(aRequest)); |
11233 | 0 | return; |
11234 | 0 | } |
11235 | 0 | |
11236 | 0 | // Per spec only HTML, <svg>, and <math> should be allowed, but |
11237 | 0 | // we also need to allow XUL elements right now. |
11238 | 0 | Element* elem = aRequest->Element(); |
11239 | 0 | if (!elem->IsHTMLElement() && !elem->IsXULElement() && |
11240 | 0 | !elem->IsSVGElement(nsGkAtoms::svg) && |
11241 | 0 | !elem->IsMathMLElement(nsGkAtoms::math)) { |
11242 | 0 | aRequest->Reject("FullscreenDeniedNotHTMLSVGOrMathML"); |
11243 | 0 | return; |
11244 | 0 | } |
11245 | 0 | |
11246 | 0 | // We don't need to check element ready before this point, because |
11247 | 0 | // if we called ApplyFullscreen, it would check that for us. |
11248 | 0 | if (!FullscreenElementReadyCheck(*aRequest)) { |
11249 | 0 | return; |
11250 | 0 | } |
11251 | 0 | |
11252 | 0 | PendingFullscreenChangeList::Add(std::move(aRequest)); |
11253 | 0 | if (XRE_GetProcessType() == GeckoProcessType_Content) { |
11254 | 0 | // If we are not the top level process, dispatch an event to make |
11255 | 0 | // our parent process go fullscreen first. |
11256 | 0 | nsContentUtils::DispatchEventOnlyToChrome( |
11257 | 0 | this, ToSupports(this), NS_LITERAL_STRING("MozDOMFullscreen:Request"), |
11258 | 0 | CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr); |
11259 | 0 | } else { |
11260 | 0 | // Make the window fullscreen. |
11261 | 0 | rootWin->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, true); |
11262 | 0 | } |
11263 | 0 | } |
11264 | | |
11265 | | /* static */ bool |
11266 | | nsIDocument::HandlePendingFullscreenRequests(nsIDocument* aDoc) |
11267 | 0 | { |
11268 | 0 | bool handled = false; |
11269 | 0 | PendingFullscreenChangeList::Iterator<FullscreenRequest> iter( |
11270 | 0 | aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot); |
11271 | 0 | while (!iter.AtEnd()) { |
11272 | 0 | UniquePtr<FullscreenRequest> request = iter.TakeAndNext(); |
11273 | 0 | nsIDocument* doc = request->Document(); |
11274 | 0 | if (doc->ApplyFullscreen(std::move(request))) { |
11275 | 0 | handled = true; |
11276 | 0 | } |
11277 | 0 | } |
11278 | 0 | return handled; |
11279 | 0 | } |
11280 | | |
11281 | | static void |
11282 | | ClearPendingFullscreenRequests(nsIDocument* aDoc) |
11283 | 0 | { |
11284 | 0 | PendingFullscreenChangeList::Iterator<FullscreenRequest> iter( |
11285 | 0 | aDoc, PendingFullscreenChangeList::eInclusiveDescendants); |
11286 | 0 | while (!iter.AtEnd()) { |
11287 | 0 | UniquePtr<FullscreenRequest> request = iter.TakeAndNext(); |
11288 | 0 | request->MayRejectPromise(); |
11289 | 0 | } |
11290 | 0 | } |
11291 | | |
11292 | | bool |
11293 | | nsIDocument::ApplyFullscreen(UniquePtr<FullscreenRequest> aRequest) |
11294 | 0 | { |
11295 | 0 | if (!FullscreenElementReadyCheck(*aRequest)) { |
11296 | 0 | return false; |
11297 | 0 | } |
11298 | 0 | |
11299 | 0 | // Stash a reference to any existing fullscreen doc, we'll use this later |
11300 | 0 | // to detect if the origin which is fullscreen has changed. |
11301 | 0 | nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this); |
11302 | 0 |
|
11303 | 0 | // Stores a list of documents which we must dispatch "fullscreenchange" |
11304 | 0 | // too. We're required by the spec to dispatch the events in root-to-leaf |
11305 | 0 | // order, but we traverse the doctree in a leaf-to-root order, so we save |
11306 | 0 | // references to the documents we must dispatch to so that we get the order |
11307 | 0 | // as specified. |
11308 | 0 | AutoTArray<nsIDocument*, 8> changed; |
11309 | 0 |
|
11310 | 0 | // Remember the root document, so that if a fullscreen document is hidden |
11311 | 0 | // we can reset fullscreen state in the remaining visible fullscreen documents. |
11312 | 0 | nsIDocument* fullScreenRootDoc = nsContentUtils::GetRootDocument(this); |
11313 | 0 |
|
11314 | 0 | // If a document is already in fullscreen, then unlock the mouse pointer |
11315 | 0 | // before setting a new document to fullscreen |
11316 | 0 | UnlockPointer(); |
11317 | 0 |
|
11318 | 0 | // Set the fullscreen element. This sets the fullscreen style on the |
11319 | 0 | // element, and the fullscreen-ancestor styles on ancestors of the element |
11320 | 0 | // in this document. |
11321 | 0 | Element* elem = aRequest->Element(); |
11322 | 0 | DebugOnly<bool> x = FullscreenStackPush(elem); |
11323 | 0 | NS_ASSERTION(x, "Fullscreen state of requesting doc should always change!"); |
11324 | 0 | // Set the iframe fullscreen flag. |
11325 | 0 | if (auto* iframe = HTMLIFrameElement::FromNode(elem)) { |
11326 | 0 | iframe->SetFullscreenFlag(true); |
11327 | 0 | } |
11328 | 0 | changed.AppendElement(this); |
11329 | 0 |
|
11330 | 0 | // Propagate up the document hierarchy, setting the fullscreen element as |
11331 | 0 | // the element's container in ancestor documents. This also sets the |
11332 | 0 | // appropriate css styles as well. Note we don't propagate down the |
11333 | 0 | // document hierarchy, the fullscreen element (or its container) is not |
11334 | 0 | // visible there. Stop when we reach the root document. |
11335 | 0 | nsIDocument* child = this; |
11336 | 0 | while (true) { |
11337 | 0 | child->SetFullscreenRoot(fullScreenRootDoc); |
11338 | 0 | NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc, |
11339 | 0 | "Fullscreen root should be set!"); |
11340 | 0 | if (child == fullScreenRootDoc) { |
11341 | 0 | break; |
11342 | 0 | } |
11343 | 0 | nsIDocument* parent = child->GetParentDocument(); |
11344 | 0 | Element* element = parent->FindContentForSubDocument(child); |
11345 | 0 | if (static_cast<nsDocument*>(parent)->FullscreenStackPush(element)) { |
11346 | 0 | changed.AppendElement(parent); |
11347 | 0 | child = parent; |
11348 | 0 | } else { |
11349 | 0 | // We've reached either the root, or a point in the doctree where the |
11350 | 0 | // new fullscreen element container is the same as the previous |
11351 | 0 | // fullscreen element's container. No more changes need to be made |
11352 | 0 | // to the fullscreen stacks of documents further up the tree. |
11353 | 0 | break; |
11354 | 0 | } |
11355 | 0 | } |
11356 | 0 |
|
11357 | 0 | FullscreenRoots::Add(this); |
11358 | 0 |
|
11359 | 0 | // If it is the first entry of the fullscreen, trigger an event so |
11360 | 0 | // that the UI can response to this change, e.g. hide chrome, or |
11361 | 0 | // notifying parent process to enter fullscreen. Note that chrome |
11362 | 0 | // code may also want to listen to MozDOMFullscreen:NewOrigin event |
11363 | 0 | // to pop up warning UI. |
11364 | 0 | if (!previousFullscreenDoc) { |
11365 | 0 | nsContentUtils::DispatchEventOnlyToChrome( |
11366 | 0 | this, ToSupports(elem), NS_LITERAL_STRING("MozDOMFullscreen:Entered"), |
11367 | 0 | CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr); |
11368 | 0 | } |
11369 | 0 |
|
11370 | 0 | // The origin which is fullscreen gets changed. Trigger an event so |
11371 | 0 | // that the chrome knows to pop up a warning UI. Note that |
11372 | 0 | // previousFullscreenDoc == nullptr upon first entry, so we always |
11373 | 0 | // take this path on the first entry. Also note that, in a multi- |
11374 | 0 | // process browser, the code in content process is responsible for |
11375 | 0 | // sending message with the origin to its parent, and the parent |
11376 | 0 | // shouldn't rely on this event itself. |
11377 | 0 | if (aRequest->mShouldNotifyNewOrigin && |
11378 | 0 | !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) { |
11379 | 0 | DispatchFullscreenNewOriginEvent(this); |
11380 | 0 | } |
11381 | 0 |
|
11382 | 0 | // Dispatch "fullscreenchange" events. Note that the loop order is |
11383 | 0 | // reversed so that events are dispatched in the tree order as |
11384 | 0 | // indicated in the spec. |
11385 | 0 | for (nsIDocument* d : Reversed(changed)) { |
11386 | 0 | DispatchFullscreenChange(d, d->FullscreenStackTop()); |
11387 | 0 | } |
11388 | 0 | aRequest->MayResolvePromise(); |
11389 | 0 | return true; |
11390 | 0 | } |
11391 | | |
11392 | | bool |
11393 | | nsIDocument::FullscreenEnabled(CallerType aCallerType) |
11394 | 0 | { |
11395 | 0 | return !GetFullscreenError(this, aCallerType); |
11396 | 0 | } |
11397 | | |
11398 | | void |
11399 | | nsIDocument::SetOrientationPendingPromise(Promise* aPromise) |
11400 | 0 | { |
11401 | 0 | mOrientationPendingPromise = aPromise; |
11402 | 0 | } |
11403 | | |
11404 | | static void |
11405 | | DispatchPointerLockChange(nsIDocument* aTarget) |
11406 | 0 | { |
11407 | 0 | if (!aTarget) { |
11408 | 0 | return; |
11409 | 0 | } |
11410 | 0 | |
11411 | 0 | RefPtr<AsyncEventDispatcher> asyncDispatcher = |
11412 | 0 | new AsyncEventDispatcher(aTarget, |
11413 | 0 | NS_LITERAL_STRING("pointerlockchange"), |
11414 | 0 | CanBubble::eYes, |
11415 | 0 | ChromeOnlyDispatch::eNo); |
11416 | 0 | asyncDispatcher->PostDOMEvent(); |
11417 | 0 | } |
11418 | | |
11419 | | static void |
11420 | | DispatchPointerLockError(nsIDocument* aTarget, const char* aMessage) |
11421 | 0 | { |
11422 | 0 | if (!aTarget) { |
11423 | 0 | return; |
11424 | 0 | } |
11425 | 0 | |
11426 | 0 | RefPtr<AsyncEventDispatcher> asyncDispatcher = |
11427 | 0 | new AsyncEventDispatcher(aTarget, |
11428 | 0 | NS_LITERAL_STRING("pointerlockerror"), |
11429 | 0 | CanBubble::eYes, |
11430 | 0 | ChromeOnlyDispatch::eNo); |
11431 | 0 | asyncDispatcher->PostDOMEvent(); |
11432 | 0 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
11433 | 0 | NS_LITERAL_CSTRING("DOM"), aTarget, |
11434 | 0 | nsContentUtils::eDOM_PROPERTIES, |
11435 | 0 | aMessage); |
11436 | 0 | } |
11437 | | |
11438 | | class PointerLockRequest final : public Runnable |
11439 | | { |
11440 | | public: |
11441 | | PointerLockRequest(Element* aElement, bool aUserInputOrChromeCaller) |
11442 | | : mozilla::Runnable("PointerLockRequest") |
11443 | | , mElement(do_GetWeakReference(aElement)) |
11444 | | , mDocument(do_GetWeakReference(aElement->OwnerDoc())) |
11445 | | , mUserInputOrChromeCaller(aUserInputOrChromeCaller) |
11446 | 0 | {} |
11447 | | |
11448 | | NS_IMETHOD Run() final; |
11449 | | |
11450 | | private: |
11451 | | nsWeakPtr mElement; |
11452 | | nsWeakPtr mDocument; |
11453 | | bool mUserInputOrChromeCaller; |
11454 | | }; |
11455 | | |
11456 | | static const char* |
11457 | | GetPointerLockError(Element* aElement, Element* aCurrentLock, |
11458 | | bool aNoFocusCheck = false) |
11459 | 0 | { |
11460 | 0 | // Check if pointer lock pref is enabled |
11461 | 0 | if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) { |
11462 | 0 | return "PointerLockDeniedDisabled"; |
11463 | 0 | } |
11464 | 0 | |
11465 | 0 | nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc(); |
11466 | 0 | if (aCurrentLock && aCurrentLock->OwnerDoc() != ownerDoc) { |
11467 | 0 | return "PointerLockDeniedInUse"; |
11468 | 0 | } |
11469 | 0 | |
11470 | 0 | if (!aElement->IsInComposedDoc()) { |
11471 | 0 | return "PointerLockDeniedNotInDocument"; |
11472 | 0 | } |
11473 | 0 | |
11474 | 0 | if (ownerDoc->GetSandboxFlags() & SANDBOXED_POINTER_LOCK) { |
11475 | 0 | return "PointerLockDeniedSandboxed"; |
11476 | 0 | } |
11477 | 0 | |
11478 | 0 | // Check if the element is in a document with a docshell. |
11479 | 0 | if (!ownerDoc->GetContainer()) { |
11480 | 0 | return "PointerLockDeniedHidden"; |
11481 | 0 | } |
11482 | 0 | nsCOMPtr<nsPIDOMWindowOuter> ownerWindow = ownerDoc->GetWindow(); |
11483 | 0 | if (!ownerWindow) { |
11484 | 0 | return "PointerLockDeniedHidden"; |
11485 | 0 | } |
11486 | 0 | nsCOMPtr<nsPIDOMWindowInner> ownerInnerWindow = ownerDoc->GetInnerWindow(); |
11487 | 0 | if (!ownerInnerWindow) { |
11488 | 0 | return "PointerLockDeniedHidden"; |
11489 | 0 | } |
11490 | 0 | if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) { |
11491 | 0 | return "PointerLockDeniedHidden"; |
11492 | 0 | } |
11493 | 0 | |
11494 | 0 | nsCOMPtr<nsPIDOMWindowOuter> top = ownerWindow->GetScriptableTop(); |
11495 | 0 | if (!top || !top->GetExtantDoc() || top->GetExtantDoc()->Hidden()) { |
11496 | 0 | return "PointerLockDeniedHidden"; |
11497 | 0 | } |
11498 | 0 | |
11499 | 0 | if (!aNoFocusCheck) { |
11500 | 0 | mozilla::ErrorResult rv; |
11501 | 0 | if (!top->GetExtantDoc()->HasFocus(rv)) { |
11502 | 0 | return "PointerLockDeniedNotFocused"; |
11503 | 0 | } |
11504 | 0 | } |
11505 | 0 | |
11506 | 0 | return nullptr; |
11507 | 0 | } |
11508 | | |
11509 | | static void |
11510 | | ChangePointerLockedElement(Element* aElement, nsIDocument* aDocument, |
11511 | | Element* aPointerLockedElement) |
11512 | 0 | { |
11513 | 0 | // aDocument here is not really necessary, as it is the uncomposed |
11514 | 0 | // document of both aElement and aPointerLockedElement as far as one |
11515 | 0 | // is not nullptr, and they wouldn't both be nullptr in any case. |
11516 | 0 | // But since the caller of this function should have known what the |
11517 | 0 | // document is, we just don't try to figure out what it should be. |
11518 | 0 | MOZ_ASSERT(aDocument); |
11519 | 0 | MOZ_ASSERT(aElement != aPointerLockedElement); |
11520 | 0 | if (aPointerLockedElement) { |
11521 | 0 | MOZ_ASSERT(aPointerLockedElement->GetComposedDoc() == aDocument); |
11522 | 0 | aPointerLockedElement->ClearPointerLock(); |
11523 | 0 | } |
11524 | 0 | if (aElement) { |
11525 | 0 | MOZ_ASSERT(aElement->GetComposedDoc() == aDocument); |
11526 | 0 | aElement->SetPointerLock(); |
11527 | 0 | EventStateManager::sPointerLockedElement = do_GetWeakReference(aElement); |
11528 | 0 | EventStateManager::sPointerLockedDoc = do_GetWeakReference(aDocument); |
11529 | 0 | NS_ASSERTION(EventStateManager::sPointerLockedElement && |
11530 | 0 | EventStateManager::sPointerLockedDoc, |
11531 | 0 | "aElement and this should support weak references!"); |
11532 | 0 | } else { |
11533 | 0 | EventStateManager::sPointerLockedElement = nullptr; |
11534 | 0 | EventStateManager::sPointerLockedDoc = nullptr; |
11535 | 0 | } |
11536 | 0 | // Retarget all events to aElement via capture or |
11537 | 0 | // stop retargeting if aElement is nullptr. |
11538 | 0 | nsIPresShell::SetCapturingContent(aElement, CAPTURE_POINTERLOCK); |
11539 | 0 | DispatchPointerLockChange(aDocument); |
11540 | 0 | } |
11541 | | |
11542 | | NS_IMETHODIMP |
11543 | | PointerLockRequest::Run() |
11544 | 0 | { |
11545 | 0 | nsCOMPtr<Element> e = do_QueryReferent(mElement); |
11546 | 0 | nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); |
11547 | 0 | nsDocument* d = static_cast<nsDocument*>(doc.get()); |
11548 | 0 | const char* error = nullptr; |
11549 | 0 | if (!e || !d || !e->GetComposedDoc()) { |
11550 | 0 | error = "PointerLockDeniedNotInDocument"; |
11551 | 0 | } else if (e->GetComposedDoc() != d) { |
11552 | 0 | error = "PointerLockDeniedMovedDocument"; |
11553 | 0 | } |
11554 | 0 | if (!error) { |
11555 | 0 | nsCOMPtr<Element> pointerLockedElement = |
11556 | 0 | do_QueryReferent(EventStateManager::sPointerLockedElement); |
11557 | 0 | if (e == pointerLockedElement) { |
11558 | 0 | DispatchPointerLockChange(d); |
11559 | 0 | return NS_OK; |
11560 | 0 | } |
11561 | 0 | // Note, we must bypass focus change, so pass true as the last parameter! |
11562 | 0 | error = GetPointerLockError(e, pointerLockedElement, true); |
11563 | 0 | // Another element in the same document is requesting pointer lock, |
11564 | 0 | // just grant it without user input check. |
11565 | 0 | if (!error && pointerLockedElement) { |
11566 | 0 | ChangePointerLockedElement(e, d, pointerLockedElement); |
11567 | 0 | return NS_OK; |
11568 | 0 | } |
11569 | 0 | } |
11570 | 0 | // If it is neither user input initiated, nor requested in fullscreen, |
11571 | 0 | // it should be rejected. |
11572 | 0 | if (!error && !mUserInputOrChromeCaller && !doc->GetFullscreenElement()) { |
11573 | 0 | error = "PointerLockDeniedNotInputDriven"; |
11574 | 0 | } |
11575 | 0 | if (!error && !d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) { |
11576 | 0 | error = "PointerLockDeniedFailedToLock"; |
11577 | 0 | } |
11578 | 0 | if (error) { |
11579 | 0 | DispatchPointerLockError(d, error); |
11580 | 0 | return NS_OK; |
11581 | 0 | } |
11582 | 0 | |
11583 | 0 | ChangePointerLockedElement(e, d, nullptr); |
11584 | 0 | nsContentUtils::DispatchEventOnlyToChrome( |
11585 | 0 | doc, ToSupports(e), NS_LITERAL_STRING("MozDOMPointerLock:Entered"), |
11586 | 0 | CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr); |
11587 | 0 | return NS_OK; |
11588 | 0 | } |
11589 | | |
11590 | | void |
11591 | | nsIDocument::RequestPointerLock(Element* aElement, CallerType aCallerType) |
11592 | 0 | { |
11593 | 0 | NS_ASSERTION(aElement, |
11594 | 0 | "Must pass non-null element to nsDocument::RequestPointerLock"); |
11595 | 0 |
|
11596 | 0 | nsCOMPtr<Element> pointerLockedElement = |
11597 | 0 | do_QueryReferent(EventStateManager::sPointerLockedElement); |
11598 | 0 | if (aElement == pointerLockedElement) { |
11599 | 0 | DispatchPointerLockChange(this); |
11600 | 0 | return; |
11601 | 0 | } |
11602 | 0 | |
11603 | 0 | if (const char* msg = GetPointerLockError(aElement, pointerLockedElement)) { |
11604 | 0 | DispatchPointerLockError(this, msg); |
11605 | 0 | return; |
11606 | 0 | } |
11607 | 0 | |
11608 | 0 | bool userInputOrSystemCaller = EventStateManager::IsHandlingUserInput() || |
11609 | 0 | aCallerType == CallerType::System; |
11610 | 0 | nsCOMPtr<nsIRunnable> request = |
11611 | 0 | new PointerLockRequest(aElement, userInputOrSystemCaller); |
11612 | 0 | Dispatch(TaskCategory::Other, request.forget()); |
11613 | 0 | } |
11614 | | |
11615 | | bool |
11616 | | nsIDocument::SetPointerLock(Element* aElement, int aCursorStyle) |
11617 | 0 | { |
11618 | 0 | MOZ_ASSERT(!aElement || aElement->OwnerDoc() == this, |
11619 | 0 | "We should be either unlocking pointer (aElement is nullptr), " |
11620 | 0 | "or locking pointer to an element in this document"); |
11621 | | #ifdef DEBUG |
11622 | | if (!aElement) { |
11623 | | nsCOMPtr<nsIDocument> pointerLockedDoc = |
11624 | | do_QueryReferent(EventStateManager::sPointerLockedDoc); |
11625 | | MOZ_ASSERT(pointerLockedDoc == this); |
11626 | | } |
11627 | | #endif |
11628 | |
|
11629 | 0 | nsIPresShell* shell = GetShell(); |
11630 | 0 | if (!shell) { |
11631 | 0 | NS_WARNING("SetPointerLock(): No PresShell"); |
11632 | 0 | if (!aElement) { |
11633 | 0 | // If we are unlocking pointer lock, but for some reason the doc |
11634 | 0 | // has already detached from the presshell, just ask the event |
11635 | 0 | // state manager to release the pointer. |
11636 | 0 | EventStateManager::SetPointerLock(nullptr, nullptr); |
11637 | 0 | return true; |
11638 | 0 | } |
11639 | 0 | return false; |
11640 | 0 | } |
11641 | 0 | nsPresContext* presContext = shell->GetPresContext(); |
11642 | 0 | if (!presContext) { |
11643 | 0 | NS_WARNING("SetPointerLock(): Unable to get PresContext"); |
11644 | 0 | return false; |
11645 | 0 | } |
11646 | 0 |
|
11647 | 0 | nsCOMPtr<nsIWidget> widget; |
11648 | 0 | nsIFrame* rootFrame = shell->GetRootFrame(); |
11649 | 0 | if (!NS_WARN_IF(!rootFrame)) { |
11650 | 0 | widget = rootFrame->GetNearestWidget(); |
11651 | 0 | NS_WARNING_ASSERTION( |
11652 | 0 | widget, |
11653 | 0 | "SetPointerLock(): Unable to find widget in " |
11654 | 0 | "shell->GetRootFrame()->GetNearestWidget();"); |
11655 | 0 | if (aElement && !widget) { |
11656 | 0 | return false; |
11657 | 0 | } |
11658 | 0 | } |
11659 | 0 | |
11660 | 0 | // Hide the cursor and set pointer lock for future mouse events |
11661 | 0 | RefPtr<EventStateManager> esm = presContext->EventStateManager(); |
11662 | 0 | esm->SetCursor(aCursorStyle, nullptr, false, |
11663 | 0 | 0.0f, 0.0f, widget, true); |
11664 | 0 | EventStateManager::SetPointerLock(widget, aElement); |
11665 | 0 |
|
11666 | 0 | return true; |
11667 | 0 | } |
11668 | | |
11669 | | void |
11670 | | nsIDocument::UnlockPointer(nsIDocument* aDoc) |
11671 | 0 | { |
11672 | 0 | if (!EventStateManager::sIsPointerLocked) { |
11673 | 0 | return; |
11674 | 0 | } |
11675 | 0 | |
11676 | 0 | nsCOMPtr<nsIDocument> pointerLockedDoc = |
11677 | 0 | do_QueryReferent(EventStateManager::sPointerLockedDoc); |
11678 | 0 | if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) { |
11679 | 0 | return; |
11680 | 0 | } |
11681 | 0 | if (!pointerLockedDoc->SetPointerLock(nullptr, NS_STYLE_CURSOR_AUTO)) { |
11682 | 0 | return; |
11683 | 0 | } |
11684 | 0 | |
11685 | 0 | nsCOMPtr<Element> pointerLockedElement = |
11686 | 0 | do_QueryReferent(EventStateManager::sPointerLockedElement); |
11687 | 0 | ChangePointerLockedElement(nullptr, pointerLockedDoc, pointerLockedElement); |
11688 | 0 |
|
11689 | 0 | RefPtr<AsyncEventDispatcher> asyncDispatcher = |
11690 | 0 | new AsyncEventDispatcher(pointerLockedElement, |
11691 | 0 | NS_LITERAL_STRING("MozDOMPointerLock:Exited"), |
11692 | 0 | CanBubble::eYes, |
11693 | 0 | ChromeOnlyDispatch::eYes); |
11694 | 0 | asyncDispatcher->RunDOMEventWhenSafe(); |
11695 | 0 | } |
11696 | | |
11697 | | void |
11698 | | nsIDocument::UpdateVisibilityState() |
11699 | 0 | { |
11700 | 0 | dom::VisibilityState oldState = mVisibilityState; |
11701 | 0 | mVisibilityState = ComputeVisibilityState(); |
11702 | 0 | if (oldState != mVisibilityState) { |
11703 | 0 | nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this), |
11704 | 0 | NS_LITERAL_STRING("visibilitychange"), |
11705 | 0 | CanBubble::eYes, |
11706 | 0 | Cancelable::eNo); |
11707 | 0 | EnumerateActivityObservers(NotifyActivityChanged, nullptr); |
11708 | 0 | } |
11709 | 0 |
|
11710 | 0 | if (mVisibilityState == dom::VisibilityState::Visible) { |
11711 | 0 | MaybeActiveMediaComponents(); |
11712 | 0 | } |
11713 | 0 | } |
11714 | | |
11715 | | VisibilityState |
11716 | | nsIDocument::ComputeVisibilityState() const |
11717 | 0 | { |
11718 | 0 | // We have to check a few pieces of information here: |
11719 | 0 | // 1) Are we in bfcache (!IsVisible())? If so, nothing else matters. |
11720 | 0 | // 2) Do we have an outer window? If not, we're hidden. Note that we don't |
11721 | 0 | // want to use GetWindow here because it does weird groveling for windows |
11722 | 0 | // in some cases. |
11723 | 0 | // 3) Is our outer window background? If so, we're hidden. |
11724 | 0 | // Otherwise, we're visible. |
11725 | 0 | if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() || |
11726 | 0 | mWindow->GetOuterWindow()->IsBackground()) { |
11727 | 0 | return dom::VisibilityState::Hidden; |
11728 | 0 | } |
11729 | 0 | |
11730 | 0 | return dom::VisibilityState::Visible; |
11731 | 0 | } |
11732 | | |
11733 | | void |
11734 | | nsIDocument::PostVisibilityUpdateEvent() |
11735 | 0 | { |
11736 | 0 | nsCOMPtr<nsIRunnable> event = |
11737 | 0 | NewRunnableMethod("nsIDocument::UpdateVisibilityState", |
11738 | 0 | this, |
11739 | 0 | &nsIDocument::UpdateVisibilityState); |
11740 | 0 | Dispatch(TaskCategory::Other, event.forget()); |
11741 | 0 | } |
11742 | | |
11743 | | void |
11744 | | nsIDocument::MaybeActiveMediaComponents() |
11745 | 0 | { |
11746 | 0 | if (!mWindow) { |
11747 | 0 | return; |
11748 | 0 | } |
11749 | 0 | |
11750 | 0 | GetWindow()->MaybeActiveMediaComponents(); |
11751 | 0 | } |
11752 | | |
11753 | | /* virtual */ void |
11754 | | nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aSizes) const |
11755 | 0 | { |
11756 | 0 | if (mPresShell) { |
11757 | 0 | mPresShell->AddSizeOfIncludingThis(aSizes); |
11758 | 0 | } |
11759 | 0 |
|
11760 | 0 | aSizes.mPropertyTablesSize += |
11761 | 0 | mPropertyTable.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf); |
11762 | 0 |
|
11763 | 0 | if (EventListenerManager* elm = GetExistingListenerManager()) { |
11764 | 0 | aSizes.mDOMEventListenersCount += elm->ListenerCount(); |
11765 | 0 | } |
11766 | 0 |
|
11767 | 0 | if (mNodeInfoManager) { |
11768 | 0 | mNodeInfoManager->AddSizeOfIncludingThis(aSizes); |
11769 | 0 | } |
11770 | 0 |
|
11771 | 0 | aSizes.mDOMMediaQueryLists += |
11772 | 0 | mDOMMediaQueryLists.sizeOfExcludingThis(aSizes.mState.mMallocSizeOf); |
11773 | 0 |
|
11774 | 0 | for (const MediaQueryList* mql : mDOMMediaQueryLists) { |
11775 | 0 | aSizes.mDOMMediaQueryLists += |
11776 | 0 | mql->SizeOfExcludingThis(aSizes.mState.mMallocSizeOf); |
11777 | 0 | } |
11778 | 0 |
|
11779 | 0 | // Measurement of the following members may be added later if DMD finds it |
11780 | 0 | // is worthwhile: |
11781 | 0 | // - many! |
11782 | 0 | } |
11783 | | |
11784 | | void |
11785 | | nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const |
11786 | 0 | { |
11787 | 0 | aWindowSizes.mDOMOtherSize += aWindowSizes.mState.mMallocSizeOf(this); |
11788 | 0 | DocAddSizeOfExcludingThis(aWindowSizes); |
11789 | 0 | } |
11790 | | |
11791 | | |
11792 | | void |
11793 | | nsDocument::AddSizeOfExcludingThis(nsWindowSizes& aSizes, |
11794 | | size_t* aNodeSize) const |
11795 | 0 | { |
11796 | 0 | // This AddSizeOfExcludingThis() overrides the one from nsINode. But |
11797 | 0 | // nsDocuments can only appear at the top of the DOM tree, and we use the |
11798 | 0 | // specialized DocAddSizeOfExcludingThis() in that case. So this should never |
11799 | 0 | // be called. |
11800 | 0 | MOZ_CRASH(); |
11801 | 0 | } |
11802 | | |
11803 | | /* static */ void |
11804 | | nsIDocument::AddSizeOfNodeTree(nsINode& aNode, nsWindowSizes& aWindowSizes) |
11805 | 0 | { |
11806 | 0 | size_t nodeSize = 0; |
11807 | 0 | aNode.AddSizeOfIncludingThis(aWindowSizes, &nodeSize); |
11808 | 0 |
|
11809 | 0 | // This is where we transfer the nodeSize obtained from |
11810 | 0 | // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes. |
11811 | 0 | switch (aNode.NodeType()) { |
11812 | 0 | case nsINode::ELEMENT_NODE: |
11813 | 0 | aWindowSizes.mDOMElementNodesSize += nodeSize; |
11814 | 0 | break; |
11815 | 0 | case nsINode::TEXT_NODE: |
11816 | 0 | aWindowSizes.mDOMTextNodesSize += nodeSize; |
11817 | 0 | break; |
11818 | 0 | case nsINode::CDATA_SECTION_NODE: |
11819 | 0 | aWindowSizes.mDOMCDATANodesSize += nodeSize; |
11820 | 0 | break; |
11821 | 0 | case nsINode::COMMENT_NODE: |
11822 | 0 | aWindowSizes.mDOMCommentNodesSize += nodeSize; |
11823 | 0 | break; |
11824 | 0 | default: |
11825 | 0 | aWindowSizes.mDOMOtherSize += nodeSize; |
11826 | 0 | break; |
11827 | 0 | } |
11828 | 0 | |
11829 | 0 | if (EventListenerManager* elm = aNode.GetExistingListenerManager()) { |
11830 | 0 | aWindowSizes.mDOMEventListenersCount += elm->ListenerCount(); |
11831 | 0 | } |
11832 | 0 |
|
11833 | 0 | if (aNode.IsContent()) { |
11834 | 0 | nsTArray<nsIContent*> anonKids; |
11835 | 0 | nsContentUtils::AppendNativeAnonymousChildren(aNode.AsContent(), |
11836 | 0 | anonKids, |
11837 | 0 | nsIContent::eAllChildren); |
11838 | 0 | for (nsIContent* anonKid : anonKids) { |
11839 | 0 | AddSizeOfNodeTree(*anonKid, aWindowSizes); |
11840 | 0 | } |
11841 | 0 |
|
11842 | 0 | if (auto* element = Element::FromNode(aNode)) { |
11843 | 0 | if (ShadowRoot* shadow = element->GetShadowRoot()) { |
11844 | 0 | AddSizeOfNodeTree(*shadow, aWindowSizes); |
11845 | 0 | } |
11846 | 0 |
|
11847 | 0 | for (nsXBLBinding* binding = element->GetXBLBinding(); |
11848 | 0 | binding; |
11849 | 0 | binding = binding->GetBaseBinding()) { |
11850 | 0 | if (nsIContent* anonContent = binding->GetAnonymousContent()) { |
11851 | 0 | AddSizeOfNodeTree(*anonContent, aWindowSizes); |
11852 | 0 | } |
11853 | 0 | } |
11854 | 0 | } |
11855 | 0 | } |
11856 | 0 |
|
11857 | 0 | // NOTE(emilio): If you feel smart and want to change this function to use |
11858 | 0 | // GetNextNode(), think twice, since you'd need to handle <xbl:content> in a |
11859 | 0 | // sane way, and kids of <content> won't point to the parent, so we'd never |
11860 | 0 | // find the root node where we should stop at. |
11861 | 0 | for (nsIContent* kid = aNode.GetFirstChild(); kid; kid = kid->GetNextSibling()) { |
11862 | 0 | AddSizeOfNodeTree(*kid, aWindowSizes); |
11863 | 0 | } |
11864 | 0 | } |
11865 | | |
11866 | | void |
11867 | | nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const |
11868 | 0 | { |
11869 | 0 | nsINode::AddSizeOfExcludingThis(aWindowSizes, &aWindowSizes.mDOMOtherSize); |
11870 | 0 |
|
11871 | 0 | for (nsIContent* kid = GetFirstChild(); kid; kid = kid->GetNextSibling()) { |
11872 | 0 | AddSizeOfNodeTree(*kid, aWindowSizes); |
11873 | 0 | } |
11874 | 0 |
|
11875 | 0 | // IMPORTANT: for our ComputedValues measurements, we want to measure |
11876 | 0 | // ComputedValues accessible from DOM elements before ComputedValues not |
11877 | 0 | // accessible from DOM elements (i.e. accessible only from the frame tree). |
11878 | 0 | // |
11879 | 0 | // Therefore, the measurement of the nsIDocument superclass must happen after |
11880 | 0 | // the measurement of DOM nodes (above), because nsIDocument contains the |
11881 | 0 | // PresShell, which contains the frame tree. |
11882 | 0 | nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes); |
11883 | 0 |
|
11884 | 0 | DocumentOrShadowRoot::AddSizeOfExcludingThis(aWindowSizes); |
11885 | 0 | for (auto& sheetArray : mAdditionalSheets) { |
11886 | 0 | AddSizeOfOwnedSheetArrayExcludingThis(aWindowSizes, sheetArray); |
11887 | 0 | } |
11888 | 0 | // Lumping in the loader with the style-sheets size is not ideal, |
11889 | 0 | // but most of the things in there are in fact stylesheets, so it |
11890 | 0 | // doesn't seem worthwhile to separate it out. |
11891 | 0 | aWindowSizes.mLayoutStyleSheetsSize += |
11892 | 0 | CSSLoader()->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf); |
11893 | 0 |
|
11894 | 0 | aWindowSizes.mDOMOtherSize += mAttrStyleSheet |
11895 | 0 | ? mAttrStyleSheet->DOMSizeOfIncludingThis( |
11896 | 0 | aWindowSizes.mState.mMallocSizeOf) |
11897 | 0 | : 0; |
11898 | 0 |
|
11899 | 0 | aWindowSizes.mDOMOtherSize += |
11900 | 0 | mStyledLinks.ShallowSizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf); |
11901 | 0 |
|
11902 | 0 | // Measurement of the following members may be added later if DMD finds it |
11903 | 0 | // is worthwhile: |
11904 | 0 | // - many! |
11905 | 0 | } |
11906 | | |
11907 | | already_AddRefed<nsIDocument> |
11908 | | nsIDocument::Constructor(const GlobalObject& aGlobal, |
11909 | | ErrorResult& rv) |
11910 | 0 | { |
11911 | 0 | nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); |
11912 | 0 | if (!global) { |
11913 | 0 | rv.Throw(NS_ERROR_UNEXPECTED); |
11914 | 0 | return nullptr; |
11915 | 0 | } |
11916 | 0 | |
11917 | 0 | nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(aGlobal.GetAsSupports()); |
11918 | 0 | if (!prin) { |
11919 | 0 | rv.Throw(NS_ERROR_UNEXPECTED); |
11920 | 0 | return nullptr; |
11921 | 0 | } |
11922 | 0 | |
11923 | 0 | nsCOMPtr<nsIURI> uri; |
11924 | 0 | NS_NewURI(getter_AddRefs(uri), "about:blank"); |
11925 | 0 | if (!uri) { |
11926 | 0 | rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
11927 | 0 | return nullptr; |
11928 | 0 | } |
11929 | 0 | |
11930 | 0 | nsCOMPtr<nsIDocument> doc; |
11931 | 0 | nsresult res = |
11932 | 0 | NS_NewDOMDocument(getter_AddRefs(doc), |
11933 | 0 | VoidString(), |
11934 | 0 | EmptyString(), |
11935 | 0 | nullptr, |
11936 | 0 | uri, |
11937 | 0 | uri, |
11938 | 0 | prin->GetPrincipal(), |
11939 | 0 | true, |
11940 | 0 | global, |
11941 | 0 | DocumentFlavorPlain); |
11942 | 0 | if (NS_FAILED(res)) { |
11943 | 0 | rv.Throw(res); |
11944 | 0 | return nullptr; |
11945 | 0 | } |
11946 | 0 | |
11947 | 0 | doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE); |
11948 | 0 |
|
11949 | 0 | return doc.forget(); |
11950 | 0 | } |
11951 | | |
11952 | | XPathExpression* |
11953 | | nsIDocument::CreateExpression(const nsAString& aExpression, |
11954 | | XPathNSResolver* aResolver, |
11955 | | ErrorResult& rv) |
11956 | 0 | { |
11957 | 0 | return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv); |
11958 | 0 | } |
11959 | | |
11960 | | nsINode* |
11961 | | nsIDocument::CreateNSResolver(nsINode& aNodeResolver) |
11962 | 0 | { |
11963 | 0 | return XPathEvaluator()->CreateNSResolver(aNodeResolver); |
11964 | 0 | } |
11965 | | |
11966 | | already_AddRefed<XPathResult> |
11967 | | nsIDocument::Evaluate(JSContext* aCx, const nsAString& aExpression, |
11968 | | nsINode& aContextNode, XPathNSResolver* aResolver, |
11969 | | uint16_t aType, JS::Handle<JSObject*> aResult, |
11970 | | ErrorResult& rv) |
11971 | 0 | { |
11972 | 0 | return XPathEvaluator()->Evaluate(aCx, aExpression, aContextNode, aResolver, |
11973 | 0 | aType, aResult, rv); |
11974 | 0 | } |
11975 | | |
11976 | | already_AddRefed<nsIXULWindow> |
11977 | | nsIDocument::GetXULWindowIfToplevelChrome() const |
11978 | 0 | { |
11979 | 0 | nsCOMPtr<nsIDocShellTreeItem> item = GetDocShell(); |
11980 | 0 | if (!item) { |
11981 | 0 | return nullptr; |
11982 | 0 | } |
11983 | 0 | nsCOMPtr<nsIDocShellTreeOwner> owner; |
11984 | 0 | item->GetTreeOwner(getter_AddRefs(owner)); |
11985 | 0 | nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(owner); |
11986 | 0 | if (!xulWin) { |
11987 | 0 | return nullptr; |
11988 | 0 | } |
11989 | 0 | nsCOMPtr<nsIDocShell> xulWinShell; |
11990 | 0 | xulWin->GetDocShell(getter_AddRefs(xulWinShell)); |
11991 | 0 | if (!SameCOMIdentity(xulWinShell, item)) { |
11992 | 0 | return nullptr; |
11993 | 0 | } |
11994 | 0 | return xulWin.forget(); |
11995 | 0 | } |
11996 | | |
11997 | | nsIDocument* |
11998 | | nsIDocument::GetTopLevelContentDocument() |
11999 | 0 | { |
12000 | 0 | nsIDocument* parent; |
12001 | 0 |
|
12002 | 0 | if (!mLoadedAsData) { |
12003 | 0 | parent = this; |
12004 | 0 | } else { |
12005 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject()); |
12006 | 0 | if (!window) { |
12007 | 0 | return nullptr; |
12008 | 0 | } |
12009 | 0 | |
12010 | 0 | parent = window->GetExtantDoc(); |
12011 | 0 | if (!parent) { |
12012 | 0 | return nullptr; |
12013 | 0 | } |
12014 | 0 | } |
12015 | 0 | |
12016 | 0 | do { |
12017 | 0 | if (parent->IsTopLevelContentDocument()) { |
12018 | 0 | break; |
12019 | 0 | } |
12020 | 0 | |
12021 | 0 | // If we ever have a non-content parent before we hit a toplevel content |
12022 | 0 | // parent, then we're never going to find one. Just bail. |
12023 | 0 | if (!parent->IsContentDocument()) { |
12024 | 0 | return nullptr; |
12025 | 0 | } |
12026 | 0 | |
12027 | 0 | nsIDocument* candidate = parent->GetParentDocument(); |
12028 | 0 | parent = static_cast<nsDocument*>(candidate); |
12029 | 0 | } while (parent); |
12030 | 0 |
|
12031 | 0 | return parent; |
12032 | 0 | } |
12033 | | |
12034 | | static bool |
12035 | | MightBeChromeScheme(nsIURI* aURI) |
12036 | 0 | { |
12037 | 0 | MOZ_ASSERT(aURI); |
12038 | 0 | bool isChrome = true; |
12039 | 0 | aURI->SchemeIs("chrome", &isChrome); |
12040 | 0 | return isChrome; |
12041 | 0 | } |
12042 | | |
12043 | | static bool |
12044 | | MightBeAboutOrChromeScheme(nsIURI* aURI) |
12045 | 0 | { |
12046 | 0 | MOZ_ASSERT(aURI); |
12047 | 0 | bool isAbout = true; |
12048 | 0 | aURI->SchemeIs("about", &isAbout); |
12049 | 0 | return isAbout || MightBeChromeScheme(aURI); |
12050 | 0 | } |
12051 | | |
12052 | | void |
12053 | | nsIDocument::PropagateUseCounters(nsIDocument* aParentDocument) |
12054 | 0 | { |
12055 | 0 | MOZ_ASSERT(this != aParentDocument); |
12056 | 0 |
|
12057 | 0 | // Don't count chrome resources, even in the web content. |
12058 | 0 | nsCOMPtr<nsIURI> uri; |
12059 | 0 | NodePrincipal()->GetURI(getter_AddRefs(uri)); |
12060 | 0 | if (!uri || MightBeChromeScheme(uri)) { |
12061 | 0 | return; |
12062 | 0 | } |
12063 | 0 | |
12064 | 0 | // What really matters here is that our use counters get propagated as |
12065 | 0 | // high up in the content document hierarchy as possible. So, |
12066 | 0 | // starting with aParentDocument, we need to find the toplevel content |
12067 | 0 | // document, and propagate our use counters into its |
12068 | 0 | // mChildDocumentUseCounters. |
12069 | 0 | nsIDocument* contentParent = aParentDocument->GetTopLevelContentDocument(); |
12070 | 0 |
|
12071 | 0 | if (!contentParent) { |
12072 | 0 | return; |
12073 | 0 | } |
12074 | 0 | |
12075 | 0 | contentParent->mChildDocumentUseCounters |= mUseCounters; |
12076 | 0 | contentParent->mChildDocumentUseCounters |= mChildDocumentUseCounters; |
12077 | 0 | } |
12078 | | |
12079 | | void |
12080 | | nsIDocument::SetPageUseCounter(UseCounter aUseCounter) |
12081 | 0 | { |
12082 | 0 | // We want to set the use counter on the "page" that owns us; the definition |
12083 | 0 | // of "page" depends on what kind of document we are. See the comments below |
12084 | 0 | // for details. In any event, checking all the conditions below is |
12085 | 0 | // reasonably expensive, so we cache whether we've notified our owning page. |
12086 | 0 | if (mNotifiedPageForUseCounter[aUseCounter]) { |
12087 | 0 | return; |
12088 | 0 | } |
12089 | 0 | mNotifiedPageForUseCounter[aUseCounter] = true; |
12090 | 0 |
|
12091 | 0 | if (mDisplayDocument) { |
12092 | 0 | // If we are a resource document, we won't have a docshell and so we won't |
12093 | 0 | // record any page use counters on this document. Instead, we should |
12094 | 0 | // forward it up to the document that loaded us. |
12095 | 0 | MOZ_ASSERT(!mDocumentContainer); |
12096 | 0 | mDisplayDocument->SetChildDocumentUseCounter(aUseCounter); |
12097 | 0 | return; |
12098 | 0 | } |
12099 | 0 |
|
12100 | 0 | if (IsBeingUsedAsImage()) { |
12101 | 0 | // If this is an SVG image document, we also won't have a docshell. |
12102 | 0 | MOZ_ASSERT(!mDocumentContainer); |
12103 | 0 | return; |
12104 | 0 | } |
12105 | 0 |
|
12106 | 0 | // We only care about use counters in content. If we're already a toplevel |
12107 | 0 | // content document, then we should have already set the use counter on |
12108 | 0 | // ourselves, and we are done. |
12109 | 0 | nsIDocument* contentParent = GetTopLevelContentDocument(); |
12110 | 0 | if (!contentParent) { |
12111 | 0 | return; |
12112 | 0 | } |
12113 | 0 | |
12114 | 0 | if (this == contentParent) { |
12115 | 0 | MOZ_ASSERT(GetUseCounter(aUseCounter)); |
12116 | 0 | return; |
12117 | 0 | } |
12118 | 0 |
|
12119 | 0 | contentParent->SetChildDocumentUseCounter(aUseCounter); |
12120 | 0 | } |
12121 | | |
12122 | | bool |
12123 | | nsIDocument::HasScriptsBlockedBySandbox() |
12124 | 0 | { |
12125 | 0 | return mSandboxFlags & SANDBOXED_SCRIPTS; |
12126 | 0 | } |
12127 | | |
12128 | | bool |
12129 | | nsIDocument::InlineScriptAllowedByCSP() |
12130 | 0 | { |
12131 | 0 | // this function assumes the inline script is parser created |
12132 | 0 | // (e.g., before setting attribute(!) event handlers) |
12133 | 0 | nsCOMPtr<nsIContentSecurityPolicy> csp; |
12134 | 0 | nsresult rv = NodePrincipal()->GetCsp(getter_AddRefs(csp)); |
12135 | 0 | NS_ENSURE_SUCCESS(rv, true); |
12136 | 0 | bool allowsInlineScript = true; |
12137 | 0 | if (csp) { |
12138 | 0 | nsresult rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT, |
12139 | 0 | EmptyString(), // aNonce |
12140 | 0 | true, // aParserCreated |
12141 | 0 | nullptr, // aTriggeringElement |
12142 | 0 | EmptyString(), // FIXME get script sample (bug 1314567) |
12143 | 0 | 0, // aLineNumber |
12144 | 0 | 0, // aColumnNumber |
12145 | 0 | &allowsInlineScript); |
12146 | 0 | NS_ENSURE_SUCCESS(rv, true); |
12147 | 0 | } |
12148 | 0 | return allowsInlineScript; |
12149 | 0 | } |
12150 | | |
12151 | | static bool |
12152 | | ReportExternalResourceUseCounters(nsIDocument* aDocument, void* aData) |
12153 | 0 | { |
12154 | 0 | const auto reportKind |
12155 | 0 | = nsDocument::UseCounterReportKind::eIncludeExternalResources; |
12156 | 0 | static_cast<nsDocument*>(aDocument)->ReportUseCounters(reportKind); |
12157 | 0 | return true; |
12158 | 0 | } |
12159 | | |
12160 | | void |
12161 | | nsIDocument::ReportUseCounters(UseCounterReportKind aKind) |
12162 | 0 | { |
12163 | 0 | static const bool sDebugUseCounters = false; |
12164 | 0 | if (mReportedUseCounters) { |
12165 | 0 | return; |
12166 | 0 | } |
12167 | 0 | |
12168 | 0 | mReportedUseCounters = true; |
12169 | 0 |
|
12170 | 0 | if (aKind == UseCounterReportKind::eIncludeExternalResources) { |
12171 | 0 | EnumerateExternalResources(ReportExternalResourceUseCounters, nullptr); |
12172 | 0 | } |
12173 | 0 |
|
12174 | 0 | if (Telemetry::HistogramUseCounterCount > 0 && |
12175 | 0 | (IsContentDocument() || IsResourceDoc())) { |
12176 | 0 | nsCOMPtr<nsIURI> uri; |
12177 | 0 | NodePrincipal()->GetURI(getter_AddRefs(uri)); |
12178 | 0 | if (!uri || MightBeAboutOrChromeScheme(uri)) { |
12179 | 0 | return; |
12180 | 0 | } |
12181 | 0 | |
12182 | 0 | if (sDebugUseCounters) { |
12183 | 0 | nsCString spec = uri->GetSpecOrDefault(); |
12184 | 0 |
|
12185 | 0 | // URIs can be rather long for data documents, so truncate them to |
12186 | 0 | // some reasonable length. |
12187 | 0 | spec.Truncate(std::min(128U, spec.Length())); |
12188 | 0 | printf("-- Use counters for %s --\n", spec.get()); |
12189 | 0 | } |
12190 | 0 |
|
12191 | 0 | // We keep separate counts for individual documents and top-level |
12192 | 0 | // pages to more accurately track how many web pages might break if |
12193 | 0 | // certain features were removed. Consider the case of a single |
12194 | 0 | // HTML document with several SVG images and/or iframes with |
12195 | 0 | // sub-documents of their own. If we maintained a single set of use |
12196 | 0 | // counters and all the sub-documents use a particular feature, then |
12197 | 0 | // telemetry would indicate that we would be breaking N documents if |
12198 | 0 | // that feature were removed. Whereas with a document/top-level |
12199 | 0 | // page split, we can see that N documents would be affected, but |
12200 | 0 | // only a single web page would be affected. |
12201 | 0 |
|
12202 | 0 | // The difference between the values of these two histograms and the |
12203 | 0 | // related use counters below tell us how many pages did *not* use |
12204 | 0 | // the feature in question. For instance, if we see that a given |
12205 | 0 | // session has destroyed 30 content documents, but a particular use |
12206 | 0 | // counter shows only a count of 5, we can infer that the use |
12207 | 0 | // counter was *not* used in 25 of those 30 documents. |
12208 | 0 | // |
12209 | 0 | // We do things this way, rather than accumulating a boolean flag |
12210 | 0 | // for each use counter, to avoid sending histograms for features |
12211 | 0 | // that don't get widely used. Doing things in this fashion means |
12212 | 0 | // smaller telemetry payloads and faster processing on the server |
12213 | 0 | // side. |
12214 | 0 | Telemetry::Accumulate(Telemetry::CONTENT_DOCUMENTS_DESTROYED, 1); |
12215 | 0 | if (IsTopLevelContentDocument()) { |
12216 | 0 | Telemetry::Accumulate(Telemetry::TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED, 1); |
12217 | 0 | } |
12218 | 0 |
|
12219 | 0 | for (int32_t c = 0; |
12220 | 0 | c < eUseCounter_Count; ++c) { |
12221 | 0 | UseCounter uc = static_cast<UseCounter>(c); |
12222 | 0 |
|
12223 | 0 | Telemetry::HistogramID id = |
12224 | 0 | static_cast<Telemetry::HistogramID>(Telemetry::HistogramFirstUseCounter + uc * 2); |
12225 | 0 | bool value = GetUseCounter(uc); |
12226 | 0 |
|
12227 | 0 | if (value) { |
12228 | 0 | if (sDebugUseCounters) { |
12229 | 0 | const char* name = Telemetry::GetHistogramName(id); |
12230 | 0 | if (name) { |
12231 | 0 | printf(" %s", name); |
12232 | 0 | } else { |
12233 | 0 | printf(" #%d", id); |
12234 | 0 | } |
12235 | 0 | printf(": %d\n", value); |
12236 | 0 | } |
12237 | 0 |
|
12238 | 0 | Telemetry::Accumulate(id, 1); |
12239 | 0 | } |
12240 | 0 |
|
12241 | 0 | if (IsTopLevelContentDocument()) { |
12242 | 0 | id = static_cast<Telemetry::HistogramID>(Telemetry::HistogramFirstUseCounter + |
12243 | 0 | uc * 2 + 1); |
12244 | 0 | value = GetUseCounter(uc) || GetChildDocumentUseCounter(uc); |
12245 | 0 |
|
12246 | 0 | if (value) { |
12247 | 0 | if (sDebugUseCounters) { |
12248 | 0 | const char* name = Telemetry::GetHistogramName(id); |
12249 | 0 | if (name) { |
12250 | 0 | printf(" %s", name); |
12251 | 0 | } else { |
12252 | 0 | printf(" #%d", id); |
12253 | 0 | } |
12254 | 0 | printf(": %d\n", value); |
12255 | 0 | } |
12256 | 0 |
|
12257 | 0 | Telemetry::Accumulate(id, 1); |
12258 | 0 | } |
12259 | 0 | } |
12260 | 0 | } |
12261 | 0 | } |
12262 | 0 |
|
12263 | 0 | if (IsTopLevelContentDocument() && !IsResourceDoc()) { |
12264 | 0 | using mozilla::Telemetry::LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE; |
12265 | 0 | LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE label; |
12266 | 0 | switch (mViewportOverflowType) { |
12267 | 0 | #define CASE_OVERFLOW_TYPE(t_) \ |
12268 | 0 | case ViewportOverflowType::t_: \ |
12269 | 0 | label = LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE::t_; \ |
12270 | 0 | break; |
12271 | 0 | CASE_OVERFLOW_TYPE(NoOverflow) |
12272 | 0 | CASE_OVERFLOW_TYPE(Desktop) |
12273 | 0 | CASE_OVERFLOW_TYPE(ButNotMinScaleSize) |
12274 | 0 | CASE_OVERFLOW_TYPE(MinScaleSize) |
12275 | 0 | #undef CASE_OVERFLOW_TYPE |
12276 | 0 | } |
12277 | 0 | Telemetry::AccumulateCategorical(label); |
12278 | 0 | } |
12279 | 0 | } |
12280 | | |
12281 | | void |
12282 | | nsIDocument::UpdateIntersectionObservations() |
12283 | 0 | { |
12284 | 0 | if (mIntersectionObservers.IsEmpty()) { |
12285 | 0 | return; |
12286 | 0 | } |
12287 | 0 | |
12288 | 0 | DOMHighResTimeStamp time = 0; |
12289 | 0 | if (nsPIDOMWindowInner* window = GetInnerWindow()) { |
12290 | 0 | Performance* perf = window->GetPerformance(); |
12291 | 0 | if (perf) { |
12292 | 0 | time = perf->Now(); |
12293 | 0 | } |
12294 | 0 | } |
12295 | 0 | nsTArray<RefPtr<DOMIntersectionObserver>> observers(mIntersectionObservers.Count()); |
12296 | 0 | for (auto iter = mIntersectionObservers.Iter(); !iter.Done(); iter.Next()) { |
12297 | 0 | DOMIntersectionObserver* observer = iter.Get()->GetKey(); |
12298 | 0 | observers.AppendElement(observer); |
12299 | 0 | } |
12300 | 0 | for (const auto& observer : observers) { |
12301 | 0 | if (observer) { |
12302 | 0 | observer->Update(this, time); |
12303 | 0 | } |
12304 | 0 | } |
12305 | 0 | } |
12306 | | |
12307 | | void |
12308 | | nsIDocument::ScheduleIntersectionObserverNotification() |
12309 | 0 | { |
12310 | 0 | if (mIntersectionObservers.IsEmpty()) { |
12311 | 0 | return; |
12312 | 0 | } |
12313 | 0 | MOZ_RELEASE_ASSERT(NS_IsMainThread()); |
12314 | 0 | nsCOMPtr<nsIRunnable> notification = |
12315 | 0 | NewRunnableMethod("nsDocument::NotifyIntersectionObservers", |
12316 | 0 | this, |
12317 | 0 | &nsDocument::NotifyIntersectionObservers); |
12318 | 0 | Dispatch(TaskCategory::Other, notification.forget()); |
12319 | 0 | } |
12320 | | |
12321 | | void |
12322 | | nsIDocument::NotifyIntersectionObservers() |
12323 | 0 | { |
12324 | 0 | nsTArray<RefPtr<DOMIntersectionObserver>> observers(mIntersectionObservers.Count()); |
12325 | 0 | for (auto iter = mIntersectionObservers.Iter(); !iter.Done(); iter.Next()) { |
12326 | 0 | DOMIntersectionObserver* observer = iter.Get()->GetKey(); |
12327 | 0 | observers.AppendElement(observer); |
12328 | 0 | } |
12329 | 0 | for (const auto& observer : observers) { |
12330 | 0 | if (observer) { |
12331 | 0 | observer->Notify(); |
12332 | 0 | } |
12333 | 0 | } |
12334 | 0 | } |
12335 | | |
12336 | | static bool |
12337 | | NotifyLayerManagerRecreatedCallback(nsIDocument* aDocument, void* aData) |
12338 | 0 | { |
12339 | 0 | aDocument->NotifyLayerManagerRecreated(); |
12340 | 0 | return true; |
12341 | 0 | } |
12342 | | |
12343 | | void |
12344 | | nsIDocument::NotifyLayerManagerRecreated() |
12345 | 0 | { |
12346 | 0 | EnumerateActivityObservers(NotifyActivityChanged, nullptr); |
12347 | 0 | EnumerateSubDocuments(NotifyLayerManagerRecreatedCallback, nullptr); |
12348 | 0 | } |
12349 | | |
12350 | | XPathEvaluator* |
12351 | | nsIDocument::XPathEvaluator() |
12352 | 0 | { |
12353 | 0 | if (!mXPathEvaluator) { |
12354 | 0 | mXPathEvaluator.reset(new dom::XPathEvaluator(this)); |
12355 | 0 | } |
12356 | 0 | return mXPathEvaluator.get(); |
12357 | 0 | } |
12358 | | |
12359 | | already_AddRefed<nsIDocumentEncoder> |
12360 | | nsIDocument::GetCachedEncoder() |
12361 | 0 | { |
12362 | 0 | return mCachedEncoder.forget(); |
12363 | 0 | } |
12364 | | |
12365 | | void |
12366 | | nsIDocument::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder) |
12367 | 0 | { |
12368 | 0 | mCachedEncoder = aEncoder; |
12369 | 0 | } |
12370 | | |
12371 | | void |
12372 | | nsIDocument::SetContentTypeInternal(const nsACString& aType) |
12373 | 0 | { |
12374 | 0 | if (!IsHTMLOrXHTML() && mDefaultElementType == kNameSpaceID_None && |
12375 | 0 | aType.EqualsLiteral("application/xhtml+xml")) { |
12376 | 0 | mDefaultElementType = kNameSpaceID_XHTML; |
12377 | 0 | } |
12378 | 0 |
|
12379 | 0 | mCachedEncoder = nullptr; |
12380 | 0 | mContentType = aType; |
12381 | 0 | mContentTypeForWriteCalls = aType; |
12382 | 0 | } |
12383 | | |
12384 | | nsILoadContext* |
12385 | | nsIDocument::GetLoadContext() const |
12386 | 0 | { |
12387 | 0 | return mDocumentContainer; |
12388 | 0 | } |
12389 | | |
12390 | | nsIDocShell* |
12391 | | nsIDocument::GetDocShell() const |
12392 | 0 | { |
12393 | 0 | return mDocumentContainer; |
12394 | 0 | } |
12395 | | |
12396 | | void |
12397 | | nsIDocument::SetStateObject(nsIStructuredCloneContainer *scContainer) |
12398 | 0 | { |
12399 | 0 | mStateObjectContainer = scContainer; |
12400 | 0 | mStateObjectCached = nullptr; |
12401 | 0 | } |
12402 | | |
12403 | | nsIDocument::DocumentTheme |
12404 | | nsIDocument::GetDocumentLWTheme() |
12405 | 0 | { |
12406 | 0 | if (mDocLWTheme == Doc_Theme_Uninitialized) { |
12407 | 0 | mDocLWTheme = ThreadSafeGetDocumentLWTheme(); |
12408 | 0 | } |
12409 | 0 | return mDocLWTheme; |
12410 | 0 | } |
12411 | | |
12412 | | nsIDocument::DocumentTheme |
12413 | | nsIDocument::ThreadSafeGetDocumentLWTheme() const |
12414 | 0 | { |
12415 | 0 | if (!nsContentUtils::IsSystemPrincipal(NodePrincipal())) { |
12416 | 0 | return Doc_Theme_None; |
12417 | 0 | } |
12418 | 0 | |
12419 | 0 | if (mDocLWTheme != Doc_Theme_Uninitialized) { |
12420 | 0 | return mDocLWTheme; |
12421 | 0 | } |
12422 | 0 | |
12423 | 0 | DocumentTheme theme = Doc_Theme_None; // No lightweight theme by default |
12424 | 0 | Element* element = GetRootElement(); |
12425 | 0 | if (element && element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::lwtheme, |
12426 | 0 | nsGkAtoms::_true, eCaseMatters)) { |
12427 | 0 | theme = Doc_Theme_Neutral; |
12428 | 0 | nsAutoString lwTheme; |
12429 | 0 | element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwthemetextcolor, lwTheme); |
12430 | 0 | if (lwTheme.EqualsLiteral("dark")) { |
12431 | 0 | theme = Doc_Theme_Dark; |
12432 | 0 | } else if (lwTheme.EqualsLiteral("bright")) { |
12433 | 0 | theme = Doc_Theme_Bright; |
12434 | 0 | } |
12435 | 0 | } |
12436 | 0 |
|
12437 | 0 | return theme; |
12438 | 0 | } |
12439 | | |
12440 | | already_AddRefed<Element> |
12441 | | nsIDocument::CreateHTMLElement(nsAtom* aTag) |
12442 | 0 | { |
12443 | 0 | RefPtr<mozilla::dom::NodeInfo> nodeInfo; |
12444 | 0 | nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML, |
12445 | 0 | ELEMENT_NODE); |
12446 | 0 | MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail"); |
12447 | 0 |
|
12448 | 0 | nsCOMPtr<Element> element; |
12449 | 0 | DebugOnly<nsresult> rv = NS_NewHTMLElement(getter_AddRefs(element), |
12450 | 0 | nodeInfo.forget(), |
12451 | 0 | mozilla::dom::NOT_FROM_PARSER); |
12452 | 0 |
|
12453 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail"); |
12454 | 0 | return element.forget(); |
12455 | 0 | } |
12456 | | |
12457 | | bool |
12458 | | MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData) |
12459 | 0 | { |
12460 | 0 | nsCOMArray<nsIDocument>* documents = |
12461 | 0 | static_cast<nsCOMArray<nsIDocument>*>(aData); |
12462 | 0 | if (aDoc) { |
12463 | 0 | aDoc->SetIsInSyncOperation(true); |
12464 | 0 | if (nsCOMPtr<nsPIDOMWindowInner> window = aDoc->GetInnerWindow()) { |
12465 | 0 | window->TimeoutManager().BeginSyncOperation(); |
12466 | 0 | } |
12467 | 0 | documents->AppendObject(aDoc); |
12468 | 0 | aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData); |
12469 | 0 | } |
12470 | 0 | return true; |
12471 | 0 | } |
12472 | | |
12473 | | nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc) |
12474 | 0 | { |
12475 | 0 | mMicroTaskLevel = 0; |
12476 | 0 | CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); |
12477 | 0 | if (ccjs) { |
12478 | 0 | mMicroTaskLevel = ccjs->MicroTaskLevel(); |
12479 | 0 | ccjs->SetMicroTaskLevel(0); |
12480 | 0 | } |
12481 | 0 | if (aDoc) { |
12482 | 0 | if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) { |
12483 | 0 | if (nsCOMPtr<nsPIDOMWindowOuter> top = win->GetTop()) { |
12484 | 0 | nsCOMPtr<nsIDocument> doc = top->GetExtantDoc(); |
12485 | 0 | MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments); |
12486 | 0 | } |
12487 | 0 | } |
12488 | 0 | } |
12489 | 0 | } |
12490 | | |
12491 | | nsAutoSyncOperation::~nsAutoSyncOperation() |
12492 | 0 | { |
12493 | 0 | for (int32_t i = 0; i < mDocuments.Count(); ++i) { |
12494 | 0 | if (nsCOMPtr<nsPIDOMWindowInner> window = mDocuments[i]->GetInnerWindow()) { |
12495 | 0 | window->TimeoutManager().EndSyncOperation(); |
12496 | 0 | } |
12497 | 0 | mDocuments[i]->SetIsInSyncOperation(false); |
12498 | 0 | } |
12499 | 0 | CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); |
12500 | 0 | if (ccjs) { |
12501 | 0 | ccjs->SetMicroTaskLevel(mMicroTaskLevel); |
12502 | 0 | } |
12503 | 0 | } |
12504 | | |
12505 | | gfxUserFontSet* |
12506 | | nsIDocument::GetUserFontSet(bool aFlushUserFontSet) |
12507 | 0 | { |
12508 | 0 | // We want to initialize the user font set lazily the first time the |
12509 | 0 | // user asks for it, rather than building it too early and forcing |
12510 | 0 | // rule cascade creation. Thus we try to enforce the invariant that |
12511 | 0 | // we *never* build the user font set until the first call to |
12512 | 0 | // GetUserFontSet. However, once it's been requested, we can't wait |
12513 | 0 | // for somebody to call GetUserFontSet in order to rebuild it (see |
12514 | 0 | // comments below in MarkUserFontSetDirty for why). |
12515 | | #ifdef DEBUG |
12516 | | bool userFontSetGottenBefore = mGetUserFontSetCalled; |
12517 | | #endif |
12518 | | // Set mGetUserFontSetCalled up front, so that FlushUserFontSet will actually |
12519 | 0 | // flush. |
12520 | 0 | mGetUserFontSetCalled = true; |
12521 | 0 | if (mFontFaceSetDirty && aFlushUserFontSet) { |
12522 | 0 | // If this assertion fails, and there have actually been changes to |
12523 | 0 | // @font-face rules, then we will call StyleChangeReflow in |
12524 | 0 | // FlushUserFontSet. If we're in the middle of reflow, |
12525 | 0 | // that's a bad thing to do, and the caller was responsible for |
12526 | 0 | // flushing first. If we're not (e.g., in frame construction), it's |
12527 | 0 | // ok. |
12528 | 0 | NS_ASSERTION(!userFontSetGottenBefore || |
12529 | 0 | !GetShell() || |
12530 | 0 | !GetShell()->IsReflowLocked(), |
12531 | 0 | "FlushUserFontSet should have been called first"); |
12532 | 0 | FlushUserFontSet(); |
12533 | 0 | } |
12534 | 0 |
|
12535 | 0 | if (!mFontFaceSet) { |
12536 | 0 | return nullptr; |
12537 | 0 | } |
12538 | 0 | |
12539 | 0 | return mFontFaceSet->GetUserFontSet(); |
12540 | 0 | } |
12541 | | |
12542 | | void |
12543 | | nsIDocument::FlushUserFontSet() |
12544 | 0 | { |
12545 | 0 | if (!mGetUserFontSetCalled) { |
12546 | 0 | return; // No one cares about this font set yet, but we want to be careful |
12547 | 0 | // to not unset our mFontFaceSetDirty bit, so when someone really |
12548 | 0 | // does we'll create it. |
12549 | 0 | } |
12550 | 0 | |
12551 | 0 | if (!mFontFaceSetDirty) { |
12552 | 0 | return; |
12553 | 0 | } |
12554 | 0 | |
12555 | 0 | mFontFaceSetDirty = false; |
12556 | 0 |
|
12557 | 0 | if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) { |
12558 | 0 | nsTArray<nsFontFaceRuleContainer> rules; |
12559 | 0 | nsIPresShell* shell = GetShell(); |
12560 | 0 | if (shell && !shell->StyleSet()->AppendFontFaceRules(rules)) { |
12561 | 0 | return; |
12562 | 0 | } |
12563 | 0 | |
12564 | 0 | |
12565 | 0 | if (!mFontFaceSet && !rules.IsEmpty()) { |
12566 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject()); |
12567 | 0 | mFontFaceSet = new FontFaceSet(window, this); |
12568 | 0 | } |
12569 | 0 |
|
12570 | 0 | bool changed = false; |
12571 | 0 | if (mFontFaceSet) { |
12572 | 0 | changed = mFontFaceSet->UpdateRules(rules); |
12573 | 0 | } |
12574 | 0 |
|
12575 | 0 | // We need to enqueue a style change reflow (for later) to |
12576 | 0 | // reflect that we're modifying @font-face rules. (However, |
12577 | 0 | // without a reflow, nothing will happen to start any downloads |
12578 | 0 | // that are needed.) |
12579 | 0 | if (changed && shell) { |
12580 | 0 | if (nsPresContext* presContext = shell->GetPresContext()) { |
12581 | 0 | presContext->UserFontSetUpdated(); |
12582 | 0 | } |
12583 | 0 | } |
12584 | 0 | } |
12585 | 0 | } |
12586 | | |
12587 | | void |
12588 | | nsIDocument::MarkUserFontSetDirty() |
12589 | 0 | { |
12590 | 0 | if (!mGetUserFontSetCalled) { |
12591 | 0 | // We want to lazily build the user font set the first time it's |
12592 | 0 | // requested (so we don't force creation of rule cascades too |
12593 | 0 | // early), so don't do anything now. |
12594 | 0 | return; |
12595 | 0 | } |
12596 | 0 | |
12597 | 0 | mFontFaceSetDirty = true; |
12598 | 0 | } |
12599 | | |
12600 | | FontFaceSet* |
12601 | | nsIDocument::Fonts() |
12602 | 0 | { |
12603 | 0 | if (!mFontFaceSet) { |
12604 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject()); |
12605 | 0 | mFontFaceSet = new FontFaceSet(window, this); |
12606 | 0 | GetUserFontSet(); // this will cause the user font set to be created/updated |
12607 | 0 | } |
12608 | 0 | return mFontFaceSet; |
12609 | 0 | } |
12610 | | |
12611 | | void |
12612 | | nsIDocument::ReportHasScrollLinkedEffect() |
12613 | 0 | { |
12614 | 0 | if (mHasScrollLinkedEffect) { |
12615 | 0 | // We already did this once for this document, don't do it again. |
12616 | 0 | return; |
12617 | 0 | } |
12618 | 0 | mHasScrollLinkedEffect = true; |
12619 | 0 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
12620 | 0 | NS_LITERAL_CSTRING("Async Pan/Zoom"), |
12621 | 0 | this, nsContentUtils::eLAYOUT_PROPERTIES, |
12622 | 0 | "ScrollLinkedEffectFound2"); |
12623 | 0 | } |
12624 | | |
12625 | | void |
12626 | | nsIDocument::SetUserHasInteracted() |
12627 | 0 | { |
12628 | 0 | MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, |
12629 | 0 | ("Document %p has been interacted by user.", this)); |
12630 | 0 |
|
12631 | 0 | // We maybe need to update the user-interaction permission. |
12632 | 0 | MaybeStoreUserInteractionAsPermission(); |
12633 | 0 |
|
12634 | 0 | if (mUserHasInteracted) { |
12635 | 0 | return; |
12636 | 0 | } |
12637 | 0 | |
12638 | 0 | mUserHasInteracted = true; |
12639 | 0 |
|
12640 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->GetLoadInfo() : nullptr; |
12641 | 0 | if (loadInfo) { |
12642 | 0 | loadInfo->SetDocumentHasUserInteracted(true); |
12643 | 0 | } |
12644 | 0 |
|
12645 | 0 | MaybeAllowStorageForOpener(); |
12646 | 0 | } |
12647 | | |
12648 | | void |
12649 | | nsIDocument::NotifyUserGestureActivation() |
12650 | 0 | { |
12651 | 0 | // Activate this document and all documents up to the top level |
12652 | 0 | // content document. |
12653 | 0 | nsIDocument* doc = this; |
12654 | 0 | while (doc && !doc->mUserGestureActivated) { |
12655 | 0 | MOZ_LOG(gUserInteractionPRLog, |
12656 | 0 | LogLevel::Debug, |
12657 | 0 | ("Document %p has been activated by user.", this)); |
12658 | 0 | doc->mUserGestureActivated = true; |
12659 | 0 | doc = doc->GetSameTypeParentDocument(); |
12660 | 0 | } |
12661 | 0 | } |
12662 | | |
12663 | | void |
12664 | | nsIDocument::SetDocTreeHadAudibleMedia() |
12665 | 0 | { |
12666 | 0 | nsIDocument* topLevelDoc = GetTopLevelContentDocument(); |
12667 | 0 | if (!topLevelDoc) { |
12668 | 0 | return; |
12669 | 0 | } |
12670 | 0 | |
12671 | 0 | if (!topLevelDoc->mDocTreeHadAudibleMedia) { |
12672 | 0 | RefPtr<AsyncEventDispatcher> asyncDispatcher = |
12673 | 0 | new AsyncEventDispatcher(topLevelDoc, |
12674 | 0 | NS_LITERAL_STRING("AudibleAutoplayMediaOccurred"), |
12675 | 0 | CanBubble::eYes, |
12676 | 0 | ChromeOnlyDispatch::eYes); |
12677 | 0 | asyncDispatcher->PostDOMEvent(); |
12678 | 0 | } |
12679 | 0 | topLevelDoc->mDocTreeHadAudibleMedia = true; |
12680 | 0 | } |
12681 | | |
12682 | | void |
12683 | | nsIDocument::SetDocTreeHadPlayRevoked() |
12684 | 0 | { |
12685 | 0 | nsIDocument* topLevelDoc = GetTopLevelContentDocument(); |
12686 | 0 | if (topLevelDoc) { |
12687 | 0 | topLevelDoc->mDocTreeHadPlayRevoked = true; |
12688 | 0 | } |
12689 | 0 | } |
12690 | | |
12691 | | void |
12692 | | nsIDocument::MaybeAllowStorageForOpener() |
12693 | 0 | { |
12694 | 0 | if (StaticPrefs::network_cookie_cookieBehavior() != |
12695 | 0 | nsICookieService::BEHAVIOR_REJECT_TRACKER || |
12696 | 0 | !AntiTrackingCommon::ShouldHonorContentBlockingCookieRestrictions()) { |
12697 | 0 | return; |
12698 | 0 | } |
12699 | 0 | |
12700 | 0 | // This will probably change for project fission, but currently this document |
12701 | 0 | // and the opener are on the same process. In the future, we should make this |
12702 | 0 | // part async. |
12703 | 0 | |
12704 | 0 | nsPIDOMWindowInner* inner = GetInnerWindow(); |
12705 | 0 | if (NS_WARN_IF(!inner)) { |
12706 | 0 | return; |
12707 | 0 | } |
12708 | 0 | |
12709 | 0 | nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow(); |
12710 | 0 | if (NS_WARN_IF(!outer)) { |
12711 | 0 | return; |
12712 | 0 | } |
12713 | 0 | |
12714 | 0 | nsCOMPtr<nsPIDOMWindowOuter> outerOpener = outer->GetOpener(); |
12715 | 0 | if (!outerOpener) { |
12716 | 0 | return; |
12717 | 0 | } |
12718 | 0 | |
12719 | 0 | nsPIDOMWindowInner* openerInner = outerOpener->GetCurrentInnerWindow(); |
12720 | 0 | if (NS_WARN_IF(!openerInner)) { |
12721 | 0 | return; |
12722 | 0 | } |
12723 | 0 | |
12724 | 0 | // Let's take the principal from the opener. |
12725 | 0 | nsIDocument* openerDocument = openerInner->GetExtantDoc(); |
12726 | 0 | if (NS_WARN_IF(!openerDocument)) { |
12727 | 0 | return; |
12728 | 0 | } |
12729 | 0 | |
12730 | 0 | nsCOMPtr<nsIURI> openerURI = openerDocument->GetDocumentURI(); |
12731 | 0 | if (NS_WARN_IF(!openerURI)) { |
12732 | 0 | return; |
12733 | 0 | } |
12734 | 0 | |
12735 | 0 | // No tracking resource. |
12736 | 0 | if (!nsContentUtils::IsTrackingResourceWindow(inner)) { |
12737 | 0 | return; |
12738 | 0 | } |
12739 | 0 | |
12740 | 0 | // If the opener is not a 3rd party and if this window is not a 3rd party, we |
12741 | 0 | // should not continue. |
12742 | 0 | if (!nsContentUtils::IsThirdPartyWindowOrChannel(inner, nullptr, openerURI) && |
12743 | 0 | !nsContentUtils::IsThirdPartyWindowOrChannel(openerInner, nullptr, |
12744 | 0 | nullptr)) { |
12745 | 0 | return; |
12746 | 0 | } |
12747 | 0 | |
12748 | 0 | nsCOMPtr<nsIURI> uri = GetDocumentURI(); |
12749 | 0 | if (NS_WARN_IF(!uri)) { |
12750 | 0 | return; |
12751 | 0 | } |
12752 | 0 | |
12753 | 0 | nsAutoString origin; |
12754 | 0 | nsresult rv = nsContentUtils::GetUTFOrigin(uri, origin); |
12755 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
12756 | 0 | return; |
12757 | 0 | } |
12758 | 0 | |
12759 | 0 | // We don't care when the asynchronous work finishes here. |
12760 | 0 | Unused << AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(origin, |
12761 | 0 | openerInner, |
12762 | 0 | AntiTrackingCommon::eHeuristic); |
12763 | 0 | } |
12764 | | |
12765 | | namespace { |
12766 | | |
12767 | | // Documents can stay alive for days. We don't want to update the permission |
12768 | | // value at any user-interaction, and, using a timer triggered any X seconds |
12769 | | // should be good enough. 'X' is taken from |
12770 | | // privacy.userInteraction.document.interval pref. |
12771 | | // We also want to store the user-interaction before shutting down, and, for |
12772 | | // this reason, this class implements nsIAsyncShutdownBlocker interface. |
12773 | | class UserIntractionTimer final : public Runnable |
12774 | | , public nsITimerCallback |
12775 | | , public nsIAsyncShutdownBlocker |
12776 | | { |
12777 | | public: |
12778 | | NS_DECL_ISUPPORTS_INHERITED |
12779 | | |
12780 | | explicit UserIntractionTimer(nsIDocument* aDocument) |
12781 | | : Runnable("UserIntractionTimer") |
12782 | | , mPrincipal(aDocument->NodePrincipal()) |
12783 | | , mDocument(do_GetWeakReference(aDocument)) |
12784 | 0 | { |
12785 | 0 | static int32_t userInteractionTimerId = 0; |
12786 | 0 | // Blocker names must be unique. Let's create it now because when needed, |
12787 | 0 | // the document could be already gone. |
12788 | 0 | mBlockerName.AppendPrintf("UserInteractionTimer %d for document %p", |
12789 | 0 | ++userInteractionTimerId, aDocument); |
12790 | 0 | } |
12791 | | |
12792 | | // Runnable interface |
12793 | | |
12794 | | NS_IMETHOD |
12795 | | Run() override |
12796 | 0 | { |
12797 | 0 | uint32_t interval = |
12798 | 0 | StaticPrefs::privacy_userInteraction_document_interval(); |
12799 | 0 | if (!interval) { |
12800 | 0 | return NS_OK; |
12801 | 0 | } |
12802 | 0 | |
12803 | 0 | RefPtr<UserIntractionTimer> self = this; |
12804 | 0 | auto raii = MakeScopeExit([self] { |
12805 | 0 | self->CancelTimerAndStoreUserInteraction(); |
12806 | 0 | }); |
12807 | 0 |
|
12808 | 0 | nsresult rv = NS_NewTimerWithCallback(getter_AddRefs(mTimer), |
12809 | 0 | this, interval * 1000, |
12810 | 0 | nsITimer::TYPE_ONE_SHOT, |
12811 | 0 | SystemGroup::EventTargetFor(TaskCategory::Other)); |
12812 | 0 | NS_ENSURE_SUCCESS(rv, NS_OK); |
12813 | 0 |
|
12814 | 0 | nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase(); |
12815 | 0 | NS_ENSURE_TRUE(!!phase, NS_OK); |
12816 | 0 |
|
12817 | 0 | rv = phase->AddBlocker(this, NS_LITERAL_STRING(__FILE__), __LINE__, |
12818 | 0 | NS_LITERAL_STRING("UserIntractionTimer shutdown")); |
12819 | 0 | NS_ENSURE_SUCCESS(rv, NS_OK); |
12820 | 0 |
|
12821 | 0 | raii.release(); |
12822 | 0 | return NS_OK; |
12823 | 0 | } |
12824 | | |
12825 | | // nsITimerCallback interface |
12826 | | |
12827 | | NS_IMETHOD |
12828 | | Notify(nsITimer* aTimer) override |
12829 | 0 | { |
12830 | 0 | StoreUserInteraction(); |
12831 | 0 | return NS_OK; |
12832 | 0 | } |
12833 | | |
12834 | | #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY |
12835 | | using nsINamed::GetName; |
12836 | | #endif |
12837 | | |
12838 | | // nsIAsyncShutdownBlocker interface |
12839 | | |
12840 | | NS_IMETHOD |
12841 | | GetName(nsAString& aName) override |
12842 | 0 | { |
12843 | 0 | aName = mBlockerName; |
12844 | 0 | return NS_OK; |
12845 | 0 | } |
12846 | | |
12847 | | NS_IMETHOD |
12848 | | BlockShutdown(nsIAsyncShutdownClient* aClient) override |
12849 | 0 | { |
12850 | 0 | CancelTimerAndStoreUserInteraction(); |
12851 | 0 | return NS_OK; |
12852 | 0 | } |
12853 | | |
12854 | | NS_IMETHOD |
12855 | | GetState(nsIPropertyBag**) override |
12856 | 0 | { |
12857 | 0 | return NS_OK; |
12858 | 0 | } |
12859 | | |
12860 | | private: |
12861 | 0 | ~UserIntractionTimer() = default; |
12862 | | |
12863 | | void |
12864 | | StoreUserInteraction() |
12865 | 0 | { |
12866 | 0 | // Remove the shutting down blocker |
12867 | 0 | nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase(); |
12868 | 0 | if (phase) { |
12869 | 0 | phase->RemoveBlocker(this); |
12870 | 0 | } |
12871 | 0 |
|
12872 | 0 | // If the document is not gone, let's reset its timer flag. |
12873 | 0 | nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocument); |
12874 | 0 | if (document) { |
12875 | 0 | AntiTrackingCommon::StoreUserInteractionFor(mPrincipal); |
12876 | 0 | document->ResetUserInteractionTimer(); |
12877 | 0 | } |
12878 | 0 | } |
12879 | | |
12880 | | void |
12881 | | CancelTimerAndStoreUserInteraction() |
12882 | 0 | { |
12883 | 0 | if (mTimer) { |
12884 | 0 | mTimer->Cancel(); |
12885 | 0 | mTimer = nullptr; |
12886 | 0 | } |
12887 | 0 |
|
12888 | 0 | StoreUserInteraction(); |
12889 | 0 | } |
12890 | | |
12891 | | static already_AddRefed<nsIAsyncShutdownClient> |
12892 | | GetShutdownPhase() |
12893 | 0 | { |
12894 | 0 | nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown(); |
12895 | 0 | NS_ENSURE_TRUE(!!svc, nullptr); |
12896 | 0 |
|
12897 | 0 | nsCOMPtr<nsIAsyncShutdownClient> phase; |
12898 | 0 | nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(phase)); |
12899 | 0 | NS_ENSURE_SUCCESS(rv, nullptr); |
12900 | 0 |
|
12901 | 0 | return phase.forget(); |
12902 | 0 | } |
12903 | | |
12904 | | nsCOMPtr<nsIPrincipal> mPrincipal; |
12905 | | nsWeakPtr mDocument; |
12906 | | |
12907 | | nsCOMPtr<nsITimer> mTimer; |
12908 | | |
12909 | | nsString mBlockerName; |
12910 | | }; |
12911 | | |
12912 | | NS_IMPL_ISUPPORTS_INHERITED(UserIntractionTimer, Runnable, nsITimerCallback, |
12913 | | nsIAsyncShutdownBlocker) |
12914 | | |
12915 | | } // anonymous |
12916 | | |
12917 | | void |
12918 | | nsIDocument::MaybeStoreUserInteractionAsPermission() |
12919 | 0 | { |
12920 | 0 | // We care about user-interaction stored only for top-level documents. |
12921 | 0 | if (GetSameTypeParentDocument()) { |
12922 | 0 | return; |
12923 | 0 | } |
12924 | 0 | |
12925 | 0 | if (!mUserHasInteracted) { |
12926 | 0 | // First interaction, let's store this info now. |
12927 | 0 | AntiTrackingCommon::StoreUserInteractionFor(NodePrincipal()); |
12928 | 0 | return; |
12929 | 0 | } |
12930 | 0 | |
12931 | 0 | if (mHasUserInteractionTimerScheduled) { |
12932 | 0 | return; |
12933 | 0 | } |
12934 | 0 | |
12935 | 0 | nsCOMPtr<nsIRunnable> task = new UserIntractionTimer(this); |
12936 | 0 | nsresult rv = NS_IdleDispatchToCurrentThread(task.forget(), 2500); |
12937 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
12938 | 0 | return; |
12939 | 0 | } |
12940 | 0 | |
12941 | 0 | // This value will be reset by the timer. |
12942 | 0 | mHasUserInteractionTimerScheduled = true; |
12943 | 0 | } |
12944 | | |
12945 | | void |
12946 | | nsIDocument::ResetUserInteractionTimer() |
12947 | 0 | { |
12948 | 0 | mHasUserInteractionTimerScheduled = false; |
12949 | 0 | } |
12950 | | |
12951 | | bool |
12952 | | nsIDocument::HasBeenUserGestureActivated() |
12953 | 0 | { |
12954 | 0 | if (mUserGestureActivated) { |
12955 | 0 | return true; |
12956 | 0 | } |
12957 | 0 | |
12958 | 0 | // If any ancestor document is activated, so are we. |
12959 | 0 | nsIDocument* doc = GetSameTypeParentDocument(); |
12960 | 0 | while (doc) { |
12961 | 0 | if (doc->mUserGestureActivated) { |
12962 | 0 | // An ancestor is also activated. Record activation on the unactivated |
12963 | 0 | // sub-branch to speed up future queries. |
12964 | 0 | NotifyUserGestureActivation(); |
12965 | 0 | break; |
12966 | 0 | } |
12967 | 0 | doc = doc->GetSameTypeParentDocument(); |
12968 | 0 | } |
12969 | 0 |
|
12970 | 0 | return mUserGestureActivated; |
12971 | 0 | } |
12972 | | |
12973 | | bool |
12974 | | nsIDocument::IsExtensionPage() const |
12975 | 0 | { |
12976 | 0 | return Preferences::GetBool("media.autoplay.allow-extension-background-pages", true) && |
12977 | 0 | BasePrincipal::Cast(NodePrincipal())->AddonPolicy(); |
12978 | 0 | } |
12979 | | |
12980 | | nsIDocument* |
12981 | | nsIDocument::GetSameTypeParentDocument() |
12982 | 0 | { |
12983 | 0 | nsCOMPtr<nsIDocShellTreeItem> current = GetDocShell(); |
12984 | 0 | if (!current) { |
12985 | 0 | return nullptr; |
12986 | 0 | } |
12987 | 0 | |
12988 | 0 | nsCOMPtr<nsIDocShellTreeItem> parent; |
12989 | 0 | current->GetSameTypeParent(getter_AddRefs(parent)); |
12990 | 0 | if (!parent) { |
12991 | 0 | return nullptr; |
12992 | 0 | } |
12993 | 0 | |
12994 | 0 | return parent->GetDocument(); |
12995 | 0 | } |
12996 | | |
12997 | | /** |
12998 | | * Retrieves the classification of the Flash plugins in the document based on |
12999 | | * the classification lists. We perform AsyncInitFlashClassification on |
13000 | | * StartDocumentLoad() and the result may not be initialized when this function |
13001 | | * gets called. In that case, We can only unfortunately have a blocking wait. |
13002 | | * |
13003 | | * For more information, see |
13004 | | * toolkit/components/url-classifier/flash-block-lists.rst |
13005 | | */ |
13006 | | FlashClassification |
13007 | | nsIDocument::PrincipalFlashClassification() |
13008 | 0 | { |
13009 | 0 | MOZ_ASSERT(mPrincipalFlashClassifier); |
13010 | 0 | return mPrincipalFlashClassifier->ClassifyMaybeSync(NodePrincipal(), |
13011 | 0 | IsThirdParty()); |
13012 | 0 | } |
13013 | | |
13014 | | /** |
13015 | | * Helper function for |nsDocument::PrincipalFlashClassification| |
13016 | | * |
13017 | | * Adds a table name string to a table list (a comma separated string). The |
13018 | | * table will not be added if the name is an empty string. |
13019 | | */ |
13020 | | static void |
13021 | | MaybeAddTableToTableList(const nsACString& aTableNames, |
13022 | | nsACString& aTableList) |
13023 | 0 | { |
13024 | 0 | if (aTableNames.IsEmpty()) { |
13025 | 0 | return; |
13026 | 0 | } |
13027 | 0 | if (!aTableList.IsEmpty()) { |
13028 | 0 | aTableList.AppendLiteral(","); |
13029 | 0 | } |
13030 | 0 | aTableList.Append(aTableNames); |
13031 | 0 | } |
13032 | | |
13033 | | /** |
13034 | | * Helper function for |nsDocument::PrincipalFlashClassification| |
13035 | | * |
13036 | | * Takes an array of table names and a comma separated list of table names |
13037 | | * Returns |true| if any table name in the array matches a table name in the |
13038 | | * comma separated list. |
13039 | | */ |
13040 | | static bool |
13041 | | ArrayContainsTable(const nsTArray<nsCString>& aTableArray, |
13042 | | const nsACString& aTableNames) |
13043 | 0 | { |
13044 | 0 | for (const nsCString& table : aTableArray) { |
13045 | 0 | // This check is sufficient because table names cannot contain commas and |
13046 | 0 | // cannot contain another existing table name. |
13047 | 0 | if (FindInReadable(table, aTableNames)) { |
13048 | 0 | return true; |
13049 | 0 | } |
13050 | 0 | } |
13051 | 0 | return false; |
13052 | 0 | } |
13053 | | |
13054 | | namespace { |
13055 | | |
13056 | | static const char* gCallbackPrefs[] = { |
13057 | | // We only need to register string-typed preferences. |
13058 | | "urlclassifier.flashAllowTable", |
13059 | | "urlclassifier.flashAllowExceptTable", |
13060 | | "urlclassifier.flashTable", |
13061 | | "urlclassifier.flashExceptTable", |
13062 | | "urlclassifier.flashSubDocTable", |
13063 | | "urlclassifier.flashSubDocExceptTable", |
13064 | | nullptr, |
13065 | | }; |
13066 | | |
13067 | | // An object to store all preferences we need for flash blocking feature. |
13068 | | struct PrefStore |
13069 | | { |
13070 | | PrefStore() |
13071 | | : mFlashBlockEnabled(false) |
13072 | | , mPluginsHttpOnly(false) |
13073 | 0 | { |
13074 | 0 | Preferences::AddBoolVarCache(&mFlashBlockEnabled, |
13075 | 0 | "plugins.flashBlock.enabled"); |
13076 | 0 | Preferences::AddBoolVarCache(&mPluginsHttpOnly, |
13077 | 0 | "plugins.http_https_only"); |
13078 | 0 |
|
13079 | 0 | Preferences::RegisterCallbacks( |
13080 | 0 | PREF_CHANGE_METHOD(PrefStore::UpdateStringPrefs), |
13081 | 0 | gCallbackPrefs, this); |
13082 | 0 |
|
13083 | 0 | UpdateStringPrefs(); |
13084 | 0 | } |
13085 | | |
13086 | | ~PrefStore() |
13087 | 0 | { |
13088 | 0 | Preferences::UnregisterCallbacks( |
13089 | 0 | PREF_CHANGE_METHOD(PrefStore::UpdateStringPrefs), |
13090 | 0 | gCallbackPrefs, this); |
13091 | 0 | } |
13092 | | |
13093 | | void UpdateStringPrefs(const char* aPref = nullptr) |
13094 | 0 | { |
13095 | 0 | Preferences::GetCString("urlclassifier.flashAllowTable", mAllowTables); |
13096 | 0 | Preferences::GetCString("urlclassifier.flashAllowExceptTable", mAllowExceptionsTables); |
13097 | 0 | Preferences::GetCString("urlclassifier.flashTable", mDenyTables); |
13098 | 0 | Preferences::GetCString("urlclassifier.flashExceptTable", mDenyExceptionsTables); |
13099 | 0 | Preferences::GetCString("urlclassifier.flashSubDocTable", mSubDocDenyTables); |
13100 | 0 | Preferences::GetCString("urlclassifier.flashSubDocExceptTable", mSubDocDenyExceptionsTables); |
13101 | 0 | } |
13102 | | |
13103 | | bool mFlashBlockEnabled; |
13104 | | bool mPluginsHttpOnly; |
13105 | | |
13106 | | nsCString mAllowTables; |
13107 | | nsCString mAllowExceptionsTables; |
13108 | | nsCString mDenyTables; |
13109 | | nsCString mDenyExceptionsTables; |
13110 | | nsCString mSubDocDenyTables; |
13111 | | nsCString mSubDocDenyExceptionsTables; |
13112 | | }; |
13113 | | |
13114 | | static const |
13115 | | PrefStore& GetPrefStore() |
13116 | 0 | { |
13117 | 0 | static UniquePtr<PrefStore> sPrefStore; |
13118 | 0 | if (!sPrefStore) { |
13119 | 0 | sPrefStore.reset(new PrefStore()); |
13120 | 0 | ClearOnShutdown(&sPrefStore); |
13121 | 0 | } |
13122 | 0 | return *sPrefStore; |
13123 | 0 | } |
13124 | | |
13125 | | } // end of unnamed namespace. |
13126 | | |
13127 | | //////////////////////////////////////////////////////////////////// |
13128 | | // PrincipalFlashClassifier implementation. |
13129 | | |
13130 | | NS_IMPL_ISUPPORTS(PrincipalFlashClassifier, nsIURIClassifierCallback) |
13131 | | |
13132 | | PrincipalFlashClassifier::PrincipalFlashClassifier() |
13133 | 0 | { |
13134 | 0 | Reset(); |
13135 | 0 | } |
13136 | | |
13137 | | void |
13138 | | PrincipalFlashClassifier::Reset() |
13139 | 0 | { |
13140 | 0 | mAsyncClassified = false; |
13141 | 0 | mMatchedTables.Clear(); |
13142 | 0 | mResult = FlashClassification::Unclassified; |
13143 | 0 | } |
13144 | | |
13145 | | void |
13146 | | PrincipalFlashClassifier::GetClassificationTables(bool aIsThirdParty, |
13147 | | nsACString& aTables) |
13148 | 0 | { |
13149 | 0 | aTables.Truncate(); |
13150 | 0 | auto& prefs = GetPrefStore(); |
13151 | 0 |
|
13152 | 0 | MaybeAddTableToTableList(prefs.mAllowTables, aTables); |
13153 | 0 | MaybeAddTableToTableList(prefs.mAllowExceptionsTables, aTables); |
13154 | 0 | MaybeAddTableToTableList(prefs.mDenyTables, aTables); |
13155 | 0 | MaybeAddTableToTableList(prefs.mDenyExceptionsTables, aTables); |
13156 | 0 |
|
13157 | 0 | if (aIsThirdParty) { |
13158 | 0 | MaybeAddTableToTableList(prefs.mSubDocDenyTables, aTables); |
13159 | 0 | MaybeAddTableToTableList(prefs.mSubDocDenyExceptionsTables, aTables); |
13160 | 0 | } |
13161 | 0 | } |
13162 | | |
13163 | | bool |
13164 | | PrincipalFlashClassifier::EnsureUriClassifier() |
13165 | 0 | { |
13166 | 0 | if (!mUriClassifier) { |
13167 | 0 | mUriClassifier = do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID); |
13168 | 0 | } |
13169 | 0 |
|
13170 | 0 | return !!mUriClassifier; |
13171 | 0 | } |
13172 | | |
13173 | | FlashClassification |
13174 | | PrincipalFlashClassifier::ClassifyMaybeSync(nsIPrincipal* aPrincipal, bool aIsThirdParty) |
13175 | 0 | { |
13176 | 0 | if (FlashClassification::Unclassified != mResult) { |
13177 | 0 | // We already have the result. Just return it. |
13178 | 0 | return mResult; |
13179 | 0 | } |
13180 | 0 | |
13181 | 0 | // TODO: Bug 1342333 - Entirely remove the use of the sync API |
13182 | 0 | // (ClassifyLocalWithTables). |
13183 | 0 | if (!mAsyncClassified) { |
13184 | 0 |
|
13185 | 0 | // |
13186 | 0 | // We may |
13187 | 0 | // 1) have called AsyncClassifyLocalWithTables but OnClassifyComplete |
13188 | 0 | // hasn't been called. |
13189 | 0 | // 2) haven't even called AsyncClassifyLocalWithTables. |
13190 | 0 | // |
13191 | 0 | // In both cases we need to do the synchronous classification as the fallback. |
13192 | 0 | // |
13193 | 0 |
|
13194 | 0 | if (!EnsureUriClassifier()) { |
13195 | 0 | return FlashClassification::Denied; |
13196 | 0 | } |
13197 | 0 | mResult = CheckIfClassifyNeeded(aPrincipal); |
13198 | 0 | if (FlashClassification::Unclassified != mResult) { |
13199 | 0 | return mResult; |
13200 | 0 | } |
13201 | 0 | |
13202 | 0 | nsresult rv; |
13203 | 0 | nsAutoCString classificationTables; |
13204 | 0 | GetClassificationTables(aIsThirdParty, classificationTables); |
13205 | 0 |
|
13206 | 0 | if (!mClassificationURI) { |
13207 | 0 | rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI)); |
13208 | 0 | if (NS_FAILED(rv) || !mClassificationURI) { |
13209 | 0 | mResult = FlashClassification::Denied; |
13210 | 0 | return mResult; |
13211 | 0 | } |
13212 | 0 | } |
13213 | 0 | |
13214 | 0 | rv = mUriClassifier->ClassifyLocalWithTables(mClassificationURI, |
13215 | 0 | classificationTables, |
13216 | 0 | mMatchedTables); |
13217 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
13218 | 0 | if (rv == NS_ERROR_MALFORMED_URI) { |
13219 | 0 | // This means that the URI had no hostname (ex: file://doc.html). In this |
13220 | 0 | // case, we allow the default (Unknown plugin) behavior. |
13221 | 0 | mResult = FlashClassification::Unknown; |
13222 | 0 | } else { |
13223 | 0 | mResult = FlashClassification::Denied; |
13224 | 0 | } |
13225 | 0 | return mResult; |
13226 | 0 | } |
13227 | 0 | } |
13228 | 0 |
|
13229 | 0 | // Resolve the result based on mMatchedTables and aIsThirdParty. |
13230 | 0 | mResult = Resolve(aIsThirdParty); |
13231 | 0 | MOZ_ASSERT(FlashClassification::Unclassified != mResult); |
13232 | 0 |
|
13233 | 0 | // The subsequent call of Result() will return the resolved result |
13234 | 0 | // and never reach here until Reset() is called. |
13235 | 0 | return mResult; |
13236 | 0 | } |
13237 | | |
13238 | | /*virtual*/ nsresult |
13239 | | PrincipalFlashClassifier::OnClassifyComplete(nsresult /*aErrorCode*/, |
13240 | | const nsACString& aLists, // Only this matters. |
13241 | | const nsACString& /*aProvider*/, |
13242 | | const nsACString& /*aPrefix*/) |
13243 | 0 | { |
13244 | 0 | mAsyncClassified = true; |
13245 | 0 |
|
13246 | 0 | if (FlashClassification::Unclassified != mResult) { |
13247 | 0 | // Result() has been called prior to this callback. |
13248 | 0 | return NS_OK; |
13249 | 0 | } |
13250 | 0 | |
13251 | 0 | // TODO: Bug 1364804 - We should use a callback type which notifies |
13252 | 0 | // the result as a string array rather than a formatted string. |
13253 | 0 | |
13254 | 0 | // We only populate the matched list without resolving the classification |
13255 | 0 | // result because we are not sure if the parent doc has been properly set. |
13256 | 0 | // We also parse the comma-separated tables to array. (the code is copied |
13257 | 0 | // from Classifier::SplitTables.) |
13258 | 0 | nsACString::const_iterator begin, iter, end; |
13259 | 0 | aLists.BeginReading(begin); |
13260 | 0 | aLists.EndReading(end); |
13261 | 0 | while (begin != end) { |
13262 | 0 | iter = begin; |
13263 | 0 | FindCharInReadable(',', iter, end); |
13264 | 0 | nsDependentCSubstring table = Substring(begin,iter); |
13265 | 0 | if (!table.IsEmpty()) { |
13266 | 0 | mMatchedTables.AppendElement(Substring(begin, iter)); |
13267 | 0 | } |
13268 | 0 | begin = iter; |
13269 | 0 | if (begin != end) { |
13270 | 0 | begin++; |
13271 | 0 | } |
13272 | 0 | } |
13273 | 0 |
|
13274 | 0 | return NS_OK; |
13275 | 0 | } |
13276 | | |
13277 | | // We resolve the classification result based on aIsThirdParty |
13278 | | // and the matched tables we got ealier on (via either sync or async API). |
13279 | | FlashClassification |
13280 | | PrincipalFlashClassifier::Resolve(bool aIsThirdParty) |
13281 | 0 | { |
13282 | 0 | MOZ_ASSERT(FlashClassification::Unclassified == mResult, |
13283 | 0 | "We already have resolved classification result."); |
13284 | 0 |
|
13285 | 0 | if (mMatchedTables.IsEmpty()) { |
13286 | 0 | return FlashClassification::Unknown; |
13287 | 0 | } |
13288 | 0 | |
13289 | 0 | auto& prefs = GetPrefStore(); |
13290 | 0 | if (ArrayContainsTable(mMatchedTables, prefs.mDenyTables) && |
13291 | 0 | !ArrayContainsTable(mMatchedTables, prefs.mDenyExceptionsTables)) { |
13292 | 0 | return FlashClassification::Denied; |
13293 | 0 | } else if (ArrayContainsTable(mMatchedTables, prefs.mAllowTables) && |
13294 | 0 | !ArrayContainsTable(mMatchedTables, prefs.mAllowExceptionsTables)) { |
13295 | 0 | return FlashClassification::Allowed; |
13296 | 0 | } |
13297 | 0 | |
13298 | 0 | if (aIsThirdParty && ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyTables) && |
13299 | 0 | !ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyExceptionsTables)) { |
13300 | 0 | return FlashClassification::Denied; |
13301 | 0 | } |
13302 | 0 | |
13303 | 0 | return FlashClassification::Unknown; |
13304 | 0 | } |
13305 | | |
13306 | | void |
13307 | | PrincipalFlashClassifier::AsyncClassify(nsIPrincipal* aPrincipal) |
13308 | 0 | { |
13309 | 0 | MOZ_ASSERT(FlashClassification::Unclassified == mResult, |
13310 | 0 | "The old classification result should be reset first."); |
13311 | 0 | Reset(); |
13312 | 0 | mResult = AsyncClassifyInternal(aPrincipal); |
13313 | 0 | } |
13314 | | |
13315 | | FlashClassification |
13316 | | PrincipalFlashClassifier::CheckIfClassifyNeeded(nsIPrincipal* aPrincipal) |
13317 | 0 | { |
13318 | 0 | nsresult rv; |
13319 | 0 |
|
13320 | 0 | auto& prefs = GetPrefStore(); |
13321 | 0 |
|
13322 | 0 | // If neither pref is on, skip the null-principal and principal URI checks. |
13323 | 0 | if (prefs.mPluginsHttpOnly && !prefs.mFlashBlockEnabled) { |
13324 | 0 | return FlashClassification::Unknown; |
13325 | 0 | } |
13326 | 0 | |
13327 | 0 | nsCOMPtr<nsIPrincipal> principal = aPrincipal; |
13328 | 0 | if (principal->GetIsNullPrincipal()) { |
13329 | 0 | return FlashClassification::Denied; |
13330 | 0 | } |
13331 | 0 | |
13332 | 0 | nsCOMPtr<nsIURI> classificationURI; |
13333 | 0 | rv = principal->GetURI(getter_AddRefs(classificationURI)); |
13334 | 0 | if (NS_FAILED(rv) || !classificationURI) { |
13335 | 0 | return FlashClassification::Denied; |
13336 | 0 | } |
13337 | 0 | |
13338 | 0 | if (prefs.mPluginsHttpOnly) { |
13339 | 0 | // Only allow plugins for documents from an HTTP/HTTPS origin. This should |
13340 | 0 | // allow dependent data: URIs to load plugins, but not: |
13341 | 0 | // * chrome documents |
13342 | 0 | // * "bare" data: loads |
13343 | 0 | // * FTP/gopher/file |
13344 | 0 | nsAutoCString scheme; |
13345 | 0 | rv = classificationURI->GetScheme(scheme); |
13346 | 0 | if (NS_WARN_IF(NS_FAILED(rv)) || |
13347 | 0 | !(scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https"))) { |
13348 | 0 | return FlashClassification::Denied; |
13349 | 0 | } |
13350 | 0 | } |
13351 | 0 | |
13352 | 0 | // If flash blocking is disabled, it is equivalent to all sites being |
13353 | 0 | // on neither list. |
13354 | 0 | if (!prefs.mFlashBlockEnabled) { |
13355 | 0 | return FlashClassification::Unknown; |
13356 | 0 | } |
13357 | 0 | |
13358 | 0 | return FlashClassification::Unclassified; |
13359 | 0 | } |
13360 | | |
13361 | | // Using nsIURIClassifier.asyncClassifyLocalWithTables to do classification |
13362 | | // against the flash related tables based on the given principal. |
13363 | | FlashClassification |
13364 | | PrincipalFlashClassifier::AsyncClassifyInternal(nsIPrincipal* aPrincipal) |
13365 | 0 | { |
13366 | 0 | auto result = CheckIfClassifyNeeded(aPrincipal); |
13367 | 0 | if (FlashClassification::Unclassified != result) { |
13368 | 0 | return result; |
13369 | 0 | } |
13370 | 0 | |
13371 | 0 | // We haven't been able to decide if it's a third party document |
13372 | 0 | // since determining if a document is third-party may depend on its |
13373 | 0 | // parent document. At the time we call AsyncClassifyInternal |
13374 | 0 | // (i.e. StartDocumentLoad) the parent document may not have been |
13375 | 0 | // set. As a result, we wait until Resolve() to be called to |
13376 | 0 | // take "is third party" into account. At this point, we just assume |
13377 | 0 | // it's third-party to include every list. |
13378 | 0 | nsAutoCString tables; |
13379 | 0 | GetClassificationTables(true, tables); |
13380 | 0 |
|
13381 | 0 | if (tables.IsEmpty()) { |
13382 | 0 | return FlashClassification::Unknown; |
13383 | 0 | } |
13384 | 0 | |
13385 | 0 | if (!EnsureUriClassifier()) { |
13386 | 0 | return FlashClassification::Denied; |
13387 | 0 | } |
13388 | 0 | |
13389 | 0 | nsresult rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI)); |
13390 | 0 | if (NS_FAILED(rv) || !mClassificationURI) { |
13391 | 0 | return FlashClassification::Denied; |
13392 | 0 | } |
13393 | 0 | |
13394 | 0 | // We don't support extra entries by pref for this classifier. |
13395 | 0 | rv = mUriClassifier->AsyncClassifyLocalWithTables(mClassificationURI, |
13396 | 0 | tables, |
13397 | 0 | nsTArray<nsCString>(), |
13398 | 0 | nsTArray<nsCString>(), |
13399 | 0 | this); |
13400 | 0 |
|
13401 | 0 | if (NS_FAILED(rv)) { |
13402 | 0 | if (rv == NS_ERROR_MALFORMED_URI) { |
13403 | 0 | // This means that the URI had no hostname (ex: file://doc.html). In this |
13404 | 0 | // case, we allow the default (Unknown plugin) behavior. |
13405 | 0 | return FlashClassification::Unknown; |
13406 | 0 | } else { |
13407 | 0 | return FlashClassification::Denied; |
13408 | 0 | } |
13409 | 0 | } |
13410 | 0 | |
13411 | 0 | return FlashClassification::Unclassified; |
13412 | 0 | } |
13413 | | |
13414 | | FlashClassification |
13415 | | nsIDocument::ComputeFlashClassification() |
13416 | 0 | { |
13417 | 0 | nsCOMPtr<nsIDocShellTreeItem> current = this->GetDocShell(); |
13418 | 0 | if (!current) { |
13419 | 0 | return FlashClassification::Denied; |
13420 | 0 | } |
13421 | 0 | nsCOMPtr<nsIDocShellTreeItem> parent; |
13422 | 0 | DebugOnly<nsresult> rv = current->GetSameTypeParent(getter_AddRefs(parent)); |
13423 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv), |
13424 | 0 | "nsIDocShellTreeItem::GetSameTypeParent should never fail"); |
13425 | 0 |
|
13426 | 0 | bool isTopLevel = !parent; |
13427 | 0 | FlashClassification classification; |
13428 | 0 | if (isTopLevel) { |
13429 | 0 | classification = PrincipalFlashClassification(); |
13430 | 0 | } else { |
13431 | 0 | nsCOMPtr<nsIDocument> parentDocument = GetParentDocument(); |
13432 | 0 | if (!parentDocument) { |
13433 | 0 | return FlashClassification::Denied; |
13434 | 0 | } |
13435 | 0 | FlashClassification parentClassification = |
13436 | 0 | parentDocument->DocumentFlashClassification(); |
13437 | 0 |
|
13438 | 0 | if (parentClassification == FlashClassification::Denied) { |
13439 | 0 | classification = FlashClassification::Denied; |
13440 | 0 | } else { |
13441 | 0 | classification = PrincipalFlashClassification(); |
13442 | 0 |
|
13443 | 0 | // Allow unknown children to inherit allowed status from parent, but |
13444 | 0 | // do not allow denied children to do so. |
13445 | 0 | if (classification == FlashClassification::Unknown && |
13446 | 0 | parentClassification == FlashClassification::Allowed) { |
13447 | 0 | classification = FlashClassification::Allowed; |
13448 | 0 | } |
13449 | 0 | } |
13450 | 0 | } |
13451 | 0 |
|
13452 | 0 | return classification; |
13453 | 0 | } |
13454 | | |
13455 | | /** |
13456 | | * Retrieves the classification of plugins in this document. This is dependent |
13457 | | * on the classification of this document and all parent documents. |
13458 | | * This function is infallible - It must return some classification that |
13459 | | * callers can act on. |
13460 | | * |
13461 | | * This function will NOT return FlashClassification::Unclassified |
13462 | | */ |
13463 | | FlashClassification |
13464 | | nsIDocument::DocumentFlashClassification() |
13465 | 0 | { |
13466 | 0 | if (mFlashClassification == FlashClassification::Unclassified) { |
13467 | 0 | FlashClassification result = ComputeFlashClassification(); |
13468 | 0 | mFlashClassification = result; |
13469 | 0 | MOZ_ASSERT(result != FlashClassification::Unclassified, |
13470 | 0 | "nsDocument::GetPluginClassification should never return Unclassified"); |
13471 | 0 | } |
13472 | 0 |
|
13473 | 0 | return mFlashClassification; |
13474 | 0 | } |
13475 | | |
13476 | | /** |
13477 | | * Initializes |mIsThirdParty| if necessary and returns its value. The value |
13478 | | * returned represents whether this document should be considered Third-Party. |
13479 | | * |
13480 | | * A top-level document cannot be a considered Third-Party; only subdocuments |
13481 | | * may. For a subdocument to be considered Third-Party, it must meet ANY ONE |
13482 | | * of the following requirements: |
13483 | | * - The document's parent is Third-Party |
13484 | | * - The document has a different scheme (http/https) than its parent document |
13485 | | * - The document's domain and subdomain do not match those of its parent |
13486 | | * document. |
13487 | | * |
13488 | | * If there is an error in determining whether the document is Third-Party, |
13489 | | * it will be assumed to be Third-Party for security reasons. |
13490 | | */ |
13491 | | bool |
13492 | | nsIDocument::IsThirdParty() |
13493 | 0 | { |
13494 | 0 | if (mIsThirdParty.isSome()) { |
13495 | 0 | return mIsThirdParty.value(); |
13496 | 0 | } |
13497 | 0 | |
13498 | 0 | nsCOMPtr<nsIDocShellTreeItem> docshell = this->GetDocShell(); |
13499 | 0 | if (!docshell) { |
13500 | 0 | mIsThirdParty.emplace(true); |
13501 | 0 | return mIsThirdParty.value(); |
13502 | 0 | } |
13503 | 0 | |
13504 | 0 | nsCOMPtr<nsIDocShellTreeItem> parent; |
13505 | 0 | nsresult rv = docshell->GetSameTypeParent(getter_AddRefs(parent)); |
13506 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv), |
13507 | 0 | "nsIDocShellTreeItem::GetSameTypeParent should never fail"); |
13508 | 0 | bool isTopLevel = !parent; |
13509 | 0 |
|
13510 | 0 | if (isTopLevel) { |
13511 | 0 | mIsThirdParty.emplace(false); |
13512 | 0 | return mIsThirdParty.value(); |
13513 | 0 | } |
13514 | 0 | |
13515 | 0 | nsCOMPtr<nsIDocument> parentDocument = GetParentDocument(); |
13516 | 0 | if (!parentDocument) { |
13517 | 0 | // Failure |
13518 | 0 | mIsThirdParty.emplace(true); |
13519 | 0 | return mIsThirdParty.value(); |
13520 | 0 | } |
13521 | 0 | |
13522 | 0 | if (parentDocument->IsThirdParty()) { |
13523 | 0 | mIsThirdParty.emplace(true); |
13524 | 0 | return mIsThirdParty.value(); |
13525 | 0 | } |
13526 | 0 | |
13527 | 0 | nsCOMPtr<nsIPrincipal> principal = NodePrincipal(); |
13528 | 0 | nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(parentDocument, |
13529 | 0 | &rv); |
13530 | 0 | if (NS_WARN_IF(NS_FAILED(rv) || !sop)) { |
13531 | 0 | // Failure |
13532 | 0 | mIsThirdParty.emplace(true); |
13533 | 0 | return mIsThirdParty.value(); |
13534 | 0 | } |
13535 | 0 | nsCOMPtr<nsIPrincipal> parentPrincipal = sop->GetPrincipal(); |
13536 | 0 |
|
13537 | 0 | bool principalsMatch = false; |
13538 | 0 | rv = principal->Equals(parentPrincipal, &principalsMatch); |
13539 | 0 |
|
13540 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
13541 | 0 | // Failure |
13542 | 0 | mIsThirdParty.emplace(true); |
13543 | 0 | return mIsThirdParty.value(); |
13544 | 0 | } |
13545 | 0 | |
13546 | 0 | if (!principalsMatch) { |
13547 | 0 | mIsThirdParty.emplace(true); |
13548 | 0 | return mIsThirdParty.value(); |
13549 | 0 | } |
13550 | 0 | |
13551 | 0 | // Fall-through. Document is not a Third-Party Document. |
13552 | 0 | mIsThirdParty.emplace(false); |
13553 | 0 | return mIsThirdParty.value(); |
13554 | 0 | } |
13555 | | |
13556 | | void |
13557 | | nsIDocument::ClearStaleServoData() |
13558 | 0 | { |
13559 | 0 | DocumentStyleRootIterator iter(this); |
13560 | 0 | while (Element* root = iter.GetNextStyleRoot()) { |
13561 | 0 | RestyleManager::ClearServoDataFromSubtree(root); |
13562 | 0 | } |
13563 | 0 | } |
13564 | | |
13565 | | Selection* |
13566 | | nsIDocument::GetSelection(ErrorResult& aRv) |
13567 | 0 | { |
13568 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow(); |
13569 | 0 | if (!window) { |
13570 | 0 | return nullptr; |
13571 | 0 | } |
13572 | 0 | |
13573 | 0 | if (!window->IsCurrentInnerWindow()) { |
13574 | 0 | return nullptr; |
13575 | 0 | } |
13576 | 0 | |
13577 | 0 | return nsGlobalWindowInner::Cast(window)->GetSelection(aRv); |
13578 | 0 | } |
13579 | | |
13580 | | already_AddRefed<mozilla::dom::Promise> |
13581 | | nsIDocument::HasStorageAccess(mozilla::ErrorResult& aRv) |
13582 | 0 | { |
13583 | 0 | nsIGlobalObject* global = GetScopeObject(); |
13584 | 0 | if (!global) { |
13585 | 0 | aRv.Throw(NS_ERROR_NOT_AVAILABLE); |
13586 | 0 | return nullptr; |
13587 | 0 | } |
13588 | 0 | |
13589 | 0 | RefPtr<Promise> promise = Promise::Create(global, aRv); |
13590 | 0 | if (aRv.Failed()) { |
13591 | 0 | return nullptr; |
13592 | 0 | } |
13593 | 0 | |
13594 | 0 | if (NodePrincipal()->GetIsNullPrincipal()) { |
13595 | 0 | promise->MaybeResolve(false); |
13596 | 0 | return promise.forget(); |
13597 | 0 | } |
13598 | 0 | |
13599 | 0 | if (IsTopLevelContentDocument()) { |
13600 | 0 | promise->MaybeResolve(true); |
13601 | 0 | return promise.forget(); |
13602 | 0 | } |
13603 | 0 | |
13604 | 0 | nsCOMPtr<nsIDocument> topLevelDoc = GetTopLevelContentDocument(); |
13605 | 0 | if (!topLevelDoc) { |
13606 | 0 | aRv.Throw(NS_ERROR_NOT_AVAILABLE); |
13607 | 0 | return nullptr; |
13608 | 0 | } |
13609 | 0 | if (topLevelDoc->NodePrincipal()->Equals(NodePrincipal())) { |
13610 | 0 | promise->MaybeResolve(true); |
13611 | 0 | return promise.forget(); |
13612 | 0 | } |
13613 | 0 | |
13614 | 0 | if (AntiTrackingCommon::ShouldHonorContentBlockingCookieRestrictions() && |
13615 | 0 | StaticPrefs::network_cookie_cookieBehavior() == |
13616 | 0 | nsICookieService::BEHAVIOR_REJECT_TRACKER) { |
13617 | 0 | // If we need to abide by Content Blocking cookie restrictions, ensure to |
13618 | 0 | // first do all of our storage access checks. If storage access isn't |
13619 | 0 | // disabled in our document, given that we're a third-party, we must either |
13620 | 0 | // not be a tracker, or be whitelisted for some reason (e.g. a storage |
13621 | 0 | // access permission being granted). In that case, resolve the promise and |
13622 | 0 | // say we have obtained storage access. |
13623 | 0 | if (!nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) { |
13624 | 0 | // Note, storage might be allowed because the top-level document is on |
13625 | 0 | // the content blocking allowlist! In that case, don't provide special |
13626 | 0 | // treatment here. |
13627 | 0 | bool isOnAllowList = false; |
13628 | 0 | if (NS_SUCCEEDED(AntiTrackingCommon::IsOnContentBlockingAllowList( |
13629 | 0 | topLevelDoc->GetDocumentURI(), |
13630 | 0 | AntiTrackingCommon::eStorageChecks, |
13631 | 0 | isOnAllowList)) && |
13632 | 0 | !isOnAllowList) { |
13633 | 0 | promise->MaybeResolve(true); |
13634 | 0 | return promise.forget(); |
13635 | 0 | } |
13636 | 0 | } |
13637 | 0 | } |
13638 | 0 | |
13639 | 0 | nsPIDOMWindowInner* inner = GetInnerWindow(); |
13640 | 0 | nsGlobalWindowOuter* outer = nullptr; |
13641 | 0 | if (inner) { |
13642 | 0 | outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow()); |
13643 | 0 | promise->MaybeResolve(outer->HasStorageAccess()); |
13644 | 0 | } else { |
13645 | 0 | promise->MaybeRejectWithUndefined(); |
13646 | 0 | } |
13647 | 0 | return promise.forget(); |
13648 | 0 | } |
13649 | | |
13650 | | already_AddRefed<mozilla::dom::Promise> |
13651 | | nsIDocument::RequestStorageAccess(mozilla::ErrorResult& aRv) |
13652 | 0 | { |
13653 | 0 | nsIGlobalObject* global = GetScopeObject(); |
13654 | 0 | if (!global) { |
13655 | 0 | aRv.Throw(NS_ERROR_NOT_AVAILABLE); |
13656 | 0 | return nullptr; |
13657 | 0 | } |
13658 | 0 | |
13659 | 0 | RefPtr<Promise> promise = Promise::Create(global, aRv); |
13660 | 0 | if (aRv.Failed()) { |
13661 | 0 | return nullptr; |
13662 | 0 | } |
13663 | 0 | |
13664 | 0 | // Step 1. If the document already has been granted access, resolve. |
13665 | 0 | nsPIDOMWindowInner* inner = GetInnerWindow(); |
13666 | 0 | nsGlobalWindowOuter* outer = nullptr; |
13667 | 0 | if (inner) { |
13668 | 0 | outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow()); |
13669 | 0 | if (outer->HasStorageAccess()) { |
13670 | 0 | promise->MaybeResolveWithUndefined(); |
13671 | 0 | return promise.forget(); |
13672 | 0 | } |
13673 | 0 | } |
13674 | 0 | |
13675 | 0 | // Step 2. If the document has a null origin, reject. |
13676 | 0 | if (NodePrincipal()->GetIsNullPrincipal()) { |
13677 | 0 | promise->MaybeRejectWithUndefined(); |
13678 | 0 | return promise.forget(); |
13679 | 0 | } |
13680 | 0 | |
13681 | 0 | // Only enforce third-party checks when there is a reason to enforce them. |
13682 | 0 | if (StaticPrefs::network_cookie_cookieBehavior() != |
13683 | 0 | nsICookieService::BEHAVIOR_ACCEPT) { |
13684 | 0 | // Step 3. If the document's frame is the main frame, resolve. |
13685 | 0 | if (IsTopLevelContentDocument()) { |
13686 | 0 | promise->MaybeResolveWithUndefined(); |
13687 | 0 | return promise.forget(); |
13688 | 0 | } |
13689 | 0 | |
13690 | 0 | // Step 4. If the sub frame's origin is equal to the main frame's, resolve. |
13691 | 0 | nsCOMPtr<nsIDocument> topLevelDoc = GetTopLevelContentDocument(); |
13692 | 0 | if (!topLevelDoc) { |
13693 | 0 | aRv.Throw(NS_ERROR_NOT_AVAILABLE); |
13694 | 0 | return nullptr; |
13695 | 0 | } |
13696 | 0 | if (topLevelDoc->NodePrincipal()->Equals(NodePrincipal())) { |
13697 | 0 | promise->MaybeResolveWithUndefined(); |
13698 | 0 | return promise.forget(); |
13699 | 0 | } |
13700 | 0 | } |
13701 | 0 | |
13702 | 0 | // Step 5. If the sub frame is not sandboxed, skip to step 7. |
13703 | 0 | // Step 6. If the sub frame doesn't have the token |
13704 | 0 | // "allow-storage-access-by-user-activation", reject. |
13705 | 0 | if (mSandboxFlags & SANDBOXED_STORAGE_ACCESS) { |
13706 | 0 | promise->MaybeRejectWithUndefined(); |
13707 | 0 | return promise.forget(); |
13708 | 0 | } |
13709 | 0 | |
13710 | 0 | // Step 7. If the sub frame's parent frame is not the top frame, reject. |
13711 | 0 | nsIDocument* parent = GetParentDocument(); |
13712 | 0 | if (parent && !parent->IsTopLevelContentDocument()) { |
13713 | 0 | promise->MaybeRejectWithUndefined(); |
13714 | 0 | return promise.forget(); |
13715 | 0 | } |
13716 | 0 | |
13717 | 0 | // Step 8. If the browser is not processing a user gesture, reject. |
13718 | 0 | if (!EventStateManager::IsHandlingUserInput()) { |
13719 | 0 | promise->MaybeRejectWithUndefined(); |
13720 | 0 | return promise.forget(); |
13721 | 0 | } |
13722 | 0 | |
13723 | 0 | // Step 9. Check any additional rules that the browser has. |
13724 | 0 | // Examples: Whitelists, blacklists, on-device classification, |
13725 | 0 | // user settings, anti-clickjacking heuristics, or prompting the |
13726 | 0 | // user for explicit permission. Reject if some rule is not fulfilled. |
13727 | 0 | |
13728 | 0 | if (nsContentUtils::IsInPrivateBrowsing(this)) { |
13729 | 0 | // If the document is in PB mode, it doesn't have access to its persistent |
13730 | 0 | // cookie jar, so reject the promise here. |
13731 | 0 | promise->MaybeRejectWithUndefined(); |
13732 | 0 | return promise.forget(); |
13733 | 0 | } |
13734 | 0 | |
13735 | 0 | bool granted = true; |
13736 | 0 | bool isTrackingWindow = false; |
13737 | 0 | if (AntiTrackingCommon::ShouldHonorContentBlockingCookieRestrictions() && |
13738 | 0 | StaticPrefs::network_cookie_cookieBehavior() == |
13739 | 0 | nsICookieService::BEHAVIOR_REJECT_TRACKER) { |
13740 | 0 | // Only do something special for third-party tracking content. |
13741 | 0 | if (nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) { |
13742 | 0 | // Note: If this has returned true, the top-level document is guaranteed |
13743 | 0 | // to not be on the Content Blocking allow list. |
13744 | 0 | DebugOnly<bool> isOnAllowList = false; |
13745 | 0 | MOZ_ASSERT_IF(NS_SUCCEEDED(AntiTrackingCommon::IsOnContentBlockingAllowList( |
13746 | 0 | parent->GetDocumentURI(), |
13747 | 0 | AntiTrackingCommon::eStorageChecks, |
13748 | 0 | isOnAllowList)), |
13749 | 0 | !isOnAllowList); |
13750 | 0 |
|
13751 | 0 | isTrackingWindow = true; |
13752 | 0 | // TODO: prompt for permission |
13753 | 0 | } |
13754 | 0 | } |
13755 | 0 |
|
13756 | 0 | // Step 10. Grant the document access to cookies and store that fact for |
13757 | 0 | // the purposes of future calls to hasStorageAccess() and |
13758 | 0 | // requestStorageAccess(). |
13759 | 0 | if (granted && inner) { |
13760 | 0 | outer->SetHasStorageAccess(true); |
13761 | 0 | if (isTrackingWindow) { |
13762 | 0 | nsCOMPtr<nsIURI> uri = GetDocumentURI(); |
13763 | 0 | if (NS_WARN_IF(!uri)) { |
13764 | 0 | aRv.Throw(NS_ERROR_NOT_AVAILABLE); |
13765 | 0 | return nullptr; |
13766 | 0 | } |
13767 | 0 | nsAutoString origin; |
13768 | 0 | nsresult rv = nsContentUtils::GetUTFOrigin(uri, origin); |
13769 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
13770 | 0 | aRv.Throw(rv); |
13771 | 0 | return nullptr; |
13772 | 0 | } |
13773 | 0 | AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(origin, |
13774 | 0 | inner, |
13775 | 0 | AntiTrackingCommon::eStorageAccessAPI) |
13776 | 0 | ->Then(GetCurrentThreadSerialEventTarget(), __func__, |
13777 | 0 | [promise] (bool) { |
13778 | 0 | promise->MaybeResolveWithUndefined(); |
13779 | 0 | }, |
13780 | 0 | [promise] (bool) { |
13781 | 0 | promise->MaybeRejectWithUndefined(); |
13782 | 0 | }); |
13783 | 0 | } else { |
13784 | 0 | promise->MaybeResolveWithUndefined(); |
13785 | 0 | } |
13786 | 0 | } |
13787 | 0 | return promise.forget(); |
13788 | 0 | } |
13789 | | |
13790 | | void |
13791 | | nsIDocument::RecordNavigationTiming(ReadyState aReadyState) |
13792 | 0 | { |
13793 | 0 | if (!XRE_IsContentProcess()) { |
13794 | 0 | return; |
13795 | 0 | } |
13796 | 0 | if (!IsTopLevelContentDocument()) { |
13797 | 0 | return; |
13798 | 0 | } |
13799 | 0 | // If we dont have the timing yet (mostly because the doc is still loading), |
13800 | 0 | // get it from docshell. |
13801 | 0 | RefPtr<nsDOMNavigationTiming> timing = mTiming; |
13802 | 0 | if (!timing) { |
13803 | 0 | if (!mDocumentContainer) { |
13804 | 0 | return; |
13805 | 0 | } |
13806 | 0 | timing = mDocumentContainer->GetNavigationTiming(); |
13807 | 0 | if (!timing) { |
13808 | 0 | return; |
13809 | 0 | } |
13810 | 0 | } |
13811 | 0 | TimeStamp startTime = timing->GetNavigationStartTimeStamp(); |
13812 | 0 | switch (aReadyState) { |
13813 | 0 | case READYSTATE_LOADING: |
13814 | 0 | if (!mDOMLoadingSet) { |
13815 | 0 | Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_LOADING_MS, |
13816 | 0 | startTime); |
13817 | 0 | mDOMLoadingSet = true; |
13818 | 0 | } |
13819 | 0 | break; |
13820 | 0 | case READYSTATE_INTERACTIVE: |
13821 | 0 | if (!mDOMInteractiveSet) { |
13822 | 0 | Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_INTERACTIVE_MS, |
13823 | 0 | startTime); |
13824 | 0 | mDOMInteractiveSet = true; |
13825 | 0 | } |
13826 | 0 | break; |
13827 | 0 | case READYSTATE_COMPLETE: |
13828 | 0 | if (!mDOMCompleteSet) { |
13829 | 0 | Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_COMPLETE_MS, |
13830 | 0 | startTime); |
13831 | 0 | mDOMCompleteSet = true; |
13832 | 0 | } |
13833 | 0 | break; |
13834 | 0 | default: |
13835 | 0 | NS_WARNING("Unexpected ReadyState value"); |
13836 | 0 | break; |
13837 | 0 | } |
13838 | 0 | } |
13839 | | |
13840 | | bool |
13841 | | nsIDocument::ModuleScriptsEnabled() |
13842 | 0 | { |
13843 | 0 | static bool sEnabledForContent = false; |
13844 | 0 | static bool sCachedPref = false; |
13845 | 0 | if (!sCachedPref) { |
13846 | 0 | sCachedPref = true; |
13847 | 0 | Preferences::AddBoolVarCache(&sEnabledForContent, "dom.moduleScripts.enabled", false); |
13848 | 0 | } |
13849 | 0 |
|
13850 | 0 | return nsContentUtils::IsChromeDoc(this) || sEnabledForContent; |
13851 | 0 | } |
13852 | | |
13853 | | void |
13854 | | nsIDocument::ReportShadowDOMUsage() |
13855 | 0 | { |
13856 | 0 | if (mHasReportedShadowDOMUsage) { |
13857 | 0 | return; |
13858 | 0 | } |
13859 | 0 | |
13860 | 0 | nsIDocument* topLevel = GetTopLevelContentDocument(); |
13861 | 0 | if (topLevel && !topLevel->mHasReportedShadowDOMUsage) { |
13862 | 0 | topLevel->mHasReportedShadowDOMUsage = true; |
13863 | 0 | nsString uri; |
13864 | 0 | Unused << topLevel->GetDocumentURI(uri); |
13865 | 0 | if (!uri.IsEmpty()) { |
13866 | 0 | nsAutoString msg = NS_LITERAL_STRING("Shadow DOM used in [") + uri + |
13867 | 0 | NS_LITERAL_STRING("] or in some of its subdocuments."); |
13868 | 0 | nsContentUtils::ReportToConsoleNonLocalized(msg, nsIScriptError::infoFlag, |
13869 | 0 | NS_LITERAL_CSTRING("DOM"), |
13870 | 0 | topLevel); |
13871 | 0 | } |
13872 | 0 | } |
13873 | 0 |
|
13874 | 0 | mHasReportedShadowDOMUsage = true; |
13875 | 0 | } |