Bug 1660051 [wpt PR 25111] - Origin isolation: expand getter test coverage, a=testonly
[gecko.git] / dom / base / nsContentUtils.cpp
blob24eda39cf2c0dd0cbd6c0bed0c1c0d33b740d0f9
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/. */
7 /* A namespace class for static layout utilities. */
9 #include "nsContentUtils.h"
11 #include <algorithm>
12 #include <math.h>
14 #include "DecoderTraits.h"
15 #include "harfbuzz/hb.h"
16 #include "imgICache.h"
17 #include "imgIContainer.h"
18 #include "imgINotificationObserver.h"
19 #include "imgLoader.h"
20 #include "imgRequestProxy.h"
21 #include "jsapi.h"
22 #include "jsfriendapi.h"
23 #include "js/Array.h" // JS::NewArrayObject
24 #include "js/ArrayBuffer.h" // JS::{GetArrayBufferData,IsArrayBufferObject,NewArrayBuffer}
25 #include "js/JSON.h"
26 #include "js/RegExp.h" // JS::ExecuteRegExpNoStatics, JS::NewUCRegExpObject, JS::RegExpFlags
27 #include "js/Value.h"
28 #include "Layers.h"
29 #include "nsAppRunner.h"
30 // nsNPAPIPluginInstance must be included before mozilla/dom/Document.h, which
31 // is included in mozAutoDocUpdate.h.
32 #include "nsNPAPIPluginInstance.h"
33 #include "gfxDrawable.h"
34 #include "ImageOps.h"
35 #include "mozAutoDocUpdate.h"
36 #include "mozilla/net/UrlClassifierCommon.h"
37 #include "mozilla/ArrayUtils.h"
38 #include "mozilla/Attributes.h"
39 #include "mozilla/AutoRestore.h"
40 #include "mozilla/AutoTimelineMarker.h"
41 #include "mozilla/BackgroundHangMonitor.h"
42 #include "mozilla/Base64.h"
43 #include "mozilla/BasePrincipal.h"
44 #include "mozilla/CheckedInt.h"
45 #include "mozilla/Components.h"
46 #include "mozilla/DebugOnly.h"
47 #include "mozilla/LoadInfo.h"
48 #include "mozilla/dom/AncestorIterator.h"
49 #include "mozilla/dom/BlobURLProtocolHandler.h"
50 #include "mozilla/dom/BrowsingContext.h"
51 #include "mozilla/dom/BrowsingContextGroup.h"
52 #include "mozilla/dom/ContentParent.h"
53 #include "mozilla/dom/ContentChild.h"
54 #include "mozilla/dom/CustomElementRegistry.h"
55 #include "mozilla/dom/Document.h"
56 #include "mozilla/dom/DocumentInlines.h"
57 #include "mozilla/dom/MessageBroadcaster.h"
58 #include "mozilla/dom/DocumentFragment.h"
59 #include "mozilla/dom/DOMException.h"
60 #include "mozilla/dom/DOMExceptionBinding.h"
61 #include "mozilla/dom/DOMSecurityMonitor.h"
62 #include "mozilla/dom/DOMTypes.h"
63 #include "mozilla/dom/Element.h"
64 #include "mozilla/dom/ElementBinding.h"
65 #include "mozilla/dom/ElementInlines.h"
66 #include "mozilla/dom/Event.h"
67 #include "mozilla/dom/FileSystemSecurity.h"
68 #include "mozilla/dom/FileBlobImpl.h"
69 #include "mozilla/dom/FontTableURIProtocolHandler.h"
70 #include "mozilla/dom/HTMLInputElement.h"
71 #include "mozilla/dom/HTMLSlotElement.h"
72 #include "mozilla/dom/HTMLTemplateElement.h"
73 #include "mozilla/dom/HTMLTextAreaElement.h"
74 #include "mozilla/dom/IDTracker.h"
75 #include "mozilla/dom/MouseEventBinding.h"
76 #include "mozilla/dom/KeyboardEventBinding.h"
77 #include "mozilla/dom/IPCBlobUtils.h"
78 #include "mozilla/dom/NodeBinding.h"
79 #include "mozilla/dom/Promise.h"
80 #include "mozilla/dom/BrowserBridgeChild.h"
81 #include "mozilla/dom/ScriptSettings.h"
82 #include "mozilla/dom/BrowserParent.h"
83 #include "mozilla/dom/Text.h"
84 #include "mozilla/dom/TouchEvent.h"
85 #include "mozilla/dom/ShadowRoot.h"
86 #include "mozilla/dom/XULCommandEvent.h"
87 #include "mozilla/dom/UserActivation.h"
88 #include "mozilla/dom/WorkerCommon.h"
89 #include "mozilla/dom/WorkerPrivate.h"
90 #include "mozilla/extensions/WebExtensionPolicy.h"
91 #include "mozilla/net/CookieJarSettings.h"
92 #include "mozilla/EventDispatcher.h"
93 #include "mozilla/EventListenerManager.h"
94 #include "mozilla/EventStateManager.h"
95 #include "mozilla/gfx/DataSurfaceHelpers.h"
96 #include "mozilla/HTMLEditor.h"
97 #include "mozilla/IMEStateManager.h"
98 #include "mozilla/InputEventOptions.h"
99 #include "mozilla/InternalMutationEvent.h"
100 #include "mozilla/Likely.h"
101 #include "mozilla/ManualNAC.h"
102 #include "mozilla/MouseEvents.h"
103 #include "mozilla/Preferences.h"
104 #include "mozilla/PresShell.h"
105 #include "mozilla/ResultExtensions.h"
106 #include "mozilla/dom/Selection.h"
107 #include "mozilla/Services.h"
108 #include "mozilla/StaticPrefs_dom.h"
109 #include "mozilla/StaticPrefs_full_screen_api.h"
110 #ifdef FUZZING
111 # include "mozilla/StaticPrefs_fuzzing.h"
112 #endif
113 #include "mozilla/StaticPrefs_privacy.h"
114 #include "mozilla/StaticPrefs_test.h"
115 #include "mozilla/StaticPrefs_ui.h"
116 #include "mozilla/StoragePrincipalHelper.h"
117 #include "mozilla/TextControlState.h"
118 #include "mozilla/TextEditor.h"
119 #include "mozilla/TextEvents.h"
120 #include "mozilla/ViewportUtils.h"
121 #include "nsArrayUtils.h"
122 #include "nsAString.h"
123 #include "nsAttrName.h"
124 #include "nsAttrValue.h"
125 #include "nsAttrValueInlines.h"
126 #include "nsCanvasFrame.h"
127 #include "nsCaret.h"
128 #include "nsCCUncollectableMarker.h"
129 #include "nsCharSeparatedTokenizer.h"
130 #include "nsCOMPtr.h"
131 #include "nsContentCreatorFunctions.h"
132 #include "nsContentDLF.h"
133 #include "nsContentList.h"
134 #include "nsContentPolicyUtils.h"
135 #include "nsContentSecurityManager.h"
136 #include "nsCRT.h"
137 #include "nsCycleCollectionParticipant.h"
138 #include "nsCycleCollector.h"
139 #include "nsDataHashtable.h"
140 #include "nsDocShellCID.h"
141 #include "nsDOMCID.h"
142 #include "mozilla/dom/DataTransfer.h"
143 #include "nsDOMJSUtils.h"
144 #include "nsDOMMutationObserver.h"
145 #include "nsError.h"
146 #include "nsFocusManager.h"
147 #include "nsFrameLoaderOwner.h"
148 #include "nsGenericHTMLElement.h"
149 #include "nsGenericHTMLFrameElement.h"
150 #include "nsGkAtoms.h"
151 #include "nsHtml5Module.h"
152 #include "nsHtml5StringParser.h"
153 #include "nsHTMLDocument.h"
154 #include "nsHTMLTags.h"
155 #include "nsIAnonymousContentCreator.h"
156 #include "nsIAsyncVerifyRedirectCallback.h"
157 #include "nsICacheInfoChannel.h"
158 #include "nsICategoryManager.h"
159 #include "nsIChannelEventSink.h"
160 #include "nsIConsoleService.h"
161 #include "nsIContent.h"
162 #include "nsIContentInlines.h"
163 #include "nsIContentSecurityPolicy.h"
164 #include "nsIContentSink.h"
165 #include "nsIContentViewer.h"
166 #include "nsIDocShell.h"
167 #include "nsIDocShellTreeOwner.h"
168 #include "mozilla/dom/Document.h"
169 #include "nsIDocumentEncoder.h"
170 #include "nsIDOMWindowUtils.h"
171 #include "nsIDragService.h"
172 #include "nsIFormControl.h"
173 #include "nsIForm.h"
174 #include "nsIFragmentContentSink.h"
175 #include "nsContainerFrame.h"
176 #include "nsIClassifiedChannel.h"
177 #include "nsIHttpChannelInternal.h"
178 #include "nsIUserIdleService.h"
179 #include "nsIImageLoadingContent.h"
180 #include "nsIInterfaceRequestor.h"
181 #include "nsIInterfaceRequestorUtils.h"
182 #include "nsIIOService.h"
183 #include "nsILoadContext.h"
184 #include "nsILoadGroup.h"
185 #include "nsIMemoryReporter.h"
186 #include "nsIMIMEService.h"
187 #include "nsINode.h"
188 #include "mozilla/dom/NodeInfo.h"
189 #include "mozilla/NullPrincipal.h"
190 #include "nsIObjectLoadingContent.h"
191 #include "nsIObserver.h"
192 #include "nsIObserverService.h"
193 #include "nsIOfflineCacheUpdate.h"
194 #include "nsIParser.h"
195 #include "nsIParserUtils.h"
196 #include "nsIPermissionManager.h"
197 #include "nsIRequest.h"
198 #include "nsIRunnable.h"
199 #include "nsIScriptContext.h"
200 #include "nsIScriptError.h"
201 #include "nsIScriptGlobalObject.h"
202 #include "nsIScriptObjectPrincipal.h"
203 #include "nsIScriptSecurityManager.h"
204 #include "nsIStreamConverter.h"
205 #include "nsIStreamConverterService.h"
206 #include "nsIStringBundle.h"
207 #include "nsIURI.h"
208 #include "nsIURIMutator.h"
209 #include "nsIURIWithSpecialOrigin.h"
210 #include "nsIUUIDGenerator.h"
211 #include "nsIWebNavigation.h"
212 #include "nsIWidget.h"
213 #include "nsIWindowMediator.h"
214 #include "nsIXPConnect.h"
215 #include "nsJSUtils.h"
216 #include "nsMappedAttributes.h"
217 #include "nsNetCID.h"
218 #include "nsNetUtil.h"
219 #include "nsNodeInfoManager.h"
220 #include "nsParserCIID.h"
221 #include "nsParserConstants.h"
222 #include "nsPIDOMWindow.h"
223 #include "nsPresContext.h"
224 #include "nsPrintfCString.h"
225 #include "nsQueryObject.h"
226 #include "nsSandboxFlags.h"
227 #include "nsScriptSecurityManager.h"
228 #include "nsSerializationHelper.h"
229 #include "nsStreamUtils.h"
230 #include "nsTextFragment.h"
231 #include "nsTextNode.h"
232 #include "nsThreadUtils.h"
233 #include "nsTreeSanitizer.h"
234 #include "nsUnicodeProperties.h"
235 #include "nsURLHelper.h"
236 #include "nsViewManager.h"
237 #include "nsViewportInfo.h"
238 #include "nsWidgetsCID.h"
239 #include "nsWrapperCacheInlines.h"
240 #include "nsXULPopupManager.h"
241 #include "xpcprivate.h" // nsXPConnect
242 #include "HTMLSplitOnSpacesTokenizer.h"
243 #include "InProcessBrowserChildMessageManager.h"
244 #include "nsContentTypeParser.h"
245 #include "ThirdPartyUtil.h"
246 #include "mozilla/EnumSet.h"
247 #include "mozilla/BloomFilter.h"
248 #include "BrowserChild.h"
249 #include "mozilla/dom/DocGroup.h"
250 #include "nsIWebNavigationInfo.h"
251 #include "nsPluginHost.h"
252 #include "nsIBrowser.h"
253 #include "mozilla/HangAnnotations.h"
254 #include "mozilla/Encoding.h"
255 #include "nsXULElement.h"
256 #include "nsThreadManager.h"
257 #include "nsIBidiKeyboard.h"
258 #include "ReferrerInfo.h"
259 #include "nsAboutProtocolUtils.h"
261 #if defined(XP_WIN)
262 // Undefine LoadImage to prevent naming conflict with Windows.
263 # undef LoadImage
264 #endif
266 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
267 const char** next, char16_t* result);
268 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end, int ns_aware,
269 const char** colon);
271 class imgLoader;
272 class nsAtom;
274 using namespace mozilla::dom;
275 using namespace mozilla::ipc;
276 using namespace mozilla::gfx;
277 using namespace mozilla::layers;
278 using namespace mozilla::widget;
279 using namespace mozilla;
281 const char kLoadAsData[] = "loadAsData";
283 nsIXPConnect* nsContentUtils::sXPConnect;
284 nsIScriptSecurityManager* nsContentUtils::sSecurityManager;
285 nsIPrincipal* nsContentUtils::sSystemPrincipal;
286 nsIPrincipal* nsContentUtils::sNullSubjectPrincipal;
287 nsNameSpaceManager* nsContentUtils::sNameSpaceManager;
288 nsIIOService* nsContentUtils::sIOService;
289 nsIUUIDGenerator* nsContentUtils::sUUIDGenerator;
290 nsIConsoleService* nsContentUtils::sConsoleService;
291 nsDataHashtable<nsRefPtrHashKey<nsAtom>, EventNameMapping>*
292 nsContentUtils::sAtomEventTable = nullptr;
293 nsDataHashtable<nsStringHashKey, EventNameMapping>*
294 nsContentUtils::sStringEventTable = nullptr;
295 nsTArray<RefPtr<nsAtom>>* nsContentUtils::sUserDefinedEvents = nullptr;
296 nsIStringBundleService* nsContentUtils::sStringBundleService;
297 nsIStringBundle* nsContentUtils::sStringBundles[PropertiesFile_COUNT];
298 nsIContentPolicy* nsContentUtils::sContentPolicyService;
299 bool nsContentUtils::sTriedToGetContentPolicy = false;
300 RefPtr<mozilla::intl::LineBreaker> nsContentUtils::sLineBreaker;
301 RefPtr<mozilla::intl::WordBreaker> nsContentUtils::sWordBreaker;
302 StaticRefPtr<nsIBidiKeyboard> nsContentUtils::sBidiKeyboard;
303 uint32_t nsContentUtils::sScriptBlockerCount = 0;
304 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
305 AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners =
306 nullptr;
307 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
308 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
310 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
312 nsString* nsContentUtils::sShiftText = nullptr;
313 nsString* nsContentUtils::sControlText = nullptr;
314 nsString* nsContentUtils::sMetaText = nullptr;
315 nsString* nsContentUtils::sOSText = nullptr;
316 nsString* nsContentUtils::sAltText = nullptr;
317 nsString* nsContentUtils::sModifierSeparator = nullptr;
319 bool nsContentUtils::sInitialized = false;
320 #ifndef RELEASE_OR_BETA
321 bool nsContentUtils::sBypassCSSOMOriginCheck = false;
322 #endif
324 nsCString* nsContentUtils::sJSBytecodeMimeType = nullptr;
326 nsContentUtils::UserInteractionObserver*
327 nsContentUtils::sUserInteractionObserver = nullptr;
329 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
330 nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
331 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
332 bool nsContentUtils::sFragmentParsingActive = false;
334 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
336 int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
337 uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
339 template Maybe<int32_t> nsContentUtils::ComparePoints(
340 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary);
341 template Maybe<int32_t> nsContentUtils::ComparePoints(
342 const RangeBoundary& aFirstBoundary,
343 const RawRangeBoundary& aSecondBoundary);
344 template Maybe<int32_t> nsContentUtils::ComparePoints(
345 const RawRangeBoundary& aFirstBoundary,
346 const RangeBoundary& aSecondBoundary);
347 template Maybe<int32_t> nsContentUtils::ComparePoints(
348 const RawRangeBoundary& aFirstBoundary,
349 const RawRangeBoundary& aSecondBoundary);
351 template int32_t nsContentUtils::ComparePoints_Deprecated(
352 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
353 bool* aDisconnected);
354 template int32_t nsContentUtils::ComparePoints_Deprecated(
355 const RangeBoundary& aFirstBoundary,
356 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
357 template int32_t nsContentUtils::ComparePoints_Deprecated(
358 const RawRangeBoundary& aFirstBoundary,
359 const RangeBoundary& aSecondBoundary, bool* aDisconnected);
360 template int32_t nsContentUtils::ComparePoints_Deprecated(
361 const RawRangeBoundary& aFirstBoundary,
362 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
364 // Subset of
365 // http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
366 enum AutocompleteUnsupportedFieldName : uint8_t {
367 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
368 eAutocompleteUnsupportedFieldName_##name_,
369 #include "AutocompleteFieldList.h"
370 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
373 enum AutocompleteNoPersistFieldName : uint8_t {
374 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
375 eAutocompleteNoPersistFieldName_##name_,
376 #include "AutocompleteFieldList.h"
377 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
380 enum AutocompleteUnsupportFieldContactHint : uint8_t {
381 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
382 eAutocompleteUnsupportedFieldContactHint_##name_,
383 #include "AutocompleteFieldList.h"
384 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
387 enum AutocompleteFieldName : uint8_t {
388 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) eAutocompleteFieldName_##name_,
389 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
390 AUTOCOMPLETE_FIELD_NAME(name_, value_)
391 #include "AutocompleteFieldList.h"
392 #undef AUTOCOMPLETE_FIELD_NAME
393 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
396 enum AutocompleteFieldHint : uint8_t {
397 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) eAutocompleteFieldHint_##name_,
398 #include "AutocompleteFieldList.h"
399 #undef AUTOCOMPLETE_FIELD_HINT
402 enum AutocompleteFieldContactHint : uint8_t {
403 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
404 eAutocompleteFieldContactHint_##name_,
405 #include "AutocompleteFieldList.h"
406 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
409 enum AutocompleteCategory {
410 #define AUTOCOMPLETE_CATEGORY(name_, value_) eAutocompleteCategory_##name_,
411 #include "AutocompleteFieldList.h"
412 #undef AUTOCOMPLETE_CATEGORY
415 static const nsAttrValue::EnumTable kAutocompleteUnsupportedFieldNameTable[] = {
416 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
417 {value_, eAutocompleteUnsupportedFieldName_##name_},
418 #include "AutocompleteFieldList.h"
419 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
420 {nullptr, 0}};
422 static const nsAttrValue::EnumTable kAutocompleteNoPersistFieldNameTable[] = {
423 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
424 {value_, eAutocompleteNoPersistFieldName_##name_},
425 #include "AutocompleteFieldList.h"
426 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
427 {nullptr, 0}};
429 static const nsAttrValue::EnumTable
430 kAutocompleteUnsupportedContactFieldHintTable[] = {
431 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
432 {value_, eAutocompleteUnsupportedFieldContactHint_##name_},
433 #include "AutocompleteFieldList.h"
434 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
435 {nullptr, 0}};
437 static const nsAttrValue::EnumTable kAutocompleteFieldNameTable[] = {
438 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
439 {value_, eAutocompleteFieldName_##name_},
440 #include "AutocompleteFieldList.h"
441 #undef AUTOCOMPLETE_FIELD_NAME
442 {nullptr, 0}};
444 static const nsAttrValue::EnumTable kAutocompleteContactFieldNameTable[] = {
445 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
446 {value_, eAutocompleteFieldName_##name_},
447 #include "AutocompleteFieldList.h"
448 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
449 {nullptr, 0}};
451 static const nsAttrValue::EnumTable kAutocompleteFieldHintTable[] = {
452 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
453 {value_, eAutocompleteFieldHint_##name_},
454 #include "AutocompleteFieldList.h"
455 #undef AUTOCOMPLETE_FIELD_HINT
456 {nullptr, 0}};
458 static const nsAttrValue::EnumTable kAutocompleteContactFieldHintTable[] = {
459 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
460 {value_, eAutocompleteFieldContactHint_##name_},
461 #include "AutocompleteFieldList.h"
462 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
463 {nullptr, 0}};
465 namespace {
467 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
469 static PLDHashTable* sEventListenerManagersHash;
471 // A global hashtable to for keeping the arena alive for cross docGroup node
472 // adoption.
473 static nsRefPtrHashtable<nsPtrHashKey<const nsINode>, mozilla::dom::DOMArena>*
474 sDOMArenaHashtable;
476 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter {
477 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
479 ~DOMEventListenerManagersHashReporter() = default;
481 public:
482 NS_DECL_ISUPPORTS
484 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
485 nsISupports* aData, bool aAnonymize) override {
486 // We don't measure the |EventListenerManager| objects pointed to by the
487 // entries because those references are non-owning.
488 int64_t amount =
489 sEventListenerManagersHash
490 ? sEventListenerManagersHash->ShallowSizeOfIncludingThis(
491 MallocSizeOf)
492 : 0;
494 MOZ_COLLECT_REPORT(
495 "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
496 amount, "Memory used by the event listener manager's hash table.");
498 return NS_OK;
502 NS_IMPL_ISUPPORTS(DOMEventListenerManagersHashReporter, nsIMemoryReporter)
504 class EventListenerManagerMapEntry : public PLDHashEntryHdr {
505 public:
506 explicit EventListenerManagerMapEntry(const void* aKey) : mKey(aKey) {}
508 ~EventListenerManagerMapEntry() {
509 NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
512 protected: // declared protected to silence clang warnings
513 const void* mKey; // must be first, to look like PLDHashEntryStub
515 public:
516 RefPtr<EventListenerManager> mListenerManager;
519 static void EventListenerManagerHashInitEntry(PLDHashEntryHdr* entry,
520 const void* key) {
521 // Initialize the entry with placement new
522 new (entry) EventListenerManagerMapEntry(key);
525 static void EventListenerManagerHashClearEntry(PLDHashTable* table,
526 PLDHashEntryHdr* entry) {
527 EventListenerManagerMapEntry* lm =
528 static_cast<EventListenerManagerMapEntry*>(entry);
530 // Let the EventListenerManagerMapEntry clean itself up...
531 lm->~EventListenerManagerMapEntry();
534 class SameOriginCheckerImpl final : public nsIChannelEventSink,
535 public nsIInterfaceRequestor {
536 ~SameOriginCheckerImpl() = default;
538 NS_DECL_ISUPPORTS
539 NS_DECL_NSICHANNELEVENTSINK
540 NS_DECL_NSIINTERFACEREQUESTOR
543 } // namespace
545 AutoSuppressEventHandlingAndSuspend::AutoSuppressEventHandlingAndSuspend(
546 BrowsingContextGroup* aGroup) {
547 for (const auto& bc : aGroup->Toplevels()) {
548 SuppressBrowsingContext(bc);
552 void AutoSuppressEventHandlingAndSuspend::SuppressBrowsingContext(
553 BrowsingContext* aBC) {
554 if (nsCOMPtr<nsPIDOMWindowOuter> win = aBC->GetDOMWindow()) {
555 if (RefPtr<Document> doc = win->GetExtantDoc()) {
556 mDocuments.AppendElement(doc);
557 mWindows.AppendElement(win->GetCurrentInnerWindow());
558 // Note: Document::SuppressEventHandling will also automatically suppress
559 // event handling for any in-process sub-documents. However, since we need
560 // to deal with cases where remote BrowsingContexts may be interleaved
561 // with in-process ones, we still need to walk the entire tree ourselves.
562 // This may be slightly redundant in some cases, but since event handling
563 // suppressions maintain a count of current blockers, it does not cause
564 // any problems.
565 doc->SuppressEventHandling();
566 win->GetCurrentInnerWindow()->Suspend();
570 for (const auto& bc : aBC->Children()) {
571 SuppressBrowsingContext(bc);
575 AutoSuppressEventHandlingAndSuspend::~AutoSuppressEventHandlingAndSuspend() {
576 for (const auto& win : mWindows) {
577 win->Resume();
579 for (const auto& doc : mDocuments) {
580 doc->UnsuppressEventHandlingAndFireEvents(true);
585 * This class is used to determine whether or not the user is currently
586 * interacting with the browser. It listens to observer events to toggle the
587 * value of the sUserActive static.
589 * This class is an internal implementation detail.
590 * nsContentUtils::GetUserIsInteracting() should be used to access current
591 * user interaction status.
593 class nsContentUtils::UserInteractionObserver final
594 : public nsIObserver,
595 public BackgroundHangAnnotator {
596 public:
597 NS_DECL_ISUPPORTS
598 NS_DECL_NSIOBSERVER
600 void Init();
601 void Shutdown();
602 void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
604 static Atomic<bool> sUserActive;
606 private:
607 ~UserInteractionObserver() = default;
610 // static
611 nsresult nsContentUtils::Init() {
612 if (sInitialized) {
613 NS_WARNING("Init() called twice");
615 return NS_OK;
618 nsHTMLTags::AddRefTable();
620 sNameSpaceManager = nsNameSpaceManager::GetInstance();
621 NS_ENSURE_TRUE(sNameSpaceManager, NS_ERROR_OUT_OF_MEMORY);
623 sXPConnect = nsXPConnect::XPConnect();
624 // We hold a strong ref to sXPConnect to ensure that it does not go away until
625 // nsLayoutStatics::Shutdown is happening. Otherwise ~nsXPConnect can be
626 // triggered by xpcModuleDtor late in shutdown and cause crashes due to
627 // various stuff already being torn down by then. Note that this means that
628 // we are effectively making sure that if we leak nsLayoutStatics then we also
629 // leak nsXPConnect.
630 NS_ADDREF(sXPConnect);
632 sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
633 if (!sSecurityManager) return NS_ERROR_FAILURE;
634 NS_ADDREF(sSecurityManager);
636 sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
637 MOZ_ASSERT(sSystemPrincipal);
639 RefPtr<NullPrincipal> nullPrincipal =
640 NullPrincipal::CreateWithoutOriginAttributes();
641 if (!nullPrincipal) {
642 return NS_ERROR_FAILURE;
645 nullPrincipal.forget(&sNullSubjectPrincipal);
647 nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
648 if (NS_FAILED(rv)) {
649 // This makes life easier, but we can live without it.
651 sIOService = nullptr;
654 sLineBreaker = mozilla::intl::LineBreaker::Create();
656 sWordBreaker = mozilla::intl::WordBreaker::Create();
658 if (!InitializeEventTable()) return NS_ERROR_FAILURE;
660 if (!sEventListenerManagersHash) {
661 static const PLDHashTableOps hash_table_ops = {
662 PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
663 PLDHashTable::MoveEntryStub, EventListenerManagerHashClearEntry,
664 EventListenerManagerHashInitEntry};
666 sEventListenerManagersHash =
667 new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
669 RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
672 sBlockedScriptRunners = new AutoTArray<nsCOMPtr<nsIRunnable>, 8>;
674 #ifndef RELEASE_OR_BETA
675 sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
676 #endif
678 nsDependentCString buildID(mozilla::PlatformBuildID());
679 sJSBytecodeMimeType = new nsCString("javascript/moz-bytecode-"_ns + buildID);
681 Element::InitCCCallbacks();
683 Unused << nsRFPService::GetOrCreate();
685 nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
686 do_GetService("@mozilla.org/uuid-generator;1", &rv);
687 if (NS_WARN_IF(NS_FAILED(rv))) {
688 return rv;
690 uuidGenerator.forget(&sUUIDGenerator);
692 if (XRE_IsParentProcess()) {
693 AsyncPrecreateStringBundles();
696 RefPtr<UserInteractionObserver> uio = new UserInteractionObserver();
697 uio->Init();
698 uio.forget(&sUserInteractionObserver);
700 sInitialized = true;
702 return NS_OK;
705 void nsContentUtils::GetShiftText(nsAString& text) {
706 if (!sShiftText) InitializeModifierStrings();
707 text.Assign(*sShiftText);
710 void nsContentUtils::GetControlText(nsAString& text) {
711 if (!sControlText) InitializeModifierStrings();
712 text.Assign(*sControlText);
715 void nsContentUtils::GetMetaText(nsAString& text) {
716 if (!sMetaText) InitializeModifierStrings();
717 text.Assign(*sMetaText);
720 void nsContentUtils::GetOSText(nsAString& text) {
721 if (!sOSText) {
722 InitializeModifierStrings();
724 text.Assign(*sOSText);
727 void nsContentUtils::GetAltText(nsAString& text) {
728 if (!sAltText) InitializeModifierStrings();
729 text.Assign(*sAltText);
732 void nsContentUtils::GetModifierSeparatorText(nsAString& text) {
733 if (!sModifierSeparator) InitializeModifierStrings();
734 text.Assign(*sModifierSeparator);
737 void nsContentUtils::InitializeModifierStrings() {
738 // load the display strings for the keyboard accelerators
739 nsCOMPtr<nsIStringBundleService> bundleService =
740 mozilla::services::GetStringBundleService();
741 nsCOMPtr<nsIStringBundle> bundle;
742 DebugOnly<nsresult> rv = NS_OK;
743 if (bundleService) {
744 rv = bundleService->CreateBundle(
745 "chrome://global-platform/locale/platformKeys.properties",
746 getter_AddRefs(bundle));
749 NS_ASSERTION(
750 NS_SUCCEEDED(rv) && bundle,
751 "chrome://global/locale/platformKeys.properties could not be loaded");
752 nsAutoString shiftModifier;
753 nsAutoString metaModifier;
754 nsAutoString osModifier;
755 nsAutoString altModifier;
756 nsAutoString controlModifier;
757 nsAutoString modifierSeparator;
758 if (bundle) {
759 // macs use symbols for each modifier key, so fetch each from the bundle,
760 // which also covers i18n
761 bundle->GetStringFromName("VK_SHIFT", shiftModifier);
762 bundle->GetStringFromName("VK_META", metaModifier);
763 bundle->GetStringFromName("VK_WIN", osModifier);
764 bundle->GetStringFromName("VK_ALT", altModifier);
765 bundle->GetStringFromName("VK_CONTROL", controlModifier);
766 bundle->GetStringFromName("MODIFIER_SEPARATOR", modifierSeparator);
768 // if any of these don't exist, we get an empty string
769 sShiftText = new nsString(shiftModifier);
770 sMetaText = new nsString(metaModifier);
771 sOSText = new nsString(osModifier);
772 sAltText = new nsString(altModifier);
773 sControlText = new nsString(controlModifier);
774 sModifierSeparator = new nsString(modifierSeparator);
777 mozilla::EventClassID nsContentUtils::GetEventClassIDFromMessage(
778 EventMessage aEventMessage) {
779 switch (aEventMessage) {
780 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
781 case message_: \
782 return struct_;
783 #include "mozilla/EventNameList.h"
784 #undef MESSAGE_TO_EVENT
785 default:
786 MOZ_ASSERT_UNREACHABLE("Invalid event message?");
787 return eBasicEventClass;
791 static nsAtom* GetEventTypeFromMessage(EventMessage aEventMessage) {
792 switch (aEventMessage) {
793 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
794 case message_: \
795 return nsGkAtoms::on##name_;
796 #include "mozilla/EventNameList.h"
797 #undef MESSAGE_TO_EVENT
798 default:
799 return nullptr;
803 // Because of SVG/SMIL we have several atoms mapped to the same
804 // id, but we can rely on MESSAGE_TO_EVENT to map id to only one atom.
805 static bool ShouldAddEventToStringEventTable(const EventNameMapping& aMapping) {
806 MOZ_ASSERT(aMapping.mAtom);
807 return GetEventTypeFromMessage(aMapping.mMessage) == aMapping.mAtom;
810 bool nsContentUtils::InitializeEventTable() {
811 NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
812 NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
814 static const EventNameMapping eventArray[] = {
815 #define EVENT(name_, _message, _type, _class) \
816 {nsGkAtoms::on##name_, _type, _message, _class, false},
817 #define WINDOW_ONLY_EVENT EVENT
818 #define DOCUMENT_ONLY_EVENT EVENT
819 #define NON_IDL_EVENT EVENT
820 #include "mozilla/EventNameList.h"
821 #undef WINDOW_ONLY_EVENT
822 #undef NON_IDL_EVENT
823 #undef EVENT
824 {nullptr}};
826 sAtomEventTable =
827 new nsDataHashtable<nsRefPtrHashKey<nsAtom>, EventNameMapping>(
828 ArrayLength(eventArray));
829 sStringEventTable = new nsDataHashtable<nsStringHashKey, EventNameMapping>(
830 ArrayLength(eventArray));
831 sUserDefinedEvents = new nsTArray<RefPtr<nsAtom>>(64);
833 // Subtract one from the length because of the trailing null
834 for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) {
835 MOZ_ASSERT(!sAtomEventTable->Lookup(eventArray[i].mAtom),
836 "Double-defining event name; fix your EventNameList.h");
837 sAtomEventTable->Put(eventArray[i].mAtom, eventArray[i]);
838 if (ShouldAddEventToStringEventTable(eventArray[i])) {
839 sStringEventTable->Put(
840 Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
841 eventArray[i]);
845 return true;
848 void nsContentUtils::InitializeTouchEventTable() {
849 static bool sEventTableInitialized = false;
850 if (!sEventTableInitialized && sAtomEventTable && sStringEventTable) {
851 sEventTableInitialized = true;
852 static const EventNameMapping touchEventArray[] = {
853 #define EVENT(name_, _message, _type, _class)
854 #define TOUCH_EVENT(name_, _message, _type, _class) \
855 {nsGkAtoms::on##name_, _type, _message, _class},
856 #include "mozilla/EventNameList.h"
857 #undef TOUCH_EVENT
858 #undef EVENT
859 {nullptr}};
860 // Subtract one from the length because of the trailing null
861 for (uint32_t i = 0; i < ArrayLength(touchEventArray) - 1; ++i) {
862 sAtomEventTable->Put(touchEventArray[i].mAtom, touchEventArray[i]);
863 sStringEventTable->Put(
864 Substring(nsDependentAtomString(touchEventArray[i].mAtom), 2),
865 touchEventArray[i]);
870 static bool Is8bit(const nsAString& aString) {
871 static const char16_t EIGHT_BIT = char16_t(~0x00FF);
873 for (nsAString::const_char_iterator start = aString.BeginReading(),
874 end = aString.EndReading();
875 start != end; ++start) {
876 if (*start & EIGHT_BIT) {
877 return false;
881 return true;
884 nsresult nsContentUtils::Btoa(const nsAString& aBinaryData,
885 nsAString& aAsciiBase64String) {
886 if (!Is8bit(aBinaryData)) {
887 aAsciiBase64String.Truncate();
888 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
891 return Base64Encode(aBinaryData, aAsciiBase64String);
894 nsresult nsContentUtils::Atob(const nsAString& aAsciiBase64String,
895 nsAString& aBinaryData) {
896 if (!Is8bit(aAsciiBase64String)) {
897 aBinaryData.Truncate();
898 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
901 const char16_t* start = aAsciiBase64String.BeginReading();
902 const char16_t* cur = start;
903 const char16_t* end = aAsciiBase64String.EndReading();
904 bool hasWhitespace = false;
906 while (cur < end) {
907 if (nsContentUtils::IsHTMLWhitespace(*cur)) {
908 hasWhitespace = true;
909 break;
911 cur++;
914 nsresult rv;
916 if (hasWhitespace) {
917 nsString trimmedString;
919 if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible)) {
920 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
923 trimmedString.Append(start, cur - start);
925 while (cur < end) {
926 if (!nsContentUtils::IsHTMLWhitespace(*cur)) {
927 trimmedString.Append(*cur);
929 cur++;
931 rv = Base64Decode(trimmedString, aBinaryData);
932 } else {
933 rv = Base64Decode(aAsciiBase64String, aBinaryData);
936 if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
937 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
939 return rv;
942 bool nsContentUtils::IsAutocompleteEnabled(
943 mozilla::dom::HTMLInputElement* aInput) {
944 MOZ_ASSERT(aInput, "aInput should not be null!");
946 nsAutoString autocomplete;
947 aInput->GetAutocomplete(autocomplete);
949 if (autocomplete.IsEmpty()) {
950 auto* form = aInput->GetForm();
951 if (!form) {
952 return true;
955 form->GetAutocomplete(autocomplete);
958 return !autocomplete.EqualsLiteral("off");
961 nsContentUtils::AutocompleteAttrState
962 nsContentUtils::SerializeAutocompleteAttribute(
963 const nsAttrValue* aAttr, nsAString& aResult,
964 AutocompleteAttrState aCachedState) {
965 if (!aAttr ||
966 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
967 return aCachedState;
970 if (aCachedState == nsContentUtils::eAutocompleteAttrState_Valid) {
971 uint32_t atomCount = aAttr->GetAtomCount();
972 for (uint32_t i = 0; i < atomCount; i++) {
973 if (i != 0) {
974 aResult.Append(' ');
976 aResult.Append(nsDependentAtomString(aAttr->AtomAt(i)));
978 nsContentUtils::ASCIIToLower(aResult);
979 return aCachedState;
982 aResult.Truncate();
984 mozilla::dom::AutocompleteInfo info;
985 AutocompleteAttrState state =
986 InternalSerializeAutocompleteAttribute(aAttr, info);
987 if (state == eAutocompleteAttrState_Valid) {
988 // Concatenate the info fields.
989 aResult = info.mSection;
991 if (!info.mAddressType.IsEmpty()) {
992 if (!aResult.IsEmpty()) {
993 aResult += ' ';
995 aResult += info.mAddressType;
998 if (!info.mContactType.IsEmpty()) {
999 if (!aResult.IsEmpty()) {
1000 aResult += ' ';
1002 aResult += info.mContactType;
1005 if (!info.mFieldName.IsEmpty()) {
1006 if (!aResult.IsEmpty()) {
1007 aResult += ' ';
1009 aResult += info.mFieldName;
1013 return state;
1016 nsContentUtils::AutocompleteAttrState
1017 nsContentUtils::SerializeAutocompleteAttribute(
1018 const nsAttrValue* aAttr, mozilla::dom::AutocompleteInfo& aInfo,
1019 AutocompleteAttrState aCachedState, bool aGrantAllValidValue) {
1020 if (!aAttr ||
1021 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1022 return aCachedState;
1025 return InternalSerializeAutocompleteAttribute(aAttr, aInfo,
1026 aGrantAllValidValue);
1030 * Helper to validate the @autocomplete tokens.
1032 * @return {AutocompleteAttrState} The state of the attribute (invalid/valid).
1034 nsContentUtils::AutocompleteAttrState
1035 nsContentUtils::InternalSerializeAutocompleteAttribute(
1036 const nsAttrValue* aAttrVal, mozilla::dom::AutocompleteInfo& aInfo,
1037 bool aGrantAllValidValue) {
1038 // No autocomplete attribute so we are done
1039 if (!aAttrVal) {
1040 return eAutocompleteAttrState_Invalid;
1043 uint32_t numTokens = aAttrVal->GetAtomCount();
1044 if (!numTokens) {
1045 return eAutocompleteAttrState_Invalid;
1048 uint32_t index = numTokens - 1;
1049 nsString tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1050 AutocompleteCategory category;
1051 nsAttrValue enumValue;
1053 bool unsupported = false;
1054 if (!aGrantAllValidValue) {
1055 unsupported = enumValue.ParseEnumValue(
1056 tokenString, kAutocompleteUnsupportedFieldNameTable, false);
1057 if (unsupported) {
1058 return eAutocompleteAttrState_Invalid;
1062 nsAutoString str;
1063 bool result =
1064 enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
1065 if (result) {
1066 // Off/Automatic/Normal categories.
1067 if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
1068 enumValue.Equals(u"on"_ns, eIgnoreCase)) {
1069 if (numTokens > 1) {
1070 return eAutocompleteAttrState_Invalid;
1072 enumValue.ToString(str);
1073 ASCIIToLower(str);
1074 aInfo.mFieldName.Assign(str);
1075 aInfo.mCanAutomaticallyPersist =
1076 !enumValue.Equals(u"off"_ns, eIgnoreCase);
1077 return eAutocompleteAttrState_Valid;
1080 // Only allow on/off if form autofill @autocomplete values aren't enabled
1081 // and it doesn't grant all valid values.
1082 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1083 !aGrantAllValidValue) {
1084 return eAutocompleteAttrState_Invalid;
1087 // Normal category
1088 if (numTokens > 3) {
1089 return eAutocompleteAttrState_Invalid;
1091 category = eAutocompleteCategory_NORMAL;
1092 } else { // Check if the last token is of the contact category instead.
1093 // Only allow on/off if form autofill @autocomplete values aren't enabled
1094 // and it doesn't grant all valid values.
1095 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1096 !aGrantAllValidValue) {
1097 return eAutocompleteAttrState_Invalid;
1100 result = enumValue.ParseEnumValue(
1101 tokenString, kAutocompleteContactFieldNameTable, false);
1102 if (!result || numTokens > 4) {
1103 return eAutocompleteAttrState_Invalid;
1106 category = eAutocompleteCategory_CONTACT;
1109 enumValue.ToString(str);
1110 ASCIIToLower(str);
1111 aInfo.mFieldName.Assign(str);
1113 aInfo.mCanAutomaticallyPersist = !enumValue.ParseEnumValue(
1114 tokenString, kAutocompleteNoPersistFieldNameTable, false);
1116 // We are done if this was the only token.
1117 if (numTokens == 1) {
1118 return eAutocompleteAttrState_Valid;
1121 --index;
1122 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1124 if (category == eAutocompleteCategory_CONTACT) {
1125 if (!aGrantAllValidValue) {
1126 unsupported = enumValue.ParseEnumValue(
1127 tokenString, kAutocompleteUnsupportedContactFieldHintTable, false);
1128 if (unsupported) {
1129 return eAutocompleteAttrState_Invalid;
1133 nsAttrValue contactFieldHint;
1134 result = contactFieldHint.ParseEnumValue(
1135 tokenString, kAutocompleteContactFieldHintTable, false);
1136 if (result) {
1137 nsAutoString contactFieldHintString;
1138 contactFieldHint.ToString(contactFieldHintString);
1139 ASCIIToLower(contactFieldHintString);
1140 aInfo.mContactType.Assign(contactFieldHintString);
1141 if (index == 0) {
1142 return eAutocompleteAttrState_Valid;
1144 --index;
1145 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1149 // Check for billing/shipping tokens
1150 nsAttrValue fieldHint;
1151 if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable,
1152 false)) {
1153 nsString fieldHintString;
1154 fieldHint.ToString(fieldHintString);
1155 ASCIIToLower(fieldHintString);
1156 aInfo.mAddressType.Assign(fieldHintString);
1157 if (index == 0) {
1158 return eAutocompleteAttrState_Valid;
1160 --index;
1161 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1164 // Check for section-* token
1165 const nsDependentSubstring& section = Substring(tokenString, 0, 8);
1166 if (section.LowerCaseEqualsASCII("section-")) {
1167 ASCIIToLower(tokenString);
1168 aInfo.mSection.Assign(tokenString);
1169 if (index == 0) {
1170 return eAutocompleteAttrState_Valid;
1174 // Clear the fields as the autocomplete attribute is invalid.
1175 aInfo.mSection.Truncate();
1176 aInfo.mAddressType.Truncate();
1177 aInfo.mContactType.Truncate();
1178 aInfo.mFieldName.Truncate();
1180 return eAutocompleteAttrState_Invalid;
1183 // Parse an integer according to HTML spec
1184 template <class StringT>
1185 int32_t nsContentUtils::ParseHTMLIntegerImpl(
1186 const StringT& aValue, ParseHTMLIntegerResultFlags* aResult) {
1187 using CharT = typename StringT::char_type;
1189 int result = eParseHTMLInteger_NoFlags;
1191 typename StringT::const_iterator iter, end;
1192 aValue.BeginReading(iter);
1193 aValue.EndReading(end);
1195 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1196 result |= eParseHTMLInteger_NonStandard;
1197 ++iter;
1200 if (iter == end) {
1201 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1202 *aResult = (ParseHTMLIntegerResultFlags)result;
1203 return 0;
1206 int sign = 1;
1207 if (*iter == CharT('-')) {
1208 sign = -1;
1209 result |= eParseHTMLInteger_Negative;
1210 ++iter;
1211 } else if (*iter == CharT('+')) {
1212 result |= eParseHTMLInteger_NonStandard;
1213 ++iter;
1216 bool foundValue = false;
1217 CheckedInt32 value = 0;
1219 // Check for leading zeros first.
1220 uint64_t leadingZeros = 0;
1221 while (iter != end) {
1222 if (*iter != CharT('0')) {
1223 break;
1226 ++leadingZeros;
1227 foundValue = true;
1228 ++iter;
1231 while (iter != end) {
1232 if (*iter >= CharT('0') && *iter <= CharT('9')) {
1233 value = (value * 10) + (*iter - CharT('0')) * sign;
1234 ++iter;
1235 if (!value.isValid()) {
1236 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
1237 break;
1239 foundValue = true;
1240 } else {
1241 break;
1245 if (!foundValue) {
1246 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1249 if (value.isValid() &&
1250 ((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
1251 (sign == -1 && value == 0))) {
1252 result |= eParseHTMLInteger_NonStandard;
1255 if (iter != end) {
1256 result |= eParseHTMLInteger_DidNotConsumeAllInput;
1259 *aResult = (ParseHTMLIntegerResultFlags)result;
1260 return value.isValid() ? value.value() : 0;
1263 // Parse an integer according to HTML spec
1264 int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
1265 ParseHTMLIntegerResultFlags* aResult) {
1266 return ParseHTMLIntegerImpl(aValue, aResult);
1269 int32_t nsContentUtils::ParseHTMLInteger(const nsACString& aValue,
1270 ParseHTMLIntegerResultFlags* aResult) {
1271 return ParseHTMLIntegerImpl(aValue, aResult);
1274 #define SKIP_WHITESPACE(iter, end_iter, end_res) \
1275 while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
1276 ++(iter); \
1278 if ((iter) == (end_iter)) { \
1279 return (end_res); \
1282 #define SKIP_ATTR_NAME(iter, end_iter) \
1283 while ((iter) != (end_iter) && !nsCRT::IsAsciiSpace(*(iter)) && \
1284 *(iter) != '=') { \
1285 ++(iter); \
1288 bool nsContentUtils::GetPseudoAttributeValue(const nsString& aSource,
1289 nsAtom* aName, nsAString& aValue) {
1290 aValue.Truncate();
1292 const char16_t* start = aSource.get();
1293 const char16_t* end = start + aSource.Length();
1294 const char16_t* iter;
1296 while (start != end) {
1297 SKIP_WHITESPACE(start, end, false)
1298 iter = start;
1299 SKIP_ATTR_NAME(iter, end)
1301 if (start == iter) {
1302 return false;
1305 // Remember the attr name.
1306 const nsDependentSubstring& attrName = Substring(start, iter);
1308 // Now check whether this is a valid name="value" pair.
1309 start = iter;
1310 SKIP_WHITESPACE(start, end, false)
1311 if (*start != '=') {
1312 // No '=', so this is not a name="value" pair. We don't know
1313 // what it is, and we have no way to handle it.
1314 return false;
1317 // Have to skip the value.
1318 ++start;
1319 SKIP_WHITESPACE(start, end, false)
1320 char16_t q = *start;
1321 if (q != kQuote && q != kApostrophe) {
1322 // Not a valid quoted value, so bail.
1323 return false;
1326 ++start; // Point to the first char of the value.
1327 iter = start;
1329 while (iter != end && *iter != q) {
1330 ++iter;
1333 if (iter == end) {
1334 // Oops, unterminated quoted string.
1335 return false;
1338 // At this point attrName holds the name of the "attribute" and
1339 // the value is between start and iter.
1341 if (aName->Equals(attrName)) {
1342 // We'll accumulate as many characters as possible (until we hit either
1343 // the end of the string or the beginning of an entity). Chunks will be
1344 // delimited by start and chunkEnd.
1345 const char16_t* chunkEnd = start;
1346 while (chunkEnd != iter) {
1347 if (*chunkEnd == kLessThan) {
1348 aValue.Truncate();
1350 return false;
1353 if (*chunkEnd == kAmpersand) {
1354 aValue.Append(start, chunkEnd - start);
1356 const char16_t* afterEntity = nullptr;
1357 char16_t result[2];
1358 uint32_t count = MOZ_XMLTranslateEntity(
1359 reinterpret_cast<const char*>(chunkEnd),
1360 reinterpret_cast<const char*>(iter),
1361 reinterpret_cast<const char**>(&afterEntity), result);
1362 if (count == 0) {
1363 aValue.Truncate();
1365 return false;
1368 aValue.Append(result, count);
1370 // Advance to after the entity and begin a new chunk.
1371 start = chunkEnd = afterEntity;
1372 } else {
1373 ++chunkEnd;
1377 // Append remainder.
1378 aValue.Append(start, iter - start);
1380 return true;
1383 // Resume scanning after the end of the attribute value (past the quote
1384 // char).
1385 start = iter + 1;
1388 return false;
1391 bool nsContentUtils::IsJavaScriptLanguage(const nsString& aName) {
1392 return aName.LowerCaseEqualsLiteral("javascript") ||
1393 aName.LowerCaseEqualsLiteral("livescript") ||
1394 aName.LowerCaseEqualsLiteral("mocha") ||
1395 aName.LowerCaseEqualsLiteral("javascript1.0") ||
1396 aName.LowerCaseEqualsLiteral("javascript1.1") ||
1397 aName.LowerCaseEqualsLiteral("javascript1.2") ||
1398 aName.LowerCaseEqualsLiteral("javascript1.3") ||
1399 aName.LowerCaseEqualsLiteral("javascript1.4") ||
1400 aName.LowerCaseEqualsLiteral("javascript1.5");
1403 void nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType,
1404 nsString& aParams) {
1405 aType.Truncate();
1406 aParams.Truncate();
1407 int32_t semiIndex = aValue.FindChar(char16_t(';'));
1408 if (-1 != semiIndex) {
1409 aType = Substring(aValue, 0, semiIndex);
1410 aParams =
1411 Substring(aValue, semiIndex + 1, aValue.Length() - (semiIndex + 1));
1412 aParams.StripWhitespace();
1413 } else {
1414 aType = aValue;
1416 aType.StripWhitespace();
1419 nsresult nsContentUtils::IsUserIdle(uint32_t aRequestedIdleTimeInMS,
1420 bool* aUserIsIdle) {
1421 nsresult rv;
1422 nsCOMPtr<nsIUserIdleService> idleService =
1423 do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
1424 NS_ENSURE_SUCCESS(rv, rv);
1426 uint32_t idleTimeInMS;
1427 rv = idleService->GetIdleTime(&idleTimeInMS);
1428 NS_ENSURE_SUCCESS(rv, rv);
1430 *aUserIsIdle = idleTimeInMS >= aRequestedIdleTimeInMS;
1431 return NS_OK;
1435 * A helper function that parses a sandbox attribute (of an <iframe> or a CSP
1436 * directive) and converts it to the set of flags used internally.
1438 * @param aSandboxAttr the sandbox attribute
1439 * @return the set of flags (SANDBOXED_NONE if aSandboxAttr is
1440 * null)
1442 uint32_t nsContentUtils::ParseSandboxAttributeToFlags(
1443 const nsAttrValue* aSandboxAttr) {
1444 if (!aSandboxAttr) {
1445 return SANDBOXED_NONE;
1448 uint32_t out = SANDBOX_ALL_FLAGS;
1450 #define SANDBOX_KEYWORD(string, atom, flags) \
1451 if (aSandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { \
1452 out &= ~(flags); \
1454 #include "IframeSandboxKeywordList.h"
1455 #undef SANDBOX_KEYWORD
1457 return out;
1461 * A helper function that checks if a string matches a valid sandbox flag.
1463 * @param aFlag the potential sandbox flag.
1464 * @return true if the flag is a sandbox flag.
1466 bool nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag) {
1467 #define SANDBOX_KEYWORD(string, atom, flags) \
1468 if (EqualsIgnoreASCIICase(nsDependentAtomString(nsGkAtoms::atom), aFlag)) { \
1469 return true; \
1471 #include "IframeSandboxKeywordList.h"
1472 #undef SANDBOX_KEYWORD
1473 return false;
1477 * A helper function that returns a string attribute corresponding to the
1478 * sandbox flags.
1480 * @param aFlags the sandbox flags
1481 * @param aString the attribute corresponding to the flags (null if aFlags
1482 * is zero)
1484 void nsContentUtils::SandboxFlagsToString(uint32_t aFlags, nsAString& aString) {
1485 if (!aFlags) {
1486 SetDOMStringToNull(aString);
1487 return;
1490 aString.Truncate();
1492 #define SANDBOX_KEYWORD(string, atom, flags) \
1493 if (!(aFlags & (flags))) { \
1494 if (!aString.IsEmpty()) { \
1495 aString.AppendLiteral(u" "); \
1497 aString.Append(nsDependentAtomString(nsGkAtoms::atom)); \
1499 #include "IframeSandboxKeywordList.h"
1500 #undef SANDBOX_KEYWORD
1503 nsIBidiKeyboard* nsContentUtils::GetBidiKeyboard() {
1504 if (!sBidiKeyboard) {
1505 sBidiKeyboard = nsIWidget::CreateBidiKeyboard();
1507 return sBidiKeyboard;
1511 * This is used to determine whether a character is in one of the classes
1512 * which CSS says should be part of the first-letter. Currently, that is
1513 * all punctuation classes (P*). Note that this is a change from CSS2
1514 * which excluded Pc and Pd.
1516 * https://www.w3.org/TR/css-pseudo-4/#first-letter-pseudo
1517 * "Punctuation (i.e, characters that belong to the Punctuation (P*) Unicode
1518 * general category [UAX44]) [...]"
1521 // static
1522 bool nsContentUtils::IsFirstLetterPunctuation(uint32_t aChar) {
1523 switch (mozilla::unicode::GetGeneralCategory(aChar)) {
1524 case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
1525 case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION: /* Pd */
1526 case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION: /* Pe */
1527 case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION: /* Pf */
1528 case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
1529 case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION: /* Po */
1530 case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION: /* Ps */
1531 return true;
1532 default:
1533 return false;
1537 // static
1538 bool nsContentUtils::IsAlphanumeric(uint32_t aChar) {
1539 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1541 return (cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber);
1544 // static
1545 bool nsContentUtils::IsAlphanumericAt(const nsTextFragment* aFrag,
1546 uint32_t aOffset) {
1547 char16_t h = aFrag->CharAt(aOffset);
1548 if (!IS_SURROGATE(h)) {
1549 return IsAlphanumeric(h);
1551 if (NS_IS_HIGH_SURROGATE(h) && aOffset + 1 < aFrag->GetLength()) {
1552 char16_t l = aFrag->CharAt(aOffset + 1);
1553 if (NS_IS_LOW_SURROGATE(l)) {
1554 return IsAlphanumeric(SURROGATE_TO_UCS4(h, l));
1557 return false;
1560 /* static */
1561 bool nsContentUtils::IsHTMLWhitespace(char16_t aChar) {
1562 return aChar == char16_t(0x0009) || aChar == char16_t(0x000A) ||
1563 aChar == char16_t(0x000C) || aChar == char16_t(0x000D) ||
1564 aChar == char16_t(0x0020);
1567 /* static */
1568 bool nsContentUtils::IsHTMLWhitespaceOrNBSP(char16_t aChar) {
1569 return IsHTMLWhitespace(aChar) || aChar == char16_t(0xA0);
1572 /* static */
1573 bool nsContentUtils::IsHTMLBlockLevelElement(nsIContent* aContent) {
1574 return aContent->IsAnyOfHTMLElements(
1575 nsGkAtoms::address, nsGkAtoms::article, nsGkAtoms::aside,
1576 nsGkAtoms::blockquote, nsGkAtoms::center, nsGkAtoms::dir, nsGkAtoms::div,
1577 nsGkAtoms::dl, // XXX why not dt and dd?
1578 nsGkAtoms::fieldset,
1579 nsGkAtoms::figure, // XXX shouldn't figcaption be on this list
1580 nsGkAtoms::footer, nsGkAtoms::form, nsGkAtoms::h1, nsGkAtoms::h2,
1581 nsGkAtoms::h3, nsGkAtoms::h4, nsGkAtoms::h5, nsGkAtoms::h6,
1582 nsGkAtoms::header, nsGkAtoms::hgroup, nsGkAtoms::hr, nsGkAtoms::li,
1583 nsGkAtoms::listing, nsGkAtoms::menu, nsGkAtoms::nav, nsGkAtoms::ol,
1584 nsGkAtoms::p, nsGkAtoms::pre, nsGkAtoms::section, nsGkAtoms::table,
1585 nsGkAtoms::ul, nsGkAtoms::xmp);
1588 /* static */
1589 bool nsContentUtils::ParseIntMarginValue(const nsAString& aString,
1590 nsIntMargin& result) {
1591 nsAutoString marginStr(aString);
1592 marginStr.CompressWhitespace(true, true);
1593 if (marginStr.IsEmpty()) {
1594 return false;
1597 int32_t start = 0, end = 0;
1598 for (int count = 0; count < 4; count++) {
1599 if ((uint32_t)end >= marginStr.Length()) return false;
1601 // top, right, bottom, left
1602 if (count < 3)
1603 end = Substring(marginStr, start).FindChar(',');
1604 else
1605 end = Substring(marginStr, start).Length();
1607 if (end <= 0) return false;
1609 nsresult ec;
1610 int32_t val = nsString(Substring(marginStr, start, end)).ToInteger(&ec);
1611 if (NS_FAILED(ec)) return false;
1613 switch (count) {
1614 case 0:
1615 result.top = val;
1616 break;
1617 case 1:
1618 result.right = val;
1619 break;
1620 case 2:
1621 result.bottom = val;
1622 break;
1623 case 3:
1624 result.left = val;
1625 break;
1627 start += end + 1;
1629 return true;
1632 // static
1633 int32_t nsContentUtils::ParseLegacyFontSize(const nsAString& aValue) {
1634 nsAString::const_iterator iter, end;
1635 aValue.BeginReading(iter);
1636 aValue.EndReading(end);
1638 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1639 ++iter;
1642 if (iter == end) {
1643 return 0;
1646 bool relative = false;
1647 bool negate = false;
1648 if (*iter == char16_t('-')) {
1649 relative = true;
1650 negate = true;
1651 ++iter;
1652 } else if (*iter == char16_t('+')) {
1653 relative = true;
1654 ++iter;
1657 if (iter == end || *iter < char16_t('0') || *iter > char16_t('9')) {
1658 return 0;
1661 // We don't have to worry about overflow, since we can bail out as soon as
1662 // we're bigger than 7.
1663 int32_t value = 0;
1664 while (iter != end && *iter >= char16_t('0') && *iter <= char16_t('9')) {
1665 value = 10 * value + (*iter - char16_t('0'));
1666 if (value >= 7) {
1667 break;
1669 ++iter;
1672 if (relative) {
1673 if (negate) {
1674 value = 3 - value;
1675 } else {
1676 value = 3 + value;
1680 return clamped(value, 1, 7);
1683 /* static */
1684 void nsContentUtils::GetOfflineAppManifest(Document* aDocument, nsIURI** aURI) {
1685 MOZ_ASSERT(NS_IsMainThread());
1686 MOZ_ASSERT(aDocument);
1687 *aURI = nullptr;
1689 if (aDocument->GetController().isSome()) {
1690 return;
1693 Element* docElement = aDocument->GetRootElement();
1694 if (!docElement) {
1695 return;
1698 nsAutoString manifestSpec;
1699 docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
1701 // Manifest URIs can't have fragment identifiers.
1702 if (manifestSpec.IsEmpty() || manifestSpec.Contains('#')) {
1703 return;
1706 nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec, aDocument,
1707 aDocument->GetDocBaseURI());
1710 /* static */
1711 bool nsContentUtils::OfflineAppAllowed(nsIURI* aURI) {
1712 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1713 components::OfflineCacheUpdate::Service();
1714 if (!updateService) {
1715 return false;
1718 bool allowed;
1719 nsresult rv = updateService->OfflineAppAllowedForURI(aURI, &allowed);
1720 return NS_SUCCEEDED(rv) && allowed;
1723 /* static */
1724 bool nsContentUtils::OfflineAppAllowed(nsIPrincipal* aPrincipal) {
1725 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1726 components::OfflineCacheUpdate::Service();
1727 if (!updateService) {
1728 return false;
1731 bool allowed;
1732 nsresult rv = updateService->OfflineAppAllowed(aPrincipal, &allowed);
1733 return NS_SUCCEEDED(rv) && allowed;
1735 // Static
1736 bool nsContentUtils::IsErrorPage(nsIURI* aURI) {
1737 if (!aURI) {
1738 return false;
1741 if (!aURI->SchemeIs("about")) {
1742 return false;
1745 nsAutoCString name;
1746 nsresult rv = NS_GetAboutModuleName(aURI, name);
1747 NS_ENSURE_SUCCESS(rv, false);
1749 return name.EqualsLiteral("certerror") || name.EqualsLiteral("neterror") ||
1750 name.EqualsLiteral("blocked");
1753 // static
1754 void nsContentUtils::Shutdown() {
1755 sInitialized = false;
1757 nsHTMLTags::ReleaseTable();
1759 NS_IF_RELEASE(sContentPolicyService);
1760 sTriedToGetContentPolicy = false;
1761 uint32_t i;
1762 for (i = 0; i < PropertiesFile_COUNT; ++i) NS_IF_RELEASE(sStringBundles[i]);
1764 NS_IF_RELEASE(sStringBundleService);
1765 NS_IF_RELEASE(sConsoleService);
1766 NS_IF_RELEASE(sXPConnect);
1767 NS_IF_RELEASE(sSecurityManager);
1768 NS_IF_RELEASE(sSystemPrincipal);
1769 NS_IF_RELEASE(sNullSubjectPrincipal);
1770 NS_IF_RELEASE(sIOService);
1771 NS_IF_RELEASE(sUUIDGenerator);
1772 sLineBreaker = nullptr;
1773 sWordBreaker = nullptr;
1774 sBidiKeyboard = nullptr;
1776 delete sAtomEventTable;
1777 sAtomEventTable = nullptr;
1778 delete sStringEventTable;
1779 sStringEventTable = nullptr;
1780 delete sUserDefinedEvents;
1781 sUserDefinedEvents = nullptr;
1783 if (sEventListenerManagersHash) {
1784 NS_ASSERTION(sEventListenerManagersHash->EntryCount() == 0,
1785 "Event listener manager hash not empty at shutdown!");
1787 // See comment above.
1789 // However, we have to handle this table differently. If it still
1790 // has entries, we want to leak it too, so that we can keep it alive
1791 // in case any elements are destroyed. Because if they are, we need
1792 // their event listener managers to be destroyed too, or otherwise
1793 // it could leave dangling references in DOMClassInfo's preserved
1794 // wrapper table.
1796 if (sEventListenerManagersHash->EntryCount() == 0) {
1797 delete sEventListenerManagersHash;
1798 sEventListenerManagersHash = nullptr;
1802 if (sDOMArenaHashtable) {
1803 MOZ_ASSERT(sDOMArenaHashtable->Count() == 0);
1804 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
1805 delete sDOMArenaHashtable;
1806 sDOMArenaHashtable = nullptr;
1809 NS_ASSERTION(!sBlockedScriptRunners || sBlockedScriptRunners->Length() == 0,
1810 "How'd this happen?");
1811 delete sBlockedScriptRunners;
1812 sBlockedScriptRunners = nullptr;
1814 delete sShiftText;
1815 sShiftText = nullptr;
1816 delete sControlText;
1817 sControlText = nullptr;
1818 delete sMetaText;
1819 sMetaText = nullptr;
1820 delete sOSText;
1821 sOSText = nullptr;
1822 delete sAltText;
1823 sAltText = nullptr;
1824 delete sModifierSeparator;
1825 sModifierSeparator = nullptr;
1827 delete sJSBytecodeMimeType;
1828 sJSBytecodeMimeType = nullptr;
1830 NS_IF_RELEASE(sSameOriginChecker);
1832 if (sUserInteractionObserver) {
1833 sUserInteractionObserver->Shutdown();
1834 NS_RELEASE(sUserInteractionObserver);
1837 TextControlState::Shutdown();
1838 nsMappedAttributes::Shutdown();
1842 * Checks whether two nodes come from the same origin. aTrustedNode is
1843 * considered 'safe' in that a user can operate on it.
1845 // static
1846 nsresult nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
1847 const nsINode* unTrustedNode) {
1848 MOZ_ASSERT(aTrustedNode);
1849 MOZ_ASSERT(unTrustedNode);
1852 * Get hold of each node's principal
1855 nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
1856 nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
1858 if (trustedPrincipal == unTrustedPrincipal) {
1859 return NS_OK;
1862 bool equal;
1863 // XXXbz should we actually have a Subsumes() check here instead? Or perhaps
1864 // a separate method for that, with callers using one or the other?
1865 if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
1866 !equal) {
1867 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
1870 return NS_OK;
1873 // static
1874 bool nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
1875 nsIPrincipal* aPrincipal) {
1876 bool subsumes;
1877 nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
1878 NS_ENSURE_SUCCESS(rv, false);
1880 if (subsumes) {
1881 return true;
1884 // The subject doesn't subsume aPrincipal. Allow access only if the subject
1885 // is chrome.
1886 return IsCallerChrome();
1889 // static
1890 bool nsContentUtils::CanCallerAccess(const nsINode* aNode) {
1891 nsIPrincipal* subject = SubjectPrincipal();
1892 if (subject->IsSystemPrincipal()) {
1893 return true;
1896 if (aNode->ChromeOnlyAccess()) {
1897 return false;
1900 return CanCallerAccess(subject, aNode->NodePrincipal());
1903 // static
1904 bool nsContentUtils::CanCallerAccess(nsPIDOMWindowInner* aWindow) {
1905 nsCOMPtr<nsIScriptObjectPrincipal> scriptObject = do_QueryInterface(aWindow);
1906 NS_ENSURE_TRUE(scriptObject, false);
1908 return CanCallerAccess(SubjectPrincipal(), scriptObject->GetPrincipal());
1911 // static
1912 bool nsContentUtils::PrincipalHasPermission(nsIPrincipal& aPrincipal,
1913 const nsAtom* aPerm) {
1914 // Chrome gets access by default.
1915 if (aPrincipal.IsSystemPrincipal()) {
1916 return true;
1919 // Otherwise, only allow if caller is an addon with the permission.
1920 return BasePrincipal::Cast(aPrincipal).AddonHasPermission(aPerm);
1923 // static
1924 bool nsContentUtils::CallerHasPermission(JSContext* aCx, const nsAtom* aPerm) {
1925 return PrincipalHasPermission(*SubjectPrincipal(aCx), aPerm);
1928 // static
1929 nsIPrincipal* nsContentUtils::GetAttrTriggeringPrincipal(
1930 nsIContent* aContent, const nsAString& aAttrValue,
1931 nsIPrincipal* aSubjectPrincipal) {
1932 nsIPrincipal* contentPrin = aContent ? aContent->NodePrincipal() : nullptr;
1934 // If the subject principal is the same as the content principal, or no
1935 // explicit subject principal was provided, we don't need to do any further
1936 // checks. Just return the content principal.
1937 if (contentPrin == aSubjectPrincipal || !aSubjectPrincipal) {
1938 return contentPrin;
1941 // Only use the subject principal if the URL string we are going to end up
1942 // fetching is under the control of that principal, which is never the case
1943 // for relative URLs.
1944 if (aAttrValue.IsEmpty() ||
1945 !IsAbsoluteURL(NS_ConvertUTF16toUTF8(aAttrValue))) {
1946 return contentPrin;
1949 // Only use the subject principal as the attr triggering principal if it
1950 // should override the CSP of the node's principal.
1951 if (BasePrincipal::Cast(aSubjectPrincipal)->OverridesCSP(contentPrin)) {
1952 return aSubjectPrincipal;
1955 return contentPrin;
1958 // static
1959 bool nsContentUtils::IsAbsoluteURL(const nsACString& aURL) {
1960 nsAutoCString scheme;
1961 if (NS_FAILED(net_ExtractURLScheme(aURL, scheme))) {
1962 // If we can't extract a scheme, it's not an absolute URL.
1963 return false;
1966 // If it parses as an absolute StandardURL, it's definitely an absolute URL,
1967 // so no need to check with the IO service.
1968 if (net_IsAbsoluteURL(aURL)) {
1969 return true;
1972 uint32_t flags;
1973 if (NS_SUCCEEDED(sIOService->GetProtocolFlags(scheme.get(), &flags))) {
1974 return flags & nsIProtocolHandler::URI_NORELATIVE;
1977 return false;
1980 // static
1981 bool nsContentUtils::InProlog(nsINode* aNode) {
1982 MOZ_ASSERT(aNode, "missing node to nsContentUtils::InProlog");
1984 nsINode* parent = aNode->GetParentNode();
1985 if (!parent || !parent->IsDocument()) {
1986 return false;
1989 Document* doc = parent->AsDocument();
1990 nsIContent* root = doc->GetRootElement();
1992 return !root || doc->ComputeIndexOf(aNode) < doc->ComputeIndexOf(root);
1995 bool nsContentUtils::IsCallerChrome() {
1996 MOZ_ASSERT(NS_IsMainThread());
1997 return SubjectPrincipal() == sSystemPrincipal;
2000 #ifdef FUZZING
2001 bool nsContentUtils::IsFuzzingEnabled() {
2002 return StaticPrefs::fuzzing_enabled();
2004 #endif
2006 /* static */
2007 bool nsContentUtils::IsCallerChromeOrElementTransformGettersEnabled(
2008 JSContext* aCx, JSObject*) {
2009 return ThreadsafeIsSystemCaller(aCx) ||
2010 StaticPrefs::dom_element_transform_getters_enabled();
2013 /* static */
2014 bool nsContentUtils::ShouldResistFingerprinting() {
2015 return StaticPrefs::privacy_resistFingerprinting();
2018 bool nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell) {
2019 if (!aDocShell) {
2020 return ShouldResistFingerprinting();
2022 return ShouldResistFingerprinting(aDocShell->GetDocument());
2025 /* static */
2026 bool nsContentUtils::ShouldResistFingerprinting(const Document* aDoc) {
2027 if (!aDoc) {
2028 return ShouldResistFingerprinting();
2030 bool isChrome = nsContentUtils::IsChromeDoc(aDoc);
2031 return !isChrome && ShouldResistFingerprinting();
2034 /* static */
2035 bool nsContentUtils::ShouldResistFingerprinting(nsIPrincipal* aPrincipal) {
2036 if (!aPrincipal) {
2037 return ShouldResistFingerprinting();
2039 bool isChrome = aPrincipal->IsSystemPrincipal();
2040 return !isChrome && ShouldResistFingerprinting();
2043 /* static */
2044 bool nsContentUtils::ShouldResistFingerprinting(WorkerPrivate* aWorkerPrivate) {
2045 if (!aWorkerPrivate) {
2046 // We may be on a non-worker thread!
2047 return ShouldResistFingerprinting();
2049 bool isChrome = aWorkerPrivate->UsesSystemPrincipal();
2050 return !isChrome && ShouldResistFingerprinting();
2053 /* static */
2054 bool nsContentUtils::UseStandinsForNativeColors() {
2055 return ShouldResistFingerprinting() ||
2056 StaticPrefs::ui_use_standins_for_native_colors();
2059 /* static */
2060 void nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
2061 int32_t aChromeWidth, int32_t aChromeHeight, int32_t aScreenWidth,
2062 int32_t aScreenHeight, int32_t aInputWidth, int32_t aInputHeight,
2063 bool aSetOuterWidth, bool aSetOuterHeight, int32_t* aOutputWidth,
2064 int32_t* aOutputHeight) {
2065 MOZ_ASSERT(aOutputWidth);
2066 MOZ_ASSERT(aOutputHeight);
2068 int32_t availContentWidth = 0;
2069 int32_t availContentHeight = 0;
2071 availContentWidth = std::min(StaticPrefs::privacy_window_maxInnerWidth(),
2072 aScreenWidth - aChromeWidth);
2073 #ifdef MOZ_WIDGET_GTK
2074 // In the GTK window, it will not report outside system decorations
2075 // when we get available window size, see Bug 581863. So, we leave a
2076 // 40 pixels space for them when calculating the available content
2077 // height. It is not necessary for the width since the content width
2078 // is usually pretty much the same as the chrome width.
2079 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2080 (-40 + aScreenHeight) - aChromeHeight);
2081 #else
2082 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2083 aScreenHeight - aChromeHeight);
2084 #endif
2086 // Ideally, we'd like to round window size to 1000x1000, but the
2087 // screen space could be too small to accommodate this size in some
2088 // cases. If it happens, we would round the window size to the nearest
2089 // 200x100.
2090 availContentWidth = availContentWidth - (availContentWidth % 200);
2091 availContentHeight = availContentHeight - (availContentHeight % 100);
2093 // If aIsOuter is true, we are setting the outer window. So we
2094 // have to consider the chrome UI.
2095 int32_t chromeOffsetWidth = aSetOuterWidth ? aChromeWidth : 0;
2096 int32_t chromeOffsetHeight = aSetOuterHeight ? aChromeHeight : 0;
2097 int32_t resultWidth = 0, resultHeight = 0;
2099 // if the original size is greater than the maximum available size, we set
2100 // it to the maximum size. And if the original value is less than the
2101 // minimum rounded size, we set it to the minimum 200x100.
2102 if (aInputWidth > (availContentWidth + chromeOffsetWidth)) {
2103 resultWidth = availContentWidth + chromeOffsetWidth;
2104 } else if (aInputWidth < (200 + chromeOffsetWidth)) {
2105 resultWidth = 200 + chromeOffsetWidth;
2106 } else {
2107 // Otherwise, we round the window to the nearest upper rounded 200x100.
2108 resultWidth = NSToIntCeil((aInputWidth - chromeOffsetWidth) / 200.0) * 200 +
2109 chromeOffsetWidth;
2112 if (aInputHeight > (availContentHeight + chromeOffsetHeight)) {
2113 resultHeight = availContentHeight + chromeOffsetHeight;
2114 } else if (aInputHeight < (100 + chromeOffsetHeight)) {
2115 resultHeight = 100 + chromeOffsetHeight;
2116 } else {
2117 resultHeight =
2118 NSToIntCeil((aInputHeight - chromeOffsetHeight) / 100.0) * 100 +
2119 chromeOffsetHeight;
2122 *aOutputWidth = resultWidth;
2123 *aOutputHeight = resultHeight;
2126 bool nsContentUtils::ThreadsafeIsCallerChrome() {
2127 return NS_IsMainThread() ? IsCallerChrome()
2128 : IsCurrentThreadRunningChromeWorker();
2131 bool nsContentUtils::IsCallerUAWidget() {
2132 JSContext* cx = GetCurrentJSContext();
2133 if (!cx) {
2134 return false;
2137 JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
2138 if (!realm) {
2139 return false;
2142 return xpc::IsUAWidgetScope(realm);
2145 bool nsContentUtils::IsSystemCaller(JSContext* aCx) {
2146 // Note that SubjectPrincipal() assumes we are in a compartment here.
2147 return SubjectPrincipal(aCx) == sSystemPrincipal;
2150 bool nsContentUtils::ThreadsafeIsSystemCaller(JSContext* aCx) {
2151 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
2152 MOZ_ASSERT(ccjscx->Context() == aCx);
2154 return ccjscx->IsSystemCaller();
2157 // static
2158 bool nsContentUtils::LookupBindingMember(
2159 JSContext* aCx, nsIContent* aContent, JS::Handle<jsid> aId,
2160 JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
2161 return true;
2164 // static
2165 nsINode* nsContentUtils::GetCrossDocParentNode(nsINode* aChild) {
2166 MOZ_ASSERT(aChild, "The child is null!");
2168 nsINode* parent = aChild->GetParentNode();
2169 if (parent && parent->IsContent() && aChild->IsContent()) {
2170 parent = aChild->AsContent()->GetFlattenedTreeParent();
2173 if (parent || !aChild->IsDocument()) {
2174 return parent;
2177 Document* doc = aChild->AsDocument();
2178 Document* parentDoc = doc->GetInProcessParentDocument();
2179 return parentDoc ? parentDoc->FindContentForSubDocument(doc) : nullptr;
2182 nsINode* nsContentUtils::GetNearestInProcessCrossDocParentNode(
2183 nsINode* aChild) {
2184 if (aChild->IsDocument()) {
2185 for (BrowsingContext* bc = aChild->AsDocument()->GetBrowsingContext(); bc;
2186 bc = bc->GetParent()) {
2187 if (bc->GetEmbedderElement()) {
2188 return bc->GetEmbedderElement();
2191 return nullptr;
2194 nsINode* parent = aChild->GetParentNode();
2195 if (parent && parent->IsContent() && aChild->IsContent()) {
2196 parent = aChild->AsContent()->GetFlattenedTreeParent();
2199 return parent;
2202 bool nsContentUtils::ContentIsHostIncludingDescendantOf(
2203 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2204 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2205 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2207 do {
2208 if (aPossibleDescendant == aPossibleAncestor) return true;
2209 if (aPossibleDescendant->IsDocumentFragment()) {
2210 aPossibleDescendant =
2211 aPossibleDescendant->AsDocumentFragment()->GetHost();
2212 } else {
2213 aPossibleDescendant = aPossibleDescendant->GetParentNode();
2215 } while (aPossibleDescendant);
2217 return false;
2220 // static
2221 bool nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
2222 nsINode* aPossibleAncestor) {
2223 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2224 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2226 do {
2227 if (aPossibleDescendant == aPossibleAncestor) {
2228 return true;
2231 aPossibleDescendant =
2232 GetNearestInProcessCrossDocParentNode(aPossibleDescendant);
2233 } while (aPossibleDescendant);
2235 return false;
2238 // static
2239 bool nsContentUtils::ContentIsFlattenedTreeDescendantOf(
2240 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2241 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2242 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2244 do {
2245 if (aPossibleDescendant == aPossibleAncestor) {
2246 return true;
2248 aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
2249 } while (aPossibleDescendant);
2251 return false;
2254 // static
2255 bool nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
2256 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2257 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2258 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2260 do {
2261 if (aPossibleDescendant == aPossibleAncestor) {
2262 return true;
2264 aPossibleDescendant =
2265 aPossibleDescendant->GetFlattenedTreeParentNodeForStyle();
2266 } while (aPossibleDescendant);
2268 return false;
2271 // static
2272 nsINode* nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB) {
2273 while (true && aTargetA) {
2274 // If A's root is not a shadow root...
2275 nsINode* root = aTargetA->SubtreeRoot();
2276 if (!root->IsShadowRoot()) {
2277 // ...then return A.
2278 return aTargetA;
2281 // or A's root is a shadow-including inclusive ancestor of B...
2282 if (aTargetB->IsShadowIncludingInclusiveDescendantOf(root)) {
2283 // ...then return A.
2284 return aTargetA;
2287 aTargetA = ShadowRoot::FromNode(root)->GetHost();
2290 return nullptr;
2293 // static
2294 nsresult nsContentUtils::GetInclusiveAncestors(nsINode* aNode,
2295 nsTArray<nsINode*>& aArray) {
2296 while (aNode) {
2297 aArray.AppendElement(aNode);
2298 aNode = aNode->GetParentNode();
2300 return NS_OK;
2303 // static
2304 nsresult nsContentUtils::GetInclusiveAncestorsAndOffsets(
2305 nsINode* aNode, int32_t aOffset, nsTArray<nsIContent*>* aAncestorNodes,
2306 nsTArray<int32_t>* aAncestorOffsets) {
2307 NS_ENSURE_ARG_POINTER(aNode);
2309 if (!aNode->IsContent()) {
2310 return NS_ERROR_FAILURE;
2312 nsIContent* content = aNode->AsContent();
2314 if (!aAncestorNodes->IsEmpty()) {
2315 NS_WARNING("aAncestorNodes is not empty");
2316 aAncestorNodes->Clear();
2319 if (!aAncestorOffsets->IsEmpty()) {
2320 NS_WARNING("aAncestorOffsets is not empty");
2321 aAncestorOffsets->Clear();
2324 // insert the node itself
2325 aAncestorNodes->AppendElement(content);
2326 aAncestorOffsets->AppendElement(aOffset);
2328 // insert all the ancestors
2329 nsIContent* child = content;
2330 nsIContent* parent = child->GetParent();
2331 while (parent) {
2332 aAncestorNodes->AppendElement(parent);
2333 aAncestorOffsets->AppendElement(parent->ComputeIndexOf(child));
2334 child = parent;
2335 parent = parent->GetParent();
2338 return NS_OK;
2341 template <typename Node, typename GetParentFunc>
2342 static Node* GetCommonAncestorInternal(Node* aNode1, Node* aNode2,
2343 GetParentFunc aGetParentFunc) {
2344 MOZ_ASSERT(aNode1 != aNode2);
2346 // Build the chain of parents
2347 AutoTArray<Node*, 30> parents1, parents2;
2348 do {
2349 parents1.AppendElement(aNode1);
2350 aNode1 = aGetParentFunc(aNode1);
2351 } while (aNode1);
2352 do {
2353 parents2.AppendElement(aNode2);
2354 aNode2 = aGetParentFunc(aNode2);
2355 } while (aNode2);
2357 // Find where the parent chain differs
2358 uint32_t pos1 = parents1.Length();
2359 uint32_t pos2 = parents2.Length();
2360 Node* parent = nullptr;
2361 uint32_t len;
2362 for (len = std::min(pos1, pos2); len > 0; --len) {
2363 Node* child1 = parents1.ElementAt(--pos1);
2364 Node* child2 = parents2.ElementAt(--pos2);
2365 if (child1 != child2) {
2366 break;
2368 parent = child1;
2371 return parent;
2374 /* static */
2375 nsINode* nsContentUtils::GetCommonAncestorHelper(nsINode* aNode1,
2376 nsINode* aNode2) {
2377 return GetCommonAncestorInternal(
2378 aNode1, aNode2, [](nsINode* aNode) { return aNode->GetParentNode(); });
2381 /* static */
2382 nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorHelper(
2383 nsIContent* aContent1, nsIContent* aContent2) {
2384 return GetCommonAncestorInternal(
2385 aContent1, aContent2,
2386 [](nsIContent* aContent) { return aContent->GetFlattenedTreeParent(); });
2389 /* static */
2390 Element* nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
2391 Element* aElement1, Element* aElement2) {
2392 return GetCommonAncestorInternal(aElement1, aElement2, [](Element* aElement) {
2393 return aElement->GetFlattenedTreeParentElementForStyle();
2397 /* static */
2398 bool nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2,
2399 int32_t* aNode1Index,
2400 int32_t* aNode2Index) {
2401 // Note, CompareDocumentPosition takes the latter params in different order.
2402 return (aNode2->CompareDocumentPosition(*aNode1, aNode2Index, aNode1Index) &
2403 (Node_Binding::DOCUMENT_POSITION_PRECEDING |
2404 Node_Binding::DOCUMENT_POSITION_DISCONNECTED)) ==
2405 Node_Binding::DOCUMENT_POSITION_PRECEDING;
2408 /* static */
2409 Maybe<int32_t> nsContentUtils::ComparePoints(
2410 const nsINode* aParent1, int32_t aOffset1, const nsINode* aParent2,
2411 int32_t aOffset2, ComparePointsCache* aParent1Cache) {
2412 bool disconnected{false};
2414 const int32_t order = ComparePoints_Deprecated(
2415 aParent1, aOffset1, aParent2, aOffset2, &disconnected, aParent1Cache);
2416 if (disconnected) {
2417 return Nothing();
2420 return Some(order);
2423 /* static */
2424 int32_t nsContentUtils::ComparePoints_Deprecated(
2425 const nsINode* aParent1, int32_t aOffset1, const nsINode* aParent2,
2426 int32_t aOffset2, bool* aDisconnected, ComparePointsCache* aParent1Cache) {
2427 if (aParent1 == aParent2) {
2428 // XXX This is odd. aOffset1 and/or aOffset2 may be -1, e.g., it's result
2429 // of nsINode::ComputeIndexOf(), but this compares such invalid
2430 // offset with valid offset.
2431 return aOffset1 < aOffset2 ? -1 : aOffset1 > aOffset2 ? 1 : 0;
2434 AutoTArray<const nsINode*, 32> parents1, parents2;
2435 const nsINode* node1 = aParent1;
2436 const nsINode* node2 = aParent2;
2437 do {
2438 parents1.AppendElement(node1);
2439 node1 = node1->GetParentNode();
2440 } while (node1);
2441 do {
2442 parents2.AppendElement(node2);
2443 node2 = node2->GetParentNode();
2444 } while (node2);
2446 uint32_t pos1 = parents1.Length() - 1;
2447 uint32_t pos2 = parents2.Length() - 1;
2449 bool disconnected = parents1.ElementAt(pos1) != parents2.ElementAt(pos2);
2450 if (aDisconnected) {
2451 *aDisconnected = disconnected;
2453 if (disconnected) {
2454 NS_ASSERTION(aDisconnected, "unexpected disconnected nodes");
2455 return 1;
2458 // Find where the parent chains differ
2459 const nsINode* parent = parents1.ElementAt(pos1);
2460 uint32_t len;
2461 for (len = std::min(pos1, pos2); len > 0; --len) {
2462 const nsINode* child1 = parents1.ElementAt(--pos1);
2463 const nsINode* child2 = parents2.ElementAt(--pos2);
2464 if (child1 != child2) {
2465 int32_t child1index = aParent1Cache
2466 ? aParent1Cache->ComputeIndexOf(parent, child1)
2467 : parent->ComputeIndexOf(child1);
2468 return child1index < parent->ComputeIndexOf(child2) ? -1 : 1;
2470 parent = child1;
2473 // The parent chains never differed, so one of the nodes is an ancestor of
2474 // the other
2476 NS_ASSERTION(!pos1 || !pos2,
2477 "should have run out of parent chain for one of the nodes");
2479 if (!pos1) {
2480 const nsINode* child2 = parents2.ElementAt(--pos2);
2481 // XXX aOffset1 may be -1 as mentioned above. So, why does this return
2482 // it's *before* of the valid DOM point?
2483 return aOffset1 <= parent->ComputeIndexOf(child2) ? -1 : 1;
2486 const nsINode* child1 = parents1.ElementAt(--pos1);
2487 // XXX aOffset2 may be -1 as mentioned above. So, why does this return it's
2488 // *after* of the valid DOM point?
2489 int32_t child1index = aParent1Cache
2490 ? aParent1Cache->ComputeIndexOf(parent, child1)
2491 : parent->ComputeIndexOf(child1);
2492 return child1index < aOffset2 ? -1 : 1;
2495 // static
2496 nsINode* nsContentUtils::GetCommonAncestorUnderInteractiveContent(
2497 nsINode* aNode1, nsINode* aNode2) {
2498 if (!aNode1 || !aNode2) {
2499 return nullptr;
2502 if (aNode1 == aNode2) {
2503 return aNode1;
2506 // Build the chain of parents
2507 AutoTArray<nsINode*, 30> parents1;
2508 do {
2509 parents1.AppendElement(aNode1);
2510 if (aNode1->IsElement() &&
2511 aNode1->AsElement()->IsInteractiveHTMLContent()) {
2512 break;
2514 aNode1 = aNode1->GetFlattenedTreeParentNode();
2515 } while (aNode1);
2517 AutoTArray<nsINode*, 30> parents2;
2518 do {
2519 parents2.AppendElement(aNode2);
2520 if (aNode2->IsElement() &&
2521 aNode2->AsElement()->IsInteractiveHTMLContent()) {
2522 break;
2524 aNode2 = aNode2->GetFlattenedTreeParentNode();
2525 } while (aNode2);
2527 // Find where the parent chain differs
2528 uint32_t pos1 = parents1.Length();
2529 uint32_t pos2 = parents2.Length();
2530 nsINode* parent = nullptr;
2531 for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
2532 nsINode* child1 = parents1.ElementAt(--pos1);
2533 nsINode* child2 = parents2.ElementAt(--pos2);
2534 if (child1 != child2) {
2535 break;
2537 parent = child1;
2540 return parent;
2543 /* static */
2544 template <typename FPT, typename FRT, typename SPT, typename SRT>
2545 Maybe<int32_t> nsContentUtils::ComparePoints(
2546 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
2547 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary) {
2548 if (!aFirstBoundary.IsSet() || !aSecondBoundary.IsSet()) {
2549 return Nothing{};
2552 bool disconnected{false};
2553 const int32_t order =
2554 ComparePoints_Deprecated(aFirstBoundary, aSecondBoundary, &disconnected);
2556 if (disconnected) {
2557 return Nothing{};
2560 return Some(order);
2563 /* static */
2564 template <typename FPT, typename FRT, typename SPT, typename SRT>
2565 int32_t nsContentUtils::ComparePoints_Deprecated(
2566 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
2567 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary, bool* aDisconnected) {
2568 if (NS_WARN_IF(!aFirstBoundary.IsSet()) ||
2569 NS_WARN_IF(!aSecondBoundary.IsSet())) {
2570 return -1;
2572 // XXX Re-implement this without calling `Offset()` as far as possible,
2573 // and the other overload should be an alias of this.
2574 return ComparePoints_Deprecated(
2575 aFirstBoundary.Container(),
2576 *aFirstBoundary.Offset(
2577 RangeBoundaryBase<FPT, FRT>::OffsetFilter::kValidOrInvalidOffsets),
2578 aSecondBoundary.Container(),
2579 *aSecondBoundary.Offset(
2580 RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOrInvalidOffsets),
2581 aDisconnected);
2584 inline bool IsCharInSet(const char* aSet, const char16_t aChar) {
2585 char16_t ch;
2586 while ((ch = *aSet)) {
2587 if (aChar == char16_t(ch)) {
2588 return true;
2590 ++aSet;
2592 return false;
2596 * This method strips leading/trailing chars, in given set, from string.
2599 // static
2600 const nsDependentSubstring nsContentUtils::TrimCharsInSet(
2601 const char* aSet, const nsAString& aValue) {
2602 nsAString::const_iterator valueCurrent, valueEnd;
2604 aValue.BeginReading(valueCurrent);
2605 aValue.EndReading(valueEnd);
2607 // Skip characters in the beginning
2608 while (valueCurrent != valueEnd) {
2609 if (!IsCharInSet(aSet, *valueCurrent)) {
2610 break;
2612 ++valueCurrent;
2615 if (valueCurrent != valueEnd) {
2616 for (;;) {
2617 --valueEnd;
2618 if (!IsCharInSet(aSet, *valueEnd)) {
2619 break;
2622 ++valueEnd; // Step beyond the last character we want in the value.
2625 // valueEnd should point to the char after the last to copy
2626 return Substring(valueCurrent, valueEnd);
2630 * This method strips leading and trailing whitespace from a string.
2633 // static
2634 template <bool IsWhitespace(char16_t)>
2635 const nsDependentSubstring nsContentUtils::TrimWhitespace(const nsAString& aStr,
2636 bool aTrimTrailing) {
2637 nsAString::const_iterator start, end;
2639 aStr.BeginReading(start);
2640 aStr.EndReading(end);
2642 // Skip whitespace characters in the beginning
2643 while (start != end && IsWhitespace(*start)) {
2644 ++start;
2647 if (aTrimTrailing) {
2648 // Skip whitespace characters in the end.
2649 while (end != start) {
2650 --end;
2652 if (!IsWhitespace(*end)) {
2653 // Step back to the last non-whitespace character.
2654 ++end;
2656 break;
2661 // Return a substring for the string w/o leading and/or trailing
2662 // whitespace
2664 return Substring(start, end);
2667 // Declaring the templates we are going to use avoid linking issues without
2668 // inlining the method. Considering there is not so much spaces checking
2669 // methods we can consider this to be better than inlining.
2670 template const nsDependentSubstring
2671 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, bool);
2672 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
2673 nsContentUtils::IsHTMLWhitespace>(const nsAString&, bool);
2674 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
2675 nsContentUtils::IsHTMLWhitespaceOrNBSP>(const nsAString&, bool);
2677 static inline void KeyAppendSep(nsACString& aKey) {
2678 if (!aKey.IsEmpty()) {
2679 aKey.Append('>');
2683 static inline void KeyAppendString(const nsAString& aString, nsACString& aKey) {
2684 KeyAppendSep(aKey);
2686 // Could escape separator here if collisions happen. > is not a legal char
2687 // for a name or type attribute, so we should be safe avoiding that extra
2688 // work.
2690 AppendUTF16toUTF8(aString, aKey);
2693 static inline void KeyAppendString(const nsACString& aString,
2694 nsACString& aKey) {
2695 KeyAppendSep(aKey);
2697 // Could escape separator here if collisions happen. > is not a legal char
2698 // for a name or type attribute, so we should be safe avoiding that extra
2699 // work.
2701 aKey.Append(aString);
2704 static inline void KeyAppendInt(int32_t aInt, nsACString& aKey) {
2705 KeyAppendSep(aKey);
2707 aKey.AppendInt(aInt);
2710 static inline bool IsAutocompleteOff(const nsIContent* aContent) {
2711 return aContent->IsElement() &&
2712 aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
2713 nsGkAtoms::autocomplete, u"off"_ns,
2714 eIgnoreCase);
2717 /*static*/
2718 void nsContentUtils::GenerateStateKey(nsIContent* aContent, Document* aDocument,
2719 nsACString& aKey) {
2720 MOZ_ASSERT(aContent);
2722 aKey.Truncate();
2724 uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
2726 // Don't capture state for anonymous content
2727 if (aContent->IsInNativeAnonymousSubtree()) {
2728 return;
2731 if (IsAutocompleteOff(aContent)) {
2732 return;
2735 RefPtr<Document> doc = aContent->GetUncomposedDoc();
2737 KeyAppendInt(partID, aKey); // first append a partID
2738 bool generatedUniqueKey = false;
2740 if (doc && doc->IsHTMLOrXHTML()) {
2741 nsHTMLDocument* htmlDoc = doc->AsHTMLDocument();
2743 // If we have a form control and can calculate form information, use that
2744 // as the key - it is more reliable than just recording position in the
2745 // DOM.
2746 // XXXbz Is it, really? We have bugs on this, I think...
2747 // Important to have a unique key, and tag/type/name may not be.
2749 // The format of the key depends on whether the control has a form,
2750 // and whether the element was parser inserted:
2752 // [Has Form, Parser Inserted]:
2753 // fp>type>FormNum>IndOfControlInForm>FormName>name
2755 // [No Form, Parser Inserted]:
2756 // dp>type>ControlNum>name
2758 // [Has Form, Not Parser Inserted]:
2759 // fn>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
2761 // [No Form, Not Parser Inserted]:
2762 // dn>type>IndOfControlInDoc>name
2764 // XXX We don't need to use index if name is there
2765 // XXXbz We don't? Why not? I don't follow.
2767 nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
2768 if (control) {
2769 // Get the control number if this was a parser inserted element from the
2770 // network.
2771 int32_t controlNumber =
2772 control->GetParserInsertedControlNumberForStateKey();
2773 bool parserInserted = controlNumber != -1;
2775 RefPtr<nsContentList> htmlForms;
2776 RefPtr<nsContentList> htmlFormControls;
2777 if (!parserInserted) {
2778 // Getting these lists is expensive, as we need to keep them up to date
2779 // as the document loads, so we avoid it if we don't need them.
2780 htmlDoc->GetFormsAndFormControls(getter_AddRefs(htmlForms),
2781 getter_AddRefs(htmlFormControls));
2784 // Append the control type
2785 KeyAppendInt(control->ControlType(), aKey);
2787 // If in a form, add form name / index of form / index in form
2788 HTMLFormElement* formElement = control->GetFormElement();
2789 if (formElement) {
2790 if (IsAutocompleteOff(formElement)) {
2791 aKey.Truncate();
2792 return;
2795 // Append the form number, if this is a parser inserted control, or
2796 // the index of the form in the document otherwise.
2797 bool appendedForm = false;
2798 if (parserInserted) {
2799 MOZ_ASSERT(formElement->GetFormNumberForStateKey() != -1,
2800 "when generating a state key for a parser inserted form "
2801 "control we should have a parser inserted <form> element");
2802 KeyAppendString("fp"_ns, aKey);
2803 KeyAppendInt(formElement->GetFormNumberForStateKey(), aKey);
2804 appendedForm = true;
2805 } else {
2806 KeyAppendString("fn"_ns, aKey);
2807 int32_t index = htmlForms->IndexOf(formElement, false);
2808 if (index <= -1) {
2810 // XXX HACK this uses some state that was dumped into the document
2811 // specifically to fix bug 138892. What we are trying to do is
2812 // *guess* which form this control's state is found in, with the
2813 // highly likely guess that the highest form parsed so far is the
2814 // one. This code should not be on trunk, only branch.
2816 index = htmlDoc->GetNumFormsSynchronous() - 1;
2818 if (index > -1) {
2819 KeyAppendInt(index, aKey);
2820 appendedForm = true;
2824 if (appendedForm) {
2825 // Append the index of the control in the form
2826 int32_t index = formElement->IndexOfControl(control);
2828 if (index > -1) {
2829 KeyAppendInt(index, aKey);
2830 generatedUniqueKey = true;
2834 // Append the form name
2835 nsAutoString formName;
2836 formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
2837 KeyAppendString(formName, aKey);
2838 } else {
2839 // Not in a form. Append the control number, if this is a parser
2840 // inserted control, or the index of the control in the document
2841 // otherwise.
2842 if (parserInserted) {
2843 KeyAppendString("dp"_ns, aKey);
2844 KeyAppendInt(control->GetParserInsertedControlNumberForStateKey(),
2845 aKey);
2846 generatedUniqueKey = true;
2847 } else {
2848 KeyAppendString("dn"_ns, aKey);
2849 int32_t index = htmlFormControls->IndexOf(aContent, true);
2850 if (index > -1) {
2851 KeyAppendInt(index, aKey);
2852 generatedUniqueKey = true;
2856 // Append the control name
2857 nsAutoString name;
2858 aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name,
2859 name);
2860 KeyAppendString(name, aKey);
2865 if (!generatedUniqueKey) {
2866 // Either we didn't have a form control or we aren't in an HTML document so
2867 // we can't figure out form info. Append the tag name if it's an element
2868 // to avoid restoring state for one type of element on another type.
2869 if (aContent->IsElement()) {
2870 KeyAppendString(nsDependentAtomString(aContent->NodeInfo()->NameAtom()),
2871 aKey);
2872 } else {
2873 // Append a character that is not "d" or "f" to disambiguate from
2874 // the case when we were a form control in an HTML document.
2875 KeyAppendString("o"_ns, aKey);
2878 // Now start at aContent and append the indices of it and all its ancestors
2879 // in their containers. That should at least pin down its position in the
2880 // DOM...
2881 nsINode* parent = aContent->GetParentNode();
2882 nsINode* content = aContent;
2883 while (parent) {
2884 KeyAppendInt(parent->ComputeIndexOf(content), aKey);
2885 content = parent;
2886 parent = content->GetParentNode();
2891 // static
2892 nsIPrincipal* nsContentUtils::SubjectPrincipal(JSContext* aCx) {
2893 MOZ_ASSERT(NS_IsMainThread());
2895 // As opposed to SubjectPrincipal(), we do in fact assume that
2896 // we're in a realm here; anyone who calls this function in
2897 // situations where that's not the case is doing it wrong.
2898 JS::Realm* realm = js::GetContextRealm(aCx);
2899 MOZ_ASSERT(realm);
2901 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
2902 return nsJSPrincipals::get(principals);
2905 // static
2906 nsIPrincipal* nsContentUtils::SubjectPrincipal() {
2907 MOZ_ASSERT(IsInitialized());
2908 MOZ_ASSERT(NS_IsMainThread());
2909 JSContext* cx = GetCurrentJSContext();
2910 if (!cx) {
2911 MOZ_CRASH(
2912 "Accessing the Subject Principal without an AutoJSAPI on the stack is "
2913 "forbidden");
2916 JS::Realm* realm = js::GetContextRealm(cx);
2918 // When an AutoJSAPI is instantiated, we are in a null realm until the
2919 // first JSAutoRealm, which is kind of a purgatory as far as permissions
2920 // go. It would be nice to just hard-abort if somebody does a security check
2921 // in this purgatory zone, but that would be too fragile, since it could be
2922 // triggered by random IsCallerChrome() checks 20-levels deep.
2924 // So we want to return _something_ here - and definitely not the System
2925 // Principal, since that would make an AutoJSAPI a very dangerous thing to
2926 // instantiate.
2928 // The natural thing to return is a null principal. Ideally, we'd return a
2929 // different null principal each time, to avoid any unexpected interactions
2930 // when the principal accidentally gets inherited somewhere. But
2931 // SubjectPrincipal doesn't return strong references, so there's no way to
2932 // sanely manage the lifetime of multiple null principals.
2934 // So we use a singleton null principal. To avoid it being accidentally
2935 // inherited and becoming a "real" subject or object principal, we do a
2936 // release-mode assert during realm creation against using this principal on
2937 // an actual global.
2938 if (!realm) {
2939 return sNullSubjectPrincipal;
2942 return SubjectPrincipal(cx);
2945 // static
2946 nsIPrincipal* nsContentUtils::ObjectPrincipal(JSObject* aObj) {
2947 MOZ_ASSERT(NS_IsMainThread());
2949 #ifdef DEBUG
2950 JS::AssertObjectBelongsToCurrentThread(aObj);
2951 #endif
2953 MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(aObj));
2955 JS::Realm* realm = js::GetNonCCWObjectRealm(aObj);
2956 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
2957 return nsJSPrincipals::get(principals);
2960 // static
2961 nsresult nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
2962 const nsAString& aSpec,
2963 Document* aDocument,
2964 nsIURI* aBaseURI) {
2965 if (aDocument) {
2966 return NS_NewURI(aResult, aSpec, aDocument->GetDocumentCharacterSet(),
2967 aBaseURI);
2969 return NS_NewURI(aResult, aSpec, nullptr, aBaseURI);
2972 // static
2973 bool nsContentUtils::IsNameWithDash(nsAtom* aName) {
2974 // A valid custom element name is a sequence of characters name which
2975 // must match the PotentialCustomElementName production:
2976 // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
2977 const char16_t* name = aName->GetUTF16String();
2978 uint32_t len = aName->GetLength();
2979 bool hasDash = false;
2981 if (!len || name[0] < 'a' || name[0] > 'z') {
2982 return false;
2985 uint32_t i = 1;
2986 while (i < len) {
2987 if (i + 1 < len && NS_IS_SURROGATE_PAIR(name[i], name[i + 1])) {
2988 // Merged two 16-bit surrogate pairs into code point.
2989 char32_t code = SURROGATE_TO_UCS4(name[i], name[i + 1]);
2991 if (code < 0x10000 || code > 0xEFFFF) {
2992 return false;
2995 i += 2;
2996 } else {
2997 if (name[i] == '-') {
2998 hasDash = true;
3001 if (name[i] != '-' && name[i] != '.' && name[i] != '_' &&
3002 name[i] != 0xB7 && (name[i] < '0' || name[i] > '9') &&
3003 (name[i] < 'a' || name[i] > 'z') &&
3004 (name[i] < 0xC0 || name[i] > 0xD6) &&
3005 (name[i] < 0xF8 || name[i] > 0x37D) &&
3006 (name[i] < 0x37F || name[i] > 0x1FFF) &&
3007 (name[i] < 0x200C || name[i] > 0x200D) &&
3008 (name[i] < 0x203F || name[i] > 0x2040) &&
3009 (name[i] < 0x2070 || name[i] > 0x218F) &&
3010 (name[i] < 0x2C00 || name[i] > 0x2FEF) &&
3011 (name[i] < 0x3001 || name[i] > 0xD7FF) &&
3012 (name[i] < 0xF900 || name[i] > 0xFDCF) &&
3013 (name[i] < 0xFDF0 || name[i] > 0xFFFD)) {
3014 return false;
3017 i++;
3021 return hasDash;
3024 // static
3025 bool nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID) {
3026 // Allow non-dashed names in XUL for XBL to Custom Element migrations.
3027 if (aNameSpaceID == kNameSpaceID_XUL) {
3028 return true;
3031 bool hasDash = IsNameWithDash(aName);
3032 if (!hasDash) {
3033 return false;
3036 // The custom element name must not be one of the following values:
3037 // annotation-xml
3038 // color-profile
3039 // font-face
3040 // font-face-src
3041 // font-face-uri
3042 // font-face-format
3043 // font-face-name
3044 // missing-glyph
3045 return aName != nsGkAtoms::annotation_xml_ &&
3046 aName != nsGkAtoms::colorProfile && aName != nsGkAtoms::font_face &&
3047 aName != nsGkAtoms::font_face_src &&
3048 aName != nsGkAtoms::font_face_uri &&
3049 aName != nsGkAtoms::font_face_format &&
3050 aName != nsGkAtoms::font_face_name && aName != nsGkAtoms::missingGlyph;
3053 // static
3054 nsresult nsContentUtils::CheckQName(const nsAString& aQualifiedName,
3055 bool aNamespaceAware,
3056 const char16_t** aColon) {
3057 const char* colon = nullptr;
3058 const char16_t* begin = aQualifiedName.BeginReading();
3059 const char16_t* end = aQualifiedName.EndReading();
3061 int result = MOZ_XMLCheckQName(reinterpret_cast<const char*>(begin),
3062 reinterpret_cast<const char*>(end),
3063 aNamespaceAware, &colon);
3065 if (!result) {
3066 if (aColon) {
3067 *aColon = reinterpret_cast<const char16_t*>(colon);
3070 return NS_OK;
3073 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
3076 // static
3077 nsresult nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
3078 const nsString& aQName, int32_t* aNamespace,
3079 nsAtom** aLocalName) {
3080 const char16_t* colon;
3081 nsresult rv = nsContentUtils::CheckQName(aQName, true, &colon);
3082 NS_ENSURE_SUCCESS(rv, rv);
3084 if (colon) {
3085 const char16_t* end;
3086 aQName.EndReading(end);
3087 nsAutoString nameSpace;
3088 rv = aNamespaceResolver->LookupNamespaceURIInternal(
3089 Substring(aQName.get(), colon), nameSpace);
3090 NS_ENSURE_SUCCESS(rv, rv);
3092 *aNamespace = NameSpaceManager()->GetNameSpaceID(
3093 nameSpace, nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
3094 if (*aNamespace == kNameSpaceID_Unknown) return NS_ERROR_FAILURE;
3096 *aLocalName = NS_AtomizeMainThread(Substring(colon + 1, end)).take();
3097 } else {
3098 *aNamespace = kNameSpaceID_None;
3099 *aLocalName = NS_AtomizeMainThread(aQName).take();
3101 NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
3102 return NS_OK;
3105 // static
3106 nsresult nsContentUtils::GetNodeInfoFromQName(
3107 const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
3108 nsNodeInfoManager* aNodeInfoManager, uint16_t aNodeType,
3109 mozilla::dom::NodeInfo** aNodeInfo) {
3110 const nsString& qName = PromiseFlatString(aQualifiedName);
3111 const char16_t* colon;
3112 nsresult rv = nsContentUtils::CheckQName(qName, true, &colon);
3113 NS_ENSURE_SUCCESS(rv, rv);
3115 int32_t nsID;
3116 sNameSpaceManager->RegisterNameSpace(aNamespaceURI, nsID);
3117 if (colon) {
3118 const char16_t* end;
3119 qName.EndReading(end);
3121 RefPtr<nsAtom> prefix = NS_AtomizeMainThread(Substring(qName.get(), colon));
3123 rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix, nsID,
3124 aNodeType, aNodeInfo);
3125 } else {
3126 rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID, aNodeType,
3127 aNodeInfo);
3129 NS_ENSURE_SUCCESS(rv, rv);
3131 return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
3132 (*aNodeInfo)->GetPrefixAtom(),
3133 (*aNodeInfo)->NamespaceID())
3134 ? NS_OK
3135 : NS_ERROR_DOM_NAMESPACE_ERR;
3138 // static
3139 void nsContentUtils::SplitExpatName(const char16_t* aExpatName,
3140 nsAtom** aPrefix, nsAtom** aLocalName,
3141 int32_t* aNameSpaceID) {
3143 * Expat can send the following:
3144 * localName
3145 * namespaceURI<separator>localName
3146 * namespaceURI<separator>localName<separator>prefix
3148 * and we use 0xFFFF for the <separator>.
3152 const char16_t* uriEnd = nullptr;
3153 const char16_t* nameEnd = nullptr;
3154 const char16_t* pos;
3155 for (pos = aExpatName; *pos; ++pos) {
3156 if (*pos == 0xFFFF) {
3157 if (uriEnd) {
3158 nameEnd = pos;
3159 } else {
3160 uriEnd = pos;
3165 const char16_t* nameStart;
3166 if (uriEnd) {
3167 if (sNameSpaceManager) {
3168 sNameSpaceManager->RegisterNameSpace(
3169 nsDependentSubstring(aExpatName, uriEnd), *aNameSpaceID);
3170 } else {
3171 *aNameSpaceID = kNameSpaceID_Unknown;
3174 nameStart = (uriEnd + 1);
3175 if (nameEnd) {
3176 const char16_t* prefixStart = nameEnd + 1;
3177 *aPrefix = NS_AtomizeMainThread(Substring(prefixStart, pos)).take();
3178 } else {
3179 nameEnd = pos;
3180 *aPrefix = nullptr;
3182 } else {
3183 *aNameSpaceID = kNameSpaceID_None;
3184 nameStart = aExpatName;
3185 nameEnd = pos;
3186 *aPrefix = nullptr;
3188 *aLocalName = NS_AtomizeMainThread(Substring(nameStart, nameEnd)).take();
3191 // static
3192 PresShell* nsContentUtils::GetPresShellForContent(const nsIContent* aContent) {
3193 Document* doc = aContent->GetComposedDoc();
3194 if (!doc) {
3195 return nullptr;
3197 return doc->GetPresShell();
3200 // static
3201 nsPresContext* nsContentUtils::GetContextForContent(
3202 const nsIContent* aContent) {
3203 PresShell* presShell = GetPresShellForContent(aContent);
3204 if (!presShell) {
3205 return nullptr;
3207 return presShell->GetPresContext();
3210 // static
3211 bool nsContentUtils::CanLoadImage(nsIURI* aURI, nsINode* aNode,
3212 Document* aLoadingDocument,
3213 nsIPrincipal* aLoadingPrincipal) {
3214 MOZ_ASSERT(aURI, "Must have a URI");
3215 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3216 MOZ_ASSERT(aLoadingPrincipal, "Must have a loading principal");
3218 nsresult rv;
3220 auto appType = nsIDocShell::APP_TYPE_UNKNOWN;
3223 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
3224 aLoadingDocument->GetDocShell();
3225 if (docShellTreeItem) {
3226 nsCOMPtr<nsIDocShellTreeItem> root;
3227 docShellTreeItem->GetInProcessRootTreeItem(getter_AddRefs(root));
3229 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
3231 if (docShell) {
3232 appType = docShell->GetAppType();
3237 if (appType != nsIDocShell::APP_TYPE_EDITOR) {
3238 // Editor apps get special treatment here, editors can load images
3239 // from anywhere. This allows editor to insert images from file://
3240 // into documents that are being edited.
3241 rv = sSecurityManager->CheckLoadURIWithPrincipal(
3242 aLoadingPrincipal, aURI, nsIScriptSecurityManager::ALLOW_CHROME,
3243 aLoadingDocument->InnerWindowID());
3244 if (NS_FAILED(rv)) {
3245 return false;
3249 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new mozilla::net::LoadInfo(
3250 aLoadingPrincipal,
3251 aLoadingPrincipal, // triggering principal
3252 aNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
3253 nsIContentPolicy::TYPE_INTERNAL_IMAGE);
3255 int16_t decision = nsIContentPolicy::ACCEPT;
3257 rv = NS_CheckContentLoadPolicy(aURI, secCheckLoadInfo,
3258 EmptyCString(), // mime guess
3259 &decision, GetContentPolicy());
3261 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(decision);
3264 // static
3265 bool nsContentUtils::IsInPrivateBrowsing(Document* aDoc) {
3266 if (!aDoc) {
3267 return false;
3270 nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
3271 if (loadGroup) {
3272 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3273 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3274 if (callbacks) {
3275 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3276 if (loadContext) {
3277 return loadContext->UsePrivateBrowsing();
3282 nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
3283 return channel && NS_UsePrivateBrowsing(channel);
3286 // static
3287 bool nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup) {
3288 if (!aLoadGroup) {
3289 return false;
3291 bool isPrivate = false;
3292 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3293 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3294 if (callbacks) {
3295 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3296 isPrivate = loadContext && loadContext->UsePrivateBrowsing();
3298 return isPrivate;
3301 // FIXME(emilio): This is (effectively) almost but not quite the same as
3302 // Document::ShouldLoadImages(), which one is right?
3303 bool nsContentUtils::DocumentInactiveForImageLoads(Document* aDocument) {
3304 if (!aDocument) {
3305 return false;
3307 if (IsChromeDoc(aDocument) || aDocument->IsResourceDoc() ||
3308 aDocument->IsStaticDocument()) {
3309 return false;
3311 nsCOMPtr<nsPIDOMWindowInner> win =
3312 do_QueryInterface(aDocument->GetScopeObject());
3313 return !win || !win->GetDocShell();
3316 imgLoader* nsContentUtils::GetImgLoaderForDocument(Document* aDoc) {
3317 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
3319 if (!aDoc) {
3320 return imgLoader::NormalLoader();
3322 bool isPrivate = IsInPrivateBrowsing(aDoc);
3323 return isPrivate ? imgLoader::PrivateBrowsingLoader()
3324 : imgLoader::NormalLoader();
3327 // static
3328 imgLoader* nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
3329 Document* aContext) {
3330 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
3332 if (!aChannel) {
3333 return imgLoader::NormalLoader();
3335 nsCOMPtr<nsILoadContext> context;
3336 NS_QueryNotificationCallbacks(aChannel, context);
3337 return context && context->UsePrivateBrowsing()
3338 ? imgLoader::PrivateBrowsingLoader()
3339 : imgLoader::NormalLoader();
3342 // static
3343 bool nsContentUtils::IsImageInCache(nsIURI* aURI, Document* aDocument) {
3344 imgILoader* loader = GetImgLoaderForDocument(aDocument);
3345 nsCOMPtr<imgICache> cache = do_QueryInterface(loader);
3347 // If something unexpected happened we return false, otherwise if props
3348 // is set, the image is cached and we return true
3349 nsCOMPtr<nsIProperties> props;
3350 nsresult rv =
3351 cache->FindEntryProperties(aURI, aDocument, getter_AddRefs(props));
3352 return (NS_SUCCEEDED(rv) && props);
3355 // static
3356 int32_t nsContentUtils::CORSModeToLoadImageFlags(mozilla::CORSMode aMode) {
3357 switch (aMode) {
3358 case CORS_ANONYMOUS:
3359 return imgILoader::LOAD_CORS_ANONYMOUS;
3360 case CORS_USE_CREDENTIALS:
3361 return imgILoader::LOAD_CORS_USE_CREDENTIALS;
3362 default:
3363 return 0;
3367 // static
3368 nsresult nsContentUtils::LoadImage(
3369 nsIURI* aURI, nsINode* aContext, Document* aLoadingDocument,
3370 nsIPrincipal* aLoadingPrincipal, uint64_t aRequestContextID,
3371 nsIReferrerInfo* aReferrerInfo, imgINotificationObserver* aObserver,
3372 int32_t aLoadFlags, const nsAString& initiatorType,
3373 imgRequestProxy** aRequest, uint32_t aContentPolicyType,
3374 bool aUseUrgentStartForChannel, bool aLinkPreload) {
3375 MOZ_ASSERT(aURI, "Must have a URI");
3376 MOZ_ASSERT(aContext, "Must have a context");
3377 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3378 MOZ_ASSERT(aLoadingPrincipal, "Must have a principal");
3379 MOZ_ASSERT(aRequest, "Null out param");
3381 imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
3382 if (!imgLoader) {
3383 // nothing we can do here
3384 return NS_ERROR_FAILURE;
3387 nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
3389 nsIURI* documentURI = aLoadingDocument->GetDocumentURI();
3391 NS_ASSERTION(loadGroup || IsFontTableURI(documentURI),
3392 "Could not get loadgroup; onload may fire too early");
3394 // XXXbz using "documentURI" for the initialDocumentURI is not quite
3395 // right, but the best we can do here...
3396 return imgLoader->LoadImage(aURI, /* uri to load */
3397 documentURI, /* initialDocumentURI */
3398 aReferrerInfo, /* referrerInfo */
3399 aLoadingPrincipal, /* loading principal */
3400 aRequestContextID, /* request context ID */
3401 loadGroup, /* loadgroup */
3402 aObserver, /* imgINotificationObserver */
3403 aContext, /* loading context */
3404 aLoadingDocument, /* uniquification key */
3405 aLoadFlags, /* load flags */
3406 nullptr, /* cache key */
3407 aContentPolicyType, /* content policy type */
3408 initiatorType, /* the load initiator */
3409 aUseUrgentStartForChannel, /* urgent-start flag */
3410 aLinkPreload, /* <link preload> initiator */
3411 aRequest);
3414 // static
3415 already_AddRefed<imgIContainer> nsContentUtils::GetImageFromContent(
3416 nsIImageLoadingContent* aContent, imgIRequest** aRequest) {
3417 if (aRequest) {
3418 *aRequest = nullptr;
3421 NS_ENSURE_TRUE(aContent, nullptr);
3423 nsCOMPtr<imgIRequest> imgRequest;
3424 aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3425 getter_AddRefs(imgRequest));
3426 if (!imgRequest) {
3427 return nullptr;
3430 nsCOMPtr<imgIContainer> imgContainer;
3431 imgRequest->GetImage(getter_AddRefs(imgContainer));
3433 if (!imgContainer) {
3434 return nullptr;
3437 if (aRequest) {
3438 // If the consumer wants the request, verify it has actually loaded
3439 // successfully.
3440 uint32_t imgStatus;
3441 imgRequest->GetImageStatus(&imgStatus);
3442 if (imgStatus & imgIRequest::STATUS_FRAME_COMPLETE &&
3443 !(imgStatus & imgIRequest::STATUS_ERROR)) {
3444 imgRequest.swap(*aRequest);
3448 return imgContainer.forget();
3451 // static
3452 bool nsContentUtils::ContentIsDraggable(nsIContent* aContent) {
3453 MOZ_ASSERT(aContent);
3455 if (auto htmlElement = nsGenericHTMLElement::FromNode(aContent)) {
3456 if (htmlElement->Draggable()) {
3457 return true;
3460 if (htmlElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
3461 nsGkAtoms::_false, eIgnoreCase)) {
3462 return false;
3465 if (aContent->IsSVGElement()) {
3466 return false;
3469 // special handling for content area image and link dragging
3470 return IsDraggableImage(aContent) || IsDraggableLink(aContent);
3473 // static
3474 bool nsContentUtils::IsDraggableImage(nsIContent* aContent) {
3475 MOZ_ASSERT(aContent, "Must have content node to test");
3477 nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(aContent));
3478 if (!imageContent) {
3479 return false;
3482 nsCOMPtr<imgIRequest> imgRequest;
3483 imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3484 getter_AddRefs(imgRequest));
3486 // XXXbz It may be draggable even if the request resulted in an error. Why?
3487 // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
3488 return imgRequest != nullptr;
3491 // static
3492 bool nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
3493 nsCOMPtr<nsIURI> absURI;
3494 return aContent->IsLink(getter_AddRefs(absURI));
3497 // static
3498 nsresult nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo,
3499 nsAtom* aName,
3500 mozilla::dom::NodeInfo** aResult) {
3501 nsNodeInfoManager* niMgr = aNodeInfo->NodeInfoManager();
3503 *aResult = niMgr
3504 ->GetNodeInfo(aName, nullptr, aNodeInfo->NamespaceID(),
3505 aNodeInfo->NodeType(), aNodeInfo->GetExtraName())
3506 .take();
3507 return NS_OK;
3510 static bool TestSitePerm(nsIPrincipal* aPrincipal, const nsACString& aType,
3511 uint32_t aPerm, bool aExactHostMatch) {
3512 if (!aPrincipal) {
3513 // We always deny (i.e. don't allow) the permission if we don't have a
3514 // principal.
3515 return aPerm != nsIPermissionManager::ALLOW_ACTION;
3518 nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
3519 NS_ENSURE_TRUE(permMgr, false);
3521 uint32_t perm;
3522 nsresult rv;
3523 if (aExactHostMatch) {
3524 rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
3525 } else {
3526 rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
3528 NS_ENSURE_SUCCESS(rv, false);
3530 return perm == aPerm;
3533 bool nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal,
3534 const nsACString& aType) {
3535 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
3536 false);
3539 bool nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal,
3540 const nsACString& aType) {
3541 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
3542 false);
3545 bool nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal,
3546 const nsACString& aType) {
3547 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
3548 true);
3551 bool nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal,
3552 const nsACString& aType) {
3553 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
3554 true);
3557 static const char* gEventNames[] = {"event"};
3558 static const char* gSVGEventNames[] = {"evt"};
3559 // for b/w compat, the first name to onerror is still 'event', even though it
3560 // is actually the error message
3561 static const char* gOnErrorNames[] = {"event", "source", "lineno", "colno",
3562 "error"};
3564 // static
3565 void nsContentUtils::GetEventArgNames(int32_t aNameSpaceID, nsAtom* aEventName,
3566 bool aIsForWindow, uint32_t* aArgCount,
3567 const char*** aArgArray) {
3568 #define SET_EVENT_ARG_NAMES(names) \
3569 *aArgCount = sizeof(names) / sizeof(names[0]); \
3570 *aArgArray = names;
3572 // JSEventHandler is what does the arg magic for onerror, and it does
3573 // not seem to take the namespace into account. So we let onerror in all
3574 // namespaces get the 3 arg names.
3575 if (aEventName == nsGkAtoms::onerror && aIsForWindow) {
3576 SET_EVENT_ARG_NAMES(gOnErrorNames);
3577 } else if (aNameSpaceID == kNameSpaceID_SVG) {
3578 SET_EVENT_ARG_NAMES(gSVGEventNames);
3579 } else {
3580 SET_EVENT_ARG_NAMES(gEventNames);
3584 // Note: The list of content bundles in nsStringBundle.cpp should be updated
3585 // whenever entries are added or removed from this list.
3586 static const char* gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT] = {
3587 // Must line up with the enum values in |PropertiesFile| enum.
3588 "chrome://global/locale/css.properties",
3589 "chrome://global/locale/xul.properties",
3590 "chrome://global/locale/layout_errors.properties",
3591 "chrome://global/locale/layout/HtmlForm.properties",
3592 "chrome://global/locale/printing.properties",
3593 "chrome://global/locale/dom/dom.properties",
3594 "chrome://global/locale/layout/htmlparser.properties",
3595 "chrome://global/locale/svg/svg.properties",
3596 "chrome://branding/locale/brand.properties",
3597 "chrome://global/locale/commonDialogs.properties",
3598 "chrome://global/locale/mathml/mathml.properties",
3599 "chrome://global/locale/security/security.properties",
3600 "chrome://necko/locale/necko.properties",
3601 "resource://gre/res/locale/layout/HtmlForm.properties",
3602 "resource://gre/res/locale/dom/dom.properties"};
3604 /* static */
3605 nsresult nsContentUtils::EnsureStringBundle(PropertiesFile aFile) {
3606 if (!sStringBundles[aFile]) {
3607 if (!sStringBundleService) {
3608 nsresult rv =
3609 CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
3610 NS_ENSURE_SUCCESS(rv, rv);
3612 nsIStringBundle* bundle;
3613 nsresult rv =
3614 sStringBundleService->CreateBundle(gPropertiesFiles[aFile], &bundle);
3615 NS_ENSURE_SUCCESS(rv, rv);
3616 sStringBundles[aFile] = bundle; // transfer ownership
3618 return NS_OK;
3621 /* static */
3622 void nsContentUtils::AsyncPrecreateStringBundles() {
3623 // We only ever want to pre-create bundles in the parent process.
3625 // All nsContentUtils bundles are shared between the parent and child
3626 // precesses, and the shared memory regions that back them *must* be created
3627 // in the parent, and then sent to all children.
3629 // If we attempt to create a bundle in the child before its memory region is
3630 // available, we need to create a temporary non-shared bundle, and later
3631 // replace that with the shared memory copy. So attempting to pre-load in the
3632 // child is wasteful and unnecessary.
3633 MOZ_ASSERT(XRE_IsParentProcess());
3635 for (uint32_t bundleIndex = 0; bundleIndex < PropertiesFile_COUNT;
3636 ++bundleIndex) {
3637 nsresult rv = NS_DispatchToCurrentThreadQueue(
3638 NS_NewRunnableFunction("AsyncPrecreateStringBundles",
3639 [bundleIndex]() {
3640 PropertiesFile file =
3641 static_cast<PropertiesFile>(bundleIndex);
3642 EnsureStringBundle(file);
3643 nsIStringBundle* bundle = sStringBundles[file];
3644 bundle->AsyncPreload();
3646 EventQueuePriority::Idle);
3647 Unused << NS_WARN_IF(NS_FAILED(rv));
3651 /* static */
3652 bool nsContentUtils::SpoofLocaleEnglish() {
3653 // 0 - will prompt
3654 // 1 - don't spoof
3655 // 2 - spoof
3656 return StaticPrefs::privacy_spoof_english() == 2;
3659 static nsContentUtils::PropertiesFile GetMaybeSpoofedPropertiesFile(
3660 nsContentUtils::PropertiesFile aFile, const char* aKey,
3661 Document* aDocument) {
3662 // When we spoof English, use en-US properties in strings that are accessible
3663 // by content.
3664 bool spoofLocale = nsContentUtils::SpoofLocaleEnglish() &&
3665 (!aDocument || !aDocument->AllowsL10n());
3666 if (spoofLocale) {
3667 switch (aFile) {
3668 case nsContentUtils::eFORMS_PROPERTIES:
3669 return nsContentUtils::eFORMS_PROPERTIES_en_US;
3670 case nsContentUtils::eDOM_PROPERTIES:
3671 return nsContentUtils::eDOM_PROPERTIES_en_US;
3672 default:
3673 break;
3676 return aFile;
3679 /* static */
3680 nsresult nsContentUtils::GetMaybeLocalizedString(PropertiesFile aFile,
3681 const char* aKey,
3682 Document* aDocument,
3683 nsAString& aResult) {
3684 return GetLocalizedString(
3685 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aResult);
3688 /* static */
3689 nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
3690 const char* aKey,
3691 nsAString& aResult) {
3692 nsresult rv = EnsureStringBundle(aFile);
3693 NS_ENSURE_SUCCESS(rv, rv);
3694 nsIStringBundle* bundle = sStringBundles[aFile];
3695 return bundle->GetStringFromName(aKey, aResult);
3698 /* static */
3699 nsresult nsContentUtils::FormatMaybeLocalizedString(
3700 PropertiesFile aFile, const char* aKey, Document* aDocument,
3701 const nsTArray<nsString>& aParams, nsAString& aResult) {
3702 return FormatLocalizedString(
3703 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aParams,
3704 aResult);
3707 /* static */
3708 nsresult nsContentUtils::FormatLocalizedString(
3709 PropertiesFile aFile, const char* aKey, const nsTArray<nsString>& aParams,
3710 nsAString& aResult) {
3711 nsresult rv = EnsureStringBundle(aFile);
3712 NS_ENSURE_SUCCESS(rv, rv);
3713 nsIStringBundle* bundle = sStringBundles[aFile];
3715 if (aParams.IsEmpty()) {
3716 return bundle->GetStringFromName(aKey, aResult);
3719 return bundle->FormatStringFromName(aKey, aParams, aResult);
3722 /* static */
3723 void nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
3724 const char* aCategory,
3725 bool aFromPrivateWindow,
3726 bool aFromChromeContext,
3727 uint32_t aErrorFlags) {
3728 nsCOMPtr<nsIScriptError> scriptError =
3729 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
3730 if (scriptError) {
3731 nsCOMPtr<nsIConsoleService> console =
3732 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
3733 if (console &&
3734 NS_SUCCEEDED(scriptError->Init(
3735 aErrorText, EmptyString(), EmptyString(), 0, 0, aErrorFlags,
3736 aCategory, aFromPrivateWindow, aFromChromeContext))) {
3737 console->LogMessage(scriptError);
3742 /* static */
3743 nsresult nsContentUtils::ReportToConsole(
3744 uint32_t aErrorFlags, const nsACString& aCategory,
3745 const Document* aDocument, PropertiesFile aFile, const char* aMessageName,
3746 const nsTArray<nsString>& aParams, nsIURI* aURI,
3747 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber) {
3748 nsresult rv;
3749 nsAutoString errorText;
3750 if (!aParams.IsEmpty()) {
3751 rv = FormatLocalizedString(aFile, aMessageName, aParams, errorText);
3752 } else {
3753 rv = GetLocalizedString(aFile, aMessageName, errorText);
3755 NS_ENSURE_SUCCESS(rv, rv);
3757 return ReportToConsoleNonLocalized(errorText, aErrorFlags, aCategory,
3758 aDocument, aURI, aSourceLine, aLineNumber,
3759 aColumnNumber);
3762 /* static */
3763 void nsContentUtils::ReportEmptyGetElementByIdArg(const Document* aDoc) {
3764 ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, aDoc,
3765 nsContentUtils::eDOM_PROPERTIES, "EmptyGetElementByIdParam");
3768 /* static */
3769 nsresult nsContentUtils::ReportToConsoleNonLocalized(
3770 const nsAString& aErrorText, uint32_t aErrorFlags,
3771 const nsACString& aCategory, const Document* aDocument, nsIURI* aURI,
3772 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
3773 MissingErrorLocationMode aLocationMode) {
3774 uint64_t innerWindowID = 0;
3775 if (aDocument) {
3776 if (!aURI) {
3777 aURI = aDocument->GetDocumentURI();
3779 innerWindowID = aDocument->InnerWindowID();
3782 return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
3783 innerWindowID, aURI, aSourceLine,
3784 aLineNumber, aColumnNumber, aLocationMode);
3787 /* static */
3788 nsresult nsContentUtils::ReportToConsoleByWindowID(
3789 const nsAString& aErrorText, uint32_t aErrorFlags,
3790 const nsACString& aCategory, uint64_t aInnerWindowID, nsIURI* aURI,
3791 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
3792 MissingErrorLocationMode aLocationMode) {
3793 nsresult rv;
3794 if (!sConsoleService) { // only need to bother null-checking here
3795 rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
3796 NS_ENSURE_SUCCESS(rv, rv);
3799 nsAutoString spec;
3800 if (!aLineNumber && aLocationMode == eUSE_CALLING_LOCATION) {
3801 JSContext* cx = GetCurrentJSContext();
3802 if (cx) {
3803 nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber, &aColumnNumber);
3807 nsCOMPtr<nsIScriptError> errorObject =
3808 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
3809 NS_ENSURE_SUCCESS(rv, rv);
3811 if (!spec.IsEmpty()) {
3812 rv = errorObject->InitWithWindowID(aErrorText,
3813 spec, // file name
3814 aSourceLine, aLineNumber, aColumnNumber,
3815 aErrorFlags, aCategory, aInnerWindowID);
3816 } else {
3817 rv = errorObject->InitWithSourceURI(aErrorText, aURI, aSourceLine,
3818 aLineNumber, aColumnNumber, aErrorFlags,
3819 aCategory, aInnerWindowID);
3821 NS_ENSURE_SUCCESS(rv, rv);
3823 return sConsoleService->LogMessage(errorObject);
3826 void nsContentUtils::LogMessageToConsole(const char* aMsg) {
3827 if (!sConsoleService) { // only need to bother null-checking here
3828 CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
3829 if (!sConsoleService) {
3830 return;
3833 sConsoleService->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
3836 bool nsContentUtils::IsChildOfSameType(Document* aDoc) {
3837 if (BrowsingContext* bc = aDoc->GetBrowsingContext()) {
3838 return bc->GetParent();
3840 return false;
3843 bool nsContentUtils::IsPlainTextType(const nsACString& aContentType) {
3844 // NOTE: if you add a type here, add it to the CONTENTDLF_CATEGORIES
3845 // define in nsContentDLF.h as well.
3846 return aContentType.EqualsLiteral(TEXT_PLAIN) ||
3847 aContentType.EqualsLiteral(TEXT_CSS) ||
3848 aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
3849 aContentType.EqualsLiteral(TEXT_VTT) ||
3850 aContentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
3851 aContentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
3852 aContentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
3853 aContentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
3854 aContentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
3855 aContentType.EqualsLiteral(APPLICATION_JSON) ||
3856 aContentType.EqualsLiteral(TEXT_JSON);
3859 bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) {
3860 // NOTE: This must be a subset of the list in IsPlainTextType().
3861 return aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
3862 aContentType.EqualsLiteral(APPLICATION_JSON) ||
3863 aContentType.EqualsLiteral(TEXT_JSON) ||
3864 aContentType.EqualsLiteral(TEXT_VTT);
3867 // static
3868 nsIContentPolicy* nsContentUtils::GetContentPolicy() {
3869 if (!sTriedToGetContentPolicy) {
3870 CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
3871 // It's OK to not have a content policy service
3872 sTriedToGetContentPolicy = true;
3875 return sContentPolicyService;
3878 // static
3879 bool nsContentUtils::IsEventAttributeName(nsAtom* aName, int32_t aType) {
3880 const char16_t* name = aName->GetUTF16String();
3881 if (name[0] != 'o' || name[1] != 'n' ||
3882 (aName == nsGkAtoms::onformdata &&
3883 !mozilla::StaticPrefs::dom_formdata_event_enabled())) {
3884 return false;
3887 EventNameMapping mapping;
3888 return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
3891 // static
3892 EventMessage nsContentUtils::GetEventMessage(nsAtom* aName) {
3893 MOZ_ASSERT(NS_IsMainThread(), "sAtomEventTable is not threadsafe");
3894 if (aName) {
3895 EventNameMapping mapping;
3896 if (sAtomEventTable->Get(aName, &mapping)) {
3897 return mapping.mMessage;
3901 return eUnidentifiedEvent;
3904 // static
3905 mozilla::EventClassID nsContentUtils::GetEventClassID(const nsAString& aName) {
3906 EventNameMapping mapping;
3907 if (sStringEventTable->Get(aName, &mapping)) return mapping.mEventClassID;
3909 return eBasicEventClass;
3912 nsAtom* nsContentUtils::GetEventMessageAndAtom(
3913 const nsAString& aName, mozilla::EventClassID aEventClassID,
3914 EventMessage* aEventMessage) {
3915 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
3916 EventNameMapping mapping;
3917 if (sStringEventTable->Get(aName, &mapping)) {
3918 *aEventMessage = mapping.mEventClassID == aEventClassID
3919 ? mapping.mMessage
3920 : eUnidentifiedEvent;
3921 return mapping.mAtom;
3924 // If we have cached lots of user defined event names, clear some of them.
3925 if (sUserDefinedEvents->Length() > 127) {
3926 while (sUserDefinedEvents->Length() > 64) {
3927 nsAtom* first = sUserDefinedEvents->ElementAt(0);
3928 sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
3929 sUserDefinedEvents->RemoveElementAt(0);
3933 *aEventMessage = eUnidentifiedEvent;
3934 RefPtr<nsAtom> atom = NS_AtomizeMainThread(u"on"_ns + aName);
3935 sUserDefinedEvents->AppendElement(atom);
3936 mapping.mAtom = atom;
3937 mapping.mMessage = eUnidentifiedEvent;
3938 mapping.mType = EventNameType_None;
3939 mapping.mEventClassID = eBasicEventClass;
3940 // This is a slow hashtable call, but at least we cache the result for the
3941 // following calls. Because GetEventMessageAndAtomForListener utilizes
3942 // sStringEventTable, it needs to know in which cases sStringEventTable
3943 // doesn't contain the information it needs so that it can use
3944 // sAtomEventTable instead.
3945 mapping.mMaybeSpecialSVGorSMILEvent =
3946 GetEventMessage(atom) != eUnidentifiedEvent;
3947 sStringEventTable->Put(aName, mapping);
3948 return mapping.mAtom;
3951 // static
3952 EventMessage nsContentUtils::GetEventMessageAndAtomForListener(
3953 const nsAString& aName, nsAtom** aOnName) {
3954 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
3956 // Because of SVG/SMIL sStringEventTable contains a subset of the event names
3957 // comparing to the sAtomEventTable. However, usually sStringEventTable
3958 // contains the information we need, so in order to reduce hashtable
3959 // lookups, start from it.
3960 EventNameMapping mapping;
3961 EventMessage msg = eUnidentifiedEvent;
3962 RefPtr<nsAtom> atom;
3963 if (sStringEventTable->Get(aName, &mapping)) {
3964 if (mapping.mMaybeSpecialSVGorSMILEvent) {
3965 // Try the atom version so that we should get the right message for
3966 // SVG/SMIL.
3967 atom = NS_AtomizeMainThread(u"on"_ns + aName);
3968 msg = GetEventMessage(atom);
3969 } else {
3970 atom = mapping.mAtom;
3971 msg = mapping.mMessage;
3973 atom.forget(aOnName);
3974 return msg;
3977 // GetEventMessageAndAtom will cache the event type for the future usage...
3978 GetEventMessageAndAtom(aName, eBasicEventClass, &msg);
3980 // ...and then call this method recursively to get the message and atom from
3981 // now updated sStringEventTable.
3982 return GetEventMessageAndAtomForListener(aName, aOnName);
3985 static nsresult GetEventAndTarget(Document* aDoc, nsISupports* aTarget,
3986 const nsAString& aEventName,
3987 CanBubble aCanBubble, Cancelable aCancelable,
3988 Composed aComposed, Trusted aTrusted,
3989 Event** aEvent, EventTarget** aTargetOut) {
3990 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
3991 NS_ENSURE_TRUE(aDoc && target, NS_ERROR_INVALID_ARG);
3993 ErrorResult err;
3994 RefPtr<Event> event =
3995 aDoc->CreateEvent(u"Events"_ns, CallerType::System, err);
3996 if (NS_WARN_IF(err.Failed())) {
3997 return err.StealNSResult();
4000 event->InitEvent(aEventName, aCanBubble, aCancelable, aComposed);
4001 event->SetTrusted(aTrusted == Trusted::eYes);
4003 event->SetTarget(target);
4005 event.forget(aEvent);
4006 target.forget(aTargetOut);
4007 return NS_OK;
4010 // static
4011 nsresult nsContentUtils::DispatchTrustedEvent(
4012 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4013 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4014 bool* aDefaultAction) {
4015 MOZ_ASSERT(!aEventName.EqualsLiteral("input") &&
4016 !aEventName.EqualsLiteral("beforeinput"),
4017 "Use DispatchInputEvent() instead");
4018 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4019 aComposed, Trusted::eYes, aDefaultAction);
4022 // static
4023 nsresult nsContentUtils::DispatchUntrustedEvent(
4024 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4025 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4026 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4027 Composed::eDefault, Trusted::eNo, aDefaultAction);
4030 // static
4031 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4032 const nsAString& aEventName,
4033 CanBubble aCanBubble,
4034 Cancelable aCancelable,
4035 Composed aComposed, Trusted aTrusted,
4036 bool* aDefaultAction,
4037 ChromeOnlyDispatch aOnlyChromeDispatch) {
4038 RefPtr<Event> event;
4039 nsCOMPtr<EventTarget> target;
4040 nsresult rv = GetEventAndTarget(
4041 aDoc, aTarget, aEventName, aCanBubble, aCancelable, aComposed, aTrusted,
4042 getter_AddRefs(event), getter_AddRefs(target));
4043 NS_ENSURE_SUCCESS(rv, rv);
4044 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch =
4045 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4047 ErrorResult err;
4048 bool doDefault = target->DispatchEvent(*event, CallerType::System, err);
4049 if (aDefaultAction) {
4050 *aDefaultAction = doDefault;
4052 return err.StealNSResult();
4055 // static
4056 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4057 WidgetEvent& aEvent,
4058 EventMessage aEventMessage,
4059 CanBubble aCanBubble,
4060 Cancelable aCancelable, Trusted aTrusted,
4061 bool* aDefaultAction,
4062 ChromeOnlyDispatch aOnlyChromeDispatch) {
4063 MOZ_ASSERT_IF(aOnlyChromeDispatch == ChromeOnlyDispatch::eYes,
4064 aTrusted == Trusted::eYes);
4066 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4068 aEvent.mTime = PR_Now();
4070 aEvent.mSpecifiedEventType = GetEventTypeFromMessage(aEventMessage);
4071 aEvent.SetDefaultComposed();
4072 aEvent.SetDefaultComposedInNativeAnonymousContent();
4074 aEvent.mFlags.mBubbles = aCanBubble == CanBubble::eYes;
4075 aEvent.mFlags.mCancelable = aCancelable == Cancelable::eYes;
4076 aEvent.mFlags.mOnlyChromeDispatch =
4077 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4079 aEvent.mTarget = target;
4081 nsEventStatus status = nsEventStatus_eIgnore;
4082 nsresult rv = EventDispatcher::DispatchDOMEvent(target, &aEvent, nullptr,
4083 nullptr, &status);
4084 if (aDefaultAction) {
4085 *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
4087 return rv;
4090 // static
4091 nsresult nsContentUtils::DispatchInputEvent(Element* aEventTarget) {
4092 return DispatchInputEvent(aEventTarget, mozilla::eEditorInput,
4093 mozilla::EditorInputType::eUnknown, nullptr,
4094 InputEventOptions());
4097 // static
4098 nsresult nsContentUtils::DispatchInputEvent(
4099 Element* aEventTargetElement, EventMessage aEventMessage,
4100 EditorInputType aEditorInputType, TextEditor* aTextEditor,
4101 InputEventOptions&& aOptions, nsEventStatus* aEventStatus /* = nullptr */) {
4102 MOZ_ASSERT(aEventMessage == eEditorInput ||
4103 aEventMessage == eEditorBeforeInput);
4105 if (NS_WARN_IF(!aEventTargetElement)) {
4106 return NS_ERROR_INVALID_ARG;
4109 // If this is called from editor, the instance should be set to aTextEditor.
4110 // Otherwise, we need to look for an editor for aEventTargetElement.
4111 // However, we don't need to do it for HTMLEditor since nobody shouldn't
4112 // dispatch "beforeinput" nor "input" event for HTMLEditor except HTMLEditor
4113 // itself.
4114 bool useInputEvent = false;
4115 if (aTextEditor) {
4116 useInputEvent = true;
4117 } else if (HTMLTextAreaElement* textAreaElement =
4118 HTMLTextAreaElement::FromNode(aEventTargetElement)) {
4119 aTextEditor = textAreaElement->GetTextEditorWithoutCreation();
4120 useInputEvent = true;
4121 } else if (HTMLInputElement* inputElement =
4122 HTMLInputElement::FromNode(aEventTargetElement)) {
4123 if (inputElement->IsInputEventTarget()) {
4124 aTextEditor = inputElement->GetTextEditorWithoutCreation();
4125 useInputEvent = true;
4128 #ifdef DEBUG
4129 else {
4130 MOZ_ASSERT(!aEventTargetElement->IsTextControlElement(),
4131 "The event target may have editor, but we've not known it yet.");
4133 #endif // #ifdef DEBUG
4135 if (!useInputEvent) {
4136 MOZ_ASSERT(aEventMessage == eEditorInput);
4137 MOZ_ASSERT(aEditorInputType == EditorInputType::eUnknown);
4138 // Dispatch "input" event with Event instance.
4139 WidgetEvent widgetEvent(true, eUnidentifiedEvent);
4140 widgetEvent.mSpecifiedEventType = nsGkAtoms::oninput;
4141 widgetEvent.mFlags.mCancelable = false;
4142 widgetEvent.mFlags.mComposed = true;
4143 // Using same time as nsContentUtils::DispatchEvent() for backward
4144 // compatibility.
4145 widgetEvent.mTime = PR_Now();
4146 (new AsyncEventDispatcher(aEventTargetElement, widgetEvent))
4147 ->RunDOMEventWhenSafe();
4148 return NS_OK;
4151 nsCOMPtr<nsIWidget> widget;
4152 if (aTextEditor) {
4153 widget = aTextEditor->GetWidget();
4154 if (NS_WARN_IF(!widget)) {
4155 return NS_ERROR_FAILURE;
4157 } else {
4158 Document* document = aEventTargetElement->OwnerDoc();
4159 if (NS_WARN_IF(!document)) {
4160 return NS_ERROR_FAILURE;
4162 // If we're running xpcshell tests, we fail to get presShell here.
4163 // Even in such case, we need to dispatch "input" event without widget.
4164 PresShell* presShell = document->GetPresShell();
4165 if (presShell) {
4166 nsPresContext* presContext = presShell->GetPresContext();
4167 if (NS_WARN_IF(!presContext)) {
4168 return NS_ERROR_FAILURE;
4170 widget = presContext->GetRootWidget();
4171 if (NS_WARN_IF(!widget)) {
4172 return NS_ERROR_FAILURE;
4177 // Dispatch "input" event with InputEvent instance.
4178 InternalEditorInputEvent inputEvent(true, aEventMessage, widget);
4180 inputEvent.mFlags.mCancelable =
4181 aEventMessage == eEditorBeforeInput &&
4182 IsCancelableBeforeInputEvent(aEditorInputType);
4183 MOZ_ASSERT(!inputEvent.mFlags.mCancelable || aEventStatus);
4185 // Using same time as old event dispatcher in EditorBase for backward
4186 // compatibility.
4187 inputEvent.mTime = static_cast<uint64_t>(PR_Now() / 1000);
4189 // If there is an editor, set isComposing to true when it has composition.
4190 // Note that EditorBase::IsIMEComposing() may return false even when we
4191 // need to set it to true.
4192 // Otherwise, i.e., editor hasn't been created for the element yet,
4193 // we should set isComposing to false since the element can never has
4194 // composition without editor.
4195 inputEvent.mIsComposing = aTextEditor && aTextEditor->GetComposition();
4197 if (!aTextEditor || !aTextEditor->AsHTMLEditor()) {
4198 if (IsDataAvailableOnTextEditor(aEditorInputType)) {
4199 inputEvent.mData = std::move(aOptions.mData);
4200 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4201 "inputEvent.mData shouldn't be void");
4203 #ifdef DEBUG
4204 else {
4205 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4207 #endif // #ifdef DEBUG
4208 MOZ_ASSERT(
4209 aOptions.mTargetRanges.IsEmpty(),
4210 "Target ranges for <input> and <textarea> should always be empty");
4211 } else {
4212 MOZ_ASSERT(aTextEditor->AsHTMLEditor());
4213 if (IsDataAvailableOnHTMLEditor(aEditorInputType)) {
4214 inputEvent.mData = std::move(aOptions.mData);
4215 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4216 "inputEvent.mData shouldn't be void");
4217 } else {
4218 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4219 if (IsDataTransferAvailableOnHTMLEditor(aEditorInputType)) {
4220 inputEvent.mDataTransfer = std::move(aOptions.mDataTransfer);
4221 MOZ_ASSERT(inputEvent.mDataTransfer,
4222 "inputEvent.mDataTransfer shouldn't be nullptr");
4223 MOZ_ASSERT(inputEvent.mDataTransfer->IsReadOnly(),
4224 "inputEvent.mDataTransfer should be read only");
4226 #ifdef DEBUG
4227 else {
4228 MOZ_ASSERT(!inputEvent.mDataTransfer,
4229 "inputEvent.mDataTransfer should be nullptr");
4231 #endif // #ifdef DEBUG
4233 if (aEventMessage == eEditorBeforeInput &&
4234 MayHaveTargetRangesOnHTMLEditor(aEditorInputType)) {
4235 inputEvent.mTargetRanges = std::move(aOptions.mTargetRanges);
4237 #ifdef DEBUG
4238 else {
4239 MOZ_ASSERT(aOptions.mTargetRanges.IsEmpty(),
4240 "Target ranges shouldn't be set for the dispatching event");
4242 #endif // #ifdef DEBUG
4245 inputEvent.mInputType = aEditorInputType;
4247 if (!IsSafeToRunScript()) {
4248 // If we cannot dispatch an event right now, we cannot make it cancelable.
4249 NS_ASSERTION(
4250 !inputEvent.mFlags.mCancelable,
4251 "Cancelable beforeinput event dispatcher should run when it's safe");
4252 inputEvent.mFlags.mCancelable = false;
4253 (new AsyncEventDispatcher(aEventTargetElement, inputEvent))
4254 ->RunDOMEventWhenSafe();
4255 return NS_OK;
4258 // If we're running xpcshell tests, we fail to get presShell here.
4259 // Even in such case, we need to dispatch "input" event without widget.
4260 RefPtr<nsPresContext> presContext =
4261 aEventTargetElement->OwnerDoc()->GetPresContext();
4262 nsresult rv = EventDispatcher::Dispatch(aEventTargetElement, presContext,
4263 &inputEvent, nullptr, aEventStatus);
4264 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
4265 "Dispatching `beforeinput` or `input` event failed");
4266 return rv;
4269 nsresult nsContentUtils::DispatchChromeEvent(
4270 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4271 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4272 RefPtr<Event> event;
4273 nsCOMPtr<EventTarget> target;
4274 nsresult rv = GetEventAndTarget(
4275 aDoc, aTarget, aEventName, aCanBubble, aCancelable, Composed::eDefault,
4276 Trusted::eYes, getter_AddRefs(event), getter_AddRefs(target));
4277 NS_ENSURE_SUCCESS(rv, rv);
4279 NS_ASSERTION(aDoc, "GetEventAndTarget lied?");
4280 if (!aDoc->GetWindow()) return NS_ERROR_INVALID_ARG;
4282 EventTarget* piTarget = aDoc->GetWindow()->GetParentTarget();
4283 if (!piTarget) return NS_ERROR_INVALID_ARG;
4285 ErrorResult err;
4286 bool defaultActionEnabled =
4287 piTarget->DispatchEvent(*event, CallerType::System, err);
4288 if (aDefaultAction) {
4289 *aDefaultAction = defaultActionEnabled;
4291 return err.StealNSResult();
4294 void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise,
4295 CallerType aCallerType) {
4296 RefPtr<Element> target = &aFrameElement;
4297 bool defaultAction = true;
4298 if (aCanRaise) {
4299 DispatchEventOnlyToChrome(target->OwnerDoc(), target,
4300 u"framefocusrequested"_ns, CanBubble::eYes,
4301 Cancelable::eYes, &defaultAction);
4303 if (!defaultAction) {
4304 return;
4307 nsCOMPtr<nsIFocusManager> fm = nsFocusManager::GetFocusManager();
4308 if (!fm) {
4309 return;
4312 uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
4313 if (aCanRaise) {
4314 flags |= nsIFocusManager::FLAG_RAISE;
4317 if (aCallerType == CallerType::NonSystem) {
4318 flags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
4321 fm->SetFocus(target, flags);
4324 nsresult nsContentUtils::DispatchEventOnlyToChrome(
4325 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4326 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4327 bool* aDefaultAction) {
4328 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4329 aComposed, Trusted::eYes, aDefaultAction,
4330 ChromeOnlyDispatch::eYes);
4333 /* static */
4334 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4335 const nsAtom* aId) {
4336 for (nsIContent* cur = aContent; cur; cur = cur->GetNextNode(aContent)) {
4337 if (aId == cur->GetID()) {
4338 return cur->AsElement();
4342 return nullptr;
4345 /* static */
4346 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4347 const nsAString& aId) {
4348 MOZ_ASSERT(!aId.IsEmpty(), "Will match random elements");
4350 // ID attrs are generally stored as atoms, so just atomize this up front
4351 RefPtr<nsAtom> id(NS_Atomize(aId));
4352 if (!id) {
4353 // OOM, so just bail
4354 return nullptr;
4357 return MatchElementId(aContent, id);
4360 /* static */
4361 void nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver) {
4362 nsCOMPtr<nsIObserverService> observerService =
4363 mozilla::services::GetObserverService();
4364 if (observerService) {
4365 observerService->AddObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
4366 false);
4370 /* static */
4371 void nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver) {
4372 nsCOMPtr<nsIObserverService> observerService =
4373 mozilla::services::GetObserverService();
4374 if (observerService) {
4375 observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
4379 /* static */
4380 bool nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent,
4381 int32_t aNameSpaceID, nsAtom* aName) {
4382 static Element::AttrValuesArray strings[] = {nsGkAtoms::_empty, nullptr};
4383 return aContent->IsElement() &&
4384 aContent->AsElement()->FindAttrValueIn(aNameSpaceID, aName, strings,
4385 eCaseMatters) ==
4386 Element::ATTR_VALUE_NO_MATCH;
4389 /* static */
4390 bool nsContentUtils::HasMutationListeners(nsINode* aNode, uint32_t aType,
4391 nsINode* aTargetForSubtreeModified) {
4392 Document* doc = aNode->OwnerDoc();
4394 // global object will be null for documents that don't have windows.
4395 nsPIDOMWindowInner* window = doc->GetInnerWindow();
4396 // This relies on EventListenerManager::AddEventListener, which sets
4397 // all mutation bits when there is a listener for DOMSubtreeModified event.
4398 if (window && !window->HasMutationListeners(aType)) {
4399 return false;
4402 if (aNode->ChromeOnlyAccess() || aNode->IsInShadowTree()) {
4403 return false;
4406 doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
4408 // If we have a window, we can check it for mutation listeners now.
4409 if (aNode->IsInUncomposedDoc()) {
4410 nsCOMPtr<EventTarget> piTarget(do_QueryInterface(window));
4411 if (piTarget) {
4412 EventListenerManager* manager = piTarget->GetExistingListenerManager();
4413 if (manager && manager->HasMutationListeners()) {
4414 return true;
4419 // If we have a window, we know a mutation listener is registered, but it
4420 // might not be in our chain. If we don't have a window, we might have a
4421 // mutation listener. Check quickly to see.
4422 while (aNode) {
4423 EventListenerManager* manager = aNode->GetExistingListenerManager();
4424 if (manager && manager->HasMutationListeners()) {
4425 return true;
4428 aNode = aNode->GetParentNode();
4431 return false;
4434 /* static */
4435 bool nsContentUtils::HasMutationListeners(Document* aDocument, uint32_t aType) {
4436 nsPIDOMWindowInner* window =
4437 aDocument ? aDocument->GetInnerWindow() : nullptr;
4439 // This relies on EventListenerManager::AddEventListener, which sets
4440 // all mutation bits when there is a listener for DOMSubtreeModified event.
4441 return !window || window->HasMutationListeners(aType);
4444 void nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent) {
4445 MOZ_ASSERT(aChild, "Missing child");
4446 MOZ_ASSERT(aChild->GetParentNode() == aParent, "Wrong parent");
4447 MOZ_ASSERT(aChild->OwnerDoc() == aParent->OwnerDoc(), "Wrong owner-doc");
4449 // Having an explicit check here since it's an easy mistake to fall into,
4450 // and there might be existing code with problems. We'd rather be safe
4451 // than fire DOMNodeRemoved in all corner cases. We also rely on it for
4452 // nsAutoScriptBlockerSuppressNodeRemoved.
4453 if (!IsSafeToRunScript()) {
4454 // This checks that IsSafeToRunScript is true since we don't want to fire
4455 // events when that is false. We can't rely on EventDispatcher to assert
4456 // this in this situation since most of the time there are no mutation
4457 // event listeners, in which case we won't even attempt to dispatch events.
4458 // However this also allows for two exceptions. First off, we don't assert
4459 // if the mutation happens to native anonymous content since we never fire
4460 // mutation events on such content anyway.
4461 // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
4462 // that is a know case when we'd normally fire a mutation event, but can't
4463 // make that safe and so we suppress it at this time. Ideally this should
4464 // go away eventually.
4465 if (!(aChild->IsContent() &&
4466 aChild->AsContent()->IsInNativeAnonymousSubtree()) &&
4467 !sDOMNodeRemovedSuppressCount) {
4468 NS_ERROR("Want to fire DOMNodeRemoved event, but it's not safe");
4469 WarnScriptWasIgnored(aChild->OwnerDoc());
4471 return;
4474 if (HasMutationListeners(aChild, NS_EVENT_BITS_MUTATION_NODEREMOVED,
4475 aParent)) {
4476 InternalMutationEvent mutation(true, eLegacyNodeRemoved);
4477 mutation.mRelatedNode = aParent;
4479 mozAutoSubtreeModified subtree(aParent->OwnerDoc(), aParent);
4480 EventDispatcher::Dispatch(aChild, nullptr, &mutation);
4484 void nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments() {
4485 if (!sEventListenerManagersHash) {
4486 return;
4489 for (auto i = sEventListenerManagersHash->Iter(); !i.Done(); i.Next()) {
4490 auto entry = static_cast<EventListenerManagerMapEntry*>(i.Get());
4491 nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
4492 if (n && n->IsInComposedDoc() &&
4493 nsCCUncollectableMarker::InGeneration(
4494 n->OwnerDoc()->GetMarkedCCGeneration())) {
4495 entry->mListenerManager->MarkForCC();
4500 /* static */
4501 void nsContentUtils::TraverseListenerManager(
4502 nsINode* aNode, nsCycleCollectionTraversalCallback& cb) {
4503 if (!sEventListenerManagersHash) {
4504 // We're already shut down, just return.
4505 return;
4508 auto entry = static_cast<EventListenerManagerMapEntry*>(
4509 sEventListenerManagersHash->Search(aNode));
4510 if (entry) {
4511 CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
4512 "[via hash] mListenerManager");
4516 EventListenerManager* nsContentUtils::GetListenerManagerForNode(
4517 nsINode* aNode) {
4518 if (!sEventListenerManagersHash) {
4519 // We're already shut down, don't bother creating an event listener
4520 // manager.
4522 return nullptr;
4525 auto entry = static_cast<EventListenerManagerMapEntry*>(
4526 sEventListenerManagersHash->Add(aNode, fallible));
4528 if (!entry) {
4529 return nullptr;
4532 if (!entry->mListenerManager) {
4533 entry->mListenerManager = new EventListenerManager(aNode);
4535 aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
4538 return entry->mListenerManager;
4541 EventListenerManager* nsContentUtils::GetExistingListenerManagerForNode(
4542 const nsINode* aNode) {
4543 if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
4544 return nullptr;
4547 if (!sEventListenerManagersHash) {
4548 // We're already shut down, don't bother creating an event listener
4549 // manager.
4551 return nullptr;
4554 auto entry = static_cast<EventListenerManagerMapEntry*>(
4555 sEventListenerManagersHash->Search(aNode));
4556 if (entry) {
4557 return entry->mListenerManager;
4560 return nullptr;
4563 void nsContentUtils::AddEntryToDOMArenaTable(nsINode* aNode,
4564 DOMArena* aDOMArena) {
4565 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
4566 if (!sDOMArenaHashtable) {
4567 sDOMArenaHashtable =
4568 new nsRefPtrHashtable<nsPtrHashKey<const nsINode>, dom::DOMArena>();
4570 aNode->SetFlags(NODE_KEEPS_DOMARENA);
4571 sDOMArenaHashtable->Put(aNode, RefPtr<DOMArena>(aDOMArena));
4574 already_AddRefed<DOMArena> nsContentUtils::TakeEntryFromDOMArenaTable(
4575 const nsINode* aNode) {
4576 MOZ_ASSERT(sDOMArenaHashtable->Contains(aNode));
4577 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
4578 RefPtr<DOMArena> arena;
4579 sDOMArenaHashtable->Remove(aNode, getter_AddRefs(arena));
4580 return arena.forget();
4583 /* static */
4584 void nsContentUtils::RemoveListenerManager(nsINode* aNode) {
4585 if (sEventListenerManagersHash) {
4586 auto entry = static_cast<EventListenerManagerMapEntry*>(
4587 sEventListenerManagersHash->Search(aNode));
4588 if (entry) {
4589 RefPtr<EventListenerManager> listenerManager;
4590 listenerManager.swap(entry->mListenerManager);
4591 // Remove the entry and *then* do operations that could cause further
4592 // modification of sEventListenerManagersHash. See bug 334177.
4593 sEventListenerManagersHash->RawRemove(entry);
4594 if (listenerManager) {
4595 listenerManager->Disconnect();
4601 /* static */
4602 bool nsContentUtils::IsValidNodeName(nsAtom* aLocalName, nsAtom* aPrefix,
4603 int32_t aNamespaceID) {
4604 if (aNamespaceID == kNameSpaceID_Unknown) {
4605 return false;
4608 if (!aPrefix) {
4609 // If the prefix is null, then either the QName must be xmlns or the
4610 // namespace must not be XMLNS.
4611 return (aLocalName == nsGkAtoms::xmlns) ==
4612 (aNamespaceID == kNameSpaceID_XMLNS);
4615 // If the prefix is non-null then the namespace must not be null.
4616 if (aNamespaceID == kNameSpaceID_None) {
4617 return false;
4620 // If the namespace is the XMLNS namespace then the prefix must be xmlns,
4621 // but the localname must not be xmlns.
4622 if (aNamespaceID == kNameSpaceID_XMLNS) {
4623 return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
4626 // If the namespace is not the XMLNS namespace then the prefix must not be
4627 // xmlns.
4628 // If the namespace is the XML namespace then the prefix can be anything.
4629 // If the namespace is not the XML namespace then the prefix must not be xml.
4630 return aPrefix != nsGkAtoms::xmlns &&
4631 (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
4634 already_AddRefed<DocumentFragment> nsContentUtils::CreateContextualFragment(
4635 nsINode* aContextNode, const nsAString& aFragment,
4636 bool aPreventScriptExecution, ErrorResult& aRv) {
4637 if (!aContextNode) {
4638 aRv.Throw(NS_ERROR_INVALID_ARG);
4639 return nullptr;
4642 // If we don't have a document here, we can't get the right security context
4643 // for compiling event handlers... so just bail out.
4644 RefPtr<Document> document = aContextNode->OwnerDoc();
4645 bool isHTML = document->IsHTMLDocument();
4647 if (isHTML) {
4648 RefPtr<DocumentFragment> frag = new (document->NodeInfoManager())
4649 DocumentFragment(document->NodeInfoManager());
4651 Element* element = aContextNode->GetAsElementOrParentElement();
4652 if (element && !element->IsHTMLElement(nsGkAtoms::html)) {
4653 aRv = ParseFragmentHTML(
4654 aFragment, frag, element->NodeInfo()->NameAtom(),
4655 element->GetNameSpaceID(),
4656 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
4657 aPreventScriptExecution);
4658 } else {
4659 aRv = ParseFragmentHTML(
4660 aFragment, frag, nsGkAtoms::body, kNameSpaceID_XHTML,
4661 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
4662 aPreventScriptExecution);
4665 return frag.forget();
4668 AutoTArray<nsString, 32> tagStack;
4669 nsAutoString uriStr, nameStr;
4670 for (Element* element : aContextNode->InclusiveAncestorsOfType<Element>()) {
4671 nsString& tagName = *tagStack.AppendElement();
4672 // It mostly doesn't actually matter what tag name we use here: XML doesn't
4673 // have parsing that depends on the open tag stack, apart from namespace
4674 // declarations. So this whole tagStack bit is just there to get the right
4675 // namespace declarations to the XML parser. That said, the parser _is_
4676 // going to create elements with the tag names we provide here, so we need
4677 // to make sure they are not names that can trigger custom element
4678 // constructors. Just make up a name that is never going to be a valid
4679 // custom element name.
4681 // The principled way to do this would probably be to add a new FromParser
4682 // value and make sure we use it when creating the context elements, then
4683 // make sure we teach all FromParser consumers (and in particular the custom
4684 // element code) about it as needed. But right now the XML parser never
4685 // actually uses FromParser values other than NOT_FROM_PARSER, and changing
4686 // that is pretty complicated.
4687 tagName.AssignLiteral("notacustomelement");
4689 // see if we need to add xmlns declarations
4690 uint32_t count = element->GetAttrCount();
4691 bool setDefaultNamespace = false;
4692 if (count > 0) {
4693 uint32_t index;
4695 for (index = 0; index < count; index++) {
4696 const BorrowedAttrInfo info = element->GetAttrInfoAt(index);
4697 const nsAttrName* name = info.mName;
4698 if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
4699 info.mValue->ToString(uriStr);
4701 // really want something like nsXMLContentSerializer::SerializeAttr
4702 tagName.AppendLiteral(" xmlns"); // space important
4703 if (name->GetPrefix()) {
4704 tagName.Append(char16_t(':'));
4705 name->LocalName()->ToString(nameStr);
4706 tagName.Append(nameStr);
4707 } else {
4708 setDefaultNamespace = true;
4710 tagName.AppendLiteral(R"(=")");
4711 tagName.Append(uriStr);
4712 tagName.Append('"');
4717 if (!setDefaultNamespace) {
4718 mozilla::dom::NodeInfo* info = element->NodeInfo();
4719 if (!info->GetPrefixAtom() && info->NamespaceID() != kNameSpaceID_None) {
4720 // We have no namespace prefix, but have a namespace ID. Push
4721 // default namespace attr in, so that our kids will be in our
4722 // namespace.
4723 info->GetNamespaceURI(uriStr);
4724 tagName.AppendLiteral(R"( xmlns=")");
4725 tagName.Append(uriStr);
4726 tagName.Append('"');
4731 RefPtr<DocumentFragment> frag;
4732 aRv = ParseFragmentXML(aFragment, document, tagStack, aPreventScriptExecution,
4733 -1, getter_AddRefs(frag));
4734 return frag.forget();
4737 /* static */
4738 void nsContentUtils::DropFragmentParsers() {
4739 NS_IF_RELEASE(sHTMLFragmentParser);
4740 NS_IF_RELEASE(sXMLFragmentParser);
4741 NS_IF_RELEASE(sXMLFragmentSink);
4744 /* static */
4745 void nsContentUtils::XPCOMShutdown() { nsContentUtils::DropFragmentParsers(); }
4747 /* Helper function to compuate Sanitization Flags for ParseFramentHTML/XML */
4748 uint32_t computeSanitizationFlags(nsIPrincipal* aPrincipal, int32_t aFlags) {
4749 uint32_t sanitizationFlags = 0;
4750 if (aPrincipal->IsSystemPrincipal()) {
4751 if (aFlags < 0) {
4752 // if this is a chrome-privileged document and no explicit flags
4753 // were passed, then use this sanitization flags.
4754 sanitizationFlags = nsIParserUtils::SanitizerAllowStyle |
4755 nsIParserUtils::SanitizerAllowComments |
4756 nsIParserUtils::SanitizerDropForms |
4757 nsIParserUtils::SanitizerLogRemovals;
4758 } else {
4759 // if the caller explicitly passes flags, then we use those
4760 // flags but additionally drop forms.
4761 sanitizationFlags = aFlags | nsIParserUtils::SanitizerDropForms;
4763 } else if (aFlags >= 0) {
4764 // aFlags by default is -1 and is only ever non equal to -1 if the
4765 // caller of ParseFragmentHTML/ParseFragmentXML is
4766 // ParserUtils::ParseFragment(). Only in that case we should use
4767 // the sanitization flags passed within aFlags.
4768 sanitizationFlags = aFlags;
4770 return sanitizationFlags;
4773 /* static */
4774 bool AllowsUnsanitizedContentForAboutNewTab(nsIPrincipal* aPrincipal) {
4775 if (StaticPrefs::dom_about_newtab_sanitization_enabled() ||
4776 !aPrincipal->SchemeIs("about")) {
4777 return false;
4779 uint32_t aboutModuleFlags = 0;
4780 aPrincipal->GetAboutModuleFlags(&aboutModuleFlags);
4781 return aboutModuleFlags & nsIAboutModule::ALLOW_UNSANITIZED_CONTENT;
4784 /* static */
4785 nsresult nsContentUtils::ParseFragmentHTML(
4786 const nsAString& aSourceBuffer, nsIContent* aTargetNode,
4787 nsAtom* aContextLocalName, int32_t aContextNamespace, bool aQuirks,
4788 bool aPreventScriptExecution, int32_t aFlags) {
4789 AutoTimelineMarker m(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");
4791 if (nsContentUtils::sFragmentParsingActive) {
4792 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
4793 return NS_ERROR_DOM_INVALID_STATE_ERR;
4795 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
4796 nsContentUtils::sFragmentParsingActive = true;
4797 if (!sHTMLFragmentParser) {
4798 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
4799 // Now sHTMLFragmentParser owns the object
4802 nsCOMPtr<nsIPrincipal> nodePrincipal = aTargetNode->NodePrincipal();
4804 #ifdef DEBUG
4805 // aFlags should always be -1 unless the caller of ParseFragmentHTML
4806 // is ParserUtils::ParseFragment() which is the only caller that intends
4807 // sanitization. For all other callers we need to ensure to call
4808 // AuditParsingOfHTMLXMLFragments.
4809 if (aFlags < 0) {
4810 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
4811 aSourceBuffer);
4813 #endif
4815 nsIContent* target = aTargetNode;
4817 RefPtr<DocumentFragment> fragment;
4818 // We sanitize if the fragment occurs in a system privileged
4819 // context, an about: page, or if there are explicit sanitization flags.
4820 // Please note that about:blank and about:srcdoc inherit the security
4821 // context from the embedding context and hence are not loaded using
4822 // an about: scheme principal.
4823 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
4824 nodePrincipal->SchemeIs("about") || aFlags >= 0;
4825 if (shouldSanitize) {
4826 if (!AllowsUnsanitizedContentForAboutNewTab(nodePrincipal)) {
4827 fragment = new (aTargetNode->OwnerDoc()->NodeInfoManager())
4828 DocumentFragment(aTargetNode->OwnerDoc()->NodeInfoManager());
4829 target = fragment;
4833 nsresult rv = sHTMLFragmentParser->ParseFragment(
4834 aSourceBuffer, target, aContextLocalName, aContextNamespace, aQuirks,
4835 aPreventScriptExecution);
4836 NS_ENSURE_SUCCESS(rv, rv);
4838 if (fragment) {
4839 uint32_t sanitizationFlags =
4840 computeSanitizationFlags(nodePrincipal, aFlags);
4841 // Don't fire mutation events for nodes removed by the sanitizer.
4842 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
4843 nsTreeSanitizer sanitizer(sanitizationFlags);
4844 sanitizer.Sanitize(fragment);
4846 ErrorResult error;
4847 aTargetNode->AppendChild(*fragment, error);
4848 rv = error.StealNSResult();
4851 return rv;
4854 /* static */
4855 nsresult nsContentUtils::ParseDocumentHTML(
4856 const nsAString& aSourceBuffer, Document* aTargetDocument,
4857 bool aScriptingEnabledForNoscriptParsing) {
4858 AutoTimelineMarker m(aTargetDocument->GetDocShell(), "Parse HTML");
4860 if (nsContentUtils::sFragmentParsingActive) {
4861 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
4862 return NS_ERROR_DOM_INVALID_STATE_ERR;
4864 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
4865 nsContentUtils::sFragmentParsingActive = true;
4866 if (!sHTMLFragmentParser) {
4867 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
4868 // Now sHTMLFragmentParser owns the object
4870 nsresult rv = sHTMLFragmentParser->ParseDocument(
4871 aSourceBuffer, aTargetDocument, aScriptingEnabledForNoscriptParsing);
4872 return rv;
4875 /* static */
4876 nsresult nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
4877 Document* aDocument,
4878 nsTArray<nsString>& aTagStack,
4879 bool aPreventScriptExecution,
4880 int32_t aFlags,
4881 DocumentFragment** aReturn) {
4882 AutoTimelineMarker m(aDocument->GetDocShell(), "Parse XML");
4884 if (nsContentUtils::sFragmentParsingActive) {
4885 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
4886 return NS_ERROR_DOM_INVALID_STATE_ERR;
4888 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
4889 nsContentUtils::sFragmentParsingActive = true;
4890 if (!sXMLFragmentParser) {
4891 nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID);
4892 parser.forget(&sXMLFragmentParser);
4893 // sXMLFragmentParser now owns the parser
4895 if (!sXMLFragmentSink) {
4896 NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
4897 // sXMLFragmentSink now owns the sink
4899 nsCOMPtr<nsIContentSink> contentsink = do_QueryInterface(sXMLFragmentSink);
4900 MOZ_ASSERT(contentsink, "Sink doesn't QI to nsIContentSink!");
4901 sXMLFragmentParser->SetContentSink(contentsink);
4903 sXMLFragmentSink->SetTargetDocument(aDocument);
4904 sXMLFragmentSink->SetPreventScriptExecution(aPreventScriptExecution);
4906 nsresult rv = sXMLFragmentParser->ParseFragment(aSourceBuffer, aTagStack);
4907 if (NS_FAILED(rv)) {
4908 // Drop the fragment parser and sink that might be in an inconsistent state
4909 NS_IF_RELEASE(sXMLFragmentParser);
4910 NS_IF_RELEASE(sXMLFragmentSink);
4911 return rv;
4914 rv = sXMLFragmentSink->FinishFragmentParsing(aReturn);
4916 sXMLFragmentParser->Reset();
4917 NS_ENSURE_SUCCESS(rv, rv);
4919 nsCOMPtr<nsIPrincipal> nodePrincipal = aDocument->NodePrincipal();
4921 #ifdef DEBUG
4922 // aFlags should always be -1 unless the caller of ParseFragmentXML
4923 // is ParserUtils::ParseFragment() which is the only caller that intends
4924 // sanitization. For all other callers we need to ensure to call
4925 // AuditParsingOfHTMLXMLFragments.
4926 if (aFlags < 0) {
4927 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
4928 aSourceBuffer);
4930 #endif
4932 // We sanitize if the fragment occurs in a system privileged
4933 // context, an about: page, or if there are explicit sanitization flags.
4934 // Please note that about:blank and about:srcdoc inherit the security
4935 // context from the embedding context and hence are not loaded using
4936 // an about: scheme principal.
4937 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
4938 nodePrincipal->SchemeIs("about") || aFlags >= 0;
4940 if (shouldSanitize) {
4941 uint32_t sanitizationFlags =
4942 computeSanitizationFlags(nodePrincipal, aFlags);
4943 // Don't fire mutation events for nodes removed by the sanitizer.
4944 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
4945 nsTreeSanitizer sanitizer(sanitizationFlags);
4946 sanitizer.Sanitize(*aReturn);
4949 return rv;
4952 /* static */
4953 nsresult nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
4954 nsAString& aResultBuffer,
4955 uint32_t aFlags,
4956 uint32_t aWrapCol) {
4957 nsCOMPtr<nsIURI> uri;
4958 NS_NewURI(getter_AddRefs(uri), "about:blank");
4959 nsCOMPtr<nsIPrincipal> principal =
4960 NullPrincipal::CreateWithoutOriginAttributes();
4961 RefPtr<Document> document;
4962 nsresult rv = NS_NewDOMDocument(getter_AddRefs(document), EmptyString(),
4963 EmptyString(), nullptr, uri, uri, principal,
4964 true, nullptr, DocumentFlavorHTML);
4965 NS_ENSURE_SUCCESS(rv, rv);
4967 rv = nsContentUtils::ParseDocumentHTML(
4968 aSourceBuffer, document,
4969 !(aFlags & nsIDocumentEncoder::OutputNoScriptContent));
4970 NS_ENSURE_SUCCESS(rv, rv);
4972 nsCOMPtr<nsIDocumentEncoder> encoder = do_createDocumentEncoder("text/plain");
4974 rv = encoder->Init(document, u"text/plain"_ns, aFlags);
4975 NS_ENSURE_SUCCESS(rv, rv);
4977 encoder->SetWrapColumn(aWrapCol);
4979 return encoder->EncodeToString(aResultBuffer);
4982 /* static */
4983 nsresult nsContentUtils::SetNodeTextContent(nsIContent* aContent,
4984 const nsAString& aValue,
4985 bool aTryReuse) {
4986 // Fire DOMNodeRemoved mutation events before we do anything else.
4987 nsCOMPtr<nsIContent> owningContent;
4989 // Batch possible DOMSubtreeModified events.
4990 mozAutoSubtreeModified subtree(nullptr, nullptr);
4992 // Scope firing mutation events so that we don't carry any state that
4993 // might be stale
4995 // We're relying on mozAutoSubtreeModified to keep a strong reference if
4996 // needed.
4997 Document* doc = aContent->OwnerDoc();
4999 // Optimize the common case of there being no observers
5000 if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
5001 subtree.UpdateTarget(doc, nullptr);
5002 owningContent = aContent;
5003 nsCOMPtr<nsINode> child;
5004 bool skipFirst = aTryReuse;
5005 for (child = aContent->GetFirstChild();
5006 child && child->GetParentNode() == aContent;
5007 child = child->GetNextSibling()) {
5008 if (skipFirst && child->IsText()) {
5009 skipFirst = false;
5010 continue;
5012 nsContentUtils::MaybeFireNodeRemoved(child, aContent);
5017 // Might as well stick a batch around this since we're performing several
5018 // mutations.
5019 mozAutoDocUpdate updateBatch(aContent->GetComposedDoc(), true);
5020 nsAutoMutationBatch mb;
5022 if (aTryReuse && !aValue.IsEmpty()) {
5023 // Let's remove nodes until we find a eTEXT.
5024 while (aContent->HasChildren()) {
5025 nsIContent* child = aContent->GetFirstChild();
5026 if (child->IsText()) {
5027 break;
5029 aContent->RemoveChildNode(child, true);
5032 // If we have a node, it must be a eTEXT and we reuse it.
5033 if (aContent->HasChildren()) {
5034 nsIContent* child = aContent->GetFirstChild();
5035 nsresult rv = child->AsText()->SetText(aValue, true);
5036 NS_ENSURE_SUCCESS(rv, rv);
5038 // All the following nodes, if they exist, must be deleted.
5039 while (nsIContent* nextChild = child->GetNextSibling()) {
5040 aContent->RemoveChildNode(nextChild, true);
5044 if (aContent->HasChildren()) {
5045 return NS_OK;
5047 } else {
5048 mb.Init(aContent, true, false);
5049 while (aContent->HasChildren()) {
5050 aContent->RemoveChildNode(aContent->GetFirstChild(), true);
5053 mb.RemovalDone();
5055 if (aValue.IsEmpty()) {
5056 return NS_OK;
5059 RefPtr<nsTextNode> textContent = new (aContent->NodeInfo()->NodeInfoManager())
5060 nsTextNode(aContent->NodeInfo()->NodeInfoManager());
5062 textContent->SetText(aValue, true);
5064 nsresult rv = aContent->AppendChildTo(textContent, true);
5065 mb.NodesAdded();
5066 return rv;
5069 static bool AppendNodeTextContentsRecurse(nsINode* aNode, nsAString& aResult,
5070 const fallible_t& aFallible) {
5071 for (nsIContent* child = aNode->GetFirstChild(); child;
5072 child = child->GetNextSibling()) {
5073 if (child->IsElement()) {
5074 bool ok = AppendNodeTextContentsRecurse(child, aResult, aFallible);
5075 if (!ok) {
5076 return false;
5078 } else if (Text* text = child->GetAsText()) {
5079 bool ok = text->AppendTextTo(aResult, aFallible);
5080 if (!ok) {
5081 return false;
5086 return true;
5089 /* static */
5090 bool nsContentUtils::AppendNodeTextContent(nsINode* aNode, bool aDeep,
5091 nsAString& aResult,
5092 const fallible_t& aFallible) {
5093 if (Text* text = aNode->GetAsText()) {
5094 return text->AppendTextTo(aResult, aFallible);
5096 if (aDeep) {
5097 return AppendNodeTextContentsRecurse(aNode, aResult, aFallible);
5100 for (nsIContent* child = aNode->GetFirstChild(); child;
5101 child = child->GetNextSibling()) {
5102 if (Text* text = child->GetAsText()) {
5103 bool ok = text->AppendTextTo(aResult, fallible);
5104 if (!ok) {
5105 return false;
5109 return true;
5112 bool nsContentUtils::HasNonEmptyTextContent(
5113 nsINode* aNode, TextContentDiscoverMode aDiscoverMode) {
5114 for (nsIContent* child = aNode->GetFirstChild(); child;
5115 child = child->GetNextSibling()) {
5116 if (child->IsText() && child->TextLength() > 0) {
5117 return true;
5120 if (aDiscoverMode == eRecurseIntoChildren &&
5121 HasNonEmptyTextContent(child, aDiscoverMode)) {
5122 return true;
5126 return false;
5129 /* static */
5130 bool nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
5131 const nsIContent* aContent) {
5132 MOZ_ASSERT(aNode, "Must have a node to work with");
5133 MOZ_ASSERT(aContent, "Must have a content to work with");
5135 if (aNode->IsInNativeAnonymousSubtree() !=
5136 aContent->IsInNativeAnonymousSubtree()) {
5137 return false;
5140 if (aNode->IsInNativeAnonymousSubtree()) {
5141 return aContent->GetClosestNativeAnonymousSubtreeRoot() ==
5142 aNode->GetClosestNativeAnonymousSubtreeRoot();
5145 // FIXME: This doesn't deal with disconnected nodes whatsoever, but it didn't
5146 // use to either. Maybe that's fine.
5147 return aNode->GetContainingShadow() == aContent->GetContainingShadow();
5150 /* static */
5151 bool nsContentUtils::IsInInteractiveHTMLContent(const Element* aElement,
5152 const Element* aStop) {
5153 const Element* element = aElement;
5154 while (element && element != aStop) {
5155 if (element->IsInteractiveHTMLContent()) {
5156 return true;
5158 element = element->GetFlattenedTreeParentElement();
5160 return false;
5163 /* static */
5164 void nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling) {
5165 IMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
5168 /* static */
5169 bool nsContentUtils::SchemeIs(nsIURI* aURI, const char* aScheme) {
5170 nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
5171 NS_ENSURE_TRUE(baseURI, false);
5172 return baseURI->SchemeIs(aScheme);
5175 bool nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal) {
5176 nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
5177 return !!ep;
5180 nsIPrincipal* nsContentUtils::GetSystemPrincipal() {
5181 MOZ_ASSERT(IsInitialized());
5182 return sSystemPrincipal;
5185 bool nsContentUtils::CombineResourcePrincipals(
5186 nsCOMPtr<nsIPrincipal>* aResourcePrincipal, nsIPrincipal* aExtraPrincipal) {
5187 if (!aExtraPrincipal) {
5188 return false;
5190 if (!*aResourcePrincipal) {
5191 *aResourcePrincipal = aExtraPrincipal;
5192 return true;
5194 if (*aResourcePrincipal == aExtraPrincipal) {
5195 return false;
5197 bool subsumes;
5198 if (NS_SUCCEEDED(
5199 (*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
5200 subsumes) {
5201 return false;
5203 *aResourcePrincipal = sSystemPrincipal;
5204 return true;
5207 /* static */
5208 void nsContentUtils::TriggerLink(nsIContent* aContent, nsIURI* aLinkURI,
5209 const nsString& aTargetSpec, bool aClick,
5210 bool aIsTrusted) {
5211 MOZ_ASSERT(aLinkURI, "No link URI");
5213 if (aContent->IsEditable() || !aContent->OwnerDoc()->LinkHandlingEnabled()) {
5214 return;
5217 nsCOMPtr<nsIDocShell> docShell = aContent->OwnerDoc()->GetDocShell();
5218 if (!docShell) {
5219 return;
5222 if (!aClick) {
5223 nsDocShell::Cast(docShell)->OnOverLink(aContent, aLinkURI, aTargetSpec);
5224 return;
5227 // Check that this page is allowed to load this URI.
5228 nsresult proceed = NS_OK;
5230 if (sSecurityManager) {
5231 uint32_t flag = static_cast<uint32_t>(nsIScriptSecurityManager::STANDARD);
5232 proceed = sSecurityManager->CheckLoadURIWithPrincipal(
5233 aContent->NodePrincipal(), aLinkURI, flag,
5234 aContent->OwnerDoc()->InnerWindowID());
5237 // Only pass off the click event if the script security manager says it's ok.
5238 // We need to rest aTargetSpec for forced downloads.
5239 if (NS_SUCCEEDED(proceed)) {
5240 // A link/area element with a download attribute is allowed to set
5241 // a pseudo Content-Disposition header.
5242 // For security reasons we only allow websites to declare same-origin
5243 // resources as downloadable. If this check fails we will just do the normal
5244 // thing (i.e. navigate to the resource).
5245 nsAutoString fileName;
5246 if ((!aContent->IsHTMLElement(nsGkAtoms::a) &&
5247 !aContent->IsHTMLElement(nsGkAtoms::area) &&
5248 !aContent->IsSVGElement(nsGkAtoms::a)) ||
5249 !aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::download,
5250 fileName) ||
5251 NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, true))) {
5252 fileName.SetIsVoid(true); // No actionable download attribute was found.
5255 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aContent->NodePrincipal();
5256 nsCOMPtr<nsIContentSecurityPolicy> csp = aContent->GetCsp();
5258 // Sanitize fileNames containing null characters by replacing them with
5259 // underscores.
5260 if (!fileName.IsVoid()) {
5261 fileName.ReplaceChar(char16_t(0), '_');
5263 nsDocShell::Cast(docShell)->OnLinkClick(
5264 aContent, aLinkURI, fileName.IsVoid() ? aTargetSpec : EmptyString(),
5265 fileName, nullptr, nullptr, UserActivation::IsHandlingUserInput(),
5266 aIsTrusted, triggeringPrincipal, csp);
5270 /* static */
5271 void nsContentUtils::GetLinkLocation(Element* aElement,
5272 nsString& aLocationString) {
5273 nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
5274 if (hrefURI) {
5275 nsAutoCString specUTF8;
5276 nsresult rv = hrefURI->GetSpec(specUTF8);
5277 if (NS_SUCCEEDED(rv)) CopyUTF8toUTF16(specUTF8, aLocationString);
5281 /* static */
5282 nsIWidget* nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget) {
5283 if (!aWidget) return nullptr;
5285 return aWidget->GetTopLevelWidget();
5288 /* static */
5289 const nsDependentString nsContentUtils::GetLocalizedEllipsis() {
5290 static char16_t sBuf[4] = {0, 0, 0, 0};
5291 if (!sBuf[0]) {
5292 if (!SpoofLocaleEnglish()) {
5293 nsAutoString tmp;
5294 Preferences::GetLocalizedString("intl.ellipsis", tmp);
5295 uint32_t len =
5296 std::min(uint32_t(tmp.Length()), uint32_t(ArrayLength(sBuf) - 1));
5297 CopyUnicodeTo(tmp, 0, sBuf, len);
5299 if (!sBuf[0]) sBuf[0] = char16_t(0x2026);
5301 return nsDependentString(sBuf);
5304 /* static */
5305 void nsContentUtils::AddScriptBlocker() {
5306 MOZ_ASSERT(NS_IsMainThread());
5307 if (!sScriptBlockerCount) {
5308 MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
5309 "Should not already have a count");
5310 sRunnersCountAtFirstBlocker =
5311 sBlockedScriptRunners ? sBlockedScriptRunners->Length() : 0;
5313 ++sScriptBlockerCount;
5316 #ifdef DEBUG
5317 static bool sRemovingScriptBlockers = false;
5318 #endif
5320 /* static */
5321 void nsContentUtils::RemoveScriptBlocker() {
5322 MOZ_ASSERT(NS_IsMainThread());
5323 MOZ_ASSERT(!sRemovingScriptBlockers);
5324 NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
5325 --sScriptBlockerCount;
5326 if (sScriptBlockerCount) {
5327 return;
5330 if (!sBlockedScriptRunners) {
5331 return;
5334 uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
5335 uint32_t lastBlocker = sBlockedScriptRunners->Length();
5336 uint32_t originalFirstBlocker = firstBlocker;
5337 uint32_t blockersCount = lastBlocker - firstBlocker;
5338 sRunnersCountAtFirstBlocker = 0;
5339 NS_ASSERTION(firstBlocker <= lastBlocker, "bad sRunnersCountAtFirstBlocker");
5341 while (firstBlocker < lastBlocker) {
5342 nsCOMPtr<nsIRunnable> runnable;
5343 runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
5344 ++firstBlocker;
5346 // Calling the runnable can reenter us
5347 runnable->Run();
5348 // So can dropping the reference to the runnable
5349 runnable = nullptr;
5351 NS_ASSERTION(sRunnersCountAtFirstBlocker == 0, "Bad count");
5352 NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
5354 #ifdef DEBUG
5355 AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
5356 sRemovingScriptBlockers = true;
5357 #endif
5358 sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
5361 /* static */
5362 already_AddRefed<nsPIDOMWindowOuter>
5363 nsContentUtils::GetMostRecentNonPBWindow() {
5364 nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
5366 nsCOMPtr<mozIDOMWindowProxy> window;
5367 wm->GetMostRecentNonPBWindow(u"navigator:browser", getter_AddRefs(window));
5368 nsCOMPtr<nsPIDOMWindowOuter> pwindow;
5369 pwindow = do_QueryInterface(window);
5371 return pwindow.forget();
5374 /* static */
5375 void nsContentUtils::WarnScriptWasIgnored(Document* aDocument) {
5376 nsAutoString msg;
5377 bool privateBrowsing = false;
5378 bool chromeContext = false;
5380 if (aDocument) {
5381 nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
5382 if (uri) {
5383 msg.Append(NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()));
5384 msg.AppendLiteral(" : ");
5386 privateBrowsing =
5387 !!aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
5388 chromeContext = aDocument->NodePrincipal()->IsSystemPrincipal();
5391 msg.AppendLiteral(
5392 "Unable to run script because scripts are blocked internally.");
5393 LogSimpleConsoleError(msg, "DOM", privateBrowsing, chromeContext);
5396 /* static */
5397 void nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable) {
5398 nsCOMPtr<nsIRunnable> runnable = aRunnable;
5399 if (!runnable) {
5400 return;
5403 if (sScriptBlockerCount) {
5404 sBlockedScriptRunners->AppendElement(runnable.forget());
5405 return;
5408 runnable->Run();
5411 /* static */
5412 void nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) {
5413 nsCOMPtr<nsIRunnable> runnable = aRunnable;
5414 AddScriptRunner(runnable.forget());
5417 /* static */
5418 void nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable) {
5419 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5420 CycleCollectedJSContext::Get()->RunInStableState(std::move(aRunnable));
5423 /* static */
5424 void nsContentUtils::AddPendingIDBTransaction(
5425 already_AddRefed<nsIRunnable> aTransaction) {
5426 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5427 CycleCollectedJSContext::Get()->AddPendingIDBTransaction(
5428 std::move(aTransaction));
5431 /* static */
5432 bool nsContentUtils::IsInStableOrMetaStableState() {
5433 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5434 return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
5437 /* static */
5438 void nsContentUtils::HidePopupsInDocument(Document* aDocument) {
5439 #ifdef MOZ_XUL
5440 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
5441 if (pm && aDocument) {
5442 nsCOMPtr<nsIDocShellTreeItem> docShellToHide = aDocument->GetDocShell();
5443 if (docShellToHide) pm->HidePopupsInDocShell(docShellToHide);
5445 #endif
5448 /* static */
5449 already_AddRefed<nsIDragSession> nsContentUtils::GetDragSession() {
5450 nsCOMPtr<nsIDragSession> dragSession;
5451 nsCOMPtr<nsIDragService> dragService =
5452 do_GetService("@mozilla.org/widget/dragservice;1");
5453 if (dragService) dragService->GetCurrentSession(getter_AddRefs(dragSession));
5454 return dragSession.forget();
5457 /* static */
5458 nsresult nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) {
5459 if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) {
5460 return NS_OK;
5463 // For dragstart events, the data transfer object is
5464 // created before the event fires, so it should already be set. For other
5465 // drag events, get the object from the drag session.
5466 NS_ASSERTION(aDragEvent->mMessage != eDragStart,
5467 "draggesture event created without a dataTransfer");
5469 nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
5470 NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
5472 RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
5473 if (!initialDataTransfer) {
5474 // A dataTransfer won't exist when a drag was started by some other
5475 // means, for instance calling the drag service directly, or a drag
5476 // from another application. In either case, a new dataTransfer should
5477 // be created that reflects the data.
5478 initialDataTransfer =
5479 new DataTransfer(aDragEvent->mTarget, aDragEvent->mMessage, true, -1);
5481 // now set it in the drag session so we don't need to create it again
5482 dragSession->SetDataTransfer(initialDataTransfer);
5485 bool isCrossDomainSubFrameDrop = false;
5486 if (aDragEvent->mMessage == eDrop) {
5487 isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
5490 // each event should use a clone of the original dataTransfer.
5491 initialDataTransfer->Clone(
5492 aDragEvent->mTarget, aDragEvent->mMessage, aDragEvent->mUserCancelled,
5493 isCrossDomainSubFrameDrop, getter_AddRefs(aDragEvent->mDataTransfer));
5494 if (NS_WARN_IF(!aDragEvent->mDataTransfer)) {
5495 return NS_ERROR_OUT_OF_MEMORY;
5498 // for the dragenter and dragover events, initialize the drop effect
5499 // from the drop action, which platform specific widget code sets before
5500 // the event is fired based on the keyboard state.
5501 if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
5502 uint32_t action;
5503 dragSession->GetDragAction(&action);
5504 uint32_t effectAllowed = aDragEvent->mDataTransfer->EffectAllowedInt();
5505 aDragEvent->mDataTransfer->SetDropEffectInt(
5506 FilterDropEffect(action, effectAllowed));
5507 } else if (aDragEvent->mMessage == eDrop ||
5508 aDragEvent->mMessage == eDragEnd) {
5509 // For the drop and dragend events, set the drop effect based on the
5510 // last value that the dropEffect had. This will have been set in
5511 // EventStateManager::PostHandleEvent for the last dragenter or
5512 // dragover event.
5513 aDragEvent->mDataTransfer->SetDropEffectInt(
5514 initialDataTransfer->DropEffectInt());
5517 return NS_OK;
5520 /* static */
5521 uint32_t nsContentUtils::FilterDropEffect(uint32_t aAction,
5522 uint32_t aEffectAllowed) {
5523 // It is possible for the drag action to include more than one action, but
5524 // the widget code which sets the action from the keyboard state should only
5525 // be including one. If multiple actions were set, we just consider them in
5526 // the following order:
5527 // copy, link, move
5528 if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
5529 aAction = nsIDragService::DRAGDROP_ACTION_COPY;
5530 else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
5531 aAction = nsIDragService::DRAGDROP_ACTION_LINK;
5532 else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
5533 aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
5535 // Filter the action based on the effectAllowed. If the effectAllowed
5536 // doesn't include the action, then that action cannot be done, so adjust
5537 // the action to something that is allowed. For a copy, adjust to move or
5538 // link. For a move, adjust to copy or link. For a link, adjust to move or
5539 // link. Otherwise, use none.
5540 if (aAction & aEffectAllowed ||
5541 aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
5542 return aAction;
5543 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
5544 return nsIDragService::DRAGDROP_ACTION_MOVE;
5545 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
5546 return nsIDragService::DRAGDROP_ACTION_COPY;
5547 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
5548 return nsIDragService::DRAGDROP_ACTION_LINK;
5549 return nsIDragService::DRAGDROP_ACTION_NONE;
5552 /* static */
5553 bool nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession,
5554 WidgetDragEvent* aDropEvent) {
5555 nsCOMPtr<nsIContent> target = do_QueryInterface(aDropEvent->mOriginalTarget);
5556 if (!target) {
5557 return true;
5560 // Always allow dropping onto chrome shells.
5561 BrowsingContext* targetBC = target->OwnerDoc()->GetBrowsingContext();
5562 if (targetBC->IsChrome()) {
5563 return false;
5566 // If there is no source node, then this is a drag from another
5567 // application, which should be allowed.
5568 RefPtr<Document> doc(aDragSession->GetSourceDocument());
5569 if (doc && doc->GetBrowsingContext()) {
5570 // Get each successive parent of the source document and compare it to
5571 // the drop document. If they match, then this is a drag from a child frame.
5572 for (BrowsingContext* bc = doc->GetBrowsingContext()->GetParent(); bc;
5573 bc = bc->GetParent()) {
5574 if (bc == targetBC) {
5575 // The drag is from a descendant frame.
5576 return true;
5581 return false;
5584 /* static */
5585 bool nsContentUtils::URIIsLocalFile(nsIURI* aURI) {
5586 bool isFile;
5587 nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
5589 // Important: we do NOT test the entire URI chain here!
5590 return util &&
5591 NS_SUCCEEDED(util->ProtocolHasFlags(
5592 aURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, &isFile)) &&
5593 isFile;
5596 /* static */
5597 JSContext* nsContentUtils::GetCurrentJSContext() {
5598 MOZ_ASSERT(IsInitialized());
5599 if (!IsJSAPIActive()) {
5600 return nullptr;
5602 return danger::GetJSContext();
5605 template <typename StringType, typename CharType>
5606 void _ASCIIToLowerInSitu(StringType& aStr) {
5607 CharType* iter = aStr.BeginWriting();
5608 CharType* end = aStr.EndWriting();
5609 MOZ_ASSERT(iter && end);
5611 while (iter != end) {
5612 CharType c = *iter;
5613 if (c >= 'A' && c <= 'Z') {
5614 *iter = c + ('a' - 'A');
5616 ++iter;
5620 /* static */
5621 void nsContentUtils::ASCIIToLower(nsAString& aStr) {
5622 return _ASCIIToLowerInSitu<nsAString, char16_t>(aStr);
5625 /* static */
5626 void nsContentUtils::ASCIIToLower(nsACString& aStr) {
5627 return _ASCIIToLowerInSitu<nsACString, char>(aStr);
5630 template <typename StringType, typename CharType>
5631 void _ASCIIToLowerCopy(const StringType& aSource, StringType& aDest) {
5632 uint32_t len = aSource.Length();
5633 aDest.SetLength(len);
5634 MOZ_ASSERT(aDest.Length() == len);
5636 CharType* dest = aDest.BeginWriting();
5637 MOZ_ASSERT(dest);
5639 const CharType* iter = aSource.BeginReading();
5640 const CharType* end = aSource.EndReading();
5641 while (iter != end) {
5642 CharType c = *iter;
5643 *dest = (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
5644 ++iter;
5645 ++dest;
5649 /* static */
5650 void nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest) {
5651 return _ASCIIToLowerCopy<nsAString, char16_t>(aSource, aDest);
5654 /* static */
5655 void nsContentUtils::ASCIIToLower(const nsACString& aSource,
5656 nsACString& aDest) {
5657 return _ASCIIToLowerCopy<nsACString, char>(aSource, aDest);
5660 template <typename StringType, typename CharType>
5661 void _ASCIIToUpperInSitu(StringType& aStr) {
5662 CharType* iter = aStr.BeginWriting();
5663 CharType* end = aStr.EndWriting();
5664 MOZ_ASSERT(iter && end);
5666 while (iter != end) {
5667 CharType c = *iter;
5668 if (c >= 'a' && c <= 'z') {
5669 *iter = c + ('A' - 'a');
5671 ++iter;
5675 /* static */
5676 void nsContentUtils::ASCIIToUpper(nsAString& aStr) {
5677 return _ASCIIToUpperInSitu<nsAString, char16_t>(aStr);
5680 /* static */
5681 void nsContentUtils::ASCIIToUpper(nsACString& aStr) {
5682 return _ASCIIToUpperInSitu<nsACString, char>(aStr);
5685 template <typename StringType, typename CharType>
5686 void _ASCIIToUpperCopy(const StringType& aSource, StringType& aDest) {
5687 uint32_t len = aSource.Length();
5688 aDest.SetLength(len);
5689 MOZ_ASSERT(aDest.Length() == len);
5691 CharType* dest = aDest.BeginWriting();
5692 MOZ_ASSERT(dest);
5694 const CharType* iter = aSource.BeginReading();
5695 const CharType* end = aSource.EndReading();
5696 while (iter != end) {
5697 CharType c = *iter;
5698 *dest = (c >= 'a' && c <= 'z') ? c + ('A' - 'a') : c;
5699 ++iter;
5700 ++dest;
5704 /* static */
5705 void nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest) {
5706 return _ASCIIToUpperCopy<nsAString, char16_t>(aSource, aDest);
5709 /* static */
5710 void nsContentUtils::ASCIIToUpper(const nsACString& aSource,
5711 nsACString& aDest) {
5712 return _ASCIIToUpperCopy<nsACString, char>(aSource, aDest);
5715 /* static */
5716 bool nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
5717 const nsAString& aStr2) {
5718 uint32_t len = aStr1.Length();
5719 if (len != aStr2.Length()) {
5720 return false;
5723 const char16_t* str1 = aStr1.BeginReading();
5724 const char16_t* str2 = aStr2.BeginReading();
5725 const char16_t* end = str1 + len;
5727 while (str1 < end) {
5728 char16_t c1 = *str1++;
5729 char16_t c2 = *str2++;
5731 // First check if any bits other than the 0x0020 differs
5732 if ((c1 ^ c2) & 0xffdf) {
5733 return false;
5736 // We know they can only differ in the 0x0020 bit.
5737 // Likely the two chars are the same, so check that first
5738 if (c1 != c2) {
5739 // They do differ, but since it's only in the 0x0020 bit, check if it's
5740 // the same ascii char, but just differing in case
5741 char16_t c1Upper = c1 & 0xffdf;
5742 if (!('A' <= c1Upper && c1Upper <= 'Z')) {
5743 return false;
5748 return true;
5751 /* static */
5752 bool nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr) {
5753 const char16_t* iter = aStr.BeginReading();
5754 const char16_t* end = aStr.EndReading();
5755 while (iter != end) {
5756 char16_t c = *iter;
5757 if (c >= 'A' && c <= 'Z') {
5758 return true;
5760 ++iter;
5763 return false;
5766 /* static */
5767 nsIInterfaceRequestor* nsContentUtils::SameOriginChecker() {
5768 if (!sSameOriginChecker) {
5769 sSameOriginChecker = new SameOriginCheckerImpl();
5770 NS_ADDREF(sSameOriginChecker);
5772 return sSameOriginChecker;
5775 /* static */
5776 nsresult nsContentUtils::CheckSameOrigin(nsIChannel* aOldChannel,
5777 nsIChannel* aNewChannel) {
5778 if (!nsContentUtils::GetSecurityManager()) return NS_ERROR_NOT_AVAILABLE;
5780 nsCOMPtr<nsIPrincipal> oldPrincipal;
5781 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
5782 aOldChannel, getter_AddRefs(oldPrincipal));
5784 nsCOMPtr<nsIURI> newURI;
5785 aNewChannel->GetURI(getter_AddRefs(newURI));
5786 nsCOMPtr<nsIURI> newOriginalURI;
5787 aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
5789 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
5791 nsresult rv = oldPrincipal->CheckMayLoad(newURI, false);
5792 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
5793 rv = oldPrincipal->CheckMayLoad(newOriginalURI, false);
5796 return rv;
5799 NS_IMPL_ISUPPORTS(SameOriginCheckerImpl, nsIChannelEventSink,
5800 nsIInterfaceRequestor)
5802 NS_IMETHODIMP
5803 SameOriginCheckerImpl::AsyncOnChannelRedirect(
5804 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
5805 nsIAsyncVerifyRedirectCallback* cb) {
5806 MOZ_ASSERT(aNewChannel, "Redirecting to null channel?");
5808 nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
5809 if (NS_SUCCEEDED(rv)) {
5810 cb->OnRedirectVerifyCallback(NS_OK);
5813 return rv;
5816 NS_IMETHODIMP
5817 SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult) {
5818 return QueryInterface(aIID, aResult);
5821 /* static */
5822 nsresult nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsACString& aOrigin) {
5823 MOZ_ASSERT(aURI, "missing uri");
5825 // For Blob URI, the path is the URL of the owning page.
5826 if (aURI->SchemeIs(BLOBURI_SCHEME)) {
5827 nsAutoCString path;
5828 nsresult rv = aURI->GetPathQueryRef(path);
5829 NS_ENSURE_SUCCESS(rv, rv);
5831 nsCOMPtr<nsIURI> uri;
5832 rv = NS_NewURI(getter_AddRefs(uri), path);
5833 if (NS_FAILED(rv)) {
5834 aOrigin.AssignLiteral("null");
5835 return NS_OK;
5838 return GetASCIIOrigin(uri, aOrigin);
5841 aOrigin.Truncate();
5843 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
5844 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
5846 nsAutoCString host;
5847 nsresult rv = uri->GetAsciiHost(host);
5849 if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
5850 nsAutoCString userPass;
5851 uri->GetUserPass(userPass);
5853 nsAutoCString prePath;
5854 if (!userPass.IsEmpty()) {
5855 rv = NS_MutateURI(uri).SetUserPass(EmptyCString()).Finalize(uri);
5856 NS_ENSURE_SUCCESS(rv, rv);
5859 rv = uri->GetPrePath(prePath);
5860 NS_ENSURE_SUCCESS(rv, rv);
5862 aOrigin = prePath;
5863 } else {
5864 aOrigin.AssignLiteral("null");
5867 return NS_OK;
5870 /* static */
5871 nsresult nsContentUtils::GetUTFOrigin(nsIPrincipal* aPrincipal,
5872 nsAString& aOrigin) {
5873 MOZ_ASSERT(aPrincipal, "missing principal");
5875 aOrigin.Truncate();
5876 nsAutoCString asciiOrigin;
5878 nsresult rv = aPrincipal->GetAsciiOrigin(asciiOrigin);
5879 if (NS_FAILED(rv)) {
5880 asciiOrigin.AssignLiteral("null");
5883 aOrigin = NS_ConvertUTF8toUTF16(asciiOrigin);
5884 return NS_OK;
5887 /* static */
5888 nsresult nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsAString& aOrigin) {
5889 MOZ_ASSERT(aURI, "missing uri");
5890 nsresult rv;
5892 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
5893 // Check if either URI has a special origin.
5894 nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin =
5895 do_QueryInterface(aURI);
5896 if (uriWithSpecialOrigin) {
5897 nsCOMPtr<nsIURI> origin;
5898 rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin));
5899 NS_ENSURE_SUCCESS(rv, rv);
5901 return GetUTFOrigin(origin, aOrigin);
5903 #endif
5905 nsAutoCString asciiOrigin;
5906 rv = GetASCIIOrigin(aURI, asciiOrigin);
5907 NS_ENSURE_SUCCESS(rv, rv);
5909 aOrigin = NS_ConvertUTF8toUTF16(asciiOrigin);
5910 return NS_OK;
5913 /* static */
5914 bool nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal,
5915 nsIChannel* aChannel,
5916 bool aAllowIfInheritsPrincipal) {
5917 nsCOMPtr<nsIURI> channelURI;
5918 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
5919 NS_ENSURE_SUCCESS(rv, false);
5921 return NS_SUCCEEDED(
5922 aPrincipal->CheckMayLoad(channelURI, aAllowIfInheritsPrincipal));
5925 /* static */
5926 bool nsContentUtils::CanAccessNativeAnon() {
5927 return LegacyIsCallerChromeOrNativeCode();
5930 /* static */
5931 nsresult nsContentUtils::DispatchXULCommand(nsIContent* aTarget, bool aTrusted,
5932 Event* aSourceEvent,
5933 PresShell* aPresShell, bool aCtrl,
5934 bool aAlt, bool aShift, bool aMeta,
5935 uint16_t aInputSource) {
5936 NS_ENSURE_STATE(aTarget);
5937 Document* doc = aTarget->OwnerDoc();
5938 nsPresContext* presContext = doc->GetPresContext();
5940 RefPtr<XULCommandEvent> xulCommand =
5941 new XULCommandEvent(doc, presContext, nullptr);
5942 xulCommand->InitCommandEvent(u"command"_ns, true, true,
5943 nsGlobalWindowInner::Cast(doc->GetInnerWindow()),
5944 0, aCtrl, aAlt, aShift, aMeta, aSourceEvent,
5945 aInputSource, IgnoreErrors());
5947 if (aPresShell) {
5948 nsEventStatus status = nsEventStatus_eIgnore;
5949 return aPresShell->HandleDOMEventWithTarget(aTarget, xulCommand, &status);
5952 ErrorResult rv;
5953 aTarget->DispatchEvent(*xulCommand, rv);
5954 return rv.StealNSResult();
5957 // static
5958 nsresult nsContentUtils::WrapNative(JSContext* cx, nsISupports* native,
5959 nsWrapperCache* cache, const nsIID* aIID,
5960 JS::MutableHandle<JS::Value> vp,
5961 bool aAllowWrapping) {
5962 MOZ_ASSERT(cx == GetCurrentJSContext());
5964 if (!native) {
5965 vp.setNull();
5967 return NS_OK;
5970 JSObject* wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
5971 if (wrapper) {
5972 return NS_OK;
5975 NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
5977 if (!NS_IsMainThread()) {
5978 MOZ_CRASH();
5981 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
5982 nsresult rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
5983 aAllowWrapping, vp);
5984 return rv;
5987 nsresult nsContentUtils::CreateArrayBuffer(JSContext* aCx,
5988 const nsACString& aData,
5989 JSObject** aResult) {
5990 if (!aCx) {
5991 return NS_ERROR_FAILURE;
5994 int32_t dataLen = aData.Length();
5995 *aResult = JS::NewArrayBuffer(aCx, dataLen);
5996 if (!*aResult) {
5997 return NS_ERROR_FAILURE;
6000 if (dataLen > 0) {
6001 NS_ASSERTION(JS::IsArrayBufferObject(*aResult), "What happened?");
6002 JS::AutoCheckCannotGC nogc;
6003 bool isShared;
6004 memcpy(JS::GetArrayBufferData(*aResult, &isShared, nogc),
6005 aData.BeginReading(), dataLen);
6006 MOZ_ASSERT(!isShared);
6009 return NS_OK;
6012 void nsContentUtils::StripNullChars(const nsAString& aInStr,
6013 nsAString& aOutStr) {
6014 // In common cases where we don't have nulls in the
6015 // string we can simple simply bypass the checking code.
6016 int32_t firstNullPos = aInStr.FindChar('\0');
6017 if (firstNullPos == kNotFound) {
6018 aOutStr.Assign(aInStr);
6019 return;
6022 aOutStr.SetCapacity(aInStr.Length() - 1);
6023 nsAString::const_iterator start, end;
6024 aInStr.BeginReading(start);
6025 aInStr.EndReading(end);
6026 while (start != end) {
6027 if (*start != '\0') aOutStr.Append(*start);
6028 ++start;
6032 struct ClassMatchingInfo {
6033 AtomArray mClasses;
6034 nsCaseTreatment mCaseTreatment;
6037 // static
6038 bool nsContentUtils::MatchClassNames(Element* aElement, int32_t aNamespaceID,
6039 nsAtom* aAtom, void* aData) {
6040 // We can't match if there are no class names
6041 const nsAttrValue* classAttr = aElement->GetClasses();
6042 if (!classAttr) {
6043 return false;
6046 // need to match *all* of the classes
6047 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6048 uint32_t length = info->mClasses.Length();
6049 if (!length) {
6050 // If we actually had no classes, don't match.
6051 return false;
6053 uint32_t i;
6054 for (i = 0; i < length; ++i) {
6055 if (!classAttr->Contains(info->mClasses[i], info->mCaseTreatment)) {
6056 return false;
6060 return true;
6063 // static
6064 void nsContentUtils::DestroyClassNameArray(void* aData) {
6065 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6066 delete info;
6069 // static
6070 void* nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
6071 const nsString* aClasses) {
6072 nsAttrValue attrValue;
6073 attrValue.ParseAtomArray(*aClasses);
6074 // nsAttrValue::Equals is sensitive to order, so we'll send an array
6075 auto* info = new ClassMatchingInfo;
6076 if (attrValue.Type() == nsAttrValue::eAtomArray) {
6077 info->mClasses = std::move(*(attrValue.GetAtomArrayValue()));
6078 } else if (attrValue.Type() == nsAttrValue::eAtom) {
6079 info->mClasses.AppendElement(attrValue.GetAtomValue());
6082 info->mCaseTreatment =
6083 aRootNode->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks
6084 ? eIgnoreCase
6085 : eCaseMatters;
6086 return info;
6089 // static
6090 bool nsContentUtils::IsFocusedContent(const nsIContent* aContent) {
6091 nsFocusManager* fm = nsFocusManager::GetFocusManager();
6093 return fm && fm->GetFocusedElement() == aContent;
6096 bool nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent) {
6097 Document* doc = aContent->GetComposedDoc();
6098 if (!doc) {
6099 return false;
6102 // If the subdocument lives in another process, the frame is
6103 // tabbable.
6104 if (EventStateManager::IsRemoteTarget(aContent)) {
6105 return true;
6108 // XXXbz should this use OwnerDoc() for GetSubDocumentFor?
6109 // sXBL/XBL2 issue!
6110 Document* subDoc = doc->GetSubDocumentFor(aContent);
6111 if (!subDoc) {
6112 return false;
6115 nsCOMPtr<nsIDocShell> docShell = subDoc->GetDocShell();
6116 if (!docShell) {
6117 return false;
6120 nsCOMPtr<nsIContentViewer> contentViewer;
6121 docShell->GetContentViewer(getter_AddRefs(contentViewer));
6122 if (!contentViewer) {
6123 return false;
6126 // If there are 2 viewers for the current docshell, that
6127 // means the current document may be a zombie document.
6128 // While load and pageshow events are dispatched, zombie viewer is the old,
6129 // to be hidden document.
6130 if (contentViewer->GetPreviousViewer()) {
6131 bool inOnLoad = false;
6132 docShell->GetIsExecutingOnLoadHandler(&inOnLoad);
6133 return inOnLoad;
6136 return true;
6139 bool nsContentUtils::HasScrollgrab(nsIContent* aContent) {
6140 // If we ever standardize this feature we'll want to hook this up properly
6141 // again. For now we're removing all the DOM-side code related to it but
6142 // leaving the layout and APZ handling for it in place.
6143 return false;
6146 void nsContentUtils::FlushLayoutForTree(nsPIDOMWindowOuter* aWindow) {
6147 if (!aWindow) {
6148 return;
6151 // Note that because FlushPendingNotifications flushes parents, this
6152 // is O(N^2) in docshell tree depth. However, the docshell tree is
6153 // usually pretty shallow.
6155 if (RefPtr<Document> doc = aWindow->GetDoc()) {
6156 doc->FlushPendingNotifications(FlushType::Layout);
6159 if (nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell()) {
6160 int32_t i = 0, i_end;
6161 docShell->GetInProcessChildCount(&i_end);
6162 for (; i < i_end; ++i) {
6163 nsCOMPtr<nsIDocShellTreeItem> item;
6164 if (docShell->GetInProcessChildAt(i, getter_AddRefs(item)) == NS_OK &&
6165 item) {
6166 if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
6167 FlushLayoutForTree(win);
6174 void nsContentUtils::RemoveNewlines(nsString& aString) { aString.StripCRLF(); }
6176 void nsContentUtils::PlatformToDOMLineBreaks(nsString& aString) {
6177 if (!PlatformToDOMLineBreaks(aString, fallible)) {
6178 aString.AllocFailed(aString.Length());
6182 bool nsContentUtils::PlatformToDOMLineBreaks(nsString& aString,
6183 const fallible_t& aFallible) {
6184 if (aString.FindChar(char16_t('\r')) != -1) {
6185 // Windows linebreaks: Map CRLF to LF:
6186 if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
6187 return false;
6190 // Mac linebreaks: Map any remaining CR to LF:
6191 if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
6192 return false;
6196 return true;
6199 void nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
6200 nsAString& aResultString) {
6201 MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
6203 uint32_t stringLen = NS_strlen(static_cast<char16_t*>(aBuf->Data()));
6205 // SANITY CHECK: In case the nsStringBuffer isn't correctly
6206 // null-terminated, let's clamp its length using the allocated size, to be
6207 // sure the resulting string doesn't sample past the end of the the buffer.
6208 // (Note that StorageSize() is in units of bytes, so we have to convert that
6209 // to units of PRUnichars, and subtract 1 for the null-terminator.)
6210 uint32_t allocStringLen = (aBuf->StorageSize() / sizeof(char16_t)) - 1;
6211 MOZ_ASSERT(stringLen <= allocStringLen,
6212 "string buffer lacks null terminator!");
6213 stringLen = std::min(stringLen, allocStringLen);
6215 aBuf->ToString(stringLen, aResultString);
6218 PresShell* nsContentUtils::FindPresShellForDocument(const Document* aDocument) {
6219 const Document* doc = aDocument;
6220 Document* displayDoc = doc->GetDisplayDocument();
6221 if (displayDoc) {
6222 doc = displayDoc;
6225 PresShell* presShell = doc->GetPresShell();
6226 if (presShell) {
6227 return presShell;
6230 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
6231 while (docShellTreeItem) {
6232 // We may be in a display:none subdocument, or we may not have a presshell
6233 // created yet.
6234 // Walk the docshell tree to find the nearest container that has a
6235 // presshell, and return that.
6236 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
6237 if (PresShell* presShell = docShell->GetPresShell()) {
6238 return presShell;
6240 nsCOMPtr<nsIDocShellTreeItem> parent;
6241 docShellTreeItem->GetInProcessParent(getter_AddRefs(parent));
6242 docShellTreeItem = parent;
6245 return nullptr;
6248 nsIWidget* nsContentUtils::WidgetForDocument(const Document* aDocument) {
6249 PresShell* presShell = FindPresShellForDocument(aDocument);
6250 if (!presShell) {
6251 return nullptr;
6253 nsViewManager* vm = presShell->GetViewManager();
6254 if (!vm) {
6255 return nullptr;
6257 nsView* rootView = vm->GetRootView();
6258 if (!rootView) {
6259 return nullptr;
6261 nsView* displayRoot = nsViewManager::GetDisplayRootFor(rootView);
6262 if (!displayRoot) {
6263 return nullptr;
6265 return displayRoot->GetNearestWidget(nullptr);
6268 nsIWidget* nsContentUtils::WidgetForContent(const nsIContent* aContent) {
6269 nsIFrame* frame = aContent->GetPrimaryFrame();
6270 if (frame) {
6271 frame = nsLayoutUtils::GetDisplayRootFrame(frame);
6273 nsView* view = frame->GetView();
6274 if (view) {
6275 return view->GetWidget();
6279 return nullptr;
6282 already_AddRefed<LayerManager> nsContentUtils::LayerManagerForContent(
6283 const nsIContent* aContent) {
6284 nsIWidget* widget = nsContentUtils::WidgetForContent(aContent);
6285 if (widget) {
6286 RefPtr<LayerManager> manager = widget->GetLayerManager();
6287 return manager.forget();
6290 return nullptr;
6293 static already_AddRefed<LayerManager> LayerManagerForDocumentInternal(
6294 const Document* aDoc, bool aRequirePersistent) {
6295 nsIWidget* widget = nsContentUtils::WidgetForDocument(aDoc);
6296 if (widget) {
6297 RefPtr<LayerManager> manager = widget->GetLayerManager(
6298 aRequirePersistent ? nsIWidget::LAYER_MANAGER_PERSISTENT
6299 : nsIWidget::LAYER_MANAGER_CURRENT);
6300 return manager.forget();
6303 return nullptr;
6306 already_AddRefed<LayerManager> nsContentUtils::LayerManagerForDocument(
6307 const Document* aDoc) {
6308 return LayerManagerForDocumentInternal(aDoc, false);
6311 already_AddRefed<LayerManager>
6312 nsContentUtils::PersistentLayerManagerForDocument(Document* aDoc) {
6313 return LayerManagerForDocumentInternal(aDoc, true);
6316 bool nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal) {
6317 if (!aPrincipal) {
6318 return false;
6321 if (aPrincipal->IsSystemPrincipal()) {
6322 return true;
6325 return (StaticPrefs::dom_allow_XUL_XBL_for_file() &&
6326 aPrincipal->SchemeIs("file")) ||
6327 IsSitePermAllow(aPrincipal, "allowXULXBL"_ns);
6330 bool nsContentUtils::IsPDFJSEnabled() {
6331 nsCOMPtr<nsIStreamConverter> conv = do_CreateInstance(
6332 "@mozilla.org/streamconv;1?from=application/pdf&to=text/html");
6333 return conv;
6336 bool nsContentUtils::IsPDFJS(nsIPrincipal* aPrincipal) {
6337 if (!aPrincipal) {
6338 return false;
6340 nsAutoCString spec;
6341 nsresult rv = aPrincipal->GetAsciiSpec(spec);
6342 NS_ENSURE_SUCCESS(rv, false);
6343 return spec.EqualsLiteral("resource://pdf.js/web/viewer.html");
6346 already_AddRefed<nsIDocumentLoaderFactory>
6347 nsContentUtils::FindInternalContentViewer(const nsACString& aType,
6348 ContentViewerType* aLoaderType) {
6349 if (aLoaderType) {
6350 *aLoaderType = TYPE_UNSUPPORTED;
6353 // one helper factory, please
6354 nsCOMPtr<nsICategoryManager> catMan(
6355 do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
6356 if (!catMan) return nullptr;
6358 nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
6360 nsCString contractID;
6361 nsresult rv =
6362 catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, contractID);
6363 if (NS_SUCCEEDED(rv)) {
6364 docFactory = do_GetService(contractID.get());
6365 if (docFactory && aLoaderType) {
6366 if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
6367 *aLoaderType = TYPE_CONTENT;
6368 else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
6369 *aLoaderType = TYPE_PLUGIN;
6370 else
6371 *aLoaderType = TYPE_UNKNOWN;
6373 return docFactory.forget();
6376 if (DecoderTraits::IsSupportedInVideoDocument(aType)) {
6377 docFactory =
6378 do_GetService("@mozilla.org/content/document-loader-factory;1");
6379 if (docFactory && aLoaderType) {
6380 *aLoaderType = TYPE_CONTENT;
6382 return docFactory.forget();
6385 return nullptr;
6388 static void ReportPatternCompileFailure(nsAString& aPattern,
6389 const Document* aDocument,
6390 JS::MutableHandle<JS::Value> error,
6391 JSContext* cx) {
6392 JS::AutoSaveExceptionState savedExc(cx);
6393 JS::RootedObject exnObj(cx, &error.toObject());
6394 JS::RootedValue messageVal(cx);
6395 if (!JS_GetProperty(cx, exnObj, "message", &messageVal)) {
6396 return;
6398 JS::RootedString messageStr(cx, messageVal.toString());
6399 MOZ_ASSERT(messageStr);
6401 AutoTArray<nsString, 2> strings;
6402 strings.AppendElement(aPattern);
6403 if (!AssignJSString(cx, *strings.AppendElement(), messageStr)) {
6404 return;
6407 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns,
6408 aDocument, nsContentUtils::eDOM_PROPERTIES,
6409 "PatternAttributeCompileFailure", strings);
6410 savedExc.drop();
6413 // static
6414 Maybe<bool> nsContentUtils::IsPatternMatching(nsAString& aValue,
6415 nsAString& aPattern,
6416 const Document* aDocument) {
6417 NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
6419 // The fact that we're using a JS regexp under the hood should not be visible
6420 // to things like window onerror handlers, so we don't initialize our JSAPI
6421 // with the document's window (which may not exist anyway).
6422 AutoJSAPI jsapi;
6423 jsapi.Init();
6424 JSContext* cx = jsapi.cx();
6425 AutoDisableJSInterruptCallback disabler(cx);
6427 // We can use the junk scope here, because we're just using it for regexp
6428 // evaluation, not actual script execution, and we disable statics so that the
6429 // evaluation does not interact with the execution global.
6430 JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
6432 // Check if the pattern by itself is valid first, and not that it only becomes
6433 // valid once we add ^(?: and )$.
6434 JS::RootedValue error(cx);
6435 if (!JS::CheckRegExpSyntax(
6436 cx, static_cast<char16_t*>(aPattern.BeginWriting()),
6437 aPattern.Length(), JS::RegExpFlag::Unicode, &error)) {
6438 return Nothing();
6441 if (!error.isUndefined()) {
6442 ReportPatternCompileFailure(aPattern, aDocument, &error, cx);
6443 return Some(true);
6446 // The pattern has to match the entire value.
6447 aPattern.InsertLiteral(u"^(?:", 0);
6448 aPattern.AppendLiteral(")$");
6450 JS::Rooted<JSObject*> re(
6452 JS::NewUCRegExpObject(cx, static_cast<char16_t*>(aPattern.BeginWriting()),
6453 aPattern.Length(), JS::RegExpFlag::Unicode));
6454 if (!re) {
6455 return Nothing();
6458 JS::Rooted<JS::Value> rval(cx, JS::NullValue());
6459 size_t idx = 0;
6460 if (!JS::ExecuteRegExpNoStatics(cx, re,
6461 static_cast<char16_t*>(aValue.BeginWriting()),
6462 aValue.Length(), &idx, true, &rval)) {
6463 return Nothing();
6466 return Some(!rval.isNull());
6469 // static
6470 nsresult nsContentUtils::URIInheritsSecurityContext(nsIURI* aURI,
6471 bool* aResult) {
6472 // Note: about:blank URIs do NOT inherit the security context from the
6473 // current document, which is what this function tests for...
6474 return NS_URIChainHasFlags(
6475 aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, aResult);
6478 // static
6479 bool nsContentUtils::ChannelShouldInheritPrincipal(
6480 nsIPrincipal* aLoadingPrincipal, nsIURI* aURI, bool aInheritForAboutBlank,
6481 bool aForceInherit) {
6482 MOZ_ASSERT(aLoadingPrincipal,
6483 "Can not check inheritance without a principal");
6485 // Only tell the channel to inherit if it can't provide its own security
6486 // context.
6488 // XXX: If this is ever changed, check all callers for what owners
6489 // they're passing in. In particular, see the code and
6490 // comments in nsDocShell::LoadURI where we fall back on
6491 // inheriting the owner if called from chrome. That would be
6492 // very wrong if this code changed anything but channels that
6493 // can't provide their own security context!
6495 // If aForceInherit is true, we will inherit, even for a channel that
6496 // can provide its own security context. This is used for srcdoc loads.
6497 bool inherit = aForceInherit;
6498 if (!inherit) {
6499 bool uriInherits;
6500 // We expect URIInheritsSecurityContext to return success for an
6501 // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
6502 // This condition needs to match the one in nsDocShell::InternalLoad where
6503 // we're checking for things that will use the owner.
6504 inherit =
6505 (NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &uriInherits)) &&
6506 (uriInherits || (aInheritForAboutBlank && NS_IsAboutBlank(aURI)))) ||
6508 // file: uri special-casing
6510 // If this is a file: load opened from another file: then it may need
6511 // to inherit the owner from the referrer so they can script each other.
6512 // If we don't set the owner explicitly then each file: gets an owner
6513 // based on its own codebase later.
6515 (URIIsLocalFile(aURI) &&
6516 NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, false)) &&
6517 // One more check here. CheckMayLoad will always return true for the
6518 // system principal, but we do NOT want to inherit in that case.
6519 !aLoadingPrincipal->IsSystemPrincipal());
6521 return inherit;
6524 /* static */
6525 bool nsContentUtils::IsCutCopyAllowed(Document* aDocument,
6526 nsIPrincipal& aSubjectPrincipal) {
6527 if (StaticPrefs::dom_allow_cut_copy() && aDocument &&
6528 aDocument->HasValidTransientUserGestureActivation()) {
6529 return true;
6532 return PrincipalHasPermission(aSubjectPrincipal, nsGkAtoms::clipboardWrite);
6535 /* static */
6536 bool nsContentUtils::HaveEqualPrincipals(Document* aDoc1, Document* aDoc2) {
6537 if (!aDoc1 || !aDoc2) {
6538 return false;
6540 bool principalsEqual = false;
6541 aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
6542 return principalsEqual;
6545 /* static */
6546 bool nsContentUtils::HasPluginWithUncontrolledEventDispatch(
6547 nsIContent* aContent) {
6548 #ifdef XP_MACOSX
6549 // We control dispatch to all mac plugins.
6550 return false;
6551 #else
6552 if (!aContent || !aContent->IsInComposedDoc()) {
6553 return false;
6556 nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(aContent);
6557 if (!olc) {
6558 return false;
6561 RefPtr<nsNPAPIPluginInstance> plugin = olc->GetPluginInstance();
6562 if (!plugin) {
6563 return false;
6566 bool isWindowless = false;
6567 nsresult res = plugin->IsWindowless(&isWindowless);
6568 if (NS_FAILED(res)) {
6569 return false;
6572 return !isWindowless;
6573 #endif
6576 /* static */
6577 void nsContentUtils::FireMutationEventsForDirectParsing(
6578 Document* aDoc, nsIContent* aDest, int32_t aOldChildCount) {
6579 // Fire mutation events. Optimize for the case when there are no listeners
6580 int32_t newChildCount = aDest->GetChildCount();
6581 if (newChildCount && nsContentUtils::HasMutationListeners(
6582 aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
6583 AutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
6584 NS_ASSERTION(newChildCount - aOldChildCount >= 0,
6585 "What, some unexpected dom mutation has happened?");
6586 childNodes.SetCapacity(newChildCount - aOldChildCount);
6587 for (nsIContent* child = aDest->GetFirstChild(); child;
6588 child = child->GetNextSibling()) {
6589 childNodes.AppendElement(child);
6591 FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
6595 /* static */
6596 Document* nsContentUtils::GetRootDocument(Document* aDoc) {
6597 if (!aDoc) {
6598 return nullptr;
6600 Document* doc = aDoc;
6601 while (doc->GetInProcessParentDocument()) {
6602 doc = doc->GetInProcessParentDocument();
6604 return doc;
6607 /* static */
6608 bool nsContentUtils::IsInPointerLockContext(nsPIDOMWindowOuter* aWin) {
6609 if (!aWin) {
6610 return false;
6613 nsCOMPtr<Document> pointerLockedDoc =
6614 do_QueryReferent(EventStateManager::sPointerLockedDoc);
6615 if (!pointerLockedDoc || !pointerLockedDoc->GetWindow()) {
6616 return false;
6619 nsCOMPtr<nsPIDOMWindowOuter> lockTop =
6620 pointerLockedDoc->GetWindow()->GetInProcessScriptableTop();
6621 nsCOMPtr<nsPIDOMWindowOuter> top = aWin->GetInProcessScriptableTop();
6623 return top == lockTop;
6626 // static
6627 int32_t nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
6628 int32_t aOffset) {
6629 // The structure of the anonymous frames within a text control frame is
6630 // an optional block frame, followed by an optional br frame.
6632 // If the offset frame has a child, then this frame is the block which
6633 // has the text frames (containing the content) as its children. This will
6634 // be the case if we click to the right of any of the text frames, or at the
6635 // bottom of the text area.
6636 nsIFrame* firstChild = aOffsetFrame->PrincipalChildList().FirstChild();
6637 if (firstChild) {
6638 // In this case, the passed-in offset is incorrect, and we want the length
6639 // of the entire content in the text control frame.
6640 return firstChild->GetContent()->Length();
6643 if (aOffsetFrame->GetPrevSibling() && !aOffsetFrame->GetNextSibling()) {
6644 // In this case, we're actually within the last frame, which is a br
6645 // frame. Our offset should therefore be the length of the first child of
6646 // our parent.
6647 int32_t aOutOffset = aOffsetFrame->GetParent()
6648 ->PrincipalChildList()
6649 .FirstChild()
6650 ->GetContent()
6651 ->Length();
6652 return aOutOffset;
6655 // Otherwise, we're within one of the text frames, in which case our offset
6656 // has already been correctly calculated.
6657 return aOffset;
6660 // static
6661 void nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
6662 Element* aRoot,
6663 uint32_t& aOutStartOffset,
6664 uint32_t& aOutEndOffset) {
6665 MOZ_ASSERT(aSelection && aRoot);
6667 // We don't care which end of this selection is anchor and which is focus. In
6668 // fact, we explicitly want to know which is the _start_ and which is the
6669 // _end_, not anchor vs focus.
6670 const nsRange* range = aSelection->GetAnchorFocusRange();
6671 if (!range) {
6672 // Nothing selected
6673 aOutStartOffset = aOutEndOffset = 0;
6674 return;
6677 // All the node pointers here are raw pointers for performance. We shouldn't
6678 // be doing anything in this function that invalidates the node tree.
6679 nsINode* startContainer = range->GetStartContainer();
6680 uint32_t startOffset = range->StartOffset();
6681 nsINode* endContainer = range->GetEndContainer();
6682 uint32_t endOffset = range->EndOffset();
6684 // We have at most two children, consisting of an optional text node followed
6685 // by an optional <br>.
6686 NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
6687 nsIContent* firstChild = aRoot->GetFirstChild();
6688 #ifdef DEBUG
6689 nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
6690 NS_ASSERTION(startContainer == aRoot || startContainer == firstChild ||
6691 startContainer == lastChild,
6692 "Unexpected startContainer");
6693 NS_ASSERTION(endContainer == aRoot || endContainer == firstChild ||
6694 endContainer == lastChild,
6695 "Unexpected endContainer");
6696 // firstChild is either text or a <br> (hence an element).
6697 MOZ_ASSERT_IF(firstChild, firstChild->IsText() || firstChild->IsElement());
6698 #endif
6699 // Testing IsElement() is faster than testing IsNodeOfType(), since it's
6700 // non-virtual.
6701 if (!firstChild || firstChild->IsElement()) {
6702 // No text node, so everything is 0
6703 startOffset = endOffset = 0;
6704 } else {
6705 // First child is text. If the start/end is already in the text node,
6706 // or the start of the root node, no change needed. If it's in the root
6707 // node but not the start, or in the trailing <br>, we need to set the
6708 // offset to the end.
6709 if ((startContainer == aRoot && startOffset != 0) ||
6710 (startContainer != aRoot && startContainer != firstChild)) {
6711 startOffset = firstChild->Length();
6713 if ((endContainer == aRoot && endOffset != 0) ||
6714 (endContainer != aRoot && endContainer != firstChild)) {
6715 endOffset = firstChild->Length();
6719 MOZ_ASSERT(startOffset <= endOffset);
6720 aOutStartOffset = startOffset;
6721 aOutEndOffset = endOffset;
6724 HTMLEditor* nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext) {
6725 if (!aPresContext) {
6726 return nullptr;
6729 nsCOMPtr<nsIDocShell> docShell(aPresContext->GetDocShell());
6730 bool isEditable;
6731 if (!docShell || NS_FAILED(docShell->GetEditable(&isEditable)) || !isEditable)
6732 return nullptr;
6734 return docShell->GetHTMLEditor();
6737 // static
6738 TextEditor* nsContentUtils::GetActiveEditor(nsPresContext* aPresContext) {
6739 if (!aPresContext) {
6740 return nullptr;
6743 nsPIDOMWindowOuter* window = aPresContext->Document()->GetWindow();
6744 if (!window) {
6745 return nullptr;
6748 // If it's in designMode, nobody can have focus. Therefore, the HTMLEditor
6749 // handles all events. I.e., it's focused editor in this case.
6750 if (aPresContext->Document()->HasFlag(NODE_IS_EDITABLE)) {
6751 return GetHTMLEditor(aPresContext);
6754 // If focused element is associated with TextEditor, it must be <input>
6755 // element or <textarea> element. Let's return it even if it's in a
6756 // contenteditable element.
6757 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
6758 if (Element* focusedElement = nsFocusManager::GetFocusedDescendant(
6759 window, nsFocusManager::SearchRange::eOnlyCurrentWindow,
6760 getter_AddRefs(focusedWindow))) {
6761 if (TextEditor* textEditor = focusedElement->GetTextEditorInternal()) {
6762 return textEditor;
6766 // Otherwise, HTMLEditor may handle inputs even non-editable element has
6767 // focus or nobody has focus.
6768 return GetHTMLEditor(aPresContext);
6771 // static
6772 TextEditor* nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(
6773 nsIContent* aAnonymousContent) {
6774 if (!aAnonymousContent) {
6775 return nullptr;
6777 nsIContent* parent = aAnonymousContent->FindFirstNonChromeOnlyAccessContent();
6778 if (!parent || parent == aAnonymousContent) {
6779 return nullptr;
6781 if (HTMLInputElement* inputElement =
6782 HTMLInputElement::FromNodeOrNull(parent)) {
6783 return inputElement->GetTextEditorWithoutCreation();
6785 if (HTMLTextAreaElement* textareaElement =
6786 HTMLTextAreaElement::FromNodeOrNull(parent)) {
6787 return textareaElement->GetTextEditorWithoutCreation();
6789 return nullptr;
6792 // static
6793 bool nsContentUtils::IsNodeInEditableRegion(nsINode* aNode) {
6794 while (aNode) {
6795 if (aNode->IsEditable()) {
6796 return true;
6798 aNode = aNode->GetParent();
6800 return false;
6803 // static
6804 bool nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader) {
6805 if (IsForbiddenSystemRequestHeader(aHeader)) {
6806 return true;
6809 return StringBeginsWith(aHeader, "proxy-"_ns,
6810 nsCaseInsensitiveCStringComparator) ||
6811 StringBeginsWith(aHeader, "sec-"_ns,
6812 nsCaseInsensitiveCStringComparator);
6815 // static
6816 bool nsContentUtils::IsForbiddenSystemRequestHeader(const nsACString& aHeader) {
6817 static const char* kInvalidHeaders[] = {"accept-charset",
6818 "accept-encoding",
6819 "access-control-request-headers",
6820 "access-control-request-method",
6821 "connection",
6822 "content-length",
6823 "cookie",
6824 "cookie2",
6825 "date",
6826 "dnt",
6827 "expect",
6828 "host",
6829 "keep-alive",
6830 "origin",
6831 "referer",
6832 "te",
6833 "trailer",
6834 "transfer-encoding",
6835 "upgrade",
6836 "via"};
6837 for (auto& kInvalidHeader : kInvalidHeaders) {
6838 if (aHeader.LowerCaseEqualsASCII(kInvalidHeader)) {
6839 return true;
6842 return false;
6845 // static
6846 bool nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader) {
6847 return (aHeader.LowerCaseEqualsASCII("set-cookie") ||
6848 aHeader.LowerCaseEqualsASCII("set-cookie2"));
6851 // static
6852 bool nsContentUtils::IsCorsUnsafeRequestHeaderValue(
6853 const nsACString& aHeaderValue) {
6854 const char* cur = aHeaderValue.BeginReading();
6855 const char* end = aHeaderValue.EndReading();
6857 while (cur != end) {
6858 // Implementation of
6859 // https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte Is less
6860 // than a space but not a horizontal tab
6861 if ((*cur < ' ' && *cur != '\t') || *cur == '"' || *cur == '(' ||
6862 *cur == ')' || *cur == ':' || *cur == '<' || *cur == '>' ||
6863 *cur == '?' || *cur == '@' || *cur == '[' || *cur == '\\' ||
6864 *cur == ']' || *cur == '{' || *cur == '}' ||
6865 *cur == 0x7F) { // 0x75 is DEL
6866 return true;
6868 cur++;
6870 return false;
6873 // static
6874 bool nsContentUtils::IsAllowedNonCorsAccept(const nsACString& aHeaderValue) {
6875 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
6876 return false;
6878 return true;
6881 // static
6882 bool nsContentUtils::IsAllowedNonCorsContentType(
6883 const nsACString& aHeaderValue) {
6884 nsAutoCString contentType;
6885 nsAutoCString unused;
6887 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
6888 return false;
6891 nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
6892 if (NS_FAILED(rv)) {
6893 return false;
6896 return contentType.LowerCaseEqualsLiteral("text/plain") ||
6897 contentType.LowerCaseEqualsLiteral(
6898 "application/x-www-form-urlencoded") ||
6899 contentType.LowerCaseEqualsLiteral("multipart/form-data");
6902 // static
6903 bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
6904 const char* cur = aHeaderValue.BeginReading();
6905 const char* end = aHeaderValue.EndReading();
6907 while (cur != end) {
6908 if ((*cur >= '0' && *cur <= '9') || (*cur >= 'A' && *cur <= 'Z') ||
6909 (*cur >= 'a' && *cur <= 'z') || *cur == ' ' || *cur == '*' ||
6910 *cur == ',' || *cur == '-' || *cur == '.' || *cur == ';' ||
6911 *cur == '=') {
6912 cur++;
6913 continue;
6915 return false;
6917 return true;
6920 // static
6921 bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
6922 const nsACString& aValue) {
6923 // see https://fetch.spec.whatwg.org/#cors-safelisted-request-header
6924 if (aValue.Length() > 128) {
6925 return false;
6927 return (aName.LowerCaseEqualsLiteral("accept") &&
6928 nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
6929 (aName.LowerCaseEqualsLiteral("accept-language") &&
6930 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
6931 (aName.LowerCaseEqualsLiteral("content-language") &&
6932 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
6933 (aName.LowerCaseEqualsLiteral("content-type") &&
6934 nsContentUtils::IsAllowedNonCorsContentType(aValue));
6937 mozilla::LogModule* nsContentUtils::DOMDumpLog() { return sDOMDumpLog; }
6939 bool nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep,
6940 nsAString& aResult,
6941 const fallible_t& aFallible) {
6942 aResult.Truncate();
6943 return AppendNodeTextContent(aNode, aDeep, aResult, aFallible);
6946 void nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep,
6947 nsAString& aResult) {
6948 if (!GetNodeTextContent(aNode, aDeep, aResult, fallible)) {
6949 NS_ABORT_OOM(0); // Unfortunately we don't know the allocation size
6953 void nsContentUtils::DestroyMatchString(void* aData) {
6954 if (aData) {
6955 nsString* matchString = static_cast<nsString*>(aData);
6956 delete matchString;
6960 bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
6961 // Table ordered from most to least likely JS MIME types.
6962 static const char* jsTypes[] = {"text/javascript",
6963 "text/ecmascript",
6964 "application/javascript",
6965 "application/ecmascript",
6966 "application/x-javascript",
6967 "application/x-ecmascript",
6968 "text/javascript1.0",
6969 "text/javascript1.1",
6970 "text/javascript1.2",
6971 "text/javascript1.3",
6972 "text/javascript1.4",
6973 "text/javascript1.5",
6974 "text/jscript",
6975 "text/livescript",
6976 "text/x-ecmascript",
6977 "text/x-javascript",
6978 nullptr};
6980 for (uint32_t i = 0; jsTypes[i]; ++i) {
6981 if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
6982 return true;
6986 return false;
6989 nsresult nsContentUtils::GenerateUUIDInPlace(nsID& aUUID) {
6990 MOZ_ASSERT(sUUIDGenerator);
6992 nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&aUUID);
6993 if (NS_WARN_IF(NS_FAILED(rv))) {
6994 return rv;
6997 return NS_OK;
7000 nsID nsContentUtils::GenerateUUID() {
7001 MOZ_DIAGNOSTIC_ASSERT(sUUIDGenerator);
7003 nsID uuid;
7004 nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&uuid);
7005 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
7007 return uuid;
7010 bool nsContentUtils::PrefetchPreloadEnabled(nsIDocShell* aDocShell) {
7012 // SECURITY CHECK: disable prefetching and preloading from mailnews!
7014 // walk up the docshell tree to see if any containing
7015 // docshell are of type MAIL.
7018 if (!aDocShell) {
7019 return false;
7022 nsCOMPtr<nsIDocShell> docshell = aDocShell;
7023 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7025 do {
7026 auto appType = docshell->GetAppType();
7027 if (appType == nsIDocShell::APP_TYPE_MAIL) {
7028 return false; // do not prefetch, preload, preconnect from mailnews
7031 docshell->GetInProcessParent(getter_AddRefs(parentItem));
7032 if (parentItem) {
7033 docshell = do_QueryInterface(parentItem);
7034 if (!docshell) {
7035 NS_ERROR("cannot get a docshell from a treeItem!");
7036 return false;
7039 } while (parentItem);
7041 return true;
7044 uint64_t nsContentUtils::GetInnerWindowID(nsIRequest* aRequest) {
7045 // can't do anything if there's no nsIRequest!
7046 if (!aRequest) {
7047 return 0;
7050 nsCOMPtr<nsILoadGroup> loadGroup;
7051 nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
7053 if (NS_FAILED(rv) || !loadGroup) {
7054 return 0;
7057 return GetInnerWindowID(loadGroup);
7060 uint64_t nsContentUtils::GetInnerWindowID(nsILoadGroup* aLoadGroup) {
7061 if (!aLoadGroup) {
7062 return 0;
7065 nsCOMPtr<nsIInterfaceRequestor> callbacks;
7066 nsresult rv = aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
7067 if (NS_FAILED(rv) || !callbacks) {
7068 return 0;
7071 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
7072 if (!loadContext) {
7073 return 0;
7076 nsCOMPtr<mozIDOMWindowProxy> window;
7077 rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
7078 if (NS_FAILED(rv) || !window) {
7079 return 0;
7082 auto* pwindow = nsPIDOMWindowOuter::From(window);
7083 if (!pwindow) {
7084 return 0;
7087 nsPIDOMWindowInner* inner = pwindow->GetCurrentInnerWindow();
7088 return inner ? inner->WindowID() : 0;
7091 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7092 nsCString& aHost) {
7093 aHost.Truncate();
7094 nsresult rv = aURI->GetHost(aHost);
7095 if (NS_FAILED(rv)) { // Some URIs do not have a host
7096 return rv;
7099 if (aHost.FindChar(':') != -1) { // Escape IPv6 address
7100 MOZ_ASSERT(!aHost.Length() ||
7101 (aHost[0] != '[' && aHost[aHost.Length() - 1] != ']'));
7102 aHost.Insert('[', 0);
7103 aHost.Append(']');
7106 return NS_OK;
7109 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7110 nsAString& aHost) {
7111 nsAutoCString hostname;
7112 nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname);
7113 if (NS_FAILED(rv)) {
7114 return rv;
7116 CopyUTF8toUTF16(hostname, aHost);
7117 return NS_OK;
7120 CallState nsContentUtils::CallOnAllRemoteChildren(
7121 MessageBroadcaster* aManager,
7122 const std::function<CallState(BrowserParent*)>& aCallback) {
7123 uint32_t browserChildCount = aManager->ChildCount();
7124 for (uint32_t j = 0; j < browserChildCount; ++j) {
7125 RefPtr<MessageListenerManager> childMM = aManager->GetChildAt(j);
7126 if (!childMM) {
7127 continue;
7130 RefPtr<MessageBroadcaster> nonLeafMM = MessageBroadcaster::From(childMM);
7131 if (nonLeafMM) {
7132 if (CallOnAllRemoteChildren(nonLeafMM, aCallback) == CallState::Stop) {
7133 return CallState::Stop;
7135 continue;
7138 mozilla::dom::ipc::MessageManagerCallback* cb = childMM->GetCallback();
7139 if (cb) {
7140 nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
7141 BrowserParent* remote = BrowserParent::GetFrom(fl);
7142 if (remote && aCallback) {
7143 if (aCallback(remote) == CallState::Stop) {
7144 return CallState::Stop;
7150 return CallState::Continue;
7153 void nsContentUtils::CallOnAllRemoteChildren(
7154 nsPIDOMWindowOuter* aWindow,
7155 const std::function<CallState(BrowserParent*)>& aCallback) {
7156 nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(aWindow);
7157 if (window->IsChromeWindow()) {
7158 RefPtr<MessageBroadcaster> windowMM = window->GetMessageManager();
7159 if (windowMM) {
7160 CallOnAllRemoteChildren(windowMM, aCallback);
7165 struct UIStateChangeInfo {
7166 UIStateChangeType mShowFocusRings;
7168 explicit UIStateChangeInfo(UIStateChangeType aShowFocusRings)
7169 : mShowFocusRings(aShowFocusRings) {}
7172 void nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(
7173 nsPIDOMWindowOuter* aWindow, UIStateChangeType aShowFocusRings) {
7174 UIStateChangeInfo stateInfo(aShowFocusRings);
7175 CallOnAllRemoteChildren(aWindow, [&stateInfo](BrowserParent* aBrowserParent) {
7176 Unused << aBrowserParent->SendSetKeyboardIndicators(
7177 stateInfo.mShowFocusRings);
7178 return CallState::Continue;
7182 nsresult nsContentUtils::IPCTransferableToTransferable(
7183 const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
7184 nsIPrincipal* aRequestingPrincipal,
7185 const nsContentPolicyType& aContentPolicyType,
7186 nsITransferable* aTransferable, mozilla::dom::ContentParent* aContentParent,
7187 mozilla::dom::BrowserChild* aBrowserChild) {
7188 nsresult rv;
7190 aTransferable->SetIsPrivateData(aIsPrivateData);
7192 const nsTArray<IPCDataTransferItem>& items = aDataTransfer.items();
7193 for (const auto& item : items) {
7194 aTransferable->AddDataFlavor(item.flavor().get());
7196 if (item.data().type() == IPCDataTransferData::TnsString) {
7197 nsCOMPtr<nsISupportsString> dataWrapper =
7198 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
7199 NS_ENSURE_SUCCESS(rv, rv);
7201 const nsString& text = item.data().get_nsString();
7202 rv = dataWrapper->SetData(text);
7203 NS_ENSURE_SUCCESS(rv, rv);
7205 rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper);
7207 NS_ENSURE_SUCCESS(rv, rv);
7208 } else if (item.data().type() == IPCDataTransferData::TShmem) {
7209 if (nsContentUtils::IsFlavorImage(item.flavor())) {
7210 nsCOMPtr<imgIContainer> imageContainer;
7211 rv = nsContentUtils::DataTransferItemToImage(
7212 item, getter_AddRefs(imageContainer));
7213 NS_ENSURE_SUCCESS(rv, rv);
7215 aTransferable->SetTransferData(item.flavor().get(), imageContainer);
7216 } else {
7217 nsCOMPtr<nsISupportsCString> dataWrapper =
7218 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
7219 NS_ENSURE_SUCCESS(rv, rv);
7221 // The buffer contains the terminating null.
7222 Shmem itemData = item.data().get_Shmem();
7223 const nsDependentCSubstring text(itemData.get<char>(),
7224 itemData.Size<char>());
7225 rv = dataWrapper->SetData(text);
7226 NS_ENSURE_SUCCESS(rv, rv);
7228 rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper);
7230 NS_ENSURE_SUCCESS(rv, rv);
7233 if (aContentParent) {
7234 Unused << aContentParent->DeallocShmem(item.data().get_Shmem());
7235 } else if (aBrowserChild) {
7236 Unused << aBrowserChild->DeallocShmem(item.data().get_Shmem());
7241 aTransferable->SetRequestingPrincipal(aRequestingPrincipal);
7242 aTransferable->SetContentPolicyType(aContentPolicyType);
7243 return NS_OK;
7246 void nsContentUtils::TransferablesToIPCTransferables(
7247 nsIArray* aTransferables, nsTArray<IPCDataTransfer>& aIPC,
7248 bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
7249 mozilla::dom::ContentParent* aParent) {
7250 aIPC.Clear();
7251 if (aTransferables) {
7252 uint32_t transferableCount = 0;
7253 aTransferables->GetLength(&transferableCount);
7254 for (uint32_t i = 0; i < transferableCount; ++i) {
7255 IPCDataTransfer* dt = aIPC.AppendElement();
7256 nsCOMPtr<nsITransferable> transferable =
7257 do_QueryElementAt(aTransferables, i);
7258 TransferableToIPCTransferable(transferable, dt, aInSyncMessage, aChild,
7259 aParent);
7264 nsresult nsContentUtils::SlurpFileToString(nsIFile* aFile,
7265 nsACString& aString) {
7266 aString.Truncate();
7268 nsCOMPtr<nsIURI> fileURI;
7269 nsresult rv = NS_NewFileURI(getter_AddRefs(fileURI), aFile);
7270 if (NS_FAILED(rv)) {
7271 return rv;
7274 nsCOMPtr<nsIChannel> channel;
7275 rv = NS_NewChannel(getter_AddRefs(channel), fileURI,
7276 nsContentUtils::GetSystemPrincipal(),
7277 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
7278 nsIContentPolicy::TYPE_OTHER);
7279 if (NS_FAILED(rv)) {
7280 return rv;
7283 nsCOMPtr<nsIInputStream> stream;
7284 rv = channel->Open(getter_AddRefs(stream));
7285 if (NS_FAILED(rv)) {
7286 return rv;
7289 rv = NS_ConsumeStream(stream, UINT32_MAX, aString);
7290 if (NS_FAILED(rv)) {
7291 return rv;
7294 rv = stream->Close();
7295 if (NS_FAILED(rv)) {
7296 return rv;
7299 return NS_OK;
7302 bool nsContentUtils::IsFileImage(nsIFile* aFile, nsACString& aType) {
7303 nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1");
7304 if (!mime) {
7305 return false;
7308 nsresult rv = mime->GetTypeFromFile(aFile, aType);
7309 if (NS_FAILED(rv)) {
7310 return false;
7313 return StringBeginsWith(aType, "image/"_ns);
7316 nsresult nsContentUtils::CalculateBufferSizeForImage(
7317 const uint32_t& aStride, const IntSize& aImageSize,
7318 const SurfaceFormat& aFormat, size_t* aMaxBufferSize,
7319 size_t* aUsedBufferSize) {
7320 CheckedInt32 requiredBytes =
7321 CheckedInt32(aStride) * CheckedInt32(aImageSize.height);
7323 CheckedInt32 usedBytes =
7324 requiredBytes - aStride +
7325 (CheckedInt32(aImageSize.width) * BytesPerPixel(aFormat));
7326 if (!usedBytes.isValid()) {
7327 return NS_ERROR_FAILURE;
7330 MOZ_ASSERT(requiredBytes.isValid(), "usedBytes valid but not required?");
7331 *aMaxBufferSize = requiredBytes.value();
7332 *aUsedBufferSize = usedBytes.value();
7333 return NS_OK;
7336 nsresult nsContentUtils::DataTransferItemToImage(
7337 const IPCDataTransferItem& aItem, imgIContainer** aContainer) {
7338 MOZ_ASSERT(aItem.data().type() == IPCDataTransferData::TShmem);
7339 MOZ_ASSERT(IsFlavorImage(aItem.flavor()));
7341 const IPCDataTransferImage& imageDetails = aItem.imageDetails();
7342 const IntSize size(imageDetails.width(), imageDetails.height());
7343 if (!size.width || !size.height) {
7344 return NS_ERROR_FAILURE;
7347 Shmem data = aItem.data().get_Shmem();
7349 // Validate shared memory buffer size
7350 size_t imageBufLen = 0;
7351 size_t maxBufLen = 0;
7352 nsresult rv = CalculateBufferSizeForImage(imageDetails.stride(), size,
7353 imageDetails.format(), &maxBufLen,
7354 &imageBufLen);
7355 if (NS_FAILED(rv)) {
7356 return rv;
7358 if (imageBufLen > data.Size<uint8_t>()) {
7359 return NS_ERROR_FAILURE;
7362 RefPtr<DataSourceSurface> image = CreateDataSourceSurfaceFromData(
7363 size, imageDetails.format(), data.get<uint8_t>(), imageDetails.stride());
7365 RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size);
7366 nsCOMPtr<imgIContainer> imageContainer =
7367 image::ImageOps::CreateFromDrawable(drawable);
7368 imageContainer.forget(aContainer);
7370 return NS_OK;
7373 bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
7374 return aFlavor.EqualsLiteral(kNativeImageMime) ||
7375 aFlavor.EqualsLiteral(kJPEGImageMime) ||
7376 aFlavor.EqualsLiteral(kJPGImageMime) ||
7377 aFlavor.EqualsLiteral(kPNGImageMime) ||
7378 aFlavor.EqualsLiteral(kGIFImageMime);
7381 static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
7382 mozilla::dom::ContentParent* aParent,
7383 const nsACString& aInput) {
7384 MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
7386 IShmemAllocator* allocator = aChild ? static_cast<IShmemAllocator*>(aChild)
7387 : static_cast<IShmemAllocator*>(aParent);
7389 Shmem result;
7390 if (!allocator->AllocShmem(aInput.Length(), SharedMemory::TYPE_BASIC,
7391 &result)) {
7392 return result;
7395 memcpy(result.get<char>(), aInput.BeginReading(), aInput.Length());
7397 return result;
7400 void nsContentUtils::TransferableToIPCTransferable(
7401 nsITransferable* aTransferable, IPCDataTransfer* aIPCDataTransfer,
7402 bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
7403 mozilla::dom::ContentParent* aParent) {
7404 MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
7406 if (aTransferable) {
7407 nsTArray<nsCString> flavorList;
7408 aTransferable->FlavorsTransferableCanExport(flavorList);
7410 for (uint32_t j = 0; j < flavorList.Length(); ++j) {
7411 nsCString& flavorStr = flavorList[j];
7412 if (!flavorStr.Length()) {
7413 continue;
7416 nsCOMPtr<nsISupports> data;
7417 nsresult rv =
7418 aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data));
7420 if (NS_FAILED(rv) || !data) {
7421 if (aInSyncMessage) {
7422 // Can't do anything.
7423 continue;
7426 // This is a hack to support kFilePromiseMime.
7427 // On Windows there just needs to be an entry for it,
7428 // and for OSX we need to create
7429 // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
7430 if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
7431 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7432 item->flavor() = flavorStr;
7433 item->data() = NS_ConvertUTF8toUTF16(flavorStr);
7434 continue;
7437 // Empty element, transfer only the flavor
7438 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7439 item->flavor() = flavorStr;
7440 item->data() = nsString();
7441 continue;
7444 if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(data)) {
7445 nsAutoString dataAsString;
7446 text->GetData(dataAsString);
7447 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7448 item->flavor() = flavorStr;
7449 item->data() = dataAsString;
7450 } else if (nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data)) {
7451 nsAutoCString dataAsString;
7452 ctext->GetData(dataAsString);
7454 Shmem dataAsShmem = ConvertToShmem(aChild, aParent, dataAsString);
7455 if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
7456 continue;
7459 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7460 item->flavor() = flavorStr;
7461 item->data() = std::move(dataAsShmem);
7462 } else if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
7463 // Images to be pasted on the clipboard are nsIInputStreams
7464 nsCString imageData;
7465 NS_ConsumeStream(stream, UINT32_MAX, imageData);
7467 Shmem imageDataShmem = ConvertToShmem(aChild, aParent, imageData);
7468 if (!imageDataShmem.IsReadable() || !imageDataShmem.Size<char>()) {
7469 continue;
7472 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7473 item->flavor() = flavorStr;
7474 item->data() = std::move(imageDataShmem);
7475 } else if (nsCOMPtr<imgIContainer> image = do_QueryInterface(data)) {
7476 // Images to be placed on the clipboard are imgIContainers.
7477 RefPtr<mozilla::gfx::SourceSurface> surface = image->GetFrame(
7478 imgIContainer::FRAME_CURRENT,
7479 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
7480 if (!surface) {
7481 continue;
7483 RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
7484 surface->GetDataSurface();
7485 if (!dataSurface) {
7486 continue;
7488 size_t length;
7489 int32_t stride;
7490 IShmemAllocator* allocator =
7491 aChild ? static_cast<IShmemAllocator*>(aChild)
7492 : static_cast<IShmemAllocator*>(aParent);
7493 Maybe<Shmem> surfaceData =
7494 GetSurfaceData(dataSurface, &length, &stride, allocator);
7496 if (surfaceData.isNothing()) {
7497 continue;
7500 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7501 item->flavor() = flavorStr;
7502 // Turn item->data() into an nsCString prior to accessing it.
7503 item->data() = std::move(surfaceData.ref());
7505 IPCDataTransferImage& imageDetails = item->imageDetails();
7506 mozilla::gfx::IntSize size = dataSurface->GetSize();
7507 imageDetails.width() = size.width;
7508 imageDetails.height() = size.height;
7509 imageDetails.stride() = stride;
7510 imageDetails.format() = dataSurface->GetFormat();
7511 } else {
7512 // Otherwise, handle this as a file.
7513 nsCOMPtr<BlobImpl> blobImpl;
7514 if (nsCOMPtr<nsIFile> file = do_QueryInterface(data)) {
7515 // If we can send this over as a blob, do so. Otherwise, we're
7516 // responding to a sync message and the child can't process the blob
7517 // constructor before processing our response, which would crash. In
7518 // that case, hope that the caller is nsClipboardProxy::GetData,
7519 // called from editor and send over images as raw data.
7520 if (aInSyncMessage) {
7521 nsAutoCString type;
7522 if (IsFileImage(file, type)) {
7523 nsAutoCString data;
7524 SlurpFileToString(file, data);
7526 Shmem dataAsShmem = ConvertToShmem(aChild, aParent, data);
7527 if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
7528 continue;
7531 IPCDataTransferItem* item =
7532 aIPCDataTransfer->items().AppendElement();
7533 item->flavor() = type;
7534 item->data() = std::move(dataAsShmem);
7537 continue;
7540 if (aParent) {
7541 bool isDir = false;
7542 if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
7543 nsAutoString path;
7544 if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
7545 continue;
7548 RefPtr<FileSystemSecurity> fss =
7549 FileSystemSecurity::GetOrCreate();
7550 fss->GrantAccessToContentProcess(aParent->ChildID(), path);
7554 blobImpl = new FileBlobImpl(file);
7556 IgnoredErrorResult rv;
7558 // Ensure that file data is cached no that the content process
7559 // has this data available to it when passed over:
7560 blobImpl->GetSize(rv);
7561 if (NS_WARN_IF(rv.Failed())) {
7562 continue;
7565 blobImpl->GetLastModified(rv);
7566 if (NS_WARN_IF(rv.Failed())) {
7567 continue;
7569 } else {
7570 if (aInSyncMessage) {
7571 // Can't do anything.
7572 continue;
7574 blobImpl = do_QueryInterface(data);
7576 if (blobImpl) {
7577 IPCDataTransferData data;
7578 IPCBlob ipcBlob;
7580 // If we failed to create the blob actor, then this blob probably
7581 // can't get the file size for the underlying file, ignore it for
7582 // now. TODO pass this through anyway.
7583 if (aChild) {
7584 nsresult rv = IPCBlobUtils::Serialize(blobImpl, aChild, ipcBlob);
7585 if (NS_WARN_IF(NS_FAILED(rv))) {
7586 continue;
7589 data = ipcBlob;
7590 } else if (aParent) {
7591 nsresult rv = IPCBlobUtils::Serialize(blobImpl, aParent, ipcBlob);
7592 if (NS_WARN_IF(NS_FAILED(rv))) {
7593 continue;
7596 data = ipcBlob;
7599 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7600 item->flavor() = flavorStr;
7601 item->data() = data;
7608 namespace {
7609 // The default type used for calling GetSurfaceData(). Gets surface data as
7610 // raw buffer.
7611 struct GetSurfaceDataRawBuffer {
7612 using ReturnType = mozilla::UniquePtr<char[]>;
7613 using BufferType = char*;
7615 ReturnType Allocate(size_t aSize) { return ReturnType(new char[aSize]); }
7617 static BufferType GetBuffer(const ReturnType& aReturnValue) {
7618 return aReturnValue.get();
7621 static ReturnType NullValue() { return ReturnType(); }
7624 // The type used for calling GetSurfaceData() that allocates and writes to
7625 // a shared memory buffer.
7626 struct GetSurfaceDataShmem {
7627 using ReturnType = Maybe<Shmem>;
7628 using BufferType = char*;
7630 explicit GetSurfaceDataShmem(IShmemAllocator* aAllocator)
7631 : mAllocator(aAllocator) {}
7633 ReturnType Allocate(size_t aSize) {
7634 Shmem shmem;
7635 if (!mAllocator->AllocShmem(aSize, SharedMemory::TYPE_BASIC, &shmem)) {
7636 return Nothing();
7639 return Some(shmem);
7642 static BufferType GetBuffer(const ReturnType& aReturnValue) {
7643 return aReturnValue.isSome() ? aReturnValue.ref().get<char>() : nullptr;
7646 static ReturnType NullValue() { return ReturnType(); }
7648 private:
7649 IShmemAllocator* mAllocator;
7653 * Get the pixel data from the given source surface and return it as a buffer.
7654 * The length and stride will be assigned from the surface.
7656 template <typename GetSurfaceDataContext = GetSurfaceDataRawBuffer>
7657 typename GetSurfaceDataContext::ReturnType GetSurfaceDataImpl(
7658 mozilla::gfx::DataSourceSurface* aSurface, size_t* aLength,
7659 int32_t* aStride,
7660 GetSurfaceDataContext aContext = GetSurfaceDataContext()) {
7661 mozilla::gfx::DataSourceSurface::MappedSurface map;
7662 if (!aSurface->Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map)) {
7663 return GetSurfaceDataContext::NullValue();
7666 size_t bufLen = 0;
7667 size_t maxBufLen = 0;
7668 nsresult rv = nsContentUtils::CalculateBufferSizeForImage(
7669 map.mStride, aSurface->GetSize(), aSurface->GetFormat(), &maxBufLen,
7670 &bufLen);
7671 if (NS_FAILED(rv)) {
7672 aSurface->Unmap();
7673 return GetSurfaceDataContext::NullValue();
7676 // nsDependentCString wants null-terminated string.
7677 typename GetSurfaceDataContext::ReturnType surfaceData =
7678 aContext.Allocate(maxBufLen + 1);
7679 if (GetSurfaceDataContext::GetBuffer(surfaceData)) {
7680 memcpy(GetSurfaceDataContext::GetBuffer(surfaceData),
7681 reinterpret_cast<char*>(map.mData), bufLen);
7682 memset(GetSurfaceDataContext::GetBuffer(surfaceData) + bufLen, 0,
7683 maxBufLen - bufLen + 1);
7686 *aLength = maxBufLen;
7687 *aStride = map.mStride;
7689 aSurface->Unmap();
7690 return surfaceData;
7692 } // Anonymous namespace.
7694 mozilla::UniquePtr<char[]> nsContentUtils::GetSurfaceData(
7695 NotNull<mozilla::gfx::DataSourceSurface*> aSurface, size_t* aLength,
7696 int32_t* aStride) {
7697 return GetSurfaceDataImpl(aSurface, aLength, aStride);
7700 Maybe<Shmem> nsContentUtils::GetSurfaceData(
7701 mozilla::gfx::DataSourceSurface* aSurface, size_t* aLength,
7702 int32_t* aStride, IShmemAllocator* aAllocator) {
7703 return GetSurfaceDataImpl(aSurface, aLength, aStride,
7704 GetSurfaceDataShmem(aAllocator));
7707 mozilla::Modifiers nsContentUtils::GetWidgetModifiers(int32_t aModifiers) {
7708 Modifiers result = 0;
7709 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SHIFT) {
7710 result |= mozilla::MODIFIER_SHIFT;
7712 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CONTROL) {
7713 result |= mozilla::MODIFIER_CONTROL;
7715 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALT) {
7716 result |= mozilla::MODIFIER_ALT;
7718 if (aModifiers & nsIDOMWindowUtils::MODIFIER_META) {
7719 result |= mozilla::MODIFIER_META;
7721 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALTGRAPH) {
7722 result |= mozilla::MODIFIER_ALTGRAPH;
7724 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CAPSLOCK) {
7725 result |= mozilla::MODIFIER_CAPSLOCK;
7727 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FN) {
7728 result |= mozilla::MODIFIER_FN;
7730 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FNLOCK) {
7731 result |= mozilla::MODIFIER_FNLOCK;
7733 if (aModifiers & nsIDOMWindowUtils::MODIFIER_NUMLOCK) {
7734 result |= mozilla::MODIFIER_NUMLOCK;
7736 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SCROLLLOCK) {
7737 result |= mozilla::MODIFIER_SCROLLLOCK;
7739 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOL) {
7740 result |= mozilla::MODIFIER_SYMBOL;
7742 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK) {
7743 result |= mozilla::MODIFIER_SYMBOLLOCK;
7745 if (aModifiers & nsIDOMWindowUtils::MODIFIER_OS) {
7746 result |= mozilla::MODIFIER_OS;
7748 return result;
7751 nsIWidget* nsContentUtils::GetWidget(PresShell* aPresShell, nsPoint* aOffset) {
7752 if (!aPresShell) {
7753 return nullptr;
7755 nsIFrame* frame = aPresShell->GetRootFrame();
7756 if (!frame) {
7757 return nullptr;
7759 return frame->GetView()->GetNearestWidget(aOffset);
7762 int16_t nsContentUtils::GetButtonsFlagForButton(int32_t aButton) {
7763 switch (aButton) {
7764 case -1:
7765 return MouseButtonsFlag::eNoButtons;
7766 case MouseButton::ePrimary:
7767 return MouseButtonsFlag::ePrimaryFlag;
7768 case MouseButton::eMiddle:
7769 return MouseButtonsFlag::eMiddleFlag;
7770 case MouseButton::eSecondary:
7771 return MouseButtonsFlag::eSecondaryFlag;
7772 case 4:
7773 return MouseButtonsFlag::e4thFlag;
7774 case 5:
7775 return MouseButtonsFlag::e5thFlag;
7776 default:
7777 NS_ERROR("Button not known.");
7778 return 0;
7782 LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(
7783 const CSSPoint& aPoint, const nsPoint& aOffset,
7784 nsPresContext* aPresContext) {
7785 nsPoint layoutRelative = CSSPoint::ToAppUnits(aPoint) + aOffset;
7786 nsPoint visualRelative =
7787 ViewportUtils::LayoutToVisual(layoutRelative, aPresContext->PresShell());
7788 return LayoutDeviceIntPoint::FromAppUnitsRounded(
7789 visualRelative, aPresContext->AppUnitsPerDevPixel());
7792 nsView* nsContentUtils::GetViewToDispatchEvent(nsPresContext* aPresContext,
7793 PresShell** aPresShell) {
7794 if (!aPresContext || !aPresShell) {
7795 return nullptr;
7797 RefPtr<PresShell> presShell = aPresContext->PresShell();
7798 if (NS_WARN_IF(!presShell)) {
7799 *aPresShell = nullptr;
7800 return nullptr;
7802 nsViewManager* viewManager = presShell->GetViewManager();
7803 if (!viewManager) {
7804 presShell.forget(aPresShell); // XXX Is this intentional?
7805 return nullptr;
7807 presShell.forget(aPresShell);
7808 return viewManager->GetRootView();
7811 nsresult nsContentUtils::SendMouseEvent(
7812 mozilla::PresShell* aPresShell, const nsAString& aType, float aX, float aY,
7813 int32_t aButton, int32_t aButtons, int32_t aClickCount, int32_t aModifiers,
7814 bool aIgnoreRootScrollFrame, float aPressure,
7815 unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
7816 bool* aPreventDefault, bool aIsDOMEventSynthesized,
7817 bool aIsWidgetEventSynthesized) {
7818 nsPoint offset;
7819 nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
7820 if (!widget) return NS_ERROR_FAILURE;
7822 EventMessage msg;
7823 WidgetMouseEvent::ExitFrom exitFrom = WidgetMouseEvent::eChild;
7824 bool contextMenuKey = false;
7825 if (aType.EqualsLiteral("mousedown")) {
7826 msg = eMouseDown;
7827 } else if (aType.EqualsLiteral("mouseup")) {
7828 msg = eMouseUp;
7829 } else if (aType.EqualsLiteral("mousemove")) {
7830 msg = eMouseMove;
7831 } else if (aType.EqualsLiteral("mouseover")) {
7832 msg = eMouseEnterIntoWidget;
7833 } else if (aType.EqualsLiteral("mouseout")) {
7834 msg = eMouseExitFromWidget;
7835 } else if (aType.EqualsLiteral("mousecancel")) {
7836 msg = eMouseExitFromWidget;
7837 exitFrom = WidgetMouseEvent::eTopLevel;
7838 } else if (aType.EqualsLiteral("mouselongtap")) {
7839 msg = eMouseLongTap;
7840 } else if (aType.EqualsLiteral("contextmenu")) {
7841 msg = eContextMenu;
7842 contextMenuKey = (aButton == 0);
7843 } else if (aType.EqualsLiteral("MozMouseHittest")) {
7844 msg = eMouseHitTest;
7845 } else {
7846 return NS_ERROR_FAILURE;
7849 if (aInputSourceArg == MouseEvent_Binding::MOZ_SOURCE_UNKNOWN) {
7850 aInputSourceArg = MouseEvent_Binding::MOZ_SOURCE_MOUSE;
7853 WidgetMouseEvent event(true, msg, widget,
7854 aIsWidgetEventSynthesized
7855 ? WidgetMouseEvent::eSynthesized
7856 : WidgetMouseEvent::eReal,
7857 contextMenuKey ? WidgetMouseEvent::eContextMenuKey
7858 : WidgetMouseEvent::eNormal);
7859 event.pointerId = aIdentifier;
7860 event.mModifiers = GetWidgetModifiers(aModifiers);
7861 event.mButton = aButton;
7862 event.mButtons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED
7863 ? aButtons
7864 : msg == eMouseUp ? 0 : GetButtonsFlagForButton(aButton);
7865 event.mPressure = aPressure;
7866 event.mInputSource = aInputSourceArg;
7867 event.mClickCount = aClickCount;
7868 event.mTime = PR_IntervalNow();
7869 event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
7870 event.mExitFrom = exitFrom;
7872 nsPresContext* presContext = aPresShell->GetPresContext();
7873 if (!presContext) return NS_ERROR_FAILURE;
7875 event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
7876 event.mIgnoreRootScrollFrame = aIgnoreRootScrollFrame;
7878 nsEventStatus status = nsEventStatus_eIgnore;
7879 if (aToWindow) {
7880 RefPtr<PresShell> presShell;
7881 nsView* view =
7882 GetViewToDispatchEvent(presContext, getter_AddRefs(presShell));
7883 if (!presShell || !view) {
7884 return NS_ERROR_FAILURE;
7886 return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
7888 if (StaticPrefs::test_events_async_enabled()) {
7889 status = widget->DispatchInputEvent(&event);
7890 } else {
7891 nsresult rv = widget->DispatchEvent(&event, status);
7892 NS_ENSURE_SUCCESS(rv, rv);
7894 if (aPreventDefault) {
7895 *aPreventDefault = (status == nsEventStatus_eConsumeNoDefault);
7898 return NS_OK;
7901 /* static */
7902 void nsContentUtils::FirePageHideEventForFrameLoaderSwap(
7903 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
7904 bool aOnlySystemGroup) {
7905 MOZ_DIAGNOSTIC_ASSERT(aItem);
7906 MOZ_DIAGNOSTIC_ASSERT(aChromeEventHandler);
7908 RefPtr<Document> doc = aItem->GetDocument();
7909 NS_ASSERTION(doc, "What happened here?");
7910 doc->OnPageHide(true, aChromeEventHandler, aOnlySystemGroup);
7912 int32_t childCount = 0;
7913 aItem->GetInProcessChildCount(&childCount);
7914 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
7915 kids.AppendElements(childCount);
7916 for (int32_t i = 0; i < childCount; ++i) {
7917 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
7920 for (uint32_t i = 0; i < kids.Length(); ++i) {
7921 if (kids[i]) {
7922 FirePageHideEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
7923 aOnlySystemGroup);
7928 // The pageshow event is fired for a given document only if IsShowing() returns
7929 // the same thing as aFireIfShowing. This gives us a way to fire pageshow only
7930 // on documents that are still loading or only on documents that are already
7931 // loaded.
7932 /* static */
7933 void nsContentUtils::FirePageShowEventForFrameLoaderSwap(
7934 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
7935 bool aFireIfShowing, bool aOnlySystemGroup) {
7936 int32_t childCount = 0;
7937 aItem->GetInProcessChildCount(&childCount);
7938 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
7939 kids.AppendElements(childCount);
7940 for (int32_t i = 0; i < childCount; ++i) {
7941 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
7944 for (uint32_t i = 0; i < kids.Length(); ++i) {
7945 if (kids[i]) {
7946 FirePageShowEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
7947 aFireIfShowing, aOnlySystemGroup);
7951 RefPtr<Document> doc = aItem->GetDocument();
7952 NS_ASSERTION(doc, "What happened here?");
7953 if (doc->IsShowing() == aFireIfShowing) {
7954 doc->OnPageShow(true, aChromeEventHandler, aOnlySystemGroup);
7958 /* static */
7959 already_AddRefed<nsPIWindowRoot> nsContentUtils::GetWindowRoot(Document* aDoc) {
7960 if (aDoc) {
7961 if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
7962 return win->GetTopWindowRoot();
7965 return nullptr;
7968 /* static */
7969 bool nsContentUtils::IsPreloadType(nsContentPolicyType aType) {
7970 return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
7971 aType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD ||
7972 aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
7973 aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD ||
7974 aType == nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD ||
7975 aType == nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD);
7978 /* static */
7979 bool nsContentUtils::IsUpgradableDisplayType(nsContentPolicyType aType) {
7980 MOZ_ASSERT(NS_IsMainThread());
7981 return (aType == nsIContentPolicy::TYPE_IMAGE ||
7982 aType == nsIContentPolicy::TYPE_MEDIA);
7985 // static
7986 ReferrerPolicy nsContentUtils::GetReferrerPolicyFromChannel(
7987 nsIChannel* aChannel) {
7988 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
7989 if (!httpChannel) {
7990 return ReferrerPolicy::_empty;
7993 nsresult rv;
7994 nsAutoCString headerValue;
7995 rv = httpChannel->GetResponseHeader("referrer-policy"_ns, headerValue);
7996 if (NS_FAILED(rv) || headerValue.IsEmpty()) {
7997 return ReferrerPolicy::_empty;
8000 return ReferrerInfo::ReferrerPolicyFromHeaderString(
8001 NS_ConvertUTF8toUTF16(headerValue));
8004 // static
8005 bool nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel) {
8006 nsLoadFlags loadFlags = 0;
8007 aChannel->GetLoadFlags(&loadFlags);
8008 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
8009 return true;
8012 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
8013 nsContentPolicyType type = loadInfo->InternalContentPolicyType();
8014 return IsNonSubresourceInternalPolicyType(type);
8017 // static
8018 bool nsContentUtils::IsNonSubresourceInternalPolicyType(
8019 nsContentPolicyType aType) {
8020 return aType == nsIContentPolicy::TYPE_DOCUMENT ||
8021 aType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
8022 aType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
8023 aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
8024 aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
8027 // static public
8028 bool nsContentUtils::IsThirdPartyWindowOrChannel(nsPIDOMWindowInner* aWindow,
8029 nsIChannel* aChannel,
8030 nsIURI* aURI) {
8031 MOZ_ASSERT(!aWindow || !aChannel,
8032 "A window and channel should not both be provided.");
8034 ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
8035 if (!thirdPartyUtil) {
8036 return false;
8039 // In the absence of a window or channel, we assume that we are first-party.
8040 bool thirdParty = false;
8042 if (aWindow) {
8043 nsresult rv = thirdPartyUtil->IsThirdPartyWindow(aWindow->GetOuterWindow(),
8044 aURI, &thirdParty);
8045 if (NS_FAILED(rv)) {
8046 // Ideally we would do something similar to the channel code path here,
8047 // but existing code depends on this behaviour.
8048 return false;
8052 if (aChannel) {
8053 // Note, we must call IsThirdPartyChannel() here and not just try to
8054 // use nsILoadInfo.isThirdPartyContext. That nsILoadInfo property only
8055 // indicates if the parent loading window is third party or not. We
8056 // want to check the channel URI against the loading principal as well.
8057 nsresult rv =
8058 thirdPartyUtil->IsThirdPartyChannel(aChannel, nullptr, &thirdParty);
8059 if (NS_FAILED(rv)) {
8060 // Assume third-party in case of failure
8061 thirdParty = true;
8064 // We check isThirdPartyWindow to expand the list of domains that are
8065 // considered first party (e.g., if facebook.com includes an iframe from
8066 // fatratgames.com, all subsources included in that iframe are considered
8067 // third-party with isThirdPartyChannel, even if they are not third-party
8068 // w.r.t. facebook.com), and isThirdPartyChannel to prevent top-level
8069 // navigations from being detected as third-party.
8070 bool isThirdPartyWindow = true;
8071 nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
8072 if (NS_SUCCEEDED(rv) && chan) {
8073 nsCOMPtr<nsIURI> topWinURI;
8074 rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
8075 if (NS_SUCCEEDED(rv) && topWinURI) {
8076 rv = thirdPartyUtil->IsThirdPartyURI(aURI, topWinURI,
8077 &isThirdPartyWindow);
8078 if (NS_SUCCEEDED(rv)) {
8079 thirdParty = thirdParty && isThirdPartyWindow;
8085 return thirdParty;
8088 // static public
8089 bool nsContentUtils::IsThirdPartyTrackingResourceWindow(
8090 nsPIDOMWindowInner* aWindow) {
8091 MOZ_ASSERT(aWindow);
8093 Document* document = aWindow->GetExtantDoc();
8094 if (!document) {
8095 return false;
8098 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8099 do_QueryInterface(document->GetChannel());
8100 if (!classifiedChannel) {
8101 return false;
8104 return classifiedChannel->IsThirdPartyTrackingResource();
8107 // static public
8108 bool nsContentUtils::IsFirstPartyTrackingResourceWindow(
8109 nsPIDOMWindowInner* aWindow) {
8110 MOZ_ASSERT(aWindow);
8112 Document* document = aWindow->GetExtantDoc();
8113 if (!document) {
8114 return false;
8117 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8118 do_QueryInterface(document->GetChannel());
8119 if (!classifiedChannel) {
8120 return false;
8123 uint32_t classificationFlags =
8124 classifiedChannel->GetFirstPartyClassificationFlags();
8126 return mozilla::net::UrlClassifierCommon::IsTrackingClassificationFlag(
8127 classificationFlags);
8130 namespace {
8132 // We put StringBuilder in the anonymous namespace to prevent anything outside
8133 // this file from accidentally being linked against it.
8134 class BulkAppender {
8135 typedef typename nsAString::size_type size_type;
8137 public:
8138 explicit BulkAppender(BulkWriteHandle<char16_t>&& aHandle)
8139 : mHandle(std::move(aHandle)), mPosition(0) {}
8140 ~BulkAppender() = default;
8142 template <int N>
8143 void AppendLiteral(const char16_t (&aStr)[N]) {
8144 size_t len = N - 1;
8145 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8146 memcpy(mHandle.Elements() + mPosition, aStr, len * sizeof(char16_t));
8147 mPosition += len;
8150 void Append(Span<const char16_t> aStr) {
8151 size_t len = aStr.Length();
8152 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8153 // Both mHandle.Elements() and aStr.Elements() are guaranteed
8154 // to be non-null (by the string implementation and by Span,
8155 // respectively), so not checking the pointers for null before
8156 // memcpy does not lead to UB even if len was zero.
8157 memcpy(mHandle.Elements() + mPosition, aStr.Elements(),
8158 len * sizeof(char16_t));
8159 mPosition += len;
8162 void Append(Span<const char> aStr) {
8163 size_t len = aStr.Length();
8164 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8165 ConvertLatin1toUtf16(aStr, mHandle.AsSpan().From(mPosition));
8166 mPosition += len;
8169 void Finish() { mHandle.Finish(mPosition, false); }
8171 private:
8172 mozilla::BulkWriteHandle<char16_t> mHandle;
8173 size_type mPosition;
8176 class StringBuilder {
8177 private:
8178 // Try to keep the size of StringBuilder close to a jemalloc bucket size.
8179 static const uint32_t STRING_BUFFER_UNITS = 1020;
8180 class Unit {
8181 public:
8182 Unit() : mAtom(nullptr), mType(eUnknown), mLength(0) {
8183 MOZ_COUNT_CTOR(StringBuilder::Unit);
8185 ~Unit() {
8186 if (mType == eString || mType == eStringWithEncode) {
8187 delete mString;
8189 MOZ_COUNT_DTOR(StringBuilder::Unit);
8192 enum Type {
8193 eUnknown,
8194 eAtom,
8195 eString,
8196 eStringWithEncode,
8197 eLiteral,
8198 eTextFragment,
8199 eTextFragmentWithEncode,
8202 union {
8203 nsAtom* mAtom;
8204 const char16_t* mLiteral;
8205 nsAutoString* mString;
8206 const nsTextFragment* mTextFragment;
8208 Type mType;
8209 uint32_t mLength;
8212 public:
8213 StringBuilder() : mLast(this), mLength(0) { MOZ_COUNT_CTOR(StringBuilder); }
8215 MOZ_COUNTED_DTOR(StringBuilder)
8217 void Append(nsAtom* aAtom) {
8218 Unit* u = AddUnit();
8219 u->mAtom = aAtom;
8220 u->mType = Unit::eAtom;
8221 uint32_t len = aAtom->GetLength();
8222 u->mLength = len;
8223 mLength += len;
8226 template <int N>
8227 void Append(const char16_t (&aLiteral)[N]) {
8228 Unit* u = AddUnit();
8229 u->mLiteral = aLiteral;
8230 u->mType = Unit::eLiteral;
8231 uint32_t len = N - 1;
8232 u->mLength = len;
8233 mLength += len;
8236 void Append(const nsAString& aString) {
8237 Unit* u = AddUnit();
8238 u->mString = new nsAutoString(aString);
8239 u->mType = Unit::eString;
8240 uint32_t len = aString.Length();
8241 u->mLength = len;
8242 mLength += len;
8245 void Append(nsAutoString* aString) {
8246 Unit* u = AddUnit();
8247 u->mString = aString;
8248 u->mType = Unit::eString;
8249 uint32_t len = aString->Length();
8250 u->mLength = len;
8251 mLength += len;
8254 void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen) {
8255 Unit* u = AddUnit();
8256 u->mString = aString;
8257 u->mType = Unit::eStringWithEncode;
8258 u->mLength = aLen;
8259 mLength += aLen;
8262 void Append(const nsTextFragment* aTextFragment) {
8263 Unit* u = AddUnit();
8264 u->mTextFragment = aTextFragment;
8265 u->mType = Unit::eTextFragment;
8266 uint32_t len = aTextFragment->GetLength();
8267 u->mLength = len;
8268 mLength += len;
8271 void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen) {
8272 Unit* u = AddUnit();
8273 u->mTextFragment = aTextFragment;
8274 u->mType = Unit::eTextFragmentWithEncode;
8275 u->mLength = aLen;
8276 mLength += aLen;
8279 bool ToString(nsAString& aOut) {
8280 if (!mLength.isValid()) {
8281 return false;
8283 auto appenderOrErr = aOut.BulkWrite(mLength.value(), 0, true);
8284 if (appenderOrErr.isErr()) {
8285 return false;
8288 BulkAppender appender{appenderOrErr.unwrap()};
8290 for (StringBuilder* current = this; current;
8291 current = current->mNext.get()) {
8292 uint32_t len = current->mUnits.Length();
8293 for (uint32_t i = 0; i < len; ++i) {
8294 Unit& u = current->mUnits[i];
8295 switch (u.mType) {
8296 case Unit::eAtom:
8297 appender.Append(*(u.mAtom));
8298 break;
8299 case Unit::eString:
8300 appender.Append(*(u.mString));
8301 break;
8302 case Unit::eStringWithEncode:
8303 EncodeAttrString(*(u.mString), appender);
8304 break;
8305 case Unit::eLiteral:
8306 appender.Append(Span(u.mLiteral, u.mLength));
8307 break;
8308 case Unit::eTextFragment:
8309 if (u.mTextFragment->Is2b()) {
8310 appender.Append(
8311 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()));
8312 } else {
8313 appender.Append(
8314 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()));
8316 break;
8317 case Unit::eTextFragmentWithEncode:
8318 if (u.mTextFragment->Is2b()) {
8319 EncodeTextFragment(
8320 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()),
8321 appender);
8322 } else {
8323 EncodeTextFragment(
8324 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()),
8325 appender);
8327 break;
8328 default:
8329 MOZ_CRASH("Unknown unit type?");
8333 appender.Finish();
8334 return true;
8337 private:
8338 Unit* AddUnit() {
8339 if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
8340 new StringBuilder(this);
8342 return mLast->mUnits.AppendElement();
8345 explicit StringBuilder(StringBuilder* aFirst) : mLast(nullptr), mLength(0) {
8346 MOZ_COUNT_CTOR(StringBuilder);
8347 aFirst->mLast->mNext = WrapUnique(this);
8348 aFirst->mLast = this;
8351 void EncodeAttrString(Span<const char16_t> aStr, BulkAppender& aAppender) {
8352 size_t flushedUntil = 0;
8353 size_t currentPosition = 0;
8354 for (char16_t c : aStr) {
8355 switch (c) {
8356 case '"':
8357 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8358 aAppender.AppendLiteral(u"&quot;");
8359 flushedUntil = currentPosition + 1;
8360 break;
8361 case '&':
8362 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8363 aAppender.AppendLiteral(u"&amp;");
8364 flushedUntil = currentPosition + 1;
8365 break;
8366 case 0x00A0:
8367 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8368 aAppender.AppendLiteral(u"&nbsp;");
8369 flushedUntil = currentPosition + 1;
8370 break;
8371 default:
8372 break;
8374 currentPosition++;
8376 if (currentPosition > flushedUntil) {
8377 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8381 template <class T>
8382 void EncodeTextFragment(Span<const T> aStr, BulkAppender& aAppender) {
8383 size_t flushedUntil = 0;
8384 size_t currentPosition = 0;
8385 for (T c : aStr) {
8386 switch (c) {
8387 case '<':
8388 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8389 aAppender.AppendLiteral(u"&lt;");
8390 flushedUntil = currentPosition + 1;
8391 break;
8392 case '>':
8393 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8394 aAppender.AppendLiteral(u"&gt;");
8395 flushedUntil = currentPosition + 1;
8396 break;
8397 case '&':
8398 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8399 aAppender.AppendLiteral(u"&amp;");
8400 flushedUntil = currentPosition + 1;
8401 break;
8402 case T(0xA0):
8403 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8404 aAppender.AppendLiteral(u"&nbsp;");
8405 flushedUntil = currentPosition + 1;
8406 break;
8407 default:
8408 break;
8410 currentPosition++;
8412 if (currentPosition > flushedUntil) {
8413 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8417 AutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
8418 mozilla::UniquePtr<StringBuilder> mNext;
8419 StringBuilder* mLast;
8420 // mLength is used only in the first StringBuilder object in the linked list.
8421 CheckedInt<uint32_t> mLength;
8424 } // namespace
8426 static void AppendEncodedCharacters(const nsTextFragment* aText,
8427 StringBuilder& aBuilder) {
8428 uint32_t extraSpaceNeeded = 0;
8429 uint32_t len = aText->GetLength();
8430 if (aText->Is2b()) {
8431 const char16_t* data = aText->Get2b();
8432 for (uint32_t i = 0; i < len; ++i) {
8433 const char16_t c = data[i];
8434 switch (c) {
8435 case '<':
8436 extraSpaceNeeded += ArrayLength("&lt;") - 2;
8437 break;
8438 case '>':
8439 extraSpaceNeeded += ArrayLength("&gt;") - 2;
8440 break;
8441 case '&':
8442 extraSpaceNeeded += ArrayLength("&amp;") - 2;
8443 break;
8444 case 0x00A0:
8445 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
8446 break;
8447 default:
8448 break;
8451 } else {
8452 const char* data = aText->Get1b();
8453 for (uint32_t i = 0; i < len; ++i) {
8454 const unsigned char c = data[i];
8455 switch (c) {
8456 case '<':
8457 extraSpaceNeeded += ArrayLength("&lt;") - 2;
8458 break;
8459 case '>':
8460 extraSpaceNeeded += ArrayLength("&gt;") - 2;
8461 break;
8462 case '&':
8463 extraSpaceNeeded += ArrayLength("&amp;") - 2;
8464 break;
8465 case 0x00A0:
8466 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
8467 break;
8468 default:
8469 break;
8474 if (extraSpaceNeeded) {
8475 aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
8476 } else {
8477 aBuilder.Append(aText);
8481 static void AppendEncodedAttributeValue(nsAutoString* aValue,
8482 StringBuilder& aBuilder) {
8483 const char16_t* c = aValue->BeginReading();
8484 const char16_t* end = aValue->EndReading();
8486 uint32_t extraSpaceNeeded = 0;
8487 while (c < end) {
8488 switch (*c) {
8489 case '"':
8490 extraSpaceNeeded += ArrayLength("&quot;") - 2;
8491 break;
8492 case '&':
8493 extraSpaceNeeded += ArrayLength("&amp;") - 2;
8494 break;
8495 case 0x00A0:
8496 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
8497 break;
8498 default:
8499 break;
8501 ++c;
8504 if (extraSpaceNeeded) {
8505 aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
8506 } else {
8507 aBuilder.Append(aValue);
8511 static void StartElement(Element* aContent, StringBuilder& aBuilder) {
8512 nsAtom* localName = aContent->NodeInfo()->NameAtom();
8513 int32_t tagNS = aContent->GetNameSpaceID();
8515 aBuilder.Append(u"<");
8516 if (aContent->IsHTMLElement() || aContent->IsSVGElement() ||
8517 aContent->IsMathMLElement()) {
8518 aBuilder.Append(localName);
8519 } else {
8520 aBuilder.Append(aContent->NodeName());
8523 CustomElementData* ceData = aContent->GetCustomElementData();
8524 if (ceData) {
8525 nsAtom* isAttr = ceData->GetIs(aContent);
8526 if (isAttr && !aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
8527 aBuilder.Append(uR"( is=")");
8528 aBuilder.Append(nsDependentAtomString(isAttr));
8529 aBuilder.Append(uR"(")");
8533 int32_t count = aContent->GetAttrCount();
8534 for (int32_t i = 0; i < count; i++) {
8535 const nsAttrName* name = aContent->GetAttrNameAt(i);
8536 int32_t attNs = name->NamespaceID();
8537 nsAtom* attName = name->LocalName();
8539 // Filter out any attribute starting with [-|_]moz
8540 nsDependentAtomString attrNameStr(attName);
8541 if (StringBeginsWith(attrNameStr, u"_moz"_ns) ||
8542 StringBeginsWith(attrNameStr, u"-moz"_ns)) {
8543 continue;
8546 auto* attValue = new nsAutoString();
8547 aContent->GetAttr(attNs, attName, *attValue);
8549 // Filter out special case of <br type="_moz*"> used by the editor.
8550 // Bug 16988. Yuck.
8551 if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
8552 attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
8553 StringBeginsWith(*attValue, u"_moz"_ns)) {
8554 delete attValue;
8555 continue;
8558 aBuilder.Append(u" ");
8560 if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
8561 (attNs == kNameSpaceID_XMLNS && attName == nsGkAtoms::xmlns)) {
8562 // Nothing else required
8563 } else if (attNs == kNameSpaceID_XML) {
8564 aBuilder.Append(u"xml:");
8565 } else if (attNs == kNameSpaceID_XMLNS) {
8566 aBuilder.Append(u"xmlns:");
8567 } else if (attNs == kNameSpaceID_XLink) {
8568 aBuilder.Append(u"xlink:");
8569 } else {
8570 nsAtom* prefix = name->GetPrefix();
8571 if (prefix) {
8572 aBuilder.Append(prefix);
8573 aBuilder.Append(u":");
8577 aBuilder.Append(attName);
8578 aBuilder.Append(uR"(=")");
8579 AppendEncodedAttributeValue(attValue, aBuilder);
8580 aBuilder.Append(uR"(")");
8583 aBuilder.Append(u">");
8586 // Per HTML spec we should append one \n if the first child of
8587 // pre/textarea/listing is a textnode and starts with a \n.
8588 // But because browsers haven't traditionally had that behavior,
8589 // we're not changing our behavior either - yet.
8590 if (aContent->IsHTMLElement()) {
8591 if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
8592 localName == nsGkAtoms::listing) {
8593 nsIContent* fc = aContent->GetFirstChild();
8594 if (fc &&
8595 (fc->NodeType() == nsINode::TEXT_NODE ||
8596 fc->NodeType() == nsINode::CDATA_SECTION_NODE)) {
8597 const nsTextFragment* text = fc->GetText();
8598 if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
8599 aBuilder.Append("\n");
8606 static inline bool ShouldEscape(nsIContent* aParent) {
8607 if (!aParent || !aParent->IsHTMLElement()) {
8608 return true;
8611 static const nsAtom* nonEscapingElements[] = {
8612 nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp, nsGkAtoms::iframe,
8613 nsGkAtoms::noembed, nsGkAtoms::noframes, nsGkAtoms::plaintext,
8614 // Per the current spec noscript should be escaped in case
8615 // scripts are disabled or if document doesn't have
8616 // browsing context. However the latter seems to be a spec bug
8617 // and Gecko hasn't traditionally done the former.
8618 nsGkAtoms::noscript};
8619 static mozilla::BloomFilter<12, nsAtom> sFilter;
8620 static bool sInitialized = false;
8621 if (!sInitialized) {
8622 sInitialized = true;
8623 for (auto& nonEscapingElement : nonEscapingElements) {
8624 sFilter.add(nonEscapingElement);
8628 nsAtom* tag = aParent->NodeInfo()->NameAtom();
8629 if (sFilter.mightContain(tag)) {
8630 for (auto& nonEscapingElement : nonEscapingElements) {
8631 if (tag == nonEscapingElement) {
8632 return false;
8636 return true;
8639 static inline bool IsVoidTag(Element* aElement) {
8640 if (!aElement->IsHTMLElement()) {
8641 return false;
8643 return FragmentOrElement::IsHTMLVoid(aElement->NodeInfo()->NameAtom());
8646 bool nsContentUtils::SerializeNodeToMarkup(nsINode* aRoot,
8647 bool aDescendentsOnly,
8648 nsAString& aOut) {
8649 // If you pass in a DOCUMENT_NODE, you must pass aDescendentsOnly as true
8650 MOZ_ASSERT(aDescendentsOnly || aRoot->NodeType() != nsINode::DOCUMENT_NODE);
8652 nsINode* current =
8653 aDescendentsOnly ? aRoot->GetFirstChildOfTemplateOrNode() : aRoot;
8655 if (!current) {
8656 return true;
8659 StringBuilder builder;
8660 nsIContent* next;
8661 while (true) {
8662 bool isVoid = false;
8663 switch (current->NodeType()) {
8664 case nsINode::ELEMENT_NODE: {
8665 Element* elem = current->AsElement();
8666 StartElement(elem, builder);
8667 isVoid = IsVoidTag(elem);
8668 if (!isVoid && (next = current->GetFirstChildOfTemplateOrNode())) {
8669 current = next;
8670 continue;
8672 break;
8675 case nsINode::TEXT_NODE:
8676 case nsINode::CDATA_SECTION_NODE: {
8677 const nsTextFragment* text = &current->AsText()->TextFragment();
8678 nsIContent* parent = current->GetParent();
8679 if (ShouldEscape(parent)) {
8680 AppendEncodedCharacters(text, builder);
8681 } else {
8682 builder.Append(text);
8684 break;
8687 case nsINode::COMMENT_NODE: {
8688 builder.Append(u"<!--");
8689 builder.Append(static_cast<nsIContent*>(current)->GetText());
8690 builder.Append(u"-->");
8691 break;
8694 case nsINode::DOCUMENT_TYPE_NODE: {
8695 builder.Append(u"<!DOCTYPE ");
8696 builder.Append(current->NodeName());
8697 builder.Append(u">");
8698 break;
8701 case nsINode::PROCESSING_INSTRUCTION_NODE: {
8702 builder.Append(u"<?");
8703 builder.Append(current->NodeName());
8704 builder.Append(u" ");
8705 builder.Append(static_cast<nsIContent*>(current)->GetText());
8706 builder.Append(u">");
8707 break;
8711 while (true) {
8712 if (!isVoid && current->NodeType() == nsINode::ELEMENT_NODE) {
8713 builder.Append(u"</");
8714 nsIContent* elem = static_cast<nsIContent*>(current);
8715 if (elem->IsHTMLElement() || elem->IsSVGElement() ||
8716 elem->IsMathMLElement()) {
8717 builder.Append(elem->NodeInfo()->NameAtom());
8718 } else {
8719 builder.Append(current->NodeName());
8721 builder.Append(u">");
8723 isVoid = false;
8725 if (current == aRoot) {
8726 return builder.ToString(aOut);
8729 if ((next = current->GetNextSibling())) {
8730 current = next;
8731 break;
8734 current = current->GetParentNode();
8736 // Handle template element. If the parent is a template's content,
8737 // then adjust the parent to be the template element.
8738 if (current != aRoot &&
8739 current->NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
8740 DocumentFragment* frag = static_cast<DocumentFragment*>(current);
8741 nsIContent* fragHost = frag->GetHost();
8742 if (fragHost && fragHost->IsTemplateElement()) {
8743 current = fragHost;
8747 if (aDescendentsOnly && current == aRoot) {
8748 return builder.ToString(aOut);
8754 bool nsContentUtils::IsSpecificAboutPage(JSObject* aGlobal, const char* aUri) {
8755 // aUri must start with about: or this isn't the right function to be using.
8756 MOZ_ASSERT(strncmp(aUri, "about:", 6) == 0);
8758 // Make sure the global is a window
8759 MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
8760 nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
8761 if (!win) {
8762 return false;
8765 nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
8766 NS_ENSURE_TRUE(principal, false);
8768 // First check the scheme to avoid getting long specs in the common case.
8769 if (!principal->SchemeIs("about")) {
8770 return false;
8773 nsAutoCString spec;
8774 principal->GetAsciiSpec(spec);
8776 return spec.EqualsASCII(aUri);
8779 /* static */
8780 void nsContentUtils::SetScrollbarsVisibility(nsIDocShell* aDocShell,
8781 bool aVisible) {
8782 if (!aDocShell) {
8783 return;
8785 auto pref = aVisible ? ScrollbarPreference::Auto : ScrollbarPreference::Never;
8786 nsDocShell::Cast(aDocShell)->SetScrollbarPreference(pref);
8789 /* static */
8790 void nsContentUtils::GetPresentationURL(nsIDocShell* aDocShell,
8791 nsAString& aPresentationUrl) {
8792 MOZ_ASSERT(aDocShell);
8794 // Simulate receiver context for web platform test
8795 if (StaticPrefs::dom_presentation_testing_simulate_receiver()) {
8796 RefPtr<Document> doc;
8798 nsCOMPtr<nsPIDOMWindowOuter> docShellWin =
8799 do_QueryInterface(aDocShell->GetScriptGlobalObject());
8800 if (docShellWin) {
8801 doc = docShellWin->GetExtantDoc();
8804 if (NS_WARN_IF(!doc)) {
8805 return;
8808 nsCOMPtr<nsIURI> uri = doc->GetDocumentURI();
8809 if (NS_WARN_IF(!uri)) {
8810 return;
8813 nsAutoCString uriStr;
8814 uri->GetSpec(uriStr);
8815 aPresentationUrl = NS_ConvertUTF8toUTF16(uriStr);
8816 return;
8819 if (XRE_IsContentProcess()) {
8820 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
8821 aDocShell->GetInProcessSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
8822 nsCOMPtr<nsIDocShellTreeItem> root;
8823 aDocShell->GetInProcessRootTreeItem(getter_AddRefs(root));
8824 if (sameTypeRoot.get() == root.get()) {
8825 // presentation URL is stored in BrowserChild for the top most
8826 // <iframe mozbrowser> in content process.
8827 BrowserChild* browserChild = BrowserChild::GetFrom(aDocShell);
8828 if (browserChild) {
8829 aPresentationUrl = browserChild->PresentationURL();
8831 return;
8835 nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(aDocShell));
8836 RefPtr<Element> topFrameElt;
8837 loadContext->GetTopFrameElement(getter_AddRefs(topFrameElt));
8838 if (!topFrameElt) {
8839 return;
8842 topFrameElt->GetAttr(nsGkAtoms::mozpresentation, aPresentationUrl);
8845 /* static */
8846 nsIDocShell* nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget) {
8847 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
8849 if (nsCOMPtr<nsINode> node = do_QueryInterface(aTarget)) {
8850 bool ignore;
8851 innerWindow =
8852 do_QueryInterface(node->OwnerDoc()->GetScriptHandlingObject(ignore));
8853 } else if ((innerWindow = do_QueryInterface(aTarget))) {
8854 // Nothing else to do
8855 } else {
8856 nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(aTarget);
8857 if (helper) {
8858 innerWindow = helper->GetOwner();
8862 if (innerWindow) {
8863 return innerWindow->GetDocShell();
8866 return nullptr;
8870 * Note: this function only relates to figuring out HTTPS state, which is an
8871 * input to the Secure Context algorithm. We are not actually implementing any
8872 * part of the Secure Context algorithm itself here.
8874 * This is a bit of a hack. Ideally we'd propagate HTTPS state through
8875 * nsIChannel as described in the Fetch and HTML specs, but making channels
8876 * know about whether they should inherit HTTPS state, propagating information
8877 * about who the channel's "client" is, exposing GetHttpsState API on channels
8878 * and modifying the various cache implementations to store and retrieve HTTPS
8879 * state involves a huge amount of code (see bug 1220687). We avoid that for
8880 * now using this function.
8882 * This function takes advantage of the observation that we can return true if
8883 * nsIContentSecurityManager::IsOriginPotentiallyTrustworthy returns true for
8884 * the document's origin (e.g. the origin has a scheme of 'https' or host
8885 * 'localhost' etc.). Since we generally propagate a creator document's origin
8886 * onto data:, blob:, etc. documents, this works for them too.
8888 * The scenario where this observation breaks down is sandboxing without the
8889 * 'allow-same-origin' flag, since in this case a document is given a unique
8890 * origin (IsOriginPotentiallyTrustworthy would return false). We handle that
8891 * by using the origin that the document would have had had it not been
8892 * sandboxed.
8894 * DEFICIENCIES: Note that this function uses nsIScriptSecurityManager's
8895 * getChannelResultPrincipalIfNotSandboxed, and that method's ignoring of
8896 * sandboxing is limited to the immediate sandbox. In the case that aDocument
8897 * should inherit its origin (e.g. data: URI) but its parent has ended up
8898 * with a unique origin due to sandboxing further up the parent chain we may
8899 * end up returning false when we would ideally return true (since we will
8900 * examine the parent's origin for 'https' and not finding it.) This means
8901 * that we may restrict the privileges of some pages unnecessarily in this
8902 * edge case.
8904 /* static */
8905 bool nsContentUtils::HttpsStateIsModern(Document* aDocument) {
8906 if (!aDocument) {
8907 return false;
8910 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
8912 if (principal->IsSystemPrincipal()) {
8913 return true;
8916 // If aDocument is sandboxed, try and get the principal that it would have
8917 // been given had it not been sandboxed:
8918 if (principal->GetIsNullPrincipal() &&
8919 (aDocument->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
8920 nsIChannel* channel = aDocument->GetChannel();
8921 if (channel) {
8922 nsCOMPtr<nsIScriptSecurityManager> ssm =
8923 nsContentUtils::GetSecurityManager();
8924 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
8925 channel, getter_AddRefs(principal));
8926 if (NS_FAILED(rv)) {
8927 return false;
8929 if (principal->IsSystemPrincipal()) {
8930 // If a document with the system principal is sandboxing a subdocument
8931 // that would normally inherit the embedding element's principal (e.g.
8932 // a srcdoc document) then the embedding document does not trust the
8933 // content that is written to the embedded document. Unlike when the
8934 // embedding document is https, in this case we have no indication as
8935 // to whether the embedded document's contents are delivered securely
8936 // or not, and the sandboxing would possibly indicate that they were
8937 // not. To play it safe we return false here. (See bug 1162772
8938 // comment 73-80.)
8939 return false;
8944 if (principal->GetIsNullPrincipal()) {
8945 return false;
8948 MOZ_ASSERT(principal->GetIsContentPrincipal());
8950 return principal->GetIsOriginPotentiallyTrustworthy();
8953 /* static */
8954 bool nsContentUtils::ComputeIsSecureContext(nsIChannel* aChannel) {
8955 MOZ_ASSERT(aChannel);
8957 nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
8958 nsCOMPtr<nsIPrincipal> principal;
8959 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
8960 aChannel, getter_AddRefs(principal));
8961 if (NS_FAILED(rv)) {
8962 return false;
8965 if (principal->IsSystemPrincipal()) {
8966 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
8967 // If the load would've been sandboxed, treat this load as an untrusted
8968 // load, as system code considers sandboxed resources insecure.
8969 return !loadInfo->GetLoadingSandboxed();
8972 if (principal->GetIsNullPrincipal()) {
8973 return false;
8976 return principal->GetIsOriginPotentiallyTrustworthy();
8979 /* static */
8980 void nsContentUtils::TryToUpgradeElement(Element* aElement) {
8981 NodeInfo* nodeInfo = aElement->NodeInfo();
8982 RefPtr<nsAtom> typeAtom =
8983 aElement->GetCustomElementData()->GetCustomElementType();
8985 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
8986 CustomElementDefinition* definition =
8987 nsContentUtils::LookupCustomElementDefinition(
8988 nodeInfo->GetDocument(), nodeInfo->NameAtom(),
8989 nodeInfo->NamespaceID(), typeAtom);
8990 if (definition) {
8991 nsContentUtils::EnqueueUpgradeReaction(aElement, definition);
8992 } else {
8993 // Add an unresolved custom element that is a candidate for upgrade when a
8994 // custom element is connected to the document.
8995 nsContentUtils::RegisterUnresolvedElement(aElement, typeAtom);
8999 MOZ_CAN_RUN_SCRIPT
9000 static void DoCustomElementCreate(Element** aElement, JSContext* aCx,
9001 Document* aDoc, NodeInfo* aNodeInfo,
9002 CustomElementConstructor* aConstructor,
9003 ErrorResult& aRv) {
9004 JS::Rooted<JS::Value> constructResult(aCx);
9005 aConstructor->Construct(&constructResult, aRv, "Custom Element Create",
9006 CallbackFunction::eRethrowExceptions);
9007 if (aRv.Failed()) {
9008 return;
9011 RefPtr<Element> element;
9012 // constructResult is an ObjectValue because construction with a callback
9013 // always forms the return value from a JSObject.
9014 UNWRAP_OBJECT(Element, &constructResult, element);
9015 if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9016 if (!element || !element->IsHTMLElement()) {
9017 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9018 "HTMLElement");
9019 return;
9021 } else {
9022 if (!element || !element->IsXULElement()) {
9023 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9024 "XULElement");
9025 return;
9029 nsAtom* localName = aNodeInfo->NameAtom();
9031 if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
9032 element->HasChildren() || element->GetAttrCount() ||
9033 element->NodeInfo()->NameAtom() != localName) {
9034 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
9035 return;
9038 element.forget(aElement);
9041 /* static */
9042 nsresult nsContentUtils::NewXULOrHTMLElement(
9043 Element** aResult, mozilla::dom::NodeInfo* aNodeInfo,
9044 FromParser aFromParser, nsAtom* aIsAtom,
9045 mozilla::dom::CustomElementDefinition* aDefinition) {
9046 RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
9047 MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
9048 nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
9049 "Can only create XUL or XHTML elements.");
9051 nsAtom* name = nodeInfo->NameAtom();
9052 int32_t tag = eHTMLTag_unknown;
9053 bool isCustomElementName = false;
9054 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9055 tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
9056 isCustomElementName =
9057 (tag == eHTMLTag_userdefined &&
9058 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML));
9059 } else { // kNameSpaceID_XUL
9060 if (aIsAtom) {
9061 // Make sure the customized built-in element to be constructed confirms
9062 // to our naming requirement, i.e. [is] must be a dashed name and
9063 // the tag name must not.
9064 // if so, set isCustomElementName to false to kick off all the logics
9065 // that pick up aIsAtom.
9066 if (nsContentUtils::IsNameWithDash(aIsAtom) &&
9067 !nsContentUtils::IsNameWithDash(name)) {
9068 isCustomElementName = false;
9069 } else {
9070 isCustomElementName =
9071 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9073 } else {
9074 isCustomElementName =
9075 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9079 nsAtom* tagAtom = nodeInfo->NameAtom();
9080 nsAtom* typeAtom = nullptr;
9081 bool isCustomElement = isCustomElementName || aIsAtom;
9082 if (isCustomElement) {
9083 typeAtom = isCustomElementName ? tagAtom : aIsAtom;
9086 MOZ_ASSERT_IF(aDefinition, isCustomElement);
9088 // https://dom.spec.whatwg.org/#concept-create-element
9089 // We only handle the "synchronous custom elements flag is set" now.
9090 // For the unset case (e.g. cloning a node), see bug 1319342 for that.
9091 // Step 4.
9092 RefPtr<CustomElementDefinition> definition = aDefinition;
9093 if (isCustomElement && !definition) {
9094 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9095 definition = nsContentUtils::LookupCustomElementDefinition(
9096 nodeInfo->GetDocument(), nodeInfo->NameAtom(), nodeInfo->NamespaceID(),
9097 typeAtom);
9100 // It might be a problem that parser synchronously calls constructor, so filed
9101 // bug 1378079 to figure out what we should do for parser case.
9102 if (definition) {
9104 * Synchronous custom elements flag is determined by 3 places in spec,
9105 * 1) create an element for a token, the flag is determined by
9106 * "will execute script" which is not originally created
9107 * for the HTML fragment parsing algorithm.
9108 * 2) createElement and createElementNS, the flag is the same as
9109 * NOT_FROM_PARSER.
9110 * 3) clone a node, our implementation will not go into this function.
9111 * For the unset case which is non-synchronous only applied for
9112 * inner/outerHTML.
9114 bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT;
9115 // Per discussion in https://github.com/w3c/webcomponents/issues/635,
9116 // use entry global in those places that are called from JS APIs and use the
9117 // node document's global object if it is called from parser.
9118 nsIGlobalObject* global;
9119 if (aFromParser == dom::NOT_FROM_PARSER) {
9120 global = GetEntryGlobal();
9122 // Documents created from the PrototypeDocumentSink always use
9123 // NOT_FROM_PARSER for non-XUL elements. We can get the global from the
9124 // document in that case.
9125 if (!global) {
9126 Document* doc = nodeInfo->GetDocument();
9127 if (doc && doc->LoadedFromPrototype()) {
9128 global = doc->GetScopeObject();
9131 } else {
9132 global = nodeInfo->GetDocument()->GetScopeObject();
9134 if (!global) {
9135 // In browser chrome code, one may have access to a document which doesn't
9136 // have scope object anymore.
9137 return NS_ERROR_FAILURE;
9140 AutoEntryScript aes(global, "create custom elements");
9141 JSContext* cx = aes.cx();
9142 ErrorResult rv;
9144 // Step 5.
9145 if (definition->IsCustomBuiltIn()) {
9146 // SetupCustomElement() should be called with an element that don't have
9147 // CustomElementData setup, if not we will hit the assertion in
9148 // SetCustomElementData().
9149 // Built-in element
9150 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9151 *aResult =
9152 CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9153 } else {
9154 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9156 (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
9157 if (synchronousCustomElements) {
9158 CustomElementRegistry::Upgrade(*aResult, definition, rv);
9159 if (rv.MaybeSetPendingException(cx)) {
9160 aes.ReportException();
9162 } else {
9163 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9166 return NS_OK;
9169 // Step 6.1.
9170 if (synchronousCustomElements) {
9171 definition->mPrefixStack.AppendElement(nodeInfo->GetPrefixAtom());
9172 RefPtr<Document> doc = nodeInfo->GetDocument();
9173 DoCustomElementCreate(aResult, cx, doc, nodeInfo,
9174 MOZ_KnownLive(definition->mConstructor), rv);
9175 if (rv.MaybeSetPendingException(cx)) {
9176 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9177 NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(),
9178 aFromParser));
9179 } else {
9180 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9182 (*aResult)->SetDefined(false);
9184 definition->mPrefixStack.RemoveLastElement();
9185 return NS_OK;
9188 // Step 6.2.
9189 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9190 NS_IF_ADDREF(*aResult =
9191 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9192 } else {
9193 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9195 (*aResult)->SetCustomElementData(new CustomElementData(definition->mType));
9196 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9197 return NS_OK;
9200 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9201 // Per the Custom Element specification, unknown tags that are valid custom
9202 // element names should be HTMLElement instead of HTMLUnknownElement.
9203 if (isCustomElementName) {
9204 NS_IF_ADDREF(*aResult =
9205 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9206 } else {
9207 *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9209 } else {
9210 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9213 if (!*aResult) {
9214 return NS_ERROR_OUT_OF_MEMORY;
9217 if (isCustomElement) {
9218 (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
9219 nsContentUtils::RegisterCallbackUpgradeElement(*aResult, typeAtom);
9222 return NS_OK;
9225 CustomElementRegistry* nsContentUtils::GetCustomElementRegistry(
9226 Document* aDoc) {
9227 MOZ_ASSERT(aDoc);
9229 if (!aDoc->GetDocShell()) {
9230 return nullptr;
9233 nsPIDOMWindowInner* window = aDoc->GetInnerWindow();
9234 if (!window) {
9235 return nullptr;
9238 return window->CustomElements();
9241 /* static */
9242 CustomElementDefinition* nsContentUtils::LookupCustomElementDefinition(
9243 Document* aDoc, nsAtom* aNameAtom, uint32_t aNameSpaceID,
9244 nsAtom* aTypeAtom) {
9245 if (aNameSpaceID != kNameSpaceID_XUL && aNameSpaceID != kNameSpaceID_XHTML) {
9246 return nullptr;
9249 RefPtr<CustomElementRegistry> registry = GetCustomElementRegistry(aDoc);
9250 if (!registry) {
9251 return nullptr;
9254 return registry->LookupCustomElementDefinition(aNameAtom, aNameSpaceID,
9255 aTypeAtom);
9258 /* static */
9259 void nsContentUtils::RegisterCallbackUpgradeElement(Element* aElement,
9260 nsAtom* aTypeName) {
9261 MOZ_ASSERT(aElement);
9263 Document* doc = aElement->OwnerDoc();
9264 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9265 if (registry) {
9266 registry->RegisterCallbackUpgradeElement(aElement, aTypeName);
9270 /* static */
9271 void nsContentUtils::RegisterUnresolvedElement(Element* aElement,
9272 nsAtom* aTypeName) {
9273 MOZ_ASSERT(aElement);
9275 Document* doc = aElement->OwnerDoc();
9276 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9277 if (registry) {
9278 registry->RegisterUnresolvedElement(aElement, aTypeName);
9282 /* static */
9283 void nsContentUtils::UnregisterUnresolvedElement(Element* aElement) {
9284 MOZ_ASSERT(aElement);
9286 nsAtom* typeAtom = aElement->GetCustomElementData()->GetCustomElementType();
9287 Document* doc = aElement->OwnerDoc();
9288 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9289 if (registry) {
9290 registry->UnregisterUnresolvedElement(aElement, typeAtom);
9294 /* static */
9295 void nsContentUtils::EnqueueUpgradeReaction(
9296 Element* aElement, CustomElementDefinition* aDefinition) {
9297 MOZ_ASSERT(aElement);
9299 Document* doc = aElement->OwnerDoc();
9301 // No DocGroup means no custom element reactions stack.
9302 if (!doc->GetDocGroup()) {
9303 return;
9306 CustomElementReactionsStack* stack =
9307 doc->GetDocGroup()->CustomElementReactionsStack();
9308 stack->EnqueueUpgradeReaction(aElement, aDefinition);
9311 /* static */
9312 void nsContentUtils::EnqueueLifecycleCallback(
9313 Document::ElementCallbackType aType, Element* aCustomElement,
9314 LifecycleCallbackArgs* aArgs,
9315 LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
9316 CustomElementDefinition* aDefinition) {
9317 // No DocGroup means no custom element reactions stack.
9318 if (!aCustomElement->OwnerDoc()->GetDocGroup()) {
9319 return;
9322 CustomElementRegistry::EnqueueLifecycleCallback(
9323 aType, aCustomElement, aArgs, aAdoptedCallbackArgs, aDefinition);
9326 /* static */
9327 void nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
9328 Document* aDocument, nsTArray<nsIContent*>& aElements) {
9329 MOZ_ASSERT(aDocument);
9330 #ifdef DEBUG
9331 size_t oldLength = aElements.Length();
9332 #endif
9334 if (PresShell* presShell = aDocument->GetPresShell()) {
9335 if (nsIFrame* scrollFrame = presShell->GetRootScrollFrame()) {
9336 nsIAnonymousContentCreator* creator = do_QueryFrame(scrollFrame);
9337 MOZ_ASSERT(
9338 creator,
9339 "scroll frame should always implement nsIAnonymousContentCreator");
9340 creator->AppendAnonymousContentTo(aElements, 0);
9342 if (nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame()) {
9343 canvasFrame->AppendAnonymousContentTo(aElements, 0);
9347 #ifdef DEBUG
9348 for (size_t i = oldLength; i < aElements.Length(); i++) {
9349 MOZ_ASSERT(
9350 aElements[i]->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent),
9351 "Someone here has lied, or missed to flag the node");
9353 #endif
9356 static void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame,
9357 nsTArray<nsIContent*>& aKids,
9358 uint32_t aFlags) {
9359 if (nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame)) {
9360 ac->AppendAnonymousContentTo(aKids, aFlags);
9364 /* static */
9365 void nsContentUtils::AppendNativeAnonymousChildren(const nsIContent* aContent,
9366 nsTArray<nsIContent*>& aKids,
9367 uint32_t aFlags) {
9368 if (aContent->MayHaveAnonymousChildren()) {
9369 if (nsIFrame* primaryFrame = aContent->GetPrimaryFrame()) {
9370 // NAC created by the element's primary frame.
9371 AppendNativeAnonymousChildrenFromFrame(primaryFrame, aKids, aFlags);
9373 // NAC created by any other non-primary frames for the element.
9374 AutoTArray<nsIFrame::OwnedAnonBox, 8> ownedAnonBoxes;
9375 primaryFrame->AppendOwnedAnonBoxes(ownedAnonBoxes);
9376 for (nsIFrame::OwnedAnonBox& box : ownedAnonBoxes) {
9377 MOZ_ASSERT(box.mAnonBoxFrame->GetContent() == aContent);
9378 AppendNativeAnonymousChildrenFromFrame(box.mAnonBoxFrame, aKids,
9379 aFlags);
9383 // Get manually created NAC (editor resize handles, etc.).
9384 if (auto nac = static_cast<ManualNACArray*>(
9385 aContent->GetProperty(nsGkAtoms::manualNACProperty))) {
9386 aKids.AppendElements(*nac);
9390 // The root scroll frame is not the primary frame of the root element.
9391 // Detect and handle this case.
9392 if (!(aFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
9393 aContent == aContent->OwnerDoc()->GetRootElement()) {
9394 AppendDocumentLevelNativeAnonymousContentTo(aContent->OwnerDoc(), aKids);
9398 /* static */
9399 bool nsContentUtils::QueryTriggeringPrincipal(
9400 nsIContent* aLoadingNode, nsIPrincipal* aDefaultPrincipal,
9401 nsIPrincipal** aTriggeringPrincipal) {
9402 MOZ_ASSERT(aLoadingNode);
9403 MOZ_ASSERT(aTriggeringPrincipal);
9405 bool result = false;
9406 nsCOMPtr<nsIPrincipal> loadingPrincipal = aDefaultPrincipal;
9407 if (!loadingPrincipal) {
9408 loadingPrincipal = aLoadingNode->NodePrincipal();
9411 // If aLoadingNode is content, bail out early.
9412 if (!aLoadingNode->NodePrincipal()->IsSystemPrincipal()) {
9413 loadingPrincipal.forget(aTriggeringPrincipal);
9414 return result;
9417 nsAutoString loadingStr;
9418 if (aLoadingNode->IsElement()) {
9419 aLoadingNode->AsElement()->GetAttr(
9420 kNameSpaceID_None, nsGkAtoms::triggeringprincipal, loadingStr);
9423 // Fall back if 'triggeringprincipal' isn't specified,
9424 if (loadingStr.IsEmpty()) {
9425 loadingPrincipal.forget(aTriggeringPrincipal);
9426 return result;
9429 nsCString binary;
9430 nsresult rv = Base64Decode(NS_ConvertUTF16toUTF8(loadingStr), binary);
9431 if (NS_SUCCEEDED(rv)) {
9432 nsCOMPtr<nsIPrincipal> serializedPrin = BasePrincipal::FromJSON(binary);
9433 if (serializedPrin) {
9434 result = true;
9435 serializedPrin.forget(aTriggeringPrincipal);
9437 } else {
9438 MOZ_ASSERT(false, "Unable to deserialize base64 principal");
9441 if (!result) {
9442 // Fallback if the deserialization is failed.
9443 loadingPrincipal.forget(aTriggeringPrincipal);
9446 return result;
9449 /* static */
9450 void nsContentUtils::GetContentPolicyTypeForUIImageLoading(
9451 nsIContent* aLoadingNode, nsIPrincipal** aTriggeringPrincipal,
9452 nsContentPolicyType& aContentPolicyType, uint64_t* aRequestContextID) {
9453 MOZ_ASSERT(aRequestContextID);
9455 bool result = QueryTriggeringPrincipal(aLoadingNode, aTriggeringPrincipal);
9456 if (result) {
9457 // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for
9458 // indicating it's a favicon loading.
9459 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
9461 nsAutoString requestContextID;
9462 if (aLoadingNode->IsElement()) {
9463 aLoadingNode->AsElement()->GetAttr(
9464 kNameSpaceID_None, nsGkAtoms::requestcontextid, requestContextID);
9466 nsresult rv;
9467 int64_t val = requestContextID.ToInteger64(&rv);
9468 *aRequestContextID = NS_SUCCEEDED(rv) ? val : 0;
9469 } else {
9470 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
9474 /* static */
9475 nsresult nsContentUtils::CreateJSValueFromSequenceOfObject(
9476 JSContext* aCx, const Sequence<JSObject*>& aTransfer,
9477 JS::MutableHandle<JS::Value> aValue) {
9478 if (aTransfer.IsEmpty()) {
9479 return NS_OK;
9482 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, aTransfer.Length()));
9483 if (!array) {
9484 return NS_ERROR_OUT_OF_MEMORY;
9487 for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
9488 JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
9489 if (!object) {
9490 continue;
9493 if (NS_WARN_IF(
9494 !JS_DefineElement(aCx, array, i, object, JSPROP_ENUMERATE))) {
9495 return NS_ERROR_OUT_OF_MEMORY;
9499 aValue.setObject(*array);
9500 return NS_OK;
9503 /* static */
9504 bool nsContentUtils::ShouldBlockReservedKeys(WidgetKeyboardEvent* aKeyEvent) {
9505 nsCOMPtr<nsIPrincipal> principal;
9506 nsCOMPtr<Element> targetElement =
9507 do_QueryInterface(aKeyEvent->mOriginalTarget);
9508 nsCOMPtr<nsIBrowser> targetBrowser;
9509 if (targetElement) {
9510 targetBrowser = targetElement->AsBrowser();
9512 bool isRemoteBrowser = false;
9513 if (targetBrowser) {
9514 targetBrowser->GetIsRemoteBrowser(&isRemoteBrowser);
9517 if (isRemoteBrowser) {
9518 targetBrowser->GetContentPrincipal(getter_AddRefs(principal));
9519 return principal ? nsContentUtils::IsSitePermDeny(principal, "shortcuts"_ns)
9520 : false;
9523 nsCOMPtr<nsIContent> content = do_QueryInterface(aKeyEvent->mOriginalTarget);
9524 if (content) {
9525 Document* doc = content->GetUncomposedDoc();
9526 if (doc) {
9527 RefPtr<WindowContext> wc = doc->GetWindowContext();
9528 if (wc) {
9529 return wc->TopWindowContext()->GetShortcutsPermission() ==
9530 nsIPermissionManager::DENY_ACTION;
9535 return false;
9539 * Checks whether the given type is a supported document type for
9540 * loading within the nsObjectLoadingContent specified by aContent.
9542 * NOTE Helper method for nsContentUtils::HtmlObjectContentTypeForMIMEType.
9543 * NOTE Does not take content policy or capabilities into account
9545 static bool HtmlObjectContentSupportsDocument(const nsCString& aMimeType,
9546 nsIContent* aContent) {
9547 nsCOMPtr<nsIWebNavigationInfo> info(
9548 do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID));
9549 if (!info) {
9550 return false;
9553 nsCOMPtr<nsIWebNavigation> webNav;
9554 if (aContent) {
9555 Document* currentDoc = aContent->GetComposedDoc();
9556 if (currentDoc) {
9557 webNav = do_GetInterface(currentDoc->GetWindow());
9561 uint32_t supported;
9562 nsresult rv = info->IsTypeSupported(aMimeType, webNav, &supported);
9564 if (NS_FAILED(rv)) {
9565 return false;
9568 if (supported != nsIWebNavigationInfo::UNSUPPORTED) {
9569 // Don't want to support plugins as documents
9570 return supported != nsIWebNavigationInfo::PLUGIN;
9573 // Try a stream converter
9574 // NOTE: We treat any type we can convert from as a supported type. If a
9575 // type is not actually supported, the URI loader will detect that and
9576 // return an error, and we'll fallback.
9577 nsCOMPtr<nsIStreamConverterService> convServ =
9578 do_GetService("@mozilla.org/streamConverters;1");
9579 bool canConvert = false;
9580 if (convServ) {
9581 rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
9583 return NS_SUCCEEDED(rv) && canConvert;
9586 /* static */
9587 already_AddRefed<nsIPluginTag> nsContentUtils::PluginTagForType(
9588 const nsCString& aMIMEType, bool aNoFakePlugin) {
9589 RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
9590 nsCOMPtr<nsIPluginTag> tag;
9591 NS_ENSURE_TRUE(pluginHost, nullptr);
9593 // ShouldPlay will handle the case where the plugin is disabled
9594 pluginHost->GetPluginTagForType(
9595 aMIMEType,
9596 aNoFakePlugin ? nsPluginHost::eExcludeFake : nsPluginHost::eExcludeNone,
9597 getter_AddRefs(tag));
9599 return tag.forget();
9602 /* static */
9603 uint32_t nsContentUtils::HtmlObjectContentTypeForMIMEType(
9604 const nsCString& aMIMEType, bool aNoFakePlugin, nsIContent* aContent) {
9605 if (aMIMEType.IsEmpty()) {
9606 return nsIObjectLoadingContent::TYPE_NULL;
9609 if (imgLoader::SupportImageWithMimeType(aMIMEType.get())) {
9610 return nsIObjectLoadingContent::TYPE_IMAGE;
9613 // Faking support of the PDF content as a document for EMBED tags
9614 // when internal PDF viewer is enabled.
9615 if (aMIMEType.LowerCaseEqualsLiteral("application/pdf") && IsPDFJSEnabled()) {
9616 return nsIObjectLoadingContent::TYPE_DOCUMENT;
9619 if (HtmlObjectContentSupportsDocument(aMIMEType, aContent)) {
9620 return nsIObjectLoadingContent::TYPE_DOCUMENT;
9623 RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
9624 if (pluginHost) {
9625 nsCOMPtr<nsIPluginTag> tag = PluginTagForType(aMIMEType, aNoFakePlugin);
9626 if (tag) {
9627 if (!aNoFakePlugin &&
9628 nsCOMPtr<nsIFakePluginTag>(do_QueryInterface(tag))) {
9629 return nsIObjectLoadingContent::TYPE_FAKE_PLUGIN;
9632 // ShouldPlay will handle checking for disabled plugins
9633 return nsIObjectLoadingContent::TYPE_PLUGIN;
9637 return nsIObjectLoadingContent::TYPE_NULL;
9640 /* static */
9641 already_AddRefed<nsISerialEventTarget> nsContentUtils::GetEventTargetByLoadInfo(
9642 nsILoadInfo* aLoadInfo, TaskCategory aCategory) {
9643 if (NS_WARN_IF(!aLoadInfo)) {
9644 return nullptr;
9647 RefPtr<Document> doc;
9648 aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
9649 nsCOMPtr<nsISerialEventTarget> target;
9650 if (doc) {
9651 if (DocGroup* group = doc->GetDocGroup()) {
9652 target = group->EventTargetFor(aCategory);
9654 } else {
9655 target = GetMainThreadSerialEventTarget();
9658 return target.forget();
9661 /* static */
9662 bool nsContentUtils::IsLocalRefURL(const nsString& aString) {
9663 return !aString.IsEmpty() && aString[0] == '#';
9666 // We use only 53 bits for the ID so that it can be converted to and from a JS
9667 // value without loss of precision. The upper bits of the ID hold the process
9668 // ID. The lower bits identify the object itself.
9669 static constexpr uint64_t kIdTotalBits = 53;
9670 static constexpr uint64_t kIdProcessBits = 22;
9671 static constexpr uint64_t kIdBits = kIdTotalBits - kIdProcessBits;
9673 /* static */
9674 uint64_t nsContentUtils::GenerateProcessSpecificId(uint64_t aId) {
9675 uint64_t processId = 0;
9676 if (XRE_IsContentProcess()) {
9677 ContentChild* cc = ContentChild::GetSingleton();
9678 processId = cc->GetID();
9681 MOZ_RELEASE_ASSERT(processId < (uint64_t(1) << kIdProcessBits));
9682 uint64_t processBits = processId & ((uint64_t(1) << kIdProcessBits) - 1);
9684 uint64_t id = aId;
9685 MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
9686 uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
9688 return (processBits << kIdBits) | bits;
9691 // Next process-local Tab ID.
9692 static uint64_t gNextTabId = 0;
9694 /* static */
9695 uint64_t nsContentUtils::GenerateTabId() {
9696 return GenerateProcessSpecificId(++gNextTabId);
9699 // Next process-local Browser ID.
9700 static uint64_t gNextBrowserId = 0;
9702 /* static */
9703 uint64_t nsContentUtils::GenerateBrowserId() {
9704 return GenerateProcessSpecificId(++gNextBrowserId);
9707 // Next process-local Browsing Context ID.
9708 static uint64_t gNextBrowsingContextId = 0;
9710 /* static */
9711 uint64_t nsContentUtils::GenerateBrowsingContextId() {
9712 return GenerateProcessSpecificId(++gNextBrowsingContextId);
9715 // Next process-local Window ID.
9716 static uint64_t gNextWindowId = 0;
9718 /* static */
9719 uint64_t nsContentUtils::GenerateWindowId() {
9720 return GenerateProcessSpecificId(++gNextWindowId);
9723 // Next process-local load.
9724 static Atomic<uint64_t> gNextLoadIdentifier(0);
9726 /* static */
9727 uint64_t nsContentUtils::GenerateLoadIdentifier() {
9728 return GenerateProcessSpecificId(++gNextLoadIdentifier);
9731 /* static */
9732 bool nsContentUtils::GetUserIsInteracting() {
9733 return UserInteractionObserver::sUserActive;
9736 /* static */
9737 bool nsContentUtils::GetSourceMapURL(nsIHttpChannel* aChannel,
9738 nsACString& aResult) {
9739 nsresult rv = aChannel->GetResponseHeader("SourceMap"_ns, aResult);
9740 if (NS_FAILED(rv)) {
9741 rv = aChannel->GetResponseHeader("X-SourceMap"_ns, aResult);
9743 return NS_SUCCEEDED(rv);
9746 /* static */
9747 bool nsContentUtils::IsMessageInputEvent(const IPC::Message& aMsg) {
9748 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
9749 mozilla::dom::PBrowser::PBrowserStart) {
9750 switch (aMsg.type()) {
9751 case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
9752 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
9753 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
9754 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
9755 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
9756 case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
9757 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
9758 case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
9759 case mozilla::dom::PBrowser::Msg_MouseEvent__ID:
9760 case mozilla::dom::PBrowser::Msg_SetDocShellIsActive__ID:
9761 return true;
9764 return false;
9767 /* static */
9768 bool nsContentUtils::IsMessageCriticalInputEvent(const IPC::Message& aMsg) {
9769 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
9770 mozilla::dom::PBrowser::PBrowserStart) {
9771 switch (aMsg.type()) {
9772 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
9773 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
9774 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
9775 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
9776 return true;
9779 return false;
9782 static const char* kUserInteractionInactive = "user-interaction-inactive";
9783 static const char* kUserInteractionActive = "user-interaction-active";
9785 void nsContentUtils::UserInteractionObserver::Init() {
9786 // Listen for the observer messages from EventStateManager which are telling
9787 // us whether or not the user is interacting.
9788 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
9789 obs->AddObserver(this, kUserInteractionInactive, false);
9790 obs->AddObserver(this, kUserInteractionActive, false);
9792 // We can't register ourselves as an annotator yet, as the
9793 // BackgroundHangMonitor hasn't started yet. It will have started by the
9794 // time we have the chance to spin the event loop.
9795 RefPtr<UserInteractionObserver> self = this;
9796 NS_DispatchToMainThread(NS_NewRunnableFunction(
9797 "nsContentUtils::UserInteractionObserver::Init",
9798 [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
9801 void nsContentUtils::UserInteractionObserver::Shutdown() {
9802 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
9803 if (obs) {
9804 obs->RemoveObserver(this, kUserInteractionInactive);
9805 obs->RemoveObserver(this, kUserInteractionActive);
9808 BackgroundHangMonitor::UnregisterAnnotator(*this);
9812 * NB: This function is always called by the BackgroundHangMonitor thread.
9813 * Plan accordingly
9815 void nsContentUtils::UserInteractionObserver::AnnotateHang(
9816 BackgroundHangAnnotations& aAnnotations) {
9817 // NOTE: Only annotate the hang report if the user is known to be interacting.
9818 if (sUserActive) {
9819 aAnnotations.AddAnnotation(u"UserInteracting"_ns, true);
9823 NS_IMETHODIMP
9824 nsContentUtils::UserInteractionObserver::Observe(nsISupports* aSubject,
9825 const char* aTopic,
9826 const char16_t* aData) {
9827 if (!strcmp(aTopic, kUserInteractionInactive)) {
9828 sUserActive = false;
9829 } else if (!strcmp(aTopic, kUserInteractionActive)) {
9830 sUserActive = true;
9831 } else {
9832 NS_WARNING("Unexpected observer notification");
9834 return NS_OK;
9837 Atomic<bool> nsContentUtils::UserInteractionObserver::sUserActive(false);
9838 NS_IMPL_ISUPPORTS(nsContentUtils::UserInteractionObserver, nsIObserver)
9840 /* static */
9841 bool nsContentUtils::IsSpecialName(const nsAString& aName) {
9842 return aName.LowerCaseEqualsLiteral("_blank") ||
9843 aName.LowerCaseEqualsLiteral("_top") ||
9844 aName.LowerCaseEqualsLiteral("_parent") ||
9845 aName.LowerCaseEqualsLiteral("_self");
9848 /* static */
9849 bool nsContentUtils::IsOverridingWindowName(const nsAString& aName) {
9850 return !aName.IsEmpty() && !IsSpecialName(aName);
9853 // Unfortunately, we can't unwrap an IDL object using only a concrete type.
9854 // We need to calculate type data based on the IDL typename. Which means
9855 // wrapping our templated function in a macro.
9856 #define EXTRACT_EXN_VALUES(T, ...) \
9857 ExtractExceptionValues<mozilla::dom::prototypes::id::T, \
9858 T##_Binding::NativeType, T>(__VA_ARGS__) \
9859 .isOk()
9861 template <prototypes::ID PrototypeID, class NativeType, typename T>
9862 static Result<Ok, nsresult> ExtractExceptionValues(
9863 JSContext* aCx, JS::HandleObject aObj, nsAString& aSourceSpecOut,
9864 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
9865 AssertStaticUnwrapOK<PrototypeID>();
9866 RefPtr<T> exn;
9867 MOZ_TRY((UnwrapObject<PrototypeID, NativeType>(aObj, exn, nullptr)));
9869 exn->GetFilename(aCx, aSourceSpecOut);
9870 if (!aSourceSpecOut.IsEmpty()) {
9871 *aLineOut = exn->LineNumber(aCx);
9872 *aColumnOut = exn->ColumnNumber();
9875 exn->GetName(aMessageOut);
9876 aMessageOut.AppendLiteral(": ");
9878 nsAutoString message;
9879 exn->GetMessageMoz(message);
9880 aMessageOut.Append(message);
9881 return Ok();
9884 /* static */
9885 void nsContentUtils::ExtractErrorValues(
9886 JSContext* aCx, JS::Handle<JS::Value> aValue, nsACString& aSourceSpecOut,
9887 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
9888 nsAutoString sourceSpec;
9889 ExtractErrorValues(aCx, aValue, sourceSpec, aLineOut, aColumnOut,
9890 aMessageOut);
9891 CopyUTF16toUTF8(sourceSpec, aSourceSpecOut);
9894 /* static */
9895 void nsContentUtils::ExtractErrorValues(
9896 JSContext* aCx, JS::Handle<JS::Value> aValue, nsAString& aSourceSpecOut,
9897 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
9898 MOZ_ASSERT(aLineOut);
9899 MOZ_ASSERT(aColumnOut);
9901 if (aValue.isObject()) {
9902 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
9904 // Try to process as an Error object. Use the file/line/column values
9905 // from the Error as they will be more specific to the root cause of
9906 // the problem.
9907 JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
9908 if (err) {
9909 // Use xpc to extract the error message only. We don't actually send
9910 // this report anywhere.
9911 RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
9912 report->Init(err,
9913 nullptr, // toString result
9914 false, // chrome
9915 0); // window ID
9917 if (!report->mFileName.IsEmpty()) {
9918 aSourceSpecOut = report->mFileName;
9919 *aLineOut = report->mLineNumber;
9920 *aColumnOut = report->mColumn;
9922 aMessageOut.Assign(report->mErrorMsg);
9925 // Next, try to unwrap the rejection value as a DOMException.
9926 else if (EXTRACT_EXN_VALUES(DOMException, aCx, obj, aSourceSpecOut,
9927 aLineOut, aColumnOut, aMessageOut)) {
9928 return;
9931 // Next, try to unwrap the rejection value as an XPC Exception.
9932 else if (EXTRACT_EXN_VALUES(Exception, aCx, obj, aSourceSpecOut, aLineOut,
9933 aColumnOut, aMessageOut)) {
9934 return;
9938 // If we could not unwrap a specific error type, then perform default safe
9939 // string conversions on primitives. Objects will result in "[Object]"
9940 // unfortunately.
9941 if (aMessageOut.IsEmpty()) {
9942 nsAutoJSString jsString;
9943 if (jsString.init(aCx, aValue)) {
9944 aMessageOut = jsString;
9945 } else {
9946 JS_ClearPendingException(aCx);
9951 #undef EXTRACT_EXN_VALUES
9953 /* static */
9954 bool nsContentUtils::ContentIsLink(nsIContent* aContent) {
9955 if (!aContent || !aContent->IsElement()) {
9956 return false;
9959 if (aContent->IsHTMLElement(nsGkAtoms::a)) {
9960 return true;
9963 return aContent->AsElement()->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
9964 nsGkAtoms::simple, eCaseMatters);
9967 /* static */
9968 already_AddRefed<ContentFrameMessageManager>
9969 nsContentUtils::TryGetBrowserChildGlobal(nsISupports* aFrom) {
9970 RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(aFrom);
9971 if (!frameLoaderOwner) {
9972 return nullptr;
9975 RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
9976 if (!frameLoader) {
9977 return nullptr;
9980 RefPtr<ContentFrameMessageManager> manager =
9981 frameLoader->GetBrowserChildMessageManager();
9982 return manager.forget();
9985 /* static */
9986 uint32_t nsContentUtils::InnerOrOuterWindowCreated() {
9987 MOZ_ASSERT(NS_IsMainThread());
9988 ++sInnerOrOuterWindowCount;
9989 return ++sInnerOrOuterWindowSerialCounter;
9992 /* static */
9993 void nsContentUtils::InnerOrOuterWindowDestroyed() {
9994 MOZ_ASSERT(NS_IsMainThread());
9995 MOZ_ASSERT(sInnerOrOuterWindowCount > 0);
9996 --sInnerOrOuterWindowCount;
9999 static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
10000 nsAString* result = static_cast<nsAString*>(aData);
10001 result->Append(static_cast<const char16_t*>(aBuf),
10002 static_cast<uint32_t>(aLen));
10003 return true;
10006 /* static */
10007 bool nsContentUtils::StringifyJSON(JSContext* aCx,
10008 JS::MutableHandle<JS::Value> aValue,
10009 nsAString& aOutStr) {
10010 MOZ_ASSERT(aCx);
10011 aOutStr.Truncate();
10012 JS::RootedValue value(aCx, aValue.get());
10013 nsAutoString serializedValue;
10014 NS_ENSURE_TRUE(JS_Stringify(aCx, &value, nullptr, JS::NullHandleValue,
10015 JSONCreator, &serializedValue),
10016 false);
10017 aOutStr = serializedValue;
10018 return true;
10021 /* static */
10022 bool nsContentUtils::
10023 HighPriorityEventPendingForTopLevelDocumentBeforeContentfulPaint(
10024 Document* aDocument) {
10025 if (!aDocument || aDocument->IsLoadedAsData()) {
10026 return false;
10029 Document* topLevel = aDocument->GetTopLevelContentDocument();
10030 return topLevel && topLevel->GetPresShell() &&
10031 topLevel->GetPresShell()->GetPresContext() &&
10032 !topLevel->GetPresShell()->GetPresContext()->HadContentfulPaint() &&
10033 nsThreadManager::MainThreadHasPendingHighPriorityEvents();
10036 /* static */
10037 nsGlobalWindowInner* nsContentUtils::CallerInnerWindow() {
10038 nsIGlobalObject* global = GetIncumbentGlobal();
10039 NS_ENSURE_TRUE(global, nullptr);
10041 if (auto* window = global->AsInnerWindow()) {
10042 return nsGlobalWindowInner::Cast(window);
10045 // When Extensions run content scripts inside a sandbox, it uses
10046 // sandboxPrototype to make them appear as though they're running in the
10047 // scope of the page. So when a content script invokes postMessage, it expects
10048 // the |source| of the received message to be the window set as the
10049 // sandboxPrototype. This used to work incidentally for unrelated reasons, but
10050 // now we need to do some special handling to support it.
10051 JS::Rooted<JSObject*> scope(RootingCx(), global->GetGlobalJSObject());
10052 NS_ENSURE_TRUE(scope, nullptr);
10054 if (xpc::IsSandbox(scope)) {
10055 AutoJSAPI jsapi;
10056 MOZ_ALWAYS_TRUE(jsapi.Init(scope));
10057 JSContext* cx = jsapi.cx();
10059 JS::Rooted<JSObject*> scopeProto(cx);
10060 bool ok = JS_GetPrototype(cx, scope, &scopeProto);
10061 NS_ENSURE_TRUE(ok, nullptr);
10062 if (scopeProto && xpc::IsSandboxPrototypeProxy(scopeProto) &&
10063 // Our current Realm on aCx is the sandbox. Using that for the
10064 // CheckedUnwrapDynamic call makes sense: if the sandbox can unwrap the
10065 // window, we can use it. And we do want CheckedUnwrapDynamic, because
10066 // the whole point is to unwrap windows.
10067 (scopeProto = js::CheckedUnwrapDynamic(
10068 scopeProto, cx, /* stopAtWindowProxy = */ false))) {
10069 global = xpc::NativeGlobal(scopeProto);
10070 NS_ENSURE_TRUE(global, nullptr);
10074 // The calling window must be holding a reference, so we can return a weak
10075 // pointer.
10076 return nsGlobalWindowInner::Cast(global->AsInnerWindow());
10079 /* static */
10080 bool nsContentUtils::IsURIInPrefList(nsIURI* aURI, const char* aPrefName) {
10081 MOZ_ASSERT(aPrefName);
10083 nsAutoCString list;
10084 Preferences::GetCString(aPrefName, list);
10085 ToLowerCase(list);
10086 return IsURIInList(aURI, list);
10089 /* static */
10090 bool nsContentUtils::IsURIInList(nsIURI* aURI, const nsCString& aList) {
10091 #ifdef DEBUG
10092 nsAutoCString listLowerCase(aList);
10093 ToLowerCase(listLowerCase);
10094 MOZ_ASSERT(listLowerCase.Equals(aList),
10095 "The aList argument should be lower-case");
10096 #endif
10098 if (!aURI) {
10099 return false;
10102 nsAutoCString scheme;
10103 aURI->GetScheme(scheme);
10104 if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
10105 return false;
10108 if (aList.IsEmpty()) {
10109 return false;
10112 // The list is comma separated domain list. Each item may start with "*.".
10113 // If starts with "*.", it matches any sub-domains.
10115 nsCCharSeparatedTokenizer tokenizer(aList, ',');
10116 while (tokenizer.hasMoreTokens()) {
10117 const nsCString token(tokenizer.nextToken());
10119 nsAutoCString host;
10120 aURI->GetHost(host);
10121 if (host.IsEmpty()) {
10122 return false;
10124 ToLowerCase(host);
10126 for (;;) {
10127 int32_t index = token.Find(host, false);
10128 if (index >= 0 &&
10129 static_cast<uint32_t>(index) + host.Length() <= token.Length()) {
10130 // If we found a full match, return true.
10131 size_t indexAfterHost = index + host.Length();
10132 if (index == 0 && indexAfterHost == token.Length()) {
10133 return true;
10135 // If next character is '/', we need to check the path too.
10136 // We assume the path in the list means "/foo" + "*".
10137 if (token[indexAfterHost] == '/') {
10138 nsDependentCSubstring pathInList(
10139 token, indexAfterHost,
10140 static_cast<nsDependentCSubstring::size_type>(-1));
10141 nsAutoCString filePath;
10142 aURI->GetFilePath(filePath);
10143 ToLowerCase(filePath);
10144 if (StringBeginsWith(filePath, pathInList) &&
10145 (filePath.Length() == pathInList.Length() ||
10146 pathInList.EqualsLiteral("/") ||
10147 filePath[pathInList.Length() - 1] == '/' ||
10148 filePath[pathInList.Length() - 1] == '?' ||
10149 filePath[pathInList.Length() - 1] == '#')) {
10150 return true;
10154 int32_t startIndexOfCurrentLevel = host[0] == '*' ? 1 : 0;
10155 int32_t startIndexOfNextLevel =
10156 host.Find(".", false, startIndexOfCurrentLevel + 1);
10157 if (startIndexOfNextLevel <= 0) {
10158 break;
10160 host = "*"_ns + nsDependentCSubstring(host, startIndexOfNextLevel);
10164 return false;
10167 /* static */
10168 ScreenIntMargin nsContentUtils::GetWindowSafeAreaInsets(
10169 nsIScreen* aScreen, const ScreenIntMargin& aSafeAreaInsets,
10170 const LayoutDeviceIntRect& aWindowRect) {
10171 // This calculates safe area insets of window from screen rectangle, window
10172 // rectangle and safe area insets of screen.
10174 // +----------------------------------------+ <-- screen
10175 // | +-------------------------------+ <------- window
10176 // | | window's safe area inset top) | |
10177 // +--+-------------------------------+--+ |
10178 // | | | |<------ safe area rectangle of
10179 // | | | | | screen
10180 // +--+-------------------------------+--+ |
10181 // | |window's safe area inset bottom| |
10182 // | +-------------------------------+ |
10183 // +----------------------------------------+
10185 ScreenIntMargin windowSafeAreaInsets;
10187 if (windowSafeAreaInsets == aSafeAreaInsets) {
10188 // no safe area insets.
10189 return windowSafeAreaInsets;
10192 int32_t screenLeft, screenTop, screenWidth, screenHeight;
10193 nsresult rv =
10194 aScreen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
10195 if (NS_WARN_IF(NS_FAILED(rv))) {
10196 return windowSafeAreaInsets;
10199 // Screen's rect of safe area
10200 LayoutDeviceIntRect safeAreaRect(
10201 screenLeft + aSafeAreaInsets.left, screenTop + aSafeAreaInsets.top,
10202 screenWidth - aSafeAreaInsets.right - aSafeAreaInsets.left,
10203 screenHeight - aSafeAreaInsets.bottom - aSafeAreaInsets.top);
10204 // window's rect of safe area
10205 safeAreaRect = safeAreaRect.Intersect(aWindowRect);
10207 windowSafeAreaInsets.top =
10208 aSafeAreaInsets.top ? std::max(safeAreaRect.y - aWindowRect.y, 0) : 0;
10209 windowSafeAreaInsets.left =
10210 aSafeAreaInsets.left ? std::max(safeAreaRect.x - aWindowRect.x, 0) : 0;
10211 windowSafeAreaInsets.right =
10212 aSafeAreaInsets.right
10213 ? std::max((aWindowRect.x + aWindowRect.width) -
10214 (safeAreaRect.x + safeAreaRect.width),
10216 : 0;
10217 windowSafeAreaInsets.bottom =
10218 aSafeAreaInsets.bottom
10219 ? std::max(aWindowRect.y + aWindowRect.height -
10220 (safeAreaRect.y + safeAreaRect.height),
10222 : 0;
10224 return windowSafeAreaInsets;
10227 /* static */
10228 nsContentUtils::SubresourceCacheValidationInfo
10229 nsContentUtils::GetSubresourceCacheValidationInfo(nsIRequest* aRequest) {
10230 SubresourceCacheValidationInfo info;
10231 if (nsCOMPtr<nsICacheInfoChannel> cache = do_QueryInterface(aRequest)) {
10232 uint32_t value = 0;
10233 if (NS_SUCCEEDED(cache->GetCacheTokenExpirationTime(&value))) {
10234 info.mExpirationTime.emplace(value);
10238 // Determine whether the cache entry must be revalidated when we try to use
10239 // it. Currently, only HTTP specifies this information...
10240 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest)) {
10241 Unused << httpChannel->IsNoStoreResponse(&info.mMustRevalidate);
10243 if (!info.mMustRevalidate) {
10244 Unused << httpChannel->IsNoCacheResponse(&info.mMustRevalidate);
10248 return info;