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