1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* A namespace class for static layout utilities. */
8 
9 #include "nsContentUtils.h"
10 
11 #include <algorithm>
12 #include <cstddef>
13 #include <cstdint>
14 #include <cstdlib>
15 #include <cstring>
16 #include <functional>
17 #include <new>
18 #include <utility>
19 #include "BrowserChild.h"
20 #include "DecoderTraits.h"
21 #include "ErrorList.h"
22 #include "ImageOps.h"
23 #include "InProcessBrowserChildMessageManager.h"
24 #include "Layers.h"
25 #include "MainThreadUtils.h"
26 #include "PLDHashTable.h"
27 #include "ReferrerInfo.h"
28 #include "ThirdPartyUtil.h"
29 #include "Units.h"
30 #include "chrome/common/ipc_message.h"
31 #include "gfxDrawable.h"
32 #include "harfbuzz/hb.h"
33 #include "imgICache.h"
34 #include "imgIContainer.h"
35 #include "imgILoader.h"
36 #include "imgIRequest.h"
37 #include "imgLoader.h"
38 #include "js/Array.h"
39 #include "js/ArrayBuffer.h"
40 #include "js/BuildId.h"
41 #include "js/GCAPI.h"
42 #include "js/Id.h"
43 #include "js/JSON.h"
44 #include "js/PropertyDescriptor.h"
45 #include "js/Realm.h"
46 #include "js/RegExp.h"
47 #include "js/RegExpFlags.h"
48 #include "js/RootingAPI.h"
49 #include "js/TypeDecls.h"
50 #include "js/Value.h"
51 #include "js/Wrapper.h"
52 #include "jsapi.h"
53 #include "jsfriendapi.h"
54 #include "mozAutoDocUpdate.h"
55 #include "mozIDOMWindow.h"
56 #include "mozilla/AlreadyAddRefed.h"
57 #include "mozilla/ArrayIterator.h"
58 #include "mozilla/ArrayUtils.h"
59 #include "mozilla/AsyncEventDispatcher.h"
60 #include "mozilla/AtomArray.h"
61 #include "mozilla/Atomics.h"
62 #include "mozilla/Attributes.h"
63 #include "mozilla/AutoRestore.h"
64 #include "mozilla/AutoTimelineMarker.h"
65 #include "mozilla/BackgroundHangMonitor.h"
66 #include "mozilla/Base64.h"
67 #include "mozilla/BasePrincipal.h"
68 #include "mozilla/BasicEvents.h"
69 #include "mozilla/BloomFilter.h"
70 #include "mozilla/CORSMode.h"
71 #include "mozilla/CallState.h"
72 #include "mozilla/CheckedInt.h"
73 #include "mozilla/Components.h"
74 #include "mozilla/CycleCollectedJSContext.h"
75 #include "mozilla/DOMEventTargetHelper.h"
76 #include "mozilla/DebugOnly.h"
77 #include "mozilla/ErrorResult.h"
78 #include "mozilla/EventDispatcher.h"
79 #include "mozilla/EventListenerManager.h"
80 #include "mozilla/EventQueue.h"
81 #include "mozilla/EventStateManager.h"
82 #include "mozilla/FlushType.h"
83 #include "mozilla/HTMLEditor.h"
84 #include "mozilla/HangAnnotations.h"
85 #include "mozilla/IMEStateManager.h"
86 #include "mozilla/InputEventOptions.h"
87 #include "mozilla/InternalMutationEvent.h"
88 #include "mozilla/Latin1.h"
89 #include "mozilla/Likely.h"
90 #include "mozilla/LoadInfo.h"
91 #include "mozilla/Logging.h"
92 #include "mozilla/MacroForEach.h"
93 #include "mozilla/ManualNAC.h"
94 #include "mozilla/Maybe.h"
95 #include "mozilla/MouseEvents.h"
96 #include "mozilla/NotNull.h"
97 #include "mozilla/NullPrincipal.h"
98 #include "mozilla/OriginAttributes.h"
99 #include "mozilla/Preferences.h"
100 #include "mozilla/PresShell.h"
101 #include "mozilla/RangeBoundary.h"
102 #include "mozilla/RefPtr.h"
103 #include "mozilla/Result.h"
104 #include "mozilla/ResultExtensions.h"
105 #include "mozilla/ScrollbarPreferences.h"
106 #include "mozilla/Span.h"
107 #include "mozilla/StaticAnalysisFunctions.h"
108 #include "mozilla/StaticPrefs_dom.h"
109 #ifdef FUZZING
110 #  include "mozilla/StaticPrefs_fuzzing.h"
111 #endif
112 #include "mozilla/StaticPrefs_privacy.h"
113 #include "mozilla/StaticPrefs_test.h"
114 #include "mozilla/StaticPrefs_ui.h"
115 #include "mozilla/StaticPtr.h"
116 #include "mozilla/TaskCategory.h"
117 #include "mozilla/TextControlState.h"
118 #include "mozilla/TextEditor.h"
119 #include "mozilla/TextEvents.h"
120 #include "mozilla/UniquePtr.h"
121 #include "mozilla/Unused.h"
122 #include "mozilla/Variant.h"
123 #include "mozilla/ViewportUtils.h"
124 #include "mozilla/dom/AncestorIterator.h"
125 #include "mozilla/dom/AutoEntryScript.h"
126 #include "mozilla/dom/AutocompleteInfoBinding.h"
127 #include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
128 #include "mozilla/dom/BindingDeclarations.h"
129 #include "mozilla/dom/BindingUtils.h"
130 #include "mozilla/dom/BlobImpl.h"
131 #include "mozilla/dom/BlobURLProtocolHandler.h"
132 #include "mozilla/dom/BorrowedAttrInfo.h"
133 #include "mozilla/dom/BrowserBridgeParent.h"
134 #include "mozilla/dom/BrowserParent.h"
135 #include "mozilla/dom/BrowsingContext.h"
136 #include "mozilla/dom/BrowsingContextGroup.h"
137 #include "mozilla/dom/CallbackFunction.h"
138 #include "mozilla/dom/CallbackObject.h"
139 #include "mozilla/dom/ChromeMessageBroadcaster.h"
140 #include "mozilla/dom/ContentChild.h"
141 #include "mozilla/dom/ContentFrameMessageManager.h"
142 #include "mozilla/dom/ContentParent.h"
143 #include "mozilla/dom/CustomElementRegistry.h"
144 #include "mozilla/dom/CustomElementRegistryBinding.h"
145 #include "mozilla/dom/DOMArena.h"
146 #include "mozilla/dom/DOMException.h"
147 #include "mozilla/dom/DOMExceptionBinding.h"
148 #include "mozilla/dom/DOMSecurityMonitor.h"
149 #include "mozilla/dom/DOMTypes.h"
150 #include "mozilla/dom/DataTransfer.h"
151 #include "mozilla/dom/DocGroup.h"
152 #include "mozilla/dom/Document.h"
153 #include "mozilla/dom/DocumentFragment.h"
154 #include "mozilla/dom/DocumentInlines.h"
155 #include "mozilla/dom/Element.h"
156 #include "mozilla/dom/ElementBinding.h"
157 #include "mozilla/dom/ElementInlines.h"
158 #include "mozilla/dom/Event.h"
159 #include "mozilla/dom/EventTarget.h"
160 #include "mozilla/dom/FileBlobImpl.h"
161 #include "mozilla/dom/FileSystemSecurity.h"
162 #include "mozilla/dom/FilteredNodeIterator.h"
163 #include "mozilla/dom/FontTableURIProtocolHandler.h"
164 #include "mozilla/dom/FragmentOrElement.h"
165 #include "mozilla/dom/FromParser.h"
166 #include "mozilla/dom/HTMLFormElement.h"
167 #include "mozilla/dom/HTMLInputElement.h"
168 #include "mozilla/dom/HTMLTextAreaElement.h"
169 #include "mozilla/dom/IPCBlob.h"
170 #include "mozilla/dom/IPCBlobUtils.h"
171 #include "mozilla/dom/MessageBroadcaster.h"
172 #include "mozilla/dom/MessageListenerManager.h"
173 #include "mozilla/dom/MouseEventBinding.h"
174 #include "mozilla/dom/NameSpaceConstants.h"
175 #include "mozilla/dom/NodeBinding.h"
176 #include "mozilla/dom/NodeInfo.h"
177 #include "mozilla/dom/PBrowser.h"
178 #include "mozilla/dom/PContentChild.h"
179 #include "mozilla/dom/PrototypeList.h"
180 #include "mozilla/dom/ReferrerPolicyBinding.h"
181 #include "mozilla/dom/ScriptSettings.h"
182 #include "mozilla/dom/Selection.h"
183 #include "mozilla/dom/ShadowRoot.h"
184 #include "mozilla/dom/Text.h"
185 #include "mozilla/dom/UserActivation.h"
186 #include "mozilla/dom/WindowContext.h"
187 #include "mozilla/dom/WorkerCommon.h"
188 #include "mozilla/dom/WorkerPrivate.h"
189 #include "mozilla/dom/XULCommandEvent.h"
190 #include "mozilla/fallible.h"
191 #include "mozilla/gfx/2D.h"
192 #include "mozilla/gfx/BaseMargin.h"
193 #include "mozilla/gfx/BasePoint.h"
194 #include "mozilla/gfx/BaseSize.h"
195 #include "mozilla/gfx/DataSurfaceHelpers.h"
196 #include "mozilla/gfx/Point.h"
197 #include "mozilla/gfx/Rect.h"
198 #include "mozilla/gfx/Types.h"
199 #include "mozilla/intl/LineBreaker.h"
200 #include "mozilla/intl/WordBreaker.h"
201 #include "mozilla/ipc/ProtocolUtils.h"
202 #include "mozilla/ipc/SharedMemory.h"
203 #include "mozilla/ipc/Shmem.h"
204 #include "mozilla/net/UrlClassifierCommon.h"
205 #include "mozilla/widget/IMEData.h"
206 #include "nsAboutProtocolUtils.h"
207 #include "nsAlgorithm.h"
208 #include "nsArrayUtils.h"
209 #include "nsAtom.h"
210 #include "nsAttrName.h"
211 #include "nsAttrValue.h"
212 #include "nsAttrValueInlines.h"
213 #include "nsBaseHashtable.h"
214 #include "nsCCUncollectableMarker.h"
215 #include "nsCOMPtr.h"
216 #include "nsCRT.h"
217 #include "nsCRTGlue.h"
218 #include "nsCanvasFrame.h"
219 #include "nsCaseTreatment.h"
220 #include "nsCharSeparatedTokenizer.h"
221 #include "nsCharTraits.h"
222 #include "nsCompatibility.h"
223 #include "nsComponentManagerUtils.h"
224 #include "nsContainerFrame.h"
225 #include "nsContentCreatorFunctions.h"
226 #include "nsContentDLF.h"
227 #include "nsContentList.h"
228 #include "nsContentListDeclarations.h"
229 #include "nsContentPolicyUtils.h"
230 #include "nsCoord.h"
231 #include "nsCycleCollectionNoteChild.h"
232 #include "nsDOMMutationObserver.h"
233 #include "nsDOMString.h"
234 #include "nsTHashMap.h"
235 #include "nsDebug.h"
236 #include "nsDocShell.h"
237 #include "nsDocShellCID.h"
238 #include "nsError.h"
239 #include "nsFocusManager.h"
240 #include "nsFrameList.h"
241 #include "nsFrameLoader.h"
242 #include "nsFrameLoaderOwner.h"
243 #include "nsGenericHTMLElement.h"
244 #include "nsGkAtoms.h"
245 #include "nsGlobalWindowInner.h"
246 #include "nsGlobalWindowOuter.h"
247 #include "nsHTMLDocument.h"
248 #include "nsHTMLTags.h"
249 #include "nsHashKeys.h"
250 #include "nsHtml5StringParser.h"
251 #include "nsIAboutModule.h"
252 #include "nsIAnonymousContentCreator.h"
253 #include "nsIArray.h"
254 #include "nsIAsyncVerifyRedirectCallback.h"
255 #include "nsIBidiKeyboard.h"
256 #include "nsIBrowser.h"
257 #include "nsICacheInfoChannel.h"
258 #include "nsICategoryManager.h"
259 #include "nsIChannel.h"
260 #include "nsIChannelEventSink.h"
261 #include "nsIClassifiedChannel.h"
262 #include "nsIConsoleService.h"
263 #include "nsIContent.h"
264 #include "nsIContentInlines.h"
265 #include "nsIContentPolicy.h"
266 #include "nsIContentSecurityPolicy.h"
267 #include "nsIContentSink.h"
268 #include "nsIContentViewer.h"
269 #include "nsID.h"
270 #include "nsIDOMWindowUtils.h"
271 #include "nsIDocShell.h"
272 #include "nsIDocShellTreeItem.h"
273 #include "nsIDocumentEncoder.h"
274 #include "nsIDocumentLoaderFactory.h"
275 #include "nsIDragService.h"
276 #include "nsIDragSession.h"
277 #include "nsIFile.h"
278 #include "nsIFocusManager.h"
279 #include "nsIFormControl.h"
280 #include "nsIFragmentContentSink.h"
281 #include "nsIFrame.h"
282 #include "nsIGlobalObject.h"
283 #include "nsIHttpChannel.h"
284 #include "nsIHttpChannelInternal.h"
285 #include "nsIIOService.h"
286 #include "nsIImageLoadingContent.h"
287 #include "nsIInputStream.h"
288 #include "nsIInterfaceRequestor.h"
289 #include "nsIInterfaceRequestorUtils.h"
290 #include "nsILoadContext.h"
291 #include "nsILoadGroup.h"
292 #include "nsILoadInfo.h"
293 #include "nsIMIMEService.h"
294 #include "nsIMemoryReporter.h"
295 #include "nsINetUtil.h"
296 #include "nsINode.h"
297 #include "nsIObjectLoadingContent.h"
298 #include "nsIObserver.h"
299 #include "nsIObserverService.h"
300 #include "nsIParser.h"
301 #include "nsIParserUtils.h"
302 #include "nsIPermissionManager.h"
303 #include "nsIPluginTag.h"
304 #include "nsIPrincipal.h"
305 #include "nsIProperties.h"
306 #include "nsIProtocolHandler.h"
307 #include "nsIRequest.h"
308 #include "nsIRunnable.h"
309 #include "nsIScreen.h"
310 #include "nsIScriptError.h"
311 #include "nsIScriptGlobalObject.h"
312 #include "nsIScriptObjectPrincipal.h"
313 #include "nsIScriptSecurityManager.h"
314 #include "nsISerialEventTarget.h"
315 #include "nsIStreamConverter.h"
316 #include "nsIStreamConverterService.h"
317 #include "nsIStringBundle.h"
318 #include "nsISupports.h"
319 #include "nsISupportsPrimitives.h"
320 #include "nsISupportsUtils.h"
321 #include "nsITransferable.h"
322 #include "nsIURI.h"
323 #include "nsIURIMutator.h"
324 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
325 #  include "nsIURIWithSpecialOrigin.h"
326 #endif
327 #include "nsIUUIDGenerator.h"
328 #include "nsIUserIdleService.h"
329 #include "nsIWeakReferenceUtils.h"
330 #include "nsIWebNavigation.h"
331 #include "nsIWebNavigationInfo.h"
332 #include "nsIWidget.h"
333 #include "nsIWindowMediator.h"
334 #include "nsIXPConnect.h"
335 #include "nsJSPrincipals.h"
336 #include "nsJSUtils.h"
337 #include "nsLayoutUtils.h"
338 #include "nsLiteralString.h"
339 #include "nsMappedAttributes.h"
340 #include "nsMargin.h"
341 #include "nsMimeTypes.h"
342 #include "nsNameSpaceManager.h"
343 #include "nsNetCID.h"
344 #include "nsNetUtil.h"
345 #include "nsNodeInfoManager.h"
346 #include "nsPIDOMWindow.h"
347 #include "nsPIDOMWindowInlines.h"
348 #include "nsParserCIID.h"
349 #include "nsParserConstants.h"
350 #include "nsPluginHost.h"
351 #include "nsPoint.h"
352 #include "nsPointerHashKeys.h"
353 #include "nsPresContext.h"
354 #include "nsQueryFrame.h"
355 #include "nsQueryObject.h"
356 #include "nsRFPService.h"
357 #include "nsRange.h"
358 #include "nsRefPtrHashtable.h"
359 #include "nsSandboxFlags.h"
360 #include "nsScriptSecurityManager.h"
361 #include "nsServiceManagerUtils.h"
362 #include "nsStreamUtils.h"
363 #include "nsString.h"
364 #include "nsStringBuffer.h"
365 #include "nsStringFlags.h"
366 #include "nsStringFwd.h"
367 #include "nsStringIterator.h"
368 #include "nsTArray.h"
369 #include "nsTLiteralString.h"
370 #include "nsTPromiseFlatString.h"
371 #include "nsTStringRepr.h"
372 #include "nsTextFragment.h"
373 #include "nsTextNode.h"
374 #include "nsThreadManager.h"
375 #include "nsThreadUtils.h"
376 #include "nsTreeSanitizer.h"
377 #include "nsUGenCategory.h"
378 #include "nsURLHelper.h"
379 #include "nsUnicodeProperties.h"
380 #include "nsView.h"
381 #include "nsViewManager.h"
382 #include "nsXPCOM.h"
383 #include "nsXPCOMCID.h"
384 #include "nsXULAppAPI.h"
385 #include "nsXULElement.h"
386 #include "nsXULPopupManager.h"
387 #include "nscore.h"
388 #include "prinrval.h"
389 #include "xpcprivate.h"
390 #include "xpcpublic.h"
391 
392 #if defined(XP_WIN)
393 // Undefine LoadImage to prevent naming conflict with Windows.
394 #  undef LoadImage
395 #endif
396 
397 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
398                                       const char** next, char16_t* result);
399 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end, int ns_aware,
400                                  const char** colon);
401 
402 using namespace mozilla::dom;
403 using namespace mozilla::ipc;
404 using namespace mozilla::gfx;
405 using namespace mozilla::layers;
406 using namespace mozilla::widget;
407 using namespace mozilla;
408 
409 const char kLoadAsData[] = "loadAsData";
410 
411 nsIXPConnect* nsContentUtils::sXPConnect;
412 nsIScriptSecurityManager* nsContentUtils::sSecurityManager;
413 nsIPrincipal* nsContentUtils::sSystemPrincipal;
414 nsIPrincipal* nsContentUtils::sNullSubjectPrincipal;
415 nsNameSpaceManager* nsContentUtils::sNameSpaceManager;
416 nsIIOService* nsContentUtils::sIOService;
417 nsIUUIDGenerator* nsContentUtils::sUUIDGenerator;
418 nsIConsoleService* nsContentUtils::sConsoleService;
419 nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>*
420     nsContentUtils::sAtomEventTable = nullptr;
421 nsTHashMap<nsStringHashKey, EventNameMapping>*
422     nsContentUtils::sStringEventTable = nullptr;
423 nsTArray<RefPtr<nsAtom>>* nsContentUtils::sUserDefinedEvents = nullptr;
424 nsIStringBundleService* nsContentUtils::sStringBundleService;
425 nsIStringBundle* nsContentUtils::sStringBundles[PropertiesFile_COUNT];
426 nsIContentPolicy* nsContentUtils::sContentPolicyService;
427 bool nsContentUtils::sTriedToGetContentPolicy = false;
428 RefPtr<mozilla::intl::LineBreaker> nsContentUtils::sLineBreaker;
429 RefPtr<mozilla::intl::WordBreaker> nsContentUtils::sWordBreaker;
430 StaticRefPtr<nsIBidiKeyboard> nsContentUtils::sBidiKeyboard;
431 uint32_t nsContentUtils::sScriptBlockerCount = 0;
432 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
433 AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners =
434     nullptr;
435 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
436 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
437 
438 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
439 
440 nsString* nsContentUtils::sShiftText = nullptr;
441 nsString* nsContentUtils::sControlText = nullptr;
442 nsString* nsContentUtils::sMetaText = nullptr;
443 nsString* nsContentUtils::sOSText = nullptr;
444 nsString* nsContentUtils::sAltText = nullptr;
445 nsString* nsContentUtils::sModifierSeparator = nullptr;
446 
447 bool nsContentUtils::sInitialized = false;
448 #ifndef RELEASE_OR_BETA
449 bool nsContentUtils::sBypassCSSOMOriginCheck = false;
450 #endif
451 
452 nsCString* nsContentUtils::sJSBytecodeMimeType = nullptr;
453 
454 nsContentUtils::UserInteractionObserver*
455     nsContentUtils::sUserInteractionObserver = nullptr;
456 
457 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
458 nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
459 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
460 bool nsContentUtils::sFragmentParsingActive = false;
461 
462 mozilla::LazyLogModule nsContentUtils::gResistFingerprintingLog(
463     "nsResistFingerprinting");
464 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
465 
466 int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
467 uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
468 
469 template Maybe<int32_t> nsContentUtils::ComparePoints(
470     const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary);
471 template Maybe<int32_t> nsContentUtils::ComparePoints(
472     const RangeBoundary& aFirstBoundary,
473     const RawRangeBoundary& aSecondBoundary);
474 template Maybe<int32_t> nsContentUtils::ComparePoints(
475     const RawRangeBoundary& aFirstBoundary,
476     const RangeBoundary& aSecondBoundary);
477 template Maybe<int32_t> nsContentUtils::ComparePoints(
478     const RawRangeBoundary& aFirstBoundary,
479     const RawRangeBoundary& aSecondBoundary);
480 
481 template int32_t nsContentUtils::ComparePoints_Deprecated(
482     const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
483     bool* aDisconnected);
484 template int32_t nsContentUtils::ComparePoints_Deprecated(
485     const RangeBoundary& aFirstBoundary,
486     const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
487 template int32_t nsContentUtils::ComparePoints_Deprecated(
488     const RawRangeBoundary& aFirstBoundary,
489     const RangeBoundary& aSecondBoundary, bool* aDisconnected);
490 template int32_t nsContentUtils::ComparePoints_Deprecated(
491     const RawRangeBoundary& aFirstBoundary,
492     const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
493 
494 // Subset of
495 // http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
496 enum AutocompleteUnsupportedFieldName : uint8_t {
497 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
498   eAutocompleteUnsupportedFieldName_##name_,
499 #include "AutocompleteFieldList.h"
500 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
501 };
502 
503 enum AutocompleteNoPersistFieldName : uint8_t {
504 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
505   eAutocompleteNoPersistFieldName_##name_,
506 #include "AutocompleteFieldList.h"
507 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
508 };
509 
510 enum AutocompleteUnsupportFieldContactHint : uint8_t {
511 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
512   eAutocompleteUnsupportedFieldContactHint_##name_,
513 #include "AutocompleteFieldList.h"
514 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
515 };
516 
517 enum AutocompleteFieldName : uint8_t {
518 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) eAutocompleteFieldName_##name_,
519 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
520   AUTOCOMPLETE_FIELD_NAME(name_, value_)
521 #include "AutocompleteFieldList.h"
522 #undef AUTOCOMPLETE_FIELD_NAME
523 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
524 };
525 
526 enum AutocompleteFieldHint : uint8_t {
527 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) eAutocompleteFieldHint_##name_,
528 #include "AutocompleteFieldList.h"
529 #undef AUTOCOMPLETE_FIELD_HINT
530 };
531 
532 enum AutocompleteFieldContactHint : uint8_t {
533 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
534   eAutocompleteFieldContactHint_##name_,
535 #include "AutocompleteFieldList.h"
536 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
537 };
538 
539 enum AutocompleteCategory {
540 #define AUTOCOMPLETE_CATEGORY(name_, value_) eAutocompleteCategory_##name_,
541 #include "AutocompleteFieldList.h"
542 #undef AUTOCOMPLETE_CATEGORY
543 };
544 
545 static const nsAttrValue::EnumTable kAutocompleteUnsupportedFieldNameTable[] = {
546 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
547   {value_, eAutocompleteUnsupportedFieldName_##name_},
548 #include "AutocompleteFieldList.h"
549 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
550     {nullptr, 0}};
551 
552 static const nsAttrValue::EnumTable kAutocompleteNoPersistFieldNameTable[] = {
553 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
554   {value_, eAutocompleteNoPersistFieldName_##name_},
555 #include "AutocompleteFieldList.h"
556 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
557     {nullptr, 0}};
558 
559 static const nsAttrValue::EnumTable
560     kAutocompleteUnsupportedContactFieldHintTable[] = {
561 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
562   {value_, eAutocompleteUnsupportedFieldContactHint_##name_},
563 #include "AutocompleteFieldList.h"
564 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
565         {nullptr, 0}};
566 
567 static const nsAttrValue::EnumTable kAutocompleteFieldNameTable[] = {
568 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
569   {value_, eAutocompleteFieldName_##name_},
570 #include "AutocompleteFieldList.h"
571 #undef AUTOCOMPLETE_FIELD_NAME
572     {nullptr, 0}};
573 
574 static const nsAttrValue::EnumTable kAutocompleteContactFieldNameTable[] = {
575 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
576   {value_, eAutocompleteFieldName_##name_},
577 #include "AutocompleteFieldList.h"
578 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
579     {nullptr, 0}};
580 
581 static const nsAttrValue::EnumTable kAutocompleteFieldHintTable[] = {
582 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
583   {value_, eAutocompleteFieldHint_##name_},
584 #include "AutocompleteFieldList.h"
585 #undef AUTOCOMPLETE_FIELD_HINT
586     {nullptr, 0}};
587 
588 static const nsAttrValue::EnumTable kAutocompleteContactFieldHintTable[] = {
589 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
590   {value_, eAutocompleteFieldContactHint_##name_},
591 #include "AutocompleteFieldList.h"
592 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
593     {nullptr, 0}};
594 
595 namespace {
596 
597 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
598 
599 static PLDHashTable* sEventListenerManagersHash;
600 
601 // A global hashtable to for keeping the arena alive for cross docGroup node
602 // adoption.
603 static nsRefPtrHashtable<nsPtrHashKey<const nsINode>, mozilla::dom::DOMArena>*
604     sDOMArenaHashtable;
605 
606 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter {
607   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
608 
609   ~DOMEventListenerManagersHashReporter() = default;
610 
611  public:
612   NS_DECL_ISUPPORTS
613 
CollectReports(nsIHandleReportCallback * aHandleReport,nsISupports * aData,bool aAnonymize)614   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
615                             nsISupports* aData, bool aAnonymize) override {
616     // We don't measure the |EventListenerManager| objects pointed to by the
617     // entries because those references are non-owning.
618     int64_t amount =
619         sEventListenerManagersHash
620             ? sEventListenerManagersHash->ShallowSizeOfIncludingThis(
621                   MallocSizeOf)
622             : 0;
623 
624     MOZ_COLLECT_REPORT(
625         "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
626         amount, "Memory used by the event listener manager's hash table.");
627 
628     return NS_OK;
629   }
630 };
631 
632 NS_IMPL_ISUPPORTS(DOMEventListenerManagersHashReporter, nsIMemoryReporter)
633 
634 class EventListenerManagerMapEntry : public PLDHashEntryHdr {
635  public:
EventListenerManagerMapEntry(const void * aKey)636   explicit EventListenerManagerMapEntry(const void* aKey) : mKey(aKey) {}
637 
~EventListenerManagerMapEntry()638   ~EventListenerManagerMapEntry() {
639     NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
640   }
641 
642  protected:          // declared protected to silence clang warnings
643   const void* mKey;  // must be first, to look like PLDHashEntryStub
644 
645  public:
646   RefPtr<EventListenerManager> mListenerManager;
647 };
648 
EventListenerManagerHashInitEntry(PLDHashEntryHdr * entry,const void * key)649 static void EventListenerManagerHashInitEntry(PLDHashEntryHdr* entry,
650                                               const void* key) {
651   // Initialize the entry with placement new
652   new (entry) EventListenerManagerMapEntry(key);
653 }
654 
EventListenerManagerHashClearEntry(PLDHashTable * table,PLDHashEntryHdr * entry)655 static void EventListenerManagerHashClearEntry(PLDHashTable* table,
656                                                PLDHashEntryHdr* entry) {
657   EventListenerManagerMapEntry* lm =
658       static_cast<EventListenerManagerMapEntry*>(entry);
659 
660   // Let the EventListenerManagerMapEntry clean itself up...
661   lm->~EventListenerManagerMapEntry();
662 }
663 
664 class SameOriginCheckerImpl final : public nsIChannelEventSink,
665                                     public nsIInterfaceRequestor {
666   ~SameOriginCheckerImpl() = default;
667 
668   NS_DECL_ISUPPORTS
669   NS_DECL_NSICHANNELEVENTSINK
670   NS_DECL_NSIINTERFACEREQUESTOR
671 };
672 
673 }  // namespace
674 
SuppressDocument(Document * aDoc)675 void AutoSuppressEventHandlingAndSuspend::SuppressDocument(Document* aDoc) {
676   // Note: Document::SuppressEventHandling will also automatically suppress
677   // event handling for any in-process sub-documents. However, since we need
678   // to deal with cases where remote BrowsingContexts may be interleaved
679   // with in-process ones, we still need to walk the entire tree ourselves.
680   // This may be slightly redundant in some cases, but since event handling
681   // suppressions maintain a count of current blockers, it does not cause
682   // any problems.
683   aDoc->SuppressEventHandling();
684   if (nsCOMPtr<nsPIDOMWindowInner> win = aDoc->GetInnerWindow()) {
685     win->Suspend();
686     mWindows.AppendElement(win);
687   }
688 }
689 
UnsuppressDocument(Document * aDoc)690 void AutoSuppressEventHandlingAndSuspend::UnsuppressDocument(Document* aDoc) {
691   aDoc->UnsuppressEventHandlingAndFireEvents(true);
692 }
693 
~AutoSuppressEventHandlingAndSuspend()694 AutoSuppressEventHandlingAndSuspend::~AutoSuppressEventHandlingAndSuspend() {
695   for (const auto& win : mWindows) {
696     win->Resume();
697   }
698   UnsuppressDocuments();
699 }
700 
701 /**
702  * This class is used to determine whether or not the user is currently
703  * interacting with the browser. It listens to observer events to toggle the
704  * value of the sUserActive static.
705  *
706  * This class is an internal implementation detail.
707  * nsContentUtils::GetUserIsInteracting() should be used to access current
708  * user interaction status.
709  */
710 class nsContentUtils::UserInteractionObserver final
711     : public nsIObserver,
712       public BackgroundHangAnnotator {
713  public:
714   NS_DECL_ISUPPORTS
715   NS_DECL_NSIOBSERVER
716 
717   void Init();
718   void Shutdown();
719   void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
720 
721   static Atomic<bool> sUserActive;
722 
723  private:
724   ~UserInteractionObserver() = default;
725 };
726 
727 // static
Init()728 nsresult nsContentUtils::Init() {
729   if (sInitialized) {
730     NS_WARNING("Init() called twice");
731 
732     return NS_OK;
733   }
734 
735   nsHTMLTags::AddRefTable();
736 
737   sNameSpaceManager = nsNameSpaceManager::GetInstance();
738   NS_ENSURE_TRUE(sNameSpaceManager, NS_ERROR_OUT_OF_MEMORY);
739   NS_ADDREF(sNameSpaceManager);
740 
741   sXPConnect = nsXPConnect::XPConnect();
742   // We hold a strong ref to sXPConnect to ensure that it does not go away until
743   // nsLayoutStatics::Shutdown is happening.  Otherwise ~nsXPConnect can be
744   // triggered by xpcModuleDtor late in shutdown and cause crashes due to
745   // various stuff already being torn down by then.  Note that this means that
746   // we are effectively making sure that if we leak nsLayoutStatics then we also
747   // leak nsXPConnect.
748   NS_ADDREF(sXPConnect);
749 
750   sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
751   if (!sSecurityManager) return NS_ERROR_FAILURE;
752   NS_ADDREF(sSecurityManager);
753 
754   sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
755   MOZ_ASSERT(sSystemPrincipal);
756 
757   RefPtr<NullPrincipal> nullPrincipal =
758       NullPrincipal::CreateWithoutOriginAttributes();
759   if (!nullPrincipal) {
760     return NS_ERROR_FAILURE;
761   }
762 
763   nullPrincipal.forget(&sNullSubjectPrincipal);
764 
765   nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
766   if (NS_FAILED(rv)) {
767     // This makes life easier, but we can live without it.
768 
769     sIOService = nullptr;
770   }
771 
772   sLineBreaker = mozilla::intl::LineBreaker::Create();
773 
774   sWordBreaker = mozilla::intl::WordBreaker::Create();
775 
776   if (!InitializeEventTable()) return NS_ERROR_FAILURE;
777 
778   if (!sEventListenerManagersHash) {
779     static const PLDHashTableOps hash_table_ops = {
780         PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
781         PLDHashTable::MoveEntryStub, EventListenerManagerHashClearEntry,
782         EventListenerManagerHashInitEntry};
783 
784     sEventListenerManagersHash =
785         new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
786 
787     RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
788   }
789 
790   sBlockedScriptRunners = new AutoTArray<nsCOMPtr<nsIRunnable>, 8>;
791 
792 #ifndef RELEASE_OR_BETA
793   sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
794 #endif
795 
796   Element::InitCCCallbacks();
797 
798   Unused << nsRFPService::GetOrCreate();
799 
800   nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
801       do_GetService("@mozilla.org/uuid-generator;1", &rv);
802   if (NS_WARN_IF(NS_FAILED(rv))) {
803     return rv;
804   }
805   uuidGenerator.forget(&sUUIDGenerator);
806 
807   if (XRE_IsParentProcess()) {
808     AsyncPrecreateStringBundles();
809   }
810 
811   RefPtr<UserInteractionObserver> uio = new UserInteractionObserver();
812   uio->Init();
813   uio.forget(&sUserInteractionObserver);
814 
815   sInitialized = true;
816 
817   return NS_OK;
818 }
819 
InitJSBytecodeMimeType()820 bool nsContentUtils::InitJSBytecodeMimeType() {
821   MOZ_ASSERT(NS_IsMainThread());
822   MOZ_ASSERT(!sJSBytecodeMimeType);
823 
824   JS::BuildIdCharVector jsBuildId;
825   if (!JS::GetScriptTranscodingBuildId(&jsBuildId)) {
826     return false;
827   }
828 
829   nsDependentCSubstring jsBuildIdStr(jsBuildId.begin(), jsBuildId.length());
830   sJSBytecodeMimeType =
831       new nsCString("javascript/moz-bytecode-"_ns + jsBuildIdStr);
832   return true;
833 }
834 
GetShiftText(nsAString & text)835 void nsContentUtils::GetShiftText(nsAString& text) {
836   if (!sShiftText) InitializeModifierStrings();
837   text.Assign(*sShiftText);
838 }
839 
GetControlText(nsAString & text)840 void nsContentUtils::GetControlText(nsAString& text) {
841   if (!sControlText) InitializeModifierStrings();
842   text.Assign(*sControlText);
843 }
844 
GetMetaText(nsAString & text)845 void nsContentUtils::GetMetaText(nsAString& text) {
846   if (!sMetaText) InitializeModifierStrings();
847   text.Assign(*sMetaText);
848 }
849 
GetOSText(nsAString & text)850 void nsContentUtils::GetOSText(nsAString& text) {
851   if (!sOSText) {
852     InitializeModifierStrings();
853   }
854   text.Assign(*sOSText);
855 }
856 
GetAltText(nsAString & text)857 void nsContentUtils::GetAltText(nsAString& text) {
858   if (!sAltText) InitializeModifierStrings();
859   text.Assign(*sAltText);
860 }
861 
GetModifierSeparatorText(nsAString & text)862 void nsContentUtils::GetModifierSeparatorText(nsAString& text) {
863   if (!sModifierSeparator) InitializeModifierStrings();
864   text.Assign(*sModifierSeparator);
865 }
866 
InitializeModifierStrings()867 void nsContentUtils::InitializeModifierStrings() {
868   // load the display strings for the keyboard accelerators
869   nsCOMPtr<nsIStringBundleService> bundleService =
870       mozilla::components::StringBundle::Service();
871   nsCOMPtr<nsIStringBundle> bundle;
872   DebugOnly<nsresult> rv = NS_OK;
873   if (bundleService) {
874     rv = bundleService->CreateBundle(
875         "chrome://global-platform/locale/platformKeys.properties",
876         getter_AddRefs(bundle));
877   }
878 
879   NS_ASSERTION(
880       NS_SUCCEEDED(rv) && bundle,
881       "chrome://global/locale/platformKeys.properties could not be loaded");
882   nsAutoString shiftModifier;
883   nsAutoString metaModifier;
884   nsAutoString osModifier;
885   nsAutoString altModifier;
886   nsAutoString controlModifier;
887   nsAutoString modifierSeparator;
888   if (bundle) {
889     // macs use symbols for each modifier key, so fetch each from the bundle,
890     // which also covers i18n
891     bundle->GetStringFromName("VK_SHIFT", shiftModifier);
892     bundle->GetStringFromName("VK_META", metaModifier);
893     bundle->GetStringFromName("VK_WIN", osModifier);
894     bundle->GetStringFromName("VK_ALT", altModifier);
895     bundle->GetStringFromName("VK_CONTROL", controlModifier);
896     bundle->GetStringFromName("MODIFIER_SEPARATOR", modifierSeparator);
897   }
898   // if any of these don't exist, we get  an empty string
899   sShiftText = new nsString(shiftModifier);
900   sMetaText = new nsString(metaModifier);
901   sOSText = new nsString(osModifier);
902   sAltText = new nsString(altModifier);
903   sControlText = new nsString(controlModifier);
904   sModifierSeparator = new nsString(modifierSeparator);
905 }
906 
GetEventClassIDFromMessage(EventMessage aEventMessage)907 mozilla::EventClassID nsContentUtils::GetEventClassIDFromMessage(
908     EventMessage aEventMessage) {
909   switch (aEventMessage) {
910 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
911   case message_:                                          \
912     return struct_;
913 #include "mozilla/EventNameList.h"
914 #undef MESSAGE_TO_EVENT
915     default:
916       MOZ_ASSERT_UNREACHABLE("Invalid event message?");
917       return eBasicEventClass;
918   }
919 }
920 
GetEventTypeFromMessage(EventMessage aEventMessage)921 static nsAtom* GetEventTypeFromMessage(EventMessage aEventMessage) {
922   switch (aEventMessage) {
923 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
924   case message_:                                          \
925     return nsGkAtoms::on##name_;
926 #include "mozilla/EventNameList.h"
927 #undef MESSAGE_TO_EVENT
928     default:
929       return nullptr;
930   }
931 }
932 
933 // Because of SVG/SMIL we have several atoms mapped to the same
934 // id, but we can rely on MESSAGE_TO_EVENT to map id to only one atom.
ShouldAddEventToStringEventTable(const EventNameMapping & aMapping)935 static bool ShouldAddEventToStringEventTable(const EventNameMapping& aMapping) {
936   MOZ_ASSERT(aMapping.mAtom);
937   return GetEventTypeFromMessage(aMapping.mMessage) == aMapping.mAtom;
938 }
939 
InitializeEventTable()940 bool nsContentUtils::InitializeEventTable() {
941   NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
942   NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
943 
944   static const EventNameMapping eventArray[] = {
945 #define EVENT(name_, _message, _type, _class) \
946   {nsGkAtoms::on##name_, _type, _message, _class, false},
947 #define WINDOW_ONLY_EVENT EVENT
948 #define DOCUMENT_ONLY_EVENT EVENT
949 #define NON_IDL_EVENT EVENT
950 #include "mozilla/EventNameList.h"
951 #undef WINDOW_ONLY_EVENT
952 #undef NON_IDL_EVENT
953 #undef EVENT
954       {nullptr}};
955 
956   sAtomEventTable = new nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>(
957       ArrayLength(eventArray));
958   sStringEventTable = new nsTHashMap<nsStringHashKey, EventNameMapping>(
959       ArrayLength(eventArray));
960   sUserDefinedEvents = new nsTArray<RefPtr<nsAtom>>(64);
961 
962   // Subtract one from the length because of the trailing null
963   for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) {
964     MOZ_ASSERT(!sAtomEventTable->Contains(eventArray[i].mAtom),
965                "Double-defining event name; fix your EventNameList.h");
966     sAtomEventTable->InsertOrUpdate(eventArray[i].mAtom, eventArray[i]);
967     if (ShouldAddEventToStringEventTable(eventArray[i])) {
968       sStringEventTable->InsertOrUpdate(
969           Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
970           eventArray[i]);
971     }
972   }
973 
974   return true;
975 }
976 
InitializeTouchEventTable()977 void nsContentUtils::InitializeTouchEventTable() {
978   static bool sEventTableInitialized = false;
979   if (!sEventTableInitialized && sAtomEventTable && sStringEventTable) {
980     sEventTableInitialized = true;
981     static const EventNameMapping touchEventArray[] = {
982 #define EVENT(name_, _message, _type, _class)
983 #define TOUCH_EVENT(name_, _message, _type, _class) \
984   {nsGkAtoms::on##name_, _type, _message, _class},
985 #include "mozilla/EventNameList.h"
986 #undef TOUCH_EVENT
987 #undef EVENT
988         {nullptr}};
989     // Subtract one from the length because of the trailing null
990     for (uint32_t i = 0; i < ArrayLength(touchEventArray) - 1; ++i) {
991       sAtomEventTable->InsertOrUpdate(touchEventArray[i].mAtom,
992                                       touchEventArray[i]);
993       sStringEventTable->InsertOrUpdate(
994           Substring(nsDependentAtomString(touchEventArray[i].mAtom), 2),
995           touchEventArray[i]);
996     }
997   }
998 }
999 
Is8bit(const nsAString & aString)1000 static bool Is8bit(const nsAString& aString) {
1001   static const char16_t EIGHT_BIT = char16_t(~0x00FF);
1002 
1003   for (nsAString::const_char_iterator start = aString.BeginReading(),
1004                                       end = aString.EndReading();
1005        start != end; ++start) {
1006     if (*start & EIGHT_BIT) {
1007       return false;
1008     }
1009   }
1010 
1011   return true;
1012 }
1013 
Btoa(const nsAString & aBinaryData,nsAString & aAsciiBase64String)1014 nsresult nsContentUtils::Btoa(const nsAString& aBinaryData,
1015                               nsAString& aAsciiBase64String) {
1016   if (!Is8bit(aBinaryData)) {
1017     aAsciiBase64String.Truncate();
1018     return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1019   }
1020 
1021   return Base64Encode(aBinaryData, aAsciiBase64String);
1022 }
1023 
Atob(const nsAString & aAsciiBase64String,nsAString & aBinaryData)1024 nsresult nsContentUtils::Atob(const nsAString& aAsciiBase64String,
1025                               nsAString& aBinaryData) {
1026   if (!Is8bit(aAsciiBase64String)) {
1027     aBinaryData.Truncate();
1028     return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1029   }
1030 
1031   const char16_t* start = aAsciiBase64String.BeginReading();
1032   const char16_t* cur = start;
1033   const char16_t* end = aAsciiBase64String.EndReading();
1034   bool hasWhitespace = false;
1035 
1036   while (cur < end) {
1037     if (nsContentUtils::IsHTMLWhitespace(*cur)) {
1038       hasWhitespace = true;
1039       break;
1040     }
1041     cur++;
1042   }
1043 
1044   nsresult rv;
1045 
1046   if (hasWhitespace) {
1047     nsString trimmedString;
1048 
1049     if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible)) {
1050       return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1051     }
1052 
1053     trimmedString.Append(start, cur - start);
1054 
1055     while (cur < end) {
1056       if (!nsContentUtils::IsHTMLWhitespace(*cur)) {
1057         trimmedString.Append(*cur);
1058       }
1059       cur++;
1060     }
1061     rv = Base64Decode(trimmedString, aBinaryData);
1062   } else {
1063     rv = Base64Decode(aAsciiBase64String, aBinaryData);
1064   }
1065 
1066   if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
1067     return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1068   }
1069   return rv;
1070 }
1071 
IsAutocompleteEnabled(mozilla::dom::HTMLInputElement * aInput)1072 bool nsContentUtils::IsAutocompleteEnabled(
1073     mozilla::dom::HTMLInputElement* aInput) {
1074   MOZ_ASSERT(aInput, "aInput should not be null!");
1075 
1076   nsAutoString autocomplete;
1077   aInput->GetAutocomplete(autocomplete);
1078 
1079   if (autocomplete.IsEmpty()) {
1080     auto* form = aInput->GetForm();
1081     if (!form) {
1082       return true;
1083     }
1084 
1085     form->GetAutocomplete(autocomplete);
1086   }
1087 
1088   return !autocomplete.EqualsLiteral("off");
1089 }
1090 
1091 nsContentUtils::AutocompleteAttrState
SerializeAutocompleteAttribute(const nsAttrValue * aAttr,nsAString & aResult,AutocompleteAttrState aCachedState)1092 nsContentUtils::SerializeAutocompleteAttribute(
1093     const nsAttrValue* aAttr, nsAString& aResult,
1094     AutocompleteAttrState aCachedState) {
1095   if (!aAttr ||
1096       aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1097     return aCachedState;
1098   }
1099 
1100   if (aCachedState == nsContentUtils::eAutocompleteAttrState_Valid) {
1101     uint32_t atomCount = aAttr->GetAtomCount();
1102     for (uint32_t i = 0; i < atomCount; i++) {
1103       if (i != 0) {
1104         aResult.Append(' ');
1105       }
1106       aResult.Append(nsDependentAtomString(aAttr->AtomAt(i)));
1107     }
1108     nsContentUtils::ASCIIToLower(aResult);
1109     return aCachedState;
1110   }
1111 
1112   aResult.Truncate();
1113 
1114   mozilla::dom::AutocompleteInfo info;
1115   AutocompleteAttrState state =
1116       InternalSerializeAutocompleteAttribute(aAttr, info);
1117   if (state == eAutocompleteAttrState_Valid) {
1118     // Concatenate the info fields.
1119     aResult = info.mSection;
1120 
1121     if (!info.mAddressType.IsEmpty()) {
1122       if (!aResult.IsEmpty()) {
1123         aResult += ' ';
1124       }
1125       aResult += info.mAddressType;
1126     }
1127 
1128     if (!info.mContactType.IsEmpty()) {
1129       if (!aResult.IsEmpty()) {
1130         aResult += ' ';
1131       }
1132       aResult += info.mContactType;
1133     }
1134 
1135     if (!info.mFieldName.IsEmpty()) {
1136       if (!aResult.IsEmpty()) {
1137         aResult += ' ';
1138       }
1139       aResult += info.mFieldName;
1140     }
1141   }
1142 
1143   return state;
1144 }
1145 
1146 nsContentUtils::AutocompleteAttrState
SerializeAutocompleteAttribute(const nsAttrValue * aAttr,mozilla::dom::AutocompleteInfo & aInfo,AutocompleteAttrState aCachedState,bool aGrantAllValidValue)1147 nsContentUtils::SerializeAutocompleteAttribute(
1148     const nsAttrValue* aAttr, mozilla::dom::AutocompleteInfo& aInfo,
1149     AutocompleteAttrState aCachedState, bool aGrantAllValidValue) {
1150   if (!aAttr ||
1151       aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1152     return aCachedState;
1153   }
1154 
1155   return InternalSerializeAutocompleteAttribute(aAttr, aInfo,
1156                                                 aGrantAllValidValue);
1157 }
1158 
1159 /**
1160  * Helper to validate the @autocomplete tokens.
1161  *
1162  * @return {AutocompleteAttrState} The state of the attribute (invalid/valid).
1163  */
1164 nsContentUtils::AutocompleteAttrState
InternalSerializeAutocompleteAttribute(const nsAttrValue * aAttrVal,mozilla::dom::AutocompleteInfo & aInfo,bool aGrantAllValidValue)1165 nsContentUtils::InternalSerializeAutocompleteAttribute(
1166     const nsAttrValue* aAttrVal, mozilla::dom::AutocompleteInfo& aInfo,
1167     bool aGrantAllValidValue) {
1168   // No autocomplete attribute so we are done
1169   if (!aAttrVal) {
1170     return eAutocompleteAttrState_Invalid;
1171   }
1172 
1173   uint32_t numTokens = aAttrVal->GetAtomCount();
1174   if (!numTokens) {
1175     return eAutocompleteAttrState_Invalid;
1176   }
1177 
1178   uint32_t index = numTokens - 1;
1179   nsString tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1180   AutocompleteCategory category;
1181   nsAttrValue enumValue;
1182 
1183   bool unsupported = false;
1184   if (!aGrantAllValidValue) {
1185     unsupported = enumValue.ParseEnumValue(
1186         tokenString, kAutocompleteUnsupportedFieldNameTable, false);
1187     if (unsupported) {
1188       return eAutocompleteAttrState_Invalid;
1189     }
1190   }
1191 
1192   nsAutoString str;
1193   bool result =
1194       enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
1195   if (result) {
1196     // Off/Automatic/Normal categories.
1197     if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
1198         enumValue.Equals(u"on"_ns, eIgnoreCase)) {
1199       if (numTokens > 1) {
1200         return eAutocompleteAttrState_Invalid;
1201       }
1202       enumValue.ToString(str);
1203       ASCIIToLower(str);
1204       aInfo.mFieldName.Assign(str);
1205       aInfo.mCanAutomaticallyPersist =
1206           !enumValue.Equals(u"off"_ns, eIgnoreCase);
1207       return eAutocompleteAttrState_Valid;
1208     }
1209 
1210     // Only allow on/off if form autofill @autocomplete values aren't enabled
1211     // and it doesn't grant all valid values.
1212     if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1213         !aGrantAllValidValue) {
1214       return eAutocompleteAttrState_Invalid;
1215     }
1216 
1217     // Normal category
1218     if (numTokens > 3) {
1219       return eAutocompleteAttrState_Invalid;
1220     }
1221     category = eAutocompleteCategory_NORMAL;
1222   } else {  // Check if the last token is of the contact category instead.
1223     // Only allow on/off if form autofill @autocomplete values aren't enabled
1224     // and it doesn't grant all valid values.
1225     if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1226         !aGrantAllValidValue) {
1227       return eAutocompleteAttrState_Invalid;
1228     }
1229 
1230     result = enumValue.ParseEnumValue(
1231         tokenString, kAutocompleteContactFieldNameTable, false);
1232     if (!result || numTokens > 4) {
1233       return eAutocompleteAttrState_Invalid;
1234     }
1235 
1236     category = eAutocompleteCategory_CONTACT;
1237   }
1238 
1239   enumValue.ToString(str);
1240   ASCIIToLower(str);
1241   aInfo.mFieldName.Assign(str);
1242 
1243   aInfo.mCanAutomaticallyPersist = !enumValue.ParseEnumValue(
1244       tokenString, kAutocompleteNoPersistFieldNameTable, false);
1245 
1246   // We are done if this was the only token.
1247   if (numTokens == 1) {
1248     return eAutocompleteAttrState_Valid;
1249   }
1250 
1251   --index;
1252   tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1253 
1254   if (category == eAutocompleteCategory_CONTACT) {
1255     if (!aGrantAllValidValue) {
1256       unsupported = enumValue.ParseEnumValue(
1257           tokenString, kAutocompleteUnsupportedContactFieldHintTable, false);
1258       if (unsupported) {
1259         return eAutocompleteAttrState_Invalid;
1260       }
1261     }
1262 
1263     nsAttrValue contactFieldHint;
1264     result = contactFieldHint.ParseEnumValue(
1265         tokenString, kAutocompleteContactFieldHintTable, false);
1266     if (result) {
1267       nsAutoString contactFieldHintString;
1268       contactFieldHint.ToString(contactFieldHintString);
1269       ASCIIToLower(contactFieldHintString);
1270       aInfo.mContactType.Assign(contactFieldHintString);
1271       if (index == 0) {
1272         return eAutocompleteAttrState_Valid;
1273       }
1274       --index;
1275       tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1276     }
1277   }
1278 
1279   // Check for billing/shipping tokens
1280   nsAttrValue fieldHint;
1281   if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable,
1282                                false)) {
1283     nsString fieldHintString;
1284     fieldHint.ToString(fieldHintString);
1285     ASCIIToLower(fieldHintString);
1286     aInfo.mAddressType.Assign(fieldHintString);
1287     if (index == 0) {
1288       return eAutocompleteAttrState_Valid;
1289     }
1290     --index;
1291     tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1292   }
1293 
1294   // Check for section-* token
1295   const nsDependentSubstring& section = Substring(tokenString, 0, 8);
1296   if (section.LowerCaseEqualsASCII("section-")) {
1297     ASCIIToLower(tokenString);
1298     aInfo.mSection.Assign(tokenString);
1299     if (index == 0) {
1300       return eAutocompleteAttrState_Valid;
1301     }
1302   }
1303 
1304   // Clear the fields as the autocomplete attribute is invalid.
1305   aInfo.mSection.Truncate();
1306   aInfo.mAddressType.Truncate();
1307   aInfo.mContactType.Truncate();
1308   aInfo.mFieldName.Truncate();
1309 
1310   return eAutocompleteAttrState_Invalid;
1311 }
1312 
1313 // Parse an integer according to HTML spec
1314 template <class StringT>
ParseHTMLIntegerImpl(const StringT & aValue,ParseHTMLIntegerResultFlags * aResult)1315 int32_t nsContentUtils::ParseHTMLIntegerImpl(
1316     const StringT& aValue, ParseHTMLIntegerResultFlags* aResult) {
1317   using CharT = typename StringT::char_type;
1318 
1319   int result = eParseHTMLInteger_NoFlags;
1320 
1321   typename StringT::const_iterator iter, end;
1322   aValue.BeginReading(iter);
1323   aValue.EndReading(end);
1324 
1325   while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1326     result |= eParseHTMLInteger_NonStandard;
1327     ++iter;
1328   }
1329 
1330   if (iter == end) {
1331     result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1332     *aResult = (ParseHTMLIntegerResultFlags)result;
1333     return 0;
1334   }
1335 
1336   int sign = 1;
1337   if (*iter == CharT('-')) {
1338     sign = -1;
1339     result |= eParseHTMLInteger_Negative;
1340     ++iter;
1341   } else if (*iter == CharT('+')) {
1342     result |= eParseHTMLInteger_NonStandard;
1343     ++iter;
1344   }
1345 
1346   bool foundValue = false;
1347   CheckedInt32 value = 0;
1348 
1349   // Check for leading zeros first.
1350   uint64_t leadingZeros = 0;
1351   while (iter != end) {
1352     if (*iter != CharT('0')) {
1353       break;
1354     }
1355 
1356     ++leadingZeros;
1357     foundValue = true;
1358     ++iter;
1359   }
1360 
1361   while (iter != end) {
1362     if (*iter >= CharT('0') && *iter <= CharT('9')) {
1363       value = (value * 10) + (*iter - CharT('0')) * sign;
1364       ++iter;
1365       if (!value.isValid()) {
1366         result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
1367         break;
1368       }
1369       foundValue = true;
1370     } else {
1371       break;
1372     }
1373   }
1374 
1375   if (!foundValue) {
1376     result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1377   }
1378 
1379   if (value.isValid() &&
1380       ((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
1381        (sign == -1 && value == 0))) {
1382     result |= eParseHTMLInteger_NonStandard;
1383   }
1384 
1385   if (iter != end) {
1386     result |= eParseHTMLInteger_DidNotConsumeAllInput;
1387   }
1388 
1389   *aResult = (ParseHTMLIntegerResultFlags)result;
1390   return value.isValid() ? value.value() : 0;
1391 }
1392 
1393 // Parse an integer according to HTML spec
ParseHTMLInteger(const nsAString & aValue,ParseHTMLIntegerResultFlags * aResult)1394 int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
1395                                          ParseHTMLIntegerResultFlags* aResult) {
1396   return ParseHTMLIntegerImpl(aValue, aResult);
1397 }
1398 
ParseHTMLInteger(const nsACString & aValue,ParseHTMLIntegerResultFlags * aResult)1399 int32_t nsContentUtils::ParseHTMLInteger(const nsACString& aValue,
1400                                          ParseHTMLIntegerResultFlags* aResult) {
1401   return ParseHTMLIntegerImpl(aValue, aResult);
1402 }
1403 
1404 #define SKIP_WHITESPACE(iter, end_iter, end_res)                 \
1405   while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
1406     ++(iter);                                                    \
1407   }                                                              \
1408   if ((iter) == (end_iter)) {                                    \
1409     return (end_res);                                            \
1410   }
1411 
1412 #define SKIP_ATTR_NAME(iter, end_iter)                            \
1413   while ((iter) != (end_iter) && !nsCRT::IsAsciiSpace(*(iter)) && \
1414          *(iter) != '=') {                                        \
1415     ++(iter);                                                     \
1416   }
1417 
GetPseudoAttributeValue(const nsString & aSource,nsAtom * aName,nsAString & aValue)1418 bool nsContentUtils::GetPseudoAttributeValue(const nsString& aSource,
1419                                              nsAtom* aName, nsAString& aValue) {
1420   aValue.Truncate();
1421 
1422   const char16_t* start = aSource.get();
1423   const char16_t* end = start + aSource.Length();
1424   const char16_t* iter;
1425 
1426   while (start != end) {
1427     SKIP_WHITESPACE(start, end, false)
1428     iter = start;
1429     SKIP_ATTR_NAME(iter, end)
1430 
1431     if (start == iter) {
1432       return false;
1433     }
1434 
1435     // Remember the attr name.
1436     const nsDependentSubstring& attrName = Substring(start, iter);
1437 
1438     // Now check whether this is a valid name="value" pair.
1439     start = iter;
1440     SKIP_WHITESPACE(start, end, false)
1441     if (*start != '=') {
1442       // No '=', so this is not a name="value" pair.  We don't know
1443       // what it is, and we have no way to handle it.
1444       return false;
1445     }
1446 
1447     // Have to skip the value.
1448     ++start;
1449     SKIP_WHITESPACE(start, end, false)
1450     char16_t q = *start;
1451     if (q != kQuote && q != kApostrophe) {
1452       // Not a valid quoted value, so bail.
1453       return false;
1454     }
1455 
1456     ++start;  // Point to the first char of the value.
1457     iter = start;
1458 
1459     while (iter != end && *iter != q) {
1460       ++iter;
1461     }
1462 
1463     if (iter == end) {
1464       // Oops, unterminated quoted string.
1465       return false;
1466     }
1467 
1468     // At this point attrName holds the name of the "attribute" and
1469     // the value is between start and iter.
1470 
1471     if (aName->Equals(attrName)) {
1472       // We'll accumulate as many characters as possible (until we hit either
1473       // the end of the string or the beginning of an entity). Chunks will be
1474       // delimited by start and chunkEnd.
1475       const char16_t* chunkEnd = start;
1476       while (chunkEnd != iter) {
1477         if (*chunkEnd == kLessThan) {
1478           aValue.Truncate();
1479 
1480           return false;
1481         }
1482 
1483         if (*chunkEnd == kAmpersand) {
1484           aValue.Append(start, chunkEnd - start);
1485 
1486           const char16_t* afterEntity = nullptr;
1487           char16_t result[2];
1488           uint32_t count = MOZ_XMLTranslateEntity(
1489               reinterpret_cast<const char*>(chunkEnd),
1490               reinterpret_cast<const char*>(iter),
1491               reinterpret_cast<const char**>(&afterEntity), result);
1492           if (count == 0) {
1493             aValue.Truncate();
1494 
1495             return false;
1496           }
1497 
1498           aValue.Append(result, count);
1499 
1500           // Advance to after the entity and begin a new chunk.
1501           start = chunkEnd = afterEntity;
1502         } else {
1503           ++chunkEnd;
1504         }
1505       }
1506 
1507       // Append remainder.
1508       aValue.Append(start, iter - start);
1509 
1510       return true;
1511     }
1512 
1513     // Resume scanning after the end of the attribute value (past the quote
1514     // char).
1515     start = iter + 1;
1516   }
1517 
1518   return false;
1519 }
1520 
IsJavaScriptLanguage(const nsString & aName)1521 bool nsContentUtils::IsJavaScriptLanguage(const nsString& aName) {
1522   return aName.LowerCaseEqualsLiteral("javascript") ||
1523          aName.LowerCaseEqualsLiteral("livescript") ||
1524          aName.LowerCaseEqualsLiteral("mocha") ||
1525          aName.LowerCaseEqualsLiteral("javascript1.0") ||
1526          aName.LowerCaseEqualsLiteral("javascript1.1") ||
1527          aName.LowerCaseEqualsLiteral("javascript1.2") ||
1528          aName.LowerCaseEqualsLiteral("javascript1.3") ||
1529          aName.LowerCaseEqualsLiteral("javascript1.4") ||
1530          aName.LowerCaseEqualsLiteral("javascript1.5");
1531 }
1532 
SplitMimeType(const nsAString & aValue,nsString & aType,nsString & aParams)1533 void nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType,
1534                                    nsString& aParams) {
1535   aType.Truncate();
1536   aParams.Truncate();
1537   int32_t semiIndex = aValue.FindChar(char16_t(';'));
1538   if (-1 != semiIndex) {
1539     aType = Substring(aValue, 0, semiIndex);
1540     aParams =
1541         Substring(aValue, semiIndex + 1, aValue.Length() - (semiIndex + 1));
1542     aParams.StripWhitespace();
1543   } else {
1544     aType = aValue;
1545   }
1546   aType.StripWhitespace();
1547 }
1548 
IsUserIdle(uint32_t aRequestedIdleTimeInMS,bool * aUserIsIdle)1549 nsresult nsContentUtils::IsUserIdle(uint32_t aRequestedIdleTimeInMS,
1550                                     bool* aUserIsIdle) {
1551   nsresult rv;
1552   nsCOMPtr<nsIUserIdleService> idleService =
1553       do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
1554   NS_ENSURE_SUCCESS(rv, rv);
1555 
1556   uint32_t idleTimeInMS;
1557   rv = idleService->GetIdleTime(&idleTimeInMS);
1558   NS_ENSURE_SUCCESS(rv, rv);
1559 
1560   *aUserIsIdle = idleTimeInMS >= aRequestedIdleTimeInMS;
1561   return NS_OK;
1562 }
1563 
1564 /**
1565  * A helper function that parses a sandbox attribute (of an <iframe> or a CSP
1566  * directive) and converts it to the set of flags used internally.
1567  *
1568  * @param aSandboxAttr  the sandbox attribute
1569  * @return              the set of flags (SANDBOXED_NONE if aSandboxAttr is
1570  *                      null)
1571  */
ParseSandboxAttributeToFlags(const nsAttrValue * aSandboxAttr)1572 uint32_t nsContentUtils::ParseSandboxAttributeToFlags(
1573     const nsAttrValue* aSandboxAttr) {
1574   if (!aSandboxAttr) {
1575     return SANDBOXED_NONE;
1576   }
1577 
1578   uint32_t out = SANDBOX_ALL_FLAGS;
1579 
1580 #define SANDBOX_KEYWORD(string, atom, flags)                  \
1581   if (aSandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { \
1582     out &= ~(flags);                                          \
1583   }
1584 #include "IframeSandboxKeywordList.h"
1585 #undef SANDBOX_KEYWORD
1586 
1587   return out;
1588 }
1589 
1590 /**
1591  * A helper function that checks if a string matches a valid sandbox flag.
1592  *
1593  * @param aFlag   the potential sandbox flag.
1594  * @return        true if the flag is a sandbox flag.
1595  */
IsValidSandboxFlag(const nsAString & aFlag)1596 bool nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag) {
1597 #define SANDBOX_KEYWORD(string, atom, flags)                                  \
1598   if (EqualsIgnoreASCIICase(nsDependentAtomString(nsGkAtoms::atom), aFlag)) { \
1599     return true;                                                              \
1600   }
1601 #include "IframeSandboxKeywordList.h"
1602 #undef SANDBOX_KEYWORD
1603   return false;
1604 }
1605 
1606 /**
1607  * A helper function that returns a string attribute corresponding to the
1608  * sandbox flags.
1609  *
1610  * @param aFlags    the sandbox flags
1611  * @param aString   the attribute corresponding to the flags (null if aFlags
1612  *                  is zero)
1613  */
SandboxFlagsToString(uint32_t aFlags,nsAString & aString)1614 void nsContentUtils::SandboxFlagsToString(uint32_t aFlags, nsAString& aString) {
1615   if (!aFlags) {
1616     SetDOMStringToNull(aString);
1617     return;
1618   }
1619 
1620   aString.Truncate();
1621 
1622 #define SANDBOX_KEYWORD(string, atom, flags)                \
1623   if (!(aFlags & (flags))) {                                \
1624     if (!aString.IsEmpty()) {                               \
1625       aString.AppendLiteral(u" ");                          \
1626     }                                                       \
1627     aString.Append(nsDependentAtomString(nsGkAtoms::atom)); \
1628   }
1629 #include "IframeSandboxKeywordList.h"
1630 #undef SANDBOX_KEYWORD
1631 }
1632 
GetBidiKeyboard()1633 nsIBidiKeyboard* nsContentUtils::GetBidiKeyboard() {
1634   if (!sBidiKeyboard) {
1635     sBidiKeyboard = nsIWidget::CreateBidiKeyboard();
1636   }
1637   return sBidiKeyboard;
1638 }
1639 
1640 /**
1641  * This is used to determine whether a character is in one of the classes
1642  * which CSS says should be part of the first-letter.  Currently, that is
1643  * all punctuation classes (P*).  Note that this is a change from CSS2
1644  * which excluded Pc and Pd.
1645  *
1646  * https://www.w3.org/TR/css-pseudo-4/#first-letter-pseudo
1647  * "Punctuation (i.e, characters that belong to the Punctuation (P*) Unicode
1648  *  general category [UAX44]) [...]"
1649  */
1650 
1651 // static
IsFirstLetterPunctuation(uint32_t aChar)1652 bool nsContentUtils::IsFirstLetterPunctuation(uint32_t aChar) {
1653   switch (mozilla::unicode::GetGeneralCategory(aChar)) {
1654     case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
1655     case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION:    /* Pd */
1656     case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION:   /* Pe */
1657     case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION:   /* Pf */
1658     case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
1659     case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION:   /* Po */
1660     case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION:    /* Ps */
1661       return true;
1662     default:
1663       return false;
1664   }
1665 }
1666 
1667 // static
IsAlphanumeric(uint32_t aChar)1668 bool nsContentUtils::IsAlphanumeric(uint32_t aChar) {
1669   nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1670 
1671   return (cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber);
1672 }
1673 
1674 // static
IsAlphanumericOrSymbol(uint32_t aChar)1675 bool nsContentUtils::IsAlphanumericOrSymbol(uint32_t aChar) {
1676   nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1677 
1678   return cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber ||
1679          cat == nsUGenCategory::kSymbol;
1680 }
1681 
1682 // static
IsAlphanumericOrSymbolAt(const nsTextFragment * aFrag,uint32_t aOffset)1683 bool nsContentUtils::IsAlphanumericOrSymbolAt(const nsTextFragment* aFrag,
1684                                               uint32_t aOffset) {
1685   char16_t h = aFrag->CharAt(aOffset);
1686   if (!IS_SURROGATE(h)) {
1687     return IsAlphanumericOrSymbol(h);
1688   }
1689   if (NS_IS_HIGH_SURROGATE(h) && aOffset + 1 < aFrag->GetLength()) {
1690     char16_t l = aFrag->CharAt(aOffset + 1);
1691     if (NS_IS_LOW_SURROGATE(l)) {
1692       return IsAlphanumericOrSymbol(SURROGATE_TO_UCS4(h, l));
1693     }
1694   }
1695   return false;
1696 }
1697 
1698 /* static */
IsHTMLWhitespace(char16_t aChar)1699 bool nsContentUtils::IsHTMLWhitespace(char16_t aChar) {
1700   return aChar == char16_t(0x0009) || aChar == char16_t(0x000A) ||
1701          aChar == char16_t(0x000C) || aChar == char16_t(0x000D) ||
1702          aChar == char16_t(0x0020);
1703 }
1704 
1705 /* static */
IsHTMLWhitespaceOrNBSP(char16_t aChar)1706 bool nsContentUtils::IsHTMLWhitespaceOrNBSP(char16_t aChar) {
1707   return IsHTMLWhitespace(aChar) || aChar == char16_t(0xA0);
1708 }
1709 
1710 /* static */
IsHTMLBlockLevelElement(nsIContent * aContent)1711 bool nsContentUtils::IsHTMLBlockLevelElement(nsIContent* aContent) {
1712   return aContent->IsAnyOfHTMLElements(
1713       nsGkAtoms::address, nsGkAtoms::article, nsGkAtoms::aside,
1714       nsGkAtoms::blockquote, nsGkAtoms::center, nsGkAtoms::dir, nsGkAtoms::div,
1715       nsGkAtoms::dl,  // XXX why not dt and dd?
1716       nsGkAtoms::fieldset,
1717       nsGkAtoms::figure,  // XXX shouldn't figcaption be on this list
1718       nsGkAtoms::footer, nsGkAtoms::form, nsGkAtoms::h1, nsGkAtoms::h2,
1719       nsGkAtoms::h3, nsGkAtoms::h4, nsGkAtoms::h5, nsGkAtoms::h6,
1720       nsGkAtoms::header, nsGkAtoms::hgroup, nsGkAtoms::hr, nsGkAtoms::li,
1721       nsGkAtoms::listing, nsGkAtoms::menu, nsGkAtoms::nav, nsGkAtoms::ol,
1722       nsGkAtoms::p, nsGkAtoms::pre, nsGkAtoms::section, nsGkAtoms::table,
1723       nsGkAtoms::ul, nsGkAtoms::xmp);
1724 }
1725 
1726 /* static */
ParseIntMarginValue(const nsAString & aString,nsIntMargin & result)1727 bool nsContentUtils::ParseIntMarginValue(const nsAString& aString,
1728                                          nsIntMargin& result) {
1729   nsAutoString marginStr(aString);
1730   marginStr.CompressWhitespace(true, true);
1731   if (marginStr.IsEmpty()) {
1732     return false;
1733   }
1734 
1735   int32_t start = 0, end = 0;
1736   for (int count = 0; count < 4; count++) {
1737     if ((uint32_t)end >= marginStr.Length()) return false;
1738 
1739     // top, right, bottom, left
1740     if (count < 3)
1741       end = Substring(marginStr, start).FindChar(',');
1742     else
1743       end = Substring(marginStr, start).Length();
1744 
1745     if (end <= 0) return false;
1746 
1747     nsresult ec;
1748     int32_t val = nsString(Substring(marginStr, start, end)).ToInteger(&ec);
1749     if (NS_FAILED(ec)) return false;
1750 
1751     switch (count) {
1752       case 0:
1753         result.top = val;
1754         break;
1755       case 1:
1756         result.right = val;
1757         break;
1758       case 2:
1759         result.bottom = val;
1760         break;
1761       case 3:
1762         result.left = val;
1763         break;
1764     }
1765     start += end + 1;
1766   }
1767   return true;
1768 }
1769 
1770 // static
ParseLegacyFontSize(const nsAString & aValue)1771 int32_t nsContentUtils::ParseLegacyFontSize(const nsAString& aValue) {
1772   nsAString::const_iterator iter, end;
1773   aValue.BeginReading(iter);
1774   aValue.EndReading(end);
1775 
1776   while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1777     ++iter;
1778   }
1779 
1780   if (iter == end) {
1781     return 0;
1782   }
1783 
1784   bool relative = false;
1785   bool negate = false;
1786   if (*iter == char16_t('-')) {
1787     relative = true;
1788     negate = true;
1789     ++iter;
1790   } else if (*iter == char16_t('+')) {
1791     relative = true;
1792     ++iter;
1793   }
1794 
1795   if (iter == end || *iter < char16_t('0') || *iter > char16_t('9')) {
1796     return 0;
1797   }
1798 
1799   // We don't have to worry about overflow, since we can bail out as soon as
1800   // we're bigger than 7.
1801   int32_t value = 0;
1802   while (iter != end && *iter >= char16_t('0') && *iter <= char16_t('9')) {
1803     value = 10 * value + (*iter - char16_t('0'));
1804     if (value >= 7) {
1805       break;
1806     }
1807     ++iter;
1808   }
1809 
1810   if (relative) {
1811     if (negate) {
1812       value = 3 - value;
1813     } else {
1814       value = 3 + value;
1815     }
1816   }
1817 
1818   return clamped(value, 1, 7);
1819 }
1820 
1821 /* static */
GetOfflineAppManifest(Document * aDocument,nsIURI ** aURI)1822 void nsContentUtils::GetOfflineAppManifest(Document* aDocument, nsIURI** aURI) {
1823   MOZ_ASSERT(NS_IsMainThread());
1824   MOZ_ASSERT(aDocument);
1825   *aURI = nullptr;
1826 
1827   if (aDocument->GetController().isSome()) {
1828     return;
1829   }
1830 
1831   Element* docElement = aDocument->GetRootElement();
1832   if (!docElement) {
1833     return;
1834   }
1835 
1836   nsAutoString manifestSpec;
1837   docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
1838 
1839   // Manifest URIs can't have fragment identifiers.
1840   if (manifestSpec.IsEmpty() || manifestSpec.Contains('#')) {
1841     return;
1842   }
1843 
1844   nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec, aDocument,
1845                                             aDocument->GetDocBaseURI());
1846 }
1847 
1848 /* static */
OfflineAppAllowed(nsIURI * aURI)1849 bool nsContentUtils::OfflineAppAllowed(nsIURI* aURI) { return false; }
1850 
1851 /* static */
OfflineAppAllowed(nsIPrincipal * aPrincipal)1852 bool nsContentUtils::OfflineAppAllowed(nsIPrincipal* aPrincipal) {
1853   return false;
1854 }
1855 // Static
IsErrorPage(nsIURI * aURI)1856 bool nsContentUtils::IsErrorPage(nsIURI* aURI) {
1857   if (!aURI) {
1858     return false;
1859   }
1860 
1861   if (!aURI->SchemeIs("about")) {
1862     return false;
1863   }
1864 
1865   nsAutoCString name;
1866   nsresult rv = NS_GetAboutModuleName(aURI, name);
1867   NS_ENSURE_SUCCESS(rv, false);
1868 
1869   return name.EqualsLiteral("certerror") || name.EqualsLiteral("neterror") ||
1870          name.EqualsLiteral("blocked");
1871 }
1872 
1873 // static
Shutdown()1874 void nsContentUtils::Shutdown() {
1875   sInitialized = false;
1876 
1877   nsHTMLTags::ReleaseTable();
1878 
1879   NS_IF_RELEASE(sContentPolicyService);
1880   sTriedToGetContentPolicy = false;
1881   uint32_t i;
1882   for (i = 0; i < PropertiesFile_COUNT; ++i) NS_IF_RELEASE(sStringBundles[i]);
1883 
1884   NS_IF_RELEASE(sStringBundleService);
1885   NS_IF_RELEASE(sConsoleService);
1886   NS_IF_RELEASE(sXPConnect);
1887   NS_IF_RELEASE(sSecurityManager);
1888   NS_IF_RELEASE(sSystemPrincipal);
1889   NS_IF_RELEASE(sNullSubjectPrincipal);
1890   NS_IF_RELEASE(sIOService);
1891   NS_IF_RELEASE(sUUIDGenerator);
1892   NS_IF_RELEASE(sNameSpaceManager);
1893   sLineBreaker = nullptr;
1894   sWordBreaker = nullptr;
1895   sBidiKeyboard = nullptr;
1896 
1897   delete sAtomEventTable;
1898   sAtomEventTable = nullptr;
1899   delete sStringEventTable;
1900   sStringEventTable = nullptr;
1901   delete sUserDefinedEvents;
1902   sUserDefinedEvents = nullptr;
1903 
1904   if (sEventListenerManagersHash) {
1905     NS_ASSERTION(sEventListenerManagersHash->EntryCount() == 0,
1906                  "Event listener manager hash not empty at shutdown!");
1907 
1908     // See comment above.
1909 
1910     // However, we have to handle this table differently.  If it still
1911     // has entries, we want to leak it too, so that we can keep it alive
1912     // in case any elements are destroyed.  Because if they are, we need
1913     // their event listener managers to be destroyed too, or otherwise
1914     // it could leave dangling references in DOMClassInfo's preserved
1915     // wrapper table.
1916 
1917     if (sEventListenerManagersHash->EntryCount() == 0) {
1918       delete sEventListenerManagersHash;
1919       sEventListenerManagersHash = nullptr;
1920     }
1921   }
1922 
1923   if (sDOMArenaHashtable) {
1924     MOZ_ASSERT(sDOMArenaHashtable->Count() == 0);
1925     MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
1926     delete sDOMArenaHashtable;
1927     sDOMArenaHashtable = nullptr;
1928   }
1929 
1930   NS_ASSERTION(!sBlockedScriptRunners || sBlockedScriptRunners->Length() == 0,
1931                "How'd this happen?");
1932   delete sBlockedScriptRunners;
1933   sBlockedScriptRunners = nullptr;
1934 
1935   delete sShiftText;
1936   sShiftText = nullptr;
1937   delete sControlText;
1938   sControlText = nullptr;
1939   delete sMetaText;
1940   sMetaText = nullptr;
1941   delete sOSText;
1942   sOSText = nullptr;
1943   delete sAltText;
1944   sAltText = nullptr;
1945   delete sModifierSeparator;
1946   sModifierSeparator = nullptr;
1947 
1948   delete sJSBytecodeMimeType;
1949   sJSBytecodeMimeType = nullptr;
1950 
1951   NS_IF_RELEASE(sSameOriginChecker);
1952 
1953   if (sUserInteractionObserver) {
1954     sUserInteractionObserver->Shutdown();
1955     NS_RELEASE(sUserInteractionObserver);
1956   }
1957 
1958   TextControlState::Shutdown();
1959   nsMappedAttributes::Shutdown();
1960 }
1961 
1962 /**
1963  * Checks whether two nodes come from the same origin. aTrustedNode is
1964  * considered 'safe' in that a user can operate on it.
1965  */
1966 // static
CheckSameOrigin(const nsINode * aTrustedNode,const nsINode * unTrustedNode)1967 nsresult nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
1968                                          const nsINode* unTrustedNode) {
1969   MOZ_ASSERT(aTrustedNode);
1970   MOZ_ASSERT(unTrustedNode);
1971 
1972   /*
1973    * Get hold of each node's principal
1974    */
1975 
1976   nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
1977   nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
1978 
1979   if (trustedPrincipal == unTrustedPrincipal) {
1980     return NS_OK;
1981   }
1982 
1983   bool equal;
1984   // XXXbz should we actually have a Subsumes() check here instead?  Or perhaps
1985   // a separate method for that, with callers using one or the other?
1986   if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
1987       !equal) {
1988     return NS_ERROR_DOM_PROP_ACCESS_DENIED;
1989   }
1990 
1991   return NS_OK;
1992 }
1993 
1994 // static
CanCallerAccess(nsIPrincipal * aSubjectPrincipal,nsIPrincipal * aPrincipal)1995 bool nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
1996                                      nsIPrincipal* aPrincipal) {
1997   bool subsumes;
1998   nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
1999   NS_ENSURE_SUCCESS(rv, false);
2000 
2001   if (subsumes) {
2002     return true;
2003   }
2004 
2005   // The subject doesn't subsume aPrincipal. Allow access only if the subject
2006   // is chrome.
2007   return IsCallerChrome();
2008 }
2009 
2010 // static
CanCallerAccess(const nsINode * aNode)2011 bool nsContentUtils::CanCallerAccess(const nsINode* aNode) {
2012   nsIPrincipal* subject = SubjectPrincipal();
2013   if (subject->IsSystemPrincipal()) {
2014     return true;
2015   }
2016 
2017   if (aNode->ChromeOnlyAccess()) {
2018     return false;
2019   }
2020 
2021   return CanCallerAccess(subject, aNode->NodePrincipal());
2022 }
2023 
2024 // static
CanCallerAccess(nsPIDOMWindowInner * aWindow)2025 bool nsContentUtils::CanCallerAccess(nsPIDOMWindowInner* aWindow) {
2026   nsCOMPtr<nsIScriptObjectPrincipal> scriptObject = do_QueryInterface(aWindow);
2027   NS_ENSURE_TRUE(scriptObject, false);
2028 
2029   return CanCallerAccess(SubjectPrincipal(), scriptObject->GetPrincipal());
2030 }
2031 
2032 // static
PrincipalHasPermission(nsIPrincipal & aPrincipal,const nsAtom * aPerm)2033 bool nsContentUtils::PrincipalHasPermission(nsIPrincipal& aPrincipal,
2034                                             const nsAtom* aPerm) {
2035   // Chrome gets access by default.
2036   if (aPrincipal.IsSystemPrincipal()) {
2037     return true;
2038   }
2039 
2040   // Otherwise, only allow if caller is an addon with the permission.
2041   return BasePrincipal::Cast(aPrincipal).AddonHasPermission(aPerm);
2042 }
2043 
2044 // static
CallerHasPermission(JSContext * aCx,const nsAtom * aPerm)2045 bool nsContentUtils::CallerHasPermission(JSContext* aCx, const nsAtom* aPerm) {
2046   return PrincipalHasPermission(*SubjectPrincipal(aCx), aPerm);
2047 }
2048 
2049 // static
GetAttrTriggeringPrincipal(nsIContent * aContent,const nsAString & aAttrValue,nsIPrincipal * aSubjectPrincipal)2050 nsIPrincipal* nsContentUtils::GetAttrTriggeringPrincipal(
2051     nsIContent* aContent, const nsAString& aAttrValue,
2052     nsIPrincipal* aSubjectPrincipal) {
2053   nsIPrincipal* contentPrin = aContent ? aContent->NodePrincipal() : nullptr;
2054 
2055   // If the subject principal is the same as the content principal, or no
2056   // explicit subject principal was provided, we don't need to do any further
2057   // checks. Just return the content principal.
2058   if (contentPrin == aSubjectPrincipal || !aSubjectPrincipal) {
2059     return contentPrin;
2060   }
2061 
2062   // Only use the subject principal if the URL string we are going to end up
2063   // fetching is under the control of that principal, which is never the case
2064   // for relative URLs.
2065   if (aAttrValue.IsEmpty() ||
2066       !IsAbsoluteURL(NS_ConvertUTF16toUTF8(aAttrValue))) {
2067     return contentPrin;
2068   }
2069 
2070   // Only use the subject principal as the attr triggering principal if it
2071   // should override the CSP of the node's principal.
2072   if (BasePrincipal::Cast(aSubjectPrincipal)->OverridesCSP(contentPrin)) {
2073     return aSubjectPrincipal;
2074   }
2075 
2076   return contentPrin;
2077 }
2078 
2079 // static
IsAbsoluteURL(const nsACString & aURL)2080 bool nsContentUtils::IsAbsoluteURL(const nsACString& aURL) {
2081   nsAutoCString scheme;
2082   if (NS_FAILED(net_ExtractURLScheme(aURL, scheme))) {
2083     // If we can't extract a scheme, it's not an absolute URL.
2084     return false;
2085   }
2086 
2087   // If it parses as an absolute StandardURL, it's definitely an absolute URL,
2088   // so no need to check with the IO service.
2089   if (net_IsAbsoluteURL(aURL)) {
2090     return true;
2091   }
2092 
2093   uint32_t flags;
2094   if (NS_SUCCEEDED(sIOService->GetProtocolFlags(scheme.get(), &flags))) {
2095     return flags & nsIProtocolHandler::URI_NORELATIVE;
2096   }
2097 
2098   return false;
2099 }
2100 
2101 // static
InProlog(nsINode * aNode)2102 bool nsContentUtils::InProlog(nsINode* aNode) {
2103   MOZ_ASSERT(aNode, "missing node to nsContentUtils::InProlog");
2104 
2105   nsINode* parent = aNode->GetParentNode();
2106   if (!parent || !parent->IsDocument()) {
2107     return false;
2108   }
2109 
2110   Document* doc = parent->AsDocument();
2111   nsIContent* root = doc->GetRootElement();
2112 
2113   return !root || doc->ComputeIndexOf(aNode) < doc->ComputeIndexOf(root);
2114 }
2115 
IsCallerChrome()2116 bool nsContentUtils::IsCallerChrome() {
2117   MOZ_ASSERT(NS_IsMainThread());
2118   return SubjectPrincipal() == sSystemPrincipal;
2119 }
2120 
2121 #ifdef FUZZING
IsFuzzingEnabled()2122 bool nsContentUtils::IsFuzzingEnabled() {
2123   return StaticPrefs::fuzzing_enabled();
2124 }
2125 #endif
2126 
2127 /* static */
IsCallerChromeOrElementTransformGettersEnabled(JSContext * aCx,JSObject *)2128 bool nsContentUtils::IsCallerChromeOrElementTransformGettersEnabled(
2129     JSContext* aCx, JSObject*) {
2130   return ThreadsafeIsSystemCaller(aCx) ||
2131          StaticPrefs::dom_element_transform_getters_enabled();
2132 }
2133 
2134 /* static */
IsCallerChromeOrErrorPage(JSContext * aCx,JSObject * aObject)2135 bool nsContentUtils::IsCallerChromeOrErrorPage(JSContext* aCx,
2136                                                JSObject* aObject) {
2137   if (ThreadsafeIsSystemCaller(aCx)) {
2138     return true;
2139   }
2140   nsGlobalWindowInner* win =
2141       xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject));
2142   if (!win) {
2143     return false;
2144   }
2145   Document* doc = win->GetExtantDoc();
2146   return doc && IsErrorPage(doc->GetDocumentURI());
2147 }
2148 
2149 /* static */
ShouldResistFingerprinting()2150 bool nsContentUtils::ShouldResistFingerprinting() {
2151   return StaticPrefs::privacy_resistFingerprinting();
2152 }
2153 
ShouldResistFingerprinting(nsIDocShell * aDocShell)2154 bool nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell) {
2155   if (!aDocShell) {
2156     return ShouldResistFingerprinting();
2157   }
2158   return ShouldResistFingerprinting(aDocShell->GetDocument());
2159 }
2160 
2161 /* static */
ShouldResistFingerprinting(const Document * aDoc)2162 bool nsContentUtils::ShouldResistFingerprinting(const Document* aDoc) {
2163   if (!aDoc) {
2164     MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2165             ("Called nsContentUtils::ShouldResistFingerprinting(const "
2166              "Document* aDoc) with NULL document"));
2167     return ShouldResistFingerprinting();
2168   }
2169   bool isChrome = nsContentUtils::IsChromeDoc(aDoc);
2170   if (isChrome) {
2171     return false;
2172   }
2173   return ShouldResistFingerprinting(aDoc->GetChannel());
2174 }
2175 
2176 /* static */
ShouldResistFingerprinting(nsIPrincipal * aPrincipal)2177 bool nsContentUtils::ShouldResistFingerprinting(nsIPrincipal* aPrincipal) {
2178   if (!aPrincipal) {
2179     return ShouldResistFingerprinting();
2180   }
2181   bool isChrome = aPrincipal->IsSystemPrincipal();
2182   return !isChrome && ShouldResistFingerprinting();
2183 }
2184 
2185 /* static */
ShouldResistFingerprinting(WorkerPrivate * aWorkerPrivate)2186 bool nsContentUtils::ShouldResistFingerprinting(WorkerPrivate* aWorkerPrivate) {
2187   if (!aWorkerPrivate) {
2188     // We may be on a non-worker thread!
2189     return ShouldResistFingerprinting();
2190   }
2191   bool isChrome = aWorkerPrivate->UsesSystemPrincipal();
2192   if (isChrome) {
2193     return false;
2194   }
2195   return ShouldResistFingerprinting(aWorkerPrivate->GetDocument());
2196 }
2197 
LogDomainAndPrefList(const char * exemptedDomainsPrefName,nsAutoCString & url,bool isExemptDomain)2198 inline void LogDomainAndPrefList(const char* exemptedDomainsPrefName,
2199                                  nsAutoCString& url, bool isExemptDomain) {
2200   nsAutoCString list;
2201   Preferences::GetCString(exemptedDomainsPrefName, list);
2202   MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2203           ("Domain \"%s\" is %s the exempt list \"%s\"",
2204            PromiseFlatCString(url).get(), isExemptDomain ? "in" : "NOT in",
2205            PromiseFlatCString(list).get()));
2206 }
2207 
2208 /* static */
ShouldResistFingerprinting(nsIChannel * aChannel)2209 bool nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel) {
2210   const unsigned int webExtensionExemptMask = 0x01;
2211   const unsigned int nonPBMExemptMask = 0x02;
2212   const unsigned int specificDomainsExemptMask = 0x04;
2213 
2214   if (!ShouldResistFingerprinting()) {
2215     return false;
2216   }
2217 
2218   if (!aChannel) {
2219     MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2220             ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2221              "aChannel) with NULL channel"));
2222     return true;
2223   }
2224 
2225   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
2226 
2227   if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2228       nonPBMExemptMask) {
2229     // if non-PBM exempt mask is true, exempt non-PBM channels.
2230     if (loadInfo->GetOriginAttributes().mPrivateBrowsingId == 0) {
2231       return false;
2232     }
2233   }
2234 
2235   bool isExemptDomain = false;
2236   const char* exemptedDomainsPrefName =
2237       "privacy.resistFingerprinting.exemptedDomains";
2238 
2239   if (loadInfo->GetExternalContentPolicyType() ==
2240       ExtContentPolicy::TYPE_DOCUMENT) {
2241     // Case 1: Top Level Load
2242 
2243     nsCOMPtr<nsIURI> channelURI;
2244     nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
2245     MOZ_ASSERT(
2246         NS_SUCCEEDED(rv),
2247         "Failed to get URI in "
2248         "nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel)");
2249     // this check is to ensure that we do not crash in non-debug builds.
2250     if (NS_FAILED(rv)) {
2251       return true;
2252     }
2253 
2254     // Exclude internal schemes
2255     if (channelURI->SchemeIs("about") || channelURI->SchemeIs("chrome") ||
2256         channelURI->SchemeIs("resource") ||
2257         channelURI->SchemeIs("view-source")) {
2258       return false;
2259     }
2260 
2261     if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2262         webExtensionExemptMask) {
2263       if (channelURI->SchemeIs("web-extension")) {
2264         return false;
2265       }
2266     }
2267 
2268     if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2269         specificDomainsExemptMask) {
2270       nsAutoCString list;
2271       Preferences::GetCString(exemptedDomainsPrefName, list);
2272       ToLowerCase(list);
2273       isExemptDomain = IsURIInList(channelURI, list);
2274 
2275       if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2276                        mozilla::LogLevel::Debug)) {
2277         nsAutoCString url;
2278         channelURI->GetHost(url);
2279         LogDomainAndPrefList(exemptedDomainsPrefName, url, isExemptDomain);
2280       }
2281     }
2282 
2283     return !isExemptDomain;
2284   }
2285   // Case 2: Subresource Load
2286 
2287   nsIPrincipal* loadingPrincipal = loadInfo->GetLoadingPrincipal();
2288   // If it is not a document load then we are promised that the
2289   // loadingPrincipal is not null.
2290   MOZ_RELEASE_ASSERT(
2291       loadingPrincipal,
2292       "Got NULL loadingPrincipal in "
2293       "nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel)");
2294 
2295   if (loadingPrincipal->IsSystemPrincipal()) {
2296     MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2297             ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2298              "aChannel) with a system loadingPrincipal"));
2299     return false;
2300   }
2301 
2302   if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2303       webExtensionExemptMask) {
2304     if (BasePrincipal::Cast(loadingPrincipal)->AddonPolicy()) {
2305       return false;
2306     }
2307   }
2308 
2309   if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2310       specificDomainsExemptMask) {
2311     loadingPrincipal->IsURIInPrefList(exemptedDomainsPrefName, &isExemptDomain);
2312 
2313     if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2314                      mozilla::LogLevel::Debug)) {
2315       nsAutoCString origin;
2316       loadingPrincipal->GetAsciiOrigin(origin);
2317       LogDomainAndPrefList(exemptedDomainsPrefName, origin, isExemptDomain);
2318     }
2319   }
2320 
2321   return !isExemptDomain;
2322 }
2323 
2324 /* static */
UseStandinsForNativeColors()2325 bool nsContentUtils::UseStandinsForNativeColors() {
2326   return ShouldResistFingerprinting() ||
2327          StaticPrefs::ui_use_standins_for_native_colors();
2328 }
2329 
2330 /* static */
CalcRoundedWindowSizeForResistingFingerprinting(int32_t aChromeWidth,int32_t aChromeHeight,int32_t aScreenWidth,int32_t aScreenHeight,int32_t aInputWidth,int32_t aInputHeight,bool aSetOuterWidth,bool aSetOuterHeight,int32_t * aOutputWidth,int32_t * aOutputHeight)2331 void nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
2332     int32_t aChromeWidth, int32_t aChromeHeight, int32_t aScreenWidth,
2333     int32_t aScreenHeight, int32_t aInputWidth, int32_t aInputHeight,
2334     bool aSetOuterWidth, bool aSetOuterHeight, int32_t* aOutputWidth,
2335     int32_t* aOutputHeight) {
2336   MOZ_ASSERT(aOutputWidth);
2337   MOZ_ASSERT(aOutputHeight);
2338 
2339   int32_t availContentWidth = 0;
2340   int32_t availContentHeight = 0;
2341 
2342   availContentWidth = std::min(StaticPrefs::privacy_window_maxInnerWidth(),
2343                                aScreenWidth - aChromeWidth);
2344 #ifdef MOZ_WIDGET_GTK
2345   // In the GTK window, it will not report outside system decorations
2346   // when we get available window size, see Bug 581863. So, we leave a
2347   // 40 pixels space for them when calculating the available content
2348   // height. It is not necessary for the width since the content width
2349   // is usually pretty much the same as the chrome width.
2350   availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2351                                 (-40 + aScreenHeight) - aChromeHeight);
2352 #else
2353   availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2354                                 aScreenHeight - aChromeHeight);
2355 #endif
2356 
2357   // Ideally, we'd like to round window size to 1000x1000, but the
2358   // screen space could be too small to accommodate this size in some
2359   // cases. If it happens, we would round the window size to the nearest
2360   // 200x100.
2361   availContentWidth = availContentWidth - (availContentWidth % 200);
2362   availContentHeight = availContentHeight - (availContentHeight % 100);
2363 
2364   // If aIsOuter is true, we are setting the outer window. So we
2365   // have to consider the chrome UI.
2366   int32_t chromeOffsetWidth = aSetOuterWidth ? aChromeWidth : 0;
2367   int32_t chromeOffsetHeight = aSetOuterHeight ? aChromeHeight : 0;
2368   int32_t resultWidth = 0, resultHeight = 0;
2369 
2370   // if the original size is greater than the maximum available size, we set
2371   // it to the maximum size. And if the original value is less than the
2372   // minimum rounded size, we set it to the minimum 200x100.
2373   if (aInputWidth > (availContentWidth + chromeOffsetWidth)) {
2374     resultWidth = availContentWidth + chromeOffsetWidth;
2375   } else if (aInputWidth < (200 + chromeOffsetWidth)) {
2376     resultWidth = 200 + chromeOffsetWidth;
2377   } else {
2378     // Otherwise, we round the window to the nearest upper rounded 200x100.
2379     resultWidth = NSToIntCeil((aInputWidth - chromeOffsetWidth) / 200.0) * 200 +
2380                   chromeOffsetWidth;
2381   }
2382 
2383   if (aInputHeight > (availContentHeight + chromeOffsetHeight)) {
2384     resultHeight = availContentHeight + chromeOffsetHeight;
2385   } else if (aInputHeight < (100 + chromeOffsetHeight)) {
2386     resultHeight = 100 + chromeOffsetHeight;
2387   } else {
2388     resultHeight =
2389         NSToIntCeil((aInputHeight - chromeOffsetHeight) / 100.0) * 100 +
2390         chromeOffsetHeight;
2391   }
2392 
2393   *aOutputWidth = resultWidth;
2394   *aOutputHeight = resultHeight;
2395 }
2396 
ThreadsafeIsCallerChrome()2397 bool nsContentUtils::ThreadsafeIsCallerChrome() {
2398   return NS_IsMainThread() ? IsCallerChrome()
2399                            : IsCurrentThreadRunningChromeWorker();
2400 }
2401 
IsCallerUAWidget()2402 bool nsContentUtils::IsCallerUAWidget() {
2403   JSContext* cx = GetCurrentJSContext();
2404   if (!cx) {
2405     return false;
2406   }
2407 
2408   JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
2409   if (!realm) {
2410     return false;
2411   }
2412 
2413   return xpc::IsUAWidgetScope(realm);
2414 }
2415 
IsSystemCaller(JSContext * aCx)2416 bool nsContentUtils::IsSystemCaller(JSContext* aCx) {
2417   // Note that SubjectPrincipal() assumes we are in a compartment here.
2418   return SubjectPrincipal(aCx) == sSystemPrincipal;
2419 }
2420 
ThreadsafeIsSystemCaller(JSContext * aCx)2421 bool nsContentUtils::ThreadsafeIsSystemCaller(JSContext* aCx) {
2422   CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
2423   MOZ_ASSERT(ccjscx->Context() == aCx);
2424 
2425   return ccjscx->IsSystemCaller();
2426 }
2427 
2428 // static
LookupBindingMember(JSContext * aCx,nsIContent * aContent,JS::Handle<jsid> aId,JS::MutableHandle<JS::PropertyDescriptor> aDesc)2429 bool nsContentUtils::LookupBindingMember(
2430     JSContext* aCx, nsIContent* aContent, JS::Handle<jsid> aId,
2431     JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
2432   return true;
2433 }
2434 
GetNearestInProcessCrossDocParentNode(nsINode * aChild)2435 nsINode* nsContentUtils::GetNearestInProcessCrossDocParentNode(
2436     nsINode* aChild) {
2437   if (aChild->IsDocument()) {
2438     for (BrowsingContext* bc = aChild->AsDocument()->GetBrowsingContext(); bc;
2439          bc = bc->GetParent()) {
2440       if (bc->GetEmbedderElement()) {
2441         return bc->GetEmbedderElement();
2442       }
2443     }
2444     return nullptr;
2445   }
2446 
2447   nsINode* parent = aChild->GetParentNode();
2448   if (parent && parent->IsContent() && aChild->IsContent()) {
2449     parent = aChild->AsContent()->GetFlattenedTreeParent();
2450   }
2451 
2452   return parent;
2453 }
2454 
ContentIsHostIncludingDescendantOf(const nsINode * aPossibleDescendant,const nsINode * aPossibleAncestor)2455 bool nsContentUtils::ContentIsHostIncludingDescendantOf(
2456     const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2457   MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2458   MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2459 
2460   do {
2461     if (aPossibleDescendant == aPossibleAncestor) return true;
2462     if (aPossibleDescendant->IsDocumentFragment()) {
2463       aPossibleDescendant =
2464           aPossibleDescendant->AsDocumentFragment()->GetHost();
2465     } else {
2466       aPossibleDescendant = aPossibleDescendant->GetParentNode();
2467     }
2468   } while (aPossibleDescendant);
2469 
2470   return false;
2471 }
2472 
2473 // static
ContentIsCrossDocDescendantOf(nsINode * aPossibleDescendant,nsINode * aPossibleAncestor)2474 bool nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
2475                                                    nsINode* aPossibleAncestor) {
2476   MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2477   MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2478 
2479   do {
2480     if (aPossibleDescendant == aPossibleAncestor) {
2481       return true;
2482     }
2483 
2484     aPossibleDescendant =
2485         GetNearestInProcessCrossDocParentNode(aPossibleDescendant);
2486   } while (aPossibleDescendant);
2487 
2488   return false;
2489 }
2490 
2491 // static
ContentIsFlattenedTreeDescendantOf(const nsINode * aPossibleDescendant,const nsINode * aPossibleAncestor)2492 bool nsContentUtils::ContentIsFlattenedTreeDescendantOf(
2493     const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2494   MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2495   MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2496 
2497   do {
2498     if (aPossibleDescendant == aPossibleAncestor) {
2499       return true;
2500     }
2501     aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
2502   } while (aPossibleDescendant);
2503 
2504   return false;
2505 }
2506 
2507 // static
ContentIsFlattenedTreeDescendantOfForStyle(const nsINode * aPossibleDescendant,const nsINode * aPossibleAncestor)2508 bool nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
2509     const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2510   MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2511   MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2512 
2513   do {
2514     if (aPossibleDescendant == aPossibleAncestor) {
2515       return true;
2516     }
2517     aPossibleDescendant =
2518         aPossibleDescendant->GetFlattenedTreeParentNodeForStyle();
2519   } while (aPossibleDescendant);
2520 
2521   return false;
2522 }
2523 
2524 // static
Retarget(nsINode * aTargetA,nsINode * aTargetB)2525 nsINode* nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB) {
2526   while (true && aTargetA) {
2527     // If A's root is not a shadow root...
2528     nsINode* root = aTargetA->SubtreeRoot();
2529     if (!root->IsShadowRoot()) {
2530       // ...then return A.
2531       return aTargetA;
2532     }
2533 
2534     // or A's root is a shadow-including inclusive ancestor of B...
2535     if (aTargetB->IsShadowIncludingInclusiveDescendantOf(root)) {
2536       // ...then return A.
2537       return aTargetA;
2538     }
2539 
2540     aTargetA = ShadowRoot::FromNode(root)->GetHost();
2541   }
2542 
2543   return nullptr;
2544 }
2545 
2546 // static
GetInclusiveAncestors(nsINode * aNode,nsTArray<nsINode * > & aArray)2547 nsresult nsContentUtils::GetInclusiveAncestors(nsINode* aNode,
2548                                                nsTArray<nsINode*>& aArray) {
2549   while (aNode) {
2550     aArray.AppendElement(aNode);
2551     aNode = aNode->GetParentNode();
2552   }
2553   return NS_OK;
2554 }
2555 
2556 // static
GetInclusiveAncestorsAndOffsets(nsINode * aNode,int32_t aOffset,nsTArray<nsIContent * > * aAncestorNodes,nsTArray<int32_t> * aAncestorOffsets)2557 nsresult nsContentUtils::GetInclusiveAncestorsAndOffsets(
2558     nsINode* aNode, int32_t aOffset, nsTArray<nsIContent*>* aAncestorNodes,
2559     nsTArray<int32_t>* aAncestorOffsets) {
2560   NS_ENSURE_ARG_POINTER(aNode);
2561 
2562   if (!aNode->IsContent()) {
2563     return NS_ERROR_FAILURE;
2564   }
2565   nsIContent* content = aNode->AsContent();
2566 
2567   if (!aAncestorNodes->IsEmpty()) {
2568     NS_WARNING("aAncestorNodes is not empty");
2569     aAncestorNodes->Clear();
2570   }
2571 
2572   if (!aAncestorOffsets->IsEmpty()) {
2573     NS_WARNING("aAncestorOffsets is not empty");
2574     aAncestorOffsets->Clear();
2575   }
2576 
2577   // insert the node itself
2578   aAncestorNodes->AppendElement(content);
2579   aAncestorOffsets->AppendElement(aOffset);
2580 
2581   // insert all the ancestors
2582   nsIContent* child = content;
2583   nsIContent* parent = child->GetParent();
2584   while (parent) {
2585     aAncestorNodes->AppendElement(parent);
2586     aAncestorOffsets->AppendElement(parent->ComputeIndexOf(child));
2587     child = parent;
2588     parent = parent->GetParent();
2589   }
2590 
2591   return NS_OK;
2592 }
2593 
2594 template <typename Node, typename GetParentFunc>
GetCommonAncestorInternal(Node * aNode1,Node * aNode2,GetParentFunc aGetParentFunc)2595 static Node* GetCommonAncestorInternal(Node* aNode1, Node* aNode2,
2596                                        GetParentFunc aGetParentFunc) {
2597   MOZ_ASSERT(aNode1 != aNode2);
2598 
2599   // Build the chain of parents
2600   AutoTArray<Node*, 30> parents1, parents2;
2601   do {
2602     parents1.AppendElement(aNode1);
2603     aNode1 = aGetParentFunc(aNode1);
2604   } while (aNode1);
2605   do {
2606     parents2.AppendElement(aNode2);
2607     aNode2 = aGetParentFunc(aNode2);
2608   } while (aNode2);
2609 
2610   // Find where the parent chain differs
2611   uint32_t pos1 = parents1.Length();
2612   uint32_t pos2 = parents2.Length();
2613   Node* parent = nullptr;
2614   uint32_t len;
2615   for (len = std::min(pos1, pos2); len > 0; --len) {
2616     Node* child1 = parents1.ElementAt(--pos1);
2617     Node* child2 = parents2.ElementAt(--pos2);
2618     if (child1 != child2) {
2619       break;
2620     }
2621     parent = child1;
2622   }
2623 
2624   return parent;
2625 }
2626 
2627 /* static */
GetCommonAncestorHelper(nsINode * aNode1,nsINode * aNode2)2628 nsINode* nsContentUtils::GetCommonAncestorHelper(nsINode* aNode1,
2629                                                  nsINode* aNode2) {
2630   return GetCommonAncestorInternal(
2631       aNode1, aNode2, [](nsINode* aNode) { return aNode->GetParentNode(); });
2632 }
2633 
2634 /* static */
GetCommonFlattenedTreeAncestorHelper(nsIContent * aContent1,nsIContent * aContent2)2635 nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorHelper(
2636     nsIContent* aContent1, nsIContent* aContent2) {
2637   return GetCommonAncestorInternal(
2638       aContent1, aContent2,
2639       [](nsIContent* aContent) { return aContent->GetFlattenedTreeParent(); });
2640 }
2641 
2642 /* static */
GetCommonFlattenedTreeAncestorForStyle(Element * aElement1,Element * aElement2)2643 Element* nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
2644     Element* aElement1, Element* aElement2) {
2645   return GetCommonAncestorInternal(aElement1, aElement2, [](Element* aElement) {
2646     return aElement->GetFlattenedTreeParentElementForStyle();
2647   });
2648 }
2649 
2650 /* static */
PositionIsBefore(nsINode * aNode1,nsINode * aNode2,int32_t * aNode1Index,int32_t * aNode2Index)2651 bool nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2,
2652                                       int32_t* aNode1Index,
2653                                       int32_t* aNode2Index) {
2654   // Note, CompareDocumentPosition takes the latter params in different order.
2655   return (aNode2->CompareDocumentPosition(*aNode1, aNode2Index, aNode1Index) &
2656           (Node_Binding::DOCUMENT_POSITION_PRECEDING |
2657            Node_Binding::DOCUMENT_POSITION_DISCONNECTED)) ==
2658          Node_Binding::DOCUMENT_POSITION_PRECEDING;
2659 }
2660 
2661 /* static */
ComparePoints(const nsINode * aParent1,int32_t aOffset1,const nsINode * aParent2,int32_t aOffset2,ComparePointsCache * aParent1Cache)2662 Maybe<int32_t> nsContentUtils::ComparePoints(
2663     const nsINode* aParent1, int32_t aOffset1, const nsINode* aParent2,
2664     int32_t aOffset2, ComparePointsCache* aParent1Cache) {
2665   bool disconnected{false};
2666 
2667   const int32_t order = ComparePoints_Deprecated(
2668       aParent1, aOffset1, aParent2, aOffset2, &disconnected, aParent1Cache);
2669   if (disconnected) {
2670     return Nothing();
2671   }
2672 
2673   return Some(order);
2674 }
2675 
2676 /* static */
ComparePoints_Deprecated(const nsINode * aParent1,int32_t aOffset1,const nsINode * aParent2,int32_t aOffset2,bool * aDisconnected,ComparePointsCache * aParent1Cache)2677 int32_t nsContentUtils::ComparePoints_Deprecated(
2678     const nsINode* aParent1, int32_t aOffset1, const nsINode* aParent2,
2679     int32_t aOffset2, bool* aDisconnected, ComparePointsCache* aParent1Cache) {
2680   if (aParent1 == aParent2) {
2681     // XXX This is odd.  aOffset1 and/or aOffset2 may be -1, e.g., it's result
2682     //     of nsINode::ComputeIndexOf(), but this compares such invalid
2683     //     offset with valid offset.
2684     return aOffset1 < aOffset2 ? -1 : aOffset1 > aOffset2 ? 1 : 0;
2685   }
2686 
2687   AutoTArray<const nsINode*, 32> parents1, parents2;
2688   const nsINode* node1 = aParent1;
2689   const nsINode* node2 = aParent2;
2690   do {
2691     parents1.AppendElement(node1);
2692     node1 = node1->GetParentNode();
2693   } while (node1);
2694   do {
2695     parents2.AppendElement(node2);
2696     node2 = node2->GetParentNode();
2697   } while (node2);
2698 
2699   uint32_t pos1 = parents1.Length() - 1;
2700   uint32_t pos2 = parents2.Length() - 1;
2701 
2702   bool disconnected = parents1.ElementAt(pos1) != parents2.ElementAt(pos2);
2703   if (aDisconnected) {
2704     *aDisconnected = disconnected;
2705   }
2706   if (disconnected) {
2707     NS_ASSERTION(aDisconnected, "unexpected disconnected nodes");
2708     return 1;
2709   }
2710 
2711   // Find where the parent chains differ
2712   const nsINode* parent = parents1.ElementAt(pos1);
2713   uint32_t len;
2714   for (len = std::min(pos1, pos2); len > 0; --len) {
2715     const nsINode* child1 = parents1.ElementAt(--pos1);
2716     const nsINode* child2 = parents2.ElementAt(--pos2);
2717     if (child1 != child2) {
2718       int32_t child1index = aParent1Cache
2719                                 ? aParent1Cache->ComputeIndexOf(parent, child1)
2720                                 : parent->ComputeIndexOf(child1);
2721       return child1index < parent->ComputeIndexOf(child2) ? -1 : 1;
2722     }
2723     parent = child1;
2724   }
2725 
2726   // The parent chains never differed, so one of the nodes is an ancestor of
2727   // the other
2728 
2729   NS_ASSERTION(!pos1 || !pos2,
2730                "should have run out of parent chain for one of the nodes");
2731 
2732   if (!pos1) {
2733     const nsINode* child2 = parents2.ElementAt(--pos2);
2734     // XXX aOffset1 may be -1 as mentioned above.  So, why does this return
2735     //     it's *before* of the valid DOM point?
2736     return aOffset1 <= parent->ComputeIndexOf(child2) ? -1 : 1;
2737   }
2738 
2739   const nsINode* child1 = parents1.ElementAt(--pos1);
2740   // XXX aOffset2 may be -1 as mentioned above.  So, why does this return it's
2741   //     *after* of the valid DOM point?
2742   int32_t child1index = aParent1Cache
2743                             ? aParent1Cache->ComputeIndexOf(parent, child1)
2744                             : parent->ComputeIndexOf(child1);
2745   return child1index < aOffset2 ? -1 : 1;
2746 }
2747 
2748 // static
GetCommonAncestorUnderInteractiveContent(nsINode * aNode1,nsINode * aNode2)2749 nsINode* nsContentUtils::GetCommonAncestorUnderInteractiveContent(
2750     nsINode* aNode1, nsINode* aNode2) {
2751   if (!aNode1 || !aNode2) {
2752     return nullptr;
2753   }
2754 
2755   if (aNode1 == aNode2) {
2756     return aNode1;
2757   }
2758 
2759   // Build the chain of parents
2760   AutoTArray<nsINode*, 30> parents1;
2761   do {
2762     parents1.AppendElement(aNode1);
2763     if (aNode1->IsElement() &&
2764         aNode1->AsElement()->IsInteractiveHTMLContent()) {
2765       break;
2766     }
2767     aNode1 = aNode1->GetFlattenedTreeParentNode();
2768   } while (aNode1);
2769 
2770   AutoTArray<nsINode*, 30> parents2;
2771   do {
2772     parents2.AppendElement(aNode2);
2773     if (aNode2->IsElement() &&
2774         aNode2->AsElement()->IsInteractiveHTMLContent()) {
2775       break;
2776     }
2777     aNode2 = aNode2->GetFlattenedTreeParentNode();
2778   } while (aNode2);
2779 
2780   // Find where the parent chain differs
2781   uint32_t pos1 = parents1.Length();
2782   uint32_t pos2 = parents2.Length();
2783   nsINode* parent = nullptr;
2784   for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
2785     nsINode* child1 = parents1.ElementAt(--pos1);
2786     nsINode* child2 = parents2.ElementAt(--pos2);
2787     if (child1 != child2) {
2788       break;
2789     }
2790     parent = child1;
2791   }
2792 
2793   return parent;
2794 }
2795 
2796 /* static */
GetCommonBrowserParentAncestor(BrowserParent * aBrowserParent1,BrowserParent * aBrowserParent2)2797 BrowserParent* nsContentUtils::GetCommonBrowserParentAncestor(
2798     BrowserParent* aBrowserParent1, BrowserParent* aBrowserParent2) {
2799   return GetCommonAncestorInternal(
2800       aBrowserParent1, aBrowserParent2, [](BrowserParent* aBrowserParent) {
2801         return aBrowserParent->GetBrowserBridgeParent()
2802                    ? aBrowserParent->GetBrowserBridgeParent()->Manager()
2803                    : nullptr;
2804       });
2805 }
2806 
2807 /* static */
2808 template <typename FPT, typename FRT, typename SPT, typename SRT>
ComparePoints(const RangeBoundaryBase<FPT,FRT> & aFirstBoundary,const RangeBoundaryBase<SPT,SRT> & aSecondBoundary)2809 Maybe<int32_t> nsContentUtils::ComparePoints(
2810     const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
2811     const RangeBoundaryBase<SPT, SRT>& aSecondBoundary) {
2812   if (!aFirstBoundary.IsSet() || !aSecondBoundary.IsSet()) {
2813     return Nothing{};
2814   }
2815 
2816   bool disconnected{false};
2817   const int32_t order =
2818       ComparePoints_Deprecated(aFirstBoundary, aSecondBoundary, &disconnected);
2819 
2820   if (disconnected) {
2821     return Nothing{};
2822   }
2823 
2824   return Some(order);
2825 }
2826 
2827 /* static */
2828 template <typename FPT, typename FRT, typename SPT, typename SRT>
ComparePoints_Deprecated(const RangeBoundaryBase<FPT,FRT> & aFirstBoundary,const RangeBoundaryBase<SPT,SRT> & aSecondBoundary,bool * aDisconnected)2829 int32_t nsContentUtils::ComparePoints_Deprecated(
2830     const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
2831     const RangeBoundaryBase<SPT, SRT>& aSecondBoundary, bool* aDisconnected) {
2832   if (NS_WARN_IF(!aFirstBoundary.IsSet()) ||
2833       NS_WARN_IF(!aSecondBoundary.IsSet())) {
2834     return -1;
2835   }
2836   // XXX Re-implement this without calling `Offset()` as far as possible,
2837   //     and the other overload should be an alias of this.
2838   return ComparePoints_Deprecated(
2839       aFirstBoundary.Container(),
2840       *aFirstBoundary.Offset(
2841           RangeBoundaryBase<FPT, FRT>::OffsetFilter::kValidOrInvalidOffsets),
2842       aSecondBoundary.Container(),
2843       *aSecondBoundary.Offset(
2844           RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOrInvalidOffsets),
2845       aDisconnected);
2846 }
2847 
IsCharInSet(const char * aSet,const char16_t aChar)2848 inline bool IsCharInSet(const char* aSet, const char16_t aChar) {
2849   char16_t ch;
2850   while ((ch = *aSet)) {
2851     if (aChar == char16_t(ch)) {
2852       return true;
2853     }
2854     ++aSet;
2855   }
2856   return false;
2857 }
2858 
2859 /**
2860  * This method strips leading/trailing chars, in given set, from string.
2861  */
2862 
2863 // static
TrimCharsInSet(const char * aSet,const nsAString & aValue)2864 const nsDependentSubstring nsContentUtils::TrimCharsInSet(
2865     const char* aSet, const nsAString& aValue) {
2866   nsAString::const_iterator valueCurrent, valueEnd;
2867 
2868   aValue.BeginReading(valueCurrent);
2869   aValue.EndReading(valueEnd);
2870 
2871   // Skip characters in the beginning
2872   while (valueCurrent != valueEnd) {
2873     if (!IsCharInSet(aSet, *valueCurrent)) {
2874       break;
2875     }
2876     ++valueCurrent;
2877   }
2878 
2879   if (valueCurrent != valueEnd) {
2880     for (;;) {
2881       --valueEnd;
2882       if (!IsCharInSet(aSet, *valueEnd)) {
2883         break;
2884       }
2885     }
2886     ++valueEnd;  // Step beyond the last character we want in the value.
2887   }
2888 
2889   // valueEnd should point to the char after the last to copy
2890   return Substring(valueCurrent, valueEnd);
2891 }
2892 
2893 /**
2894  * This method strips leading and trailing whitespace from a string.
2895  */
2896 
2897 // static
2898 template <bool IsWhitespace(char16_t)>
TrimWhitespace(const nsAString & aStr,bool aTrimTrailing)2899 const nsDependentSubstring nsContentUtils::TrimWhitespace(const nsAString& aStr,
2900                                                           bool aTrimTrailing) {
2901   nsAString::const_iterator start, end;
2902 
2903   aStr.BeginReading(start);
2904   aStr.EndReading(end);
2905 
2906   // Skip whitespace characters in the beginning
2907   while (start != end && IsWhitespace(*start)) {
2908     ++start;
2909   }
2910 
2911   if (aTrimTrailing) {
2912     // Skip whitespace characters in the end.
2913     while (end != start) {
2914       --end;
2915 
2916       if (!IsWhitespace(*end)) {
2917         // Step back to the last non-whitespace character.
2918         ++end;
2919 
2920         break;
2921       }
2922     }
2923   }
2924 
2925   // Return a substring for the string w/o leading and/or trailing
2926   // whitespace
2927 
2928   return Substring(start, end);
2929 }
2930 
2931 // Declaring the templates we are going to use avoid linking issues without
2932 // inlining the method. Considering there is not so much spaces checking
2933 // methods we can consider this to be better than inlining.
2934 template const nsDependentSubstring
2935 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, bool);
2936 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
2937     nsContentUtils::IsHTMLWhitespace>(const nsAString&, bool);
2938 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
2939     nsContentUtils::IsHTMLWhitespaceOrNBSP>(const nsAString&, bool);
2940 
KeyAppendSep(nsACString & aKey)2941 static inline void KeyAppendSep(nsACString& aKey) {
2942   if (!aKey.IsEmpty()) {
2943     aKey.Append('>');
2944   }
2945 }
2946 
KeyAppendString(const nsAString & aString,nsACString & aKey)2947 static inline void KeyAppendString(const nsAString& aString, nsACString& aKey) {
2948   KeyAppendSep(aKey);
2949 
2950   // Could escape separator here if collisions happen.  > is not a legal char
2951   // for a name or type attribute, so we should be safe avoiding that extra
2952   // work.
2953 
2954   AppendUTF16toUTF8(aString, aKey);
2955 }
2956 
KeyAppendString(const nsACString & aString,nsACString & aKey)2957 static inline void KeyAppendString(const nsACString& aString,
2958                                    nsACString& aKey) {
2959   KeyAppendSep(aKey);
2960 
2961   // Could escape separator here if collisions happen.  > is not a legal char
2962   // for a name or type attribute, so we should be safe avoiding that extra
2963   // work.
2964 
2965   aKey.Append(aString);
2966 }
2967 
KeyAppendInt(int32_t aInt,nsACString & aKey)2968 static inline void KeyAppendInt(int32_t aInt, nsACString& aKey) {
2969   KeyAppendSep(aKey);
2970 
2971   aKey.AppendInt(aInt);
2972 }
2973 
IsAutocompleteOff(const nsIContent * aContent)2974 static inline bool IsAutocompleteOff(const nsIContent* aContent) {
2975   return aContent->IsElement() &&
2976          aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
2977                                             nsGkAtoms::autocomplete, u"off"_ns,
2978                                             eIgnoreCase);
2979 }
2980 
2981 /*static*/
GenerateStateKey(nsIContent * aContent,Document * aDocument,nsACString & aKey)2982 void nsContentUtils::GenerateStateKey(nsIContent* aContent, Document* aDocument,
2983                                       nsACString& aKey) {
2984   MOZ_ASSERT(aContent);
2985 
2986   aKey.Truncate();
2987 
2988   uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
2989 
2990   // Don't capture state for anonymous content
2991   if (aContent->IsInNativeAnonymousSubtree()) {
2992     return;
2993   }
2994 
2995   if (IsAutocompleteOff(aContent)) {
2996     return;
2997   }
2998 
2999   RefPtr<Document> doc = aContent->GetUncomposedDoc();
3000 
3001   KeyAppendInt(partID, aKey);  // first append a partID
3002   bool generatedUniqueKey = false;
3003 
3004   if (doc && doc->IsHTMLOrXHTML()) {
3005     nsHTMLDocument* htmlDoc = doc->AsHTMLDocument();
3006 
3007     // If we have a form control and can calculate form information, use that
3008     // as the key - it is more reliable than just recording position in the
3009     // DOM.
3010     // XXXbz Is it, really?  We have bugs on this, I think...
3011     // Important to have a unique key, and tag/type/name may not be.
3012     //
3013     // The format of the key depends on whether the control has a form,
3014     // and whether the element was parser inserted:
3015     //
3016     // [Has Form, Parser Inserted]:
3017     //   fp>type>FormNum>IndOfControlInForm>FormName>name
3018     //
3019     // [No Form, Parser Inserted]:
3020     //   dp>type>ControlNum>name
3021     //
3022     // [Has Form, Not Parser Inserted]:
3023     //   fn>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
3024     //
3025     // [No Form, Not Parser Inserted]:
3026     //   dn>type>IndOfControlInDoc>name
3027     //
3028     // XXX We don't need to use index if name is there
3029     // XXXbz We don't?  Why not?  I don't follow.
3030     //
3031     nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
3032     if (control) {
3033       // Get the control number if this was a parser inserted element from the
3034       // network.
3035       int32_t controlNumber =
3036           control->GetParserInsertedControlNumberForStateKey();
3037       bool parserInserted = controlNumber != -1;
3038 
3039       RefPtr<nsContentList> htmlForms;
3040       RefPtr<nsContentList> htmlFormControls;
3041       if (!parserInserted) {
3042         // Getting these lists is expensive, as we need to keep them up to date
3043         // as the document loads, so we avoid it if we don't need them.
3044         htmlDoc->GetFormsAndFormControls(getter_AddRefs(htmlForms),
3045                                          getter_AddRefs(htmlFormControls));
3046       }
3047 
3048       // Append the control type
3049       KeyAppendInt(int32_t(control->ControlType()), aKey);
3050 
3051       // If in a form, add form name / index of form / index in form
3052       HTMLFormElement* formElement = control->GetFormElement();
3053       if (formElement) {
3054         if (IsAutocompleteOff(formElement)) {
3055           aKey.Truncate();
3056           return;
3057         }
3058 
3059         // Append the form number, if this is a parser inserted control, or
3060         // the index of the form in the document otherwise.
3061         bool appendedForm = false;
3062         if (parserInserted) {
3063           MOZ_ASSERT(formElement->GetFormNumberForStateKey() != -1,
3064                      "when generating a state key for a parser inserted form "
3065                      "control we should have a parser inserted <form> element");
3066           KeyAppendString("fp"_ns, aKey);
3067           KeyAppendInt(formElement->GetFormNumberForStateKey(), aKey);
3068           appendedForm = true;
3069         } else {
3070           KeyAppendString("fn"_ns, aKey);
3071           int32_t index = htmlForms->IndexOf(formElement, false);
3072           if (index <= -1) {
3073             //
3074             // XXX HACK this uses some state that was dumped into the document
3075             // specifically to fix bug 138892.  What we are trying to do is
3076             // *guess* which form this control's state is found in, with the
3077             // highly likely guess that the highest form parsed so far is the
3078             // one. This code should not be on trunk, only branch.
3079             //
3080             index = htmlDoc->GetNumFormsSynchronous() - 1;
3081           }
3082           if (index > -1) {
3083             KeyAppendInt(index, aKey);
3084             appendedForm = true;
3085           }
3086         }
3087 
3088         if (appendedForm) {
3089           // Append the index of the control in the form
3090           int32_t index = formElement->IndexOfControl(control);
3091 
3092           if (index > -1) {
3093             KeyAppendInt(index, aKey);
3094             generatedUniqueKey = true;
3095           }
3096         }
3097 
3098         // Append the form name
3099         nsAutoString formName;
3100         formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
3101         KeyAppendString(formName, aKey);
3102       } else {
3103         // Not in a form.  Append the control number, if this is a parser
3104         // inserted control, or the index of the control in the document
3105         // otherwise.
3106         if (parserInserted) {
3107           KeyAppendString("dp"_ns, aKey);
3108           KeyAppendInt(control->GetParserInsertedControlNumberForStateKey(),
3109                        aKey);
3110           generatedUniqueKey = true;
3111         } else {
3112           KeyAppendString("dn"_ns, aKey);
3113           int32_t index = htmlFormControls->IndexOf(aContent, true);
3114           if (index > -1) {
3115             KeyAppendInt(index, aKey);
3116             generatedUniqueKey = true;
3117           }
3118         }
3119 
3120         // Append the control name
3121         nsAutoString name;
3122         aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name,
3123                                        name);
3124         KeyAppendString(name, aKey);
3125       }
3126     }
3127   }
3128 
3129   if (!generatedUniqueKey) {
3130     // Either we didn't have a form control or we aren't in an HTML document so
3131     // we can't figure out form info.  Append the tag name if it's an element
3132     // to avoid restoring state for one type of element on another type.
3133     if (aContent->IsElement()) {
3134       KeyAppendString(nsDependentAtomString(aContent->NodeInfo()->NameAtom()),
3135                       aKey);
3136     } else {
3137       // Append a character that is not "d" or "f" to disambiguate from
3138       // the case when we were a form control in an HTML document.
3139       KeyAppendString("o"_ns, aKey);
3140     }
3141 
3142     // Now start at aContent and append the indices of it and all its ancestors
3143     // in their containers.  That should at least pin down its position in the
3144     // DOM...
3145     nsINode* parent = aContent->GetParentNode();
3146     nsINode* content = aContent;
3147     while (parent) {
3148       KeyAppendInt(parent->ComputeIndexOf(content), aKey);
3149       content = parent;
3150       parent = content->GetParentNode();
3151     }
3152   }
3153 }
3154 
3155 // static
SubjectPrincipal(JSContext * aCx)3156 nsIPrincipal* nsContentUtils::SubjectPrincipal(JSContext* aCx) {
3157   MOZ_ASSERT(NS_IsMainThread());
3158 
3159   // As opposed to SubjectPrincipal(), we do in fact assume that
3160   // we're in a realm here; anyone who calls this function in
3161   // situations where that's not the case is doing it wrong.
3162   JS::Realm* realm = js::GetContextRealm(aCx);
3163   MOZ_ASSERT(realm);
3164 
3165   JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3166   return nsJSPrincipals::get(principals);
3167 }
3168 
3169 // static
SubjectPrincipal()3170 nsIPrincipal* nsContentUtils::SubjectPrincipal() {
3171   MOZ_ASSERT(IsInitialized());
3172   MOZ_ASSERT(NS_IsMainThread());
3173   JSContext* cx = GetCurrentJSContext();
3174   if (!cx) {
3175     MOZ_CRASH(
3176         "Accessing the Subject Principal without an AutoJSAPI on the stack is "
3177         "forbidden");
3178   }
3179 
3180   JS::Realm* realm = js::GetContextRealm(cx);
3181 
3182   // When an AutoJSAPI is instantiated, we are in a null realm until the
3183   // first JSAutoRealm, which is kind of a purgatory as far as permissions
3184   // go. It would be nice to just hard-abort if somebody does a security check
3185   // in this purgatory zone, but that would be too fragile, since it could be
3186   // triggered by random IsCallerChrome() checks 20-levels deep.
3187   //
3188   // So we want to return _something_ here - and definitely not the System
3189   // Principal, since that would make an AutoJSAPI a very dangerous thing to
3190   // instantiate.
3191   //
3192   // The natural thing to return is a null principal. Ideally, we'd return a
3193   // different null principal each time, to avoid any unexpected interactions
3194   // when the principal accidentally gets inherited somewhere. But
3195   // SubjectPrincipal doesn't return strong references, so there's no way to
3196   // sanely manage the lifetime of multiple null principals.
3197   //
3198   // So we use a singleton null principal. To avoid it being accidentally
3199   // inherited and becoming a "real" subject or object principal, we do a
3200   // release-mode assert during realm creation against using this principal on
3201   // an actual global.
3202   if (!realm) {
3203     return sNullSubjectPrincipal;
3204   }
3205 
3206   return SubjectPrincipal(cx);
3207 }
3208 
3209 // static
ObjectPrincipal(JSObject * aObj)3210 nsIPrincipal* nsContentUtils::ObjectPrincipal(JSObject* aObj) {
3211   MOZ_ASSERT(NS_IsMainThread());
3212 
3213 #ifdef DEBUG
3214   JS::AssertObjectBelongsToCurrentThread(aObj);
3215 #endif
3216 
3217   MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(aObj));
3218 
3219   JS::Realm* realm = js::GetNonCCWObjectRealm(aObj);
3220   JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3221   return nsJSPrincipals::get(principals);
3222 }
3223 
3224 // static
NewURIWithDocumentCharset(nsIURI ** aResult,const nsAString & aSpec,Document * aDocument,nsIURI * aBaseURI)3225 nsresult nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
3226                                                    const nsAString& aSpec,
3227                                                    Document* aDocument,
3228                                                    nsIURI* aBaseURI) {
3229   if (aDocument) {
3230     return NS_NewURI(aResult, aSpec, aDocument->GetDocumentCharacterSet(),
3231                      aBaseURI);
3232   }
3233   return NS_NewURI(aResult, aSpec, nullptr, aBaseURI);
3234 }
3235 
3236 // static
IsNameWithDash(nsAtom * aName)3237 bool nsContentUtils::IsNameWithDash(nsAtom* aName) {
3238   // A valid custom element name is a sequence of characters name which
3239   // must match the PotentialCustomElementName production:
3240   // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
3241   const char16_t* name = aName->GetUTF16String();
3242   uint32_t len = aName->GetLength();
3243   bool hasDash = false;
3244 
3245   if (!len || name[0] < 'a' || name[0] > 'z') {
3246     return false;
3247   }
3248 
3249   uint32_t i = 1;
3250   while (i < len) {
3251     if (i + 1 < len && NS_IS_SURROGATE_PAIR(name[i], name[i + 1])) {
3252       // Merged two 16-bit surrogate pairs into code point.
3253       char32_t code = SURROGATE_TO_UCS4(name[i], name[i + 1]);
3254 
3255       if (code < 0x10000 || code > 0xEFFFF) {
3256         return false;
3257       }
3258 
3259       i += 2;
3260     } else {
3261       if (name[i] == '-') {
3262         hasDash = true;
3263       }
3264 
3265       if (name[i] != '-' && name[i] != '.' && name[i] != '_' &&
3266           name[i] != 0xB7 && (name[i] < '0' || name[i] > '9') &&
3267           (name[i] < 'a' || name[i] > 'z') &&
3268           (name[i] < 0xC0 || name[i] > 0xD6) &&
3269           (name[i] < 0xF8 || name[i] > 0x37D) &&
3270           (name[i] < 0x37F || name[i] > 0x1FFF) &&
3271           (name[i] < 0x200C || name[i] > 0x200D) &&
3272           (name[i] < 0x203F || name[i] > 0x2040) &&
3273           (name[i] < 0x2070 || name[i] > 0x218F) &&
3274           (name[i] < 0x2C00 || name[i] > 0x2FEF) &&
3275           (name[i] < 0x3001 || name[i] > 0xD7FF) &&
3276           (name[i] < 0xF900 || name[i] > 0xFDCF) &&
3277           (name[i] < 0xFDF0 || name[i] > 0xFFFD)) {
3278         return false;
3279       }
3280 
3281       i++;
3282     }
3283   }
3284 
3285   return hasDash;
3286 }
3287 
3288 // static
IsCustomElementName(nsAtom * aName,uint32_t aNameSpaceID)3289 bool nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID) {
3290   // Allow non-dashed names in XUL for XBL to Custom Element migrations.
3291   if (aNameSpaceID == kNameSpaceID_XUL) {
3292     return true;
3293   }
3294 
3295   bool hasDash = IsNameWithDash(aName);
3296   if (!hasDash) {
3297     return false;
3298   }
3299 
3300   // The custom element name must not be one of the following values:
3301   //  annotation-xml
3302   //  color-profile
3303   //  font-face
3304   //  font-face-src
3305   //  font-face-uri
3306   //  font-face-format
3307   //  font-face-name
3308   //  missing-glyph
3309   return aName != nsGkAtoms::annotation_xml_ &&
3310          aName != nsGkAtoms::colorProfile && aName != nsGkAtoms::font_face &&
3311          aName != nsGkAtoms::font_face_src &&
3312          aName != nsGkAtoms::font_face_uri &&
3313          aName != nsGkAtoms::font_face_format &&
3314          aName != nsGkAtoms::font_face_name && aName != nsGkAtoms::missingGlyph;
3315 }
3316 
3317 // static
CheckQName(const nsAString & aQualifiedName,bool aNamespaceAware,const char16_t ** aColon)3318 nsresult nsContentUtils::CheckQName(const nsAString& aQualifiedName,
3319                                     bool aNamespaceAware,
3320                                     const char16_t** aColon) {
3321   const char* colon = nullptr;
3322   const char16_t* begin = aQualifiedName.BeginReading();
3323   const char16_t* end = aQualifiedName.EndReading();
3324 
3325   int result = MOZ_XMLCheckQName(reinterpret_cast<const char*>(begin),
3326                                  reinterpret_cast<const char*>(end),
3327                                  aNamespaceAware, &colon);
3328 
3329   if (!result) {
3330     if (aColon) {
3331       *aColon = reinterpret_cast<const char16_t*>(colon);
3332     }
3333 
3334     return NS_OK;
3335   }
3336 
3337   return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
3338 }
3339 
3340 // static
SplitQName(const nsIContent * aNamespaceResolver,const nsString & aQName,int32_t * aNamespace,nsAtom ** aLocalName)3341 nsresult nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
3342                                     const nsString& aQName, int32_t* aNamespace,
3343                                     nsAtom** aLocalName) {
3344   const char16_t* colon;
3345   nsresult rv = nsContentUtils::CheckQName(aQName, true, &colon);
3346   NS_ENSURE_SUCCESS(rv, rv);
3347 
3348   if (colon) {
3349     const char16_t* end;
3350     aQName.EndReading(end);
3351     nsAutoString nameSpace;
3352     rv = aNamespaceResolver->LookupNamespaceURIInternal(
3353         Substring(aQName.get(), colon), nameSpace);
3354     NS_ENSURE_SUCCESS(rv, rv);
3355 
3356     *aNamespace = NameSpaceManager()->GetNameSpaceID(
3357         nameSpace, nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
3358     if (*aNamespace == kNameSpaceID_Unknown) return NS_ERROR_FAILURE;
3359 
3360     *aLocalName = NS_AtomizeMainThread(Substring(colon + 1, end)).take();
3361   } else {
3362     *aNamespace = kNameSpaceID_None;
3363     *aLocalName = NS_AtomizeMainThread(aQName).take();
3364   }
3365   NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
3366   return NS_OK;
3367 }
3368 
3369 // static
GetNodeInfoFromQName(const nsAString & aNamespaceURI,const nsAString & aQualifiedName,nsNodeInfoManager * aNodeInfoManager,uint16_t aNodeType,mozilla::dom::NodeInfo ** aNodeInfo)3370 nsresult nsContentUtils::GetNodeInfoFromQName(
3371     const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
3372     nsNodeInfoManager* aNodeInfoManager, uint16_t aNodeType,
3373     mozilla::dom::NodeInfo** aNodeInfo) {
3374   const nsString& qName = PromiseFlatString(aQualifiedName);
3375   const char16_t* colon;
3376   nsresult rv = nsContentUtils::CheckQName(qName, true, &colon);
3377   NS_ENSURE_SUCCESS(rv, rv);
3378 
3379   int32_t nsID;
3380   sNameSpaceManager->RegisterNameSpace(aNamespaceURI, nsID);
3381   if (colon) {
3382     const char16_t* end;
3383     qName.EndReading(end);
3384 
3385     RefPtr<nsAtom> prefix = NS_AtomizeMainThread(Substring(qName.get(), colon));
3386 
3387     rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix, nsID,
3388                                        aNodeType, aNodeInfo);
3389   } else {
3390     rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID, aNodeType,
3391                                        aNodeInfo);
3392   }
3393   NS_ENSURE_SUCCESS(rv, rv);
3394 
3395   return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
3396                                          (*aNodeInfo)->GetPrefixAtom(),
3397                                          (*aNodeInfo)->NamespaceID())
3398              ? NS_OK
3399              : NS_ERROR_DOM_NAMESPACE_ERR;
3400 }
3401 
3402 // static
SplitExpatName(const char16_t * aExpatName,nsAtom ** aPrefix,nsAtom ** aLocalName,int32_t * aNameSpaceID)3403 void nsContentUtils::SplitExpatName(const char16_t* aExpatName,
3404                                     nsAtom** aPrefix, nsAtom** aLocalName,
3405                                     int32_t* aNameSpaceID) {
3406   /**
3407    *  Expat can send the following:
3408    *    localName
3409    *    namespaceURI<separator>localName
3410    *    namespaceURI<separator>localName<separator>prefix
3411    *
3412    *  and we use 0xFFFF for the <separator>.
3413    *
3414    */
3415 
3416   const char16_t* uriEnd = nullptr;
3417   const char16_t* nameEnd = nullptr;
3418   const char16_t* pos;
3419   for (pos = aExpatName; *pos; ++pos) {
3420     if (*pos == 0xFFFF) {
3421       if (uriEnd) {
3422         nameEnd = pos;
3423       } else {
3424         uriEnd = pos;
3425       }
3426     }
3427   }
3428 
3429   const char16_t* nameStart;
3430   if (uriEnd) {
3431     if (sNameSpaceManager) {
3432       sNameSpaceManager->RegisterNameSpace(
3433           nsDependentSubstring(aExpatName, uriEnd), *aNameSpaceID);
3434     } else {
3435       *aNameSpaceID = kNameSpaceID_Unknown;
3436     }
3437 
3438     nameStart = (uriEnd + 1);
3439     if (nameEnd) {
3440       const char16_t* prefixStart = nameEnd + 1;
3441       *aPrefix = NS_AtomizeMainThread(Substring(prefixStart, pos)).take();
3442     } else {
3443       nameEnd = pos;
3444       *aPrefix = nullptr;
3445     }
3446   } else {
3447     *aNameSpaceID = kNameSpaceID_None;
3448     nameStart = aExpatName;
3449     nameEnd = pos;
3450     *aPrefix = nullptr;
3451   }
3452   *aLocalName = NS_AtomizeMainThread(Substring(nameStart, nameEnd)).take();
3453 }
3454 
3455 // static
GetPresShellForContent(const nsIContent * aContent)3456 PresShell* nsContentUtils::GetPresShellForContent(const nsIContent* aContent) {
3457   Document* doc = aContent->GetComposedDoc();
3458   if (!doc) {
3459     return nullptr;
3460   }
3461   return doc->GetPresShell();
3462 }
3463 
3464 // static
GetContextForContent(const nsIContent * aContent)3465 nsPresContext* nsContentUtils::GetContextForContent(
3466     const nsIContent* aContent) {
3467   PresShell* presShell = GetPresShellForContent(aContent);
3468   if (!presShell) {
3469     return nullptr;
3470   }
3471   return presShell->GetPresContext();
3472 }
3473 
3474 // static
CanLoadImage(nsIURI * aURI,nsINode * aNode,Document * aLoadingDocument,nsIPrincipal * aLoadingPrincipal)3475 bool nsContentUtils::CanLoadImage(nsIURI* aURI, nsINode* aNode,
3476                                   Document* aLoadingDocument,
3477                                   nsIPrincipal* aLoadingPrincipal) {
3478   MOZ_ASSERT(aURI, "Must have a URI");
3479   MOZ_ASSERT(aLoadingDocument, "Must have a document");
3480   MOZ_ASSERT(aLoadingPrincipal, "Must have a loading principal");
3481 
3482   nsresult rv;
3483 
3484   auto appType = nsIDocShell::APP_TYPE_UNKNOWN;
3485 
3486   {
3487     nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
3488         aLoadingDocument->GetDocShell();
3489     if (docShellTreeItem) {
3490       nsCOMPtr<nsIDocShellTreeItem> root;
3491       docShellTreeItem->GetInProcessRootTreeItem(getter_AddRefs(root));
3492 
3493       nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
3494 
3495       if (docShell) {
3496         appType = docShell->GetAppType();
3497       }
3498     }
3499   }
3500 
3501   if (appType != nsIDocShell::APP_TYPE_EDITOR) {
3502     // Editor apps get special treatment here, editors can load images
3503     // from anywhere.  This allows editor to insert images from file://
3504     // into documents that are being edited.
3505     rv = sSecurityManager->CheckLoadURIWithPrincipal(
3506         aLoadingPrincipal, aURI, nsIScriptSecurityManager::ALLOW_CHROME,
3507         aLoadingDocument->InnerWindowID());
3508     if (NS_FAILED(rv)) {
3509       return false;
3510     }
3511   }
3512 
3513   nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new mozilla::net::LoadInfo(
3514       aLoadingPrincipal,
3515       aLoadingPrincipal,  // triggering principal
3516       aNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
3517       nsIContentPolicy::TYPE_INTERNAL_IMAGE);
3518 
3519   int16_t decision = nsIContentPolicy::ACCEPT;
3520 
3521   rv = NS_CheckContentLoadPolicy(aURI, secCheckLoadInfo,
3522                                  ""_ns,  // mime guess
3523                                  &decision, GetContentPolicy());
3524 
3525   return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(decision);
3526 }
3527 
3528 // static
IsInPrivateBrowsing(Document * aDoc)3529 bool nsContentUtils::IsInPrivateBrowsing(Document* aDoc) {
3530   if (!aDoc) {
3531     return false;
3532   }
3533 
3534   nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
3535   if (loadGroup) {
3536     nsCOMPtr<nsIInterfaceRequestor> callbacks;
3537     loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3538     if (callbacks) {
3539       nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3540       if (loadContext) {
3541         return loadContext->UsePrivateBrowsing();
3542       }
3543     }
3544   }
3545 
3546   nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
3547   return channel && NS_UsePrivateBrowsing(channel);
3548 }
3549 
3550 // static
IsInPrivateBrowsing(nsILoadGroup * aLoadGroup)3551 bool nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup) {
3552   if (!aLoadGroup) {
3553     return false;
3554   }
3555   bool isPrivate = false;
3556   nsCOMPtr<nsIInterfaceRequestor> callbacks;
3557   aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3558   if (callbacks) {
3559     nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3560     isPrivate = loadContext && loadContext->UsePrivateBrowsing();
3561   }
3562   return isPrivate;
3563 }
3564 
3565 // FIXME(emilio): This is (effectively) almost but not quite the same as
3566 // Document::ShouldLoadImages(), which one is right?
DocumentInactiveForImageLoads(Document * aDocument)3567 bool nsContentUtils::DocumentInactiveForImageLoads(Document* aDocument) {
3568   if (!aDocument) {
3569     return false;
3570   }
3571   if (IsChromeDoc(aDocument) || aDocument->IsResourceDoc() ||
3572       aDocument->IsStaticDocument()) {
3573     return false;
3574   }
3575   nsCOMPtr<nsPIDOMWindowInner> win =
3576       do_QueryInterface(aDocument->GetScopeObject());
3577   return !win || !win->GetDocShell();
3578 }
3579 
GetImgLoaderForDocument(Document * aDoc)3580 imgLoader* nsContentUtils::GetImgLoaderForDocument(Document* aDoc) {
3581   NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
3582 
3583   if (!aDoc) {
3584     return imgLoader::NormalLoader();
3585   }
3586   bool isPrivate = IsInPrivateBrowsing(aDoc);
3587   return isPrivate ? imgLoader::PrivateBrowsingLoader()
3588                    : imgLoader::NormalLoader();
3589 }
3590 
3591 // static
GetImgLoaderForChannel(nsIChannel * aChannel,Document * aContext)3592 imgLoader* nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
3593                                                   Document* aContext) {
3594   NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
3595 
3596   if (!aChannel) {
3597     return imgLoader::NormalLoader();
3598   }
3599   nsCOMPtr<nsILoadContext> context;
3600   NS_QueryNotificationCallbacks(aChannel, context);
3601   return context && context->UsePrivateBrowsing()
3602              ? imgLoader::PrivateBrowsingLoader()
3603              : imgLoader::NormalLoader();
3604 }
3605 
3606 // static
IsImageInCache(nsIURI * aURI,Document * aDocument)3607 bool nsContentUtils::IsImageInCache(nsIURI* aURI, Document* aDocument) {
3608   imgILoader* loader = GetImgLoaderForDocument(aDocument);
3609   nsCOMPtr<imgICache> cache = do_QueryInterface(loader);
3610 
3611   // If something unexpected happened we return false, otherwise if props
3612   // is set, the image is cached and we return true
3613   nsCOMPtr<nsIProperties> props;
3614   nsresult rv =
3615       cache->FindEntryProperties(aURI, aDocument, getter_AddRefs(props));
3616   return (NS_SUCCEEDED(rv) && props);
3617 }
3618 
3619 // static
CORSModeToLoadImageFlags(mozilla::CORSMode aMode)3620 int32_t nsContentUtils::CORSModeToLoadImageFlags(mozilla::CORSMode aMode) {
3621   switch (aMode) {
3622     case CORS_ANONYMOUS:
3623       return imgILoader::LOAD_CORS_ANONYMOUS;
3624     case CORS_USE_CREDENTIALS:
3625       return imgILoader::LOAD_CORS_USE_CREDENTIALS;
3626     default:
3627       return 0;
3628   }
3629 }
3630 
3631 // static
LoadImage(nsIURI * aURI,nsINode * aContext,Document * aLoadingDocument,nsIPrincipal * aLoadingPrincipal,uint64_t aRequestContextID,nsIReferrerInfo * aReferrerInfo,imgINotificationObserver * aObserver,int32_t aLoadFlags,const nsAString & initiatorType,imgRequestProxy ** aRequest,nsContentPolicyType aContentPolicyType,bool aUseUrgentStartForChannel,bool aLinkPreload)3632 nsresult nsContentUtils::LoadImage(
3633     nsIURI* aURI, nsINode* aContext, Document* aLoadingDocument,
3634     nsIPrincipal* aLoadingPrincipal, uint64_t aRequestContextID,
3635     nsIReferrerInfo* aReferrerInfo, imgINotificationObserver* aObserver,
3636     int32_t aLoadFlags, const nsAString& initiatorType,
3637     imgRequestProxy** aRequest, nsContentPolicyType aContentPolicyType,
3638     bool aUseUrgentStartForChannel, bool aLinkPreload) {
3639   MOZ_ASSERT(aURI, "Must have a URI");
3640   MOZ_ASSERT(aContext, "Must have a context");
3641   MOZ_ASSERT(aLoadingDocument, "Must have a document");
3642   MOZ_ASSERT(aLoadingPrincipal, "Must have a principal");
3643   MOZ_ASSERT(aRequest, "Null out param");
3644 
3645   imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
3646   if (!imgLoader) {
3647     // nothing we can do here
3648     return NS_ERROR_FAILURE;
3649   }
3650 
3651   nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
3652 
3653   nsIURI* documentURI = aLoadingDocument->GetDocumentURI();
3654 
3655   NS_ASSERTION(loadGroup || IsFontTableURI(documentURI),
3656                "Could not get loadgroup; onload may fire too early");
3657 
3658   // XXXbz using "documentURI" for the initialDocumentURI is not quite
3659   // right, but the best we can do here...
3660   return imgLoader->LoadImage(aURI,               /* uri to load */
3661                               documentURI,        /* initialDocumentURI */
3662                               aReferrerInfo,      /* referrerInfo */
3663                               aLoadingPrincipal,  /* loading principal */
3664                               aRequestContextID,  /* request context ID */
3665                               loadGroup,          /* loadgroup */
3666                               aObserver,          /* imgINotificationObserver */
3667                               aContext,           /* loading context */
3668                               aLoadingDocument,   /* uniquification key */
3669                               aLoadFlags,         /* load flags */
3670                               nullptr,            /* cache key */
3671                               aContentPolicyType, /* content policy type */
3672                               initiatorType,      /* the load initiator */
3673                               aUseUrgentStartForChannel, /* urgent-start flag */
3674                               aLinkPreload, /* <link preload> initiator */
3675                               aRequest);
3676 }
3677 
3678 // static
GetImageFromContent(nsIImageLoadingContent * aContent,imgIRequest ** aRequest)3679 already_AddRefed<imgIContainer> nsContentUtils::GetImageFromContent(
3680     nsIImageLoadingContent* aContent, imgIRequest** aRequest) {
3681   if (aRequest) {
3682     *aRequest = nullptr;
3683   }
3684 
3685   NS_ENSURE_TRUE(aContent, nullptr);
3686 
3687   nsCOMPtr<imgIRequest> imgRequest;
3688   aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3689                        getter_AddRefs(imgRequest));
3690   if (!imgRequest) {
3691     return nullptr;
3692   }
3693 
3694   nsCOMPtr<imgIContainer> imgContainer;
3695   imgRequest->GetImage(getter_AddRefs(imgContainer));
3696 
3697   if (!imgContainer) {
3698     return nullptr;
3699   }
3700 
3701   if (aRequest) {
3702     // If the consumer wants the request, verify it has actually loaded
3703     // successfully.
3704     uint32_t imgStatus;
3705     imgRequest->GetImageStatus(&imgStatus);
3706     if (imgStatus & imgIRequest::STATUS_FRAME_COMPLETE &&
3707         !(imgStatus & imgIRequest::STATUS_ERROR)) {
3708       imgRequest.swap(*aRequest);
3709     }
3710   }
3711 
3712   return imgContainer.forget();
3713 }
3714 
3715 // static
ContentIsDraggable(nsIContent * aContent)3716 bool nsContentUtils::ContentIsDraggable(nsIContent* aContent) {
3717   MOZ_ASSERT(aContent);
3718 
3719   if (auto htmlElement = nsGenericHTMLElement::FromNode(aContent)) {
3720     if (htmlElement->Draggable()) {
3721       return true;
3722     }
3723 
3724     if (htmlElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
3725                                  nsGkAtoms::_false, eIgnoreCase)) {
3726       return false;
3727     }
3728   }
3729   if (aContent->IsSVGElement()) {
3730     return false;
3731   }
3732 
3733   // special handling for content area image and link dragging
3734   return IsDraggableImage(aContent) || IsDraggableLink(aContent);
3735 }
3736 
3737 // static
IsDraggableImage(nsIContent * aContent)3738 bool nsContentUtils::IsDraggableImage(nsIContent* aContent) {
3739   MOZ_ASSERT(aContent, "Must have content node to test");
3740 
3741   nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(aContent));
3742   if (!imageContent) {
3743     return false;
3744   }
3745 
3746   nsCOMPtr<imgIRequest> imgRequest;
3747   imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3748                            getter_AddRefs(imgRequest));
3749 
3750   // XXXbz It may be draggable even if the request resulted in an error.  Why?
3751   // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
3752   return imgRequest != nullptr;
3753 }
3754 
3755 // static
IsDraggableLink(const nsIContent * aContent)3756 bool nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
3757   nsCOMPtr<nsIURI> absURI;
3758   return aContent->IsLink(getter_AddRefs(absURI));
3759 }
3760 
3761 // static
QNameChanged(mozilla::dom::NodeInfo * aNodeInfo,nsAtom * aName,mozilla::dom::NodeInfo ** aResult)3762 nsresult nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo,
3763                                       nsAtom* aName,
3764                                       mozilla::dom::NodeInfo** aResult) {
3765   nsNodeInfoManager* niMgr = aNodeInfo->NodeInfoManager();
3766 
3767   *aResult = niMgr
3768                  ->GetNodeInfo(aName, nullptr, aNodeInfo->NamespaceID(),
3769                                aNodeInfo->NodeType(), aNodeInfo->GetExtraName())
3770                  .take();
3771   return NS_OK;
3772 }
3773 
TestSitePerm(nsIPrincipal * aPrincipal,const nsACString & aType,uint32_t aPerm,bool aExactHostMatch)3774 static bool TestSitePerm(nsIPrincipal* aPrincipal, const nsACString& aType,
3775                          uint32_t aPerm, bool aExactHostMatch) {
3776   if (!aPrincipal) {
3777     // We always deny (i.e. don't allow) the permission if we don't have a
3778     // principal.
3779     return aPerm != nsIPermissionManager::ALLOW_ACTION;
3780   }
3781 
3782   nsCOMPtr<nsIPermissionManager> permMgr =
3783       components::PermissionManager::Service();
3784   NS_ENSURE_TRUE(permMgr, false);
3785 
3786   uint32_t perm;
3787   nsresult rv;
3788   if (aExactHostMatch) {
3789     rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
3790   } else {
3791     rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
3792   }
3793   NS_ENSURE_SUCCESS(rv, false);
3794 
3795   return perm == aPerm;
3796 }
3797 
IsSitePermAllow(nsIPrincipal * aPrincipal,const nsACString & aType)3798 bool nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal,
3799                                      const nsACString& aType) {
3800   return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
3801                       false);
3802 }
3803 
IsSitePermDeny(nsIPrincipal * aPrincipal,const nsACString & aType)3804 bool nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal,
3805                                     const nsACString& aType) {
3806   return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
3807                       false);
3808 }
3809 
IsExactSitePermAllow(nsIPrincipal * aPrincipal,const nsACString & aType)3810 bool nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal,
3811                                           const nsACString& aType) {
3812   return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
3813                       true);
3814 }
3815 
IsExactSitePermDeny(nsIPrincipal * aPrincipal,const nsACString & aType)3816 bool nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal,
3817                                          const nsACString& aType) {
3818   return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
3819                       true);
3820 }
3821 
3822 static const char* gEventNames[] = {"event"};
3823 static const char* gSVGEventNames[] = {"evt"};
3824 // for b/w compat, the first name to onerror is still 'event', even though it
3825 // is actually the error message
3826 static const char* gOnErrorNames[] = {"event", "source", "lineno", "colno",
3827                                       "error"};
3828 
3829 // static
GetEventArgNames(int32_t aNameSpaceID,nsAtom * aEventName,bool aIsForWindow,uint32_t * aArgCount,const char *** aArgArray)3830 void nsContentUtils::GetEventArgNames(int32_t aNameSpaceID, nsAtom* aEventName,
3831                                       bool aIsForWindow, uint32_t* aArgCount,
3832                                       const char*** aArgArray) {
3833 #define SET_EVENT_ARG_NAMES(names)               \
3834   *aArgCount = sizeof(names) / sizeof(names[0]); \
3835   *aArgArray = names;
3836 
3837   // JSEventHandler is what does the arg magic for onerror, and it does
3838   // not seem to take the namespace into account.  So we let onerror in all
3839   // namespaces get the 3 arg names.
3840   if (aEventName == nsGkAtoms::onerror && aIsForWindow) {
3841     SET_EVENT_ARG_NAMES(gOnErrorNames);
3842   } else if (aNameSpaceID == kNameSpaceID_SVG) {
3843     SET_EVENT_ARG_NAMES(gSVGEventNames);
3844   } else {
3845     SET_EVENT_ARG_NAMES(gEventNames);
3846   }
3847 }
3848 
3849 // Note: The list of content bundles in nsStringBundle.cpp should be updated
3850 // whenever entries are added or removed from this list.
3851 static const char* gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT] = {
3852     // Must line up with the enum values in |PropertiesFile| enum.
3853     "chrome://global/locale/css.properties",
3854     "chrome://global/locale/xul.properties",
3855     "chrome://global/locale/layout_errors.properties",
3856     "chrome://global/locale/layout/HtmlForm.properties",
3857     "chrome://global/locale/printing.properties",
3858     "chrome://global/locale/dom/dom.properties",
3859     "chrome://global/locale/layout/htmlparser.properties",
3860     "chrome://global/locale/svg/svg.properties",
3861     "chrome://branding/locale/brand.properties",
3862     "chrome://global/locale/commonDialogs.properties",
3863     "chrome://global/locale/mathml/mathml.properties",
3864     "chrome://global/locale/security/security.properties",
3865     "chrome://necko/locale/necko.properties",
3866     "resource://gre/res/locale/layout/HtmlForm.properties",
3867     "resource://gre/res/locale/dom/dom.properties"};
3868 
3869 /* static */
EnsureStringBundle(PropertiesFile aFile)3870 nsresult nsContentUtils::EnsureStringBundle(PropertiesFile aFile) {
3871   if (!sStringBundles[aFile]) {
3872     if (!sStringBundleService) {
3873       nsresult rv =
3874           CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
3875       NS_ENSURE_SUCCESS(rv, rv);
3876     }
3877     nsIStringBundle* bundle;
3878     nsresult rv =
3879         sStringBundleService->CreateBundle(gPropertiesFiles[aFile], &bundle);
3880     NS_ENSURE_SUCCESS(rv, rv);
3881     sStringBundles[aFile] = bundle;  // transfer ownership
3882   }
3883   return NS_OK;
3884 }
3885 
3886 /* static */
AsyncPrecreateStringBundles()3887 void nsContentUtils::AsyncPrecreateStringBundles() {
3888   // We only ever want to pre-create bundles in the parent process.
3889   //
3890   // All nsContentUtils bundles are shared between the parent and child
3891   // precesses, and the shared memory regions that back them *must* be created
3892   // in the parent, and then sent to all children.
3893   //
3894   // If we attempt to create a bundle in the child before its memory region is
3895   // available, we need to create a temporary non-shared bundle, and later
3896   // replace that with the shared memory copy. So attempting to pre-load in the
3897   // child is wasteful and unnecessary.
3898   MOZ_ASSERT(XRE_IsParentProcess());
3899 
3900   for (uint32_t bundleIndex = 0; bundleIndex < PropertiesFile_COUNT;
3901        ++bundleIndex) {
3902     nsresult rv = NS_DispatchToCurrentThreadQueue(
3903         NS_NewRunnableFunction("AsyncPrecreateStringBundles",
3904                                [bundleIndex]() {
3905                                  PropertiesFile file =
3906                                      static_cast<PropertiesFile>(bundleIndex);
3907                                  EnsureStringBundle(file);
3908                                  nsIStringBundle* bundle = sStringBundles[file];
3909                                  bundle->AsyncPreload();
3910                                }),
3911         EventQueuePriority::Idle);
3912     Unused << NS_WARN_IF(NS_FAILED(rv));
3913   }
3914 }
3915 
3916 /* static */
SpoofLocaleEnglish()3917 bool nsContentUtils::SpoofLocaleEnglish() {
3918   // 0 - will prompt
3919   // 1 - don't spoof
3920   // 2 - spoof
3921   return StaticPrefs::privacy_spoof_english() == 2;
3922 }
3923 
GetMaybeSpoofedPropertiesFile(nsContentUtils::PropertiesFile aFile,const char * aKey,Document * aDocument)3924 static nsContentUtils::PropertiesFile GetMaybeSpoofedPropertiesFile(
3925     nsContentUtils::PropertiesFile aFile, const char* aKey,
3926     Document* aDocument) {
3927   // When we spoof English, use en-US properties in strings that are accessible
3928   // by content.
3929   bool spoofLocale = nsContentUtils::SpoofLocaleEnglish() &&
3930                      (!aDocument || !aDocument->AllowsL10n());
3931   if (spoofLocale) {
3932     switch (aFile) {
3933       case nsContentUtils::eFORMS_PROPERTIES:
3934         return nsContentUtils::eFORMS_PROPERTIES_en_US;
3935       case nsContentUtils::eDOM_PROPERTIES:
3936         return nsContentUtils::eDOM_PROPERTIES_en_US;
3937       default:
3938         break;
3939     }
3940   }
3941   return aFile;
3942 }
3943 
3944 /* static */
GetMaybeLocalizedString(PropertiesFile aFile,const char * aKey,Document * aDocument,nsAString & aResult)3945 nsresult nsContentUtils::GetMaybeLocalizedString(PropertiesFile aFile,
3946                                                  const char* aKey,
3947                                                  Document* aDocument,
3948                                                  nsAString& aResult) {
3949   return GetLocalizedString(
3950       GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aResult);
3951 }
3952 
3953 /* static */
GetLocalizedString(PropertiesFile aFile,const char * aKey,nsAString & aResult)3954 nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
3955                                             const char* aKey,
3956                                             nsAString& aResult) {
3957   nsresult rv = EnsureStringBundle(aFile);
3958   NS_ENSURE_SUCCESS(rv, rv);
3959   nsIStringBundle* bundle = sStringBundles[aFile];
3960   return bundle->GetStringFromName(aKey, aResult);
3961 }
3962 
3963 /* static */
FormatMaybeLocalizedString(PropertiesFile aFile,const char * aKey,Document * aDocument,const nsTArray<nsString> & aParams,nsAString & aResult)3964 nsresult nsContentUtils::FormatMaybeLocalizedString(
3965     PropertiesFile aFile, const char* aKey, Document* aDocument,
3966     const nsTArray<nsString>& aParams, nsAString& aResult) {
3967   return FormatLocalizedString(
3968       GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aParams,
3969       aResult);
3970 }
3971 
3972 /* static */
FormatLocalizedString(PropertiesFile aFile,const char * aKey,const nsTArray<nsString> & aParams,nsAString & aResult)3973 nsresult nsContentUtils::FormatLocalizedString(
3974     PropertiesFile aFile, const char* aKey, const nsTArray<nsString>& aParams,
3975     nsAString& aResult) {
3976   nsresult rv = EnsureStringBundle(aFile);
3977   NS_ENSURE_SUCCESS(rv, rv);
3978   nsIStringBundle* bundle = sStringBundles[aFile];
3979 
3980   if (aParams.IsEmpty()) {
3981     return bundle->GetStringFromName(aKey, aResult);
3982   }
3983 
3984   return bundle->FormatStringFromName(aKey, aParams, aResult);
3985 }
3986 
3987 /* static */
LogSimpleConsoleError(const nsAString & aErrorText,const char * aCategory,bool aFromPrivateWindow,bool aFromChromeContext,uint32_t aErrorFlags)3988 void nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
3989                                            const char* aCategory,
3990                                            bool aFromPrivateWindow,
3991                                            bool aFromChromeContext,
3992                                            uint32_t aErrorFlags) {
3993   nsCOMPtr<nsIScriptError> scriptError =
3994       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
3995   if (scriptError) {
3996     nsCOMPtr<nsIConsoleService> console =
3997         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
3998     if (console && NS_SUCCEEDED(scriptError->Init(
3999                        aErrorText, u""_ns, u""_ns, 0, 0, aErrorFlags, aCategory,
4000                        aFromPrivateWindow, aFromChromeContext))) {
4001       console->LogMessage(scriptError);
4002     }
4003   }
4004 }
4005 
4006 /* static */
ReportToConsole(uint32_t aErrorFlags,const nsACString & aCategory,const Document * aDocument,PropertiesFile aFile,const char * aMessageName,const nsTArray<nsString> & aParams,nsIURI * aURI,const nsString & aSourceLine,uint32_t aLineNumber,uint32_t aColumnNumber)4007 nsresult nsContentUtils::ReportToConsole(
4008     uint32_t aErrorFlags, const nsACString& aCategory,
4009     const Document* aDocument, PropertiesFile aFile, const char* aMessageName,
4010     const nsTArray<nsString>& aParams, nsIURI* aURI,
4011     const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber) {
4012   nsresult rv;
4013   nsAutoString errorText;
4014   if (!aParams.IsEmpty()) {
4015     rv = FormatLocalizedString(aFile, aMessageName, aParams, errorText);
4016   } else {
4017     rv = GetLocalizedString(aFile, aMessageName, errorText);
4018   }
4019   NS_ENSURE_SUCCESS(rv, rv);
4020 
4021   return ReportToConsoleNonLocalized(errorText, aErrorFlags, aCategory,
4022                                      aDocument, aURI, aSourceLine, aLineNumber,
4023                                      aColumnNumber);
4024 }
4025 
4026 /* static */
ReportEmptyGetElementByIdArg(const Document * aDoc)4027 void nsContentUtils::ReportEmptyGetElementByIdArg(const Document* aDoc) {
4028   ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, aDoc,
4029                   nsContentUtils::eDOM_PROPERTIES, "EmptyGetElementByIdParam");
4030 }
4031 
4032 /* static */
ReportToConsoleNonLocalized(const nsAString & aErrorText,uint32_t aErrorFlags,const nsACString & aCategory,const Document * aDocument,nsIURI * aURI,const nsString & aSourceLine,uint32_t aLineNumber,uint32_t aColumnNumber,MissingErrorLocationMode aLocationMode)4033 nsresult nsContentUtils::ReportToConsoleNonLocalized(
4034     const nsAString& aErrorText, uint32_t aErrorFlags,
4035     const nsACString& aCategory, const Document* aDocument, nsIURI* aURI,
4036     const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4037     MissingErrorLocationMode aLocationMode) {
4038   uint64_t innerWindowID = 0;
4039   if (aDocument) {
4040     if (!aURI) {
4041       aURI = aDocument->GetDocumentURI();
4042     }
4043     innerWindowID = aDocument->InnerWindowID();
4044   }
4045 
4046   return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
4047                                    innerWindowID, aURI, aSourceLine,
4048                                    aLineNumber, aColumnNumber, aLocationMode);
4049 }
4050 
4051 /* static */
ReportToConsoleByWindowID(const nsAString & aErrorText,uint32_t aErrorFlags,const nsACString & aCategory,uint64_t aInnerWindowID,nsIURI * aURI,const nsString & aSourceLine,uint32_t aLineNumber,uint32_t aColumnNumber,MissingErrorLocationMode aLocationMode)4052 nsresult nsContentUtils::ReportToConsoleByWindowID(
4053     const nsAString& aErrorText, uint32_t aErrorFlags,
4054     const nsACString& aCategory, uint64_t aInnerWindowID, nsIURI* aURI,
4055     const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4056     MissingErrorLocationMode aLocationMode) {
4057   nsresult rv;
4058   if (!sConsoleService) {  // only need to bother null-checking here
4059     rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4060     NS_ENSURE_SUCCESS(rv, rv);
4061   }
4062 
4063   nsAutoString spec;
4064   if (!aLineNumber && aLocationMode == eUSE_CALLING_LOCATION) {
4065     JSContext* cx = GetCurrentJSContext();
4066     if (cx) {
4067       nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber, &aColumnNumber);
4068     }
4069   }
4070 
4071   nsCOMPtr<nsIScriptError> errorObject =
4072       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
4073   NS_ENSURE_SUCCESS(rv, rv);
4074 
4075   if (!spec.IsEmpty()) {
4076     rv = errorObject->InitWithWindowID(aErrorText,
4077                                        spec,  // file name
4078                                        aSourceLine, aLineNumber, aColumnNumber,
4079                                        aErrorFlags, aCategory, aInnerWindowID);
4080   } else {
4081     rv = errorObject->InitWithSourceURI(aErrorText, aURI, aSourceLine,
4082                                         aLineNumber, aColumnNumber, aErrorFlags,
4083                                         aCategory, aInnerWindowID);
4084   }
4085   NS_ENSURE_SUCCESS(rv, rv);
4086 
4087   return sConsoleService->LogMessage(errorObject);
4088 }
4089 
LogMessageToConsole(const char * aMsg)4090 void nsContentUtils::LogMessageToConsole(const char* aMsg) {
4091   if (!sConsoleService) {  // only need to bother null-checking here
4092     CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4093     if (!sConsoleService) {
4094       return;
4095     }
4096   }
4097   sConsoleService->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
4098 }
4099 
IsChromeDoc(const Document * aDocument)4100 bool nsContentUtils::IsChromeDoc(const Document* aDocument) {
4101   return aDocument && aDocument->NodePrincipal() == sSystemPrincipal;
4102 }
4103 
IsChildOfSameType(Document * aDoc)4104 bool nsContentUtils::IsChildOfSameType(Document* aDoc) {
4105   if (BrowsingContext* bc = aDoc->GetBrowsingContext()) {
4106     return bc->GetParent();
4107   }
4108   return false;
4109 }
4110 
IsPlainTextType(const nsACString & aContentType)4111 bool nsContentUtils::IsPlainTextType(const nsACString& aContentType) {
4112   // NOTE: if you add a type here, add it to the CONTENTDLF_CATEGORIES
4113   // define in nsContentDLF.h as well.
4114   return aContentType.EqualsLiteral(TEXT_PLAIN) ||
4115          aContentType.EqualsLiteral(TEXT_CSS) ||
4116          aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4117          aContentType.EqualsLiteral(TEXT_VTT) ||
4118          aContentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
4119          aContentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
4120          aContentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
4121          aContentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
4122          aContentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
4123          aContentType.EqualsLiteral(APPLICATION_JSON) ||
4124          aContentType.EqualsLiteral(TEXT_JSON);
4125 }
4126 
IsUtf8OnlyPlainTextType(const nsACString & aContentType)4127 bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) {
4128   // NOTE: This must be a subset of the list in IsPlainTextType().
4129   return aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4130          aContentType.EqualsLiteral(APPLICATION_JSON) ||
4131          aContentType.EqualsLiteral(TEXT_JSON) ||
4132          aContentType.EqualsLiteral(TEXT_VTT);
4133 }
4134 
IsInChromeDocshell(const Document * aDocument)4135 bool nsContentUtils::IsInChromeDocshell(const Document* aDocument) {
4136   return aDocument && aDocument->IsInChromeDocShell();
4137 }
4138 
4139 // static
GetContentPolicy()4140 nsIContentPolicy* nsContentUtils::GetContentPolicy() {
4141   if (!sTriedToGetContentPolicy) {
4142     CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
4143     // It's OK to not have a content policy service
4144     sTriedToGetContentPolicy = true;
4145   }
4146 
4147   return sContentPolicyService;
4148 }
4149 
4150 // static
IsEventAttributeName(nsAtom * aName,int32_t aType)4151 bool nsContentUtils::IsEventAttributeName(nsAtom* aName, int32_t aType) {
4152   const char16_t* name = aName->GetUTF16String();
4153   if (name[0] != 'o' || name[1] != 'n') {
4154     return false;
4155   }
4156 
4157   EventNameMapping mapping;
4158   return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
4159 }
4160 
4161 // static
GetEventMessage(nsAtom * aName)4162 EventMessage nsContentUtils::GetEventMessage(nsAtom* aName) {
4163   MOZ_ASSERT(NS_IsMainThread(), "sAtomEventTable is not threadsafe");
4164   if (aName) {
4165     EventNameMapping mapping;
4166     if (sAtomEventTable->Get(aName, &mapping)) {
4167       return mapping.mMessage;
4168     }
4169   }
4170 
4171   return eUnidentifiedEvent;
4172 }
4173 
4174 // static
GetEventClassID(const nsAString & aName)4175 mozilla::EventClassID nsContentUtils::GetEventClassID(const nsAString& aName) {
4176   EventNameMapping mapping;
4177   if (sStringEventTable->Get(aName, &mapping)) return mapping.mEventClassID;
4178 
4179   return eBasicEventClass;
4180 }
4181 
GetEventMessageAndAtom(const nsAString & aName,mozilla::EventClassID aEventClassID,EventMessage * aEventMessage)4182 nsAtom* nsContentUtils::GetEventMessageAndAtom(
4183     const nsAString& aName, mozilla::EventClassID aEventClassID,
4184     EventMessage* aEventMessage) {
4185   MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4186   EventNameMapping mapping;
4187   if (sStringEventTable->Get(aName, &mapping)) {
4188     *aEventMessage = mapping.mEventClassID == aEventClassID
4189                          ? mapping.mMessage
4190                          : eUnidentifiedEvent;
4191     return mapping.mAtom;
4192   }
4193 
4194   // If we have cached lots of user defined event names, clear some of them.
4195   if (sUserDefinedEvents->Length() > 127) {
4196     while (sUserDefinedEvents->Length() > 64) {
4197       nsAtom* first = sUserDefinedEvents->ElementAt(0);
4198       sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
4199       sUserDefinedEvents->RemoveElementAt(0);
4200     }
4201   }
4202 
4203   *aEventMessage = eUnidentifiedEvent;
4204   RefPtr<nsAtom> atom = NS_AtomizeMainThread(u"on"_ns + aName);
4205   sUserDefinedEvents->AppendElement(atom);
4206   mapping.mAtom = atom;
4207   mapping.mMessage = eUnidentifiedEvent;
4208   mapping.mType = EventNameType_None;
4209   mapping.mEventClassID = eBasicEventClass;
4210   // This is a slow hashtable call, but at least we cache the result for the
4211   // following calls. Because GetEventMessageAndAtomForListener utilizes
4212   // sStringEventTable, it needs to know in which cases sStringEventTable
4213   // doesn't contain the information it needs so that it can use
4214   // sAtomEventTable instead.
4215   mapping.mMaybeSpecialSVGorSMILEvent =
4216       GetEventMessage(atom) != eUnidentifiedEvent;
4217   sStringEventTable->InsertOrUpdate(aName, mapping);
4218   return mapping.mAtom;
4219 }
4220 
4221 // static
GetEventMessageAndAtomForListener(const nsAString & aName,nsAtom ** aOnName)4222 EventMessage nsContentUtils::GetEventMessageAndAtomForListener(
4223     const nsAString& aName, nsAtom** aOnName) {
4224   MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4225 
4226   // Because of SVG/SMIL sStringEventTable contains a subset of the event names
4227   // comparing to the sAtomEventTable. However, usually sStringEventTable
4228   // contains the information we need, so in order to reduce hashtable
4229   // lookups, start from it.
4230   EventNameMapping mapping;
4231   EventMessage msg = eUnidentifiedEvent;
4232   RefPtr<nsAtom> atom;
4233   if (sStringEventTable->Get(aName, &mapping)) {
4234     if (mapping.mMaybeSpecialSVGorSMILEvent) {
4235       // Try the atom version so that we should get the right message for
4236       // SVG/SMIL.
4237       atom = NS_AtomizeMainThread(u"on"_ns + aName);
4238       msg = GetEventMessage(atom);
4239     } else {
4240       atom = mapping.mAtom;
4241       msg = mapping.mMessage;
4242     }
4243     atom.forget(aOnName);
4244     return msg;
4245   }
4246 
4247   // GetEventMessageAndAtom will cache the event type for the future usage...
4248   GetEventMessageAndAtom(aName, eBasicEventClass, &msg);
4249 
4250   // ...and then call this method recursively to get the message and atom from
4251   // now updated sStringEventTable.
4252   return GetEventMessageAndAtomForListener(aName, aOnName);
4253 }
4254 
GetEventAndTarget(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,Composed aComposed,Trusted aTrusted,Event ** aEvent,EventTarget ** aTargetOut)4255 static nsresult GetEventAndTarget(Document* aDoc, nsISupports* aTarget,
4256                                   const nsAString& aEventName,
4257                                   CanBubble aCanBubble, Cancelable aCancelable,
4258                                   Composed aComposed, Trusted aTrusted,
4259                                   Event** aEvent, EventTarget** aTargetOut) {
4260   nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4261   NS_ENSURE_TRUE(aDoc && target, NS_ERROR_INVALID_ARG);
4262 
4263   ErrorResult err;
4264   RefPtr<Event> event =
4265       aDoc->CreateEvent(u"Events"_ns, CallerType::System, err);
4266   if (NS_WARN_IF(err.Failed())) {
4267     return err.StealNSResult();
4268   }
4269 
4270   event->InitEvent(aEventName, aCanBubble, aCancelable, aComposed);
4271   event->SetTrusted(aTrusted == Trusted::eYes);
4272 
4273   event->SetTarget(target);
4274 
4275   event.forget(aEvent);
4276   target.forget(aTargetOut);
4277   return NS_OK;
4278 }
4279 
4280 // static
DispatchTrustedEvent(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,Composed aComposed,bool * aDefaultAction)4281 nsresult nsContentUtils::DispatchTrustedEvent(
4282     Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4283     CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4284     bool* aDefaultAction) {
4285   MOZ_ASSERT(!aEventName.EqualsLiteral("input") &&
4286                  !aEventName.EqualsLiteral("beforeinput"),
4287              "Use DispatchInputEvent() instead");
4288   return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4289                        aComposed, Trusted::eYes, aDefaultAction);
4290 }
4291 
4292 // static
DispatchUntrustedEvent(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,bool * aDefaultAction)4293 nsresult nsContentUtils::DispatchUntrustedEvent(
4294     Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4295     CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4296   return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4297                        Composed::eDefault, Trusted::eNo, aDefaultAction);
4298 }
4299 
4300 // static
DispatchEvent(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,Composed aComposed,Trusted aTrusted,bool * aDefaultAction,ChromeOnlyDispatch aOnlyChromeDispatch)4301 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4302                                        const nsAString& aEventName,
4303                                        CanBubble aCanBubble,
4304                                        Cancelable aCancelable,
4305                                        Composed aComposed, Trusted aTrusted,
4306                                        bool* aDefaultAction,
4307                                        ChromeOnlyDispatch aOnlyChromeDispatch) {
4308   RefPtr<Event> event;
4309   nsCOMPtr<EventTarget> target;
4310   nsresult rv = GetEventAndTarget(
4311       aDoc, aTarget, aEventName, aCanBubble, aCancelable, aComposed, aTrusted,
4312       getter_AddRefs(event), getter_AddRefs(target));
4313   NS_ENSURE_SUCCESS(rv, rv);
4314   event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch =
4315       aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4316 
4317   ErrorResult err;
4318   bool doDefault = target->DispatchEvent(*event, CallerType::System, err);
4319   if (aDefaultAction) {
4320     *aDefaultAction = doDefault;
4321   }
4322   return err.StealNSResult();
4323 }
4324 
4325 // static
DispatchEvent(Document * aDoc,nsISupports * aTarget,WidgetEvent & aEvent,EventMessage aEventMessage,CanBubble aCanBubble,Cancelable aCancelable,Trusted aTrusted,bool * aDefaultAction,ChromeOnlyDispatch aOnlyChromeDispatch)4326 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4327                                        WidgetEvent& aEvent,
4328                                        EventMessage aEventMessage,
4329                                        CanBubble aCanBubble,
4330                                        Cancelable aCancelable, Trusted aTrusted,
4331                                        bool* aDefaultAction,
4332                                        ChromeOnlyDispatch aOnlyChromeDispatch) {
4333   MOZ_ASSERT_IF(aOnlyChromeDispatch == ChromeOnlyDispatch::eYes,
4334                 aTrusted == Trusted::eYes);
4335 
4336   nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4337 
4338   aEvent.mTime = PR_Now();
4339 
4340   aEvent.mSpecifiedEventType = GetEventTypeFromMessage(aEventMessage);
4341   aEvent.SetDefaultComposed();
4342   aEvent.SetDefaultComposedInNativeAnonymousContent();
4343 
4344   aEvent.mFlags.mBubbles = aCanBubble == CanBubble::eYes;
4345   aEvent.mFlags.mCancelable = aCancelable == Cancelable::eYes;
4346   aEvent.mFlags.mOnlyChromeDispatch =
4347       aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4348 
4349   aEvent.mTarget = target;
4350 
4351   nsEventStatus status = nsEventStatus_eIgnore;
4352   nsresult rv = EventDispatcher::DispatchDOMEvent(target, &aEvent, nullptr,
4353                                                   nullptr, &status);
4354   if (aDefaultAction) {
4355     *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
4356   }
4357   return rv;
4358 }
4359 
4360 // static
DispatchInputEvent(Element * aEventTarget)4361 nsresult nsContentUtils::DispatchInputEvent(Element* aEventTarget) {
4362   return DispatchInputEvent(aEventTarget, mozilla::eEditorInput,
4363                             mozilla::EditorInputType::eUnknown, nullptr,
4364                             InputEventOptions());
4365 }
4366 
4367 // static
DispatchInputEvent(Element * aEventTargetElement,EventMessage aEventMessage,EditorInputType aEditorInputType,EditorBase * aEditorBase,InputEventOptions && aOptions,nsEventStatus * aEventStatus)4368 nsresult nsContentUtils::DispatchInputEvent(
4369     Element* aEventTargetElement, EventMessage aEventMessage,
4370     EditorInputType aEditorInputType, EditorBase* aEditorBase,
4371     InputEventOptions&& aOptions, nsEventStatus* aEventStatus /* = nullptr */) {
4372   MOZ_ASSERT(aEventMessage == eEditorInput ||
4373              aEventMessage == eEditorBeforeInput);
4374 
4375   if (NS_WARN_IF(!aEventTargetElement)) {
4376     return NS_ERROR_INVALID_ARG;
4377   }
4378 
4379   // If this is called from editor, the instance should be set to aEditorBase.
4380   // Otherwise, we need to look for an editor for aEventTargetElement.
4381   // However, we don't need to do it for HTMLEditor since nobody shouldn't
4382   // dispatch "beforeinput" nor "input" event for HTMLEditor except HTMLEditor
4383   // itself.
4384   bool useInputEvent = false;
4385   if (aEditorBase) {
4386     useInputEvent = true;
4387   } else if (HTMLTextAreaElement* textAreaElement =
4388                  HTMLTextAreaElement::FromNode(aEventTargetElement)) {
4389     aEditorBase = textAreaElement->GetTextEditorWithoutCreation();
4390     useInputEvent = true;
4391   } else if (HTMLInputElement* inputElement =
4392                  HTMLInputElement::FromNode(aEventTargetElement)) {
4393     if (inputElement->IsInputEventTarget()) {
4394       aEditorBase = inputElement->GetTextEditorWithoutCreation();
4395       useInputEvent = true;
4396     }
4397   }
4398 #ifdef DEBUG
4399   else {
4400     MOZ_ASSERT(!aEventTargetElement->IsTextControlElement(),
4401                "The event target may have editor, but we've not known it yet.");
4402   }
4403 #endif  // #ifdef DEBUG
4404 
4405   if (!useInputEvent) {
4406     MOZ_ASSERT(aEventMessage == eEditorInput);
4407     MOZ_ASSERT(aEditorInputType == EditorInputType::eUnknown);
4408     MOZ_ASSERT(!aOptions.mNeverCancelable);
4409     // Dispatch "input" event with Event instance.
4410     WidgetEvent widgetEvent(true, eUnidentifiedEvent);
4411     widgetEvent.mSpecifiedEventType = nsGkAtoms::oninput;
4412     widgetEvent.mFlags.mCancelable = false;
4413     widgetEvent.mFlags.mComposed = true;
4414     // Using same time as nsContentUtils::DispatchEvent() for backward
4415     // compatibility.
4416     widgetEvent.mTime = PR_Now();
4417     (new AsyncEventDispatcher(aEventTargetElement, widgetEvent))
4418         ->RunDOMEventWhenSafe();
4419     return NS_OK;
4420   }
4421 
4422   MOZ_ASSERT_IF(aEventMessage != eEditorBeforeInput,
4423                 !aOptions.mNeverCancelable);
4424   MOZ_ASSERT_IF(
4425       aEventMessage == eEditorBeforeInput && aOptions.mNeverCancelable,
4426       aEditorInputType == EditorInputType::eInsertReplacementText);
4427 
4428   nsCOMPtr<nsIWidget> widget;
4429   if (aEditorBase) {
4430     widget = aEditorBase->GetWidget();
4431     if (NS_WARN_IF(!widget)) {
4432       return NS_ERROR_FAILURE;
4433     }
4434   } else {
4435     Document* document = aEventTargetElement->OwnerDoc();
4436     if (NS_WARN_IF(!document)) {
4437       return NS_ERROR_FAILURE;
4438     }
4439     // If we're running xpcshell tests, we fail to get presShell here.
4440     // Even in such case, we need to dispatch "input" event without widget.
4441     PresShell* presShell = document->GetPresShell();
4442     if (presShell) {
4443       nsPresContext* presContext = presShell->GetPresContext();
4444       if (NS_WARN_IF(!presContext)) {
4445         return NS_ERROR_FAILURE;
4446       }
4447       widget = presContext->GetRootWidget();
4448       if (NS_WARN_IF(!widget)) {
4449         return NS_ERROR_FAILURE;
4450       }
4451     }
4452   }
4453 
4454   // Dispatch "input" event with InputEvent instance.
4455   InternalEditorInputEvent inputEvent(true, aEventMessage, widget);
4456 
4457   inputEvent.mFlags.mCancelable =
4458       !aOptions.mNeverCancelable && aEventMessage == eEditorBeforeInput &&
4459       IsCancelableBeforeInputEvent(aEditorInputType);
4460   MOZ_ASSERT(!inputEvent.mFlags.mCancelable || aEventStatus);
4461 
4462   // Using same time as old event dispatcher in EditorBase for backward
4463   // compatibility.
4464   inputEvent.mTime = static_cast<uint64_t>(PR_Now() / 1000);
4465 
4466   // If there is an editor, set isComposing to true when it has composition.
4467   // Note that EditorBase::IsIMEComposing() may return false even when we
4468   // need to set it to true.
4469   // Otherwise, i.e., editor hasn't been created for the element yet,
4470   // we should set isComposing to false since the element can never has
4471   // composition without editor.
4472   inputEvent.mIsComposing = aEditorBase && aEditorBase->GetComposition();
4473 
4474   if (!aEditorBase || aEditorBase->IsTextEditor()) {
4475     if (IsDataAvailableOnTextEditor(aEditorInputType)) {
4476       inputEvent.mData = std::move(aOptions.mData);
4477       MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4478                  "inputEvent.mData shouldn't be void");
4479     }
4480 #ifdef DEBUG
4481     else {
4482       MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4483     }
4484 #endif  // #ifdef DEBUG
4485     MOZ_ASSERT(
4486         aOptions.mTargetRanges.IsEmpty(),
4487         "Target ranges for <input> and <textarea> should always be empty");
4488   } else {
4489     MOZ_ASSERT(aEditorBase->IsHTMLEditor());
4490     if (IsDataAvailableOnHTMLEditor(aEditorInputType)) {
4491       inputEvent.mData = std::move(aOptions.mData);
4492       MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4493                  "inputEvent.mData shouldn't be void");
4494     } else {
4495       MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4496       if (IsDataTransferAvailableOnHTMLEditor(aEditorInputType)) {
4497         inputEvent.mDataTransfer = std::move(aOptions.mDataTransfer);
4498         MOZ_ASSERT(inputEvent.mDataTransfer,
4499                    "inputEvent.mDataTransfer shouldn't be nullptr");
4500         MOZ_ASSERT(inputEvent.mDataTransfer->IsReadOnly(),
4501                    "inputEvent.mDataTransfer should be read only");
4502       }
4503 #ifdef DEBUG
4504       else {
4505         MOZ_ASSERT(!inputEvent.mDataTransfer,
4506                    "inputEvent.mDataTransfer should be nullptr");
4507       }
4508 #endif  // #ifdef DEBUG
4509     }
4510     if (aEventMessage == eEditorBeforeInput &&
4511         MayHaveTargetRangesOnHTMLEditor(aEditorInputType)) {
4512       inputEvent.mTargetRanges = std::move(aOptions.mTargetRanges);
4513     }
4514 #ifdef DEBUG
4515     else {
4516       MOZ_ASSERT(aOptions.mTargetRanges.IsEmpty(),
4517                  "Target ranges shouldn't be set for the dispatching event");
4518     }
4519 #endif  // #ifdef DEBUG
4520   }
4521 
4522   inputEvent.mInputType = aEditorInputType;
4523 
4524   if (!IsSafeToRunScript()) {
4525     // If we cannot dispatch an event right now, we cannot make it cancelable.
4526     NS_ASSERTION(
4527         !inputEvent.mFlags.mCancelable,
4528         "Cancelable beforeinput event dispatcher should run when it's safe");
4529     inputEvent.mFlags.mCancelable = false;
4530     (new AsyncEventDispatcher(aEventTargetElement, inputEvent))
4531         ->RunDOMEventWhenSafe();
4532     return NS_OK;
4533   }
4534 
4535   // If we're running xpcshell tests, we fail to get presShell here.
4536   // Even in such case, we need to dispatch "input" event without widget.
4537   RefPtr<nsPresContext> presContext =
4538       aEventTargetElement->OwnerDoc()->GetPresContext();
4539   nsresult rv = EventDispatcher::Dispatch(aEventTargetElement, presContext,
4540                                           &inputEvent, nullptr, aEventStatus);
4541   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
4542                        "Dispatching `beforeinput` or `input` event failed");
4543   return rv;
4544 }
4545 
DispatchChromeEvent(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,bool * aDefaultAction)4546 nsresult nsContentUtils::DispatchChromeEvent(
4547     Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4548     CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4549   RefPtr<Event> event;
4550   nsCOMPtr<EventTarget> target;
4551   nsresult rv = GetEventAndTarget(
4552       aDoc, aTarget, aEventName, aCanBubble, aCancelable, Composed::eDefault,
4553       Trusted::eYes, getter_AddRefs(event), getter_AddRefs(target));
4554   NS_ENSURE_SUCCESS(rv, rv);
4555 
4556   NS_ASSERTION(aDoc, "GetEventAndTarget lied?");
4557   if (!aDoc->GetWindow()) return NS_ERROR_INVALID_ARG;
4558 
4559   EventTarget* piTarget = aDoc->GetWindow()->GetParentTarget();
4560   if (!piTarget) return NS_ERROR_INVALID_ARG;
4561 
4562   ErrorResult err;
4563   bool defaultActionEnabled =
4564       piTarget->DispatchEvent(*event, CallerType::System, err);
4565   if (aDefaultAction) {
4566     *aDefaultAction = defaultActionEnabled;
4567   }
4568   return err.StealNSResult();
4569 }
4570 
RequestFrameFocus(Element & aFrameElement,bool aCanRaise,CallerType aCallerType)4571 void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise,
4572                                        CallerType aCallerType) {
4573   RefPtr<Element> target = &aFrameElement;
4574   bool defaultAction = true;
4575   if (aCanRaise) {
4576     DispatchEventOnlyToChrome(target->OwnerDoc(), target,
4577                               u"framefocusrequested"_ns, CanBubble::eYes,
4578                               Cancelable::eYes, &defaultAction);
4579   }
4580   if (!defaultAction) {
4581     return;
4582   }
4583 
4584   RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
4585   if (!fm) {
4586     return;
4587   }
4588 
4589   uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
4590   if (aCanRaise) {
4591     flags |= nsIFocusManager::FLAG_RAISE;
4592   }
4593 
4594   if (aCallerType == CallerType::NonSystem) {
4595     flags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
4596   }
4597 
4598   fm->SetFocus(target, flags);
4599 }
4600 
DispatchEventOnlyToChrome(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,Composed aComposed,bool * aDefaultAction)4601 nsresult nsContentUtils::DispatchEventOnlyToChrome(
4602     Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4603     CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4604     bool* aDefaultAction) {
4605   return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4606                        aComposed, Trusted::eYes, aDefaultAction,
4607                        ChromeOnlyDispatch::eYes);
4608 }
4609 
4610 /* static */
MatchElementId(nsIContent * aContent,const nsAtom * aId)4611 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4612                                         const nsAtom* aId) {
4613   for (nsIContent* cur = aContent; cur; cur = cur->GetNextNode(aContent)) {
4614     if (aId == cur->GetID()) {
4615       return cur->AsElement();
4616     }
4617   }
4618 
4619   return nullptr;
4620 }
4621 
4622 /* static */
MatchElementId(nsIContent * aContent,const nsAString & aId)4623 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4624                                         const nsAString& aId) {
4625   MOZ_ASSERT(!aId.IsEmpty(), "Will match random elements");
4626 
4627   // ID attrs are generally stored as atoms, so just atomize this up front
4628   RefPtr<nsAtom> id(NS_Atomize(aId));
4629   if (!id) {
4630     // OOM, so just bail
4631     return nullptr;
4632   }
4633 
4634   return MatchElementId(aContent, id);
4635 }
4636 
4637 /* static */
RegisterShutdownObserver(nsIObserver * aObserver)4638 void nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver) {
4639   nsCOMPtr<nsIObserverService> observerService =
4640       mozilla::services::GetObserverService();
4641   if (observerService) {
4642     observerService->AddObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
4643                                  false);
4644   }
4645 }
4646 
4647 /* static */
UnregisterShutdownObserver(nsIObserver * aObserver)4648 void nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver) {
4649   nsCOMPtr<nsIObserverService> observerService =
4650       mozilla::services::GetObserverService();
4651   if (observerService) {
4652     observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
4653   }
4654 }
4655 
4656 /* static */
HasNonEmptyAttr(const nsIContent * aContent,int32_t aNameSpaceID,nsAtom * aName)4657 bool nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent,
4658                                      int32_t aNameSpaceID, nsAtom* aName) {
4659   static Element::AttrValuesArray strings[] = {nsGkAtoms::_empty, nullptr};
4660   return aContent->IsElement() &&
4661          aContent->AsElement()->FindAttrValueIn(aNameSpaceID, aName, strings,
4662                                                 eCaseMatters) ==
4663              Element::ATTR_VALUE_NO_MATCH;
4664 }
4665 
4666 /* static */
HasMutationListeners(nsINode * aNode,uint32_t aType,nsINode * aTargetForSubtreeModified)4667 bool nsContentUtils::HasMutationListeners(nsINode* aNode, uint32_t aType,
4668                                           nsINode* aTargetForSubtreeModified) {
4669   Document* doc = aNode->OwnerDoc();
4670 
4671   // global object will be null for documents that don't have windows.
4672   nsPIDOMWindowInner* window = doc->GetInnerWindow();
4673   // This relies on EventListenerManager::AddEventListener, which sets
4674   // all mutation bits when there is a listener for DOMSubtreeModified event.
4675   if (window && !window->HasMutationListeners(aType)) {
4676     return false;
4677   }
4678 
4679   if (aNode->ChromeOnlyAccess() || aNode->IsInShadowTree()) {
4680     return false;
4681   }
4682 
4683   doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
4684 
4685   // If we have a window, we can check it for mutation listeners now.
4686   if (aNode->IsInUncomposedDoc()) {
4687     nsCOMPtr<EventTarget> piTarget(do_QueryInterface(window));
4688     if (piTarget) {
4689       EventListenerManager* manager = piTarget->GetExistingListenerManager();
4690       if (manager && manager->HasMutationListeners()) {
4691         return true;
4692       }
4693     }
4694   }
4695 
4696   // If we have a window, we know a mutation listener is registered, but it
4697   // might not be in our chain.  If we don't have a window, we might have a
4698   // mutation listener.  Check quickly to see.
4699   while (aNode) {
4700     EventListenerManager* manager = aNode->GetExistingListenerManager();
4701     if (manager && manager->HasMutationListeners()) {
4702       return true;
4703     }
4704 
4705     aNode = aNode->GetParentNode();
4706   }
4707 
4708   return false;
4709 }
4710 
4711 /* static */
HasMutationListeners(Document * aDocument,uint32_t aType)4712 bool nsContentUtils::HasMutationListeners(Document* aDocument, uint32_t aType) {
4713   nsPIDOMWindowInner* window =
4714       aDocument ? aDocument->GetInnerWindow() : nullptr;
4715 
4716   // This relies on EventListenerManager::AddEventListener, which sets
4717   // all mutation bits when there is a listener for DOMSubtreeModified event.
4718   return !window || window->HasMutationListeners(aType);
4719 }
4720 
MaybeFireNodeRemoved(nsINode * aChild,nsINode * aParent)4721 void nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent) {
4722   MOZ_ASSERT(aChild, "Missing child");
4723   MOZ_ASSERT(aChild->GetParentNode() == aParent, "Wrong parent");
4724   MOZ_ASSERT(aChild->OwnerDoc() == aParent->OwnerDoc(), "Wrong owner-doc");
4725 
4726   // Having an explicit check here since it's an easy mistake to fall into,
4727   // and there might be existing code with problems. We'd rather be safe
4728   // than fire DOMNodeRemoved in all corner cases. We also rely on it for
4729   // nsAutoScriptBlockerSuppressNodeRemoved.
4730   if (!IsSafeToRunScript()) {
4731     // This checks that IsSafeToRunScript is true since we don't want to fire
4732     // events when that is false. We can't rely on EventDispatcher to assert
4733     // this in this situation since most of the time there are no mutation
4734     // event listeners, in which case we won't even attempt to dispatch events.
4735     // However this also allows for two exceptions. First off, we don't assert
4736     // if the mutation happens to native anonymous content since we never fire
4737     // mutation events on such content anyway.
4738     // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
4739     // that is a know case when we'd normally fire a mutation event, but can't
4740     // make that safe and so we suppress it at this time. Ideally this should
4741     // go away eventually.
4742     if (!aChild->IsInNativeAnonymousSubtree() &&
4743         !sDOMNodeRemovedSuppressCount) {
4744       NS_ERROR("Want to fire DOMNodeRemoved event, but it's not safe");
4745       WarnScriptWasIgnored(aChild->OwnerDoc());
4746     }
4747     return;
4748   }
4749 
4750   {
4751     Document* doc = aParent->OwnerDoc();
4752     if (MOZ_UNLIKELY(doc->DevToolsWatchingDOMMutations()) &&
4753         aChild->IsInComposedDoc() && !aChild->ChromeOnlyAccess()) {
4754       DispatchChromeEvent(doc, aChild, u"devtoolschildremoved"_ns,
4755                           CanBubble::eNo, Cancelable::eNo);
4756     }
4757   }
4758 
4759   if (HasMutationListeners(aChild, NS_EVENT_BITS_MUTATION_NODEREMOVED,
4760                            aParent)) {
4761     InternalMutationEvent mutation(true, eLegacyNodeRemoved);
4762     mutation.mRelatedNode = aParent;
4763 
4764     mozAutoSubtreeModified subtree(aParent->OwnerDoc(), aParent);
4765     EventDispatcher::Dispatch(aChild, nullptr, &mutation);
4766   }
4767 }
4768 
UnmarkGrayJSListenersInCCGenerationDocuments()4769 void nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments() {
4770   if (!sEventListenerManagersHash) {
4771     return;
4772   }
4773 
4774   for (auto i = sEventListenerManagersHash->Iter(); !i.Done(); i.Next()) {
4775     auto entry = static_cast<EventListenerManagerMapEntry*>(i.Get());
4776     nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
4777     if (n && n->IsInComposedDoc() &&
4778         nsCCUncollectableMarker::InGeneration(
4779             n->OwnerDoc()->GetMarkedCCGeneration())) {
4780       entry->mListenerManager->MarkForCC();
4781     }
4782   }
4783 }
4784 
4785 /* static */
TraverseListenerManager(nsINode * aNode,nsCycleCollectionTraversalCallback & cb)4786 void nsContentUtils::TraverseListenerManager(
4787     nsINode* aNode, nsCycleCollectionTraversalCallback& cb) {
4788   if (!sEventListenerManagersHash) {
4789     // We're already shut down, just return.
4790     return;
4791   }
4792 
4793   auto entry = static_cast<EventListenerManagerMapEntry*>(
4794       sEventListenerManagersHash->Search(aNode));
4795   if (entry) {
4796     CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
4797                              "[via hash] mListenerManager");
4798   }
4799 }
4800 
GetListenerManagerForNode(nsINode * aNode)4801 EventListenerManager* nsContentUtils::GetListenerManagerForNode(
4802     nsINode* aNode) {
4803   if (!sEventListenerManagersHash) {
4804     // We're already shut down, don't bother creating an event listener
4805     // manager.
4806 
4807     return nullptr;
4808   }
4809 
4810   auto entry = static_cast<EventListenerManagerMapEntry*>(
4811       sEventListenerManagersHash->Add(aNode, fallible));
4812 
4813   if (!entry) {
4814     return nullptr;
4815   }
4816 
4817   if (!entry->mListenerManager) {
4818     entry->mListenerManager = new EventListenerManager(aNode);
4819 
4820     aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
4821   }
4822 
4823   return entry->mListenerManager;
4824 }
4825 
GetExistingListenerManagerForNode(const nsINode * aNode)4826 EventListenerManager* nsContentUtils::GetExistingListenerManagerForNode(
4827     const nsINode* aNode) {
4828   if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
4829     return nullptr;
4830   }
4831 
4832   if (!sEventListenerManagersHash) {
4833     // We're already shut down, don't bother creating an event listener
4834     // manager.
4835 
4836     return nullptr;
4837   }
4838 
4839   auto entry = static_cast<EventListenerManagerMapEntry*>(
4840       sEventListenerManagersHash->Search(aNode));
4841   if (entry) {
4842     return entry->mListenerManager;
4843   }
4844 
4845   return nullptr;
4846 }
4847 
AddEntryToDOMArenaTable(nsINode * aNode,DOMArena * aDOMArena)4848 void nsContentUtils::AddEntryToDOMArenaTable(nsINode* aNode,
4849                                              DOMArena* aDOMArena) {
4850   MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
4851   MOZ_ASSERT_IF(sDOMArenaHashtable, !sDOMArenaHashtable->Contains(aNode));
4852   MOZ_ASSERT(!aNode->HasFlag(NODE_KEEPS_DOMARENA));
4853   if (!sDOMArenaHashtable) {
4854     sDOMArenaHashtable =
4855         new nsRefPtrHashtable<nsPtrHashKey<const nsINode>, dom::DOMArena>();
4856   }
4857   aNode->SetFlags(NODE_KEEPS_DOMARENA);
4858   sDOMArenaHashtable->InsertOrUpdate(aNode, RefPtr<DOMArena>(aDOMArena));
4859 }
4860 
TakeEntryFromDOMArenaTable(const nsINode * aNode)4861 already_AddRefed<DOMArena> nsContentUtils::TakeEntryFromDOMArenaTable(
4862     const nsINode* aNode) {
4863   MOZ_ASSERT(sDOMArenaHashtable->Contains(aNode));
4864   MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
4865   RefPtr<DOMArena> arena;
4866   sDOMArenaHashtable->Remove(aNode, getter_AddRefs(arena));
4867   return arena.forget();
4868 }
4869 
4870 /* static */
RemoveListenerManager(nsINode * aNode)4871 void nsContentUtils::RemoveListenerManager(nsINode* aNode) {
4872   if (sEventListenerManagersHash) {
4873     auto entry = static_cast<EventListenerManagerMapEntry*>(
4874         sEventListenerManagersHash->Search(aNode));
4875     if (entry) {
4876       RefPtr<EventListenerManager> listenerManager;
4877       listenerManager.swap(entry->mListenerManager);
4878       // Remove the entry and *then* do operations that could cause further
4879       // modification of sEventListenerManagersHash.  See bug 334177.
4880       sEventListenerManagersHash->RawRemove(entry);
4881       if (listenerManager) {
4882         listenerManager->Disconnect();
4883       }
4884     }
4885   }
4886 }
4887 
4888 /* static */
IsValidNodeName(nsAtom * aLocalName,nsAtom * aPrefix,int32_t aNamespaceID)4889 bool nsContentUtils::IsValidNodeName(nsAtom* aLocalName, nsAtom* aPrefix,
4890                                      int32_t aNamespaceID) {
4891   if (aNamespaceID == kNameSpaceID_Unknown) {
4892     return false;
4893   }
4894 
4895   if (!aPrefix) {
4896     // If the prefix is null, then either the QName must be xmlns or the
4897     // namespace must not be XMLNS.
4898     return (aLocalName == nsGkAtoms::xmlns) ==
4899            (aNamespaceID == kNameSpaceID_XMLNS);
4900   }
4901 
4902   // If the prefix is non-null then the namespace must not be null.
4903   if (aNamespaceID == kNameSpaceID_None) {
4904     return false;
4905   }
4906 
4907   // If the namespace is the XMLNS namespace then the prefix must be xmlns,
4908   // but the localname must not be xmlns.
4909   if (aNamespaceID == kNameSpaceID_XMLNS) {
4910     return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
4911   }
4912 
4913   // If the namespace is not the XMLNS namespace then the prefix must not be
4914   // xmlns.
4915   // If the namespace is the XML namespace then the prefix can be anything.
4916   // If the namespace is not the XML namespace then the prefix must not be xml.
4917   return aPrefix != nsGkAtoms::xmlns &&
4918          (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
4919 }
4920 
CreateContextualFragment(nsINode * aContextNode,const nsAString & aFragment,bool aPreventScriptExecution,ErrorResult & aRv)4921 already_AddRefed<DocumentFragment> nsContentUtils::CreateContextualFragment(
4922     nsINode* aContextNode, const nsAString& aFragment,
4923     bool aPreventScriptExecution, ErrorResult& aRv) {
4924   if (!aContextNode) {
4925     aRv.Throw(NS_ERROR_INVALID_ARG);
4926     return nullptr;
4927   }
4928 
4929   // If we don't have a document here, we can't get the right security context
4930   // for compiling event handlers... so just bail out.
4931   RefPtr<Document> document = aContextNode->OwnerDoc();
4932   bool isHTML = document->IsHTMLDocument();
4933 
4934   if (isHTML) {
4935     RefPtr<DocumentFragment> frag = new (document->NodeInfoManager())
4936         DocumentFragment(document->NodeInfoManager());
4937 
4938     Element* element = aContextNode->GetAsElementOrParentElement();
4939     if (element && !element->IsHTMLElement(nsGkAtoms::html)) {
4940       aRv = ParseFragmentHTML(
4941           aFragment, frag, element->NodeInfo()->NameAtom(),
4942           element->GetNameSpaceID(),
4943           (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
4944           aPreventScriptExecution);
4945     } else {
4946       aRv = ParseFragmentHTML(
4947           aFragment, frag, nsGkAtoms::body, kNameSpaceID_XHTML,
4948           (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
4949           aPreventScriptExecution);
4950     }
4951 
4952     return frag.forget();
4953   }
4954 
4955   AutoTArray<nsString, 32> tagStack;
4956   nsAutoString uriStr, nameStr;
4957   for (Element* element : aContextNode->InclusiveAncestorsOfType<Element>()) {
4958     nsString& tagName = *tagStack.AppendElement();
4959     // It mostly doesn't actually matter what tag name we use here: XML doesn't
4960     // have parsing that depends on the open tag stack, apart from namespace
4961     // declarations.  So this whole tagStack bit is just there to get the right
4962     // namespace declarations to the XML parser.  That said, the parser _is_
4963     // going to create elements with the tag names we provide here, so we need
4964     // to make sure they are not names that can trigger custom element
4965     // constructors.  Just make up a name that is never going to be a valid
4966     // custom element name.
4967     //
4968     // The principled way to do this would probably be to add a new FromParser
4969     // value and make sure we use it when creating the context elements, then
4970     // make sure we teach all FromParser consumers (and in particular the custom
4971     // element code) about it as needed.  But right now the XML parser never
4972     // actually uses FromParser values other than NOT_FROM_PARSER, and changing
4973     // that is pretty complicated.
4974     tagName.AssignLiteral("notacustomelement");
4975 
4976     // see if we need to add xmlns declarations
4977     uint32_t count = element->GetAttrCount();
4978     bool setDefaultNamespace = false;
4979     if (count > 0) {
4980       uint32_t index;
4981 
4982       for (index = 0; index < count; index++) {
4983         const BorrowedAttrInfo info = element->GetAttrInfoAt(index);
4984         const nsAttrName* name = info.mName;
4985         if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
4986           info.mValue->ToString(uriStr);
4987 
4988           // really want something like nsXMLContentSerializer::SerializeAttr
4989           tagName.AppendLiteral(" xmlns");  // space important
4990           if (name->GetPrefix()) {
4991             tagName.Append(char16_t(':'));
4992             name->LocalName()->ToString(nameStr);
4993             tagName.Append(nameStr);
4994           } else {
4995             setDefaultNamespace = true;
4996           }
4997           tagName.AppendLiteral(R"(=")");
4998           tagName.Append(uriStr);
4999           tagName.Append('"');
5000         }
5001       }
5002     }
5003 
5004     if (!setDefaultNamespace) {
5005       mozilla::dom::NodeInfo* info = element->NodeInfo();
5006       if (!info->GetPrefixAtom() && info->NamespaceID() != kNameSpaceID_None) {
5007         // We have no namespace prefix, but have a namespace ID.  Push
5008         // default namespace attr in, so that our kids will be in our
5009         // namespace.
5010         info->GetNamespaceURI(uriStr);
5011         tagName.AppendLiteral(R"( xmlns=")");
5012         tagName.Append(uriStr);
5013         tagName.Append('"');
5014       }
5015     }
5016   }
5017 
5018   RefPtr<DocumentFragment> frag;
5019   aRv = ParseFragmentXML(aFragment, document, tagStack, aPreventScriptExecution,
5020                          -1, getter_AddRefs(frag));
5021   return frag.forget();
5022 }
5023 
5024 /* static */
5025 void nsContentUtils::DropFragmentParsers() {
5026   NS_IF_RELEASE(sHTMLFragmentParser);
5027   NS_IF_RELEASE(sXMLFragmentParser);
5028   NS_IF_RELEASE(sXMLFragmentSink);
5029 }
5030 
5031 /* static */
5032 void nsContentUtils::XPCOMShutdown() { nsContentUtils::DropFragmentParsers(); }
5033 
5034 /* Helper function to compuate Sanitization Flags for ParseFramentHTML/XML */
5035 uint32_t computeSanitizationFlags(nsIPrincipal* aPrincipal, int32_t aFlags) {
5036   uint32_t sanitizationFlags = 0;
5037   if (aPrincipal->IsSystemPrincipal()) {
5038     if (aFlags < 0) {
5039       // if this is a chrome-privileged document and no explicit flags
5040       // were passed, then use this sanitization flags.
5041       sanitizationFlags = nsIParserUtils::SanitizerAllowStyle |
5042                           nsIParserUtils::SanitizerAllowComments |
5043                           nsIParserUtils::SanitizerDropForms |
5044                           nsIParserUtils::SanitizerLogRemovals;
5045     } else {
5046       // if the caller explicitly passes flags, then we use those
5047       // flags but additionally drop forms.
5048       sanitizationFlags = aFlags | nsIParserUtils::SanitizerDropForms;
5049     }
5050   } else if (aFlags >= 0) {
5051     // aFlags by default is -1 and is only ever non equal to -1 if the
5052     // caller of ParseFragmentHTML/ParseFragmentXML is
5053     // ParserUtils::ParseFragment(). Only in that case we should use
5054     // the sanitization flags passed within aFlags.
5055     sanitizationFlags = aFlags;
5056   }
5057   return sanitizationFlags;
5058 }
5059 
5060 /* static */
5061 bool AllowsUnsanitizedContentForAboutNewTab(nsIPrincipal* aPrincipal) {
5062   if (StaticPrefs::dom_about_newtab_sanitization_enabled() ||
5063       !aPrincipal->SchemeIs("about")) {
5064     return false;
5065   }
5066   uint32_t aboutModuleFlags = 0;
5067   aPrincipal->GetAboutModuleFlags(&aboutModuleFlags);
5068   return aboutModuleFlags & nsIAboutModule::ALLOW_UNSANITIZED_CONTENT;
5069 }
5070 
5071 /* static */
5072 nsresult nsContentUtils::ParseFragmentHTML(
5073     const nsAString& aSourceBuffer, nsIContent* aTargetNode,
5074     nsAtom* aContextLocalName, int32_t aContextNamespace, bool aQuirks,
5075     bool aPreventScriptExecution, int32_t aFlags) {
5076   AutoTimelineMarker m(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");
5077 
5078   if (nsContentUtils::sFragmentParsingActive) {
5079     MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5080     return NS_ERROR_DOM_INVALID_STATE_ERR;
5081   }
5082   mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5083   nsContentUtils::sFragmentParsingActive = true;
5084   if (!sHTMLFragmentParser) {
5085     NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5086     // Now sHTMLFragmentParser owns the object
5087   }
5088 
5089   nsCOMPtr<nsIPrincipal> nodePrincipal = aTargetNode->NodePrincipal();
5090 
5091 #ifdef DEBUG
5092   // aFlags should always be -1 unless the caller of ParseFragmentHTML
5093   // is ParserUtils::ParseFragment() which is the only caller that intends
5094   // sanitization. For all other callers we need to ensure to call
5095   // AuditParsingOfHTMLXMLFragments.
5096   if (aFlags < 0) {
5097     DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5098                                                        aSourceBuffer);
5099   }
5100 #endif
5101 
5102   nsIContent* target = aTargetNode;
5103 
5104   RefPtr<Document> doc = aTargetNode->OwnerDoc();
5105   RefPtr<DocumentFragment> fragment;
5106   // We sanitize if the fragment occurs in a system privileged
5107   // context, an about: page, or if there are explicit sanitization flags.
5108   // Please note that about:blank and about:srcdoc inherit the security
5109   // context from the embedding context and hence are not loaded using
5110   // an about: scheme principal.
5111   bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5112                         nodePrincipal->SchemeIs("about") || aFlags >= 0;
5113   if (shouldSanitize &&
5114       !AllowsUnsanitizedContentForAboutNewTab(nodePrincipal)) {
5115     if (!doc->IsLoadedAsData()) {
5116       doc = nsContentUtils::CreateInertHTMLDocument(doc);
5117       if (!doc) {
5118         return NS_ERROR_FAILURE;
5119       }
5120     }
5121     fragment =
5122         new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager());
5123     target = fragment;
5124   }
5125 
5126   nsresult rv = sHTMLFragmentParser->ParseFragment(
5127       aSourceBuffer, target, aContextLocalName, aContextNamespace, aQuirks,
5128       aPreventScriptExecution);
5129   NS_ENSURE_SUCCESS(rv, rv);
5130 
5131   if (fragment) {
5132     uint32_t sanitizationFlags =
5133         computeSanitizationFlags(nodePrincipal, aFlags);
5134     // Don't fire mutation events for nodes removed by the sanitizer.
5135     nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5136     nsTreeSanitizer sanitizer(sanitizationFlags);
5137     sanitizer.Sanitize(fragment);
5138 
5139     ErrorResult error;
5140     aTargetNode->AppendChild(*fragment, error);
5141     rv = error.StealNSResult();
5142   }
5143 
5144   return rv;
5145 }
5146 
5147 /* static */
5148 nsresult nsContentUtils::ParseDocumentHTML(
5149     const nsAString& aSourceBuffer, Document* aTargetDocument,
5150     bool aScriptingEnabledForNoscriptParsing) {
5151   AutoTimelineMarker m(aTargetDocument->GetDocShell(), "Parse HTML");
5152 
5153   if (nsContentUtils::sFragmentParsingActive) {
5154     MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5155     return NS_ERROR_DOM_INVALID_STATE_ERR;
5156   }
5157   mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5158   nsContentUtils::sFragmentParsingActive = true;
5159   if (!sHTMLFragmentParser) {
5160     NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5161     // Now sHTMLFragmentParser owns the object
5162   }
5163   nsresult rv = sHTMLFragmentParser->ParseDocument(
5164       aSourceBuffer, aTargetDocument, aScriptingEnabledForNoscriptParsing);
5165   return rv;
5166 }
5167 
5168 /* static */
5169 nsresult nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
5170                                           Document* aDocument,
5171                                           nsTArray<nsString>& aTagStack,
5172                                           bool aPreventScriptExecution,
5173                                           int32_t aFlags,
5174                                           DocumentFragment** aReturn) {
5175   AutoTimelineMarker m(aDocument->GetDocShell(), "Parse XML");
5176 
5177   if (nsContentUtils::sFragmentParsingActive) {
5178     MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5179     return NS_ERROR_DOM_INVALID_STATE_ERR;
5180   }
5181   mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5182   nsContentUtils::sFragmentParsingActive = true;
5183   if (!sXMLFragmentParser) {
5184     nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID);
5185     parser.forget(&sXMLFragmentParser);
5186     // sXMLFragmentParser now owns the parser
5187   }
5188   if (!sXMLFragmentSink) {
5189     NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
5190     // sXMLFragmentSink now owns the sink
5191   }
5192   nsCOMPtr<nsIContentSink> contentsink = do_QueryInterface(sXMLFragmentSink);
5193   MOZ_ASSERT(contentsink, "Sink doesn't QI to nsIContentSink!");
5194   sXMLFragmentParser->SetContentSink(contentsink);
5195 
5196   RefPtr<Document> doc;
5197   nsCOMPtr<nsIPrincipal> nodePrincipal = aDocument->NodePrincipal();
5198 
5199 #ifdef DEBUG
5200   // aFlags should always be -1 unless the caller of ParseFragmentXML
5201   // is ParserUtils::ParseFragment() which is the only caller that intends
5202   // sanitization. For all other callers we need to ensure to call
5203   // AuditParsingOfHTMLXMLFragments.
5204   if (aFlags < 0) {
5205     DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5206                                                        aSourceBuffer);
5207   }
5208 #endif
5209 
5210   // We sanitize if the fragment occurs in a system privileged
5211   // context, an about: page, or if there are explicit sanitization flags.
5212   // Please note that about:blank and about:srcdoc inherit the security
5213   // context from the embedding context and hence are not loaded using
5214   // an about: scheme principal.
5215   bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5216                         nodePrincipal->SchemeIs("about") || aFlags >= 0;
5217   if (shouldSanitize && !aDocument->IsLoadedAsData()) {
5218     doc = nsContentUtils::CreateInertXMLDocument(aDocument);
5219   } else {
5220     doc = aDocument;
5221   }
5222 
5223   sXMLFragmentSink->SetTargetDocument(doc);
5224   sXMLFragmentSink->SetPreventScriptExecution(aPreventScriptExecution);
5225 
5226   nsresult rv = sXMLFragmentParser->ParseFragment(aSourceBuffer, aTagStack);
5227   if (NS_FAILED(rv)) {
5228     // Drop the fragment parser and sink that might be in an inconsistent state
5229     NS_IF_RELEASE(sXMLFragmentParser);
5230     NS_IF_RELEASE(sXMLFragmentSink);
5231     return rv;
5232   }
5233 
5234   rv = sXMLFragmentSink->FinishFragmentParsing(aReturn);
5235 
5236   sXMLFragmentParser->Reset();
5237   NS_ENSURE_SUCCESS(rv, rv);
5238 
5239   if (shouldSanitize) {
5240     uint32_t sanitizationFlags =
5241         computeSanitizationFlags(nodePrincipal, aFlags);
5242     // Don't fire mutation events for nodes removed by the sanitizer.
5243     nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5244     nsTreeSanitizer sanitizer(sanitizationFlags);
5245     sanitizer.Sanitize(*aReturn);
5246   }
5247 
5248   return rv;
5249 }
5250 
5251 /* static */
5252 nsresult nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
5253                                             nsAString& aResultBuffer,
5254                                             uint32_t aFlags,
5255                                             uint32_t aWrapCol) {
5256   RefPtr<Document> document = nsContentUtils::CreateInertHTMLDocument(nullptr);
5257   if (!document) {
5258     return NS_ERROR_FAILURE;
5259   }
5260 
5261   nsresult rv = nsContentUtils::ParseDocumentHTML(
5262       aSourceBuffer, document,
5263       !(aFlags & nsIDocumentEncoder::OutputNoScriptContent));
5264   NS_ENSURE_SUCCESS(rv, rv);
5265 
5266   nsCOMPtr<nsIDocumentEncoder> encoder = do_createDocumentEncoder("text/plain");
5267 
5268   rv = encoder->Init(document, u"text/plain"_ns, aFlags);
5269   NS_ENSURE_SUCCESS(rv, rv);
5270 
5271   encoder->SetWrapColumn(aWrapCol);
5272 
5273   return encoder->EncodeToString(aResultBuffer);
5274 }
5275 
5276 static already_AddRefed<Document> CreateInertDocument(const Document* aTemplate,
5277                                                       DocumentFlavor aFlavor) {
5278   if (aTemplate) {
5279     bool hasHad = true;
5280     nsIScriptGlobalObject* sgo = aTemplate->GetScriptHandlingObject(hasHad);
5281     NS_ENSURE_TRUE(sgo || !hasHad, nullptr);
5282 
5283     nsCOMPtr<Document> doc;
5284     nsresult rv = NS_NewDOMDocument(
5285         getter_AddRefs(doc), u""_ns, u""_ns, nullptr,
5286         aTemplate->GetDocumentURI(), aTemplate->GetDocBaseURI(),
5287         aTemplate->NodePrincipal(), true, sgo, aFlavor);
5288     if (NS_FAILED(rv)) {
5289       return nullptr;
5290     }
5291     return doc.forget();
5292   }
5293   nsCOMPtr<nsIURI> uri;
5294   NS_NewURI(getter_AddRefs(uri), "about:blank"_ns);
5295   if (!uri) {
5296     return nullptr;
5297   }
5298 
5299   RefPtr<NullPrincipal> nullPrincipal =
5300       NullPrincipal::CreateWithoutOriginAttributes();
5301   if (!nullPrincipal) {
5302     return nullptr;
5303   }
5304 
5305   nsCOMPtr<Document> doc;
5306   nsresult rv =
5307       NS_NewDOMDocument(getter_AddRefs(doc), u""_ns, u""_ns, nullptr, uri, uri,
5308                         nullPrincipal, true, nullptr, aFlavor);
5309   if (NS_FAILED(rv)) {
5310     return nullptr;
5311   }
5312   return doc.forget();
5313 }
5314 
5315 /* static */
5316 already_AddRefed<Document> nsContentUtils::CreateInertXMLDocument(
5317     const Document* aTemplate) {
5318   return CreateInertDocument(aTemplate, DocumentFlavorXML);
5319 }
5320 
5321 /* static */
5322 already_AddRefed<Document> nsContentUtils::CreateInertHTMLDocument(
5323     const Document* aTemplate) {
5324   return CreateInertDocument(aTemplate, DocumentFlavorHTML);
5325 }
5326 
5327 /* static */
5328 nsresult nsContentUtils::SetNodeTextContent(nsIContent* aContent,
5329                                             const nsAString& aValue,
5330                                             bool aTryReuse) {
5331   // Fire DOMNodeRemoved mutation events before we do anything else.
5332   nsCOMPtr<nsIContent> owningContent;
5333 
5334   // Batch possible DOMSubtreeModified events.
5335   mozAutoSubtreeModified subtree(nullptr, nullptr);
5336 
5337   // Scope firing mutation events so that we don't carry any state that
5338   // might be stale
5339   {
5340     // We're relying on mozAutoSubtreeModified to keep a strong reference if
5341     // needed.
5342     Document* doc = aContent->OwnerDoc();
5343 
5344     // Optimize the common case of there being no observers
5345     if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
5346       subtree.UpdateTarget(doc, nullptr);
5347       owningContent = aContent;
5348       nsCOMPtr<nsINode> child;
5349       bool skipFirst = aTryReuse;
5350       for (child = aContent->GetFirstChild();
5351            child && child->GetParentNode() == aContent;
5352            child = child->GetNextSibling()) {
5353         if (skipFirst && child->IsText()) {
5354           skipFirst = false;
5355           continue;
5356         }
5357         nsContentUtils::MaybeFireNodeRemoved(child, aContent);
5358       }
5359     }
5360   }
5361 
5362   // Might as well stick a batch around this since we're performing several
5363   // mutations.
5364   mozAutoDocUpdate updateBatch(aContent->GetComposedDoc(), true);
5365   nsAutoMutationBatch mb;
5366 
5367   if (aTryReuse && !aValue.IsEmpty()) {
5368     // Let's remove nodes until we find a eTEXT.
5369     while (aContent->HasChildren()) {
5370       nsIContent* child = aContent->GetFirstChild();
5371       if (child->IsText()) {
5372         break;
5373       }
5374       aContent->RemoveChildNode(child, true);
5375     }
5376 
5377     // If we have a node, it must be a eTEXT and we reuse it.
5378     if (aContent->HasChildren()) {
5379       nsIContent* child = aContent->GetFirstChild();
5380       nsresult rv = child->AsText()->SetText(aValue, true);
5381       NS_ENSURE_SUCCESS(rv, rv);
5382 
5383       // All the following nodes, if they exist, must be deleted.
5384       while (nsIContent* nextChild = child->GetNextSibling()) {
5385         aContent->RemoveChildNode(nextChild, true);
5386       }
5387     }
5388 
5389     if (aContent->HasChildren()) {
5390       return NS_OK;
5391     }
5392   } else {
5393     mb.Init(aContent, true, false);
5394     while (aContent->HasChildren()) {
5395       aContent->RemoveChildNode(aContent->GetFirstChild(), true);
5396     }
5397   }
5398   mb.RemovalDone();
5399 
5400   if (aValue.IsEmpty()) {
5401     return NS_OK;
5402   }
5403 
5404   RefPtr<nsTextNode> textContent = new (aContent->NodeInfo()->NodeInfoManager())
5405       nsTextNode(aContent->NodeInfo()->NodeInfoManager());
5406 
5407   textContent->SetText(aValue, true);
5408 
5409   ErrorResult rv;
5410   aContent->AppendChildTo(textContent, true, rv);
5411   mb.NodesAdded();
5412   return rv.StealNSResult();
5413 }
5414 
5415 static bool AppendNodeTextContentsRecurse(nsINode* aNode, nsAString& aResult,
5416                                           const fallible_t& aFallible) {
5417   for (nsIContent* child = aNode->GetFirstChild(); child;
5418        child = child->GetNextSibling()) {
5419     if (child->IsElement()) {
5420       bool ok = AppendNodeTextContentsRecurse(child, aResult, aFallible);
5421       if (!ok) {
5422         return false;
5423       }
5424     } else if (Text* text = child->GetAsText()) {
5425       bool ok = text->AppendTextTo(aResult, aFallible);
5426       if (!ok) {
5427         return false;
5428       }
5429     }
5430   }
5431 
5432   return true;
5433 }
5434 
5435 /* static */
5436 bool nsContentUtils::AppendNodeTextContent(nsINode* aNode, bool aDeep,
5437                                            nsAString& aResult,
5438                                            const fallible_t& aFallible) {
5439   if (Text* text = aNode->GetAsText()) {
5440     return text->AppendTextTo(aResult, aFallible);
5441   }
5442   if (aDeep) {
5443     return AppendNodeTextContentsRecurse(aNode, aResult, aFallible);
5444   }
5445 
5446   for (nsIContent* child = aNode->GetFirstChild(); child;
5447        child = child->GetNextSibling()) {
5448     if (Text* text = child->GetAsText()) {
5449       bool ok = text->AppendTextTo(aResult, fallible);
5450       if (!ok) {
5451         return false;
5452       }
5453     }
5454   }
5455   return true;
5456 }
5457 
5458 bool nsContentUtils::HasNonEmptyTextContent(
5459     nsINode* aNode, TextContentDiscoverMode aDiscoverMode) {
5460   for (nsIContent* child = aNode->GetFirstChild(); child;
5461        child = child->GetNextSibling()) {
5462     if (child->IsText() && child->TextLength() > 0) {
5463       return true;
5464     }
5465 
5466     if (aDiscoverMode == eRecurseIntoChildren &&
5467         HasNonEmptyTextContent(child, aDiscoverMode)) {
5468       return true;
5469     }
5470   }
5471 
5472   return false;
5473 }
5474 
5475 /* static */
5476 bool nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
5477                                            const nsIContent* aContent) {
5478   MOZ_ASSERT(aNode, "Must have a node to work with");
5479   MOZ_ASSERT(aContent, "Must have a content to work with");
5480 
5481   if (aNode->IsInNativeAnonymousSubtree() !=
5482       aContent->IsInNativeAnonymousSubtree()) {
5483     return false;
5484   }
5485 
5486   if (aNode->IsInNativeAnonymousSubtree()) {
5487     return aContent->GetClosestNativeAnonymousSubtreeRoot() ==
5488            aNode->GetClosestNativeAnonymousSubtreeRoot();
5489   }
5490 
5491   // FIXME: This doesn't deal with disconnected nodes whatsoever, but it didn't
5492   // use to either. Maybe that's fine.
5493   return aNode->GetContainingShadow() == aContent->GetContainingShadow();
5494 }
5495 
5496 /* static */
5497 bool nsContentUtils::IsInInteractiveHTMLContent(const Element* aElement,
5498                                                 const Element* aStop) {
5499   const Element* element = aElement;
5500   while (element && element != aStop) {
5501     if (element->IsInteractiveHTMLContent()) {
5502       return true;
5503     }
5504     element = element->GetFlattenedTreeParentElement();
5505   }
5506   return false;
5507 }
5508 
5509 /* static */
5510 void nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling) {
5511   IMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
5512 }
5513 
5514 /* static */
5515 bool nsContentUtils::SchemeIs(nsIURI* aURI, const char* aScheme) {
5516   nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
5517   NS_ENSURE_TRUE(baseURI, false);
5518   return baseURI->SchemeIs(aScheme);
5519 }
5520 
5521 bool nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal) {
5522   nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
5523   return !!ep;
5524 }
5525 
5526 bool nsContentUtils::IsSystemOrExpandedPrincipal(nsIPrincipal* aPrincipal) {
5527   return (aPrincipal && aPrincipal->IsSystemPrincipal()) ||
5528          IsExpandedPrincipal(aPrincipal);
5529 }
5530 
5531 nsIPrincipal* nsContentUtils::GetSystemPrincipal() {
5532   MOZ_ASSERT(IsInitialized());
5533   return sSystemPrincipal;
5534 }
5535 
5536 bool nsContentUtils::CombineResourcePrincipals(
5537     nsCOMPtr<nsIPrincipal>* aResourcePrincipal, nsIPrincipal* aExtraPrincipal) {
5538   if (!aExtraPrincipal) {
5539     return false;
5540   }
5541   if (!*aResourcePrincipal) {
5542     *aResourcePrincipal = aExtraPrincipal;
5543     return true;
5544   }
5545   if (*aResourcePrincipal == aExtraPrincipal) {
5546     return false;
5547   }
5548   bool subsumes;
5549   if (NS_SUCCEEDED(
5550           (*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
5551       subsumes) {
5552     return false;
5553   }
5554   *aResourcePrincipal = sSystemPrincipal;
5555   return true;
5556 }
5557 
5558 /* static */
5559 void nsContentUtils::TriggerLink(nsIContent* aContent, nsIURI* aLinkURI,
5560                                  const nsString& aTargetSpec, bool aClick,
5561                                  bool aIsTrusted) {
5562   MOZ_ASSERT(aLinkURI, "No link URI");
5563 
5564   if (aContent->IsEditable() || !aContent->OwnerDoc()->LinkHandlingEnabled()) {
5565     return;
5566   }
5567 
5568   nsCOMPtr<nsIDocShell> docShell = aContent->OwnerDoc()->GetDocShell();
5569   if (!docShell) {
5570     return;
5571   }
5572 
5573   if (!aClick) {
5574     nsDocShell::Cast(docShell)->OnOverLink(aContent, aLinkURI, aTargetSpec);
5575     return;
5576   }
5577 
5578   // Check that this page is allowed to load this URI.
5579   nsresult proceed = NS_OK;
5580 
5581   if (sSecurityManager) {
5582     uint32_t flag = static_cast<uint32_t>(nsIScriptSecurityManager::STANDARD);
5583     proceed = sSecurityManager->CheckLoadURIWithPrincipal(
5584         aContent->NodePrincipal(), aLinkURI, flag,
5585         aContent->OwnerDoc()->InnerWindowID());
5586   }
5587 
5588   // Only pass off the click event if the script security manager says it's ok.
5589   // We need to rest aTargetSpec for forced downloads.
5590   if (NS_SUCCEEDED(proceed)) {
5591     // A link/area element with a download attribute is allowed to set
5592     // a pseudo Content-Disposition header.
5593     // For security reasons we only allow websites to declare same-origin
5594     // resources as downloadable. If this check fails we will just do the normal
5595     // thing (i.e. navigate to the resource).
5596     nsAutoString fileName;
5597     if ((!aContent->IsHTMLElement(nsGkAtoms::a) &&
5598          !aContent->IsHTMLElement(nsGkAtoms::area) &&
5599          !aContent->IsSVGElement(nsGkAtoms::a)) ||
5600         !aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::download,
5601                                         fileName) ||
5602         NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, true))) {
5603       fileName.SetIsVoid(true);  // No actionable download attribute was found.
5604     }
5605 
5606     nsCOMPtr<nsIPrincipal> triggeringPrincipal = aContent->NodePrincipal();
5607     nsCOMPtr<nsIContentSecurityPolicy> csp = aContent->GetCsp();
5608 
5609     // Sanitize fileNames containing null characters by replacing them with
5610     // underscores.
5611     if (!fileName.IsVoid()) {
5612       fileName.ReplaceChar(char16_t(0), '_');
5613     }
5614     nsDocShell::Cast(docShell)->OnLinkClick(
5615         aContent, aLinkURI, fileName.IsVoid() ? aTargetSpec : u""_ns, fileName,
5616         nullptr, nullptr, UserActivation::IsHandlingUserInput(), aIsTrusted,
5617         triggeringPrincipal, csp);
5618   }
5619 }
5620 
5621 /* static */
5622 void nsContentUtils::GetLinkLocation(Element* aElement,
5623                                      nsString& aLocationString) {
5624   nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
5625   if (hrefURI) {
5626     nsAutoCString specUTF8;
5627     nsresult rv = hrefURI->GetSpec(specUTF8);
5628     if (NS_SUCCEEDED(rv)) CopyUTF8toUTF16(specUTF8, aLocationString);
5629   }
5630 }
5631 
5632 /* static */
5633 nsIWidget* nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget) {
5634   if (!aWidget) return nullptr;
5635 
5636   return aWidget->GetTopLevelWidget();
5637 }
5638 
5639 /* static */
5640 const nsDependentString nsContentUtils::GetLocalizedEllipsis() {
5641   static char16_t sBuf[4] = {0, 0, 0, 0};
5642   if (!sBuf[0]) {
5643     if (!SpoofLocaleEnglish()) {
5644       nsAutoString tmp;
5645       Preferences::GetLocalizedString("intl.ellipsis", tmp);
5646       uint32_t len =
5647           std::min(uint32_t(tmp.Length()), uint32_t(ArrayLength(sBuf) - 1));
5648       CopyUnicodeTo(tmp, 0, sBuf, len);
5649     }
5650     if (!sBuf[0]) sBuf[0] = char16_t(0x2026);
5651   }
5652   return nsDependentString(sBuf);
5653 }
5654 
5655 /* static */
5656 void nsContentUtils::AddScriptBlocker() {
5657   MOZ_ASSERT(NS_IsMainThread());
5658   if (!sScriptBlockerCount) {
5659     MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
5660                "Should not already have a count");
5661     sRunnersCountAtFirstBlocker =
5662         sBlockedScriptRunners ? sBlockedScriptRunners->Length() : 0;
5663   }
5664   ++sScriptBlockerCount;
5665 }
5666 
5667 #ifdef DEBUG
5668 static bool sRemovingScriptBlockers = false;
5669 #endif
5670 
5671 /* static */
5672 void nsContentUtils::RemoveScriptBlocker() {
5673   MOZ_ASSERT(NS_IsMainThread());
5674   MOZ_ASSERT(!sRemovingScriptBlockers);
5675   NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
5676   --sScriptBlockerCount;
5677   if (sScriptBlockerCount) {
5678     return;
5679   }
5680 
5681   if (!sBlockedScriptRunners) {
5682     return;
5683   }
5684 
5685   uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
5686   uint32_t lastBlocker = sBlockedScriptRunners->Length();
5687   uint32_t originalFirstBlocker = firstBlocker;
5688   uint32_t blockersCount = lastBlocker - firstBlocker;
5689   sRunnersCountAtFirstBlocker = 0;
5690   NS_ASSERTION(firstBlocker <= lastBlocker, "bad sRunnersCountAtFirstBlocker");
5691 
5692   while (firstBlocker < lastBlocker) {
5693     nsCOMPtr<nsIRunnable> runnable;
5694     runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
5695     ++firstBlocker;
5696 
5697     // Calling the runnable can reenter us
5698     {
5699       AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
5700       runnable->Run();
5701     }
5702     // So can dropping the reference to the runnable
5703     runnable = nullptr;
5704 
5705     NS_ASSERTION(sRunnersCountAtFirstBlocker == 0, "Bad count");
5706     NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
5707   }
5708 #ifdef DEBUG
5709   AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
5710   sRemovingScriptBlockers = true;
5711 #endif
5712   sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
5713 }
5714 
5715 /* static */
5716 already_AddRefed<nsPIDOMWindowOuter>
5717 nsContentUtils::GetMostRecentNonPBWindow() {
5718   nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
5719 
5720   nsCOMPtr<mozIDOMWindowProxy> window;
5721   wm->GetMostRecentNonPBWindow(u"navigator:browser", getter_AddRefs(window));
5722   nsCOMPtr<nsPIDOMWindowOuter> pwindow;
5723   pwindow = do_QueryInterface(window);
5724 
5725   return pwindow.forget();
5726 }
5727 
5728 /* static */
5729 void nsContentUtils::WarnScriptWasIgnored(Document* aDocument) {
5730   nsAutoString msg;
5731   bool privateBrowsing = false;
5732   bool chromeContext = false;
5733 
5734   if (aDocument) {
5735     nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
5736     if (uri) {
5737       msg.Append(NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()));
5738       msg.AppendLiteral(" : ");
5739     }
5740     privateBrowsing =
5741         !!aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
5742     chromeContext = aDocument->NodePrincipal()->IsSystemPrincipal();
5743   }
5744 
5745   msg.AppendLiteral(
5746       "Unable to run script because scripts are blocked internally.");
5747   LogSimpleConsoleError(msg, "DOM", privateBrowsing, chromeContext);
5748 }
5749 
5750 /* static */
5751 void nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable) {
5752   nsCOMPtr<nsIRunnable> runnable = aRunnable;
5753   if (!runnable) {
5754     return;
5755   }
5756 
5757   if (sScriptBlockerCount) {
5758     sBlockedScriptRunners->AppendElement(runnable.forget());
5759     return;
5760   }
5761 
5762   AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
5763   runnable->Run();
5764 }
5765 
5766 /* static */
5767 void nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) {
5768   nsCOMPtr<nsIRunnable> runnable = aRunnable;
5769   AddScriptRunner(runnable.forget());
5770 }
5771 
5772 /* static */ bool nsContentUtils::IsSafeToRunScript() {
5773   MOZ_ASSERT(NS_IsMainThread(),
5774              "This static variable only makes sense on the main thread!");
5775   return sScriptBlockerCount == 0;
5776 }
5777 
5778 /* static */
5779 void nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable) {
5780   MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5781   CycleCollectedJSContext::Get()->RunInStableState(std::move(aRunnable));
5782 }
5783 
5784 /* static */
5785 void nsContentUtils::AddPendingIDBTransaction(
5786     already_AddRefed<nsIRunnable> aTransaction) {
5787   MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5788   CycleCollectedJSContext::Get()->AddPendingIDBTransaction(
5789       std::move(aTransaction));
5790 }
5791 
5792 /* static */
5793 bool nsContentUtils::IsInStableOrMetaStableState() {
5794   MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5795   return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
5796 }
5797 
5798 /* static */
5799 void nsContentUtils::HidePopupsInDocument(Document* aDocument) {
5800 #ifdef MOZ_XUL
5801   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
5802   if (pm && aDocument) {
5803     nsCOMPtr<nsIDocShellTreeItem> docShellToHide = aDocument->GetDocShell();
5804     if (docShellToHide) pm->HidePopupsInDocShell(docShellToHide);
5805   }
5806 #endif
5807 }
5808 
5809 /* static */
5810 already_AddRefed<nsIDragSession> nsContentUtils::GetDragSession() {
5811   nsCOMPtr<nsIDragSession> dragSession;
5812   nsCOMPtr<nsIDragService> dragService =
5813       do_GetService("@mozilla.org/widget/dragservice;1");
5814   if (dragService) dragService->GetCurrentSession(getter_AddRefs(dragSession));
5815   return dragSession.forget();
5816 }
5817 
5818 /* static */
5819 nsresult nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) {
5820   if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) {
5821     return NS_OK;
5822   }
5823 
5824   // For dragstart events, the data transfer object is
5825   // created before the event fires, so it should already be set. For other
5826   // drag events, get the object from the drag session.
5827   NS_ASSERTION(aDragEvent->mMessage != eDragStart,
5828                "draggesture event created without a dataTransfer");
5829 
5830   nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
5831   NS_ENSURE_TRUE(dragSession, NS_OK);  // no drag in progress
5832 
5833   RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
5834   if (!initialDataTransfer) {
5835     // A dataTransfer won't exist when a drag was started by some other
5836     // means, for instance calling the drag service directly, or a drag
5837     // from another application. In either case, a new dataTransfer should
5838     // be created that reflects the data.
5839     initialDataTransfer =
5840         new DataTransfer(aDragEvent->mTarget, aDragEvent->mMessage, true, -1);
5841 
5842     // now set it in the drag session so we don't need to create it again
5843     dragSession->SetDataTransfer(initialDataTransfer);
5844   }
5845 
5846   bool isCrossDomainSubFrameDrop = false;
5847   if (aDragEvent->mMessage == eDrop) {
5848     isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
5849   }
5850 
5851   // each event should use a clone of the original dataTransfer.
5852   initialDataTransfer->Clone(
5853       aDragEvent->mTarget, aDragEvent->mMessage, aDragEvent->mUserCancelled,
5854       isCrossDomainSubFrameDrop, getter_AddRefs(aDragEvent->mDataTransfer));
5855   if (NS_WARN_IF(!aDragEvent->mDataTransfer)) {
5856     return NS_ERROR_OUT_OF_MEMORY;
5857   }
5858 
5859   // for the dragenter and dragover events, initialize the drop effect
5860   // from the drop action, which platform specific widget code sets before
5861   // the event is fired based on the keyboard state.
5862   if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
5863     uint32_t action;
5864     dragSession->GetDragAction(&action);
5865     uint32_t effectAllowed = aDragEvent->mDataTransfer->EffectAllowedInt();
5866     aDragEvent->mDataTransfer->SetDropEffectInt(
5867         FilterDropEffect(action, effectAllowed));
5868   } else if (aDragEvent->mMessage == eDrop ||
5869              aDragEvent->mMessage == eDragEnd) {
5870     // For the drop and dragend events, set the drop effect based on the
5871     // last value that the dropEffect had. This will have been set in
5872     // EventStateManager::PostHandleEvent for the last dragenter or
5873     // dragover event.
5874     aDragEvent->mDataTransfer->SetDropEffectInt(
5875         initialDataTransfer->DropEffectInt());
5876   }
5877 
5878   return NS_OK;
5879 }
5880 
5881 /* static */
5882 uint32_t nsContentUtils::FilterDropEffect(uint32_t aAction,
5883                                           uint32_t aEffectAllowed) {
5884   // It is possible for the drag action to include more than one action, but
5885   // the widget code which sets the action from the keyboard state should only
5886   // be including one. If multiple actions were set, we just consider them in
5887   //  the following order:
5888   //   copy, link, move
5889   if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
5890     aAction = nsIDragService::DRAGDROP_ACTION_COPY;
5891   else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
5892     aAction = nsIDragService::DRAGDROP_ACTION_LINK;
5893   else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
5894     aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
5895 
5896   // Filter the action based on the effectAllowed. If the effectAllowed
5897   // doesn't include the action, then that action cannot be done, so adjust
5898   // the action to something that is allowed. For a copy, adjust to move or
5899   // link. For a move, adjust to copy or link. For a link, adjust to move or
5900   // link. Otherwise, use none.
5901   if (aAction & aEffectAllowed ||
5902       aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
5903     return aAction;
5904   if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
5905     return nsIDragService::DRAGDROP_ACTION_MOVE;
5906   if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
5907     return nsIDragService::DRAGDROP_ACTION_COPY;
5908   if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
5909     return nsIDragService::DRAGDROP_ACTION_LINK;
5910   return nsIDragService::DRAGDROP_ACTION_NONE;
5911 }
5912 
5913 /* static */
5914 bool nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession,
5915                                           WidgetDragEvent* aDropEvent) {
5916   nsCOMPtr<nsIContent> target = do_QueryInterface(aDropEvent->mOriginalTarget);
5917   if (!target) {
5918     return true;
5919   }
5920 
5921   // Always allow dropping onto chrome shells.
5922   BrowsingContext* targetBC = target->OwnerDoc()->GetBrowsingContext();
5923   if (targetBC->IsChrome()) {
5924     return false;
5925   }
5926 
5927   // If there is no source node, then this is a drag from another
5928   // application, which should be allowed.
5929   RefPtr<Document> doc(aDragSession->GetSourceDocument());
5930   if (doc && doc->GetBrowsingContext()) {
5931     // Get each successive parent of the source document and compare it to
5932     // the drop document. If they match, then this is a drag from a child frame.
5933     for (BrowsingContext* bc = doc->GetBrowsingContext()->GetParent(); bc;
5934          bc = bc->GetParent()) {
5935       if (bc == targetBC) {
5936         // The drag is from a descendant frame.
5937         return true;
5938       }
5939     }
5940   }
5941 
5942   return false;
5943 }
5944 
5945 /* static */
5946 bool nsContentUtils::URIIsLocalFile(nsIURI* aURI) {
5947   bool isFile;
5948   nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
5949 
5950   // Important: we do NOT test the entire URI chain here!
5951   return util &&
5952          NS_SUCCEEDED(util->ProtocolHasFlags(
5953              aURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, &isFile)) &&
5954          isFile;
5955 }
5956 
5957 /* static */
5958 JSContext* nsContentUtils::GetCurrentJSContext() {
5959   MOZ_ASSERT(IsInitialized());
5960   if (!IsJSAPIActive()) {
5961     return nullptr;
5962   }
5963   return danger::GetJSContext();
5964 }
5965 
5966 template <typename StringType, typename CharType>
5967 void _ASCIIToLowerInSitu(StringType& aStr) {
5968   CharType* iter = aStr.BeginWriting();
5969   CharType* end = aStr.EndWriting();
5970   MOZ_ASSERT(iter && end);
5971 
5972   while (iter != end) {
5973     CharType c = *iter;
5974     if (c >= 'A' && c <= 'Z') {
5975       *iter = c + ('a' - 'A');
5976     }
5977     ++iter;
5978   }
5979 }
5980 
5981 /* static */
5982 void nsContentUtils::ASCIIToLower(nsAString& aStr) {
5983   return _ASCIIToLowerInSitu<nsAString, char16_t>(aStr);
5984 }
5985 
5986 /* static */
5987 void nsContentUtils::ASCIIToLower(nsACString& aStr) {
5988   return _ASCIIToLowerInSitu<nsACString, char>(aStr);
5989 }
5990 
5991 template <typename StringType, typename CharType>
5992 void _ASCIIToLowerCopy(const StringType& aSource, StringType& aDest) {
5993   uint32_t len = aSource.Length();
5994   aDest.SetLength(len);
5995   MOZ_ASSERT(aDest.Length() == len);
5996 
5997   CharType* dest = aDest.BeginWriting();
5998   MOZ_ASSERT(dest);
5999 
6000   const CharType* iter = aSource.BeginReading();
6001   const CharType* end = aSource.EndReading();
6002   while (iter != end) {
6003     CharType c = *iter;
6004     *dest = (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
6005     ++iter;
6006     ++dest;
6007   }
6008 }
6009 
6010 /* static */
6011 void nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest) {
6012   return _ASCIIToLowerCopy<nsAString, char16_t>(aSource, aDest);
6013 }
6014 
6015 /* static */
6016 void nsContentUtils::ASCIIToLower(const nsACString& aSource,
6017                                   nsACString& aDest) {
6018   return _ASCIIToLowerCopy<nsACString, char>(aSource, aDest);
6019 }
6020 
6021 template <typename StringType, typename CharType>
6022 void _ASCIIToUpperInSitu(StringType& aStr) {
6023   CharType* iter = aStr.BeginWriting();
6024   CharType* end = aStr.EndWriting();
6025   MOZ_ASSERT(iter && end);
6026 
6027   while (iter != end) {
6028     CharType c = *iter;
6029     if (c >= 'a' && c <= 'z') {
6030       *iter = c + ('A' - 'a');
6031     }
6032     ++iter;
6033   }
6034 }
6035 
6036 /* static */
6037 void nsContentUtils::ASCIIToUpper(nsAString& aStr) {
6038   return _ASCIIToUpperInSitu<nsAString, char16_t>(aStr);
6039 }
6040 
6041 /* static */
6042 void nsContentUtils::ASCIIToUpper(nsACString& aStr) {
6043   return _ASCIIToUpperInSitu<nsACString, char>(aStr);
6044 }
6045 
6046 template <typename StringType, typename CharType>
6047 void _ASCIIToUpperCopy(const StringType& aSource, StringType& aDest) {
6048   uint32_t len = aSource.Length();
6049   aDest.SetLength(len);
6050   MOZ_ASSERT(aDest.Length() == len);
6051 
6052   CharType* dest = aDest.BeginWriting();
6053   MOZ_ASSERT(dest);
6054 
6055   const CharType* iter = aSource.BeginReading();
6056   const CharType* end = aSource.EndReading();
6057   while (iter != end) {
6058     CharType c = *iter;
6059     *dest = (c >= 'a' && c <= 'z') ? c + ('A' - 'a') : c;
6060     ++iter;
6061     ++dest;
6062   }
6063 }
6064 
6065 /* static */
6066 void nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest) {
6067   return _ASCIIToUpperCopy<nsAString, char16_t>(aSource, aDest);
6068 }
6069 
6070 /* static */
6071 void nsContentUtils::ASCIIToUpper(const nsACString& aSource,
6072                                   nsACString& aDest) {
6073   return _ASCIIToUpperCopy<nsACString, char>(aSource, aDest);
6074 }
6075 
6076 /* static */
6077 bool nsContentUtils::EqualsIgnoreASCIICase(nsAtom* aAtom1, nsAtom* aAtom2) {
6078   if (aAtom1 == aAtom2) {
6079     return true;
6080   }
6081 
6082   // If both are ascii lowercase already, we know that the slow comparison
6083   // below is going to return false.
6084   if (aAtom1->IsAsciiLowercase() && aAtom2->IsAsciiLowercase()) {
6085     return false;
6086   }
6087 
6088   return EqualsIgnoreASCIICase(nsDependentAtomString(aAtom1),
6089                                nsDependentAtomString(aAtom2));
6090 }
6091 
6092 /* static */
6093 bool nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
6094                                            const nsAString& aStr2) {
6095   uint32_t len = aStr1.Length();
6096   if (len != aStr2.Length()) {
6097     return false;
6098   }
6099 
6100   const char16_t* str1 = aStr1.BeginReading();
6101   const char16_t* str2 = aStr2.BeginReading();
6102   const char16_t* end = str1 + len;
6103 
6104   while (str1 < end) {
6105     char16_t c1 = *str1++;
6106     char16_t c2 = *str2++;
6107 
6108     // First check if any bits other than the 0x0020 differs
6109     if ((c1 ^ c2) & 0xffdf) {
6110       return false;
6111     }
6112 
6113     // We know they can only differ in the 0x0020 bit.
6114     // Likely the two chars are the same, so check that first
6115     if (c1 != c2) {
6116       // They do differ, but since it's only in the 0x0020 bit, check if it's
6117       // the same ascii char, but just differing in case
6118       char16_t c1Upper = c1 & 0xffdf;
6119       if (!('A' <= c1Upper && c1Upper <= 'Z')) {
6120         return false;
6121       }
6122     }
6123   }
6124 
6125   return true;
6126 }
6127 
6128 /* static */
6129 bool nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr) {
6130   const char16_t* iter = aStr.BeginReading();
6131   const char16_t* end = aStr.EndReading();
6132   while (iter != end) {
6133     char16_t c = *iter;
6134     if (c >= 'A' && c <= 'Z') {
6135       return true;
6136     }
6137     ++iter;
6138   }
6139 
6140   return false;
6141 }
6142 
6143 /* static */
6144 nsIInterfaceRequestor* nsContentUtils::SameOriginChecker() {
6145   if (!sSameOriginChecker) {
6146     sSameOriginChecker = new SameOriginCheckerImpl();
6147     NS_ADDREF(sSameOriginChecker);
6148   }
6149   return sSameOriginChecker;
6150 }
6151 
6152 /* static */
6153 nsresult nsContentUtils::CheckSameOrigin(nsIChannel* aOldChannel,
6154                                          nsIChannel* aNewChannel) {
6155   if (!nsContentUtils::GetSecurityManager()) return NS_ERROR_NOT_AVAILABLE;
6156 
6157   nsCOMPtr<nsIPrincipal> oldPrincipal;
6158   nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
6159       aOldChannel, getter_AddRefs(oldPrincipal));
6160 
6161   nsCOMPtr<nsIURI> newURI;
6162   aNewChannel->GetURI(getter_AddRefs(newURI));
6163   nsCOMPtr<nsIURI> newOriginalURI;
6164   aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
6165 
6166   NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
6167 
6168   nsresult rv = oldPrincipal->CheckMayLoad(newURI, false);
6169   if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
6170     rv = oldPrincipal->CheckMayLoad(newOriginalURI, false);
6171   }
6172 
6173   return rv;
6174 }
6175 
6176 NS_IMPL_ISUPPORTS(SameOriginCheckerImpl, nsIChannelEventSink,
6177                   nsIInterfaceRequestor)
6178 
6179 NS_IMETHODIMP
6180 SameOriginCheckerImpl::AsyncOnChannelRedirect(
6181     nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
6182     nsIAsyncVerifyRedirectCallback* cb) {
6183   MOZ_ASSERT(aNewChannel, "Redirecting to null channel?");
6184 
6185   nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
6186   if (NS_SUCCEEDED(rv)) {
6187     cb->OnRedirectVerifyCallback(NS_OK);
6188   }
6189 
6190   return rv;
6191 }
6192 
6193 NS_IMETHODIMP
6194 SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult) {
6195   return QueryInterface(aIID, aResult);
6196 }
6197 
6198 /* static */
6199 nsresult nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsACString& aOrigin) {
6200   MOZ_ASSERT(aURI, "missing uri");
6201 
6202   // For Blob URI, the path is the URL of the owning page.
6203   if (aURI->SchemeIs(BLOBURI_SCHEME)) {
6204     nsAutoCString path;
6205     nsresult rv = aURI->GetPathQueryRef(path);
6206     NS_ENSURE_SUCCESS(rv, rv);
6207 
6208     nsCOMPtr<nsIURI> uri;
6209     rv = NS_NewURI(getter_AddRefs(uri), path);
6210     if (NS_FAILED(rv)) {
6211       aOrigin.AssignLiteral("null");
6212       return NS_OK;
6213     }
6214 
6215     return GetASCIIOrigin(uri, aOrigin);
6216   }
6217 
6218   aOrigin.Truncate();
6219 
6220   nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
6221   NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
6222 
6223   nsAutoCString host;
6224   nsresult rv = uri->GetAsciiHost(host);
6225 
6226   if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
6227     nsAutoCString userPass;
6228     uri->GetUserPass(userPass);
6229 
6230     nsAutoCString prePath;
6231     if (!userPass.IsEmpty()) {
6232       rv = NS_MutateURI(uri).SetUserPass(""_ns).Finalize(uri);
6233       NS_ENSURE_SUCCESS(rv, rv);
6234     }
6235 
6236     rv = uri->GetPrePath(prePath);
6237     NS_ENSURE_SUCCESS(rv, rv);
6238 
6239     aOrigin = prePath;
6240   } else {
6241     aOrigin.AssignLiteral("null");
6242   }
6243 
6244   return NS_OK;
6245 }
6246 
6247 /* static */
6248 nsresult nsContentUtils::GetUTFOrigin(nsIPrincipal* aPrincipal,
6249                                       nsAString& aOrigin) {
6250   MOZ_ASSERT(aPrincipal, "missing principal");
6251 
6252   aOrigin.Truncate();
6253   nsAutoCString asciiOrigin;
6254 
6255   nsresult rv = aPrincipal->GetAsciiOrigin(asciiOrigin);
6256   if (NS_FAILED(rv)) {
6257     asciiOrigin.AssignLiteral("null");
6258   }
6259 
6260   CopyUTF8toUTF16(asciiOrigin, aOrigin);
6261   return NS_OK;
6262 }
6263 
6264 /* static */
6265 nsresult nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsAString& aOrigin) {
6266   MOZ_ASSERT(aURI, "missing uri");
6267   nsresult rv;
6268 
6269 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
6270   // Check if either URI has a special origin.
6271   nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin =
6272       do_QueryInterface(aURI);
6273   if (uriWithSpecialOrigin) {
6274     nsCOMPtr<nsIURI> origin;
6275     rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin));
6276     NS_ENSURE_SUCCESS(rv, rv);
6277 
6278     return GetUTFOrigin(origin, aOrigin);
6279   }
6280 #endif
6281 
6282   nsAutoCString asciiOrigin;
6283   rv = GetASCIIOrigin(aURI, asciiOrigin);
6284   NS_ENSURE_SUCCESS(rv, rv);
6285 
6286   CopyUTF8toUTF16(asciiOrigin, aOrigin);
6287   return NS_OK;
6288 }
6289 
6290 /* static */
6291 bool nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal,
6292                                   nsIChannel* aChannel,
6293                                   bool aAllowIfInheritsPrincipal) {
6294   nsCOMPtr<nsIURI> channelURI;
6295   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
6296   NS_ENSURE_SUCCESS(rv, false);
6297 
6298   return NS_SUCCEEDED(
6299       aPrincipal->CheckMayLoad(channelURI, aAllowIfInheritsPrincipal));
6300 }
6301 
6302 /* static */
6303 bool nsContentUtils::CanAccessNativeAnon() {
6304   return LegacyIsCallerChromeOrNativeCode();
6305 }
6306 
6307 /* static */
6308 nsresult nsContentUtils::DispatchXULCommand(nsIContent* aTarget, bool aTrusted,
6309                                             Event* aSourceEvent,
6310                                             PresShell* aPresShell, bool aCtrl,
6311                                             bool aAlt, bool aShift, bool aMeta,
6312                                             uint16_t aInputSource,
6313                                             int16_t aButton) {
6314   NS_ENSURE_STATE(aTarget);
6315   Document* doc = aTarget->OwnerDoc();
6316   nsPresContext* presContext = doc->GetPresContext();
6317 
6318   RefPtr<XULCommandEvent> xulCommand =
6319       new XULCommandEvent(doc, presContext, nullptr);
6320   xulCommand->InitCommandEvent(u"command"_ns, true, true,
6321                                nsGlobalWindowInner::Cast(doc->GetInnerWindow()),
6322                                0, aCtrl, aAlt, aShift, aMeta, aButton,
6323                                aSourceEvent, aInputSource, IgnoreErrors());
6324 
6325   if (aPresShell) {
6326     nsEventStatus status = nsEventStatus_eIgnore;
6327     return aPresShell->HandleDOMEventWithTarget(aTarget, xulCommand, &status);
6328   }
6329 
6330   ErrorResult rv;
6331   aTarget->DispatchEvent(*xulCommand, rv);
6332   return rv.StealNSResult();
6333 }
6334 
6335 // static
6336 nsresult nsContentUtils::WrapNative(JSContext* cx, nsISupports* native,
6337                                     nsWrapperCache* cache, const nsIID* aIID,
6338                                     JS::MutableHandle<JS::Value> vp,
6339                                     bool aAllowWrapping) {
6340   MOZ_ASSERT(cx == GetCurrentJSContext());
6341 
6342   if (!native) {
6343     vp.setNull();
6344 
6345     return NS_OK;
6346   }
6347 
6348   JSObject* wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
6349   if (wrapper) {
6350     return NS_OK;
6351   }
6352 
6353   NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
6354 
6355   if (!NS_IsMainThread()) {
6356     MOZ_CRASH();
6357   }
6358 
6359   JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
6360   nsresult rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
6361                                               aAllowWrapping, vp);
6362   return rv;
6363 }
6364 
6365 nsresult nsContentUtils::CreateArrayBuffer(JSContext* aCx,
6366                                            const nsACString& aData,
6367                                            JSObject** aResult) {
6368   if (!aCx) {
6369     return NS_ERROR_FAILURE;
6370   }
6371 
6372   size_t dataLen = aData.Length();
6373   *aResult = JS::NewArrayBuffer(aCx, dataLen);
6374   if (!*aResult) {
6375     return NS_ERROR_FAILURE;
6376   }
6377 
6378   if (dataLen > 0) {
6379     NS_ASSERTION(JS::IsArrayBufferObject(*aResult), "What happened?");
6380     JS::AutoCheckCannotGC nogc;
6381     bool isShared;
6382     memcpy(JS::GetArrayBufferData(*aResult, &isShared, nogc),
6383            aData.BeginReading(), dataLen);
6384     MOZ_ASSERT(!isShared);
6385   }
6386 
6387   return NS_OK;
6388 }
6389 
6390 void nsContentUtils::StripNullChars(const nsAString& aInStr,
6391                                     nsAString& aOutStr) {
6392   // In common cases where we don't have nulls in the
6393   // string we can simple simply bypass the checking code.
6394   int32_t firstNullPos = aInStr.FindChar('\0');
6395   if (firstNullPos == kNotFound) {
6396     aOutStr.Assign(aInStr);
6397     return;
6398   }
6399 
6400   aOutStr.SetCapacity(aInStr.Length() - 1);
6401   nsAString::const_iterator start, end;
6402   aInStr.BeginReading(start);
6403   aInStr.EndReading(end);
6404   while (start != end) {
6405     if (*start != '\0') aOutStr.Append(*start);
6406     ++start;
6407   }
6408 }
6409 
6410 struct ClassMatchingInfo {
6411   AtomArray mClasses;
6412   nsCaseTreatment mCaseTreatment;
6413 };
6414 
6415 // static
6416 bool nsContentUtils::MatchClassNames(Element* aElement, int32_t aNamespaceID,
6417                                      nsAtom* aAtom, void* aData) {
6418   // We can't match if there are no class names
6419   const nsAttrValue* classAttr = aElement->GetClasses();
6420   if (!classAttr) {
6421     return false;
6422   }
6423 
6424   // need to match *all* of the classes
6425   ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6426   uint32_t length = info->mClasses.Length();
6427   if (!length) {
6428     // If we actually had no classes, don't match.
6429     return false;
6430   }
6431   uint32_t i;
6432   for (i = 0; i < length; ++i) {
6433     if (!classAttr->Contains(info->mClasses[i], info->mCaseTreatment)) {
6434       return false;
6435     }
6436   }
6437 
6438   return true;
6439 }
6440 
6441 // static
6442 void nsContentUtils::DestroyClassNameArray(void* aData) {
6443   ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6444   delete info;
6445 }
6446 
6447 // static
6448 void* nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
6449                                              const nsString* aClasses) {
6450   nsAttrValue attrValue;
6451   attrValue.ParseAtomArray(*aClasses);
6452   // nsAttrValue::Equals is sensitive to order, so we'll send an array
6453   auto* info = new ClassMatchingInfo;
6454   if (attrValue.Type() == nsAttrValue::eAtomArray) {
6455     info->mClasses = std::move(*(attrValue.GetAtomArrayValue()));
6456   } else if (attrValue.Type() == nsAttrValue::eAtom) {
6457     info->mClasses.AppendElement(attrValue.GetAtomValue());
6458   }
6459 
6460   info->mCaseTreatment =
6461       aRootNode->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks
6462           ? eIgnoreCase
6463           : eCaseMatters;
6464   return info;
6465 }
6466 
6467 // static
6468 bool nsContentUtils::IsFocusedContent(const nsIContent* aContent) {
6469   nsFocusManager* fm = nsFocusManager::GetFocusManager();
6470 
6471   return fm && fm->GetFocusedElement() == aContent;
6472 }
6473 
6474 bool nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent) {
6475   Document* doc = aContent->GetComposedDoc();
6476   if (!doc) {
6477     return false;
6478   }
6479 
6480   // If the subdocument lives in another process, the frame is
6481   // tabbable.
6482   if (EventStateManager::IsRemoteTarget(aContent)) {
6483     return true;
6484   }
6485 
6486   // XXXbz should this use OwnerDoc() for GetSubDocumentFor?
6487   // sXBL/XBL2 issue!
6488   Document* subDoc = doc->GetSubDocumentFor(aContent);
6489   if (!subDoc) {
6490     return false;
6491   }
6492 
6493   nsCOMPtr<nsIDocShell> docShell = subDoc->GetDocShell();
6494   if (!docShell) {
6495     return false;
6496   }
6497 
6498   nsCOMPtr<nsIContentViewer> contentViewer;
6499   docShell->GetContentViewer(getter_AddRefs(contentViewer));
6500   if (!contentViewer) {
6501     return false;
6502   }
6503 
6504   // If there are 2 viewers for the current docshell, that
6505   // means the current document may be a zombie document.
6506   // While load and pageshow events are dispatched, zombie viewer is the old,
6507   // to be hidden document.
6508   if (contentViewer->GetPreviousViewer()) {
6509     bool inOnLoad = false;
6510     docShell->GetIsExecutingOnLoadHandler(&inOnLoad);
6511     return inOnLoad;
6512   }
6513 
6514   return true;
6515 }
6516 
6517 bool nsContentUtils::HasScrollgrab(nsIContent* aContent) {
6518   // If we ever standardize this feature we'll want to hook this up properly
6519   // again. For now we're removing all the DOM-side code related to it but
6520   // leaving the layout and APZ handling for it in place.
6521   return false;
6522 }
6523 
6524 void nsContentUtils::FlushLayoutForTree(nsPIDOMWindowOuter* aWindow) {
6525   if (!aWindow) {
6526     return;
6527   }
6528 
6529   // Note that because FlushPendingNotifications flushes parents, this
6530   // is O(N^2) in docshell tree depth.  However, the docshell tree is
6531   // usually pretty shallow.
6532 
6533   if (RefPtr<Document> doc = aWindow->GetDoc()) {
6534     doc->FlushPendingNotifications(FlushType::Layout);
6535   }
6536 
6537   if (nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell()) {
6538     int32_t i = 0, i_end;
6539     docShell->GetInProcessChildCount(&i_end);
6540     for (; i < i_end; ++i) {
6541       nsCOMPtr<nsIDocShellTreeItem> item;
6542       if (docShell->GetInProcessChildAt(i, getter_AddRefs(item)) == NS_OK &&
6543           item) {
6544         if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
6545           FlushLayoutForTree(win);
6546         }
6547       }
6548     }
6549   }
6550 }
6551 
6552 void nsContentUtils::RemoveNewlines(nsString& aString) { aString.StripCRLF(); }
6553 
6554 void nsContentUtils::PlatformToDOMLineBreaks(nsString& aString) {
6555   if (!PlatformToDOMLineBreaks(aString, fallible)) {
6556     aString.AllocFailed(aString.Length());
6557   }
6558 }
6559 
6560 bool nsContentUtils::PlatformToDOMLineBreaks(nsString& aString,
6561                                              const fallible_t& aFallible) {
6562   if (aString.FindChar(char16_t('\r')) != -1) {
6563     // Windows linebreaks: Map CRLF to LF:
6564     if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
6565       return false;
6566     }
6567 
6568     // Mac linebreaks: Map any remaining CR to LF:
6569     if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
6570       return false;
6571     }
6572   }
6573 
6574   return true;
6575 }
6576 
6577 void nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
6578                                                     nsAString& aResultString) {
6579   MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
6580 
6581   uint32_t stringLen = NS_strlen(static_cast<char16_t*>(aBuf->Data()));
6582 
6583   // SANITY CHECK: In case the nsStringBuffer isn't correctly
6584   // null-terminated, let's clamp its length using the allocated size, to be
6585   // sure the resulting string doesn't sample past the end of the the buffer.
6586   // (Note that StorageSize() is in units of bytes, so we have to convert that
6587   // to units of PRUnichars, and subtract 1 for the null-terminator.)
6588   uint32_t allocStringLen = (aBuf->StorageSize() / sizeof(char16_t)) - 1;
6589   MOZ_ASSERT(stringLen <= allocStringLen,
6590              "string buffer lacks null terminator!");
6591   stringLen = std::min(stringLen, allocStringLen);
6592 
6593   aBuf->ToString(stringLen, aResultString);
6594 }
6595 
6596 already_AddRefed<nsContentList> nsContentUtils::GetElementsByClassName(
6597     nsINode* aRootNode, const nsAString& aClasses) {
6598   MOZ_ASSERT(aRootNode, "Must have root node");
6599 
6600   return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(
6601       aRootNode, MatchClassNames, DestroyClassNameArray, AllocClassMatchingInfo,
6602       aClasses);
6603 }
6604 
6605 PresShell* nsContentUtils::FindPresShellForDocument(const Document* aDocument) {
6606   const Document* doc = aDocument;
6607   Document* displayDoc = doc->GetDisplayDocument();
6608   if (displayDoc) {
6609     doc = displayDoc;
6610   }
6611 
6612   PresShell* presShell = doc->GetPresShell();
6613   if (presShell) {
6614     return presShell;
6615   }
6616 
6617   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
6618   while (docShellTreeItem) {
6619     // We may be in a display:none subdocument, or we may not have a presshell
6620     // created yet.
6621     // Walk the docshell tree to find the nearest container that has a
6622     // presshell, and return that.
6623     nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
6624     if (PresShell* presShell = docShell->GetPresShell()) {
6625       return presShell;
6626     }
6627     nsCOMPtr<nsIDocShellTreeItem> parent;
6628     docShellTreeItem->GetInProcessParent(getter_AddRefs(parent));
6629     docShellTreeItem = parent;
6630   }
6631 
6632   return nullptr;
6633 }
6634 
6635 /* static */
6636 nsPresContext* nsContentUtils::FindPresContextForDocument(
6637     const Document* aDocument) {
6638   if (PresShell* presShell = FindPresShellForDocument(aDocument)) {
6639     return presShell->GetPresContext();
6640   }
6641   return nullptr;
6642 }
6643 
6644 nsIWidget* nsContentUtils::WidgetForDocument(const Document* aDocument) {
6645   PresShell* presShell = FindPresShellForDocument(aDocument);
6646   if (!presShell) {
6647     return nullptr;
6648   }
6649   nsViewManager* vm = presShell->GetViewManager();
6650   if (!vm) {
6651     return nullptr;
6652   }
6653   nsView* rootView = vm->GetRootView();
6654   if (!rootView) {
6655     return nullptr;
6656   }
6657   nsView* displayRoot = nsViewManager::GetDisplayRootFor(rootView);
6658   if (!displayRoot) {
6659     return nullptr;
6660   }
6661   return displayRoot->GetNearestWidget(nullptr);
6662 }
6663 
6664 nsIWidget* nsContentUtils::WidgetForContent(const nsIContent* aContent) {
6665   nsIFrame* frame = aContent->GetPrimaryFrame();
6666   if (frame) {
6667     frame = nsLayoutUtils::GetDisplayRootFrame(frame);
6668 
6669     nsView* view = frame->GetView();
6670     if (view) {
6671       return view->GetWidget();
6672     }
6673   }
6674 
6675   return nullptr;
6676 }
6677 
6678 already_AddRefed<LayerManager> nsContentUtils::LayerManagerForContent(
6679     const nsIContent* aContent) {
6680   nsIWidget* widget = nsContentUtils::WidgetForContent(aContent);
6681   if (widget) {
6682     RefPtr<LayerManager> manager = widget->GetLayerManager();
6683     return manager.forget();
6684   }
6685 
6686   return nullptr;
6687 }
6688 
6689 static already_AddRefed<LayerManager> LayerManagerForDocumentInternal(
6690     const Document* aDoc, bool aRequirePersistent) {
6691   nsIWidget* widget = nsContentUtils::WidgetForDocument(aDoc);
6692   if (widget) {
6693     RefPtr<LayerManager> manager = widget->GetLayerManager(
6694         aRequirePersistent ? nsIWidget::LAYER_MANAGER_PERSISTENT
6695                            : nsIWidget::LAYER_MANAGER_CURRENT);
6696     return manager.forget();
6697   }
6698 
6699   return nullptr;
6700 }
6701 
6702 already_AddRefed<LayerManager> nsContentUtils::LayerManagerForDocument(
6703     const Document* aDoc) {
6704   return LayerManagerForDocumentInternal(aDoc, false);
6705 }
6706 
6707 already_AddRefed<LayerManager>
6708 nsContentUtils::PersistentLayerManagerForDocument(Document* aDoc) {
6709   return LayerManagerForDocumentInternal(aDoc, true);
6710 }
6711 
6712 bool nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal) {
6713   if (!aPrincipal) {
6714     return false;
6715   }
6716 
6717   if (aPrincipal->IsSystemPrincipal()) {
6718     return true;
6719   }
6720 
6721   return (StaticPrefs::dom_allow_XUL_XBL_for_file() &&
6722           aPrincipal->SchemeIs("file")) ||
6723          IsSitePermAllow(aPrincipal, "allowXULXBL"_ns);
6724 }
6725 
6726 bool nsContentUtils::IsPDFJSEnabled() {
6727   nsCOMPtr<nsIStreamConverter> conv = do_CreateInstance(
6728       "@mozilla.org/streamconv;1?from=application/pdf&to=text/html");
6729   return conv;
6730 }
6731 
6732 bool nsContentUtils::IsPDFJS(nsIPrincipal* aPrincipal) {
6733   if (!aPrincipal) {
6734     return false;
6735   }
6736   nsAutoCString spec;
6737   nsresult rv = aPrincipal->GetAsciiSpec(spec);
6738   NS_ENSURE_SUCCESS(rv, false);
6739   return spec.EqualsLiteral("resource://pdf.js/web/viewer.html");
6740 }
6741 
6742 bool nsContentUtils::IsPDFJS(JSContext* aCx, JSObject*) {
6743   return IsPDFJS(SubjectPrincipal(aCx));
6744 }
6745 
6746 already_AddRefed<nsIDocumentLoaderFactory>
6747 nsContentUtils::FindInternalContentViewer(const nsACString& aType,
6748                                           ContentViewerType* aLoaderType) {
6749   if (aLoaderType) {
6750     *aLoaderType = TYPE_UNSUPPORTED;
6751   }
6752 
6753   // one helper factory, please
6754   nsCOMPtr<nsICategoryManager> catMan(
6755       do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
6756   if (!catMan) return nullptr;
6757 
6758   nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
6759 
6760   nsCString contractID;
6761   nsresult rv =
6762       catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, contractID);
6763   if (NS_SUCCEEDED(rv)) {
6764     docFactory = do_GetService(contractID.get());
6765     if (docFactory && aLoaderType) {
6766       if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
6767         *aLoaderType = TYPE_CONTENT;
6768       else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
6769         *aLoaderType = TYPE_FALLBACK;
6770       else
6771         *aLoaderType = TYPE_UNKNOWN;
6772     }
6773     return docFactory.forget();
6774   }
6775 
6776   if (DecoderTraits::IsSupportedInVideoDocument(aType)) {
6777     docFactory =
6778         do_GetService("@mozilla.org/content/document-loader-factory;1");
6779     if (docFactory && aLoaderType) {
6780       *aLoaderType = TYPE_CONTENT;
6781     }
6782     return docFactory.forget();
6783   }
6784 
6785   return nullptr;
6786 }
6787 
6788 static void ReportPatternCompileFailure(nsAString& aPattern,
6789                                         const Document* aDocument,
6790                                         JS::MutableHandle<JS::Value> error,
6791                                         JSContext* cx) {
6792   JS::AutoSaveExceptionState savedExc(cx);
6793   JS::RootedObject exnObj(cx, &error.toObject());
6794   JS::RootedValue messageVal(cx);
6795   if (!JS_GetProperty(cx, exnObj, "message", &messageVal)) {
6796     return;
6797   }
6798   JS::RootedString messageStr(cx, messageVal.toString());
6799   MOZ_ASSERT(messageStr);
6800 
6801   AutoTArray<nsString, 2> strings;
6802   strings.AppendElement(aPattern);
6803   if (!AssignJSString(cx, *strings.AppendElement(), messageStr)) {
6804     return;
6805   }
6806 
6807   nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns,
6808                                   aDocument, nsContentUtils::eDOM_PROPERTIES,
6809                                   "PatternAttributeCompileFailure", strings);
6810   savedExc.drop();
6811 }
6812 
6813 // static
6814 Maybe<bool> nsContentUtils::IsPatternMatching(nsAString& aValue,
6815                                               nsAString& aPattern,
6816                                               const Document* aDocument) {
6817   NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
6818 
6819   // The fact that we're using a JS regexp under the hood should not be visible
6820   // to things like window onerror handlers, so we don't initialize our JSAPI
6821   // with the document's window (which may not exist anyway).
6822   AutoJSAPI jsapi;
6823   jsapi.Init();
6824   JSContext* cx = jsapi.cx();
6825   AutoDisableJSInterruptCallback disabler(cx);
6826 
6827   // We can use the junk scope here, because we're just using it for regexp
6828   // evaluation, not actual script execution, and we disable statics so that the
6829   // evaluation does not interact with the execution global.
6830   JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
6831 
6832   // Check if the pattern by itself is valid first, and not that it only becomes
6833   // valid once we add ^(?: and )$.
6834   JS::RootedValue error(cx);
6835   if (!JS::CheckRegExpSyntax(
6836           cx, static_cast<char16_t*>(aPattern.BeginWriting()),
6837           aPattern.Length(), JS::RegExpFlag::Unicode, &error)) {
6838     return Nothing();
6839   }
6840 
6841   if (!error.isUndefined()) {
6842     ReportPatternCompileFailure(aPattern, aDocument, &error, cx);
6843     return Some(true);
6844   }
6845 
6846   // The pattern has to match the entire value.
6847   aPattern.InsertLiteral(u"^(?:", 0);
6848   aPattern.AppendLiteral(")$");
6849 
6850   JS::Rooted<JSObject*> re(
6851       cx,
6852       JS::NewUCRegExpObject(cx, static_cast<char16_t*>(aPattern.BeginWriting()),
6853                             aPattern.Length(), JS::RegExpFlag::Unicode));
6854   if (!re) {
6855     return Nothing();
6856   }
6857 
6858   JS::Rooted<JS::Value> rval(cx, JS::NullValue());
6859   size_t idx = 0;
6860   if (!JS::ExecuteRegExpNoStatics(cx, re,
6861                                   static_cast<char16_t*>(aValue.BeginWriting()),
6862                                   aValue.Length(), &idx, true, &rval)) {
6863     return Nothing();
6864   }
6865 
6866   return Some(!rval.isNull());
6867 }
6868 
6869 // static
6870 nsresult nsContentUtils::URIInheritsSecurityContext(nsIURI* aURI,
6871                                                     bool* aResult) {
6872   // Note: about:blank URIs do NOT inherit the security context from the
6873   // current document, which is what this function tests for...
6874   return NS_URIChainHasFlags(
6875       aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, aResult);
6876 }
6877 
6878 // static
6879 bool nsContentUtils::ChannelShouldInheritPrincipal(
6880     nsIPrincipal* aLoadingPrincipal, nsIURI* aURI, bool aInheritForAboutBlank,
6881     bool aForceInherit) {
6882   MOZ_ASSERT(aLoadingPrincipal,
6883              "Can not check inheritance without a principal");
6884 
6885   // Only tell the channel to inherit if it can't provide its own security
6886   // context.
6887   //
6888   // XXX: If this is ever changed, check all callers for what owners
6889   //      they're passing in.  In particular, see the code and
6890   //      comments in nsDocShell::LoadURI where we fall back on
6891   //      inheriting the owner if called from chrome.  That would be
6892   //      very wrong if this code changed anything but channels that
6893   //      can't provide their own security context!
6894   //
6895   // If aForceInherit is true, we will inherit, even for a channel that
6896   // can provide its own security context. This is used for srcdoc loads.
6897   bool inherit = aForceInherit;
6898   if (!inherit) {
6899     bool uriInherits;
6900     // We expect URIInheritsSecurityContext to return success for an
6901     // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
6902     // This condition needs to match the one in nsDocShell::InternalLoad where
6903     // we're checking for things that will use the owner.
6904     inherit =
6905         (NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &uriInherits)) &&
6906          (uriInherits || (aInheritForAboutBlank && NS_IsAboutBlank(aURI)))) ||
6907         //
6908         // file: uri special-casing
6909         //
6910         // If this is a file: load opened from another file: then it may need
6911         // to inherit the owner from the referrer so they can script each other.
6912         // If we don't set the owner explicitly then each file: gets an owner
6913         // based on its own codebase later.
6914         //
6915         (URIIsLocalFile(aURI) &&
6916          NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, false)) &&
6917          // One more check here.  CheckMayLoad will always return true for the
6918          // system principal, but we do NOT want to inherit in that case.
6919          !aLoadingPrincipal->IsSystemPrincipal());
6920   }
6921   return inherit;
6922 }
6923 
6924 /* static */
6925 bool nsContentUtils::IsCutCopyAllowed(Document* aDocument,
6926                                       nsIPrincipal& aSubjectPrincipal) {
6927   if (StaticPrefs::dom_allow_cut_copy() && aDocument &&
6928       aDocument->HasValidTransientUserGestureActivation()) {
6929     return true;
6930   }
6931 
6932   return PrincipalHasPermission(aSubjectPrincipal, nsGkAtoms::clipboardWrite);
6933 }
6934 
6935 /* static */
6936 bool nsContentUtils::HaveEqualPrincipals(Document* aDoc1, Document* aDoc2) {
6937   if (!aDoc1 || !aDoc2) {
6938     return false;
6939   }
6940   bool principalsEqual = false;
6941   aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
6942   return principalsEqual;
6943 }
6944 
6945 /* static */
6946 bool nsContentUtils::HasPluginWithUncontrolledEventDispatch(
6947     nsIContent* aContent) {
6948   return false;
6949 }
6950 
6951 /* static */
6952 void nsContentUtils::FireMutationEventsForDirectParsing(
6953     Document* aDoc, nsIContent* aDest, int32_t aOldChildCount) {
6954   // Fire mutation events. Optimize for the case when there are no listeners
6955   int32_t newChildCount = aDest->GetChildCount();
6956   if (newChildCount && nsContentUtils::HasMutationListeners(
6957                            aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
6958     AutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
6959     NS_ASSERTION(newChildCount - aOldChildCount >= 0,
6960                  "What, some unexpected dom mutation has happened?");
6961     childNodes.SetCapacity(newChildCount - aOldChildCount);
6962     for (nsIContent* child = aDest->GetFirstChild(); child;
6963          child = child->GetNextSibling()) {
6964       childNodes.AppendElement(child);
6965     }
6966     FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
6967   }
6968 }
6969 
6970 /* static */
6971 Document* nsContentUtils::GetInProcessSubtreeRootDocument(Document* aDoc) {
6972   if (!aDoc) {
6973     return nullptr;
6974   }
6975   Document* doc = aDoc;
6976   while (doc->GetInProcessParentDocument()) {
6977     doc = doc->GetInProcessParentDocument();
6978   }
6979   return doc;
6980 }
6981 
6982 // static
6983 int32_t nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
6984                                                        int32_t aOffset) {
6985   // The structure of the anonymous frames within a text control frame is
6986   // an optional block frame, followed by an optional br frame.
6987 
6988   // If the offset frame has a child, then this frame is the block which
6989   // has the text frames (containing the content) as its children. This will
6990   // be the case if we click to the right of any of the text frames, or at the
6991   // bottom of the text area.
6992   nsIFrame* firstChild = aOffsetFrame->PrincipalChildList().FirstChild();
6993   if (firstChild) {
6994     // In this case, the passed-in offset is incorrect, and we want the length
6995     // of the entire content in the text control frame.
6996     return firstChild->GetContent()->Length();
6997   }
6998 
6999   if (aOffsetFrame->GetPrevSibling() && !aOffsetFrame->GetNextSibling()) {
7000     // In this case, we're actually within the last frame, which is a br
7001     // frame. Our offset should therefore be the length of the first child of
7002     // our parent.
7003     int32_t aOutOffset = aOffsetFrame->GetParent()
7004                              ->PrincipalChildList()
7005                              .FirstChild()
7006                              ->GetContent()
7007                              ->Length();
7008     return aOutOffset;
7009   }
7010 
7011   // Otherwise, we're within one of the text frames, in which case our offset
7012   // has already been correctly calculated.
7013   return aOffset;
7014 }
7015 
7016 // static
7017 void nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
7018                                                Element* aRoot,
7019                                                uint32_t& aOutStartOffset,
7020                                                uint32_t& aOutEndOffset) {
7021   MOZ_ASSERT(aSelection && aRoot);
7022 
7023   // We don't care which end of this selection is anchor and which is focus.  In
7024   // fact, we explicitly want to know which is the _start_ and which is the
7025   // _end_, not anchor vs focus.
7026   const nsRange* range = aSelection->GetAnchorFocusRange();
7027   if (!range) {
7028     // Nothing selected
7029     aOutStartOffset = aOutEndOffset = 0;
7030     return;
7031   }
7032 
7033   // All the node pointers here are raw pointers for performance.  We shouldn't
7034   // be doing anything in this function that invalidates the node tree.
7035   nsINode* startContainer = range->GetStartContainer();
7036   uint32_t startOffset = range->StartOffset();
7037   nsINode* endContainer = range->GetEndContainer();
7038   uint32_t endOffset = range->EndOffset();
7039 
7040   // We have at most two children, consisting of an optional text node followed
7041   // by an optional <br>.
7042   NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
7043   nsIContent* firstChild = aRoot->GetFirstChild();
7044 #ifdef DEBUG
7045   nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
7046   NS_ASSERTION(startContainer == aRoot || startContainer == firstChild ||
7047                    startContainer == lastChild,
7048                "Unexpected startContainer");
7049   NS_ASSERTION(endContainer == aRoot || endContainer == firstChild ||
7050                    endContainer == lastChild,
7051                "Unexpected endContainer");
7052   // firstChild is either text or a <br> (hence an element).
7053   MOZ_ASSERT_IF(firstChild, firstChild->IsText() || firstChild->IsElement());
7054 #endif
7055   // Testing IsElement() is faster than testing IsNodeOfType(), since it's
7056   // non-virtual.
7057   if (!firstChild || firstChild->IsElement()) {
7058     // No text node, so everything is 0
7059     startOffset = endOffset = 0;
7060   } else {
7061     // First child is text.  If the start/end is already in the text node,
7062     // or the start of the root node, no change needed.  If it's in the root
7063     // node but not the start, or in the trailing <br>, we need to set the
7064     // offset to the end.
7065     if ((startContainer == aRoot && startOffset != 0) ||
7066         (startContainer != aRoot && startContainer != firstChild)) {
7067       startOffset = firstChild->Length();
7068     }
7069     if ((endContainer == aRoot && endOffset != 0) ||
7070         (endContainer != aRoot && endContainer != firstChild)) {
7071       endOffset = firstChild->Length();
7072     }
7073   }
7074 
7075   MOZ_ASSERT(startOffset <= endOffset);
7076   aOutStartOffset = startOffset;
7077   aOutEndOffset = endOffset;
7078 }
7079 
7080 // static
7081 HTMLEditor* nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext) {
7082   if (!aPresContext) {
7083     return nullptr;
7084   }
7085   return GetHTMLEditor(aPresContext->GetDocShell());
7086 }
7087 
7088 // static
7089 HTMLEditor* nsContentUtils::GetHTMLEditor(nsDocShell* aDocShell) {
7090   bool isEditable;
7091   if (!aDocShell || NS_FAILED(aDocShell->GetEditable(&isEditable)) ||
7092       !isEditable) {
7093     return nullptr;
7094   }
7095   return aDocShell->GetHTMLEditor();
7096 }
7097 
7098 // static
7099 EditorBase* nsContentUtils::GetActiveEditor(nsPresContext* aPresContext) {
7100   if (!aPresContext) {
7101     return nullptr;
7102   }
7103 
7104   return GetActiveEditor(aPresContext->Document()->GetWindow());
7105 }
7106 
7107 // static
7108 EditorBase* nsContentUtils::GetActiveEditor(nsPIDOMWindowOuter* aWindow) {
7109   if (!aWindow || !aWindow->GetExtantDoc()) {
7110     return nullptr;
7111   }
7112 
7113   // If it's in designMode, nobody can have focus.  Therefore, the HTMLEditor
7114   // handles all events.  I.e., it's focused editor in this case.
7115   if (aWindow->GetExtantDoc()->HasFlag(NODE_IS_EDITABLE)) {
7116     return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7117   }
7118 
7119   // If focused element is associated with TextEditor, it must be <input>
7120   // element or <textarea> element.  Let's return it even if it's in a
7121   // contenteditable element.
7122   nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
7123   if (Element* focusedElement = nsFocusManager::GetFocusedDescendant(
7124           aWindow, nsFocusManager::SearchRange::eOnlyCurrentWindow,
7125           getter_AddRefs(focusedWindow))) {
7126     if (TextEditor* textEditor = focusedElement->GetTextEditorInternal()) {
7127       return textEditor;
7128     }
7129   }
7130 
7131   // Otherwise, HTMLEditor may handle inputs even non-editable element has
7132   // focus or nobody has focus.
7133   return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7134 }
7135 
7136 // static
7137 TextEditor* nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(
7138     nsIContent* aAnonymousContent) {
7139   if (!aAnonymousContent) {
7140     return nullptr;
7141   }
7142   nsIContent* parent = aAnonymousContent->FindFirstNonChromeOnlyAccessContent();
7143   if (!parent || parent == aAnonymousContent) {
7144     return nullptr;
7145   }
7146   if (HTMLInputElement* inputElement =
7147           HTMLInputElement::FromNodeOrNull(parent)) {
7148     return inputElement->GetTextEditorWithoutCreation();
7149   }
7150   if (HTMLTextAreaElement* textareaElement =
7151           HTMLTextAreaElement::FromNodeOrNull(parent)) {
7152     return textareaElement->GetTextEditorWithoutCreation();
7153   }
7154   return nullptr;
7155 }
7156 
7157 // static
7158 bool nsContentUtils::IsNodeInEditableRegion(nsINode* aNode) {
7159   while (aNode) {
7160     if (aNode->IsEditable()) {
7161       return true;
7162     }
7163     aNode = aNode->GetParent();
7164   }
7165   return false;
7166 }
7167 
7168 // static
7169 bool nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader) {
7170   if (IsForbiddenSystemRequestHeader(aHeader)) {
7171     return true;
7172   }
7173 
7174   return StringBeginsWith(aHeader, "proxy-"_ns,
7175                           nsCaseInsensitiveCStringComparator) ||
7176          StringBeginsWith(aHeader, "sec-"_ns,
7177                           nsCaseInsensitiveCStringComparator);
7178 }
7179 
7180 // static
7181 bool nsContentUtils::IsForbiddenSystemRequestHeader(const nsACString& aHeader) {
7182   static const char* kInvalidHeaders[] = {"accept-charset",
7183                                           "accept-encoding",
7184                                           "access-control-request-headers",
7185                                           "access-control-request-method",
7186                                           "connection",
7187                                           "content-length",
7188                                           "cookie",
7189                                           "cookie2",
7190                                           "date",
7191                                           "dnt",
7192                                           "expect",
7193                                           "host",
7194                                           "keep-alive",
7195                                           "origin",
7196                                           "referer",
7197                                           "te",
7198                                           "trailer",
7199                                           "transfer-encoding",
7200                                           "upgrade",
7201                                           "via"};
7202   for (auto& kInvalidHeader : kInvalidHeaders) {
7203     if (aHeader.LowerCaseEqualsASCII(kInvalidHeader)) {
7204       return true;
7205     }
7206   }
7207   return false;
7208 }
7209 
7210 // static
7211 bool nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader) {
7212   return (aHeader.LowerCaseEqualsASCII("set-cookie") ||
7213           aHeader.LowerCaseEqualsASCII("set-cookie2"));
7214 }
7215 
7216 // static
7217 bool nsContentUtils::IsCorsUnsafeRequestHeaderValue(
7218     const nsACString& aHeaderValue) {
7219   const char* cur = aHeaderValue.BeginReading();
7220   const char* end = aHeaderValue.EndReading();
7221 
7222   while (cur != end) {
7223     // Implementation of
7224     // https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte Is less
7225     // than a space but not a horizontal tab
7226     if ((*cur < ' ' && *cur != '\t') || *cur == '"' || *cur == '(' ||
7227         *cur == ')' || *cur == ':' || *cur == '<' || *cur == '>' ||
7228         *cur == '?' || *cur == '@' || *cur == '[' || *cur == '\\' ||
7229         *cur == ']' || *cur == '{' || *cur == '}' ||
7230         *cur == 0x7F) {  // 0x75 is DEL
7231       return true;
7232     }
7233     cur++;
7234   }
7235   return false;
7236 }
7237 
7238 // static
7239 bool nsContentUtils::IsAllowedNonCorsAccept(const nsACString& aHeaderValue) {
7240   if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7241     return false;
7242   }
7243   return true;
7244 }
7245 
7246 // static
7247 bool nsContentUtils::IsAllowedNonCorsContentType(
7248     const nsACString& aHeaderValue) {
7249   nsAutoCString contentType;
7250   nsAutoCString unused;
7251 
7252   if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7253     return false;
7254   }
7255 
7256   nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
7257   if (NS_FAILED(rv)) {
7258     return false;
7259   }
7260 
7261   return contentType.LowerCaseEqualsLiteral("text/plain") ||
7262          contentType.LowerCaseEqualsLiteral(
7263              "application/x-www-form-urlencoded") ||
7264          contentType.LowerCaseEqualsLiteral("multipart/form-data");
7265 }
7266 
7267 // static
7268 bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
7269   const char* cur = aHeaderValue.BeginReading();
7270   const char* end = aHeaderValue.EndReading();
7271 
7272   while (cur != end) {
7273     if ((*cur >= '0' && *cur <= '9') || (*cur >= 'A' && *cur <= 'Z') ||
7274         (*cur >= 'a' && *cur <= 'z') || *cur == ' ' || *cur == '*' ||
7275         *cur == ',' || *cur == '-' || *cur == '.' || *cur == ';' ||
7276         *cur == '=') {
7277       cur++;
7278       continue;
7279     }
7280     return false;
7281   }
7282   return true;
7283 }
7284 
7285 // static
7286 bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
7287                                                    const nsACString& aValue) {
7288   // see https://fetch.spec.whatwg.org/#cors-safelisted-request-header
7289   if (aValue.Length() > 128) {
7290     return false;
7291   }
7292   return (aName.LowerCaseEqualsLiteral("accept") &&
7293           nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
7294          (aName.LowerCaseEqualsLiteral("accept-language") &&
7295           nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7296          (aName.LowerCaseEqualsLiteral("content-language") &&
7297           nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7298          (aName.LowerCaseEqualsLiteral("content-type") &&
7299           nsContentUtils::IsAllowedNonCorsContentType(aValue));
7300 }
7301 
7302 mozilla::LogModule* nsContentUtils::ResistFingerprintingLog() {
7303   return gResistFingerprintingLog;
7304 }
7305 mozilla::LogModule* nsContentUtils::DOMDumpLog() { return sDOMDumpLog; }
7306 
7307 bool nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep,
7308                                         nsAString& aResult,
7309                                         const fallible_t& aFallible) {
7310   aResult.Truncate();
7311   return AppendNodeTextContent(aNode, aDeep, aResult, aFallible);
7312 }
7313 
7314 void nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep,
7315                                         nsAString& aResult) {
7316   if (!GetNodeTextContent(aNode, aDeep, aResult, fallible)) {
7317     NS_ABORT_OOM(0);  // Unfortunately we don't know the allocation size
7318   }
7319 }
7320 
7321 void nsContentUtils::DestroyMatchString(void* aData) {
7322   if (aData) {
7323     nsString* matchString = static_cast<nsString*>(aData);
7324     delete matchString;
7325   }
7326 }
7327 
7328 bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
7329   // Table ordered from most to least likely JS MIME types.
7330   static const char* jsTypes[] = {"text/javascript",
7331                                   "text/ecmascript",
7332                                   "application/javascript",
7333                                   "application/ecmascript",
7334                                   "application/x-javascript",
7335                                   "application/x-ecmascript",
7336                                   "text/javascript1.0",
7337                                   "text/javascript1.1",
7338                                   "text/javascript1.2",
7339                                   "text/javascript1.3",
7340                                   "text/javascript1.4",
7341                                   "text/javascript1.5",
7342                                   "text/jscript",
7343                                   "text/livescript",
7344                                   "text/x-ecmascript",
7345                                   "text/x-javascript",
7346                                   nullptr};
7347 
7348   for (uint32_t i = 0; jsTypes[i]; ++i) {
7349     if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
7350       return true;
7351     }
7352   }
7353 
7354   return false;
7355 }
7356 
7357 nsresult nsContentUtils::GenerateUUIDInPlace(nsID& aUUID) {
7358   MOZ_ASSERT(sUUIDGenerator);
7359 
7360   nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&aUUID);
7361   if (NS_WARN_IF(NS_FAILED(rv))) {
7362     return rv;
7363   }
7364 
7365   return NS_OK;
7366 }
7367 
7368 nsID nsContentUtils::GenerateUUID() {
7369   MOZ_DIAGNOSTIC_ASSERT(sUUIDGenerator);
7370 
7371   nsID uuid;
7372   nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&uuid);
7373   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
7374 
7375   return uuid;
7376 }
7377 
7378 bool nsContentUtils::PrefetchPreloadEnabled(nsIDocShell* aDocShell) {
7379   //
7380   // SECURITY CHECK: disable prefetching and preloading from mailnews!
7381   //
7382   // walk up the docshell tree to see if any containing
7383   // docshell are of type MAIL.
7384   //
7385 
7386   if (!aDocShell) {
7387     return false;
7388   }
7389 
7390   nsCOMPtr<nsIDocShell> docshell = aDocShell;
7391   nsCOMPtr<nsIDocShellTreeItem> parentItem;
7392 
7393   do {
7394     auto appType = docshell->GetAppType();
7395     if (appType == nsIDocShell::APP_TYPE_MAIL) {
7396       return false;  // do not prefetch, preload, preconnect from mailnews
7397     }
7398 
7399     docshell->GetInProcessParent(getter_AddRefs(parentItem));
7400     if (parentItem) {
7401       docshell = do_QueryInterface(parentItem);
7402       if (!docshell) {
7403         NS_ERROR("cannot get a docshell from a treeItem!");
7404         return false;
7405       }
7406     }
7407   } while (parentItem);
7408 
7409   return true;
7410 }
7411 
7412 uint64_t nsContentUtils::GetInnerWindowID(nsIRequest* aRequest) {
7413   // can't do anything if there's no nsIRequest!
7414   if (!aRequest) {
7415     return 0;
7416   }
7417 
7418   nsCOMPtr<nsILoadGroup> loadGroup;
7419   nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
7420 
7421   if (NS_FAILED(rv) || !loadGroup) {
7422     return 0;
7423   }
7424 
7425   return GetInnerWindowID(loadGroup);
7426 }
7427 
7428 uint64_t nsContentUtils::GetInnerWindowID(nsILoadGroup* aLoadGroup) {
7429   if (!aLoadGroup) {
7430     return 0;
7431   }
7432 
7433   nsCOMPtr<nsIInterfaceRequestor> callbacks;
7434   nsresult rv = aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
7435   if (NS_FAILED(rv) || !callbacks) {
7436     return 0;
7437   }
7438 
7439   nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
7440   if (!loadContext) {
7441     return 0;
7442   }
7443 
7444   nsCOMPtr<mozIDOMWindowProxy> window;
7445   rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
7446   if (NS_FAILED(rv) || !window) {
7447     return 0;
7448   }
7449 
7450   auto* pwindow = nsPIDOMWindowOuter::From(window);
7451   if (!pwindow) {
7452     return 0;
7453   }
7454 
7455   nsPIDOMWindowInner* inner = pwindow->GetCurrentInnerWindow();
7456   return inner ? inner->WindowID() : 0;
7457 }
7458 
7459 // static
7460 void nsContentUtils::MaybeFixIPv6Host(nsACString& aHost) {
7461   if (aHost.FindChar(':') != -1) {  // Escape IPv6 address
7462     MOZ_ASSERT(!aHost.Length() ||
7463                (aHost[0] != '[' && aHost[aHost.Length() - 1] != ']'));
7464     aHost.Insert('[', 0);
7465     aHost.Append(']');
7466   }
7467 }
7468 
7469 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7470                                                    nsACString& aHost) {
7471   aHost.Truncate();
7472   nsresult rv = aURI->GetHost(aHost);
7473   if (NS_FAILED(rv)) {  // Some URIs do not have a host
7474     return rv;
7475   }
7476 
7477   MaybeFixIPv6Host(aHost);
7478 
7479   return NS_OK;
7480 }
7481 
7482 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7483                                                    nsAString& aHost) {
7484   nsAutoCString hostname;
7485   nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname);
7486   if (NS_FAILED(rv)) {
7487     return rv;
7488   }
7489   CopyUTF8toUTF16(hostname, aHost);
7490   return NS_OK;
7491 }
7492 
7493 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIPrincipal* aPrincipal,
7494                                                    nsACString& aHost) {
7495   nsresult rv = aPrincipal->GetAsciiHost(aHost);
7496   if (NS_FAILED(rv)) {  // Some URIs do not have a host
7497     return rv;
7498   }
7499 
7500   MaybeFixIPv6Host(aHost);
7501   return NS_OK;
7502 }
7503 
7504 CallState nsContentUtils::CallOnAllRemoteChildren(
7505     MessageBroadcaster* aManager,
7506     const std::function<CallState(BrowserParent*)>& aCallback) {
7507   uint32_t browserChildCount = aManager->ChildCount();
7508   for (uint32_t j = 0; j < browserChildCount; ++j) {
7509     RefPtr<MessageListenerManager> childMM = aManager->GetChildAt(j);
7510     if (!childMM) {
7511       continue;
7512     }
7513 
7514     RefPtr<MessageBroadcaster> nonLeafMM = MessageBroadcaster::From(childMM);
7515     if (nonLeafMM) {
7516       if (CallOnAllRemoteChildren(nonLeafMM, aCallback) == CallState::Stop) {
7517         return CallState::Stop;
7518       }
7519       continue;
7520     }
7521 
7522     mozilla::dom::ipc::MessageManagerCallback* cb = childMM->GetCallback();
7523     if (cb) {
7524       nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
7525       BrowserParent* remote = BrowserParent::GetFrom(fl);
7526       if (remote && aCallback) {
7527         if (aCallback(remote) == CallState::Stop) {
7528           return CallState::Stop;
7529         }
7530       }
7531     }
7532   }
7533 
7534   return CallState::Continue;
7535 }
7536 
7537 void nsContentUtils::CallOnAllRemoteChildren(
7538     nsPIDOMWindowOuter* aWindow,
7539     const std::function<CallState(BrowserParent*)>& aCallback) {
7540   nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(aWindow);
7541   if (window->IsChromeWindow()) {
7542     RefPtr<MessageBroadcaster> windowMM = window->GetMessageManager();
7543     if (windowMM) {
7544       CallOnAllRemoteChildren(windowMM, aCallback);
7545     }
7546   }
7547 }
7548 
7549 struct UIStateChangeInfo {
7550   UIStateChangeType mShowFocusRings;
7551 
7552   explicit UIStateChangeInfo(UIStateChangeType aShowFocusRings)
7553       : mShowFocusRings(aShowFocusRings) {}
7554 };
7555 
7556 void nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(
7557     nsPIDOMWindowOuter* aWindow, UIStateChangeType aShowFocusRings) {
7558   UIStateChangeInfo stateInfo(aShowFocusRings);
7559   CallOnAllRemoteChildren(aWindow, [&stateInfo](BrowserParent* aBrowserParent) {
7560     Unused << aBrowserParent->SendSetKeyboardIndicators(
7561         stateInfo.mShowFocusRings);
7562     return CallState::Continue;
7563   });
7564 }
7565 
7566 nsresult nsContentUtils::IPCTransferableToTransferable(
7567     const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
7568     nsIPrincipal* aRequestingPrincipal,
7569     const nsContentPolicyType& aContentPolicyType,
7570     nsITransferable* aTransferable, mozilla::dom::ContentParent* aContentParent,
7571     mozilla::dom::BrowserChild* aBrowserChild) {
7572   nsresult rv;
7573 
7574   aTransferable->SetIsPrivateData(aIsPrivateData);
7575 
7576   const nsTArray<IPCDataTransferItem>& items = aDataTransfer.items();
7577   for (const auto& item : items) {
7578     aTransferable->AddDataFlavor(item.flavor().get());
7579 
7580     if (item.data().type() == IPCDataTransferData::TnsString) {
7581       nsCOMPtr<nsISupportsString> dataWrapper =
7582           do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
7583       NS_ENSURE_SUCCESS(rv, rv);
7584 
7585       const nsString& text = item.data().get_nsString();
7586       rv = dataWrapper->SetData(text);
7587       NS_ENSURE_SUCCESS(rv, rv);
7588 
7589       rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper);
7590 
7591       NS_ENSURE_SUCCESS(rv, rv);
7592     } else if (item.data().type() == IPCDataTransferData::TShmem) {
7593       if (nsContentUtils::IsFlavorImage(item.flavor())) {
7594         nsCOMPtr<imgIContainer> imageContainer;
7595         rv = nsContentUtils::DataTransferItemToImage(
7596             item, getter_AddRefs(imageContainer));
7597         NS_ENSURE_SUCCESS(rv, rv);
7598 
7599         aTransferable->SetTransferData(item.flavor().get(), imageContainer);
7600       } else {
7601         nsCOMPtr<nsISupportsCString> dataWrapper =
7602             do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
7603         NS_ENSURE_SUCCESS(rv, rv);
7604 
7605         // The buffer contains the terminating null.
7606         Shmem itemData = item.data().get_Shmem();
7607         const nsDependentCSubstring text(itemData.get<char>(),
7608                                          itemData.Size<char>());
7609         rv = dataWrapper->SetData(text);
7610         NS_ENSURE_SUCCESS(rv, rv);
7611 
7612         rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper);
7613 
7614         NS_ENSURE_SUCCESS(rv, rv);
7615       }
7616 
7617       if (aContentParent) {
7618         Unused << aContentParent->DeallocShmem(item.data().get_Shmem());
7619       } else if (aBrowserChild) {
7620         Unused << aBrowserChild->DeallocShmem(item.data().get_Shmem());
7621       }
7622     }
7623   }
7624 
7625   aTransferable->SetRequestingPrincipal(aRequestingPrincipal);
7626   aTransferable->SetContentPolicyType(aContentPolicyType);
7627   return NS_OK;
7628 }
7629 
7630 void nsContentUtils::TransferablesToIPCTransferables(
7631     nsIArray* aTransferables, nsTArray<IPCDataTransfer>& aIPC,
7632     bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
7633     mozilla::dom::ContentParent* aParent) {
7634   aIPC.Clear();
7635   if (aTransferables) {
7636     uint32_t transferableCount = 0;
7637     aTransferables->GetLength(&transferableCount);
7638     for (uint32_t i = 0; i < transferableCount; ++i) {
7639       IPCDataTransfer* dt = aIPC.AppendElement();
7640       nsCOMPtr<nsITransferable> transferable =
7641           do_QueryElementAt(aTransferables, i);
7642       TransferableToIPCTransferable(transferable, dt, aInSyncMessage, aChild,
7643                                     aParent);
7644     }
7645   }
7646 }
7647 
7648 nsresult nsContentUtils::SlurpFileToString(nsIFile* aFile,
7649                                            nsACString& aString) {
7650   aString.Truncate();
7651 
7652   nsCOMPtr<nsIURI> fileURI;
7653   nsresult rv = NS_NewFileURI(getter_AddRefs(fileURI), aFile);
7654   if (NS_FAILED(rv)) {
7655     return rv;
7656   }
7657 
7658   nsCOMPtr<nsIChannel> channel;
7659   rv = NS_NewChannel(getter_AddRefs(channel), fileURI,
7660                      nsContentUtils::GetSystemPrincipal(),
7661                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
7662                      nsIContentPolicy::TYPE_OTHER);
7663   if (NS_FAILED(rv)) {
7664     return rv;
7665   }
7666 
7667   nsCOMPtr<nsIInputStream> stream;
7668   rv = channel->Open(getter_AddRefs(stream));
7669   if (NS_FAILED(rv)) {
7670     return rv;
7671   }
7672 
7673   rv = NS_ConsumeStream(stream, UINT32_MAX, aString);
7674   if (NS_FAILED(rv)) {
7675     return rv;
7676   }
7677 
7678   rv = stream->Close();
7679   if (NS_FAILED(rv)) {
7680     return rv;
7681   }
7682 
7683   return NS_OK;
7684 }
7685 
7686 bool nsContentUtils::IsFileImage(nsIFile* aFile, nsACString& aType) {
7687   nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1");
7688   if (!mime) {
7689     return false;
7690   }
7691 
7692   nsresult rv = mime->GetTypeFromFile(aFile, aType);
7693   if (NS_FAILED(rv)) {
7694     return false;
7695   }
7696 
7697   return StringBeginsWith(aType, "image/"_ns);
7698 }
7699 
7700 nsresult nsContentUtils::CalculateBufferSizeForImage(
7701     const uint32_t& aStride, const IntSize& aImageSize,
7702     const SurfaceFormat& aFormat, size_t* aMaxBufferSize,
7703     size_t* aUsedBufferSize) {
7704   CheckedInt32 requiredBytes =
7705       CheckedInt32(aStride) * CheckedInt32(aImageSize.height);
7706 
7707   CheckedInt32 usedBytes =
7708       requiredBytes - aStride +
7709       (CheckedInt32(aImageSize.width) * BytesPerPixel(aFormat));
7710   if (!usedBytes.isValid()) {
7711     return NS_ERROR_FAILURE;
7712   }
7713 
7714   MOZ_ASSERT(requiredBytes.isValid(), "usedBytes valid but not required?");
7715   *aMaxBufferSize = requiredBytes.value();
7716   *aUsedBufferSize = usedBytes.value();
7717   return NS_OK;
7718 }
7719 
7720 nsresult nsContentUtils::DataTransferItemToImage(
7721     const IPCDataTransferItem& aItem, imgIContainer** aContainer) {
7722   MOZ_ASSERT(aItem.data().type() == IPCDataTransferData::TShmem);
7723   MOZ_ASSERT(IsFlavorImage(aItem.flavor()));
7724 
7725   const IPCDataTransferImage& imageDetails = aItem.imageDetails();
7726   const IntSize size(imageDetails.width(), imageDetails.height());
7727   if (!size.width || !size.height) {
7728     return NS_ERROR_FAILURE;
7729   }
7730 
7731   Shmem data = aItem.data().get_Shmem();
7732 
7733   // Validate shared memory buffer size
7734   size_t imageBufLen = 0;
7735   size_t maxBufLen = 0;
7736   nsresult rv = CalculateBufferSizeForImage(imageDetails.stride(), size,
7737                                             imageDetails.format(), &maxBufLen,
7738                                             &imageBufLen);
7739   if (NS_FAILED(rv)) {
7740     return rv;
7741   }
7742   if (imageBufLen > data.Size<uint8_t>()) {
7743     return NS_ERROR_FAILURE;
7744   }
7745 
7746   RefPtr<DataSourceSurface> image = CreateDataSourceSurfaceFromData(
7747       size, imageDetails.format(), data.get<uint8_t>(), imageDetails.stride());
7748 
7749   RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size);
7750   nsCOMPtr<imgIContainer> imageContainer =
7751       image::ImageOps::CreateFromDrawable(drawable);
7752   imageContainer.forget(aContainer);
7753 
7754   return NS_OK;
7755 }
7756 
7757 bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
7758   return aFlavor.EqualsLiteral(kNativeImageMime) ||
7759          aFlavor.EqualsLiteral(kJPEGImageMime) ||
7760          aFlavor.EqualsLiteral(kJPGImageMime) ||
7761          aFlavor.EqualsLiteral(kPNGImageMime) ||
7762          aFlavor.EqualsLiteral(kGIFImageMime);
7763 }
7764 
7765 static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
7766                             mozilla::dom::ContentParent* aParent,
7767                             const nsACString& aInput) {
7768   MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
7769 
7770   IShmemAllocator* allocator = aChild ? static_cast<IShmemAllocator*>(aChild)
7771                                       : static_cast<IShmemAllocator*>(aParent);
7772 
7773   Shmem result;
7774   if (!allocator->AllocShmem(aInput.Length(), SharedMemory::TYPE_BASIC,
7775                              &result)) {
7776     return result;
7777   }
7778 
7779   memcpy(result.get<char>(), aInput.BeginReading(), aInput.Length());
7780 
7781   return result;
7782 }
7783 
7784 void nsContentUtils::TransferableToIPCTransferable(
7785     nsITransferable* aTransferable, IPCDataTransfer* aIPCDataTransfer,
7786     bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
7787     mozilla::dom::ContentParent* aParent) {
7788   MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
7789 
7790   if (aTransferable) {
7791     nsTArray<nsCString> flavorList;
7792     aTransferable->FlavorsTransferableCanExport(flavorList);
7793 
7794     for (uint32_t j = 0; j < flavorList.Length(); ++j) {
7795       nsCString& flavorStr = flavorList[j];
7796       if (!flavorStr.Length()) {
7797         continue;
7798       }
7799 
7800       nsCOMPtr<nsISupports> data;
7801       nsresult rv =
7802           aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data));
7803 
7804       if (NS_FAILED(rv) || !data) {
7805         if (aInSyncMessage) {
7806           // Can't do anything.
7807           continue;
7808         }
7809 
7810         // This is a hack to support kFilePromiseMime.
7811         // On Windows there just needs to be an entry for it,
7812         // and for OSX we need to create
7813         // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
7814         if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
7815           IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7816           item->flavor() = flavorStr;
7817           item->data() = NS_ConvertUTF8toUTF16(flavorStr);
7818           continue;
7819         }
7820 
7821         // Empty element, transfer only the flavor
7822         IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7823         item->flavor() = flavorStr;
7824         item->data() = nsString();
7825         continue;
7826       }
7827 
7828       if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(data)) {
7829         nsAutoString dataAsString;
7830         text->GetData(dataAsString);
7831         IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7832         item->flavor() = flavorStr;
7833         item->data() = dataAsString;
7834       } else if (nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data)) {
7835         nsAutoCString dataAsString;
7836         ctext->GetData(dataAsString);
7837 
7838         Shmem dataAsShmem = ConvertToShmem(aChild, aParent, dataAsString);
7839         if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
7840           continue;
7841         }
7842 
7843         IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7844         item->flavor() = flavorStr;
7845         item->data() = std::move(dataAsShmem);
7846       } else if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
7847         // Images to be pasted on the clipboard are nsIInputStreams
7848         nsCString imageData;
7849         NS_ConsumeStream(stream, UINT32_MAX, imageData);
7850 
7851         Shmem imageDataShmem = ConvertToShmem(aChild, aParent, imageData);
7852         if (!imageDataShmem.IsReadable() || !imageDataShmem.Size<char>()) {
7853           continue;
7854         }
7855 
7856         IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7857         item->flavor() = flavorStr;
7858         item->data() = std::move(imageDataShmem);
7859       } else if (nsCOMPtr<imgIContainer> image = do_QueryInterface(data)) {
7860         // Images to be placed on the clipboard are imgIContainers.
7861         RefPtr<mozilla::gfx::SourceSurface> surface = image->GetFrame(
7862             imgIContainer::FRAME_CURRENT,
7863             imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
7864         if (!surface) {
7865           continue;
7866         }
7867         RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
7868             surface->GetDataSurface();
7869         if (!dataSurface) {
7870           continue;
7871         }
7872         size_t length;
7873         int32_t stride;
7874         IShmemAllocator* allocator =
7875             aChild ? static_cast<IShmemAllocator*>(aChild)
7876                    : static_cast<IShmemAllocator*>(aParent);
7877         Maybe<Shmem> surfaceData =
7878             GetSurfaceData(dataSurface, &length, &stride, allocator);
7879 
7880         if (surfaceData.isNothing()) {
7881           continue;
7882         }
7883 
7884         IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7885         item->flavor() = flavorStr;
7886         // Turn item->data() into an nsCString prior to accessing it.
7887         item->data() = std::move(surfaceData.ref());
7888 
7889         IPCDataTransferImage& imageDetails = item->imageDetails();
7890         mozilla::gfx::IntSize size = dataSurface->GetSize();
7891         imageDetails.width() = size.width;
7892         imageDetails.height() = size.height;
7893         imageDetails.stride() = stride;
7894         imageDetails.format() = dataSurface->GetFormat();
7895       } else {
7896         // Otherwise, handle this as a file.
7897         nsCOMPtr<BlobImpl> blobImpl;
7898         if (nsCOMPtr<nsIFile> file = do_QueryInterface(data)) {
7899           // If we can send this over as a blob, do so. Otherwise, we're
7900           // responding to a sync message and the child can't process the blob
7901           // constructor before processing our response, which would crash. In
7902           // that case, hope that the caller is nsClipboardProxy::GetData,
7903           // called from editor and send over images as raw data.
7904           if (aInSyncMessage) {
7905             nsAutoCString type;
7906             if (IsFileImage(file, type)) {
7907               nsAutoCString data;
7908               SlurpFileToString(file, data);
7909 
7910               Shmem dataAsShmem = ConvertToShmem(aChild, aParent, data);
7911               if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
7912                 continue;
7913               }
7914 
7915               IPCDataTransferItem* item =
7916                   aIPCDataTransfer->items().AppendElement();
7917               item->flavor() = type;
7918               item->data() = std::move(dataAsShmem);
7919             }
7920 
7921             continue;
7922           }
7923 
7924           if (aParent) {
7925             bool isDir = false;
7926             if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
7927               nsAutoString path;
7928               if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
7929                 continue;
7930               }
7931 
7932               RefPtr<FileSystemSecurity> fss =
7933                   FileSystemSecurity::GetOrCreate();
7934               fss->GrantAccessToContentProcess(aParent->ChildID(), path);
7935             }
7936           }
7937 
7938           blobImpl = new FileBlobImpl(file);
7939 
7940           IgnoredErrorResult rv;
7941 
7942           // Ensure that file data is cached no that the content process
7943           // has this data available to it when passed over:
7944           blobImpl->GetSize(rv);
7945           if (NS_WARN_IF(rv.Failed())) {
7946             continue;
7947           }
7948 
7949           blobImpl->GetLastModified(rv);
7950           if (NS_WARN_IF(rv.Failed())) {
7951             continue;
7952           }
7953         } else {
7954           if (aInSyncMessage) {
7955             // Can't do anything.
7956             continue;
7957           }
7958           blobImpl = do_QueryInterface(data);
7959         }
7960         if (blobImpl) {
7961           IPCDataTransferData data;
7962           IPCBlob ipcBlob;
7963 
7964           // If we failed to create the blob actor, then this blob probably
7965           // can't get the file size for the underlying file, ignore it for
7966           // now. TODO pass this through anyway.
7967           if (aChild) {
7968             nsresult rv = IPCBlobUtils::Serialize(blobImpl, aChild, ipcBlob);
7969             if (NS_WARN_IF(NS_FAILED(rv))) {
7970               continue;
7971             }
7972 
7973             data = ipcBlob;
7974           } else if (aParent) {
7975             nsresult rv = IPCBlobUtils::Serialize(blobImpl, aParent, ipcBlob);
7976             if (NS_WARN_IF(NS_FAILED(rv))) {
7977               continue;
7978             }
7979 
7980             data = ipcBlob;
7981           }
7982 
7983           IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7984           item->flavor() = flavorStr;
7985           item->data() = data;
7986         }
7987       }
7988     }
7989   }
7990 }
7991 
7992 namespace {
7993 // The default type used for calling GetSurfaceData(). Gets surface data as
7994 // raw buffer.
7995 struct GetSurfaceDataRawBuffer {
7996   using ReturnType = mozilla::UniquePtr<char[]>;
7997   using BufferType = char*;
7998 
7999   ReturnType Allocate(size_t aSize) { return ReturnType(new char[aSize]); }
8000 
8001   static BufferType GetBuffer(const ReturnType& aReturnValue) {
8002     return aReturnValue.get();
8003   }
8004 
8005   static ReturnType NullValue() { return ReturnType(); }
8006 };
8007 
8008 // The type used for calling GetSurfaceData() that allocates and writes to
8009 // a shared memory buffer.
8010 struct GetSurfaceDataShmem {
8011   using ReturnType = Maybe<Shmem>;
8012   using BufferType = char*;
8013 
8014   explicit GetSurfaceDataShmem(IShmemAllocator* aAllocator)
8015       : mAllocator(aAllocator) {}
8016 
8017   ReturnType Allocate(size_t aSize) {
8018     Shmem shmem;
8019     if (!mAllocator->AllocShmem(aSize, SharedMemory::TYPE_BASIC, &shmem)) {
8020       return Nothing();
8021     }
8022 
8023     return Some(shmem);
8024   }
8025 
8026   static BufferType GetBuffer(const ReturnType& aReturnValue) {
8027     return aReturnValue.isSome() ? aReturnValue.ref().get<char>() : nullptr;
8028   }
8029 
8030   static ReturnType NullValue() { return ReturnType(); }
8031 
8032  private:
8033   IShmemAllocator* mAllocator;
8034 };
8035 
8036 /*
8037  * Get the pixel data from the given source surface and return it as a buffer.
8038  * The length and stride will be assigned from the surface.
8039  */
8040 template <typename GetSurfaceDataContext = GetSurfaceDataRawBuffer>
8041 typename GetSurfaceDataContext::ReturnType GetSurfaceDataImpl(
8042     mozilla::gfx::DataSourceSurface* aSurface, size_t* aLength,
8043     int32_t* aStride,
8044     GetSurfaceDataContext aContext = GetSurfaceDataContext()) {
8045   mozilla::gfx::DataSourceSurface::MappedSurface map;
8046   if (!aSurface->Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map)) {
8047     return GetSurfaceDataContext::NullValue();
8048   }
8049 
8050   size_t bufLen = 0;
8051   size_t maxBufLen = 0;
8052   nsresult rv = nsContentUtils::CalculateBufferSizeForImage(
8053       map.mStride, aSurface->GetSize(), aSurface->GetFormat(), &maxBufLen,
8054       &bufLen);
8055   if (NS_FAILED(rv)) {
8056     aSurface->Unmap();
8057     return GetSurfaceDataContext::NullValue();
8058   }
8059 
8060   // nsDependentCString wants null-terminated string.
8061   typename GetSurfaceDataContext::ReturnType surfaceData =
8062       aContext.Allocate(maxBufLen + 1);
8063   if (GetSurfaceDataContext::GetBuffer(surfaceData)) {
8064     memcpy(GetSurfaceDataContext::GetBuffer(surfaceData),
8065            reinterpret_cast<char*>(map.mData), bufLen);
8066     memset(GetSurfaceDataContext::GetBuffer(surfaceData) + bufLen, 0,
8067            maxBufLen - bufLen + 1);
8068   }
8069 
8070   *aLength = maxBufLen;
8071   *aStride = map.mStride;
8072 
8073   aSurface->Unmap();
8074   return surfaceData;
8075 }
8076 }  // Anonymous namespace.
8077 
8078 mozilla::UniquePtr<char[]> nsContentUtils::GetSurfaceData(
8079     NotNull<mozilla::gfx::DataSourceSurface*> aSurface, size_t* aLength,
8080     int32_t* aStride) {
8081   return GetSurfaceDataImpl(aSurface, aLength, aStride);
8082 }
8083 
8084 Maybe<Shmem> nsContentUtils::GetSurfaceData(
8085     mozilla::gfx::DataSourceSurface* aSurface, size_t* aLength,
8086     int32_t* aStride, IShmemAllocator* aAllocator) {
8087   return GetSurfaceDataImpl(aSurface, aLength, aStride,
8088                             GetSurfaceDataShmem(aAllocator));
8089 }
8090 
8091 mozilla::Modifiers nsContentUtils::GetWidgetModifiers(int32_t aModifiers) {
8092   Modifiers result = 0;
8093   if (aModifiers & nsIDOMWindowUtils::MODIFIER_SHIFT) {
8094     result |= mozilla::MODIFIER_SHIFT;
8095   }
8096   if (aModifiers & nsIDOMWindowUtils::MODIFIER_CONTROL) {
8097     result |= mozilla::MODIFIER_CONTROL;
8098   }
8099   if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALT) {
8100     result |= mozilla::MODIFIER_ALT;
8101   }
8102   if (aModifiers & nsIDOMWindowUtils::MODIFIER_META) {
8103     result |= mozilla::MODIFIER_META;
8104   }
8105   if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALTGRAPH) {
8106     result |= mozilla::MODIFIER_ALTGRAPH;
8107   }
8108   if (aModifiers & nsIDOMWindowUtils::MODIFIER_CAPSLOCK) {
8109     result |= mozilla::MODIFIER_CAPSLOCK;
8110   }
8111   if (aModifiers & nsIDOMWindowUtils::MODIFIER_FN) {
8112     result |= mozilla::MODIFIER_FN;
8113   }
8114   if (aModifiers & nsIDOMWindowUtils::MODIFIER_FNLOCK) {
8115     result |= mozilla::MODIFIER_FNLOCK;
8116   }
8117   if (aModifiers & nsIDOMWindowUtils::MODIFIER_NUMLOCK) {
8118     result |= mozilla::MODIFIER_NUMLOCK;
8119   }
8120   if (aModifiers & nsIDOMWindowUtils::MODIFIER_SCROLLLOCK) {
8121     result |= mozilla::MODIFIER_SCROLLLOCK;
8122   }
8123   if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOL) {
8124     result |= mozilla::MODIFIER_SYMBOL;
8125   }
8126   if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK) {
8127     result |= mozilla::MODIFIER_SYMBOLLOCK;
8128   }
8129   if (aModifiers & nsIDOMWindowUtils::MODIFIER_OS) {
8130     result |= mozilla::MODIFIER_OS;
8131   }
8132   return result;
8133 }
8134 
8135 nsIWidget* nsContentUtils::GetWidget(PresShell* aPresShell, nsPoint* aOffset) {
8136   if (!aPresShell) {
8137     return nullptr;
8138   }
8139   nsIFrame* frame = aPresShell->GetRootFrame();
8140   if (!frame) {
8141     return nullptr;
8142   }
8143   return frame->GetView()->GetNearestWidget(aOffset);
8144 }
8145 
8146 int16_t nsContentUtils::GetButtonsFlagForButton(int32_t aButton) {
8147   switch (aButton) {
8148     case -1:
8149       return MouseButtonsFlag::eNoButtons;
8150     case MouseButton::ePrimary:
8151       return MouseButtonsFlag::ePrimaryFlag;
8152     case MouseButton::eMiddle:
8153       return MouseButtonsFlag::eMiddleFlag;
8154     case MouseButton::eSecondary:
8155       return MouseButtonsFlag::eSecondaryFlag;
8156     case 3:
8157       return MouseButtonsFlag::e4thFlag;
8158     case 4:
8159       return MouseButtonsFlag::e5thFlag;
8160     case MouseButton::eEraser:
8161       return MouseButtonsFlag::eEraserFlag;
8162     default:
8163       NS_ERROR("Button not known.");
8164       return 0;
8165   }
8166 }
8167 
8168 LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(
8169     const CSSPoint& aPoint, const nsPoint& aOffset,
8170     nsPresContext* aPresContext) {
8171   nsPoint layoutRelative = CSSPoint::ToAppUnits(aPoint) + aOffset;
8172   nsPoint visualRelative =
8173       ViewportUtils::LayoutToVisual(layoutRelative, aPresContext->PresShell());
8174   return LayoutDeviceIntPoint::FromAppUnitsRounded(
8175       visualRelative, aPresContext->AppUnitsPerDevPixel());
8176 }
8177 
8178 nsView* nsContentUtils::GetViewToDispatchEvent(nsPresContext* aPresContext,
8179                                                PresShell** aPresShell) {
8180   if (!aPresContext || !aPresShell) {
8181     return nullptr;
8182   }
8183   RefPtr<PresShell> presShell = aPresContext->PresShell();
8184   if (NS_WARN_IF(!presShell)) {
8185     *aPresShell = nullptr;
8186     return nullptr;
8187   }
8188   nsViewManager* viewManager = presShell->GetViewManager();
8189   if (!viewManager) {
8190     presShell.forget(aPresShell);  // XXX Is this intentional?
8191     return nullptr;
8192   }
8193   presShell.forget(aPresShell);
8194   return viewManager->GetRootView();
8195 }
8196 
8197 nsresult nsContentUtils::SendMouseEvent(
8198     mozilla::PresShell* aPresShell, const nsAString& aType, float aX, float aY,
8199     int32_t aButton, int32_t aButtons, int32_t aClickCount, int32_t aModifiers,
8200     bool aIgnoreRootScrollFrame, float aPressure,
8201     unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
8202     PreventDefaultResult* aPreventDefault, bool aIsDOMEventSynthesized,
8203     bool aIsWidgetEventSynthesized) {
8204   nsPoint offset;
8205   nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
8206   if (!widget) return NS_ERROR_FAILURE;
8207 
8208   EventMessage msg;
8209   Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
8210   bool contextMenuKey = false;
8211   if (aType.EqualsLiteral("mousedown")) {
8212     msg = eMouseDown;
8213   } else if (aType.EqualsLiteral("mouseup")) {
8214     msg = eMouseUp;
8215   } else if (aType.EqualsLiteral("mousemove")) {
8216     msg = eMouseMove;
8217   } else if (aType.EqualsLiteral("mouseover")) {
8218     msg = eMouseEnterIntoWidget;
8219   } else if (aType.EqualsLiteral("mouseout")) {
8220     msg = eMouseExitFromWidget;
8221     exitFrom = Some(WidgetMouseEvent::ePlatformChild);
8222   } else if (aType.EqualsLiteral("mousecancel")) {
8223     msg = eMouseExitFromWidget;
8224     exitFrom = Some(XRE_IsParentProcess() ? WidgetMouseEvent::ePlatformTopLevel
8225                                           : WidgetMouseEvent::ePuppet);
8226   } else if (aType.EqualsLiteral("mouselongtap")) {
8227     msg = eMouseLongTap;
8228   } else if (aType.EqualsLiteral("contextmenu")) {
8229     msg = eContextMenu;
8230     contextMenuKey = (aButton == 0);
8231   } else if (aType.EqualsLiteral("MozMouseHittest")) {
8232     msg = eMouseHitTest;
8233   } else {
8234     return NS_ERROR_FAILURE;
8235   }
8236 
8237   if (aInputSourceArg == MouseEvent_Binding::MOZ_SOURCE_UNKNOWN) {
8238     aInputSourceArg = MouseEvent_Binding::MOZ_SOURCE_MOUSE;
8239   }
8240 
8241   WidgetMouseEvent event(true, msg, widget,
8242                          aIsWidgetEventSynthesized
8243                              ? WidgetMouseEvent::eSynthesized
8244                              : WidgetMouseEvent::eReal,
8245                          contextMenuKey ? WidgetMouseEvent::eContextMenuKey
8246                                         : WidgetMouseEvent::eNormal);
8247   event.pointerId = aIdentifier;
8248   event.mModifiers = GetWidgetModifiers(aModifiers);
8249   event.mButton = aButton;
8250   event.mButtons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED
8251                        ? aButtons
8252                    : msg == eMouseUp ? 0
8253                                      : GetButtonsFlagForButton(aButton);
8254   event.mPressure = aPressure;
8255   event.mInputSource = aInputSourceArg;
8256   event.mClickCount = aClickCount;
8257   event.mTime = PR_IntervalNow();
8258   event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
8259   event.mExitFrom = exitFrom;
8260 
8261   nsPresContext* presContext = aPresShell->GetPresContext();
8262   if (!presContext) return NS_ERROR_FAILURE;
8263 
8264   event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
8265   event.mIgnoreRootScrollFrame = aIgnoreRootScrollFrame;
8266 
8267   nsEventStatus status = nsEventStatus_eIgnore;
8268   if (aToWindow) {
8269     RefPtr<PresShell> presShell;
8270     nsView* view =
8271         GetViewToDispatchEvent(presContext, getter_AddRefs(presShell));
8272     if (!presShell || !view) {
8273       return NS_ERROR_FAILURE;
8274     }
8275     return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
8276   }
8277   if (StaticPrefs::test_events_async_enabled()) {
8278     status = widget->DispatchInputEvent(&event).mContentStatus;
8279   } else {
8280     nsresult rv = widget->DispatchEvent(&event, status);
8281     NS_ENSURE_SUCCESS(rv, rv);
8282   }
8283   if (aPreventDefault) {
8284     if (status == nsEventStatus_eConsumeNoDefault) {
8285       if (event.mFlags.mDefaultPreventedByContent) {
8286         *aPreventDefault = PreventDefaultResult::ByContent;
8287       } else {
8288         *aPreventDefault = PreventDefaultResult::ByChrome;
8289       }
8290     } else {
8291       *aPreventDefault = PreventDefaultResult::No;
8292     }
8293   }
8294 
8295   return NS_OK;
8296 }
8297 
8298 /* static */
8299 void nsContentUtils::FirePageHideEventForFrameLoaderSwap(
8300     nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8301     bool aOnlySystemGroup) {
8302   MOZ_DIAGNOSTIC_ASSERT(aItem);
8303   MOZ_DIAGNOSTIC_ASSERT(aChromeEventHandler);
8304 
8305   RefPtr<Document> doc = aItem->GetDocument();
8306   NS_ASSERTION(doc, "What happened here?");
8307   doc->OnPageHide(true, aChromeEventHandler, aOnlySystemGroup);
8308 
8309   int32_t childCount = 0;
8310   aItem->GetInProcessChildCount(&childCount);
8311   AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8312   kids.AppendElements(childCount);
8313   for (int32_t i = 0; i < childCount; ++i) {
8314     aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8315   }
8316 
8317   for (uint32_t i = 0; i < kids.Length(); ++i) {
8318     if (kids[i]) {
8319       FirePageHideEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8320                                           aOnlySystemGroup);
8321     }
8322   }
8323 }
8324 
8325 // The pageshow event is fired for a given document only if IsShowing() returns
8326 // the same thing as aFireIfShowing.  This gives us a way to fire pageshow only
8327 // on documents that are still loading or only on documents that are already
8328 // loaded.
8329 /* static */
8330 void nsContentUtils::FirePageShowEventForFrameLoaderSwap(
8331     nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8332     bool aFireIfShowing, bool aOnlySystemGroup) {
8333   int32_t childCount = 0;
8334   aItem->GetInProcessChildCount(&childCount);
8335   AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8336   kids.AppendElements(childCount);
8337   for (int32_t i = 0; i < childCount; ++i) {
8338     aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8339   }
8340 
8341   for (uint32_t i = 0; i < kids.Length(); ++i) {
8342     if (kids[i]) {
8343       FirePageShowEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8344                                           aFireIfShowing, aOnlySystemGroup);
8345     }
8346   }
8347 
8348   RefPtr<Document> doc = aItem->GetDocument();
8349   NS_ASSERTION(doc, "What happened here?");
8350   if (doc->IsShowing() == aFireIfShowing) {
8351     doc->OnPageShow(true, aChromeEventHandler, aOnlySystemGroup);
8352   }
8353 }
8354 
8355 /* static */
8356 already_AddRefed<nsPIWindowRoot> nsContentUtils::GetWindowRoot(Document* aDoc) {
8357   if (aDoc) {
8358     if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
8359       return win->GetTopWindowRoot();
8360     }
8361   }
8362   return nullptr;
8363 }
8364 
8365 /* static */
8366 bool nsContentUtils::IsPreloadType(nsContentPolicyType aType) {
8367   return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
8368           aType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD ||
8369           aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
8370           aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD ||
8371           aType == nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD ||
8372           aType == nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD);
8373 }
8374 
8375 /* static */
8376 bool nsContentUtils::IsUpgradableDisplayType(ExtContentPolicyType aType) {
8377   MOZ_ASSERT(NS_IsMainThread());
8378   return (aType == ExtContentPolicy::TYPE_IMAGE ||
8379           aType == ExtContentPolicy::TYPE_MEDIA);
8380 }
8381 
8382 // static
8383 ReferrerPolicy nsContentUtils::GetReferrerPolicyFromChannel(
8384     nsIChannel* aChannel) {
8385   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
8386   if (!httpChannel) {
8387     return ReferrerPolicy::_empty;
8388   }
8389 
8390   nsresult rv;
8391   nsAutoCString headerValue;
8392   rv = httpChannel->GetResponseHeader("referrer-policy"_ns, headerValue);
8393   if (NS_FAILED(rv) || headerValue.IsEmpty()) {
8394     return ReferrerPolicy::_empty;
8395   }
8396 
8397   return ReferrerInfo::ReferrerPolicyFromHeaderString(
8398       NS_ConvertUTF8toUTF16(headerValue));
8399 }
8400 
8401 // static
8402 bool nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel) {
8403   nsLoadFlags loadFlags = 0;
8404   aChannel->GetLoadFlags(&loadFlags);
8405   if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
8406     return true;
8407   }
8408 
8409   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
8410   nsContentPolicyType type = loadInfo->InternalContentPolicyType();
8411   return IsNonSubresourceInternalPolicyType(type);
8412 }
8413 
8414 // static
8415 bool nsContentUtils::IsNonSubresourceInternalPolicyType(
8416     nsContentPolicyType aType) {
8417   return aType == nsIContentPolicy::TYPE_DOCUMENT ||
8418          aType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
8419          aType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
8420          aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
8421          aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
8422 }
8423 
8424 // static public
8425 bool nsContentUtils::IsThirdPartyWindowOrChannel(nsPIDOMWindowInner* aWindow,
8426                                                  nsIChannel* aChannel,
8427                                                  nsIURI* aURI) {
8428   MOZ_ASSERT(!aWindow || !aChannel,
8429              "A window and channel should not both be provided.");
8430 
8431   ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
8432   if (!thirdPartyUtil) {
8433     return false;
8434   }
8435 
8436   // In the absence of a window or channel, we assume that we are first-party.
8437   bool thirdParty = false;
8438 
8439   if (aWindow) {
8440     nsresult rv = thirdPartyUtil->IsThirdPartyWindow(aWindow->GetOuterWindow(),
8441                                                      aURI, &thirdParty);
8442     if (NS_FAILED(rv)) {
8443       // Ideally we would do something similar to the channel code path here,
8444       // but existing code depends on this behaviour.
8445       return false;
8446     }
8447   }
8448 
8449   if (aChannel) {
8450     // Note, we must call IsThirdPartyChannel() here and not just try to
8451     // use nsILoadInfo.isThirdPartyContext.  That nsILoadInfo property only
8452     // indicates if the parent loading window is third party or not.  We
8453     // want to check the channel URI against the loading principal as well.
8454     nsresult rv =
8455         thirdPartyUtil->IsThirdPartyChannel(aChannel, nullptr, &thirdParty);
8456     if (NS_FAILED(rv)) {
8457       // Assume third-party in case of failure
8458       thirdParty = true;
8459     }
8460 
8461     // We check isThirdPartyWindow to expand the list of domains that are
8462     // considered first party (e.g., if facebook.com includes an iframe from
8463     // fatratgames.com, all subsources included in that iframe are considered
8464     // third-party with isThirdPartyChannel, even if they are not third-party
8465     // w.r.t.  facebook.com), and isThirdPartyChannel to prevent top-level
8466     // navigations from being detected as third-party.
8467     bool isThirdPartyWindow = true;
8468     nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
8469     if (NS_SUCCEEDED(rv) && chan) {
8470       nsCOMPtr<nsIURI> topWinURI;
8471       rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
8472       if (NS_SUCCEEDED(rv) && topWinURI) {
8473         rv = thirdPartyUtil->IsThirdPartyURI(aURI, topWinURI,
8474                                              &isThirdPartyWindow);
8475         if (NS_SUCCEEDED(rv)) {
8476           thirdParty = thirdParty && isThirdPartyWindow;
8477         }
8478       }
8479     }
8480   }
8481 
8482   return thirdParty;
8483 }
8484 
8485 // static public
8486 bool nsContentUtils::IsThirdPartyTrackingResourceWindow(
8487     nsPIDOMWindowInner* aWindow) {
8488   MOZ_ASSERT(aWindow);
8489 
8490   Document* document = aWindow->GetExtantDoc();
8491   if (!document) {
8492     return false;
8493   }
8494 
8495   nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8496       do_QueryInterface(document->GetChannel());
8497   if (!classifiedChannel) {
8498     return false;
8499   }
8500 
8501   return classifiedChannel->IsThirdPartyTrackingResource();
8502 }
8503 
8504 // static public
8505 bool nsContentUtils::IsFirstPartyTrackingResourceWindow(
8506     nsPIDOMWindowInner* aWindow) {
8507   MOZ_ASSERT(aWindow);
8508 
8509   Document* document = aWindow->GetExtantDoc();
8510   if (!document) {
8511     return false;
8512   }
8513 
8514   nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8515       do_QueryInterface(document->GetChannel());
8516   if (!classifiedChannel) {
8517     return false;
8518   }
8519 
8520   uint32_t classificationFlags =
8521       classifiedChannel->GetFirstPartyClassificationFlags();
8522 
8523   return mozilla::net::UrlClassifierCommon::IsTrackingClassificationFlag(
8524       classificationFlags);
8525 }
8526 
8527 namespace {
8528 
8529 // We put StringBuilder in the anonymous namespace to prevent anything outside
8530 // this file from accidentally being linked against it.
8531 class BulkAppender {
8532   typedef typename nsAString::size_type size_type;
8533 
8534  public:
8535   explicit BulkAppender(BulkWriteHandle<char16_t>&& aHandle)
8536       : mHandle(std::move(aHandle)), mPosition(0) {}
8537   ~BulkAppender() = default;
8538 
8539   template <int N>
8540   void AppendLiteral(const char16_t (&aStr)[N]) {
8541     size_t len = N - 1;
8542     MOZ_ASSERT(mPosition + len <= mHandle.Length());
8543     memcpy(mHandle.Elements() + mPosition, aStr, len * sizeof(char16_t));
8544     mPosition += len;
8545   }
8546 
8547   void Append(Span<const char16_t> aStr) {
8548     size_t len = aStr.Length();
8549     MOZ_ASSERT(mPosition + len <= mHandle.Length());
8550     // Both mHandle.Elements() and aStr.Elements() are guaranteed
8551     // to be non-null (by the string implementation and by Span,
8552     // respectively), so not checking the pointers for null before
8553     // memcpy does not lead to UB even if len was zero.
8554     memcpy(mHandle.Elements() + mPosition, aStr.Elements(),
8555            len * sizeof(char16_t));
8556     mPosition += len;
8557   }
8558 
8559   void Append(Span<const char> aStr) {
8560     size_t len = aStr.Length();
8561     MOZ_ASSERT(mPosition + len <= mHandle.Length());
8562     ConvertLatin1toUtf16(aStr, mHandle.AsSpan().From(mPosition));
8563     mPosition += len;
8564   }
8565 
8566   void Finish() { mHandle.Finish(mPosition, false); }
8567 
8568  private:
8569   mozilla::BulkWriteHandle<char16_t> mHandle;
8570   size_type mPosition;
8571 };
8572 
8573 class StringBuilder {
8574  private:
8575   // Try to keep the size of StringBuilder close to a jemalloc bucket size.
8576   static const uint32_t STRING_BUFFER_UNITS = 1020;
8577   class Unit {
8578    public:
8579     Unit() : mAtom(nullptr), mType(eUnknown), mLength(0) {
8580       MOZ_COUNT_CTOR(StringBuilder::Unit);
8581     }
8582     ~Unit() {
8583       if (mType == eString || mType == eStringWithEncode) {
8584         delete mString;
8585       }
8586       MOZ_COUNT_DTOR(StringBuilder::Unit);
8587     }
8588 
8589     enum Type {
8590       eUnknown,
8591       eAtom,
8592       eString,
8593       eStringWithEncode,
8594       eLiteral,
8595       eTextFragment,
8596       eTextFragmentWithEncode,
8597     };
8598 
8599     union {
8600       nsAtom* mAtom;
8601       const char16_t* mLiteral;
8602       nsAutoString* mString;
8603       const nsTextFragment* mTextFragment;
8604     };
8605     Type mType;
8606     uint32_t mLength;
8607   };
8608 
8609  public:
8610   StringBuilder() : mLast(this), mLength(0) { MOZ_COUNT_CTOR(StringBuilder); }
8611 
8612   MOZ_COUNTED_DTOR(StringBuilder)
8613 
8614   void Append(nsAtom* aAtom) {
8615     Unit* u = AddUnit();
8616     u->mAtom = aAtom;
8617     u->mType = Unit::eAtom;
8618     uint32_t len = aAtom->GetLength();
8619     u->mLength = len;
8620     mLength += len;
8621   }
8622 
8623   template <int N>
8624   void Append(const char16_t (&aLiteral)[N]) {
8625     Unit* u = AddUnit();
8626     u->mLiteral = aLiteral;
8627     u->mType = Unit::eLiteral;
8628     uint32_t len = N - 1;
8629     u->mLength = len;
8630     mLength += len;
8631   }
8632 
8633   void Append(const nsAString& aString) {
8634     Unit* u = AddUnit();
8635     u->mString = new nsAutoString(aString);
8636     u->mType = Unit::eString;
8637     uint32_t len = aString.Length();
8638     u->mLength = len;
8639     mLength += len;
8640   }
8641 
8642   void Append(nsAutoString* aString) {
8643     Unit* u = AddUnit();
8644     u->mString = aString;
8645     u->mType = Unit::eString;
8646     uint32_t len = aString->Length();
8647     u->mLength = len;
8648     mLength += len;
8649   }
8650 
8651   void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen) {
8652     Unit* u = AddUnit();
8653     u->mString = aString;
8654     u->mType = Unit::eStringWithEncode;
8655     u->mLength = aLen;
8656     mLength += aLen;
8657   }
8658 
8659   void Append(const nsTextFragment* aTextFragment) {
8660     Unit* u = AddUnit();
8661     u->mTextFragment = aTextFragment;
8662     u->mType = Unit::eTextFragment;
8663     uint32_t len = aTextFragment->GetLength();
8664     u->mLength = len;
8665     mLength += len;
8666   }
8667 
8668   void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen) {
8669     Unit* u = AddUnit();
8670     u->mTextFragment = aTextFragment;
8671     u->mType = Unit::eTextFragmentWithEncode;
8672     u->mLength = aLen;
8673     mLength += aLen;
8674   }
8675 
8676   bool ToString(nsAString& aOut) {
8677     if (!mLength.isValid()) {
8678       return false;
8679     }
8680     auto appenderOrErr = aOut.BulkWrite(mLength.value(), 0, true);
8681     if (appenderOrErr.isErr()) {
8682       return false;
8683     }
8684 
8685     BulkAppender appender{appenderOrErr.unwrap()};
8686 
8687     for (StringBuilder* current = this; current;
8688          current = current->mNext.get()) {
8689       uint32_t len = current->mUnits.Length();
8690       for (uint32_t i = 0; i < len; ++i) {
8691         Unit& u = current->mUnits[i];
8692         switch (u.mType) {
8693           case Unit::eAtom:
8694             appender.Append(*(u.mAtom));
8695             break;
8696           case Unit::eString:
8697             appender.Append(*(u.mString));
8698             break;
8699           case Unit::eStringWithEncode:
8700             EncodeAttrString(*(u.mString), appender);
8701             break;
8702           case Unit::eLiteral:
8703             appender.Append(Span(u.mLiteral, u.mLength));
8704             break;
8705           case Unit::eTextFragment:
8706             if (u.mTextFragment->Is2b()) {
8707               appender.Append(
8708                   Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()));
8709             } else {
8710               appender.Append(
8711                   Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()));
8712             }
8713             break;
8714           case Unit::eTextFragmentWithEncode:
8715             if (u.mTextFragment->Is2b()) {
8716               EncodeTextFragment(
8717                   Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()),
8718                   appender);
8719             } else {
8720               EncodeTextFragment(
8721                   Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()),
8722                   appender);
8723             }
8724             break;
8725           default:
8726             MOZ_CRASH("Unknown unit type?");
8727         }
8728       }
8729     }
8730     appender.Finish();
8731     return true;
8732   }
8733 
8734  private:
8735   Unit* AddUnit() {
8736     if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
8737       new StringBuilder(this);
8738     }
8739     return mLast->mUnits.AppendElement();
8740   }
8741 
8742   explicit StringBuilder(StringBuilder* aFirst) : mLast(nullptr), mLength(0) {
8743     MOZ_COUNT_CTOR(StringBuilder);
8744     aFirst->mLast->mNext = WrapUnique(this);
8745     aFirst->mLast = this;
8746   }
8747 
8748   void EncodeAttrString(Span<const char16_t> aStr, BulkAppender& aAppender) {
8749     size_t flushedUntil = 0;
8750     size_t currentPosition = 0;
8751     for (char16_t c : aStr) {
8752       switch (c) {
8753         case '"':
8754           aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8755           aAppender.AppendLiteral(u"&quot;");
8756           flushedUntil = currentPosition + 1;
8757           break;
8758         case '&':
8759           aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8760           aAppender.AppendLiteral(u"&amp;");
8761           flushedUntil = currentPosition + 1;
8762           break;
8763         case 0x00A0:
8764           aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8765           aAppender.AppendLiteral(u"&nbsp;");
8766           flushedUntil = currentPosition + 1;
8767           break;
8768         default:
8769           break;
8770       }
8771       currentPosition++;
8772     }
8773     if (currentPosition > flushedUntil) {
8774       aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8775     }
8776   }
8777 
8778   template <class T>
8779   void EncodeTextFragment(Span<const T> aStr, BulkAppender& aAppender) {
8780     size_t flushedUntil = 0;
8781     size_t currentPosition = 0;
8782     for (T c : aStr) {
8783       switch (c) {
8784         case '<':
8785           aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8786           aAppender.AppendLiteral(u"&lt;");
8787           flushedUntil = currentPosition + 1;
8788           break;
8789         case '>':
8790           aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8791           aAppender.AppendLiteral(u"&gt;");
8792           flushedUntil = currentPosition + 1;
8793           break;
8794         case '&':
8795           aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8796           aAppender.AppendLiteral(u"&amp;");
8797           flushedUntil = currentPosition + 1;
8798           break;
8799         case T(0xA0):
8800           aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8801           aAppender.AppendLiteral(u"&nbsp;");
8802           flushedUntil = currentPosition + 1;
8803           break;
8804         default:
8805           break;
8806       }
8807       currentPosition++;
8808     }
8809     if (currentPosition > flushedUntil) {
8810       aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8811     }
8812   }
8813 
8814   AutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
8815   mozilla::UniquePtr<StringBuilder> mNext;
8816   StringBuilder* mLast;
8817   // mLength is used only in the first StringBuilder object in the linked list.
8818   CheckedInt<uint32_t> mLength;
8819 };
8820 
8821 }  // namespace
8822 
8823 static void AppendEncodedCharacters(const nsTextFragment* aText,
8824                                     StringBuilder& aBuilder) {
8825   uint32_t extraSpaceNeeded = 0;
8826   uint32_t len = aText->GetLength();
8827   if (aText->Is2b()) {
8828     const char16_t* data = aText->Get2b();
8829     for (uint32_t i = 0; i < len; ++i) {
8830       const char16_t c = data[i];
8831       switch (c) {
8832         case '<':
8833           extraSpaceNeeded += ArrayLength("&lt;") - 2;
8834           break;
8835         case '>':
8836           extraSpaceNeeded += ArrayLength("&gt;") - 2;
8837           break;
8838         case '&':
8839           extraSpaceNeeded += ArrayLength("&amp;") - 2;
8840           break;
8841         case 0x00A0:
8842           extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
8843           break;
8844         default:
8845           break;
8846       }
8847     }
8848   } else {
8849     const char* data = aText->Get1b();
8850     for (uint32_t i = 0; i < len; ++i) {
8851       const unsigned char c = data[i];
8852       switch (c) {
8853         case '<':
8854           extraSpaceNeeded += ArrayLength("&lt;") - 2;
8855           break;
8856         case '>':
8857           extraSpaceNeeded += ArrayLength("&gt;") - 2;
8858           break;
8859         case '&':
8860           extraSpaceNeeded += ArrayLength("&amp;") - 2;
8861           break;
8862         case 0x00A0:
8863           extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
8864           break;
8865         default:
8866           break;
8867       }
8868     }
8869   }
8870 
8871   if (extraSpaceNeeded) {
8872     aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
8873   } else {
8874     aBuilder.Append(aText);
8875   }
8876 }
8877 
8878 static void AppendEncodedAttributeValue(nsAutoString* aValue,
8879                                         StringBuilder& aBuilder) {
8880   const char16_t* c = aValue->BeginReading();
8881   const char16_t* end = aValue->EndReading();
8882 
8883   uint32_t extraSpaceNeeded = 0;
8884   while (c < end) {
8885     switch (*c) {
8886       case '"':
8887         extraSpaceNeeded += ArrayLength("&quot;") - 2;
8888         break;
8889       case '&':
8890         extraSpaceNeeded += ArrayLength("&amp;") - 2;
8891         break;
8892       case 0x00A0:
8893         extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
8894         break;
8895       default:
8896         break;
8897     }
8898     ++c;
8899   }
8900 
8901   if (extraSpaceNeeded) {
8902     aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
8903   } else {
8904     aBuilder.Append(aValue);
8905   }
8906 }
8907 
8908 static void StartElement(Element* aContent, StringBuilder& aBuilder) {
8909   nsAtom* localName = aContent->NodeInfo()->NameAtom();
8910   int32_t tagNS = aContent->GetNameSpaceID();
8911 
8912   aBuilder.Append(u"<");
8913   if (aContent->IsHTMLElement() || aContent->IsSVGElement() ||
8914       aContent->IsMathMLElement()) {
8915     aBuilder.Append(localName);
8916   } else {
8917     aBuilder.Append(aContent->NodeName());
8918   }
8919 
8920   CustomElementData* ceData = aContent->GetCustomElementData();
8921   if (ceData) {
8922     nsAtom* isAttr = ceData->GetIs(aContent);
8923     if (isAttr && !aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
8924       aBuilder.Append(uR"( is=")");
8925       aBuilder.Append(nsDependentAtomString(isAttr));
8926       aBuilder.Append(uR"(")");
8927     }
8928   }
8929 
8930   int32_t count = aContent->GetAttrCount();
8931   for (int32_t i = 0; i < count; i++) {
8932     const nsAttrName* name = aContent->GetAttrNameAt(i);
8933     int32_t attNs = name->NamespaceID();
8934     nsAtom* attName = name->LocalName();
8935 
8936     // Filter out any attribute starting with [-|_]moz
8937     nsDependentAtomString attrNameStr(attName);
8938     if (StringBeginsWith(attrNameStr, u"_moz"_ns) ||
8939         StringBeginsWith(attrNameStr, u"-moz"_ns)) {
8940       continue;
8941     }
8942 
8943     auto* attValue = new nsAutoString();
8944     aContent->GetAttr(attNs, attName, *attValue);
8945 
8946     // Filter out special case of <br type="_moz*"> used by the editor.
8947     // Bug 16988.  Yuck.
8948     if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
8949         attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
8950         StringBeginsWith(*attValue, u"_moz"_ns)) {
8951       delete attValue;
8952       continue;
8953     }
8954 
8955     aBuilder.Append(u" ");
8956 
8957     if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
8958         (attNs == kNameSpaceID_XMLNS && attName == nsGkAtoms::xmlns)) {
8959       // Nothing else required
8960     } else if (attNs == kNameSpaceID_XML) {
8961       aBuilder.Append(u"xml:");
8962     } else if (attNs == kNameSpaceID_XMLNS) {
8963       aBuilder.Append(u"xmlns:");
8964     } else if (attNs == kNameSpaceID_XLink) {
8965       aBuilder.Append(u"xlink:");
8966     } else {
8967       nsAtom* prefix = name->GetPrefix();
8968       if (prefix) {
8969         aBuilder.Append(prefix);
8970         aBuilder.Append(u":");
8971       }
8972     }
8973 
8974     aBuilder.Append(attName);
8975     aBuilder.Append(uR"(=")");
8976     AppendEncodedAttributeValue(attValue, aBuilder);
8977     aBuilder.Append(uR"(")");
8978   }
8979 
8980   aBuilder.Append(u">");
8981 
8982   /*
8983   // Per HTML spec we should append one \n if the first child of
8984   // pre/textarea/listing is a textnode and starts with a \n.
8985   // But because browsers haven't traditionally had that behavior,
8986   // we're not changing our behavior either - yet.
8987   if (aContent->IsHTMLElement()) {
8988     if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
8989         localName == nsGkAtoms::listing) {
8990       nsIContent* fc = aContent->GetFirstChild();
8991       if (fc &&
8992           (fc->NodeType() == nsINode::TEXT_NODE ||
8993            fc->NodeType() == nsINode::CDATA_SECTION_NODE)) {
8994         const nsTextFragment* text = fc->GetText();
8995         if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
8996           aBuilder.Append("\n");
8997         }
8998       }
8999     }
9000   }*/
9001 }
9002 
9003 static inline bool ShouldEscape(nsIContent* aParent) {
9004   if (!aParent || !aParent->IsHTMLElement()) {
9005     return true;
9006   }
9007 
9008   static const nsAtom* nonEscapingElements[] = {
9009       nsGkAtoms::style,     nsGkAtoms::script,  nsGkAtoms::xmp,
9010       nsGkAtoms::iframe,    nsGkAtoms::noembed, nsGkAtoms::noframes,
9011       nsGkAtoms::plaintext, nsGkAtoms::noscript};
9012   static mozilla::BitBloomFilter<12, nsAtom> sFilter;
9013   static bool sInitialized = false;
9014   if (!sInitialized) {
9015     sInitialized = true;
9016     for (auto& nonEscapingElement : nonEscapingElements) {
9017       sFilter.add(nonEscapingElement);
9018     }
9019   }
9020 
9021   nsAtom* tag = aParent->NodeInfo()->NameAtom();
9022   if (sFilter.mightContain(tag)) {
9023     for (auto& nonEscapingElement : nonEscapingElements) {
9024       if (tag == nonEscapingElement) {
9025         if (MOZ_UNLIKELY(tag == nsGkAtoms::noscript) &&
9026             MOZ_UNLIKELY(!aParent->OwnerDoc()->IsScriptEnabled())) {
9027           return true;
9028         }
9029         return false;
9030       }
9031     }
9032   }
9033   return true;
9034 }
9035 
9036 static inline bool IsVoidTag(Element* aElement) {
9037   if (!aElement->IsHTMLElement()) {
9038     return false;
9039   }
9040   return FragmentOrElement::IsHTMLVoid(aElement->NodeInfo()->NameAtom());
9041 }
9042 
9043 bool nsContentUtils::SerializeNodeToMarkup(nsINode* aRoot,
9044                                            bool aDescendentsOnly,
9045                                            nsAString& aOut) {
9046   // If you pass in a DOCUMENT_NODE, you must pass aDescendentsOnly as true
9047   MOZ_ASSERT(aDescendentsOnly || aRoot->NodeType() != nsINode::DOCUMENT_NODE);
9048 
9049   nsINode* current =
9050       aDescendentsOnly ? aRoot->GetFirstChildOfTemplateOrNode() : aRoot;
9051 
9052   if (!current) {
9053     return true;
9054   }
9055 
9056   StringBuilder builder;
9057   nsIContent* next;
9058   while (true) {
9059     bool isVoid = false;
9060     switch (current->NodeType()) {
9061       case nsINode::ELEMENT_NODE: {
9062         Element* elem = current->AsElement();
9063         StartElement(elem, builder);
9064         isVoid = IsVoidTag(elem);
9065         if (!isVoid && (next = current->GetFirstChildOfTemplateOrNode())) {
9066           current = next;
9067           continue;
9068         }
9069         break;
9070       }
9071 
9072       case nsINode::TEXT_NODE:
9073       case nsINode::CDATA_SECTION_NODE: {
9074         const nsTextFragment* text = &current->AsText()->TextFragment();
9075         nsIContent* parent = current->GetParent();
9076         if (ShouldEscape(parent)) {
9077           AppendEncodedCharacters(text, builder);
9078         } else {
9079           builder.Append(text);
9080         }
9081         break;
9082       }
9083 
9084       case nsINode::COMMENT_NODE: {
9085         builder.Append(u"<!--");
9086         builder.Append(static_cast<nsIContent*>(current)->GetText());
9087         builder.Append(u"-->");
9088         break;
9089       }
9090 
9091       case nsINode::DOCUMENT_TYPE_NODE: {
9092         builder.Append(u"<!DOCTYPE ");
9093         builder.Append(current->NodeName());
9094         builder.Append(u">");
9095         break;
9096       }
9097 
9098       case nsINode::PROCESSING_INSTRUCTION_NODE: {
9099         builder.Append(u"<?");
9100         builder.Append(current->NodeName());
9101         builder.Append(u" ");
9102         builder.Append(static_cast<nsIContent*>(current)->GetText());
9103         builder.Append(u">");
9104         break;
9105       }
9106     }
9107 
9108     while (true) {
9109       if (!isVoid && current->NodeType() == nsINode::ELEMENT_NODE) {
9110         builder.Append(u"</");
9111         nsIContent* elem = static_cast<nsIContent*>(current);
9112         if (elem->IsHTMLElement() || elem->IsSVGElement() ||
9113             elem->IsMathMLElement()) {
9114           builder.Append(elem->NodeInfo()->NameAtom());
9115         } else {
9116           builder.Append(current->NodeName());
9117         }
9118         builder.Append(u">");
9119       }
9120       isVoid = false;
9121 
9122       if (current == aRoot) {
9123         return builder.ToString(aOut);
9124       }
9125 
9126       if ((next = current->GetNextSibling())) {
9127         current = next;
9128         break;
9129       }
9130 
9131       current = current->GetParentNode();
9132 
9133       // Handle template element. If the parent is a template's content,
9134       // then adjust the parent to be the template element.
9135       if (current != aRoot &&
9136           current->NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
9137         DocumentFragment* frag = static_cast<DocumentFragment*>(current);
9138         nsIContent* fragHost = frag->GetHost();
9139         if (fragHost && fragHost->IsTemplateElement()) {
9140           current = fragHost;
9141         }
9142       }
9143 
9144       if (aDescendentsOnly && current == aRoot) {
9145         return builder.ToString(aOut);
9146       }
9147     }
9148   }
9149 }
9150 
9151 bool nsContentUtils::IsSpecificAboutPage(JSObject* aGlobal, const char* aUri) {
9152   // aUri must start with about: or this isn't the right function to be using.
9153   MOZ_ASSERT(strncmp(aUri, "about:", 6) == 0);
9154 
9155   // Make sure the global is a window
9156   MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
9157   nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
9158   if (!win) {
9159     return false;
9160   }
9161 
9162   nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
9163   NS_ENSURE_TRUE(principal, false);
9164 
9165   // First check the scheme to avoid getting long specs in the common case.
9166   if (!principal->SchemeIs("about")) {
9167     return false;
9168   }
9169 
9170   nsAutoCString spec;
9171   principal->GetAsciiSpec(spec);
9172 
9173   return spec.EqualsASCII(aUri);
9174 }
9175 
9176 /* static */
9177 void nsContentUtils::SetScrollbarsVisibility(nsIDocShell* aDocShell,
9178                                              bool aVisible) {
9179   if (!aDocShell) {
9180     return;
9181   }
9182   auto pref = aVisible ? ScrollbarPreference::Auto : ScrollbarPreference::Never;
9183   nsDocShell::Cast(aDocShell)->SetScrollbarPreference(pref);
9184 }
9185 
9186 /* static */
9187 nsIDocShell* nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget) {
9188   nsCOMPtr<nsPIDOMWindowInner> innerWindow;
9189 
9190   if (nsCOMPtr<nsINode> node = do_QueryInterface(aTarget)) {
9191     bool ignore;
9192     innerWindow =
9193         do_QueryInterface(node->OwnerDoc()->GetScriptHandlingObject(ignore));
9194   } else if ((innerWindow = do_QueryInterface(aTarget))) {
9195     // Nothing else to do
9196   } else {
9197     nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(aTarget);
9198     if (helper) {
9199       innerWindow = helper->GetOwner();
9200     }
9201   }
9202 
9203   if (innerWindow) {
9204     return innerWindow->GetDocShell();
9205   }
9206 
9207   return nullptr;
9208 }
9209 
9210 /*
9211  * Note: this function only relates to figuring out HTTPS state, which is an
9212  * input to the Secure Context algorithm.  We are not actually implementing any
9213  * part of the Secure Context algorithm itself here.
9214  *
9215  * This is a bit of a hack.  Ideally we'd propagate HTTPS state through
9216  * nsIChannel as described in the Fetch and HTML specs, but making channels
9217  * know about whether they should inherit HTTPS state, propagating information
9218  * about who the channel's "client" is, exposing GetHttpsState API on channels
9219  * and modifying the various cache implementations to store and retrieve HTTPS
9220  * state involves a huge amount of code (see bug 1220687).  We avoid that for
9221  * now using this function.
9222  *
9223  * This function takes advantage of the observation that we can return true if
9224  * nsIContentSecurityManager::IsOriginPotentiallyTrustworthy returns true for
9225  * the document's origin (e.g. the origin has a scheme of 'https' or host
9226  * 'localhost' etc.).  Since we generally propagate a creator document's origin
9227  * onto data:, blob:, etc. documents, this works for them too.
9228  *
9229  * The scenario where this observation breaks down is sandboxing without the
9230  * 'allow-same-origin' flag, since in this case a document is given a unique
9231  * origin (IsOriginPotentiallyTrustworthy would return false).  We handle that
9232  * by using the origin that the document would have had had it not been
9233  * sandboxed.
9234  *
9235  * DEFICIENCIES: Note that this function uses nsIScriptSecurityManager's
9236  * getChannelResultPrincipalIfNotSandboxed, and that method's ignoring of
9237  * sandboxing is limited to the immediate sandbox.  In the case that aDocument
9238  * should inherit its origin (e.g. data: URI) but its parent has ended up
9239  * with a unique origin due to sandboxing further up the parent chain we may
9240  * end up returning false when we would ideally return true (since we will
9241  * examine the parent's origin for 'https' and not finding it.)  This means
9242  * that we may restrict the privileges of some pages unnecessarily in this
9243  * edge case.
9244  */
9245 /* static */
9246 bool nsContentUtils::HttpsStateIsModern(Document* aDocument) {
9247   if (!aDocument) {
9248     return false;
9249   }
9250 
9251   nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
9252 
9253   if (principal->IsSystemPrincipal()) {
9254     return true;
9255   }
9256 
9257   // If aDocument is sandboxed, try and get the principal that it would have
9258   // been given had it not been sandboxed:
9259   if (principal->GetIsNullPrincipal() &&
9260       (aDocument->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
9261     nsIChannel* channel = aDocument->GetChannel();
9262     if (channel) {
9263       nsCOMPtr<nsIScriptSecurityManager> ssm =
9264           nsContentUtils::GetSecurityManager();
9265       nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9266           channel, getter_AddRefs(principal));
9267       if (NS_FAILED(rv)) {
9268         return false;
9269       }
9270       if (principal->IsSystemPrincipal()) {
9271         // If a document with the system principal is sandboxing a subdocument
9272         // that would normally inherit the embedding element's principal (e.g.
9273         // a srcdoc document) then the embedding document does not trust the
9274         // content that is written to the embedded document.  Unlike when the
9275         // embedding document is https, in this case we have no indication as
9276         // to whether the embedded document's contents are delivered securely
9277         // or not, and the sandboxing would possibly indicate that they were
9278         // not.  To play it safe we return false here.  (See bug 1162772
9279         // comment 73-80.)
9280         return false;
9281       }
9282     }
9283   }
9284 
9285   if (principal->GetIsNullPrincipal()) {
9286     return false;
9287   }
9288 
9289   MOZ_ASSERT(principal->GetIsContentPrincipal());
9290 
9291   return principal->GetIsOriginPotentiallyTrustworthy();
9292 }
9293 
9294 /* static */
9295 bool nsContentUtils::ComputeIsSecureContext(nsIChannel* aChannel) {
9296   MOZ_ASSERT(aChannel);
9297 
9298   nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
9299   nsCOMPtr<nsIPrincipal> principal;
9300   nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9301       aChannel, getter_AddRefs(principal));
9302   if (NS_FAILED(rv)) {
9303     return false;
9304   }
9305 
9306   const RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
9307 
9308   if (principal->IsSystemPrincipal()) {
9309     // If the load would've been sandboxed, treat this load as an untrusted
9310     // load, as system code considers sandboxed resources insecure.
9311     return !loadInfo->GetLoadingSandboxed();
9312   }
9313 
9314   if (principal->GetIsNullPrincipal()) {
9315     return false;
9316   }
9317 
9318   if (const RefPtr<WindowContext> windowContext =
9319           WindowContext::GetById(loadInfo->GetInnerWindowID())) {
9320     if (!windowContext->GetIsSecureContext()) {
9321       return false;
9322     }
9323   }
9324 
9325   return principal->GetIsOriginPotentiallyTrustworthy();
9326 }
9327 
9328 /* static */
9329 void nsContentUtils::TryToUpgradeElement(Element* aElement) {
9330   NodeInfo* nodeInfo = aElement->NodeInfo();
9331   RefPtr<nsAtom> typeAtom =
9332       aElement->GetCustomElementData()->GetCustomElementType();
9333 
9334   MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9335   CustomElementDefinition* definition =
9336       nsContentUtils::LookupCustomElementDefinition(
9337           nodeInfo->GetDocument(), nodeInfo->NameAtom(),
9338           nodeInfo->NamespaceID(), typeAtom);
9339   if (definition) {
9340     nsContentUtils::EnqueueUpgradeReaction(aElement, definition);
9341   } else {
9342     // Add an unresolved custom element that is a candidate for upgrade when a
9343     // custom element is connected to the document.
9344     nsContentUtils::RegisterUnresolvedElement(aElement, typeAtom);
9345   }
9346 }
9347 
9348 MOZ_CAN_RUN_SCRIPT
9349 static void DoCustomElementCreate(Element** aElement, JSContext* aCx,
9350                                   Document* aDoc, NodeInfo* aNodeInfo,
9351                                   CustomElementConstructor* aConstructor,
9352                                   ErrorResult& aRv) {
9353   JS::Rooted<JS::Value> constructResult(aCx);
9354   aConstructor->Construct(&constructResult, aRv, "Custom Element Create",
9355                           CallbackFunction::eRethrowExceptions);
9356   if (aRv.Failed()) {
9357     return;
9358   }
9359 
9360   RefPtr<Element> element;
9361   // constructResult is an ObjectValue because construction with a callback
9362   // always forms the return value from a JSObject.
9363   UNWRAP_OBJECT(Element, &constructResult, element);
9364   if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9365     if (!element || !element->IsHTMLElement()) {
9366       aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9367                                                            "HTMLElement");
9368       return;
9369     }
9370   } else {
9371     if (!element || !element->IsXULElement()) {
9372       aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9373                                                            "XULElement");
9374       return;
9375     }
9376   }
9377 
9378   nsAtom* localName = aNodeInfo->NameAtom();
9379 
9380   if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
9381       element->HasChildren() || element->GetAttrCount() ||
9382       element->NodeInfo()->NameAtom() != localName) {
9383     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
9384     return;
9385   }
9386 
9387   element.forget(aElement);
9388 }
9389 
9390 /* static */
9391 nsresult nsContentUtils::NewXULOrHTMLElement(
9392     Element** aResult, mozilla::dom::NodeInfo* aNodeInfo,
9393     FromParser aFromParser, nsAtom* aIsAtom,
9394     mozilla::dom::CustomElementDefinition* aDefinition) {
9395   RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
9396   MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
9397                  nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
9398              "Can only create XUL or XHTML elements.");
9399 
9400   nsAtom* name = nodeInfo->NameAtom();
9401   int32_t tag = eHTMLTag_unknown;
9402   bool isCustomElementName = false;
9403   if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9404     tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
9405     isCustomElementName =
9406         (tag == eHTMLTag_userdefined &&
9407          nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML));
9408   } else {  // kNameSpaceID_XUL
9409     if (aIsAtom) {
9410       // Make sure the customized built-in element to be constructed confirms
9411       // to our naming requirement, i.e. [is] must be a dashed name and
9412       // the tag name must not.
9413       // if so, set isCustomElementName to false to kick off all the logics
9414       // that pick up aIsAtom.
9415       if (nsContentUtils::IsNameWithDash(aIsAtom) &&
9416           !nsContentUtils::IsNameWithDash(name)) {
9417         isCustomElementName = false;
9418       } else {
9419         isCustomElementName =
9420             nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9421       }
9422     } else {
9423       isCustomElementName =
9424           nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9425     }
9426   }
9427 
9428   nsAtom* tagAtom = nodeInfo->NameAtom();
9429   nsAtom* typeAtom = nullptr;
9430   bool isCustomElement = isCustomElementName || aIsAtom;
9431   if (isCustomElement) {
9432     typeAtom = isCustomElementName ? tagAtom : aIsAtom;
9433   }
9434 
9435   MOZ_ASSERT_IF(aDefinition, isCustomElement);
9436 
9437   // https://dom.spec.whatwg.org/#concept-create-element
9438   // We only handle the "synchronous custom elements flag is set" now.
9439   // For the unset case (e.g. cloning a node), see bug 1319342 for that.
9440   // Step 4.
9441   RefPtr<CustomElementDefinition> definition = aDefinition;
9442   if (isCustomElement && !definition) {
9443     MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9444     definition = nsContentUtils::LookupCustomElementDefinition(
9445         nodeInfo->GetDocument(), nodeInfo->NameAtom(), nodeInfo->NamespaceID(),
9446         typeAtom);
9447   }
9448 
9449   // It might be a problem that parser synchronously calls constructor, so filed
9450   // bug 1378079 to figure out what we should do for parser case.
9451   if (definition) {
9452     /*
9453      * Synchronous custom elements flag is determined by 3 places in spec,
9454      * 1) create an element for a token, the flag is determined by
9455      *    "will execute script" which is not originally created
9456      *    for the HTML fragment parsing algorithm.
9457      * 2) createElement and createElementNS, the flag is the same as
9458      *    NOT_FROM_PARSER.
9459      * 3) clone a node, our implementation will not go into this function.
9460      * For the unset case which is non-synchronous only applied for
9461      * inner/outerHTML.
9462      */
9463     bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT;
9464     // Per discussion in https://github.com/w3c/webcomponents/issues/635,
9465     // use entry global in those places that are called from JS APIs and use the
9466     // node document's global object if it is called from parser.
9467     nsIGlobalObject* global;
9468     if (aFromParser == dom::NOT_FROM_PARSER) {
9469       global = GetEntryGlobal();
9470 
9471       // Documents created from the PrototypeDocumentSink always use
9472       // NOT_FROM_PARSER for non-XUL elements. We can get the global from the
9473       // document in that case.
9474       if (!global) {
9475         Document* doc = nodeInfo->GetDocument();
9476         if (doc && doc->LoadedFromPrototype()) {
9477           global = doc->GetScopeObject();
9478         }
9479       }
9480     } else {
9481       global = nodeInfo->GetDocument()->GetScopeObject();
9482     }
9483     if (!global) {
9484       // In browser chrome code, one may have access to a document which doesn't
9485       // have scope object anymore.
9486       return NS_ERROR_FAILURE;
9487     }
9488 
9489     AutoAllowLegacyScriptExecution exemption;
9490     AutoEntryScript aes(global, "create custom elements");
9491     JSContext* cx = aes.cx();
9492     ErrorResult rv;
9493 
9494     // Step 5.
9495     if (definition->IsCustomBuiltIn()) {
9496       // SetupCustomElement() should be called with an element that don't have
9497       // CustomElementData setup, if not we will hit the assertion in
9498       // SetCustomElementData().
9499       // Built-in element
9500       if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9501         *aResult =
9502             CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9503       } else {
9504         NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9505       }
9506       (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
9507       if (synchronousCustomElements) {
9508         CustomElementRegistry::Upgrade(*aResult, definition, rv);
9509         if (rv.MaybeSetPendingException(cx)) {
9510           aes.ReportException();
9511         }
9512       } else {
9513         nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9514       }
9515 
9516       return NS_OK;
9517     }
9518 
9519     // Step 6.1.
9520     if (synchronousCustomElements) {
9521       definition->mPrefixStack.AppendElement(nodeInfo->GetPrefixAtom());
9522       RefPtr<Document> doc = nodeInfo->GetDocument();
9523       DoCustomElementCreate(aResult, cx, doc, nodeInfo,
9524                             MOZ_KnownLive(definition->mConstructor), rv);
9525       if (rv.MaybeSetPendingException(cx)) {
9526         if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9527           NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(),
9528                                                            aFromParser));
9529         } else {
9530           NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9531         }
9532         (*aResult)->SetDefined(false);
9533       }
9534       definition->mPrefixStack.RemoveLastElement();
9535       return NS_OK;
9536     }
9537 
9538     // Step 6.2.
9539     if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9540       NS_IF_ADDREF(*aResult =
9541                        NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9542     } else {
9543       NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9544     }
9545     (*aResult)->SetCustomElementData(new CustomElementData(definition->mType));
9546     nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9547     return NS_OK;
9548   }
9549 
9550   if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9551     // Per the Custom Element specification, unknown tags that are valid custom
9552     // element names should be HTMLElement instead of HTMLUnknownElement.
9553     if (isCustomElementName) {
9554       NS_IF_ADDREF(*aResult =
9555                        NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9556     } else {
9557       *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9558     }
9559   } else {
9560     NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9561   }
9562 
9563   if (!*aResult) {
9564     return NS_ERROR_OUT_OF_MEMORY;
9565   }
9566 
9567   if (isCustomElement) {
9568     (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
9569     nsContentUtils::RegisterCallbackUpgradeElement(*aResult, typeAtom);
9570   }
9571 
9572   return NS_OK;
9573 }
9574 
9575 CustomElementRegistry* nsContentUtils::GetCustomElementRegistry(
9576     Document* aDoc) {
9577   MOZ_ASSERT(aDoc);
9578 
9579   if (!aDoc->GetDocShell()) {
9580     return nullptr;
9581   }
9582 
9583   nsPIDOMWindowInner* window = aDoc->GetInnerWindow();
9584   if (!window) {
9585     return nullptr;
9586   }
9587 
9588   return window->CustomElements();
9589 }
9590 
9591 /* static */
9592 CustomElementDefinition* nsContentUtils::LookupCustomElementDefinition(
9593     Document* aDoc, nsAtom* aNameAtom, uint32_t aNameSpaceID,
9594     nsAtom* aTypeAtom) {
9595   if (aNameSpaceID != kNameSpaceID_XUL && aNameSpaceID != kNameSpaceID_XHTML) {
9596     return nullptr;
9597   }
9598 
9599   RefPtr<CustomElementRegistry> registry = GetCustomElementRegistry(aDoc);
9600   if (!registry) {
9601     return nullptr;
9602   }
9603 
9604   return registry->LookupCustomElementDefinition(aNameAtom, aNameSpaceID,
9605                                                  aTypeAtom);
9606 }
9607 
9608 /* static */
9609 void nsContentUtils::RegisterCallbackUpgradeElement(Element* aElement,
9610                                                     nsAtom* aTypeName) {
9611   MOZ_ASSERT(aElement);
9612 
9613   Document* doc = aElement->OwnerDoc();
9614   CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9615   if (registry) {
9616     registry->RegisterCallbackUpgradeElement(aElement, aTypeName);
9617   }
9618 }
9619 
9620 /* static */
9621 void nsContentUtils::RegisterUnresolvedElement(Element* aElement,
9622                                                nsAtom* aTypeName) {
9623   MOZ_ASSERT(aElement);
9624 
9625   Document* doc = aElement->OwnerDoc();
9626   CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9627   if (registry) {
9628     registry->RegisterUnresolvedElement(aElement, aTypeName);
9629   }
9630 }
9631 
9632 /* static */
9633 void nsContentUtils::UnregisterUnresolvedElement(Element* aElement) {
9634   MOZ_ASSERT(aElement);
9635 
9636   nsAtom* typeAtom = aElement->GetCustomElementData()->GetCustomElementType();
9637   Document* doc = aElement->OwnerDoc();
9638   CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9639   if (registry) {
9640     registry->UnregisterUnresolvedElement(aElement, typeAtom);
9641   }
9642 }
9643 
9644 /* static */
9645 void nsContentUtils::EnqueueUpgradeReaction(
9646     Element* aElement, CustomElementDefinition* aDefinition) {
9647   MOZ_ASSERT(aElement);
9648 
9649   Document* doc = aElement->OwnerDoc();
9650 
9651   // No DocGroup means no custom element reactions stack.
9652   if (!doc->GetDocGroup()) {
9653     return;
9654   }
9655 
9656   CustomElementReactionsStack* stack =
9657       doc->GetDocGroup()->CustomElementReactionsStack();
9658   stack->EnqueueUpgradeReaction(aElement, aDefinition);
9659 }
9660 
9661 /* static */
9662 void nsContentUtils::EnqueueLifecycleCallback(
9663     ElementCallbackType aType, Element* aCustomElement,
9664     LifecycleCallbackArgs* aArgs,
9665     LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
9666     CustomElementDefinition* aDefinition) {
9667   // No DocGroup means no custom element reactions stack.
9668   if (!aCustomElement->OwnerDoc()->GetDocGroup()) {
9669     return;
9670   }
9671 
9672   CustomElementRegistry::EnqueueLifecycleCallback(
9673       aType, aCustomElement, aArgs, aAdoptedCallbackArgs, aDefinition);
9674 }
9675 
9676 /* static */
9677 void nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
9678     Document* aDocument, nsTArray<nsIContent*>& aElements) {
9679   MOZ_ASSERT(aDocument);
9680 #ifdef DEBUG
9681   size_t oldLength = aElements.Length();
9682 #endif
9683 
9684   if (PresShell* presShell = aDocument->GetPresShell()) {
9685     if (nsIFrame* scrollFrame = presShell->GetRootScrollFrame()) {
9686       nsIAnonymousContentCreator* creator = do_QueryFrame(scrollFrame);
9687       MOZ_ASSERT(
9688           creator,
9689           "scroll frame should always implement nsIAnonymousContentCreator");
9690       creator->AppendAnonymousContentTo(aElements, 0);
9691     }
9692     if (nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame()) {
9693       canvasFrame->AppendAnonymousContentTo(aElements, 0);
9694     }
9695   }
9696 
9697 #ifdef DEBUG
9698   for (size_t i = oldLength; i < aElements.Length(); i++) {
9699     MOZ_ASSERT(
9700         aElements[i]->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent),
9701         "Someone here has lied, or missed to flag the node");
9702   }
9703 #endif
9704 }
9705 
9706 static void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame,
9707                                                    nsTArray<nsIContent*>& aKids,
9708                                                    uint32_t aFlags) {
9709   if (nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame)) {
9710     ac->AppendAnonymousContentTo(aKids, aFlags);
9711   }
9712 }
9713 
9714 /* static */
9715 void nsContentUtils::AppendNativeAnonymousChildren(const nsIContent* aContent,
9716                                                    nsTArray<nsIContent*>& aKids,
9717                                                    uint32_t aFlags) {
9718   if (aContent->MayHaveAnonymousChildren()) {
9719     if (nsIFrame* primaryFrame = aContent->GetPrimaryFrame()) {
9720       // NAC created by the element's primary frame.
9721       AppendNativeAnonymousChildrenFromFrame(primaryFrame, aKids, aFlags);
9722 
9723       // NAC created by any other non-primary frames for the element.
9724       AutoTArray<nsIFrame::OwnedAnonBox, 8> ownedAnonBoxes;
9725       primaryFrame->AppendOwnedAnonBoxes(ownedAnonBoxes);
9726       for (nsIFrame::OwnedAnonBox& box : ownedAnonBoxes) {
9727         MOZ_ASSERT(box.mAnonBoxFrame->GetContent() == aContent);
9728         AppendNativeAnonymousChildrenFromFrame(box.mAnonBoxFrame, aKids,
9729                                                aFlags);
9730       }
9731     }
9732 
9733     // Get manually created NAC (editor resize handles, etc.).
9734     if (auto nac = static_cast<ManualNACArray*>(
9735             aContent->GetProperty(nsGkAtoms::manualNACProperty))) {
9736       aKids.AppendElements(*nac);
9737     }
9738   }
9739 
9740   // The root scroll frame is not the primary frame of the root element.
9741   // Detect and handle this case.
9742   if (!(aFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
9743       aContent == aContent->OwnerDoc()->GetRootElement()) {
9744     AppendDocumentLevelNativeAnonymousContentTo(aContent->OwnerDoc(), aKids);
9745   }
9746 }
9747 
9748 bool nsContentUtils::IsImageAvailable(nsIContent* aLoadingNode, nsIURI* aURI,
9749                                       nsIPrincipal* aDefaultTriggeringPrincipal,
9750                                       CORSMode aCORSMode) {
9751   nsCOMPtr<nsIPrincipal> triggeringPrincipal;
9752   QueryTriggeringPrincipal(aLoadingNode, aDefaultTriggeringPrincipal,
9753                            getter_AddRefs(triggeringPrincipal));
9754   MOZ_ASSERT(triggeringPrincipal);
9755 
9756   Document* doc = aLoadingNode->OwnerDoc();
9757   imgLoader* imgLoader = GetImgLoaderForDocument(doc);
9758   return imgLoader->IsImageAvailable(aURI, triggeringPrincipal, aCORSMode, doc);
9759 }
9760 
9761 /* static */
9762 bool nsContentUtils::QueryTriggeringPrincipal(
9763     nsIContent* aLoadingNode, nsIPrincipal* aDefaultPrincipal,
9764     nsIPrincipal** aTriggeringPrincipal) {
9765   MOZ_ASSERT(aLoadingNode);
9766   MOZ_ASSERT(aTriggeringPrincipal);
9767 
9768   bool result = false;
9769   nsCOMPtr<nsIPrincipal> loadingPrincipal = aDefaultPrincipal;
9770   if (!loadingPrincipal) {
9771     loadingPrincipal = aLoadingNode->NodePrincipal();
9772   }
9773 
9774   // If aLoadingNode is content, bail out early.
9775   if (!aLoadingNode->NodePrincipal()->IsSystemPrincipal()) {
9776     loadingPrincipal.forget(aTriggeringPrincipal);
9777     return result;
9778   }
9779 
9780   nsAutoString loadingStr;
9781   if (aLoadingNode->IsElement()) {
9782     aLoadingNode->AsElement()->GetAttr(
9783         kNameSpaceID_None, nsGkAtoms::triggeringprincipal, loadingStr);
9784   }
9785 
9786   // Fall back if 'triggeringprincipal' isn't specified,
9787   if (loadingStr.IsEmpty()) {
9788     loadingPrincipal.forget(aTriggeringPrincipal);
9789     return result;
9790   }
9791 
9792   nsCString binary;
9793   nsresult rv = Base64Decode(NS_ConvertUTF16toUTF8(loadingStr), binary);
9794   if (NS_SUCCEEDED(rv)) {
9795     nsCOMPtr<nsIPrincipal> serializedPrin = BasePrincipal::FromJSON(binary);
9796     if (serializedPrin) {
9797       result = true;
9798       serializedPrin.forget(aTriggeringPrincipal);
9799     }
9800   } else {
9801     MOZ_ASSERT(false, "Unable to deserialize base64 principal");
9802   }
9803 
9804   if (!result) {
9805     // Fallback if the deserialization is failed.
9806     loadingPrincipal.forget(aTriggeringPrincipal);
9807   }
9808 
9809   return result;
9810 }
9811 
9812 /* static */
9813 void nsContentUtils::GetContentPolicyTypeForUIImageLoading(
9814     nsIContent* aLoadingNode, nsIPrincipal** aTriggeringPrincipal,
9815     nsContentPolicyType& aContentPolicyType, uint64_t* aRequestContextID) {
9816   MOZ_ASSERT(aRequestContextID);
9817 
9818   bool result = QueryTriggeringPrincipal(aLoadingNode, aTriggeringPrincipal);
9819   if (result) {
9820     // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for
9821     // indicating it's a favicon loading.
9822     aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
9823 
9824     nsAutoString requestContextID;
9825     if (aLoadingNode->IsElement()) {
9826       aLoadingNode->AsElement()->GetAttr(
9827           kNameSpaceID_None, nsGkAtoms::requestcontextid, requestContextID);
9828     }
9829     nsresult rv;
9830     int64_t val = requestContextID.ToInteger64(&rv);
9831     *aRequestContextID = NS_SUCCEEDED(rv) ? val : 0;
9832   } else {
9833     aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
9834   }
9835 }
9836 
9837 /* static */
9838 nsresult nsContentUtils::CreateJSValueFromSequenceOfObject(
9839     JSContext* aCx, const Sequence<JSObject*>& aTransfer,
9840     JS::MutableHandle<JS::Value> aValue) {
9841   if (aTransfer.IsEmpty()) {
9842     return NS_OK;
9843   }
9844 
9845   JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, aTransfer.Length()));
9846   if (!array) {
9847     return NS_ERROR_OUT_OF_MEMORY;
9848   }
9849 
9850   for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
9851     JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
9852     if (!object) {
9853       continue;
9854     }
9855 
9856     if (NS_WARN_IF(
9857             !JS_DefineElement(aCx, array, i, object, JSPROP_ENUMERATE))) {
9858       return NS_ERROR_OUT_OF_MEMORY;
9859     }
9860   }
9861 
9862   aValue.setObject(*array);
9863   return NS_OK;
9864 }
9865 
9866 /* static */
9867 bool nsContentUtils::ShouldBlockReservedKeys(WidgetKeyboardEvent* aKeyEvent) {
9868   nsCOMPtr<nsIPrincipal> principal;
9869   nsCOMPtr<Element> targetElement =
9870       do_QueryInterface(aKeyEvent->mOriginalTarget);
9871   nsCOMPtr<nsIBrowser> targetBrowser;
9872   if (targetElement) {
9873     targetBrowser = targetElement->AsBrowser();
9874   }
9875   bool isRemoteBrowser = false;
9876   if (targetBrowser) {
9877     targetBrowser->GetIsRemoteBrowser(&isRemoteBrowser);
9878   }
9879 
9880   if (isRemoteBrowser) {
9881     targetBrowser->GetContentPrincipal(getter_AddRefs(principal));
9882     return principal ? nsContentUtils::IsSitePermDeny(principal, "shortcuts"_ns)
9883                      : false;
9884   }
9885 
9886   nsCOMPtr<nsIContent> content = do_QueryInterface(aKeyEvent->mOriginalTarget);
9887   if (content) {
9888     Document* doc = content->GetUncomposedDoc();
9889     if (doc) {
9890       RefPtr<WindowContext> wc = doc->GetWindowContext();
9891       if (wc) {
9892         return wc->TopWindowContext()->GetShortcutsPermission() ==
9893                nsIPermissionManager::DENY_ACTION;
9894       }
9895     }
9896   }
9897 
9898   return false;
9899 }
9900 
9901 /**
9902  * Checks whether the given type is a supported document type for
9903  * loading within the nsObjectLoadingContent specified by aContent.
9904  *
9905  * NOTE Helper method for nsContentUtils::HtmlObjectContentTypeForMIMEType.
9906  * NOTE Does not take content policy or capabilities into account
9907  */
9908 static bool HtmlObjectContentSupportsDocument(const nsCString& aMimeType,
9909                                               nsIContent* aContent) {
9910   nsCOMPtr<nsIWebNavigationInfo> info(
9911       do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID));
9912   if (!info) {
9913     return false;
9914   }
9915 
9916   nsCOMPtr<nsIWebNavigation> webNav;
9917   if (aContent) {
9918     Document* currentDoc = aContent->GetComposedDoc();
9919     if (currentDoc) {
9920       webNav = do_GetInterface(currentDoc->GetWindow());
9921     }
9922   }
9923 
9924   uint32_t supported;
9925   nsresult rv = info->IsTypeSupported(aMimeType, webNav, &supported);
9926 
9927   if (NS_FAILED(rv)) {
9928     return false;
9929   }
9930 
9931   if (supported != nsIWebNavigationInfo::UNSUPPORTED) {
9932     // Don't want to support plugins as documents
9933     return supported != nsIWebNavigationInfo::FALLBACK;
9934   }
9935 
9936   // Try a stream converter
9937   // NOTE: We treat any type we can convert from as a supported type. If a
9938   // type is not actually supported, the URI loader will detect that and
9939   // return an error, and we'll fallback.
9940   nsCOMPtr<nsIStreamConverterService> convServ =
9941       do_GetService("@mozilla.org/streamConverters;1");
9942   bool canConvert = false;
9943   if (convServ) {
9944     rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
9945   }
9946   return NS_SUCCEEDED(rv) && canConvert;
9947 }
9948 
9949 /* static */
9950 already_AddRefed<nsIPluginTag> nsContentUtils::PluginTagForType(
9951     const nsCString& aMIMEType, bool aNoFakePlugin) {
9952   RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
9953   nsCOMPtr<nsIPluginTag> tag;
9954   NS_ENSURE_TRUE(pluginHost, nullptr);
9955 
9956   // ShouldPlay will handle the case where the plugin is disabled
9957   pluginHost->GetPluginTagForType(
9958       aMIMEType,
9959       aNoFakePlugin ? nsPluginHost::eExcludeFake : nsPluginHost::eExcludeNone,
9960       getter_AddRefs(tag));
9961 
9962   return tag.forget();
9963 }
9964 
9965 /* static */
9966 uint32_t nsContentUtils::HtmlObjectContentTypeForMIMEType(
9967     const nsCString& aMIMEType, bool aNoFakePlugin, nsIContent* aContent) {
9968   if (aMIMEType.IsEmpty()) {
9969     return nsIObjectLoadingContent::TYPE_NULL;
9970   }
9971 
9972   if (imgLoader::SupportImageWithMimeType(aMIMEType)) {
9973     return nsIObjectLoadingContent::TYPE_IMAGE;
9974   }
9975 
9976   // Faking support of the PDF content as a document for EMBED tags
9977   // when internal PDF viewer is enabled.
9978   if (aMIMEType.LowerCaseEqualsLiteral("application/pdf") && IsPDFJSEnabled()) {
9979     return nsIObjectLoadingContent::TYPE_DOCUMENT;
9980   }
9981 
9982   if (HtmlObjectContentSupportsDocument(aMIMEType, aContent)) {
9983     return nsIObjectLoadingContent::TYPE_DOCUMENT;
9984   }
9985 
9986   bool isSpecialPlugin = nsPluginHost::GetSpecialType(aMIMEType) !=
9987                          nsPluginHost::eSpecialType_None;
9988   if (isSpecialPlugin) {
9989     return nsIObjectLoadingContent::TYPE_FALLBACK;
9990   }
9991 
9992   return nsIObjectLoadingContent::TYPE_NULL;
9993 }
9994 
9995 /* static */
9996 already_AddRefed<nsISerialEventTarget> nsContentUtils::GetEventTargetByLoadInfo(
9997     nsILoadInfo* aLoadInfo, TaskCategory aCategory) {
9998   if (NS_WARN_IF(!aLoadInfo)) {
9999     return nullptr;
10000   }
10001 
10002   RefPtr<Document> doc;
10003   aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
10004   nsCOMPtr<nsISerialEventTarget> target;
10005   if (doc) {
10006     if (DocGroup* group = doc->GetDocGroup()) {
10007       target = group->EventTargetFor(aCategory);
10008     }
10009   } else {
10010     target = GetMainThreadSerialEventTarget();
10011   }
10012 
10013   return target.forget();
10014 }
10015 
10016 /* static */
10017 bool nsContentUtils::IsLocalRefURL(const nsAString& aString) {
10018   return !aString.IsEmpty() && aString[0] == '#';
10019 }
10020 
10021 // We use only 53 bits for the ID so that it can be converted to and from a JS
10022 // value without loss of precision. The upper bits of the ID hold the process
10023 // ID. The lower bits identify the object itself.
10024 static constexpr uint64_t kIdTotalBits = 53;
10025 static constexpr uint64_t kIdProcessBits = 22;
10026 static constexpr uint64_t kIdBits = kIdTotalBits - kIdProcessBits;
10027 
10028 /* static */
10029 uint64_t nsContentUtils::GenerateProcessSpecificId(uint64_t aId) {
10030   uint64_t processId = 0;
10031   if (XRE_IsContentProcess()) {
10032     ContentChild* cc = ContentChild::GetSingleton();
10033     processId = cc->GetID();
10034   }
10035 
10036   MOZ_RELEASE_ASSERT(processId < (uint64_t(1) << kIdProcessBits));
10037   uint64_t processBits = processId & ((uint64_t(1) << kIdProcessBits) - 1);
10038 
10039   uint64_t id = aId;
10040   MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
10041   uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
10042 
10043   return (processBits << kIdBits) | bits;
10044 }
10045 
10046 /* static */
10047 std::tuple<uint64_t, uint64_t> nsContentUtils::SplitProcessSpecificId(
10048     uint64_t aId) {
10049   return {aId >> kIdBits, aId & ((uint64_t(1) << kIdBits) - 1)};
10050 }
10051 
10052 // Next process-local Tab ID.
10053 static uint64_t gNextTabId = 0;
10054 
10055 /* static */
10056 uint64_t nsContentUtils::GenerateTabId() {
10057   return GenerateProcessSpecificId(++gNextTabId);
10058 }
10059 
10060 // Next process-local Browser ID.
10061 static uint64_t gNextBrowserId = 0;
10062 
10063 /* static */
10064 uint64_t nsContentUtils::GenerateBrowserId() {
10065   return GenerateProcessSpecificId(++gNextBrowserId);
10066 }
10067 
10068 // Next process-local Browsing Context ID.
10069 static uint64_t gNextBrowsingContextId = 0;
10070 
10071 /* static */
10072 uint64_t nsContentUtils::GenerateBrowsingContextId() {
10073   return GenerateProcessSpecificId(++gNextBrowsingContextId);
10074 }
10075 
10076 // Next process-local Window ID.
10077 static uint64_t gNextWindowId = 0;
10078 
10079 /* static */
10080 uint64_t nsContentUtils::GenerateWindowId() {
10081   return GenerateProcessSpecificId(++gNextWindowId);
10082 }
10083 
10084 // Next process-local load.
10085 static Atomic<uint64_t> gNextLoadIdentifier(0);
10086 
10087 /* static */
10088 uint64_t nsContentUtils::GenerateLoadIdentifier() {
10089   return GenerateProcessSpecificId(++gNextLoadIdentifier);
10090 }
10091 
10092 /* static */
10093 bool nsContentUtils::GetUserIsInteracting() {
10094   return UserInteractionObserver::sUserActive;
10095 }
10096 
10097 /* static */
10098 bool nsContentUtils::GetSourceMapURL(nsIHttpChannel* aChannel,
10099                                      nsACString& aResult) {
10100   nsresult rv = aChannel->GetResponseHeader("SourceMap"_ns, aResult);
10101   if (NS_FAILED(rv)) {
10102     rv = aChannel->GetResponseHeader("X-SourceMap"_ns, aResult);
10103   }
10104   return NS_SUCCEEDED(rv);
10105 }
10106 
10107 /* static */
10108 bool nsContentUtils::IsMessageInputEvent(const IPC::Message& aMsg) {
10109   if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10110       mozilla::dom::PBrowser::PBrowserStart) {
10111     switch (aMsg.type()) {
10112       case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
10113       case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10114       case mozilla::dom::PBrowser::Msg_RealMouseEnterExitWidgetEvent__ID:
10115       case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10116       case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10117       case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10118       case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
10119       case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10120       case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
10121         return true;
10122     }
10123   }
10124   return false;
10125 }
10126 
10127 /* static */
10128 bool nsContentUtils::IsMessageCriticalInputEvent(const IPC::Message& aMsg) {
10129   if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10130       mozilla::dom::PBrowser::PBrowserStart) {
10131     switch (aMsg.type()) {
10132       case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10133       case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10134       case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10135       case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10136       case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10137         return true;
10138     }
10139   }
10140   return false;
10141 }
10142 
10143 static const char* kUserInteractionInactive = "user-interaction-inactive";
10144 static const char* kUserInteractionActive = "user-interaction-active";
10145 
10146 void nsContentUtils::UserInteractionObserver::Init() {
10147   // Listen for the observer messages from EventStateManager which are telling
10148   // us whether or not the user is interacting.
10149   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10150   obs->AddObserver(this, kUserInteractionInactive, false);
10151   obs->AddObserver(this, kUserInteractionActive, false);
10152 
10153   // We can't register ourselves as an annotator yet, as the
10154   // BackgroundHangMonitor hasn't started yet. It will have started by the
10155   // time we have the chance to spin the event loop.
10156   RefPtr<UserInteractionObserver> self = this;
10157   NS_DispatchToMainThread(NS_NewRunnableFunction(
10158       "nsContentUtils::UserInteractionObserver::Init",
10159       [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
10160 }
10161 
10162 void nsContentUtils::UserInteractionObserver::Shutdown() {
10163   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10164   if (obs) {
10165     obs->RemoveObserver(this, kUserInteractionInactive);
10166     obs->RemoveObserver(this, kUserInteractionActive);
10167   }
10168 
10169   BackgroundHangMonitor::UnregisterAnnotator(*this);
10170 }
10171 
10172 /**
10173  * NB: This function is always called by the BackgroundHangMonitor thread.
10174  *     Plan accordingly
10175  */
10176 void nsContentUtils::UserInteractionObserver::AnnotateHang(
10177     BackgroundHangAnnotations& aAnnotations) {
10178   // NOTE: Only annotate the hang report if the user is known to be interacting.
10179   if (sUserActive) {
10180     aAnnotations.AddAnnotation(u"UserInteracting"_ns, true);
10181   }
10182 }
10183 
10184 NS_IMETHODIMP
10185 nsContentUtils::UserInteractionObserver::Observe(nsISupports* aSubject,
10186                                                  const char* aTopic,
10187                                                  const char16_t* aData) {
10188   if (!strcmp(aTopic, kUserInteractionInactive)) {
10189     sUserActive = false;
10190   } else if (!strcmp(aTopic, kUserInteractionActive)) {
10191     sUserActive = true;
10192   } else {
10193     NS_WARNING("Unexpected observer notification");
10194   }
10195   return NS_OK;
10196 }
10197 
10198 Atomic<bool> nsContentUtils::UserInteractionObserver::sUserActive(false);
10199 NS_IMPL_ISUPPORTS(nsContentUtils::UserInteractionObserver, nsIObserver)
10200 
10201 /* static */
10202 bool nsContentUtils::IsSpecialName(const nsAString& aName) {
10203   return aName.LowerCaseEqualsLiteral("_blank") ||
10204          aName.LowerCaseEqualsLiteral("_top") ||
10205          aName.LowerCaseEqualsLiteral("_parent") ||
10206          aName.LowerCaseEqualsLiteral("_self");
10207 }
10208 
10209 /* static */
10210 bool nsContentUtils::IsOverridingWindowName(const nsAString& aName) {
10211   return !aName.IsEmpty() && !IsSpecialName(aName);
10212 }
10213 
10214 // Unfortunately, we can't unwrap an IDL object using only a concrete type.
10215 // We need to calculate type data based on the IDL typename. Which means
10216 // wrapping our templated function in a macro.
10217 #define EXTRACT_EXN_VALUES(T, ...)                                \
10218   ExtractExceptionValues<mozilla::dom::prototypes::id::T,         \
10219                          T##_Binding::NativeType, T>(__VA_ARGS__) \
10220       .isOk()
10221 
10222 template <prototypes::ID PrototypeID, class NativeType, typename T>
10223 static Result<Ok, nsresult> ExtractExceptionValues(
10224     JSContext* aCx, JS::HandleObject aObj, nsAString& aSourceSpecOut,
10225     uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10226   AssertStaticUnwrapOK<PrototypeID>();
10227   RefPtr<T> exn;
10228   MOZ_TRY((UnwrapObject<PrototypeID, NativeType>(aObj, exn, nullptr)));
10229 
10230   exn->GetFilename(aCx, aSourceSpecOut);
10231   if (!aSourceSpecOut.IsEmpty()) {
10232     *aLineOut = exn->LineNumber(aCx);
10233     *aColumnOut = exn->ColumnNumber();
10234   }
10235 
10236   exn->GetName(aMessageOut);
10237   aMessageOut.AppendLiteral(": ");
10238 
10239   nsAutoString message;
10240   exn->GetMessageMoz(message);
10241   aMessageOut.Append(message);
10242   return Ok();
10243 }
10244 
10245 /* static */
10246 void nsContentUtils::ExtractErrorValues(
10247     JSContext* aCx, JS::Handle<JS::Value> aValue, nsACString& aSourceSpecOut,
10248     uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10249   nsAutoString sourceSpec;
10250   ExtractErrorValues(aCx, aValue, sourceSpec, aLineOut, aColumnOut,
10251                      aMessageOut);
10252   CopyUTF16toUTF8(sourceSpec, aSourceSpecOut);
10253 }
10254 
10255 /* static */
10256 void nsContentUtils::ExtractErrorValues(
10257     JSContext* aCx, JS::Handle<JS::Value> aValue, nsAString& aSourceSpecOut,
10258     uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10259   MOZ_ASSERT(aLineOut);
10260   MOZ_ASSERT(aColumnOut);
10261 
10262   if (aValue.isObject()) {
10263     JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
10264 
10265     // Try to process as an Error object.  Use the file/line/column values
10266     // from the Error as they will be more specific to the root cause of
10267     // the problem.
10268     JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
10269     if (err) {
10270       // Use xpc to extract the error message only.  We don't actually send
10271       // this report anywhere.
10272       RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
10273       report->Init(err,
10274                    nullptr,  // toString result
10275                    false,    // chrome
10276                    0);       // window ID
10277 
10278       if (!report->mFileName.IsEmpty()) {
10279         aSourceSpecOut = report->mFileName;
10280         *aLineOut = report->mLineNumber;
10281         *aColumnOut = report->mColumn;
10282       }
10283       aMessageOut.Assign(report->mErrorMsg);
10284     }
10285 
10286     // Next, try to unwrap the rejection value as a DOMException.
10287     else if (EXTRACT_EXN_VALUES(DOMException, aCx, obj, aSourceSpecOut,
10288                                 aLineOut, aColumnOut, aMessageOut)) {
10289       return;
10290     }
10291 
10292     // Next, try to unwrap the rejection value as an XPC Exception.
10293     else if (EXTRACT_EXN_VALUES(Exception, aCx, obj, aSourceSpecOut, aLineOut,
10294                                 aColumnOut, aMessageOut)) {
10295       return;
10296     }
10297   }
10298 
10299   // If we could not unwrap a specific error type, then perform default safe
10300   // string conversions on primitives.  Objects will result in "[Object]"
10301   // unfortunately.
10302   if (aMessageOut.IsEmpty()) {
10303     nsAutoJSString jsString;
10304     if (jsString.init(aCx, aValue)) {
10305       aMessageOut = jsString;
10306     } else {
10307       JS_ClearPendingException(aCx);
10308     }
10309   }
10310 }
10311 
10312 #undef EXTRACT_EXN_VALUES
10313 
10314 /* static */
10315 bool nsContentUtils::ContentIsLink(nsIContent* aContent) {
10316   if (!aContent || !aContent->IsElement()) {
10317     return false;
10318   }
10319 
10320   if (aContent->IsHTMLElement(nsGkAtoms::a)) {
10321     return true;
10322   }
10323 
10324   return aContent->AsElement()->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
10325                                             nsGkAtoms::simple, eCaseMatters);
10326 }
10327 
10328 /* static */
10329 already_AddRefed<ContentFrameMessageManager>
10330 nsContentUtils::TryGetBrowserChildGlobal(nsISupports* aFrom) {
10331   RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(aFrom);
10332   if (!frameLoaderOwner) {
10333     return nullptr;
10334   }
10335 
10336   RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
10337   if (!frameLoader) {
10338     return nullptr;
10339   }
10340 
10341   RefPtr<ContentFrameMessageManager> manager =
10342       frameLoader->GetBrowserChildMessageManager();
10343   return manager.forget();
10344 }
10345 
10346 /* static */
10347 uint32_t nsContentUtils::InnerOrOuterWindowCreated() {
10348   MOZ_ASSERT(NS_IsMainThread());
10349   ++sInnerOrOuterWindowCount;
10350   return ++sInnerOrOuterWindowSerialCounter;
10351 }
10352 
10353 /* static */
10354 void nsContentUtils::InnerOrOuterWindowDestroyed() {
10355   MOZ_ASSERT(NS_IsMainThread());
10356   MOZ_ASSERT(sInnerOrOuterWindowCount > 0);
10357   --sInnerOrOuterWindowCount;
10358 }
10359 
10360 static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
10361   nsAString* result = static_cast<nsAString*>(aData);
10362   result->Append(static_cast<const char16_t*>(aBuf),
10363                  static_cast<uint32_t>(aLen));
10364   return true;
10365 }
10366 
10367 /* static */
10368 bool nsContentUtils::StringifyJSON(JSContext* aCx,
10369                                    JS::MutableHandle<JS::Value> aValue,
10370                                    nsAString& aOutStr) {
10371   MOZ_ASSERT(aCx);
10372   aOutStr.Truncate();
10373   JS::RootedValue value(aCx, aValue.get());
10374   nsAutoString serializedValue;
10375   NS_ENSURE_TRUE(JS_Stringify(aCx, &value, nullptr, JS::NullHandleValue,
10376                               JSONCreator, &serializedValue),
10377                  false);
10378   aOutStr = serializedValue;
10379   return true;
10380 }
10381 
10382 /* static */
10383 bool nsContentUtils::
10384     HighPriorityEventPendingForTopLevelDocumentBeforeContentfulPaint(
10385         Document* aDocument) {
10386   MOZ_ASSERT(XRE_IsContentProcess(),
10387              "This function only makes sense in content processes");
10388 
10389   if (aDocument && !aDocument->IsLoadedAsData()) {
10390     if (nsPresContext* presContext = FindPresContextForDocument(aDocument)) {
10391       MOZ_ASSERT(!presContext->IsChrome(),
10392                  "Should never have a chrome PresContext in a content process");
10393 
10394       return !presContext->GetInProcessRootContentDocumentPresContext()
10395                   ->HadContentfulPaint() &&
10396              nsThreadManager::MainThreadHasPendingHighPriorityEvents();
10397     }
10398   }
10399   return false;
10400 }
10401 
10402 /* static */
10403 nsGlobalWindowInner* nsContentUtils::CallerInnerWindow() {
10404   nsIGlobalObject* global = GetIncumbentGlobal();
10405   NS_ENSURE_TRUE(global, nullptr);
10406 
10407   if (auto* window = global->AsInnerWindow()) {
10408     return nsGlobalWindowInner::Cast(window);
10409   }
10410 
10411   // When Extensions run content scripts inside a sandbox, it uses
10412   // sandboxPrototype to make them appear as though they're running in the
10413   // scope of the page. So when a content script invokes postMessage, it expects
10414   // the |source| of the received message to be the window set as the
10415   // sandboxPrototype. This used to work incidentally for unrelated reasons, but
10416   // now we need to do some special handling to support it.
10417   JS::Rooted<JSObject*> scope(RootingCx(), global->GetGlobalJSObject());
10418   NS_ENSURE_TRUE(scope, nullptr);
10419 
10420   if (xpc::IsSandbox(scope)) {
10421     AutoJSAPI jsapi;
10422     MOZ_ALWAYS_TRUE(jsapi.Init(scope));
10423     JSContext* cx = jsapi.cx();
10424     // Our current Realm on aCx is the sandbox.  Using that for unwrapping
10425     // makes sense: if the sandbox can unwrap the window, we can use it.
10426     return xpc::SandboxWindowOrNull(scope, cx);
10427   }
10428 
10429   // The calling window must be holding a reference, so we can return a weak
10430   // pointer.
10431   return nsGlobalWindowInner::Cast(global->AsInnerWindow());
10432 }
10433 
10434 /* static */
10435 bool nsContentUtils::IsURIInPrefList(nsIURI* aURI, const char* aPrefName) {
10436   MOZ_ASSERT(aPrefName);
10437 
10438   nsAutoCString list;
10439   Preferences::GetCString(aPrefName, list);
10440   ToLowerCase(list);
10441   return IsURIInList(aURI, list);
10442 }
10443 
10444 /* static */
10445 bool nsContentUtils::IsURIInList(nsIURI* aURI, const nsCString& aList) {
10446 #ifdef DEBUG
10447   nsAutoCString listLowerCase(aList);
10448   ToLowerCase(listLowerCase);
10449   MOZ_ASSERT(listLowerCase.Equals(aList),
10450              "The aList argument should be lower-case");
10451 #endif
10452 
10453   if (!aURI) {
10454     return false;
10455   }
10456 
10457   nsAutoCString scheme;
10458   aURI->GetScheme(scheme);
10459   if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
10460     return false;
10461   }
10462 
10463   if (aList.IsEmpty()) {
10464     return false;
10465   }
10466 
10467   // The list is comma separated domain list.  Each item may start with "*.".
10468   // If starts with "*.", it matches any sub-domains.
10469 
10470   nsCCharSeparatedTokenizer tokenizer(aList, ',');
10471   while (tokenizer.hasMoreTokens()) {
10472     const nsCString token(tokenizer.nextToken());
10473 
10474     nsAutoCString host;
10475     aURI->GetHost(host);
10476     if (host.IsEmpty()) {
10477       return false;
10478     }
10479     ToLowerCase(host);
10480 
10481     for (;;) {
10482       int32_t index = token.Find(host, false);
10483       if (index >= 0 &&
10484           static_cast<uint32_t>(index) + host.Length() <= token.Length()) {
10485         // If we found a full match, return true.
10486         size_t indexAfterHost = index + host.Length();
10487         if (index == 0 && indexAfterHost == token.Length()) {
10488           return true;
10489         }
10490         // If next character is '/', we need to check the path too.
10491         // We assume the path in the list means "/foo" + "*".
10492         if (token[indexAfterHost] == '/') {
10493           nsDependentCSubstring pathInList(
10494               token, indexAfterHost,
10495               static_cast<nsDependentCSubstring::size_type>(-1));
10496           nsAutoCString filePath;
10497           aURI->GetFilePath(filePath);
10498           ToLowerCase(filePath);
10499           if (StringBeginsWith(filePath, pathInList) &&
10500               (filePath.Length() == pathInList.Length() ||
10501                pathInList.EqualsLiteral("/") ||
10502                filePath[pathInList.Length() - 1] == '/' ||
10503                filePath[pathInList.Length() - 1] == '?' ||
10504                filePath[pathInList.Length() - 1] == '#')) {
10505             return true;
10506           }
10507         }
10508       }
10509       int32_t startIndexOfCurrentLevel = host[0] == '*' ? 1 : 0;
10510       int32_t startIndexOfNextLevel =
10511           host.Find(".", false, startIndexOfCurrentLevel + 1);
10512       if (startIndexOfNextLevel <= 0) {
10513         break;
10514       }
10515       host = "*"_ns + nsDependentCSubstring(host, startIndexOfNextLevel);
10516     }
10517   }
10518 
10519   return false;
10520 }
10521 
10522 /* static */
10523 ScreenIntMargin nsContentUtils::GetWindowSafeAreaInsets(
10524     nsIScreen* aScreen, const ScreenIntMargin& aSafeAreaInsets,
10525     const LayoutDeviceIntRect& aWindowRect) {
10526   // This calculates safe area insets of window from screen rectangle, window
10527   // rectangle and safe area insets of screen.
10528   //
10529   // +----------------------------------------+ <-- screen
10530   // |  +-------------------------------+  <------- window
10531   // |  | window's safe area inset top) |     |
10532   // +--+-------------------------------+--+  |
10533   // |  |                               |  |<------ safe area rectangle of
10534   // |  |                               |  |  |     screen
10535   // +--+-------------------------------+--+  |
10536   // |  |window's safe area inset bottom|     |
10537   // |  +-------------------------------+     |
10538   // +----------------------------------------+
10539   //
10540   ScreenIntMargin windowSafeAreaInsets;
10541 
10542   if (windowSafeAreaInsets == aSafeAreaInsets) {
10543     // no safe area insets.
10544     return windowSafeAreaInsets;
10545   }
10546 
10547   int32_t screenLeft, screenTop, screenWidth, screenHeight;
10548   nsresult rv =
10549       aScreen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
10550   if (NS_WARN_IF(NS_FAILED(rv))) {
10551     return windowSafeAreaInsets;
10552   }
10553 
10554   // Screen's rect of safe area
10555   LayoutDeviceIntRect safeAreaRect(
10556       screenLeft + aSafeAreaInsets.left, screenTop + aSafeAreaInsets.top,
10557       screenWidth - aSafeAreaInsets.right - aSafeAreaInsets.left,
10558       screenHeight - aSafeAreaInsets.bottom - aSafeAreaInsets.top);
10559   // window's rect of safe area
10560   safeAreaRect = safeAreaRect.Intersect(aWindowRect);
10561 
10562   windowSafeAreaInsets.top =
10563       aSafeAreaInsets.top ? std::max(safeAreaRect.y - aWindowRect.y, 0) : 0;
10564   windowSafeAreaInsets.left =
10565       aSafeAreaInsets.left ? std::max(safeAreaRect.x - aWindowRect.x, 0) : 0;
10566   windowSafeAreaInsets.right =
10567       aSafeAreaInsets.right
10568           ? std::max((aWindowRect.x + aWindowRect.width) -
10569                          (safeAreaRect.x + safeAreaRect.width),
10570                      0)
10571           : 0;
10572   windowSafeAreaInsets.bottom =
10573       aSafeAreaInsets.bottom
10574           ? std::max(aWindowRect.y + aWindowRect.height -
10575                          (safeAreaRect.y + safeAreaRect.height),
10576                      0)
10577           : 0;
10578 
10579   return windowSafeAreaInsets;
10580 }
10581 
10582 /* static */
10583 nsContentUtils::SubresourceCacheValidationInfo
10584 nsContentUtils::GetSubresourceCacheValidationInfo(nsIRequest* aRequest) {
10585   SubresourceCacheValidationInfo info;
10586   if (nsCOMPtr<nsICacheInfoChannel> cache = do_QueryInterface(aRequest)) {
10587     uint32_t value = 0;
10588     if (NS_SUCCEEDED(cache->GetCacheTokenExpirationTime(&value))) {
10589       info.mExpirationTime.emplace(value);
10590     }
10591   }
10592 
10593   // Determine whether the cache entry must be revalidated when we try to use
10594   // it. Currently, only HTTP specifies this information...
10595   if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest)) {
10596     Unused << httpChannel->IsNoStoreResponse(&info.mMustRevalidate);
10597 
10598     if (!info.mMustRevalidate) {
10599       Unused << httpChannel->IsNoCacheResponse(&info.mMustRevalidate);
10600     }
10601   }
10602 
10603   return info;
10604 }
10605 
10606 nsCString nsContentUtils::TruncatedURLForDisplay(nsIURI* aURL, size_t aMaxLen) {
10607   nsCString spec;
10608   if (aURL) {
10609     aURL->GetSpec(spec);
10610     spec.Truncate(std::min(aMaxLen, spec.Length()));
10611   }
10612   return spec;
10613 }
10614 
10615 namespace mozilla {
10616 std::ostream& operator<<(std::ostream& aOut,
10617                          const PreventDefaultResult aPreventDefaultResult) {
10618   switch (aPreventDefaultResult) {
10619     case PreventDefaultResult::No:
10620       aOut << "unhandled";
10621       break;
10622     case PreventDefaultResult::ByContent:
10623       aOut << "handled-by-content";
10624       break;
10625     case PreventDefaultResult::ByChrome:
10626       aOut << "handled-by-chrome";
10627       break;
10628   }
10629   return aOut;
10630 }
10631 }  // namespace mozilla
10632