Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / base / nsContentUtils.cpp
blob8238613623bd0772d3d198acd00b9f11f784a7d8
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* A namespace class for static layout utilities. */
9 #include "nsContentUtils.h"
11 #include <algorithm>
12 #include <cstddef>
13 #include <cstdint>
14 #include <cstdlib>
15 #include <cstring>
16 #include <functional>
17 #include <new>
18 #include <utility>
19 #include "BrowserChild.h"
20 #include "DecoderTraits.h"
21 #include "ErrorList.h"
22 #include "HTMLSplitOnSpacesTokenizer.h"
23 #include "ImageOps.h"
24 #include "InProcessBrowserChildMessageManager.h"
25 #include "MainThreadUtils.h"
26 #include "PLDHashTable.h"
27 #include "ReferrerInfo.h"
28 #include "ScopedNSSTypes.h"
29 #include "ThirdPartyUtil.h"
30 #include "Units.h"
31 #include "chrome/common/ipc_message.h"
32 #include "gfxDrawable.h"
33 #include "harfbuzz/hb.h"
34 #include "imgICache.h"
35 #include "imgIContainer.h"
36 #include "imgILoader.h"
37 #include "imgIRequest.h"
38 #include "imgLoader.h"
39 #include "js/Array.h"
40 #include "js/ArrayBuffer.h"
41 #include "js/BuildId.h"
42 #include "js/GCAPI.h"
43 #include "js/Id.h"
44 #include "js/JSON.h"
45 #include "js/PropertyAndElement.h" // JS_DefineElement, JS_GetProperty
46 #include "js/PropertyDescriptor.h"
47 #include "js/Realm.h"
48 #include "js/RegExp.h"
49 #include "js/RegExpFlags.h"
50 #include "js/RootingAPI.h"
51 #include "js/TypeDecls.h"
52 #include "js/Value.h"
53 #include "js/Wrapper.h"
54 #include "jsapi.h"
55 #include "jsfriendapi.h"
56 #include "mozAutoDocUpdate.h"
57 #include "mozIDOMWindow.h"
58 #include "nsIOService.h"
59 #include "mozilla/AlreadyAddRefed.h"
60 #include "mozilla/ArrayIterator.h"
61 #include "mozilla/ArrayUtils.h"
62 #include "mozilla/AsyncEventDispatcher.h"
63 #include "mozilla/AtomArray.h"
64 #include "mozilla/Atomics.h"
65 #include "mozilla/Attributes.h"
66 #include "mozilla/AutoRestore.h"
67 #include "mozilla/AutoTimelineMarker.h"
68 #include "mozilla/BackgroundHangMonitor.h"
69 #include "mozilla/Base64.h"
70 #include "mozilla/BasePrincipal.h"
71 #include "mozilla/BasicEvents.h"
72 #include "mozilla/BloomFilter.h"
73 #include "mozilla/CORSMode.h"
74 #include "mozilla/CallState.h"
75 #include "mozilla/CheckedInt.h"
76 #include "mozilla/Components.h"
77 #include "mozilla/ContentBlockingAllowList.h"
78 #include "mozilla/CycleCollectedJSContext.h"
79 #include "mozilla/DOMEventTargetHelper.h"
80 #include "mozilla/DebugOnly.h"
81 #include "mozilla/ErrorResult.h"
82 #include "mozilla/EventDispatcher.h"
83 #include "mozilla/EventListenerManager.h"
84 #include "mozilla/EventQueue.h"
85 #include "mozilla/EventStateManager.h"
86 #include "mozilla/FlushType.h"
87 #include "mozilla/FOGIPC.h"
88 #include "mozilla/HTMLEditor.h"
89 #include "mozilla/HangAnnotations.h"
90 #include "mozilla/IMEStateManager.h"
91 #include "mozilla/InputEventOptions.h"
92 #include "mozilla/InternalMutationEvent.h"
93 #include "mozilla/Latin1.h"
94 #include "mozilla/Likely.h"
95 #include "mozilla/LoadInfo.h"
96 #include "mozilla/Logging.h"
97 #include "mozilla/MacroForEach.h"
98 #include "mozilla/ManualNAC.h"
99 #include "mozilla/Maybe.h"
100 #include "mozilla/MediaFeatureChange.h"
101 #include "mozilla/MouseEvents.h"
102 #include "mozilla/NotNull.h"
103 #include "mozilla/NullPrincipal.h"
104 #include "mozilla/OriginAttributes.h"
105 #include "mozilla/Preferences.h"
106 #include "mozilla/PresShell.h"
107 #include "mozilla/ProfilerRunnable.h"
108 #include "mozilla/RangeBoundary.h"
109 #include "mozilla/RefPtr.h"
110 #include "mozilla/Result.h"
111 #include "mozilla/ResultExtensions.h"
112 #include "mozilla/ScrollbarPreferences.h"
113 #include "mozilla/Span.h"
114 #include "mozilla/StaticAnalysisFunctions.h"
115 #include "mozilla/StaticPrefs_browser.h"
116 #include "mozilla/StaticPrefs_dom.h"
117 #ifdef FUZZING
118 # include "mozilla/StaticPrefs_fuzzing.h"
119 #endif
120 #include "mozilla/StaticPrefs_nglayout.h"
121 #include "mozilla/StaticPrefs_privacy.h"
122 #include "mozilla/StaticPrefs_test.h"
123 #include "mozilla/StaticPrefs_ui.h"
124 #include "mozilla/StaticPtr.h"
125 #include "mozilla/TaskCategory.h"
126 #include "mozilla/TextControlState.h"
127 #include "mozilla/TextEditor.h"
128 #include "mozilla/TextEvents.h"
129 #include "mozilla/UniquePtr.h"
130 #include "mozilla/Unused.h"
131 #include "mozilla/Variant.h"
132 #include "mozilla/ViewportUtils.h"
133 #include "mozilla/dom/AncestorIterator.h"
134 #include "mozilla/dom/AutoEntryScript.h"
135 #include "mozilla/dom/AutocompleteInfoBinding.h"
136 #include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
137 #include "mozilla/dom/BindingDeclarations.h"
138 #include "mozilla/dom/BindingUtils.h"
139 #include "mozilla/dom/BlobImpl.h"
140 #include "mozilla/dom/BlobURLProtocolHandler.h"
141 #include "mozilla/dom/BorrowedAttrInfo.h"
142 #include "mozilla/dom/BrowserBridgeParent.h"
143 #include "mozilla/dom/BrowserParent.h"
144 #include "mozilla/dom/BrowsingContext.h"
145 #include "mozilla/dom/BrowsingContextGroup.h"
146 #include "mozilla/dom/CallbackFunction.h"
147 #include "mozilla/dom/CallbackObject.h"
148 #include "mozilla/dom/ChromeMessageBroadcaster.h"
149 #include "mozilla/dom/ContentChild.h"
150 #include "mozilla/dom/ContentFrameMessageManager.h"
151 #include "mozilla/dom/ContentParent.h"
152 #include "mozilla/dom/CustomElementRegistry.h"
153 #include "mozilla/dom/CustomElementRegistryBinding.h"
154 #include "mozilla/dom/CustomElementTypes.h"
155 #include "mozilla/dom/DOMArena.h"
156 #include "mozilla/dom/DOMException.h"
157 #include "mozilla/dom/DOMExceptionBinding.h"
158 #include "mozilla/dom/DOMSecurityMonitor.h"
159 #include "mozilla/dom/DOMTypes.h"
160 #include "mozilla/dom/DataTransfer.h"
161 #include "mozilla/dom/DocGroup.h"
162 #include "mozilla/dom/Document.h"
163 #include "mozilla/dom/DocumentFragment.h"
164 #include "mozilla/dom/DocumentInlines.h"
165 #include "mozilla/dom/Element.h"
166 #include "mozilla/dom/ElementBinding.h"
167 #include "mozilla/dom/ElementInlines.h"
168 #include "mozilla/dom/Event.h"
169 #include "mozilla/dom/EventTarget.h"
170 #include "mozilla/dom/FileBlobImpl.h"
171 #include "mozilla/dom/FileSystemSecurity.h"
172 #include "mozilla/dom/FilteredNodeIterator.h"
173 #include "mozilla/dom/FormData.h"
174 #include "mozilla/dom/FragmentOrElement.h"
175 #include "mozilla/dom/FromParser.h"
176 #include "mozilla/dom/HTMLElement.h"
177 #include "mozilla/dom/HTMLFormElement.h"
178 #include "mozilla/dom/HTMLInputElement.h"
179 #include "mozilla/dom/HTMLTextAreaElement.h"
180 #include "mozilla/dom/IPCBlob.h"
181 #include "mozilla/dom/IPCBlobUtils.h"
182 #include "mozilla/dom/MessageBroadcaster.h"
183 #include "mozilla/dom/MessageListenerManager.h"
184 #include "mozilla/dom/MessagePort.h"
185 #include "mozilla/dom/MouseEventBinding.h"
186 #include "mozilla/dom/NameSpaceConstants.h"
187 #include "mozilla/dom/NodeBinding.h"
188 #include "mozilla/dom/NodeInfo.h"
189 #include "mozilla/dom/PBrowser.h"
190 #include "mozilla/dom/PContentChild.h"
191 #include "mozilla/dom/PrototypeList.h"
192 #include "mozilla/dom/ReferrerPolicyBinding.h"
193 #include "mozilla/dom/ScriptSettings.h"
194 #include "mozilla/dom/Selection.h"
195 #include "mozilla/dom/ShadowRoot.h"
196 #include "mozilla/dom/Text.h"
197 #include "mozilla/dom/UserActivation.h"
198 #include "mozilla/dom/WindowContext.h"
199 #include "mozilla/dom/WorkerCommon.h"
200 #include "mozilla/dom/WorkerPrivate.h"
201 #include "mozilla/dom/WorkerRunnable.h"
202 #include "mozilla/dom/XULCommandEvent.h"
203 #include "mozilla/fallible.h"
204 #include "mozilla/gfx/2D.h"
205 #include "mozilla/gfx/BaseMargin.h"
206 #include "mozilla/gfx/BasePoint.h"
207 #include "mozilla/gfx/BaseSize.h"
208 #include "mozilla/gfx/DataSurfaceHelpers.h"
209 #include "mozilla/gfx/Point.h"
210 #include "mozilla/gfx/Rect.h"
211 #include "mozilla/gfx/Types.h"
212 #include "mozilla/ipc/ProtocolUtils.h"
213 #include "mozilla/ipc/SharedMemory.h"
214 #include "mozilla/net/UrlClassifierCommon.h"
215 #include "mozilla/Tokenizer.h"
216 #include "mozilla/widget/IMEData.h"
217 #include "nsAboutProtocolUtils.h"
218 #include "nsAlgorithm.h"
219 #include "nsArrayUtils.h"
220 #include "nsAtom.h"
221 #include "nsAttrName.h"
222 #include "nsAttrValue.h"
223 #include "nsAttrValueInlines.h"
224 #include "nsBaseHashtable.h"
225 #include "nsCCUncollectableMarker.h"
226 #include "nsCOMPtr.h"
227 #include "nsCRT.h"
228 #include "nsCRTGlue.h"
229 #include "nsCanvasFrame.h"
230 #include "nsCaseTreatment.h"
231 #include "nsCharSeparatedTokenizer.h"
232 #include "nsCharTraits.h"
233 #include "nsCompatibility.h"
234 #include "nsComponentManagerUtils.h"
235 #include "nsContainerFrame.h"
236 #include "nsContentCreatorFunctions.h"
237 #include "nsContentDLF.h"
238 #include "nsContentList.h"
239 #include "nsContentListDeclarations.h"
240 #include "nsContentPolicyUtils.h"
241 #include "nsCoord.h"
242 #include "nsCycleCollectionNoteChild.h"
243 #include "nsDOMMutationObserver.h"
244 #include "nsDOMString.h"
245 #include "nsTHashMap.h"
246 #include "nsDebug.h"
247 #include "nsDocShell.h"
248 #include "nsDocShellCID.h"
249 #include "nsError.h"
250 #include "nsFocusManager.h"
251 #include "nsFrameList.h"
252 #include "nsFrameLoader.h"
253 #include "nsFrameLoaderOwner.h"
254 #include "nsGenericHTMLElement.h"
255 #include "nsGkAtoms.h"
256 #include "nsGlobalWindowInner.h"
257 #include "nsGlobalWindowOuter.h"
258 #include "nsHTMLDocument.h"
259 #include "nsHTMLTags.h"
260 #include "nsHashKeys.h"
261 #include "nsHtml5StringParser.h"
262 #include "nsIAboutModule.h"
263 #include "nsIAnonymousContentCreator.h"
264 #include "nsIAppShell.h"
265 #include "nsIArray.h"
266 #include "nsIAsyncVerifyRedirectCallback.h"
267 #include "nsIBidiKeyboard.h"
268 #include "nsIBrowser.h"
269 #include "nsICacheInfoChannel.h"
270 #include "nsICategoryManager.h"
271 #include "nsIChannel.h"
272 #include "nsIChannelEventSink.h"
273 #include "nsIClassifiedChannel.h"
274 #include "nsIConsoleService.h"
275 #include "nsIContent.h"
276 #include "nsIContentInlines.h"
277 #include "nsIContentPolicy.h"
278 #include "nsIContentSecurityPolicy.h"
279 #include "nsIContentSink.h"
280 #include "nsIContentViewer.h"
281 #include "nsIDOMWindowUtils.h"
282 #include "nsIDocShell.h"
283 #include "nsIDocShellTreeItem.h"
284 #include "nsIDocumentEncoder.h"
285 #include "nsIDocumentLoaderFactory.h"
286 #include "nsIDragService.h"
287 #include "nsIDragSession.h"
288 #include "nsIFile.h"
289 #include "nsIFocusManager.h"
290 #include "nsIFormControl.h"
291 #include "nsIFragmentContentSink.h"
292 #include "nsIFrame.h"
293 #include "nsIGlobalObject.h"
294 #include "nsIHttpChannel.h"
295 #include "nsIHttpChannelInternal.h"
296 #include "nsIIOService.h"
297 #include "nsIImageLoadingContent.h"
298 #include "nsIInputStream.h"
299 #include "nsIInterfaceRequestor.h"
300 #include "nsIInterfaceRequestorUtils.h"
301 #include "nsILoadContext.h"
302 #include "nsILoadGroup.h"
303 #include "nsILoadInfo.h"
304 #include "nsIMIMEService.h"
305 #include "nsIMemoryReporter.h"
306 #include "nsINetUtil.h"
307 #include "nsINode.h"
308 #include "nsIObjectLoadingContent.h"
309 #include "nsIObserver.h"
310 #include "nsIObserverService.h"
311 #include "nsIParserUtils.h"
312 #include "nsIPermissionManager.h"
313 #include "nsIPluginTag.h"
314 #include "nsIPrincipal.h"
315 #include "nsIProperties.h"
316 #include "nsIProtocolHandler.h"
317 #include "nsIRequest.h"
318 #include "nsIRunnable.h"
319 #include "nsIScreen.h"
320 #include "nsIScriptError.h"
321 #include "nsIScriptGlobalObject.h"
322 #include "nsIScriptObjectPrincipal.h"
323 #include "nsIScriptSecurityManager.h"
324 #include "nsISerialEventTarget.h"
325 #include "nsIStreamConverter.h"
326 #include "nsIStreamConverterService.h"
327 #include "nsIStringBundle.h"
328 #include "nsISupports.h"
329 #include "nsISupportsPrimitives.h"
330 #include "nsISupportsUtils.h"
331 #include "nsITransferable.h"
332 #include "nsIURI.h"
333 #include "nsIURIMutator.h"
334 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
335 # include "nsIURIWithSpecialOrigin.h"
336 #endif
337 #include "nsIUserIdleServiceInternal.h"
338 #include "nsIWeakReferenceUtils.h"
339 #include "nsIWebNavigation.h"
340 #include "nsIWebNavigationInfo.h"
341 #include "nsIWidget.h"
342 #include "nsIWindowMediator.h"
343 #include "nsIXPConnect.h"
344 #include "nsJSPrincipals.h"
345 #include "nsJSUtils.h"
346 #include "nsLayoutUtils.h"
347 #include "nsLiteralString.h"
348 #include "nsMargin.h"
349 #include "nsMimeTypes.h"
350 #include "nsNameSpaceManager.h"
351 #include "nsNetCID.h"
352 #include "nsNetUtil.h"
353 #include "nsNodeInfoManager.h"
354 #include "nsPIDOMWindow.h"
355 #include "nsPIDOMWindowInlines.h"
356 #include "nsParser.h"
357 #include "nsParserConstants.h"
358 #include "nsPluginHost.h"
359 #include "nsPoint.h"
360 #include "nsPointerHashKeys.h"
361 #include "nsPresContext.h"
362 #include "nsQueryFrame.h"
363 #include "nsQueryObject.h"
364 #include "nsRange.h"
365 #include "nsRefPtrHashtable.h"
366 #include "nsSandboxFlags.h"
367 #include "nsScriptSecurityManager.h"
368 #include "nsServiceManagerUtils.h"
369 #include "nsStreamUtils.h"
370 #include "nsString.h"
371 #include "nsStringBuffer.h"
372 #include "nsStringBundle.h"
373 #include "nsStringFlags.h"
374 #include "nsStringFwd.h"
375 #include "nsStringIterator.h"
376 #include "nsStringStream.h"
377 #include "nsTArray.h"
378 #include "nsTLiteralString.h"
379 #include "nsTPromiseFlatString.h"
380 #include "nsTStringRepr.h"
381 #include "nsTextFragment.h"
382 #include "nsTextNode.h"
383 #include "nsThreadManager.h"
384 #include "nsThreadUtils.h"
385 #include "nsTreeSanitizer.h"
386 #include "nsUGenCategory.h"
387 #include "nsURLHelper.h"
388 #include "nsUnicodeProperties.h"
389 #include "nsVariant.h"
390 #include "nsWidgetsCID.h"
391 #include "nsView.h"
392 #include "nsViewManager.h"
393 #include "nsXPCOM.h"
394 #include "nsXPCOMCID.h"
395 #include "nsXULAppAPI.h"
396 #include "nsXULElement.h"
397 #include "nsXULPopupManager.h"
398 #include "nscore.h"
399 #include "prinrval.h"
400 #include "xpcprivate.h"
401 #include "xpcpublic.h"
403 #if defined(XP_WIN)
404 // Undefine LoadImage to prevent naming conflict with Windows.
405 # undef LoadImage
406 #endif
408 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
409 const char** next, char16_t* result);
410 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end, int ns_aware,
411 const char** colon);
413 using namespace mozilla::dom;
414 using namespace mozilla::ipc;
415 using namespace mozilla::gfx;
416 using namespace mozilla::layers;
417 using namespace mozilla::widget;
418 using namespace mozilla;
420 const char kLoadAsData[] = "loadAsData";
422 nsIXPConnect* nsContentUtils::sXPConnect;
423 nsIScriptSecurityManager* nsContentUtils::sSecurityManager;
424 nsIPrincipal* nsContentUtils::sSystemPrincipal;
425 nsIPrincipal* nsContentUtils::sNullSubjectPrincipal;
426 nsIIOService* nsContentUtils::sIOService;
427 nsIConsoleService* nsContentUtils::sConsoleService;
428 nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>*
429 nsContentUtils::sAtomEventTable = nullptr;
430 nsTHashMap<nsStringHashKey, EventNameMapping>*
431 nsContentUtils::sStringEventTable = nullptr;
432 nsTArray<RefPtr<nsAtom>>* nsContentUtils::sUserDefinedEvents = nullptr;
433 nsIStringBundleService* nsContentUtils::sStringBundleService;
435 static StaticRefPtr<nsIStringBundle>
436 sStringBundles[nsContentUtils::PropertiesFile_COUNT];
438 nsIContentPolicy* nsContentUtils::sContentPolicyService;
439 bool nsContentUtils::sTriedToGetContentPolicy = false;
440 StaticRefPtr<nsIBidiKeyboard> nsContentUtils::sBidiKeyboard;
441 uint32_t nsContentUtils::sScriptBlockerCount = 0;
442 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
443 AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners =
444 nullptr;
445 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
446 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
448 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
450 nsString* nsContentUtils::sShiftText = nullptr;
451 nsString* nsContentUtils::sControlText = nullptr;
452 nsString* nsContentUtils::sMetaText = nullptr;
453 nsString* nsContentUtils::sOSText = nullptr;
454 nsString* nsContentUtils::sAltText = nullptr;
455 nsString* nsContentUtils::sModifierSeparator = nullptr;
457 bool nsContentUtils::sInitialized = false;
458 #ifndef RELEASE_OR_BETA
459 bool nsContentUtils::sBypassCSSOMOriginCheck = false;
460 #endif
462 nsCString* nsContentUtils::sJSScriptBytecodeMimeType = nullptr;
463 nsCString* nsContentUtils::sJSModuleBytecodeMimeType = nullptr;
465 nsContentUtils::UserInteractionObserver*
466 nsContentUtils::sUserInteractionObserver = nullptr;
468 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
469 nsParser* nsContentUtils::sXMLFragmentParser = nullptr;
470 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
471 bool nsContentUtils::sFragmentParsingActive = false;
473 bool nsContentUtils::sMayHaveFormCheckboxStateChangeListeners = false;
474 bool nsContentUtils::sMayHaveFormRadioStateChangeListeners = false;
476 mozilla::LazyLogModule nsContentUtils::gResistFingerprintingLog(
477 "nsResistFingerprinting");
478 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
480 int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
481 uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
483 template Maybe<int32_t> nsContentUtils::ComparePoints(
484 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary);
485 template Maybe<int32_t> nsContentUtils::ComparePoints(
486 const RangeBoundary& aFirstBoundary,
487 const RawRangeBoundary& aSecondBoundary);
488 template Maybe<int32_t> nsContentUtils::ComparePoints(
489 const RawRangeBoundary& aFirstBoundary,
490 const RangeBoundary& aSecondBoundary);
491 template Maybe<int32_t> nsContentUtils::ComparePoints(
492 const RawRangeBoundary& aFirstBoundary,
493 const RawRangeBoundary& aSecondBoundary);
495 template int32_t nsContentUtils::ComparePoints_Deprecated(
496 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
497 bool* aDisconnected);
498 template int32_t nsContentUtils::ComparePoints_Deprecated(
499 const RangeBoundary& aFirstBoundary,
500 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
501 template int32_t nsContentUtils::ComparePoints_Deprecated(
502 const RawRangeBoundary& aFirstBoundary,
503 const RangeBoundary& aSecondBoundary, bool* aDisconnected);
504 template int32_t nsContentUtils::ComparePoints_Deprecated(
505 const RawRangeBoundary& aFirstBoundary,
506 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
508 // Subset of
509 // http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
510 enum AutocompleteUnsupportedFieldName : uint8_t {
511 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
512 eAutocompleteUnsupportedFieldName_##name_,
513 #include "AutocompleteFieldList.h"
514 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
517 enum AutocompleteNoPersistFieldName : uint8_t {
518 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
519 eAutocompleteNoPersistFieldName_##name_,
520 #include "AutocompleteFieldList.h"
521 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
524 enum AutocompleteUnsupportFieldContactHint : uint8_t {
525 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
526 eAutocompleteUnsupportedFieldContactHint_##name_,
527 #include "AutocompleteFieldList.h"
528 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
531 enum AutocompleteFieldName : uint8_t {
532 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) eAutocompleteFieldName_##name_,
533 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
534 AUTOCOMPLETE_FIELD_NAME(name_, value_)
535 #include "AutocompleteFieldList.h"
536 #undef AUTOCOMPLETE_FIELD_NAME
537 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
540 enum AutocompleteFieldHint : uint8_t {
541 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) eAutocompleteFieldHint_##name_,
542 #include "AutocompleteFieldList.h"
543 #undef AUTOCOMPLETE_FIELD_HINT
546 enum AutocompleteFieldContactHint : uint8_t {
547 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
548 eAutocompleteFieldContactHint_##name_,
549 #include "AutocompleteFieldList.h"
550 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
553 enum AutocompleteCategory {
554 #define AUTOCOMPLETE_CATEGORY(name_, value_) eAutocompleteCategory_##name_,
555 #include "AutocompleteFieldList.h"
556 #undef AUTOCOMPLETE_CATEGORY
559 static const nsAttrValue::EnumTable kAutocompleteUnsupportedFieldNameTable[] = {
560 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
561 {value_, eAutocompleteUnsupportedFieldName_##name_},
562 #include "AutocompleteFieldList.h"
563 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
564 {nullptr, 0}};
566 static const nsAttrValue::EnumTable kAutocompleteNoPersistFieldNameTable[] = {
567 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
568 {value_, eAutocompleteNoPersistFieldName_##name_},
569 #include "AutocompleteFieldList.h"
570 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
571 {nullptr, 0}};
573 static const nsAttrValue::EnumTable
574 kAutocompleteUnsupportedContactFieldHintTable[] = {
575 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
576 {value_, eAutocompleteUnsupportedFieldContactHint_##name_},
577 #include "AutocompleteFieldList.h"
578 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
579 {nullptr, 0}};
581 static const nsAttrValue::EnumTable kAutocompleteFieldNameTable[] = {
582 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
583 {value_, eAutocompleteFieldName_##name_},
584 #include "AutocompleteFieldList.h"
585 #undef AUTOCOMPLETE_FIELD_NAME
586 {nullptr, 0}};
588 static const nsAttrValue::EnumTable kAutocompleteContactFieldNameTable[] = {
589 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
590 {value_, eAutocompleteFieldName_##name_},
591 #include "AutocompleteFieldList.h"
592 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
593 {nullptr, 0}};
595 static const nsAttrValue::EnumTable kAutocompleteFieldHintTable[] = {
596 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
597 {value_, eAutocompleteFieldHint_##name_},
598 #include "AutocompleteFieldList.h"
599 #undef AUTOCOMPLETE_FIELD_HINT
600 {nullptr, 0}};
602 static const nsAttrValue::EnumTable kAutocompleteContactFieldHintTable[] = {
603 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
604 {value_, eAutocompleteFieldContactHint_##name_},
605 #include "AutocompleteFieldList.h"
606 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
607 {nullptr, 0}};
609 namespace {
611 static PLDHashTable* sEventListenerManagersHash;
613 // A global hashtable to for keeping the arena alive for cross docGroup node
614 // adoption.
615 static nsRefPtrHashtable<nsPtrHashKey<const nsINode>, mozilla::dom::DOMArena>*
616 sDOMArenaHashtable;
618 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter {
619 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
621 ~DOMEventListenerManagersHashReporter() = default;
623 public:
624 NS_DECL_ISUPPORTS
626 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
627 nsISupports* aData, bool aAnonymize) override {
628 // We don't measure the |EventListenerManager| objects pointed to by the
629 // entries because those references are non-owning.
630 int64_t amount =
631 sEventListenerManagersHash
632 ? sEventListenerManagersHash->ShallowSizeOfIncludingThis(
633 MallocSizeOf)
634 : 0;
636 MOZ_COLLECT_REPORT(
637 "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
638 amount, "Memory used by the event listener manager's hash table.");
640 return NS_OK;
644 NS_IMPL_ISUPPORTS(DOMEventListenerManagersHashReporter, nsIMemoryReporter)
646 class EventListenerManagerMapEntry : public PLDHashEntryHdr {
647 public:
648 explicit EventListenerManagerMapEntry(const void* aKey) : mKey(aKey) {}
650 ~EventListenerManagerMapEntry() {
651 NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
654 protected: // declared protected to silence clang warnings
655 const void* mKey; // must be first, to look like PLDHashEntryStub
657 public:
658 RefPtr<EventListenerManager> mListenerManager;
661 static void EventListenerManagerHashInitEntry(PLDHashEntryHdr* entry,
662 const void* key) {
663 // Initialize the entry with placement new
664 new (entry) EventListenerManagerMapEntry(key);
667 static void EventListenerManagerHashClearEntry(PLDHashTable* table,
668 PLDHashEntryHdr* entry) {
669 EventListenerManagerMapEntry* lm =
670 static_cast<EventListenerManagerMapEntry*>(entry);
672 // Let the EventListenerManagerMapEntry clean itself up...
673 lm->~EventListenerManagerMapEntry();
676 class SameOriginCheckerImpl final : public nsIChannelEventSink,
677 public nsIInterfaceRequestor {
678 ~SameOriginCheckerImpl() = default;
680 NS_DECL_ISUPPORTS
681 NS_DECL_NSICHANNELEVENTSINK
682 NS_DECL_NSIINTERFACEREQUESTOR
685 } // namespace
687 void AutoSuppressEventHandling::SuppressDocument(Document* aDoc) {
688 // Note: Document::SuppressEventHandling will also automatically suppress
689 // event handling for any in-process sub-documents. However, since we need
690 // to deal with cases where remote BrowsingContexts may be interleaved
691 // with in-process ones, we still need to walk the entire tree ourselves.
692 // This may be slightly redundant in some cases, but since event handling
693 // suppressions maintain a count of current blockers, it does not cause
694 // any problems.
695 aDoc->SuppressEventHandling();
698 void AutoSuppressEventHandling::UnsuppressDocument(Document* aDoc) {
699 aDoc->UnsuppressEventHandlingAndFireEvents(true);
702 AutoSuppressEventHandling::~AutoSuppressEventHandling() {
703 UnsuppressDocuments();
706 void AutoSuppressEventHandlingAndSuspend::SuppressDocument(Document* aDoc) {
707 AutoSuppressEventHandling::SuppressDocument(aDoc);
708 if (nsCOMPtr<nsPIDOMWindowInner> win = aDoc->GetInnerWindow()) {
709 win->Suspend();
710 mWindows.AppendElement(win);
714 AutoSuppressEventHandlingAndSuspend::~AutoSuppressEventHandlingAndSuspend() {
715 for (const auto& win : mWindows) {
716 win->Resume();
721 * This class is used to determine whether or not the user is currently
722 * interacting with the browser. It listens to observer events to toggle the
723 * value of the sUserActive static.
725 * This class is an internal implementation detail.
726 * nsContentUtils::GetUserIsInteracting() should be used to access current
727 * user interaction status.
729 class nsContentUtils::UserInteractionObserver final
730 : public nsIObserver,
731 public BackgroundHangAnnotator {
732 public:
733 NS_DECL_ISUPPORTS
734 NS_DECL_NSIOBSERVER
736 void Init();
737 void Shutdown();
738 void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
740 static Atomic<bool> sUserActive;
742 private:
743 ~UserInteractionObserver() = default;
746 static constexpr nsLiteralCString kRfpPrefs[] = {
747 "privacy.resistFingerprinting"_ns,
748 "privacy.resistFingerprinting.pbmode"_ns,
749 "privacy.fingerprintingProtection"_ns,
750 "privacy.fingerprintingProtection.pbmode"_ns,
751 "privacy.fingerprintingProtection.overrides"_ns,
754 static void RecomputeResistFingerprintingAllDocs(const char*, void*) {
755 AutoTArray<RefPtr<BrowsingContextGroup>, 5> bcGroups;
756 BrowsingContextGroup::GetAllGroups(bcGroups);
757 for (auto& bcGroup : bcGroups) {
758 AutoTArray<DocGroup*, 5> docGroups;
759 bcGroup->GetDocGroups(docGroups);
760 for (auto* docGroup : docGroups) {
761 for (Document* doc : *docGroup) {
762 if (doc->RecomputeResistFingerprinting()) {
763 if (auto* pc = doc->GetPresContext()) {
764 pc->MediaFeatureValuesChanged(
765 {MediaFeatureChangeReason::PreferenceChange},
766 MediaFeatureChangePropagation::JustThisDocument);
774 // static
775 nsresult nsContentUtils::Init() {
776 if (sInitialized) {
777 NS_WARNING("Init() called twice");
779 return NS_OK;
782 nsHTMLTags::AddRefTable();
784 sXPConnect = nsXPConnect::XPConnect();
785 // We hold a strong ref to sXPConnect to ensure that it does not go away until
786 // nsLayoutStatics::Shutdown is happening. Otherwise ~nsXPConnect can be
787 // triggered by xpcModuleDtor late in shutdown and cause crashes due to
788 // various stuff already being torn down by then. Note that this means that
789 // we are effectively making sure that if we leak nsLayoutStatics then we also
790 // leak nsXPConnect.
791 NS_ADDREF(sXPConnect);
793 sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
794 if (!sSecurityManager) return NS_ERROR_FAILURE;
795 NS_ADDREF(sSecurityManager);
797 sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
798 MOZ_ASSERT(sSystemPrincipal);
800 RefPtr<NullPrincipal> nullPrincipal =
801 NullPrincipal::CreateWithoutOriginAttributes();
802 if (!nullPrincipal) {
803 return NS_ERROR_FAILURE;
806 nullPrincipal.forget(&sNullSubjectPrincipal);
808 nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
809 if (NS_FAILED(rv)) {
810 // This makes life easier, but we can live without it.
812 sIOService = nullptr;
815 if (!InitializeEventTable()) return NS_ERROR_FAILURE;
817 if (!sEventListenerManagersHash) {
818 static const PLDHashTableOps hash_table_ops = {
819 PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
820 PLDHashTable::MoveEntryStub, EventListenerManagerHashClearEntry,
821 EventListenerManagerHashInitEntry};
823 sEventListenerManagersHash =
824 new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
826 RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
829 sBlockedScriptRunners = new AutoTArray<nsCOMPtr<nsIRunnable>, 8>;
831 #ifndef RELEASE_OR_BETA
832 sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
833 #endif
835 Element::InitCCCallbacks();
837 Unused << nsRFPService::GetOrCreate();
839 if (XRE_IsParentProcess()) {
840 AsyncPrecreateStringBundles();
843 RefPtr<UserInteractionObserver> uio = new UserInteractionObserver();
844 uio->Init();
845 uio.forget(&sUserInteractionObserver);
847 for (const auto& pref : kRfpPrefs) {
848 Preferences::RegisterCallback(RecomputeResistFingerprintingAllDocs, pref);
851 sInitialized = true;
853 return NS_OK;
856 bool nsContentUtils::InitJSBytecodeMimeType() {
857 MOZ_ASSERT(NS_IsMainThread());
858 MOZ_ASSERT(!sJSScriptBytecodeMimeType);
859 MOZ_ASSERT(!sJSModuleBytecodeMimeType);
861 JS::BuildIdCharVector jsBuildId;
862 if (!JS::GetScriptTranscodingBuildId(&jsBuildId)) {
863 return false;
866 nsDependentCSubstring jsBuildIdStr(jsBuildId.begin(), jsBuildId.length());
867 sJSScriptBytecodeMimeType =
868 new nsCString("javascript/moz-script-bytecode-"_ns + jsBuildIdStr);
869 sJSModuleBytecodeMimeType =
870 new nsCString("javascript/moz-module-bytecode-"_ns + jsBuildIdStr);
871 return true;
874 void nsContentUtils::GetShiftText(nsAString& text) {
875 if (!sShiftText) InitializeModifierStrings();
876 text.Assign(*sShiftText);
879 void nsContentUtils::GetControlText(nsAString& text) {
880 if (!sControlText) InitializeModifierStrings();
881 text.Assign(*sControlText);
884 void nsContentUtils::GetMetaText(nsAString& text) {
885 if (!sMetaText) InitializeModifierStrings();
886 text.Assign(*sMetaText);
889 void nsContentUtils::GetOSText(nsAString& text) {
890 if (!sOSText) {
891 InitializeModifierStrings();
893 text.Assign(*sOSText);
896 void nsContentUtils::GetAltText(nsAString& text) {
897 if (!sAltText) InitializeModifierStrings();
898 text.Assign(*sAltText);
901 void nsContentUtils::GetModifierSeparatorText(nsAString& text) {
902 if (!sModifierSeparator) InitializeModifierStrings();
903 text.Assign(*sModifierSeparator);
906 void nsContentUtils::InitializeModifierStrings() {
907 // load the display strings for the keyboard accelerators
908 nsCOMPtr<nsIStringBundleService> bundleService =
909 mozilla::components::StringBundle::Service();
910 nsCOMPtr<nsIStringBundle> bundle;
911 DebugOnly<nsresult> rv = NS_OK;
912 if (bundleService) {
913 rv = bundleService->CreateBundle(
914 "chrome://global-platform/locale/platformKeys.properties",
915 getter_AddRefs(bundle));
918 NS_ASSERTION(
919 NS_SUCCEEDED(rv) && bundle,
920 "chrome://global/locale/platformKeys.properties could not be loaded");
921 nsAutoString shiftModifier;
922 nsAutoString metaModifier;
923 nsAutoString osModifier;
924 nsAutoString altModifier;
925 nsAutoString controlModifier;
926 nsAutoString modifierSeparator;
927 if (bundle) {
928 // macs use symbols for each modifier key, so fetch each from the bundle,
929 // which also covers i18n
930 bundle->GetStringFromName("VK_SHIFT", shiftModifier);
931 bundle->GetStringFromName("VK_META", metaModifier);
932 bundle->GetStringFromName("VK_WIN", osModifier);
933 bundle->GetStringFromName("VK_ALT", altModifier);
934 bundle->GetStringFromName("VK_CONTROL", controlModifier);
935 bundle->GetStringFromName("MODIFIER_SEPARATOR", modifierSeparator);
937 // if any of these don't exist, we get an empty string
938 sShiftText = new nsString(shiftModifier);
939 sMetaText = new nsString(metaModifier);
940 sOSText = new nsString(osModifier);
941 sAltText = new nsString(altModifier);
942 sControlText = new nsString(controlModifier);
943 sModifierSeparator = new nsString(modifierSeparator);
946 mozilla::EventClassID nsContentUtils::GetEventClassIDFromMessage(
947 EventMessage aEventMessage) {
948 switch (aEventMessage) {
949 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
950 case message_: \
951 return struct_;
952 #include "mozilla/EventNameList.h"
953 #undef MESSAGE_TO_EVENT
954 default:
955 MOZ_ASSERT_UNREACHABLE("Invalid event message?");
956 return eBasicEventClass;
960 bool nsContentUtils::IsExternalProtocol(nsIURI* aURI) {
961 bool doesNotReturnData = false;
962 nsresult rv = NS_URIChainHasFlags(
963 aURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &doesNotReturnData);
964 return NS_SUCCEEDED(rv) && doesNotReturnData;
967 static nsAtom* GetEventTypeFromMessage(EventMessage aEventMessage) {
968 switch (aEventMessage) {
969 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
970 case message_: \
971 return nsGkAtoms::on##name_;
972 #include "mozilla/EventNameList.h"
973 #undef MESSAGE_TO_EVENT
974 default:
975 return nullptr;
979 // Because of SVG/SMIL we have several atoms mapped to the same
980 // id, but we can rely on MESSAGE_TO_EVENT to map id to only one atom.
981 static bool ShouldAddEventToStringEventTable(const EventNameMapping& aMapping) {
982 MOZ_ASSERT(aMapping.mAtom);
983 return GetEventTypeFromMessage(aMapping.mMessage) == aMapping.mAtom;
986 bool nsContentUtils::InitializeEventTable() {
987 NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
988 NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
990 static const EventNameMapping eventArray[] = {
991 #define EVENT(name_, _message, _type, _class) \
992 {nsGkAtoms::on##name_, _type, _message, _class, false},
993 #define WINDOW_ONLY_EVENT EVENT
994 #define DOCUMENT_ONLY_EVENT EVENT
995 #define NON_IDL_EVENT EVENT
996 #include "mozilla/EventNameList.h"
997 #undef WINDOW_ONLY_EVENT
998 #undef NON_IDL_EVENT
999 #undef EVENT
1000 {nullptr}};
1002 sAtomEventTable = new nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>(
1003 ArrayLength(eventArray));
1004 sStringEventTable = new nsTHashMap<nsStringHashKey, EventNameMapping>(
1005 ArrayLength(eventArray));
1006 sUserDefinedEvents = new nsTArray<RefPtr<nsAtom>>(64);
1008 // Subtract one from the length because of the trailing null
1009 for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) {
1010 MOZ_ASSERT(!sAtomEventTable->Contains(eventArray[i].mAtom),
1011 "Double-defining event name; fix your EventNameList.h");
1012 sAtomEventTable->InsertOrUpdate(eventArray[i].mAtom, eventArray[i]);
1013 if (ShouldAddEventToStringEventTable(eventArray[i])) {
1014 sStringEventTable->InsertOrUpdate(
1015 Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
1016 eventArray[i]);
1020 return true;
1023 void nsContentUtils::InitializeTouchEventTable() {
1024 static bool sEventTableInitialized = false;
1025 if (!sEventTableInitialized && sAtomEventTable && sStringEventTable) {
1026 sEventTableInitialized = true;
1027 static const EventNameMapping touchEventArray[] = {
1028 #define EVENT(name_, _message, _type, _class)
1029 #define TOUCH_EVENT(name_, _message, _type, _class) \
1030 {nsGkAtoms::on##name_, _type, _message, _class},
1031 #include "mozilla/EventNameList.h"
1032 #undef TOUCH_EVENT
1033 #undef EVENT
1034 {nullptr}};
1035 // Subtract one from the length because of the trailing null
1036 for (uint32_t i = 0; i < ArrayLength(touchEventArray) - 1; ++i) {
1037 sAtomEventTable->InsertOrUpdate(touchEventArray[i].mAtom,
1038 touchEventArray[i]);
1039 sStringEventTable->InsertOrUpdate(
1040 Substring(nsDependentAtomString(touchEventArray[i].mAtom), 2),
1041 touchEventArray[i]);
1046 static bool Is8bit(const nsAString& aString) {
1047 static const char16_t EIGHT_BIT = char16_t(~0x00FF);
1049 for (nsAString::const_char_iterator start = aString.BeginReading(),
1050 end = aString.EndReading();
1051 start != end; ++start) {
1052 if (*start & EIGHT_BIT) {
1053 return false;
1057 return true;
1060 nsresult nsContentUtils::Btoa(const nsAString& aBinaryData,
1061 nsAString& aAsciiBase64String) {
1062 if (!Is8bit(aBinaryData)) {
1063 aAsciiBase64String.Truncate();
1064 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1067 return Base64Encode(aBinaryData, aAsciiBase64String);
1070 nsresult nsContentUtils::Atob(const nsAString& aAsciiBase64String,
1071 nsAString& aBinaryData) {
1072 if (!Is8bit(aAsciiBase64String)) {
1073 aBinaryData.Truncate();
1074 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1077 const char16_t* start = aAsciiBase64String.BeginReading();
1078 const char16_t* cur = start;
1079 const char16_t* end = aAsciiBase64String.EndReading();
1080 bool hasWhitespace = false;
1082 while (cur < end) {
1083 if (nsContentUtils::IsHTMLWhitespace(*cur)) {
1084 hasWhitespace = true;
1085 break;
1087 cur++;
1090 nsresult rv;
1092 if (hasWhitespace) {
1093 nsString trimmedString;
1095 if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible)) {
1096 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1099 trimmedString.Append(start, cur - start);
1101 while (cur < end) {
1102 if (!nsContentUtils::IsHTMLWhitespace(*cur)) {
1103 trimmedString.Append(*cur);
1105 cur++;
1107 rv = Base64Decode(trimmedString, aBinaryData);
1108 } else {
1109 rv = Base64Decode(aAsciiBase64String, aBinaryData);
1112 if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
1113 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1115 return rv;
1118 bool nsContentUtils::IsAutocompleteEnabled(
1119 mozilla::dom::HTMLInputElement* aInput) {
1120 MOZ_ASSERT(aInput, "aInput should not be null!");
1122 nsAutoString autocomplete;
1123 aInput->GetAutocomplete(autocomplete);
1125 if (autocomplete.IsEmpty()) {
1126 auto* form = aInput->GetForm();
1127 if (!form) {
1128 return true;
1131 form->GetAutocomplete(autocomplete);
1134 return !autocomplete.EqualsLiteral("off");
1137 nsContentUtils::AutocompleteAttrState
1138 nsContentUtils::SerializeAutocompleteAttribute(
1139 const nsAttrValue* aAttr, nsAString& aResult,
1140 AutocompleteAttrState aCachedState) {
1141 if (!aAttr ||
1142 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1143 return aCachedState;
1146 if (aCachedState == nsContentUtils::eAutocompleteAttrState_Valid) {
1147 uint32_t atomCount = aAttr->GetAtomCount();
1148 for (uint32_t i = 0; i < atomCount; i++) {
1149 if (i != 0) {
1150 aResult.Append(' ');
1152 aResult.Append(nsDependentAtomString(aAttr->AtomAt(i)));
1154 nsContentUtils::ASCIIToLower(aResult);
1155 return aCachedState;
1158 aResult.Truncate();
1160 mozilla::dom::AutocompleteInfo info;
1161 AutocompleteAttrState state =
1162 InternalSerializeAutocompleteAttribute(aAttr, info);
1163 if (state == eAutocompleteAttrState_Valid) {
1164 // Concatenate the info fields.
1165 aResult = info.mSection;
1167 if (!info.mAddressType.IsEmpty()) {
1168 if (!aResult.IsEmpty()) {
1169 aResult += ' ';
1171 aResult += info.mAddressType;
1174 if (!info.mContactType.IsEmpty()) {
1175 if (!aResult.IsEmpty()) {
1176 aResult += ' ';
1178 aResult += info.mContactType;
1181 if (!info.mFieldName.IsEmpty()) {
1182 if (!aResult.IsEmpty()) {
1183 aResult += ' ';
1185 aResult += info.mFieldName;
1189 return state;
1192 nsContentUtils::AutocompleteAttrState
1193 nsContentUtils::SerializeAutocompleteAttribute(
1194 const nsAttrValue* aAttr, mozilla::dom::AutocompleteInfo& aInfo,
1195 AutocompleteAttrState aCachedState, bool aGrantAllValidValue) {
1196 if (!aAttr ||
1197 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1198 return aCachedState;
1201 return InternalSerializeAutocompleteAttribute(aAttr, aInfo,
1202 aGrantAllValidValue);
1206 * Helper to validate the @autocomplete tokens.
1208 * @return {AutocompleteAttrState} The state of the attribute (invalid/valid).
1210 nsContentUtils::AutocompleteAttrState
1211 nsContentUtils::InternalSerializeAutocompleteAttribute(
1212 const nsAttrValue* aAttrVal, mozilla::dom::AutocompleteInfo& aInfo,
1213 bool aGrantAllValidValue) {
1214 // No autocomplete attribute so we are done
1215 if (!aAttrVal) {
1216 return eAutocompleteAttrState_Invalid;
1219 uint32_t numTokens = aAttrVal->GetAtomCount();
1220 if (!numTokens) {
1221 return eAutocompleteAttrState_Invalid;
1224 uint32_t index = numTokens - 1;
1225 nsString tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1226 AutocompleteCategory category;
1227 nsAttrValue enumValue;
1229 bool unsupported = false;
1230 if (!aGrantAllValidValue) {
1231 unsupported = enumValue.ParseEnumValue(
1232 tokenString, kAutocompleteUnsupportedFieldNameTable, false);
1233 if (unsupported) {
1234 return eAutocompleteAttrState_Invalid;
1238 nsAutoString str;
1239 bool result =
1240 enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
1241 if (result) {
1242 // Off/Automatic/Normal categories.
1243 if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
1244 enumValue.Equals(u"on"_ns, eIgnoreCase)) {
1245 if (numTokens > 1) {
1246 return eAutocompleteAttrState_Invalid;
1248 enumValue.ToString(str);
1249 ASCIIToLower(str);
1250 aInfo.mFieldName.Assign(str);
1251 aInfo.mCanAutomaticallyPersist =
1252 !enumValue.Equals(u"off"_ns, eIgnoreCase);
1253 return eAutocompleteAttrState_Valid;
1256 // Only allow on/off if form autofill @autocomplete values aren't enabled
1257 // and it doesn't grant all valid values.
1258 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1259 !aGrantAllValidValue) {
1260 return eAutocompleteAttrState_Invalid;
1263 // Normal category
1264 if (numTokens > 3) {
1265 return eAutocompleteAttrState_Invalid;
1267 category = eAutocompleteCategory_NORMAL;
1268 } else { // Check if the last token is of the contact category instead.
1269 // Only allow on/off if form autofill @autocomplete values aren't enabled
1270 // and it doesn't grant all valid values.
1271 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1272 !aGrantAllValidValue) {
1273 return eAutocompleteAttrState_Invalid;
1276 result = enumValue.ParseEnumValue(
1277 tokenString, kAutocompleteContactFieldNameTable, false);
1278 if (!result || numTokens > 4) {
1279 return eAutocompleteAttrState_Invalid;
1282 category = eAutocompleteCategory_CONTACT;
1285 enumValue.ToString(str);
1286 ASCIIToLower(str);
1287 aInfo.mFieldName.Assign(str);
1289 aInfo.mCanAutomaticallyPersist = !enumValue.ParseEnumValue(
1290 tokenString, kAutocompleteNoPersistFieldNameTable, false);
1292 // We are done if this was the only token.
1293 if (numTokens == 1) {
1294 return eAutocompleteAttrState_Valid;
1297 --index;
1298 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1300 if (category == eAutocompleteCategory_CONTACT) {
1301 if (!aGrantAllValidValue) {
1302 unsupported = enumValue.ParseEnumValue(
1303 tokenString, kAutocompleteUnsupportedContactFieldHintTable, false);
1304 if (unsupported) {
1305 return eAutocompleteAttrState_Invalid;
1309 nsAttrValue contactFieldHint;
1310 result = contactFieldHint.ParseEnumValue(
1311 tokenString, kAutocompleteContactFieldHintTable, false);
1312 if (result) {
1313 nsAutoString contactFieldHintString;
1314 contactFieldHint.ToString(contactFieldHintString);
1315 ASCIIToLower(contactFieldHintString);
1316 aInfo.mContactType.Assign(contactFieldHintString);
1317 if (index == 0) {
1318 return eAutocompleteAttrState_Valid;
1320 --index;
1321 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1325 // Check for billing/shipping tokens
1326 nsAttrValue fieldHint;
1327 if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable,
1328 false)) {
1329 nsString fieldHintString;
1330 fieldHint.ToString(fieldHintString);
1331 ASCIIToLower(fieldHintString);
1332 aInfo.mAddressType.Assign(fieldHintString);
1333 if (index == 0) {
1334 return eAutocompleteAttrState_Valid;
1336 --index;
1337 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1340 // Check for section-* token
1341 const nsDependentSubstring& section = Substring(tokenString, 0, 8);
1342 if (section.LowerCaseEqualsASCII("section-")) {
1343 ASCIIToLower(tokenString);
1344 aInfo.mSection.Assign(tokenString);
1345 if (index == 0) {
1346 return eAutocompleteAttrState_Valid;
1350 // Clear the fields as the autocomplete attribute is invalid.
1351 aInfo.mSection.Truncate();
1352 aInfo.mAddressType.Truncate();
1353 aInfo.mContactType.Truncate();
1354 aInfo.mFieldName.Truncate();
1356 return eAutocompleteAttrState_Invalid;
1359 // Parse an integer according to HTML spec
1360 template <class CharT>
1361 int32_t nsContentUtils::ParseHTMLIntegerImpl(
1362 const CharT* aStart, const CharT* aEnd,
1363 ParseHTMLIntegerResultFlags* aResult) {
1364 int result = eParseHTMLInteger_NoFlags;
1366 const CharT* iter = aStart;
1368 while (iter != aEnd && nsContentUtils::IsHTMLWhitespace(*iter)) {
1369 result |= eParseHTMLInteger_NonStandard;
1370 ++iter;
1373 if (iter == aEnd) {
1374 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1375 *aResult = (ParseHTMLIntegerResultFlags)result;
1376 return 0;
1379 int sign = 1;
1380 if (*iter == CharT('-')) {
1381 sign = -1;
1382 result |= eParseHTMLInteger_Negative;
1383 ++iter;
1384 } else if (*iter == CharT('+')) {
1385 result |= eParseHTMLInteger_NonStandard;
1386 ++iter;
1389 bool foundValue = false;
1390 CheckedInt32 value = 0;
1392 // Check for leading zeros first.
1393 uint64_t leadingZeros = 0;
1394 while (iter != aEnd) {
1395 if (*iter != CharT('0')) {
1396 break;
1399 ++leadingZeros;
1400 foundValue = true;
1401 ++iter;
1404 while (iter != aEnd) {
1405 if (*iter >= CharT('0') && *iter <= CharT('9')) {
1406 value = (value * 10) + (*iter - CharT('0')) * sign;
1407 ++iter;
1408 if (!value.isValid()) {
1409 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
1410 break;
1412 foundValue = true;
1413 } else {
1414 break;
1418 if (!foundValue) {
1419 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1422 if (value.isValid() &&
1423 ((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
1424 (sign == -1 && value == 0))) {
1425 result |= eParseHTMLInteger_NonStandard;
1428 if (iter != aEnd) {
1429 result |= eParseHTMLInteger_DidNotConsumeAllInput;
1432 *aResult = (ParseHTMLIntegerResultFlags)result;
1433 return value.isValid() ? value.value() : 0;
1436 // Parse an integer according to HTML spec
1437 int32_t nsContentUtils::ParseHTMLInteger(const char16_t* aStart,
1438 const char16_t* aEnd,
1439 ParseHTMLIntegerResultFlags* aResult) {
1440 return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
1443 int32_t nsContentUtils::ParseHTMLInteger(const char* aStart, const char* aEnd,
1444 ParseHTMLIntegerResultFlags* aResult) {
1445 return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
1448 #define SKIP_WHITESPACE(iter, end_iter, end_res) \
1449 while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
1450 ++(iter); \
1452 if ((iter) == (end_iter)) { \
1453 return (end_res); \
1456 #define SKIP_ATTR_NAME(iter, end_iter) \
1457 while ((iter) != (end_iter) && !nsCRT::IsAsciiSpace(*(iter)) && \
1458 *(iter) != '=') { \
1459 ++(iter); \
1462 bool nsContentUtils::GetPseudoAttributeValue(const nsString& aSource,
1463 nsAtom* aName, nsAString& aValue) {
1464 aValue.Truncate();
1466 const char16_t* start = aSource.get();
1467 const char16_t* end = start + aSource.Length();
1468 const char16_t* iter;
1470 while (start != end) {
1471 SKIP_WHITESPACE(start, end, false)
1472 iter = start;
1473 SKIP_ATTR_NAME(iter, end)
1475 if (start == iter) {
1476 return false;
1479 // Remember the attr name.
1480 const nsDependentSubstring& attrName = Substring(start, iter);
1482 // Now check whether this is a valid name="value" pair.
1483 start = iter;
1484 SKIP_WHITESPACE(start, end, false)
1485 if (*start != '=') {
1486 // No '=', so this is not a name="value" pair. We don't know
1487 // what it is, and we have no way to handle it.
1488 return false;
1491 // Have to skip the value.
1492 ++start;
1493 SKIP_WHITESPACE(start, end, false)
1494 char16_t q = *start;
1495 if (q != kQuote && q != kApostrophe) {
1496 // Not a valid quoted value, so bail.
1497 return false;
1500 ++start; // Point to the first char of the value.
1501 iter = start;
1503 while (iter != end && *iter != q) {
1504 ++iter;
1507 if (iter == end) {
1508 // Oops, unterminated quoted string.
1509 return false;
1512 // At this point attrName holds the name of the "attribute" and
1513 // the value is between start and iter.
1515 if (aName->Equals(attrName)) {
1516 // We'll accumulate as many characters as possible (until we hit either
1517 // the end of the string or the beginning of an entity). Chunks will be
1518 // delimited by start and chunkEnd.
1519 const char16_t* chunkEnd = start;
1520 while (chunkEnd != iter) {
1521 if (*chunkEnd == kLessThan) {
1522 aValue.Truncate();
1524 return false;
1527 if (*chunkEnd == kAmpersand) {
1528 aValue.Append(start, chunkEnd - start);
1530 const char16_t* afterEntity = nullptr;
1531 char16_t result[2];
1532 uint32_t count = MOZ_XMLTranslateEntity(
1533 reinterpret_cast<const char*>(chunkEnd),
1534 reinterpret_cast<const char*>(iter),
1535 reinterpret_cast<const char**>(&afterEntity), result);
1536 if (count == 0) {
1537 aValue.Truncate();
1539 return false;
1542 aValue.Append(result, count);
1544 // Advance to after the entity and begin a new chunk.
1545 start = chunkEnd = afterEntity;
1546 } else {
1547 ++chunkEnd;
1551 // Append remainder.
1552 aValue.Append(start, iter - start);
1554 return true;
1557 // Resume scanning after the end of the attribute value (past the quote
1558 // char).
1559 start = iter + 1;
1562 return false;
1565 bool nsContentUtils::IsJavaScriptLanguage(const nsString& aName) {
1566 // Create MIME type as "text/" + given input
1567 nsAutoString mimeType(u"text/");
1568 mimeType.Append(aName);
1570 return IsJavascriptMIMEType(mimeType);
1573 void nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType,
1574 nsString& aParams) {
1575 aType.Truncate();
1576 aParams.Truncate();
1577 int32_t semiIndex = aValue.FindChar(char16_t(';'));
1578 if (-1 != semiIndex) {
1579 aType = Substring(aValue, 0, semiIndex);
1580 aParams =
1581 Substring(aValue, semiIndex + 1, aValue.Length() - (semiIndex + 1));
1582 aParams.StripWhitespace();
1583 } else {
1584 aType = aValue;
1586 aType.StripWhitespace();
1590 * A helper function that parses a sandbox attribute (of an <iframe> or a CSP
1591 * directive) and converts it to the set of flags used internally.
1593 * @param aSandboxAttr the sandbox attribute
1594 * @return the set of flags (SANDBOXED_NONE if aSandboxAttr is
1595 * null)
1597 uint32_t nsContentUtils::ParseSandboxAttributeToFlags(
1598 const nsAttrValue* aSandboxAttr) {
1599 if (!aSandboxAttr) {
1600 return SANDBOXED_NONE;
1603 uint32_t out = SANDBOX_ALL_FLAGS;
1605 #define SANDBOX_KEYWORD(string, atom, flags) \
1606 if (aSandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { \
1607 out &= ~(flags); \
1609 #include "IframeSandboxKeywordList.h"
1610 #undef SANDBOX_KEYWORD
1612 return out;
1616 * A helper function that checks if a string matches a valid sandbox flag.
1618 * @param aFlag the potential sandbox flag.
1619 * @return true if the flag is a sandbox flag.
1621 bool nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag) {
1622 #define SANDBOX_KEYWORD(string, atom, flags) \
1623 if (EqualsIgnoreASCIICase(nsDependentAtomString(nsGkAtoms::atom), aFlag)) { \
1624 return true; \
1626 #include "IframeSandboxKeywordList.h"
1627 #undef SANDBOX_KEYWORD
1628 return false;
1632 * A helper function that returns a string attribute corresponding to the
1633 * sandbox flags.
1635 * @param aFlags the sandbox flags
1636 * @param aString the attribute corresponding to the flags (null if aFlags
1637 * is zero)
1639 void nsContentUtils::SandboxFlagsToString(uint32_t aFlags, nsAString& aString) {
1640 if (!aFlags) {
1641 SetDOMStringToNull(aString);
1642 return;
1645 aString.Truncate();
1647 #define SANDBOX_KEYWORD(string, atom, flags) \
1648 if (!(aFlags & (flags))) { \
1649 if (!aString.IsEmpty()) { \
1650 aString.AppendLiteral(u" "); \
1652 aString.Append(nsDependentAtomString(nsGkAtoms::atom)); \
1654 #include "IframeSandboxKeywordList.h"
1655 #undef SANDBOX_KEYWORD
1658 nsIBidiKeyboard* nsContentUtils::GetBidiKeyboard() {
1659 if (!sBidiKeyboard) {
1660 sBidiKeyboard = nsIWidget::CreateBidiKeyboard();
1662 return sBidiKeyboard;
1666 * This is used to determine whether a character is in one of the classes
1667 * which CSS says should be part of the first-letter. Currently, that is
1668 * all punctuation classes (P*). Note that this is a change from CSS2
1669 * which excluded Pc and Pd.
1671 * https://www.w3.org/TR/css-pseudo-4/#first-letter-pseudo
1672 * "Punctuation (i.e, characters that belong to the Punctuation (P*) Unicode
1673 * general category [UAX44]) [...]"
1676 // static
1677 bool nsContentUtils::IsFirstLetterPunctuation(uint32_t aChar) {
1678 switch (mozilla::unicode::GetGeneralCategory(aChar)) {
1679 case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
1680 case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION: /* Pd */
1681 case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION: /* Pe */
1682 case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION: /* Pf */
1683 case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
1684 case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION: /* Po */
1685 case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION: /* Ps */
1686 return true;
1687 default:
1688 return false;
1692 // static
1693 bool nsContentUtils::IsAlphanumeric(uint32_t aChar) {
1694 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1696 return (cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber);
1699 // static
1700 bool nsContentUtils::IsAlphanumericOrSymbol(uint32_t aChar) {
1701 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1703 return cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber ||
1704 cat == nsUGenCategory::kSymbol;
1707 /* static */
1708 bool nsContentUtils::IsHTMLWhitespace(char16_t aChar) {
1709 return aChar == char16_t(0x0009) || aChar == char16_t(0x000A) ||
1710 aChar == char16_t(0x000C) || aChar == char16_t(0x000D) ||
1711 aChar == char16_t(0x0020);
1714 /* static */
1715 bool nsContentUtils::IsHTMLWhitespaceOrNBSP(char16_t aChar) {
1716 return IsHTMLWhitespace(aChar) || aChar == char16_t(0xA0);
1719 /* static */
1720 bool nsContentUtils::IsHTMLBlockLevelElement(nsIContent* aContent) {
1721 return aContent->IsAnyOfHTMLElements(
1722 nsGkAtoms::address, nsGkAtoms::article, nsGkAtoms::aside,
1723 nsGkAtoms::blockquote, nsGkAtoms::center, nsGkAtoms::dir, nsGkAtoms::div,
1724 nsGkAtoms::dl, // XXX why not dt and dd?
1725 nsGkAtoms::fieldset,
1726 nsGkAtoms::figure, // XXX shouldn't figcaption be on this list
1727 nsGkAtoms::footer, nsGkAtoms::form, nsGkAtoms::h1, nsGkAtoms::h2,
1728 nsGkAtoms::h3, nsGkAtoms::h4, nsGkAtoms::h5, nsGkAtoms::h6,
1729 nsGkAtoms::header, nsGkAtoms::hgroup, nsGkAtoms::hr, nsGkAtoms::li,
1730 nsGkAtoms::listing, nsGkAtoms::menu, nsGkAtoms::nav, nsGkAtoms::ol,
1731 nsGkAtoms::p, nsGkAtoms::pre, nsGkAtoms::section, nsGkAtoms::table,
1732 nsGkAtoms::ul, nsGkAtoms::xmp);
1735 /* static */
1736 bool nsContentUtils::ParseIntMarginValue(const nsAString& aString,
1737 nsIntMargin& result) {
1738 nsAutoString marginStr(aString);
1739 marginStr.CompressWhitespace(true, true);
1740 if (marginStr.IsEmpty()) {
1741 return false;
1744 int32_t start = 0, end = 0;
1745 for (int count = 0; count < 4; count++) {
1746 if ((uint32_t)end >= marginStr.Length()) return false;
1748 // top, right, bottom, left
1749 if (count < 3)
1750 end = Substring(marginStr, start).FindChar(',');
1751 else
1752 end = Substring(marginStr, start).Length();
1754 if (end <= 0) return false;
1756 nsresult ec;
1757 int32_t val = nsString(Substring(marginStr, start, end)).ToInteger(&ec);
1758 if (NS_FAILED(ec)) return false;
1760 switch (count) {
1761 case 0:
1762 result.top = val;
1763 break;
1764 case 1:
1765 result.right = val;
1766 break;
1767 case 2:
1768 result.bottom = val;
1769 break;
1770 case 3:
1771 result.left = val;
1772 break;
1774 start += end + 1;
1776 return true;
1779 // static
1780 int32_t nsContentUtils::ParseLegacyFontSize(const nsAString& aValue) {
1781 nsAString::const_iterator iter, end;
1782 aValue.BeginReading(iter);
1783 aValue.EndReading(end);
1785 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1786 ++iter;
1789 if (iter == end) {
1790 return 0;
1793 bool relative = false;
1794 bool negate = false;
1795 if (*iter == char16_t('-')) {
1796 relative = true;
1797 negate = true;
1798 ++iter;
1799 } else if (*iter == char16_t('+')) {
1800 relative = true;
1801 ++iter;
1804 if (iter == end || *iter < char16_t('0') || *iter > char16_t('9')) {
1805 return 0;
1808 // We don't have to worry about overflow, since we can bail out as soon as
1809 // we're bigger than 7.
1810 int32_t value = 0;
1811 while (iter != end && *iter >= char16_t('0') && *iter <= char16_t('9')) {
1812 value = 10 * value + (*iter - char16_t('0'));
1813 if (value >= 7) {
1814 break;
1816 ++iter;
1819 if (relative) {
1820 if (negate) {
1821 value = 3 - value;
1822 } else {
1823 value = 3 + value;
1827 return clamped(value, 1, 7);
1830 /* static */
1831 void nsContentUtils::GetOfflineAppManifest(Document* aDocument, nsIURI** aURI) {
1832 MOZ_ASSERT(NS_IsMainThread());
1833 MOZ_ASSERT(aDocument);
1834 *aURI = nullptr;
1836 if (aDocument->GetController().isSome()) {
1837 return;
1840 Element* docElement = aDocument->GetRootElement();
1841 if (!docElement) {
1842 return;
1845 nsAutoString manifestSpec;
1846 docElement->GetAttr(nsGkAtoms::manifest, manifestSpec);
1848 // Manifest URIs can't have fragment identifiers.
1849 if (manifestSpec.IsEmpty() || manifestSpec.Contains('#')) {
1850 return;
1853 nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec, aDocument,
1854 aDocument->GetDocBaseURI());
1857 /* static */
1858 bool nsContentUtils::OfflineAppAllowed(nsIURI* aURI) { return false; }
1860 /* static */
1861 bool nsContentUtils::OfflineAppAllowed(nsIPrincipal* aPrincipal) {
1862 return false;
1864 // Static
1865 bool nsContentUtils::IsErrorPage(nsIURI* aURI) {
1866 if (!aURI) {
1867 return false;
1870 if (!aURI->SchemeIs("about")) {
1871 return false;
1874 nsAutoCString name;
1875 nsresult rv = NS_GetAboutModuleName(aURI, name);
1876 NS_ENSURE_SUCCESS(rv, false);
1878 return name.EqualsLiteral("certerror") || name.EqualsLiteral("neterror") ||
1879 name.EqualsLiteral("blocked");
1882 // static
1883 void nsContentUtils::Shutdown() {
1884 sInitialized = false;
1886 nsHTMLTags::ReleaseTable();
1888 NS_IF_RELEASE(sContentPolicyService);
1889 sTriedToGetContentPolicy = false;
1890 for (StaticRefPtr<nsIStringBundle>& bundle : sStringBundles) {
1891 bundle = nullptr;
1894 NS_IF_RELEASE(sStringBundleService);
1895 NS_IF_RELEASE(sConsoleService);
1896 NS_IF_RELEASE(sXPConnect);
1897 NS_IF_RELEASE(sSecurityManager);
1898 NS_IF_RELEASE(sSystemPrincipal);
1899 NS_IF_RELEASE(sNullSubjectPrincipal);
1900 NS_IF_RELEASE(sIOService);
1902 sBidiKeyboard = nullptr;
1904 delete sAtomEventTable;
1905 sAtomEventTable = nullptr;
1906 delete sStringEventTable;
1907 sStringEventTable = nullptr;
1908 delete sUserDefinedEvents;
1909 sUserDefinedEvents = nullptr;
1911 if (sEventListenerManagersHash) {
1912 NS_ASSERTION(sEventListenerManagersHash->EntryCount() == 0,
1913 "Event listener manager hash not empty at shutdown!");
1915 // See comment above.
1917 // However, we have to handle this table differently. If it still
1918 // has entries, we want to leak it too, so that we can keep it alive
1919 // in case any elements are destroyed. Because if they are, we need
1920 // their event listener managers to be destroyed too, or otherwise
1921 // it could leave dangling references in DOMClassInfo's preserved
1922 // wrapper table.
1924 if (sEventListenerManagersHash->EntryCount() == 0) {
1925 delete sEventListenerManagersHash;
1926 sEventListenerManagersHash = nullptr;
1930 if (sDOMArenaHashtable) {
1931 MOZ_ASSERT(sDOMArenaHashtable->Count() == 0);
1932 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
1933 delete sDOMArenaHashtable;
1934 sDOMArenaHashtable = nullptr;
1937 NS_ASSERTION(!sBlockedScriptRunners || sBlockedScriptRunners->Length() == 0,
1938 "How'd this happen?");
1939 delete sBlockedScriptRunners;
1940 sBlockedScriptRunners = nullptr;
1942 delete sShiftText;
1943 sShiftText = nullptr;
1944 delete sControlText;
1945 sControlText = nullptr;
1946 delete sMetaText;
1947 sMetaText = nullptr;
1948 delete sOSText;
1949 sOSText = nullptr;
1950 delete sAltText;
1951 sAltText = nullptr;
1952 delete sModifierSeparator;
1953 sModifierSeparator = nullptr;
1955 delete sJSScriptBytecodeMimeType;
1956 sJSScriptBytecodeMimeType = nullptr;
1958 delete sJSModuleBytecodeMimeType;
1959 sJSModuleBytecodeMimeType = nullptr;
1961 NS_IF_RELEASE(sSameOriginChecker);
1963 if (sUserInteractionObserver) {
1964 sUserInteractionObserver->Shutdown();
1965 NS_RELEASE(sUserInteractionObserver);
1968 for (const auto& pref : kRfpPrefs) {
1969 Preferences::UnregisterCallback(RecomputeResistFingerprintingAllDocs, pref);
1972 TextControlState::Shutdown();
1976 * Checks whether two nodes come from the same origin. aTrustedNode is
1977 * considered 'safe' in that a user can operate on it.
1979 // static
1980 nsresult nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
1981 const nsINode* unTrustedNode) {
1982 MOZ_ASSERT(aTrustedNode);
1983 MOZ_ASSERT(unTrustedNode);
1986 * Get hold of each node's principal
1989 nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
1990 nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
1992 if (trustedPrincipal == unTrustedPrincipal) {
1993 return NS_OK;
1996 bool equal;
1997 // XXXbz should we actually have a Subsumes() check here instead? Or perhaps
1998 // a separate method for that, with callers using one or the other?
1999 if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
2000 !equal) {
2001 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
2004 return NS_OK;
2007 // static
2008 bool nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
2009 nsIPrincipal* aPrincipal) {
2010 bool subsumes;
2011 nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
2012 NS_ENSURE_SUCCESS(rv, false);
2014 if (subsumes) {
2015 return true;
2018 // The subject doesn't subsume aPrincipal. Allow access only if the subject
2019 // is chrome.
2020 return IsCallerChrome();
2023 // static
2024 bool nsContentUtils::CanCallerAccess(const nsINode* aNode) {
2025 nsIPrincipal* subject = SubjectPrincipal();
2026 if (subject->IsSystemPrincipal()) {
2027 return true;
2030 if (aNode->ChromeOnlyAccess()) {
2031 return false;
2034 return CanCallerAccess(subject, aNode->NodePrincipal());
2037 // static
2038 bool nsContentUtils::CanCallerAccess(nsPIDOMWindowInner* aWindow) {
2039 nsCOMPtr<nsIScriptObjectPrincipal> scriptObject = do_QueryInterface(aWindow);
2040 NS_ENSURE_TRUE(scriptObject, false);
2042 return CanCallerAccess(SubjectPrincipal(), scriptObject->GetPrincipal());
2045 // static
2046 bool nsContentUtils::PrincipalHasPermission(nsIPrincipal& aPrincipal,
2047 const nsAtom* aPerm) {
2048 // Chrome gets access by default.
2049 if (aPrincipal.IsSystemPrincipal()) {
2050 return true;
2053 // Otherwise, only allow if caller is an addon with the permission.
2054 return BasePrincipal::Cast(aPrincipal).AddonHasPermission(aPerm);
2057 // static
2058 bool nsContentUtils::CallerHasPermission(JSContext* aCx, const nsAtom* aPerm) {
2059 return PrincipalHasPermission(*SubjectPrincipal(aCx), aPerm);
2062 // static
2063 nsIPrincipal* nsContentUtils::GetAttrTriggeringPrincipal(
2064 nsIContent* aContent, const nsAString& aAttrValue,
2065 nsIPrincipal* aSubjectPrincipal) {
2066 nsIPrincipal* contentPrin = aContent ? aContent->NodePrincipal() : nullptr;
2068 // If the subject principal is the same as the content principal, or no
2069 // explicit subject principal was provided, we don't need to do any further
2070 // checks. Just return the content principal.
2071 if (contentPrin == aSubjectPrincipal || !aSubjectPrincipal) {
2072 return contentPrin;
2075 // Only use the subject principal if the URL string we are going to end up
2076 // fetching is under the control of that principal, which is never the case
2077 // for relative URLs.
2078 if (aAttrValue.IsEmpty() ||
2079 !IsAbsoluteURL(NS_ConvertUTF16toUTF8(aAttrValue))) {
2080 return contentPrin;
2083 // Only use the subject principal as the attr triggering principal if it
2084 // should override the CSP of the node's principal.
2085 if (BasePrincipal::Cast(aSubjectPrincipal)->OverridesCSP(contentPrin)) {
2086 return aSubjectPrincipal;
2089 return contentPrin;
2092 // static
2093 bool nsContentUtils::IsAbsoluteURL(const nsACString& aURL) {
2094 nsAutoCString scheme;
2095 if (NS_FAILED(net_ExtractURLScheme(aURL, scheme))) {
2096 // If we can't extract a scheme, it's not an absolute URL.
2097 return false;
2100 // If it parses as an absolute StandardURL, it's definitely an absolute URL,
2101 // so no need to check with the IO service.
2102 if (net_IsAbsoluteURL(aURL)) {
2103 return true;
2106 uint32_t flags;
2107 if (NS_SUCCEEDED(sIOService->GetProtocolFlags(scheme.get(), &flags))) {
2108 return flags & nsIProtocolHandler::URI_NORELATIVE;
2111 return false;
2114 // static
2115 bool nsContentUtils::InProlog(nsINode* aNode) {
2116 MOZ_ASSERT(aNode, "missing node to nsContentUtils::InProlog");
2118 nsINode* parent = aNode->GetParentNode();
2119 if (!parent || !parent->IsDocument()) {
2120 return false;
2123 const Document* doc = parent->AsDocument();
2124 const nsIContent* root = doc->GetRootElement();
2125 if (!root) {
2126 return true;
2128 const Maybe<uint32_t> indexOfNode = doc->ComputeIndexOf(aNode);
2129 const Maybe<uint32_t> indexOfRoot = doc->ComputeIndexOf(root);
2130 if (MOZ_LIKELY(indexOfNode.isSome() && indexOfRoot.isSome())) {
2131 return *indexOfNode < *indexOfRoot;
2133 // XXX Keep the odd traditional behavior for now.
2134 return indexOfNode.isNothing() && indexOfRoot.isSome();
2137 bool nsContentUtils::IsCallerChrome() {
2138 MOZ_ASSERT(NS_IsMainThread());
2139 return SubjectPrincipal() == sSystemPrincipal;
2142 #ifdef FUZZING
2143 bool nsContentUtils::IsFuzzingEnabled() {
2144 return StaticPrefs::fuzzing_enabled();
2146 #endif
2148 /* static */
2149 bool nsContentUtils::IsCallerChromeOrElementTransformGettersEnabled(
2150 JSContext* aCx, JSObject*) {
2151 return ThreadsafeIsSystemCaller(aCx) ||
2152 StaticPrefs::dom_element_transform_getters_enabled();
2155 // Older Should RFP Functions ----------------------------------
2157 /* static */
2158 bool nsContentUtils::ShouldResistFingerprinting(RFPTarget aTarget) {
2159 return nsRFPService::IsRFPEnabledFor(aTarget);
2162 /* static */
2163 bool nsContentUtils::ShouldResistFingerprinting(nsIGlobalObject* aGlobalObject,
2164 RFPTarget aTarget) {
2165 if (!aGlobalObject) {
2166 return ShouldResistFingerprinting("Null Object", aTarget);
2168 return aGlobalObject->ShouldResistFingerprinting(aTarget);
2171 // Newer Should RFP Functions ----------------------------------
2172 // Utilities ---------------------------------------------------
2174 inline void LogDomainAndPrefList(const char* urlType,
2175 const char* exemptedDomainsPrefName,
2176 nsAutoCString& url, bool isExemptDomain) {
2177 nsAutoCString list;
2178 Preferences::GetCString(exemptedDomainsPrefName, list);
2179 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2180 ("%s \"%s\" is %s the exempt list \"%s\"", urlType,
2181 PromiseFlatCString(url).get(), isExemptDomain ? "in" : "NOT in",
2182 PromiseFlatCString(list).get()));
2185 inline already_AddRefed<nsICookieJarSettings> GetCookieJarSettings(
2186 nsILoadInfo* aLoadInfo) {
2187 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
2188 nsresult rv =
2189 aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
2190 if (rv == NS_ERROR_NOT_IMPLEMENTED) {
2191 // The TRRLoadInfo in particular does not implement this method
2192 // In that instance. We will return false and let other code decide if
2193 // we shouldRFP for this connection
2194 return nullptr;
2196 if (NS_WARN_IF(NS_FAILED(rv))) {
2197 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2198 ("Called CookieJarSettingsSaysShouldResistFingerprinting but the "
2199 "loadinfo's CookieJarSettings couldn't be retrieved"));
2200 return nullptr;
2203 MOZ_ASSERT(cookieJarSettings);
2204 return cookieJarSettings.forget();
2207 bool ETPSaysShouldNotResistFingerprinting(nsIChannel* aChannel,
2208 nsILoadInfo* aLoadInfo) {
2209 // A positive return from this function should always be obeyed.
2210 // A negative return means we should keep checking things.
2212 // We do not want this check to apply to RFP, only to FPP
2213 // There is one problematic combination of prefs; however:
2214 // If RFP is enabled in PBMode only and FPP is enabled globally
2215 // (so, in non-PBM mode) - we need to know if we're in PBMode or not.
2216 // But that's kind of expensive and we'd like to avoid it if we
2217 // don't have to, so special-case that scenario
2218 if (StaticPrefs::privacy_fingerprintingProtection_DoNotUseDirectly() &&
2219 !StaticPrefs::privacy_resistFingerprinting_DoNotUseDirectly() &&
2220 StaticPrefs::privacy_resistFingerprinting_pbmode_DoNotUseDirectly()) {
2221 if (NS_UsePrivateBrowsing(aChannel)) {
2222 // In PBM (where RFP is enabled) do not exempt based on the ETP toggle
2223 return false;
2225 } else if (StaticPrefs::privacy_resistFingerprinting_DoNotUseDirectly() ||
2226 StaticPrefs::
2227 privacy_resistFingerprinting_pbmode_DoNotUseDirectly()) {
2228 // In RFP, never use the ETP toggle to exempt.
2229 // We can safely return false here even if we are not in PBM mode
2230 // and RFP_pbmode is enabled because we will later see that and
2231 // return false from the ShouldRFP function entirely.
2232 return false;
2235 nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
2236 GetCookieJarSettings(aLoadInfo);
2237 if (!cookieJarSettings) {
2238 return false;
2241 return ContentBlockingAllowList::Check(cookieJarSettings);
2244 inline bool CookieJarSettingsSaysShouldResistFingerprinting(
2245 nsILoadInfo* aLoadInfo) {
2246 // A positive return from this function should always be obeyed.
2247 // A negative return means we should keep checking things.
2249 nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
2250 GetCookieJarSettings(aLoadInfo);
2251 if (!cookieJarSettings) {
2252 return false;
2254 return cookieJarSettings->GetShouldResistFingerprinting();
2257 inline bool SchemeSaysShouldNotResistFingerprinting(nsIURI* aURI) {
2258 return aURI->SchemeIs("chrome") || aURI->SchemeIs("resource") ||
2259 aURI->SchemeIs("view-source") || aURI->SchemeIs("moz-extension") ||
2260 (aURI->SchemeIs("about") && !NS_IsContentAccessibleAboutURI(aURI));
2263 inline bool SchemeSaysShouldNotResistFingerprinting(nsIPrincipal* aPrincipal) {
2264 if (aPrincipal->SchemeIs("chrome") || aPrincipal->SchemeIs("resource") ||
2265 aPrincipal->SchemeIs("view-source") ||
2266 aPrincipal->SchemeIs("moz-extension")) {
2267 return true;
2270 if (!aPrincipal->SchemeIs("about")) {
2271 return false;
2274 bool isContentAccessibleAboutURI;
2275 Unused << aPrincipal->IsContentAccessibleAboutURI(
2276 &isContentAccessibleAboutURI);
2277 return !isContentAccessibleAboutURI;
2280 const char* kExemptedDomainsPrefName =
2281 "privacy.resistFingerprinting.exemptedDomains";
2283 inline bool PartionKeyIsAlsoExempted(
2284 const mozilla::OriginAttributes& aOriginAttributes) {
2285 // If we've gotten here we have (probably) passed the CookieJarSettings
2286 // check that would tell us that if we _are_ a subdocument, then we are on
2287 // an exempted top-level domain and we should see if we ourselves are
2288 // exempted. But we may have gotten here because we directly called the
2289 // _dangerous function and we haven't done that check, but we _were_
2290 // instatiated from a state where we could have been partitioned.
2291 // So perform this last-ditch check for that scenario.
2292 // We arbitrarily use https as the scheme, but it doesn't matter.
2293 nsresult rv = NS_ERROR_NOT_INITIALIZED;
2294 nsCOMPtr<nsIURI> uri;
2295 if (StaticPrefs::privacy_firstparty_isolate() &&
2296 !aOriginAttributes.mFirstPartyDomain.IsEmpty()) {
2297 rv = NS_NewURI(getter_AddRefs(uri),
2298 u"https://"_ns + aOriginAttributes.mFirstPartyDomain);
2299 } else if (!aOriginAttributes.mPartitionKey.IsEmpty()) {
2300 rv = NS_NewURI(getter_AddRefs(uri),
2301 u"https://"_ns + aOriginAttributes.mPartitionKey);
2304 if (!NS_FAILED(rv)) {
2305 bool isExemptPartitionKey =
2306 nsContentUtils::IsURIInPrefList(uri, kExemptedDomainsPrefName);
2307 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2308 mozilla::LogLevel::Debug)) {
2309 nsAutoCString url;
2310 uri->GetHost(url);
2311 LogDomainAndPrefList("Partition Key", kExemptedDomainsPrefName, url,
2312 isExemptPartitionKey);
2314 return isExemptPartitionKey;
2316 return true;
2319 // Functions ---------------------------------------------------
2321 /* static */
2322 bool nsContentUtils::ShouldResistFingerprinting(const char* aJustification,
2323 RFPTarget aTarget) {
2324 // See comment in header file for information about usage
2325 return ShouldResistFingerprinting(aTarget);
2328 /* static */
2329 bool nsContentUtils::ShouldResistFingerprinting(CallerType aCallerType,
2330 nsIGlobalObject* aGlobalObject,
2331 RFPTarget aTarget) {
2332 if (aCallerType == CallerType::System) {
2333 return false;
2335 return ShouldResistFingerprinting(aGlobalObject, aTarget);
2338 bool nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell,
2339 RFPTarget aTarget) {
2340 if (!aDocShell) {
2341 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2342 ("Called nsContentUtils::ShouldResistFingerprinting(nsIDocShell*) "
2343 "with NULL docshell"));
2344 return ShouldResistFingerprinting("Null Object", aTarget);
2346 Document* doc = aDocShell->GetDocument();
2347 if (!doc) {
2348 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2349 ("Called nsContentUtils::ShouldResistFingerprinting(nsIDocShell*) "
2350 "with NULL doc"));
2351 return ShouldResistFingerprinting(aTarget);
2353 return doc->ShouldResistFingerprinting(aTarget);
2356 /* static */
2357 bool nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel,
2358 RFPTarget aTarget) {
2359 if (!aChannel) {
2360 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2361 ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2362 "aChannel) with NULL channel"));
2363 return ShouldResistFingerprinting("Null Object", aTarget);
2366 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
2367 if (!loadInfo) {
2368 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2369 ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2370 "aChannel) but the channel's loadinfo was NULL"));
2371 return ShouldResistFingerprinting("Null Object", aTarget);
2374 // With this check, we can ensure that the prefs and target say yes, so only
2375 // an exemption would cause us to return false.
2376 if (!ShouldResistFingerprinting("Positive return check", aTarget)) {
2377 return false;
2380 if (ETPSaysShouldNotResistFingerprinting(aChannel, loadInfo)) {
2381 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2382 ("Inside ShouldResistFingerprinting(nsIChannel*)"
2383 " ETPSaysShouldNotResistFingerprinting said false"));
2384 return false;
2387 if (CookieJarSettingsSaysShouldResistFingerprinting(loadInfo)) {
2388 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2389 ("Inside ShouldResistFingerprinting(nsIChannel*)"
2390 " CookieJarSettingsSaysShouldResistFingerprinting said true"));
2391 return true;
2394 // Document types have no loading principal. Subdocument types do have a
2395 // loading principal, but it is the loading principal of the parent
2396 // document; not the subdocument.
2397 auto contentType = loadInfo->GetExternalContentPolicyType();
2398 // Case 1: Document or Subdocument load
2399 if (contentType == ExtContentPolicy::TYPE_DOCUMENT ||
2400 contentType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
2401 nsCOMPtr<nsIURI> channelURI;
2402 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
2403 MOZ_ASSERT(
2404 NS_SUCCEEDED(rv),
2405 "Failed to get URI in "
2406 "nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel)");
2407 // this check is to ensure that we do not crash in non-debug builds.
2408 if (NS_FAILED(rv)) {
2409 return true;
2412 #if 0
2413 if (loadInfo->GetExternalContentPolicyType() == ExtContentPolicy::TYPE_SUBDOCUMENT) {
2414 nsCOMPtr<nsIURI> channelURI;
2415 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
2416 nsAutoCString channelSpec;
2417 channelURI->GetSpec(channelSpec);
2419 if (!loadInfo->GetLoadingPrincipal()) {
2420 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2421 ("Sub Document Type. FinalChannelURI is %s, Loading Principal is NULL\n",
2422 channelSpec.get()));
2424 } else {
2425 nsAutoCString loadingPrincipalSpec;
2426 loadInfo->GetLoadingPrincipal()->GetOrigin(loadingPrincipalSpec);
2428 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2429 ("Sub Document Type. FinalChannelURI is %s, Loading Principal Origin is %s\n",
2430 channelSpec.get(), loadingPrincipalSpec.get()));
2434 #endif
2436 return ShouldResistFingerprinting_dangerous(
2437 channelURI, loadInfo->GetOriginAttributes(), "Internal Call", aTarget);
2440 // Case 2: Subresource Load
2441 // Because this code is only used for subresource loads, this
2442 // will check the parent's principal
2443 nsIPrincipal* principal = loadInfo->GetLoadingPrincipal();
2445 MOZ_ASSERT_IF(principal && !principal->IsSystemPrincipal() &&
2446 !principal->GetIsAddonOrExpandedAddonPrincipal(),
2447 BasePrincipal::Cast(principal)->OriginAttributesRef() ==
2448 loadInfo->GetOriginAttributes());
2449 return ShouldResistFingerprinting_dangerous(principal, "Internal Call",
2450 aTarget);
2453 /* static */
2454 bool nsContentUtils::ShouldResistFingerprinting_dangerous(
2455 nsIURI* aURI, const mozilla::OriginAttributes& aOriginAttributes,
2456 const char* aJustification, RFPTarget aTarget) {
2457 // With this check, we can ensure that the prefs and target say yes, so only
2458 // an exemption would cause us to return false.
2459 if (!ShouldResistFingerprinting("Positive return check", aTarget)) {
2460 return false;
2463 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2464 ("Inside ShouldResistFingerprinting_dangerous(nsIURI*,"
2465 " OriginAttributes) and the URI is %s",
2466 aURI->GetSpecOrDefault().get()));
2468 if (!StaticPrefs::privacy_resistFingerprinting_DoNotUseDirectly() &&
2469 !StaticPrefs::privacy_fingerprintingProtection_DoNotUseDirectly()) {
2470 // If neither of the 'regular' RFP prefs are set, then one (or both)
2471 // of the PBM-Only prefs are set (or we would have failed the
2472 // Positive return check.) Therefore, if we are not in PBM, return false
2473 if (aOriginAttributes.mPrivateBrowsingId == 0) {
2474 return false;
2478 // Exclude internal schemes and web extensions
2479 if (SchemeSaysShouldNotResistFingerprinting(aURI)) {
2480 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2481 ("Inside ShouldResistFingerprinting(nsIURI*)"
2482 " SchemeSaysShouldNotResistFingerprinting said false"));
2483 return false;
2486 bool isExemptDomain = false;
2487 nsAutoCString list;
2488 Preferences::GetCString(kExemptedDomainsPrefName, list);
2489 ToLowerCase(list);
2490 isExemptDomain = IsURIInList(aURI, list);
2492 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2493 mozilla::LogLevel::Debug)) {
2494 nsAutoCString url;
2495 aURI->GetHost(url);
2496 LogDomainAndPrefList("URI", kExemptedDomainsPrefName, url, isExemptDomain);
2499 if (isExemptDomain) {
2500 isExemptDomain &= PartionKeyIsAlsoExempted(aOriginAttributes);
2503 return !isExemptDomain;
2506 /* static */
2507 bool nsContentUtils::ShouldResistFingerprinting_dangerous(
2508 nsIPrincipal* aPrincipal, const char* aJustification, RFPTarget aTarget) {
2509 if (!aPrincipal) {
2510 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2511 ("Called nsContentUtils::ShouldResistFingerprinting(nsILoadInfo* "
2512 "aChannel) but the loadinfo's loadingprincipal was NULL"));
2513 return ShouldResistFingerprinting("Null object", aTarget);
2516 // With this check, we can ensure that the prefs and target say yes, so only
2517 // an exemption would cause us to return false.
2518 if (!ShouldResistFingerprinting("Positive return check", aTarget)) {
2519 return false;
2522 if (aPrincipal->IsSystemPrincipal()) {
2523 return false;
2526 auto originAttributes =
2527 BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
2528 if (!StaticPrefs::privacy_resistFingerprinting_DoNotUseDirectly() &&
2529 !StaticPrefs::privacy_fingerprintingProtection_DoNotUseDirectly()) {
2530 // If neither of the 'regular' RFP prefs are set, then one (or both)
2531 // of the PBM-Only prefs are set (or we would have failed the
2532 // Positive return check.) Therefore, if we are not in PBM, return false
2533 if (originAttributes.mPrivateBrowsingId == 0) {
2534 return false;
2538 // Exclude internal schemes and web extensions
2539 if (SchemeSaysShouldNotResistFingerprinting(aPrincipal)) {
2540 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2541 ("Inside ShouldResistFingerprinting(nsIPrincipal*)"
2542 " SchemeSaysShouldNotResistFingerprinting said false"));
2543 return false;
2546 // Web extension principals are also excluded
2547 if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
2548 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2549 ("Inside ShouldResistFingerprinting_dangerous(nsIPrincipal*)"
2550 " and AddonPolicy said false"));
2551 return false;
2554 bool isExemptDomain = false;
2555 aPrincipal->IsURIInPrefList(kExemptedDomainsPrefName, &isExemptDomain);
2557 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2558 mozilla::LogLevel::Debug)) {
2559 nsAutoCString origin;
2560 aPrincipal->GetOrigin(origin);
2561 LogDomainAndPrefList("URI", kExemptedDomainsPrefName, origin,
2562 isExemptDomain);
2565 if (isExemptDomain) {
2566 isExemptDomain &= PartionKeyIsAlsoExempted(originAttributes);
2569 return !isExemptDomain;
2572 // --------------------------------------------------------------------
2574 /* static */
2575 void nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
2576 int32_t aChromeWidth, int32_t aChromeHeight, int32_t aScreenWidth,
2577 int32_t aScreenHeight, int32_t aInputWidth, int32_t aInputHeight,
2578 bool aSetOuterWidth, bool aSetOuterHeight, int32_t* aOutputWidth,
2579 int32_t* aOutputHeight) {
2580 MOZ_ASSERT(aOutputWidth);
2581 MOZ_ASSERT(aOutputHeight);
2583 int32_t availContentWidth = 0;
2584 int32_t availContentHeight = 0;
2586 availContentWidth = std::min(StaticPrefs::privacy_window_maxInnerWidth(),
2587 aScreenWidth - aChromeWidth);
2588 #ifdef MOZ_WIDGET_GTK
2589 // In the GTK window, it will not report outside system decorations
2590 // when we get available window size, see Bug 581863. So, we leave a
2591 // 40 pixels space for them when calculating the available content
2592 // height. It is not necessary for the width since the content width
2593 // is usually pretty much the same as the chrome width.
2594 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2595 (-40 + aScreenHeight) - aChromeHeight);
2596 #else
2597 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2598 aScreenHeight - aChromeHeight);
2599 #endif
2601 // Ideally, we'd like to round window size to 1000x1000, but the
2602 // screen space could be too small to accommodate this size in some
2603 // cases. If it happens, we would round the window size to the nearest
2604 // 200x100.
2605 availContentWidth = availContentWidth - (availContentWidth % 200);
2606 availContentHeight = availContentHeight - (availContentHeight % 100);
2608 // If aIsOuter is true, we are setting the outer window. So we
2609 // have to consider the chrome UI.
2610 int32_t chromeOffsetWidth = aSetOuterWidth ? aChromeWidth : 0;
2611 int32_t chromeOffsetHeight = aSetOuterHeight ? aChromeHeight : 0;
2612 int32_t resultWidth = 0, resultHeight = 0;
2614 // if the original size is greater than the maximum available size, we set
2615 // it to the maximum size. And if the original value is less than the
2616 // minimum rounded size, we set it to the minimum 200x100.
2617 if (aInputWidth > (availContentWidth + chromeOffsetWidth)) {
2618 resultWidth = availContentWidth + chromeOffsetWidth;
2619 } else if (aInputWidth < (200 + chromeOffsetWidth)) {
2620 resultWidth = 200 + chromeOffsetWidth;
2621 } else {
2622 // Otherwise, we round the window to the nearest upper rounded 200x100.
2623 resultWidth = NSToIntCeil((aInputWidth - chromeOffsetWidth) / 200.0) * 200 +
2624 chromeOffsetWidth;
2627 if (aInputHeight > (availContentHeight + chromeOffsetHeight)) {
2628 resultHeight = availContentHeight + chromeOffsetHeight;
2629 } else if (aInputHeight < (100 + chromeOffsetHeight)) {
2630 resultHeight = 100 + chromeOffsetHeight;
2631 } else {
2632 resultHeight =
2633 NSToIntCeil((aInputHeight - chromeOffsetHeight) / 100.0) * 100 +
2634 chromeOffsetHeight;
2637 *aOutputWidth = resultWidth;
2638 *aOutputHeight = resultHeight;
2641 bool nsContentUtils::ThreadsafeIsCallerChrome() {
2642 return NS_IsMainThread() ? IsCallerChrome()
2643 : IsCurrentThreadRunningChromeWorker();
2646 bool nsContentUtils::IsCallerUAWidget() {
2647 JSContext* cx = GetCurrentJSContext();
2648 if (!cx) {
2649 return false;
2652 JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
2653 if (!realm) {
2654 return false;
2657 return xpc::IsUAWidgetScope(realm);
2660 bool nsContentUtils::IsSystemCaller(JSContext* aCx) {
2661 // Note that SubjectPrincipal() assumes we are in a compartment here.
2662 return SubjectPrincipal(aCx) == sSystemPrincipal;
2665 bool nsContentUtils::ThreadsafeIsSystemCaller(JSContext* aCx) {
2666 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
2667 MOZ_ASSERT(ccjscx->Context() == aCx);
2669 return ccjscx->IsSystemCaller();
2672 // static
2673 bool nsContentUtils::LookupBindingMember(
2674 JSContext* aCx, nsIContent* aContent, JS::Handle<jsid> aId,
2675 JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
2676 return true;
2679 nsINode* nsContentUtils::GetNearestInProcessCrossDocParentNode(
2680 nsINode* aChild) {
2681 if (aChild->IsDocument()) {
2682 for (BrowsingContext* bc = aChild->AsDocument()->GetBrowsingContext(); bc;
2683 bc = bc->GetParent()) {
2684 if (bc->GetEmbedderElement()) {
2685 return bc->GetEmbedderElement();
2688 return nullptr;
2691 nsINode* parent = aChild->GetParentNode();
2692 if (parent && parent->IsContent() && aChild->IsContent()) {
2693 parent = aChild->AsContent()->GetFlattenedTreeParent();
2696 return parent;
2699 bool nsContentUtils::ContentIsHostIncludingDescendantOf(
2700 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2701 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2702 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2704 do {
2705 if (aPossibleDescendant == aPossibleAncestor) return true;
2706 if (aPossibleDescendant->IsDocumentFragment()) {
2707 aPossibleDescendant =
2708 aPossibleDescendant->AsDocumentFragment()->GetHost();
2709 } else {
2710 aPossibleDescendant = aPossibleDescendant->GetParentNode();
2712 } while (aPossibleDescendant);
2714 return false;
2717 // static
2718 bool nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
2719 nsINode* aPossibleAncestor) {
2720 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2721 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2723 do {
2724 if (aPossibleDescendant == aPossibleAncestor) {
2725 return true;
2728 aPossibleDescendant =
2729 GetNearestInProcessCrossDocParentNode(aPossibleDescendant);
2730 } while (aPossibleDescendant);
2732 return false;
2735 // static
2736 bool nsContentUtils::ContentIsFlattenedTreeDescendantOf(
2737 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2738 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2739 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2741 do {
2742 if (aPossibleDescendant == aPossibleAncestor) {
2743 return true;
2745 aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
2746 } while (aPossibleDescendant);
2748 return false;
2751 // static
2752 bool nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
2753 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2754 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2755 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2757 do {
2758 if (aPossibleDescendant == aPossibleAncestor) {
2759 return true;
2761 aPossibleDescendant =
2762 aPossibleDescendant->GetFlattenedTreeParentNodeForStyle();
2763 } while (aPossibleDescendant);
2765 return false;
2768 // static
2769 nsINode* nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB) {
2770 while (true && aTargetA) {
2771 // If A's root is not a shadow root...
2772 nsINode* root = aTargetA->SubtreeRoot();
2773 if (!root->IsShadowRoot()) {
2774 // ...then return A.
2775 return aTargetA;
2778 // or A's root is a shadow-including inclusive ancestor of B...
2779 if (aTargetB->IsShadowIncludingInclusiveDescendantOf(root)) {
2780 // ...then return A.
2781 return aTargetA;
2784 aTargetA = ShadowRoot::FromNode(root)->GetHost();
2787 return nullptr;
2790 // static
2791 Element* nsContentUtils::GetAnElementForTiming(Element* aTarget,
2792 const Document* aDocument,
2793 nsIGlobalObject* aGlobal) {
2794 if (!aTarget->IsInComposedDoc()) {
2795 return nullptr;
2798 if (!aDocument) {
2799 nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(aGlobal);
2800 if (!inner) {
2801 return nullptr;
2803 aDocument = inner->GetExtantDoc();
2806 MOZ_ASSERT(aDocument);
2808 if (aTarget->GetUncomposedDocOrConnectedShadowRoot() != aDocument ||
2809 !aDocument->IsCurrentActiveDocument()) {
2810 return nullptr;
2813 return aTarget;
2816 // static
2817 nsresult nsContentUtils::GetInclusiveAncestors(nsINode* aNode,
2818 nsTArray<nsINode*>& aArray) {
2819 while (aNode) {
2820 aArray.AppendElement(aNode);
2821 aNode = aNode->GetParentNode();
2823 return NS_OK;
2826 // static
2827 nsresult nsContentUtils::GetInclusiveAncestorsAndOffsets(
2828 nsINode* aNode, uint32_t aOffset, nsTArray<nsIContent*>* aAncestorNodes,
2829 nsTArray<Maybe<uint32_t>>* aAncestorOffsets) {
2830 NS_ENSURE_ARG_POINTER(aNode);
2832 if (!aNode->IsContent()) {
2833 return NS_ERROR_FAILURE;
2835 nsIContent* content = aNode->AsContent();
2837 if (!aAncestorNodes->IsEmpty()) {
2838 NS_WARNING("aAncestorNodes is not empty");
2839 aAncestorNodes->Clear();
2842 if (!aAncestorOffsets->IsEmpty()) {
2843 NS_WARNING("aAncestorOffsets is not empty");
2844 aAncestorOffsets->Clear();
2847 // insert the node itself
2848 aAncestorNodes->AppendElement(content);
2849 aAncestorOffsets->AppendElement(Some(aOffset));
2851 // insert all the ancestors
2852 nsIContent* child = content;
2853 nsIContent* parent = child->GetParent();
2854 while (parent) {
2855 aAncestorNodes->AppendElement(parent);
2856 aAncestorOffsets->AppendElement(parent->ComputeIndexOf(child));
2857 child = parent;
2858 parent = parent->GetParent();
2861 return NS_OK;
2864 template <typename Node, typename GetParentFunc>
2865 static Node* GetCommonAncestorInternal(Node* aNode1, Node* aNode2,
2866 GetParentFunc aGetParentFunc) {
2867 MOZ_ASSERT(aNode1 != aNode2);
2869 // Build the chain of parents
2870 AutoTArray<Node*, 30> parents1, parents2;
2871 do {
2872 parents1.AppendElement(aNode1);
2873 aNode1 = aGetParentFunc(aNode1);
2874 } while (aNode1);
2875 do {
2876 parents2.AppendElement(aNode2);
2877 aNode2 = aGetParentFunc(aNode2);
2878 } while (aNode2);
2880 // Find where the parent chain differs
2881 uint32_t pos1 = parents1.Length();
2882 uint32_t pos2 = parents2.Length();
2883 Node* parent = nullptr;
2884 uint32_t len;
2885 for (len = std::min(pos1, pos2); len > 0; --len) {
2886 Node* child1 = parents1.ElementAt(--pos1);
2887 Node* child2 = parents2.ElementAt(--pos2);
2888 if (child1 != child2) {
2889 break;
2891 parent = child1;
2894 return parent;
2897 /* static */
2898 nsINode* nsContentUtils::GetCommonAncestorHelper(nsINode* aNode1,
2899 nsINode* aNode2) {
2900 return GetCommonAncestorInternal(
2901 aNode1, aNode2, [](nsINode* aNode) { return aNode->GetParentNode(); });
2904 /* static */
2905 nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorHelper(
2906 nsIContent* aContent1, nsIContent* aContent2) {
2907 return GetCommonAncestorInternal(
2908 aContent1, aContent2,
2909 [](nsIContent* aContent) { return aContent->GetFlattenedTreeParent(); });
2912 /* static */
2913 Element* nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
2914 Element* aElement1, Element* aElement2) {
2915 return GetCommonAncestorInternal(aElement1, aElement2, [](Element* aElement) {
2916 return aElement->GetFlattenedTreeParentElementForStyle();
2920 /* static */
2921 bool nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2,
2922 Maybe<uint32_t>* aNode1Index,
2923 Maybe<uint32_t>* aNode2Index) {
2924 // Note, CompareDocumentPosition takes the latter params in different order.
2925 return (aNode2->CompareDocumentPosition(*aNode1, aNode2Index, aNode1Index) &
2926 (Node_Binding::DOCUMENT_POSITION_PRECEDING |
2927 Node_Binding::DOCUMENT_POSITION_DISCONNECTED)) ==
2928 Node_Binding::DOCUMENT_POSITION_PRECEDING;
2931 /* static */
2932 Maybe<int32_t> nsContentUtils::ComparePoints(
2933 const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
2934 uint32_t aOffset2, ComparePointsCache* aParent1Cache) {
2935 bool disconnected{false};
2937 const int32_t order = ComparePoints_Deprecated(
2938 aParent1, aOffset1, aParent2, aOffset2, &disconnected, aParent1Cache);
2939 if (disconnected) {
2940 return Nothing();
2943 return Some(order);
2946 /* static */
2947 int32_t nsContentUtils::ComparePoints_Deprecated(
2948 const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
2949 uint32_t aOffset2, bool* aDisconnected, ComparePointsCache* aParent1Cache) {
2950 if (aParent1 == aParent2) {
2951 return aOffset1 < aOffset2 ? -1 : aOffset1 > aOffset2 ? 1 : 0;
2954 AutoTArray<const nsINode*, 32> parents1, parents2;
2955 const nsINode* node1 = aParent1;
2956 const nsINode* node2 = aParent2;
2957 do {
2958 parents1.AppendElement(node1);
2959 node1 = node1->GetParentOrShadowHostNode();
2960 } while (node1);
2961 do {
2962 parents2.AppendElement(node2);
2963 node2 = node2->GetParentOrShadowHostNode();
2964 } while (node2);
2966 uint32_t pos1 = parents1.Length() - 1;
2967 uint32_t pos2 = parents2.Length() - 1;
2969 bool disconnected = parents1.ElementAt(pos1) != parents2.ElementAt(pos2);
2970 if (aDisconnected) {
2971 *aDisconnected = disconnected;
2973 if (disconnected) {
2974 NS_ASSERTION(aDisconnected, "unexpected disconnected nodes");
2975 return 1;
2978 // Find where the parent chains differ
2979 const nsINode* parent = parents1.ElementAt(pos1);
2980 uint32_t len;
2981 for (len = std::min(pos1, pos2); len > 0; --len) {
2982 const nsINode* child1 = parents1.ElementAt(--pos1);
2983 const nsINode* child2 = parents2.ElementAt(--pos2);
2984 if (child1 != child2) {
2985 if (MOZ_UNLIKELY(child1->IsShadowRoot())) {
2986 // Shadow roots come before light DOM per
2987 // https://dom.spec.whatwg.org/#concept-shadow-including-tree-order
2988 MOZ_ASSERT(!child2->IsShadowRoot(), "Two shadow roots?");
2989 return -1;
2991 if (MOZ_UNLIKELY(child2->IsShadowRoot())) {
2992 return 1;
2994 const Maybe<uint32_t> child1Index =
2995 aParent1Cache ? aParent1Cache->ComputeIndexOf(parent, child1)
2996 : parent->ComputeIndexOf(child1);
2997 const Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2);
2998 if (MOZ_LIKELY(child1Index.isSome() && child2Index.isSome())) {
2999 return *child1Index < *child2Index ? -1 : 1;
3001 // XXX Keep the odd traditional behavior for now.
3002 return child1Index.isNothing() && child2Index.isSome() ? -1 : 1;
3004 parent = child1;
3007 // The parent chains never differed, so one of the nodes is an ancestor of
3008 // the other
3010 NS_ASSERTION(!pos1 || !pos2,
3011 "should have run out of parent chain for one of the nodes");
3013 if (!pos1) {
3014 const nsINode* child2 = parents2.ElementAt(--pos2);
3015 const Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2);
3016 if (MOZ_UNLIKELY(NS_WARN_IF(child2Index.isNothing()))) {
3017 return 1;
3019 return aOffset1 <= *child2Index ? -1 : 1;
3022 const nsINode* child1 = parents1.ElementAt(--pos1);
3023 const Maybe<uint32_t> child1Index =
3024 aParent1Cache ? aParent1Cache->ComputeIndexOf(parent, child1)
3025 : parent->ComputeIndexOf(child1);
3026 if (MOZ_UNLIKELY(NS_WARN_IF(child1Index.isNothing()))) {
3027 return -1;
3029 return *child1Index < aOffset2 ? -1 : 1;
3032 /* static */
3033 BrowserParent* nsContentUtils::GetCommonBrowserParentAncestor(
3034 BrowserParent* aBrowserParent1, BrowserParent* aBrowserParent2) {
3035 return GetCommonAncestorInternal(
3036 aBrowserParent1, aBrowserParent2, [](BrowserParent* aBrowserParent) {
3037 return aBrowserParent->GetBrowserBridgeParent()
3038 ? aBrowserParent->GetBrowserBridgeParent()->Manager()
3039 : nullptr;
3043 /* static */
3044 Element* nsContentUtils::GetTargetElement(Document* aDocument,
3045 const nsAString& aAnchorName) {
3046 MOZ_ASSERT(aDocument);
3048 if (aAnchorName.IsEmpty()) {
3049 return nullptr;
3051 // 1. If there is an element in the document tree that has an ID equal to
3052 // fragment, then return the first such element in tree order.
3053 if (Element* el = aDocument->GetElementById(aAnchorName)) {
3054 return el;
3057 // 2. If there is an a element in the document tree that has a name
3058 // attribute whose value is equal to fragment, then return the first such
3059 // element in tree order.
3061 // FIXME(emilio): Why the different code-paths for HTML and non-HTML docs?
3062 if (aDocument->IsHTMLDocument()) {
3063 nsCOMPtr<nsINodeList> list = aDocument->GetElementsByName(aAnchorName);
3064 // Loop through the named nodes looking for the first anchor
3065 uint32_t length = list->Length();
3066 for (uint32_t i = 0; i < length; i++) {
3067 nsIContent* node = list->Item(i);
3068 if (node->IsHTMLElement(nsGkAtoms::a)) {
3069 return node->AsElement();
3072 } else {
3073 constexpr auto nameSpace = u"http://www.w3.org/1999/xhtml"_ns;
3074 // Get the list of anchor elements
3075 nsCOMPtr<nsINodeList> list =
3076 aDocument->GetElementsByTagNameNS(nameSpace, u"a"_ns);
3077 // Loop through the anchors looking for the first one with the given name.
3078 for (uint32_t i = 0; true; i++) {
3079 nsIContent* node = list->Item(i);
3080 if (!node) { // End of list
3081 break;
3084 // Compare the name attribute
3085 if (node->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
3086 aAnchorName, eCaseMatters)) {
3087 return node->AsElement();
3092 // 3. Return null.
3093 return nullptr;
3096 /* static */
3097 template <typename FPT, typename FRT, typename SPT, typename SRT>
3098 Maybe<int32_t> nsContentUtils::ComparePoints(
3099 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
3100 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary) {
3101 if (!aFirstBoundary.IsSet() || !aSecondBoundary.IsSet()) {
3102 return Nothing{};
3105 bool disconnected{false};
3106 const int32_t order =
3107 ComparePoints_Deprecated(aFirstBoundary, aSecondBoundary, &disconnected);
3109 if (disconnected) {
3110 return Nothing{};
3113 return Some(order);
3116 /* static */
3117 template <typename FPT, typename FRT, typename SPT, typename SRT>
3118 int32_t nsContentUtils::ComparePoints_Deprecated(
3119 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
3120 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary, bool* aDisconnected) {
3121 if (NS_WARN_IF(!aFirstBoundary.IsSet()) ||
3122 NS_WARN_IF(!aSecondBoundary.IsSet())) {
3123 return -1;
3125 // XXX Re-implement this without calling `Offset()` as far as possible,
3126 // and the other overload should be an alias of this.
3127 return ComparePoints_Deprecated(
3128 aFirstBoundary.Container(),
3129 *aFirstBoundary.Offset(
3130 RangeBoundaryBase<FPT, FRT>::OffsetFilter::kValidOrInvalidOffsets),
3131 aSecondBoundary.Container(),
3132 *aSecondBoundary.Offset(
3133 RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOrInvalidOffsets),
3134 aDisconnected);
3137 inline bool IsCharInSet(const char* aSet, const char16_t aChar) {
3138 char16_t ch;
3139 while ((ch = *aSet)) {
3140 if (aChar == char16_t(ch)) {
3141 return true;
3143 ++aSet;
3145 return false;
3149 * This method strips leading/trailing chars, in given set, from string.
3152 // static
3153 const nsDependentSubstring nsContentUtils::TrimCharsInSet(
3154 const char* aSet, const nsAString& aValue) {
3155 nsAString::const_iterator valueCurrent, valueEnd;
3157 aValue.BeginReading(valueCurrent);
3158 aValue.EndReading(valueEnd);
3160 // Skip characters in the beginning
3161 while (valueCurrent != valueEnd) {
3162 if (!IsCharInSet(aSet, *valueCurrent)) {
3163 break;
3165 ++valueCurrent;
3168 if (valueCurrent != valueEnd) {
3169 for (;;) {
3170 --valueEnd;
3171 if (!IsCharInSet(aSet, *valueEnd)) {
3172 break;
3175 ++valueEnd; // Step beyond the last character we want in the value.
3178 // valueEnd should point to the char after the last to copy
3179 return Substring(valueCurrent, valueEnd);
3183 * This method strips leading and trailing whitespace from a string.
3186 // static
3187 template <bool IsWhitespace(char16_t)>
3188 const nsDependentSubstring nsContentUtils::TrimWhitespace(const nsAString& aStr,
3189 bool aTrimTrailing) {
3190 nsAString::const_iterator start, end;
3192 aStr.BeginReading(start);
3193 aStr.EndReading(end);
3195 // Skip whitespace characters in the beginning
3196 while (start != end && IsWhitespace(*start)) {
3197 ++start;
3200 if (aTrimTrailing) {
3201 // Skip whitespace characters in the end.
3202 while (end != start) {
3203 --end;
3205 if (!IsWhitespace(*end)) {
3206 // Step back to the last non-whitespace character.
3207 ++end;
3209 break;
3214 // Return a substring for the string w/o leading and/or trailing
3215 // whitespace
3217 return Substring(start, end);
3220 // Declaring the templates we are going to use avoid linking issues without
3221 // inlining the method. Considering there is not so much spaces checking
3222 // methods we can consider this to be better than inlining.
3223 template const nsDependentSubstring
3224 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, bool);
3225 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
3226 nsContentUtils::IsHTMLWhitespace>(const nsAString&, bool);
3227 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
3228 nsContentUtils::IsHTMLWhitespaceOrNBSP>(const nsAString&, bool);
3230 static inline void KeyAppendSep(nsACString& aKey) {
3231 if (!aKey.IsEmpty()) {
3232 aKey.Append('>');
3236 static inline void KeyAppendString(const nsAString& aString, nsACString& aKey) {
3237 KeyAppendSep(aKey);
3239 // Could escape separator here if collisions happen. > is not a legal char
3240 // for a name or type attribute, so we should be safe avoiding that extra
3241 // work.
3243 AppendUTF16toUTF8(aString, aKey);
3246 static inline void KeyAppendString(const nsACString& aString,
3247 nsACString& aKey) {
3248 KeyAppendSep(aKey);
3250 // Could escape separator here if collisions happen. > is not a legal char
3251 // for a name or type attribute, so we should be safe avoiding that extra
3252 // work.
3254 aKey.Append(aString);
3257 static inline void KeyAppendInt(int32_t aInt, nsACString& aKey) {
3258 KeyAppendSep(aKey);
3260 aKey.AppendInt(aInt);
3263 static inline bool IsAutocompleteOff(const nsIContent* aContent) {
3264 return aContent->IsElement() &&
3265 aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
3266 nsGkAtoms::autocomplete, u"off"_ns,
3267 eIgnoreCase);
3270 /*static*/
3271 void nsContentUtils::GenerateStateKey(nsIContent* aContent, Document* aDocument,
3272 nsACString& aKey) {
3273 MOZ_ASSERT(aContent);
3275 aKey.Truncate();
3277 uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
3279 // Don't capture state for anonymous content
3280 if (aContent->IsInNativeAnonymousSubtree()) {
3281 return;
3284 if (IsAutocompleteOff(aContent)) {
3285 return;
3288 RefPtr<Document> doc = aContent->GetUncomposedDoc();
3290 KeyAppendInt(partID, aKey); // first append a partID
3291 bool generatedUniqueKey = false;
3293 if (doc && doc->IsHTMLOrXHTML()) {
3294 nsHTMLDocument* htmlDoc = doc->AsHTMLDocument();
3296 // If we have a form control and can calculate form information, use that
3297 // as the key - it is more reliable than just recording position in the
3298 // DOM.
3299 // XXXbz Is it, really? We have bugs on this, I think...
3300 // Important to have a unique key, and tag/type/name may not be.
3302 // The format of the key depends on whether the control has a form,
3303 // and whether the element was parser inserted:
3305 // [Has Form, Parser Inserted]:
3306 // fp>type>FormNum>IndOfControlInForm>FormName>name
3308 // [No Form, Parser Inserted]:
3309 // dp>type>ControlNum>name
3311 // [Has Form, Not Parser Inserted]:
3312 // fn>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
3314 // [No Form, Not Parser Inserted]:
3315 // dn>type>IndOfControlInDoc>name
3317 // XXX We don't need to use index if name is there
3318 // XXXbz We don't? Why not? I don't follow.
3320 nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
3321 if (control) {
3322 // Get the control number if this was a parser inserted element from the
3323 // network.
3324 int32_t controlNumber =
3325 control->GetParserInsertedControlNumberForStateKey();
3326 bool parserInserted = controlNumber != -1;
3328 RefPtr<nsContentList> htmlForms;
3329 RefPtr<nsContentList> htmlFormControls;
3330 if (!parserInserted) {
3331 // Getting these lists is expensive, as we need to keep them up to date
3332 // as the document loads, so we avoid it if we don't need them.
3333 htmlDoc->GetFormsAndFormControls(getter_AddRefs(htmlForms),
3334 getter_AddRefs(htmlFormControls));
3337 // Append the control type
3338 KeyAppendInt(int32_t(control->ControlType()), aKey);
3340 // If in a form, add form name / index of form / index in form
3341 HTMLFormElement* formElement = control->GetForm();
3342 if (formElement) {
3343 if (IsAutocompleteOff(formElement)) {
3344 aKey.Truncate();
3345 return;
3348 // Append the form number, if this is a parser inserted control, or
3349 // the index of the form in the document otherwise.
3350 bool appendedForm = false;
3351 if (parserInserted) {
3352 MOZ_ASSERT(formElement->GetFormNumberForStateKey() != -1,
3353 "when generating a state key for a parser inserted form "
3354 "control we should have a parser inserted <form> element");
3355 KeyAppendString("fp"_ns, aKey);
3356 KeyAppendInt(formElement->GetFormNumberForStateKey(), aKey);
3357 appendedForm = true;
3358 } else {
3359 KeyAppendString("fn"_ns, aKey);
3360 int32_t index = htmlForms->IndexOf(formElement, false);
3361 if (index <= -1) {
3363 // XXX HACK this uses some state that was dumped into the document
3364 // specifically to fix bug 138892. What we are trying to do is
3365 // *guess* which form this control's state is found in, with the
3366 // highly likely guess that the highest form parsed so far is the
3367 // one. This code should not be on trunk, only branch.
3369 index = htmlDoc->GetNumFormsSynchronous() - 1;
3371 if (index > -1) {
3372 KeyAppendInt(index, aKey);
3373 appendedForm = true;
3377 if (appendedForm) {
3378 // Append the index of the control in the form
3379 int32_t index = formElement->IndexOfContent(aContent);
3381 if (index > -1) {
3382 KeyAppendInt(index, aKey);
3383 generatedUniqueKey = true;
3387 // Append the form name
3388 nsAutoString formName;
3389 formElement->GetAttr(nsGkAtoms::name, formName);
3390 KeyAppendString(formName, aKey);
3391 } else {
3392 // Not in a form. Append the control number, if this is a parser
3393 // inserted control, or the index of the control in the document
3394 // otherwise.
3395 if (parserInserted) {
3396 KeyAppendString("dp"_ns, aKey);
3397 KeyAppendInt(control->GetParserInsertedControlNumberForStateKey(),
3398 aKey);
3399 generatedUniqueKey = true;
3400 } else {
3401 KeyAppendString("dn"_ns, aKey);
3402 int32_t index = htmlFormControls->IndexOf(aContent, true);
3403 if (index > -1) {
3404 KeyAppendInt(index, aKey);
3405 generatedUniqueKey = true;
3409 // Append the control name
3410 nsAutoString name;
3411 aContent->AsElement()->GetAttr(nsGkAtoms::name, name);
3412 KeyAppendString(name, aKey);
3417 if (!generatedUniqueKey) {
3418 // Either we didn't have a form control or we aren't in an HTML document so
3419 // we can't figure out form info. Append the tag name if it's an element
3420 // to avoid restoring state for one type of element on another type.
3421 if (aContent->IsElement()) {
3422 KeyAppendString(nsDependentAtomString(aContent->NodeInfo()->NameAtom()),
3423 aKey);
3424 } else {
3425 // Append a character that is not "d" or "f" to disambiguate from
3426 // the case when we were a form control in an HTML document.
3427 KeyAppendString("o"_ns, aKey);
3430 // Now start at aContent and append the indices of it and all its ancestors
3431 // in their containers. That should at least pin down its position in the
3432 // DOM...
3433 nsINode* parent = aContent->GetParentNode();
3434 nsINode* content = aContent;
3435 while (parent) {
3436 KeyAppendInt(parent->ComputeIndexOf_Deprecated(content), aKey);
3437 content = parent;
3438 parent = content->GetParentNode();
3443 // static
3444 nsIPrincipal* nsContentUtils::SubjectPrincipal(JSContext* aCx) {
3445 MOZ_ASSERT(NS_IsMainThread());
3447 // As opposed to SubjectPrincipal(), we do in fact assume that
3448 // we're in a realm here; anyone who calls this function in
3449 // situations where that's not the case is doing it wrong.
3450 JS::Realm* realm = js::GetContextRealm(aCx);
3451 MOZ_ASSERT(realm);
3453 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3454 return nsJSPrincipals::get(principals);
3457 // static
3458 nsIPrincipal* nsContentUtils::SubjectPrincipal() {
3459 MOZ_ASSERT(IsInitialized());
3460 MOZ_ASSERT(NS_IsMainThread());
3461 JSContext* cx = GetCurrentJSContext();
3462 if (!cx) {
3463 MOZ_CRASH(
3464 "Accessing the Subject Principal without an AutoJSAPI on the stack is "
3465 "forbidden");
3468 JS::Realm* realm = js::GetContextRealm(cx);
3470 // When an AutoJSAPI is instantiated, we are in a null realm until the
3471 // first JSAutoRealm, which is kind of a purgatory as far as permissions
3472 // go. It would be nice to just hard-abort if somebody does a security check
3473 // in this purgatory zone, but that would be too fragile, since it could be
3474 // triggered by random IsCallerChrome() checks 20-levels deep.
3476 // So we want to return _something_ here - and definitely not the System
3477 // Principal, since that would make an AutoJSAPI a very dangerous thing to
3478 // instantiate.
3480 // The natural thing to return is a null principal. Ideally, we'd return a
3481 // different null principal each time, to avoid any unexpected interactions
3482 // when the principal accidentally gets inherited somewhere. But
3483 // SubjectPrincipal doesn't return strong references, so there's no way to
3484 // sanely manage the lifetime of multiple null principals.
3486 // So we use a singleton null principal. To avoid it being accidentally
3487 // inherited and becoming a "real" subject or object principal, we do a
3488 // release-mode assert during realm creation against using this principal on
3489 // an actual global.
3490 if (!realm) {
3491 return sNullSubjectPrincipal;
3494 return SubjectPrincipal(cx);
3497 // static
3498 nsIPrincipal* nsContentUtils::ObjectPrincipal(JSObject* aObj) {
3499 #ifdef DEBUG
3500 JS::AssertObjectBelongsToCurrentThread(aObj);
3501 #endif
3503 MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(aObj));
3505 JS::Realm* realm = js::GetNonCCWObjectRealm(aObj);
3506 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3507 return nsJSPrincipals::get(principals);
3510 // static
3511 nsresult nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
3512 const nsAString& aSpec,
3513 Document* aDocument,
3514 nsIURI* aBaseURI) {
3515 if (aDocument) {
3516 return NS_NewURI(aResult, aSpec, aDocument->GetDocumentCharacterSet(),
3517 aBaseURI);
3519 return NS_NewURI(aResult, aSpec, nullptr, aBaseURI);
3522 // static
3523 bool nsContentUtils::IsNameWithDash(nsAtom* aName) {
3524 // A valid custom element name is a sequence of characters name which
3525 // must match the PotentialCustomElementName production:
3526 // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
3527 const char16_t* name = aName->GetUTF16String();
3528 uint32_t len = aName->GetLength();
3529 bool hasDash = false;
3531 if (!len || name[0] < 'a' || name[0] > 'z') {
3532 return false;
3535 uint32_t i = 1;
3536 while (i < len) {
3537 if (i + 1 < len && NS_IS_SURROGATE_PAIR(name[i], name[i + 1])) {
3538 // Merged two 16-bit surrogate pairs into code point.
3539 char32_t code = SURROGATE_TO_UCS4(name[i], name[i + 1]);
3541 if (code < 0x10000 || code > 0xEFFFF) {
3542 return false;
3545 i += 2;
3546 } else {
3547 if (name[i] == '-') {
3548 hasDash = true;
3551 if (name[i] != '-' && name[i] != '.' && name[i] != '_' &&
3552 name[i] != 0xB7 && (name[i] < '0' || name[i] > '9') &&
3553 (name[i] < 'a' || name[i] > 'z') &&
3554 (name[i] < 0xC0 || name[i] > 0xD6) &&
3555 (name[i] < 0xF8 || name[i] > 0x37D) &&
3556 (name[i] < 0x37F || name[i] > 0x1FFF) &&
3557 (name[i] < 0x200C || name[i] > 0x200D) &&
3558 (name[i] < 0x203F || name[i] > 0x2040) &&
3559 (name[i] < 0x2070 || name[i] > 0x218F) &&
3560 (name[i] < 0x2C00 || name[i] > 0x2FEF) &&
3561 (name[i] < 0x3001 || name[i] > 0xD7FF) &&
3562 (name[i] < 0xF900 || name[i] > 0xFDCF) &&
3563 (name[i] < 0xFDF0 || name[i] > 0xFFFD)) {
3564 return false;
3567 i++;
3571 return hasDash;
3574 // static
3575 bool nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID) {
3576 // Allow non-dashed names in XUL for XBL to Custom Element migrations.
3577 if (aNameSpaceID == kNameSpaceID_XUL) {
3578 return true;
3581 bool hasDash = IsNameWithDash(aName);
3582 if (!hasDash) {
3583 return false;
3586 // The custom element name must not be one of the following values:
3587 // annotation-xml
3588 // color-profile
3589 // font-face
3590 // font-face-src
3591 // font-face-uri
3592 // font-face-format
3593 // font-face-name
3594 // missing-glyph
3595 return aName != nsGkAtoms::annotation_xml_ &&
3596 aName != nsGkAtoms::colorProfile && aName != nsGkAtoms::font_face &&
3597 aName != nsGkAtoms::font_face_src &&
3598 aName != nsGkAtoms::font_face_uri &&
3599 aName != nsGkAtoms::font_face_format &&
3600 aName != nsGkAtoms::font_face_name && aName != nsGkAtoms::missingGlyph;
3603 // static
3604 nsresult nsContentUtils::CheckQName(const nsAString& aQualifiedName,
3605 bool aNamespaceAware,
3606 const char16_t** aColon) {
3607 const char* colon = nullptr;
3608 const char16_t* begin = aQualifiedName.BeginReading();
3609 const char16_t* end = aQualifiedName.EndReading();
3611 int result = MOZ_XMLCheckQName(reinterpret_cast<const char*>(begin),
3612 reinterpret_cast<const char*>(end),
3613 aNamespaceAware, &colon);
3615 if (!result) {
3616 if (aColon) {
3617 *aColon = reinterpret_cast<const char16_t*>(colon);
3620 return NS_OK;
3623 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
3626 // static
3627 nsresult nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
3628 const nsString& aQName, int32_t* aNamespace,
3629 nsAtom** aLocalName) {
3630 const char16_t* colon;
3631 nsresult rv = nsContentUtils::CheckQName(aQName, true, &colon);
3632 NS_ENSURE_SUCCESS(rv, rv);
3634 if (colon) {
3635 const char16_t* end;
3636 aQName.EndReading(end);
3637 nsAutoString nameSpace;
3638 rv = aNamespaceResolver->LookupNamespaceURIInternal(
3639 Substring(aQName.get(), colon), nameSpace);
3640 NS_ENSURE_SUCCESS(rv, rv);
3642 *aNamespace = nsNameSpaceManager::GetInstance()->GetNameSpaceID(
3643 nameSpace, nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
3644 if (*aNamespace == kNameSpaceID_Unknown) return NS_ERROR_FAILURE;
3646 *aLocalName = NS_AtomizeMainThread(Substring(colon + 1, end)).take();
3647 } else {
3648 *aNamespace = kNameSpaceID_None;
3649 *aLocalName = NS_AtomizeMainThread(aQName).take();
3651 NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
3652 return NS_OK;
3655 // static
3656 nsresult nsContentUtils::GetNodeInfoFromQName(
3657 const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
3658 nsNodeInfoManager* aNodeInfoManager, uint16_t aNodeType,
3659 mozilla::dom::NodeInfo** aNodeInfo) {
3660 const nsString& qName = PromiseFlatString(aQualifiedName);
3661 const char16_t* colon;
3662 nsresult rv = nsContentUtils::CheckQName(qName, true, &colon);
3663 NS_ENSURE_SUCCESS(rv, rv);
3665 int32_t nsID;
3666 nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, nsID);
3667 if (colon) {
3668 const char16_t* end;
3669 qName.EndReading(end);
3671 RefPtr<nsAtom> prefix = NS_AtomizeMainThread(Substring(qName.get(), colon));
3673 rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix, nsID,
3674 aNodeType, aNodeInfo);
3675 } else {
3676 rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID, aNodeType,
3677 aNodeInfo);
3679 NS_ENSURE_SUCCESS(rv, rv);
3681 return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
3682 (*aNodeInfo)->GetPrefixAtom(),
3683 (*aNodeInfo)->NamespaceID())
3684 ? NS_OK
3685 : NS_ERROR_DOM_NAMESPACE_ERR;
3688 // static
3689 void nsContentUtils::SplitExpatName(const char16_t* aExpatName,
3690 nsAtom** aPrefix, nsAtom** aLocalName,
3691 int32_t* aNameSpaceID) {
3693 * Expat can send the following:
3694 * localName
3695 * namespaceURI<separator>localName
3696 * namespaceURI<separator>localName<separator>prefix
3698 * and we use 0xFFFF for the <separator>.
3702 const char16_t* uriEnd = nullptr;
3703 const char16_t* nameEnd = nullptr;
3704 const char16_t* pos;
3705 for (pos = aExpatName; *pos; ++pos) {
3706 if (*pos == 0xFFFF) {
3707 if (uriEnd) {
3708 nameEnd = pos;
3709 } else {
3710 uriEnd = pos;
3715 const char16_t* nameStart;
3716 if (uriEnd) {
3717 nsNameSpaceManager::GetInstance()->RegisterNameSpace(
3718 nsDependentSubstring(aExpatName, uriEnd), *aNameSpaceID);
3720 nameStart = (uriEnd + 1);
3721 if (nameEnd) {
3722 const char16_t* prefixStart = nameEnd + 1;
3723 *aPrefix = NS_AtomizeMainThread(Substring(prefixStart, pos)).take();
3724 } else {
3725 nameEnd = pos;
3726 *aPrefix = nullptr;
3728 } else {
3729 *aNameSpaceID = kNameSpaceID_None;
3730 nameStart = aExpatName;
3731 nameEnd = pos;
3732 *aPrefix = nullptr;
3734 *aLocalName = NS_AtomizeMainThread(Substring(nameStart, nameEnd)).take();
3737 // static
3738 PresShell* nsContentUtils::GetPresShellForContent(const nsIContent* aContent) {
3739 Document* doc = aContent->GetComposedDoc();
3740 if (!doc) {
3741 return nullptr;
3743 return doc->GetPresShell();
3746 // static
3747 nsPresContext* nsContentUtils::GetContextForContent(
3748 const nsIContent* aContent) {
3749 PresShell* presShell = GetPresShellForContent(aContent);
3750 if (!presShell) {
3751 return nullptr;
3753 return presShell->GetPresContext();
3756 // static
3757 bool nsContentUtils::CanLoadImage(nsIURI* aURI, nsINode* aNode,
3758 Document* aLoadingDocument,
3759 nsIPrincipal* aLoadingPrincipal) {
3760 MOZ_ASSERT(aURI, "Must have a URI");
3761 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3762 MOZ_ASSERT(aLoadingPrincipal, "Must have a loading principal");
3764 nsresult rv;
3766 auto appType = nsIDocShell::APP_TYPE_UNKNOWN;
3769 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
3770 aLoadingDocument->GetDocShell();
3771 if (docShellTreeItem) {
3772 nsCOMPtr<nsIDocShellTreeItem> root;
3773 docShellTreeItem->GetInProcessRootTreeItem(getter_AddRefs(root));
3775 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
3777 if (docShell) {
3778 appType = docShell->GetAppType();
3783 if (appType != nsIDocShell::APP_TYPE_EDITOR) {
3784 // Editor apps get special treatment here, editors can load images
3785 // from anywhere. This allows editor to insert images from file://
3786 // into documents that are being edited.
3787 rv = sSecurityManager->CheckLoadURIWithPrincipal(
3788 aLoadingPrincipal, aURI, nsIScriptSecurityManager::ALLOW_CHROME,
3789 aLoadingDocument->InnerWindowID());
3790 if (NS_FAILED(rv)) {
3791 return false;
3795 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new mozilla::net::LoadInfo(
3796 aLoadingPrincipal,
3797 aLoadingPrincipal, // triggering principal
3798 aNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
3799 nsIContentPolicy::TYPE_INTERNAL_IMAGE);
3801 int16_t decision = nsIContentPolicy::ACCEPT;
3803 rv = NS_CheckContentLoadPolicy(aURI, secCheckLoadInfo,
3804 ""_ns, // mime guess
3805 &decision, GetContentPolicy());
3807 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(decision);
3810 // static
3811 bool nsContentUtils::IsInPrivateBrowsing(Document* aDoc) {
3812 if (!aDoc) {
3813 return false;
3816 nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
3817 if (loadGroup) {
3818 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3819 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3820 if (callbacks) {
3821 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3822 if (loadContext) {
3823 return loadContext->UsePrivateBrowsing();
3828 nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
3829 return channel && NS_UsePrivateBrowsing(channel);
3832 // static
3833 bool nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup) {
3834 if (!aLoadGroup) {
3835 return false;
3837 bool isPrivate = false;
3838 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3839 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3840 if (callbacks) {
3841 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3842 isPrivate = loadContext && loadContext->UsePrivateBrowsing();
3844 return isPrivate;
3847 // FIXME(emilio): This is (effectively) almost but not quite the same as
3848 // Document::ShouldLoadImages(), which one is right?
3849 bool nsContentUtils::DocumentInactiveForImageLoads(Document* aDocument) {
3850 if (!aDocument) {
3851 return false;
3853 if (IsChromeDoc(aDocument) || aDocument->IsResourceDoc() ||
3854 aDocument->IsStaticDocument()) {
3855 return false;
3857 nsCOMPtr<nsPIDOMWindowInner> win =
3858 do_QueryInterface(aDocument->GetScopeObject());
3859 return !win || !win->GetDocShell();
3862 imgLoader* nsContentUtils::GetImgLoaderForDocument(Document* aDoc) {
3863 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
3865 if (!aDoc) {
3866 return imgLoader::NormalLoader();
3868 bool isPrivate = IsInPrivateBrowsing(aDoc);
3869 return isPrivate ? imgLoader::PrivateBrowsingLoader()
3870 : imgLoader::NormalLoader();
3873 // static
3874 imgLoader* nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
3875 Document* aContext) {
3876 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
3878 if (!aChannel) {
3879 return imgLoader::NormalLoader();
3881 nsCOMPtr<nsILoadContext> context;
3882 NS_QueryNotificationCallbacks(aChannel, context);
3883 return context && context->UsePrivateBrowsing()
3884 ? imgLoader::PrivateBrowsingLoader()
3885 : imgLoader::NormalLoader();
3888 // static
3889 int32_t nsContentUtils::CORSModeToLoadImageFlags(mozilla::CORSMode aMode) {
3890 switch (aMode) {
3891 case CORS_ANONYMOUS:
3892 return imgILoader::LOAD_CORS_ANONYMOUS;
3893 case CORS_USE_CREDENTIALS:
3894 return imgILoader::LOAD_CORS_USE_CREDENTIALS;
3895 default:
3896 return 0;
3900 // static
3901 nsresult nsContentUtils::LoadImage(
3902 nsIURI* aURI, nsINode* aContext, Document* aLoadingDocument,
3903 nsIPrincipal* aLoadingPrincipal, uint64_t aRequestContextID,
3904 nsIReferrerInfo* aReferrerInfo, imgINotificationObserver* aObserver,
3905 int32_t aLoadFlags, const nsAString& initiatorType,
3906 imgRequestProxy** aRequest, nsContentPolicyType aContentPolicyType,
3907 bool aUseUrgentStartForChannel, bool aLinkPreload,
3908 uint64_t aEarlyHintPreloaderId) {
3909 MOZ_ASSERT(aURI, "Must have a URI");
3910 MOZ_ASSERT(aContext, "Must have a context");
3911 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3912 MOZ_ASSERT(aLoadingPrincipal, "Must have a principal");
3913 MOZ_ASSERT(aRequest, "Null out param");
3915 imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
3916 if (!imgLoader) {
3917 // nothing we can do here
3918 return NS_ERROR_FAILURE;
3921 nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
3923 nsIURI* documentURI = aLoadingDocument->GetDocumentURI();
3925 NS_ASSERTION(loadGroup || aLoadingDocument->IsSVGGlyphsDocument(),
3926 "Could not get loadgroup; onload may fire too early");
3928 // XXXbz using "documentURI" for the initialDocumentURI is not quite
3929 // right, but the best we can do here...
3930 return imgLoader->LoadImage(aURI, /* uri to load */
3931 documentURI, /* initialDocumentURI */
3932 aReferrerInfo, /* referrerInfo */
3933 aLoadingPrincipal, /* loading principal */
3934 aRequestContextID, /* request context ID */
3935 loadGroup, /* loadgroup */
3936 aObserver, /* imgINotificationObserver */
3937 aContext, /* loading context */
3938 aLoadingDocument, /* uniquification key */
3939 aLoadFlags, /* load flags */
3940 nullptr, /* cache key */
3941 aContentPolicyType, /* content policy type */
3942 initiatorType, /* the load initiator */
3943 aUseUrgentStartForChannel, /* urgent-start flag */
3944 aLinkPreload, /* <link preload> initiator */
3945 aEarlyHintPreloaderId, aRequest);
3948 // static
3949 already_AddRefed<imgIContainer> nsContentUtils::GetImageFromContent(
3950 nsIImageLoadingContent* aContent, imgIRequest** aRequest) {
3951 if (aRequest) {
3952 *aRequest = nullptr;
3955 NS_ENSURE_TRUE(aContent, nullptr);
3957 nsCOMPtr<imgIRequest> imgRequest;
3958 aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3959 getter_AddRefs(imgRequest));
3960 if (!imgRequest) {
3961 return nullptr;
3964 nsCOMPtr<imgIContainer> imgContainer;
3965 imgRequest->GetImage(getter_AddRefs(imgContainer));
3967 if (!imgContainer) {
3968 return nullptr;
3971 if (aRequest) {
3972 // If the consumer wants the request, verify it has actually loaded
3973 // successfully.
3974 uint32_t imgStatus;
3975 imgRequest->GetImageStatus(&imgStatus);
3976 if (imgStatus & imgIRequest::STATUS_FRAME_COMPLETE &&
3977 !(imgStatus & imgIRequest::STATUS_ERROR)) {
3978 imgRequest.swap(*aRequest);
3982 return imgContainer.forget();
3985 static bool IsLinkWithURI(const nsIContent& aContent) {
3986 const auto* element = Element::FromNode(aContent);
3987 if (!element || !element->IsLink()) {
3988 return false;
3990 nsCOMPtr<nsIURI> absURI = element->GetHrefURI();
3991 return !!absURI;
3994 static bool HasImageRequest(nsIContent& aContent) {
3995 nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(&aContent));
3996 if (!imageContent) {
3997 return false;
4000 nsCOMPtr<imgIRequest> imgRequest;
4001 imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
4002 getter_AddRefs(imgRequest));
4004 // XXXbz It may be draggable even if the request resulted in an error. Why?
4005 // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
4006 return !!imgRequest;
4009 static Maybe<bool> DraggableOverride(const nsIContent& aContent) {
4010 if (auto* el = nsGenericHTMLElement::FromNode(aContent)) {
4011 if (el->Draggable()) {
4012 return Some(true);
4015 if (el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
4016 nsGkAtoms::_false, eIgnoreCase)) {
4017 return Some(false);
4020 if (aContent.IsSVGElement()) {
4021 return Some(false);
4023 return Nothing();
4026 // static
4027 bool nsContentUtils::ContentIsDraggable(nsIContent* aContent) {
4028 MOZ_ASSERT(aContent);
4030 if (auto draggable = DraggableOverride(*aContent)) {
4031 return *draggable;
4034 // special handling for content area image and link dragging
4035 return HasImageRequest(*aContent) || IsLinkWithURI(*aContent);
4038 // static
4039 bool nsContentUtils::IsDraggableImage(nsIContent* aContent) {
4040 MOZ_ASSERT(aContent);
4041 return HasImageRequest(*aContent) &&
4042 DraggableOverride(*aContent).valueOr(true);
4045 // static
4046 bool nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
4047 MOZ_ASSERT(aContent);
4048 return IsLinkWithURI(*aContent) && DraggableOverride(*aContent).valueOr(true);
4051 // static
4052 nsresult nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo,
4053 nsAtom* aName,
4054 mozilla::dom::NodeInfo** aResult) {
4055 nsNodeInfoManager* niMgr = aNodeInfo->NodeInfoManager();
4057 *aResult = niMgr
4058 ->GetNodeInfo(aName, nullptr, aNodeInfo->NamespaceID(),
4059 aNodeInfo->NodeType(), aNodeInfo->GetExtraName())
4060 .take();
4061 return NS_OK;
4064 static bool TestSitePerm(nsIPrincipal* aPrincipal, const nsACString& aType,
4065 uint32_t aPerm, bool aExactHostMatch) {
4066 if (!aPrincipal) {
4067 // We always deny (i.e. don't allow) the permission if we don't have a
4068 // principal.
4069 return aPerm != nsIPermissionManager::ALLOW_ACTION;
4072 nsCOMPtr<nsIPermissionManager> permMgr =
4073 components::PermissionManager::Service();
4074 NS_ENSURE_TRUE(permMgr, false);
4076 uint32_t perm;
4077 nsresult rv;
4078 if (aExactHostMatch) {
4079 rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
4080 } else {
4081 rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
4083 NS_ENSURE_SUCCESS(rv, false);
4085 return perm == aPerm;
4088 bool nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal,
4089 const nsACString& aType) {
4090 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
4091 false);
4094 bool nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal,
4095 const nsACString& aType) {
4096 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
4097 false);
4100 bool nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal,
4101 const nsACString& aType) {
4102 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
4103 true);
4106 bool nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal,
4107 const nsACString& aType) {
4108 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
4109 true);
4112 bool nsContentUtils::HasSitePerm(nsIPrincipal* aPrincipal,
4113 const nsACString& aType) {
4114 if (!aPrincipal) {
4115 return false;
4118 nsCOMPtr<nsIPermissionManager> permMgr =
4119 components::PermissionManager::Service();
4120 NS_ENSURE_TRUE(permMgr, false);
4122 uint32_t perm;
4123 nsresult rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
4124 NS_ENSURE_SUCCESS(rv, false);
4126 return perm != nsIPermissionManager::UNKNOWN_ACTION;
4129 static const char* gEventNames[] = {"event"};
4130 static const char* gSVGEventNames[] = {"evt"};
4131 // for b/w compat, the first name to onerror is still 'event', even though it
4132 // is actually the error message
4133 static const char* gOnErrorNames[] = {"event", "source", "lineno", "colno",
4134 "error"};
4136 // static
4137 void nsContentUtils::GetEventArgNames(int32_t aNameSpaceID, nsAtom* aEventName,
4138 bool aIsForWindow, uint32_t* aArgCount,
4139 const char*** aArgArray) {
4140 #define SET_EVENT_ARG_NAMES(names) \
4141 *aArgCount = sizeof(names) / sizeof(names[0]); \
4142 *aArgArray = names;
4144 // JSEventHandler is what does the arg magic for onerror, and it does
4145 // not seem to take the namespace into account. So we let onerror in all
4146 // namespaces get the 3 arg names.
4147 if (aEventName == nsGkAtoms::onerror && aIsForWindow) {
4148 SET_EVENT_ARG_NAMES(gOnErrorNames);
4149 } else if (aNameSpaceID == kNameSpaceID_SVG) {
4150 SET_EVENT_ARG_NAMES(gSVGEventNames);
4151 } else {
4152 SET_EVENT_ARG_NAMES(gEventNames);
4156 // Note: The list of content bundles in nsStringBundle.cpp should be updated
4157 // whenever entries are added or removed from this list.
4158 static const char* gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT] = {
4159 // Must line up with the enum values in |PropertiesFile| enum.
4160 "chrome://global/locale/css.properties",
4161 "chrome://global/locale/xul.properties",
4162 "chrome://global/locale/layout_errors.properties",
4163 "chrome://global/locale/layout/HtmlForm.properties",
4164 "chrome://global/locale/printing.properties",
4165 "chrome://global/locale/dom/dom.properties",
4166 "chrome://global/locale/layout/htmlparser.properties",
4167 "chrome://global/locale/svg/svg.properties",
4168 "chrome://branding/locale/brand.properties",
4169 "chrome://global/locale/commonDialogs.properties",
4170 "chrome://global/locale/mathml/mathml.properties",
4171 "chrome://global/locale/security/security.properties",
4172 "chrome://necko/locale/necko.properties",
4173 "resource://gre/res/locale/layout/HtmlForm.properties",
4174 "resource://gre/res/locale/dom/dom.properties"};
4176 /* static */
4177 nsresult nsContentUtils::EnsureStringBundle(PropertiesFile aFile) {
4178 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(),
4179 "Should not create bundles off main thread.");
4180 if (!sStringBundles[aFile]) {
4181 if (!sStringBundleService) {
4182 nsresult rv =
4183 CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
4184 NS_ENSURE_SUCCESS(rv, rv);
4186 RefPtr<nsIStringBundle> bundle;
4187 MOZ_TRY(sStringBundleService->CreateBundle(gPropertiesFiles[aFile],
4188 getter_AddRefs(bundle)));
4189 sStringBundles[aFile] = bundle.forget();
4191 return NS_OK;
4194 /* static */
4195 void nsContentUtils::AsyncPrecreateStringBundles() {
4196 // We only ever want to pre-create bundles in the parent process.
4198 // All nsContentUtils bundles are shared between the parent and child
4199 // precesses, and the shared memory regions that back them *must* be created
4200 // in the parent, and then sent to all children.
4202 // If we attempt to create a bundle in the child before its memory region is
4203 // available, we need to create a temporary non-shared bundle, and later
4204 // replace that with the shared memory copy. So attempting to pre-load in the
4205 // child is wasteful and unnecessary.
4206 MOZ_ASSERT(XRE_IsParentProcess());
4208 for (uint32_t bundleIndex = 0; bundleIndex < PropertiesFile_COUNT;
4209 ++bundleIndex) {
4210 nsresult rv = NS_DispatchToCurrentThreadQueue(
4211 NS_NewRunnableFunction("AsyncPrecreateStringBundles",
4212 [bundleIndex]() {
4213 PropertiesFile file =
4214 static_cast<PropertiesFile>(bundleIndex);
4215 EnsureStringBundle(file);
4216 nsIStringBundle* bundle = sStringBundles[file];
4217 bundle->AsyncPreload();
4219 EventQueuePriority::Idle);
4220 Unused << NS_WARN_IF(NS_FAILED(rv));
4224 /* static */
4225 bool nsContentUtils::SpoofLocaleEnglish() {
4226 // 0 - will prompt
4227 // 1 - don't spoof
4228 // 2 - spoof
4229 return StaticPrefs::privacy_spoof_english() == 2;
4232 static nsContentUtils::PropertiesFile GetMaybeSpoofedPropertiesFile(
4233 nsContentUtils::PropertiesFile aFile, const char* aKey,
4234 Document* aDocument) {
4235 // When we spoof English, use en-US properties in strings that are accessible
4236 // by content.
4237 bool spoofLocale = nsContentUtils::SpoofLocaleEnglish() &&
4238 (!aDocument || !aDocument->AllowsL10n());
4239 if (spoofLocale) {
4240 switch (aFile) {
4241 case nsContentUtils::eFORMS_PROPERTIES:
4242 return nsContentUtils::eFORMS_PROPERTIES_en_US;
4243 case nsContentUtils::eDOM_PROPERTIES:
4244 return nsContentUtils::eDOM_PROPERTIES_en_US;
4245 default:
4246 break;
4249 return aFile;
4252 /* static */
4253 nsresult nsContentUtils::GetMaybeLocalizedString(PropertiesFile aFile,
4254 const char* aKey,
4255 Document* aDocument,
4256 nsAString& aResult) {
4257 return GetLocalizedString(
4258 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aResult);
4261 /* static */
4262 nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
4263 const char* aKey,
4264 nsAString& aResult) {
4265 return FormatLocalizedString(aFile, aKey, {}, aResult);
4268 /* static */
4269 nsresult nsContentUtils::FormatMaybeLocalizedString(
4270 PropertiesFile aFile, const char* aKey, Document* aDocument,
4271 const nsTArray<nsString>& aParams, nsAString& aResult) {
4272 return FormatLocalizedString(
4273 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aParams,
4274 aResult);
4277 class FormatLocalizedStringRunnable final : public WorkerMainThreadRunnable {
4278 public:
4279 FormatLocalizedStringRunnable(WorkerPrivate* aWorkerPrivate,
4280 nsContentUtils::PropertiesFile aFile,
4281 const char* aKey,
4282 const nsTArray<nsString>& aParams,
4283 nsAString& aLocalizedString)
4284 : WorkerMainThreadRunnable(aWorkerPrivate,
4285 "FormatLocalizedStringRunnable"_ns),
4286 mFile(aFile),
4287 mKey(aKey),
4288 mParams(aParams),
4289 mLocalizedString(aLocalizedString) {
4290 MOZ_ASSERT(aWorkerPrivate);
4291 aWorkerPrivate->AssertIsOnWorkerThread();
4294 bool MainThreadRun() override {
4295 AssertIsOnMainThread();
4297 mResult = nsContentUtils::FormatLocalizedString(mFile, mKey, mParams,
4298 mLocalizedString);
4299 Unused << NS_WARN_IF(NS_FAILED(mResult));
4300 return true;
4303 nsresult GetResult() const { return mResult; }
4305 private:
4306 const nsContentUtils::PropertiesFile mFile;
4307 const char* mKey;
4308 const nsTArray<nsString>& mParams;
4309 nsresult mResult = NS_ERROR_FAILURE;
4310 nsAString& mLocalizedString;
4313 /* static */
4314 nsresult nsContentUtils::FormatLocalizedString(
4315 PropertiesFile aFile, const char* aKey, const nsTArray<nsString>& aParams,
4316 nsAString& aResult) {
4317 if (!NS_IsMainThread()) {
4318 // nsIStringBundle is thread-safe but its creation is not, and in particular
4319 // we don't create and store nsIStringBundle objects in a thread-safe way.
4321 // TODO(emilio): Maybe if we already have the right bundle created we could
4322 // just call into it, but we should make sure that Shutdown() doesn't get
4323 // called on the main thread when that happens which is a bit tricky to
4324 // prove?
4325 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
4326 if (NS_WARN_IF(!workerPrivate)) {
4327 return NS_ERROR_UNEXPECTED;
4330 auto runnable = MakeRefPtr<FormatLocalizedStringRunnable>(
4331 workerPrivate, aFile, aKey, aParams, aResult);
4333 runnable->Dispatch(Canceling, IgnoreErrors());
4334 return runnable->GetResult();
4337 MOZ_TRY(EnsureStringBundle(aFile));
4338 nsIStringBundle* bundle = sStringBundles[aFile];
4339 if (aParams.IsEmpty()) {
4340 return bundle->GetStringFromName(aKey, aResult);
4342 return bundle->FormatStringFromName(aKey, aParams, aResult);
4345 /* static */
4346 void nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
4347 const nsACString& aCategory,
4348 bool aFromPrivateWindow,
4349 bool aFromChromeContext,
4350 uint32_t aErrorFlags) {
4351 nsCOMPtr<nsIScriptError> scriptError =
4352 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
4353 if (scriptError) {
4354 nsCOMPtr<nsIConsoleService> console =
4355 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
4356 if (console && NS_SUCCEEDED(scriptError->Init(
4357 aErrorText, u""_ns, u""_ns, 0, 0, aErrorFlags, aCategory,
4358 aFromPrivateWindow, aFromChromeContext))) {
4359 console->LogMessage(scriptError);
4364 /* static */
4365 nsresult nsContentUtils::ReportToConsole(
4366 uint32_t aErrorFlags, const nsACString& aCategory,
4367 const Document* aDocument, PropertiesFile aFile, const char* aMessageName,
4368 const nsTArray<nsString>& aParams, nsIURI* aURI,
4369 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber) {
4370 nsresult rv;
4371 nsAutoString errorText;
4372 if (!aParams.IsEmpty()) {
4373 rv = FormatLocalizedString(aFile, aMessageName, aParams, errorText);
4374 } else {
4375 rv = GetLocalizedString(aFile, aMessageName, errorText);
4377 NS_ENSURE_SUCCESS(rv, rv);
4379 return ReportToConsoleNonLocalized(errorText, aErrorFlags, aCategory,
4380 aDocument, aURI, aSourceLine, aLineNumber,
4381 aColumnNumber);
4384 /* static */
4385 void nsContentUtils::ReportEmptyGetElementByIdArg(const Document* aDoc) {
4386 ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, aDoc,
4387 nsContentUtils::eDOM_PROPERTIES, "EmptyGetElementByIdParam");
4390 /* static */
4391 nsresult nsContentUtils::ReportToConsoleNonLocalized(
4392 const nsAString& aErrorText, uint32_t aErrorFlags,
4393 const nsACString& aCategory, const Document* aDocument, nsIURI* aURI,
4394 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4395 MissingErrorLocationMode aLocationMode) {
4396 uint64_t innerWindowID = 0;
4397 if (aDocument) {
4398 if (!aURI) {
4399 aURI = aDocument->GetDocumentURI();
4401 innerWindowID = aDocument->InnerWindowID();
4404 return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
4405 innerWindowID, aURI, aSourceLine,
4406 aLineNumber, aColumnNumber, aLocationMode);
4409 /* static */
4410 nsresult nsContentUtils::ReportToConsoleByWindowID(
4411 const nsAString& aErrorText, uint32_t aErrorFlags,
4412 const nsACString& aCategory, uint64_t aInnerWindowID, nsIURI* aURI,
4413 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4414 MissingErrorLocationMode aLocationMode) {
4415 nsresult rv;
4416 if (!sConsoleService) { // only need to bother null-checking here
4417 rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4418 NS_ENSURE_SUCCESS(rv, rv);
4421 nsAutoString spec;
4422 if (!aLineNumber && aLocationMode == eUSE_CALLING_LOCATION) {
4423 JSContext* cx = GetCurrentJSContext();
4424 if (cx) {
4425 nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber, &aColumnNumber);
4429 nsCOMPtr<nsIScriptError> errorObject =
4430 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
4431 NS_ENSURE_SUCCESS(rv, rv);
4433 if (!spec.IsEmpty()) {
4434 rv = errorObject->InitWithWindowID(aErrorText,
4435 spec, // file name
4436 aSourceLine, aLineNumber, aColumnNumber,
4437 aErrorFlags, aCategory, aInnerWindowID);
4438 } else {
4439 rv = errorObject->InitWithSourceURI(aErrorText, aURI, aSourceLine,
4440 aLineNumber, aColumnNumber, aErrorFlags,
4441 aCategory, aInnerWindowID);
4443 NS_ENSURE_SUCCESS(rv, rv);
4445 return sConsoleService->LogMessage(errorObject);
4448 void nsContentUtils::LogMessageToConsole(const char* aMsg) {
4449 if (!sConsoleService) { // only need to bother null-checking here
4450 CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4451 if (!sConsoleService) {
4452 return;
4455 sConsoleService->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
4458 bool nsContentUtils::IsChromeDoc(const Document* aDocument) {
4459 return aDocument && aDocument->NodePrincipal() == sSystemPrincipal;
4462 bool nsContentUtils::IsChildOfSameType(Document* aDoc) {
4463 if (BrowsingContext* bc = aDoc->GetBrowsingContext()) {
4464 return bc->GetParent();
4466 return false;
4469 bool nsContentUtils::IsPlainTextType(const nsACString& aContentType) {
4470 // NOTE: if you add a type here, add it to the CONTENTDLF_CATEGORIES
4471 // define in nsContentDLF.h as well.
4472 return aContentType.EqualsLiteral(TEXT_PLAIN) ||
4473 aContentType.EqualsLiteral(TEXT_CSS) ||
4474 aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4475 aContentType.EqualsLiteral(TEXT_VTT) ||
4476 aContentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
4477 aContentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
4478 aContentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
4479 aContentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
4480 aContentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
4481 aContentType.EqualsLiteral(APPLICATION_JSON) ||
4482 aContentType.EqualsLiteral(TEXT_JSON);
4485 bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) {
4486 // NOTE: This must be a subset of the list in IsPlainTextType().
4487 return aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4488 aContentType.EqualsLiteral(APPLICATION_JSON) ||
4489 aContentType.EqualsLiteral(TEXT_JSON) ||
4490 aContentType.EqualsLiteral(TEXT_VTT);
4493 bool nsContentUtils::IsInChromeDocshell(const Document* aDocument) {
4494 return aDocument && aDocument->IsInChromeDocShell();
4497 // static
4498 nsIContentPolicy* nsContentUtils::GetContentPolicy() {
4499 if (!sTriedToGetContentPolicy) {
4500 CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
4501 // It's OK to not have a content policy service
4502 sTriedToGetContentPolicy = true;
4505 return sContentPolicyService;
4508 // static
4509 bool nsContentUtils::IsEventAttributeName(nsAtom* aName, int32_t aType) {
4510 const char16_t* name = aName->GetUTF16String();
4511 if (name[0] != 'o' || name[1] != 'n') {
4512 return false;
4515 EventNameMapping mapping;
4516 return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
4519 // static
4520 EventMessage nsContentUtils::GetEventMessage(nsAtom* aName) {
4521 MOZ_ASSERT(NS_IsMainThread(), "sAtomEventTable is not threadsafe");
4522 if (aName) {
4523 EventNameMapping mapping;
4524 if (sAtomEventTable->Get(aName, &mapping)) {
4525 return mapping.mMessage;
4529 return eUnidentifiedEvent;
4532 // static
4533 mozilla::EventClassID nsContentUtils::GetEventClassID(const nsAString& aName) {
4534 EventNameMapping mapping;
4535 if (sStringEventTable->Get(aName, &mapping)) return mapping.mEventClassID;
4537 return eBasicEventClass;
4540 nsAtom* nsContentUtils::GetEventMessageAndAtom(
4541 const nsAString& aName, mozilla::EventClassID aEventClassID,
4542 EventMessage* aEventMessage) {
4543 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4544 EventNameMapping mapping;
4545 if (sStringEventTable->Get(aName, &mapping)) {
4546 *aEventMessage = mapping.mEventClassID == aEventClassID
4547 ? mapping.mMessage
4548 : eUnidentifiedEvent;
4549 return mapping.mAtom;
4552 // If we have cached lots of user defined event names, clear some of them.
4553 if (sUserDefinedEvents->Length() > 127) {
4554 while (sUserDefinedEvents->Length() > 64) {
4555 nsAtom* first = sUserDefinedEvents->ElementAt(0);
4556 sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
4557 sUserDefinedEvents->RemoveElementAt(0);
4561 *aEventMessage = eUnidentifiedEvent;
4562 RefPtr<nsAtom> atom = NS_AtomizeMainThread(u"on"_ns + aName);
4563 sUserDefinedEvents->AppendElement(atom);
4564 mapping.mAtom = atom;
4565 mapping.mMessage = eUnidentifiedEvent;
4566 mapping.mType = EventNameType_None;
4567 mapping.mEventClassID = eBasicEventClass;
4568 // This is a slow hashtable call, but at least we cache the result for the
4569 // following calls. Because GetEventMessageAndAtomForListener utilizes
4570 // sStringEventTable, it needs to know in which cases sStringEventTable
4571 // doesn't contain the information it needs so that it can use
4572 // sAtomEventTable instead.
4573 mapping.mMaybeSpecialSVGorSMILEvent =
4574 GetEventMessage(atom) != eUnidentifiedEvent;
4575 sStringEventTable->InsertOrUpdate(aName, mapping);
4576 return mapping.mAtom;
4579 // static
4580 EventMessage nsContentUtils::GetEventMessageAndAtomForListener(
4581 const nsAString& aName, nsAtom** aOnName) {
4582 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4584 // Because of SVG/SMIL sStringEventTable contains a subset of the event names
4585 // comparing to the sAtomEventTable. However, usually sStringEventTable
4586 // contains the information we need, so in order to reduce hashtable
4587 // lookups, start from it.
4588 EventNameMapping mapping;
4589 EventMessage msg = eUnidentifiedEvent;
4590 RefPtr<nsAtom> atom;
4591 if (sStringEventTable->Get(aName, &mapping)) {
4592 if (mapping.mMaybeSpecialSVGorSMILEvent) {
4593 // Try the atom version so that we should get the right message for
4594 // SVG/SMIL.
4595 atom = NS_AtomizeMainThread(u"on"_ns + aName);
4596 msg = GetEventMessage(atom);
4597 } else {
4598 atom = mapping.mAtom;
4599 msg = mapping.mMessage;
4601 atom.forget(aOnName);
4602 return msg;
4605 // GetEventMessageAndAtom will cache the event type for the future usage...
4606 GetEventMessageAndAtom(aName, eBasicEventClass, &msg);
4608 // ...and then call this method recursively to get the message and atom from
4609 // now updated sStringEventTable.
4610 return GetEventMessageAndAtomForListener(aName, aOnName);
4613 static nsresult GetEventAndTarget(Document* aDoc, nsISupports* aTarget,
4614 const nsAString& aEventName,
4615 CanBubble aCanBubble, Cancelable aCancelable,
4616 Composed aComposed, Trusted aTrusted,
4617 Event** aEvent, EventTarget** aTargetOut) {
4618 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4619 NS_ENSURE_TRUE(aDoc && target, NS_ERROR_INVALID_ARG);
4621 ErrorResult err;
4622 RefPtr<Event> event =
4623 aDoc->CreateEvent(u"Events"_ns, CallerType::System, err);
4624 if (NS_WARN_IF(err.Failed())) {
4625 return err.StealNSResult();
4628 event->InitEvent(aEventName, aCanBubble, aCancelable, aComposed);
4629 event->SetTrusted(aTrusted == Trusted::eYes);
4631 event->SetTarget(target);
4633 event.forget(aEvent);
4634 target.forget(aTargetOut);
4635 return NS_OK;
4638 // static
4639 nsresult nsContentUtils::DispatchTrustedEvent(
4640 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4641 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4642 bool* aDefaultAction) {
4643 MOZ_ASSERT(!aEventName.EqualsLiteral("input") &&
4644 !aEventName.EqualsLiteral("beforeinput"),
4645 "Use DispatchInputEvent() instead");
4646 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4647 aComposed, Trusted::eYes, aDefaultAction);
4650 // static
4651 nsresult nsContentUtils::DispatchUntrustedEvent(
4652 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4653 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4654 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4655 Composed::eDefault, Trusted::eNo, aDefaultAction);
4658 // static
4659 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4660 const nsAString& aEventName,
4661 CanBubble aCanBubble,
4662 Cancelable aCancelable,
4663 Composed aComposed, Trusted aTrusted,
4664 bool* aDefaultAction,
4665 ChromeOnlyDispatch aOnlyChromeDispatch) {
4666 RefPtr<Event> event;
4667 nsCOMPtr<EventTarget> target;
4668 nsresult rv = GetEventAndTarget(
4669 aDoc, aTarget, aEventName, aCanBubble, aCancelable, aComposed, aTrusted,
4670 getter_AddRefs(event), getter_AddRefs(target));
4671 NS_ENSURE_SUCCESS(rv, rv);
4672 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch =
4673 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4675 ErrorResult err;
4676 bool doDefault = target->DispatchEvent(*event, CallerType::System, err);
4677 if (aDefaultAction) {
4678 *aDefaultAction = doDefault;
4680 return err.StealNSResult();
4683 // static
4684 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4685 WidgetEvent& aEvent,
4686 EventMessage aEventMessage,
4687 CanBubble aCanBubble,
4688 Cancelable aCancelable, Trusted aTrusted,
4689 bool* aDefaultAction,
4690 ChromeOnlyDispatch aOnlyChromeDispatch) {
4691 MOZ_ASSERT_IF(aOnlyChromeDispatch == ChromeOnlyDispatch::eYes,
4692 aTrusted == Trusted::eYes);
4694 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4696 aEvent.mSpecifiedEventType = GetEventTypeFromMessage(aEventMessage);
4697 aEvent.SetDefaultComposed();
4698 aEvent.SetDefaultComposedInNativeAnonymousContent();
4700 aEvent.mFlags.mBubbles = aCanBubble == CanBubble::eYes;
4701 aEvent.mFlags.mCancelable = aCancelable == Cancelable::eYes;
4702 aEvent.mFlags.mOnlyChromeDispatch =
4703 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4705 aEvent.mTarget = target;
4707 nsEventStatus status = nsEventStatus_eIgnore;
4708 nsresult rv = EventDispatcher::DispatchDOMEvent(target, &aEvent, nullptr,
4709 nullptr, &status);
4710 if (aDefaultAction) {
4711 *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
4713 return rv;
4716 // static
4717 nsresult nsContentUtils::DispatchInputEvent(Element* aEventTarget) {
4718 return DispatchInputEvent(aEventTarget, mozilla::eEditorInput,
4719 mozilla::EditorInputType::eUnknown, nullptr,
4720 InputEventOptions());
4723 // static
4724 nsresult nsContentUtils::DispatchInputEvent(
4725 Element* aEventTargetElement, EventMessage aEventMessage,
4726 EditorInputType aEditorInputType, EditorBase* aEditorBase,
4727 InputEventOptions&& aOptions, nsEventStatus* aEventStatus /* = nullptr */) {
4728 MOZ_ASSERT(aEventMessage == eEditorInput ||
4729 aEventMessage == eEditorBeforeInput);
4731 if (NS_WARN_IF(!aEventTargetElement)) {
4732 return NS_ERROR_INVALID_ARG;
4735 // If this is called from editor, the instance should be set to aEditorBase.
4736 // Otherwise, we need to look for an editor for aEventTargetElement.
4737 // However, we don't need to do it for HTMLEditor since nobody shouldn't
4738 // dispatch "beforeinput" nor "input" event for HTMLEditor except HTMLEditor
4739 // itself.
4740 bool useInputEvent = false;
4741 if (aEditorBase) {
4742 useInputEvent = true;
4743 } else if (HTMLTextAreaElement* textAreaElement =
4744 HTMLTextAreaElement::FromNode(aEventTargetElement)) {
4745 aEditorBase = textAreaElement->GetTextEditorWithoutCreation();
4746 useInputEvent = true;
4747 } else if (HTMLInputElement* inputElement =
4748 HTMLInputElement::FromNode(aEventTargetElement)) {
4749 if (inputElement->IsInputEventTarget()) {
4750 aEditorBase = inputElement->GetTextEditorWithoutCreation();
4751 useInputEvent = true;
4754 #ifdef DEBUG
4755 else {
4756 MOZ_ASSERT(!aEventTargetElement->IsTextControlElement(),
4757 "The event target may have editor, but we've not known it yet.");
4759 #endif // #ifdef DEBUG
4761 if (!useInputEvent) {
4762 MOZ_ASSERT(aEventMessage == eEditorInput);
4763 MOZ_ASSERT(aEditorInputType == EditorInputType::eUnknown);
4764 MOZ_ASSERT(!aOptions.mNeverCancelable);
4765 // Dispatch "input" event with Event instance.
4766 WidgetEvent widgetEvent(true, eUnidentifiedEvent);
4767 widgetEvent.mSpecifiedEventType = nsGkAtoms::oninput;
4768 widgetEvent.mFlags.mCancelable = false;
4769 widgetEvent.mFlags.mComposed = true;
4770 return AsyncEventDispatcher::RunDOMEventWhenSafe(*aEventTargetElement,
4771 widgetEvent, aEventStatus);
4774 MOZ_ASSERT_IF(aEventMessage != eEditorBeforeInput,
4775 !aOptions.mNeverCancelable);
4776 MOZ_ASSERT_IF(
4777 aEventMessage == eEditorBeforeInput && aOptions.mNeverCancelable,
4778 aEditorInputType == EditorInputType::eInsertReplacementText);
4780 nsCOMPtr<nsIWidget> widget;
4781 if (aEditorBase) {
4782 widget = aEditorBase->GetWidget();
4783 if (NS_WARN_IF(!widget)) {
4784 return NS_ERROR_FAILURE;
4786 } else {
4787 Document* document = aEventTargetElement->OwnerDoc();
4788 if (NS_WARN_IF(!document)) {
4789 return NS_ERROR_FAILURE;
4791 // If we're running xpcshell tests, we fail to get presShell here.
4792 // Even in such case, we need to dispatch "input" event without widget.
4793 PresShell* presShell = document->GetPresShell();
4794 if (presShell) {
4795 nsPresContext* presContext = presShell->GetPresContext();
4796 if (NS_WARN_IF(!presContext)) {
4797 return NS_ERROR_FAILURE;
4799 widget = presContext->GetRootWidget();
4800 if (NS_WARN_IF(!widget)) {
4801 return NS_ERROR_FAILURE;
4806 // Dispatch "input" event with InputEvent instance.
4807 InternalEditorInputEvent inputEvent(true, aEventMessage, widget);
4809 inputEvent.mFlags.mCancelable =
4810 !aOptions.mNeverCancelable && aEventMessage == eEditorBeforeInput &&
4811 IsCancelableBeforeInputEvent(aEditorInputType);
4812 MOZ_ASSERT(!inputEvent.mFlags.mCancelable || aEventStatus);
4814 // If there is an editor, set isComposing to true when it has composition.
4815 // Note that EditorBase::IsIMEComposing() may return false even when we
4816 // need to set it to true.
4817 // Otherwise, i.e., editor hasn't been created for the element yet,
4818 // we should set isComposing to false since the element can never has
4819 // composition without editor.
4820 inputEvent.mIsComposing = aEditorBase && aEditorBase->GetComposition();
4822 if (!aEditorBase || aEditorBase->IsTextEditor()) {
4823 if (IsDataAvailableOnTextEditor(aEditorInputType)) {
4824 inputEvent.mData = std::move(aOptions.mData);
4825 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4826 "inputEvent.mData shouldn't be void");
4828 #ifdef DEBUG
4829 else {
4830 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4832 #endif // #ifdef DEBUG
4833 MOZ_ASSERT(
4834 aOptions.mTargetRanges.IsEmpty(),
4835 "Target ranges for <input> and <textarea> should always be empty");
4836 } else {
4837 MOZ_ASSERT(aEditorBase->IsHTMLEditor());
4838 if (IsDataAvailableOnHTMLEditor(aEditorInputType)) {
4839 inputEvent.mData = std::move(aOptions.mData);
4840 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4841 "inputEvent.mData shouldn't be void");
4842 } else {
4843 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4844 if (IsDataTransferAvailableOnHTMLEditor(aEditorInputType)) {
4845 inputEvent.mDataTransfer = std::move(aOptions.mDataTransfer);
4846 MOZ_ASSERT(inputEvent.mDataTransfer,
4847 "inputEvent.mDataTransfer shouldn't be nullptr");
4848 MOZ_ASSERT(inputEvent.mDataTransfer->IsReadOnly(),
4849 "inputEvent.mDataTransfer should be read only");
4851 #ifdef DEBUG
4852 else {
4853 MOZ_ASSERT(!inputEvent.mDataTransfer,
4854 "inputEvent.mDataTransfer should be nullptr");
4856 #endif // #ifdef DEBUG
4858 if (aEventMessage == eEditorBeforeInput &&
4859 MayHaveTargetRangesOnHTMLEditor(aEditorInputType)) {
4860 inputEvent.mTargetRanges = std::move(aOptions.mTargetRanges);
4862 #ifdef DEBUG
4863 else {
4864 MOZ_ASSERT(aOptions.mTargetRanges.IsEmpty(),
4865 "Target ranges shouldn't be set for the dispatching event");
4867 #endif // #ifdef DEBUG
4870 inputEvent.mInputType = aEditorInputType;
4872 // If we cannot dispatch an event right now, we cannot make it cancelable.
4873 if (!nsContentUtils::IsSafeToRunScript()) {
4874 NS_ASSERTION(
4875 !inputEvent.mFlags.mCancelable,
4876 "Cancelable beforeinput event dispatcher should run when it's safe");
4877 inputEvent.mFlags.mCancelable = false;
4879 return AsyncEventDispatcher::RunDOMEventWhenSafe(*aEventTargetElement,
4880 inputEvent, aEventStatus);
4883 nsresult nsContentUtils::DispatchChromeEvent(
4884 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4885 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4886 RefPtr<Event> event;
4887 nsCOMPtr<EventTarget> target;
4888 nsresult rv = GetEventAndTarget(
4889 aDoc, aTarget, aEventName, aCanBubble, aCancelable, Composed::eDefault,
4890 Trusted::eYes, getter_AddRefs(event), getter_AddRefs(target));
4891 NS_ENSURE_SUCCESS(rv, rv);
4893 NS_ASSERTION(aDoc, "GetEventAndTarget lied?");
4894 if (!aDoc->GetWindow()) return NS_ERROR_INVALID_ARG;
4896 EventTarget* piTarget = aDoc->GetWindow()->GetParentTarget();
4897 if (!piTarget) return NS_ERROR_INVALID_ARG;
4899 ErrorResult err;
4900 bool defaultActionEnabled =
4901 piTarget->DispatchEvent(*event, CallerType::System, err);
4902 if (aDefaultAction) {
4903 *aDefaultAction = defaultActionEnabled;
4905 return err.StealNSResult();
4908 void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise,
4909 CallerType aCallerType) {
4910 RefPtr<Element> target = &aFrameElement;
4911 bool defaultAction = true;
4912 if (aCanRaise) {
4913 DispatchEventOnlyToChrome(target->OwnerDoc(), target,
4914 u"framefocusrequested"_ns, CanBubble::eYes,
4915 Cancelable::eYes, &defaultAction);
4917 if (!defaultAction) {
4918 return;
4921 RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
4922 if (!fm) {
4923 return;
4926 uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
4927 if (aCanRaise) {
4928 flags |= nsIFocusManager::FLAG_RAISE;
4931 if (aCallerType == CallerType::NonSystem) {
4932 flags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
4935 fm->SetFocus(target, flags);
4938 nsresult nsContentUtils::DispatchEventOnlyToChrome(
4939 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4940 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4941 bool* aDefaultAction) {
4942 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4943 aComposed, Trusted::eYes, aDefaultAction,
4944 ChromeOnlyDispatch::eYes);
4947 /* static */
4948 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4949 const nsAtom* aId) {
4950 for (nsIContent* cur = aContent; cur; cur = cur->GetNextNode(aContent)) {
4951 if (aId == cur->GetID()) {
4952 return cur->AsElement();
4956 return nullptr;
4959 /* static */
4960 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4961 const nsAString& aId) {
4962 MOZ_ASSERT(!aId.IsEmpty(), "Will match random elements");
4964 // ID attrs are generally stored as atoms, so just atomize this up front
4965 RefPtr<nsAtom> id(NS_Atomize(aId));
4966 if (!id) {
4967 // OOM, so just bail
4968 return nullptr;
4971 return MatchElementId(aContent, id);
4974 /* static */
4975 void nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver) {
4976 nsCOMPtr<nsIObserverService> observerService =
4977 mozilla::services::GetObserverService();
4978 if (observerService) {
4979 observerService->AddObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
4980 false);
4984 /* static */
4985 void nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver) {
4986 nsCOMPtr<nsIObserverService> observerService =
4987 mozilla::services::GetObserverService();
4988 if (observerService) {
4989 observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
4993 /* static */
4994 bool nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent,
4995 int32_t aNameSpaceID, nsAtom* aName) {
4996 static AttrArray::AttrValuesArray strings[] = {nsGkAtoms::_empty, nullptr};
4997 return aContent->IsElement() &&
4998 aContent->AsElement()->FindAttrValueIn(aNameSpaceID, aName, strings,
4999 eCaseMatters) ==
5000 AttrArray::ATTR_VALUE_NO_MATCH;
5003 /* static */
5004 bool nsContentUtils::HasMutationListeners(nsINode* aNode, uint32_t aType,
5005 nsINode* aTargetForSubtreeModified) {
5006 Document* doc = aNode->OwnerDoc();
5008 // global object will be null for documents that don't have windows.
5009 nsPIDOMWindowInner* window = doc->GetInnerWindow();
5010 // This relies on EventListenerManager::AddEventListener, which sets
5011 // all mutation bits when there is a listener for DOMSubtreeModified event.
5012 if (window && !window->HasMutationListeners(aType)) {
5013 return false;
5016 if (aNode->ChromeOnlyAccess() || aNode->IsInShadowTree()) {
5017 return false;
5020 doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
5022 // If we have a window, we can check it for mutation listeners now.
5023 if (aNode->IsInUncomposedDoc()) {
5024 nsCOMPtr<EventTarget> piTarget(do_QueryInterface(window));
5025 if (piTarget) {
5026 EventListenerManager* manager = piTarget->GetExistingListenerManager();
5027 if (manager && manager->HasMutationListeners()) {
5028 return true;
5033 // If we have a window, we know a mutation listener is registered, but it
5034 // might not be in our chain. If we don't have a window, we might have a
5035 // mutation listener. Check quickly to see.
5036 while (aNode) {
5037 EventListenerManager* manager = aNode->GetExistingListenerManager();
5038 if (manager && manager->HasMutationListeners()) {
5039 return true;
5042 aNode = aNode->GetParentNode();
5045 return false;
5048 /* static */
5049 bool nsContentUtils::HasMutationListeners(Document* aDocument, uint32_t aType) {
5050 nsPIDOMWindowInner* window =
5051 aDocument ? aDocument->GetInnerWindow() : nullptr;
5053 // This relies on EventListenerManager::AddEventListener, which sets
5054 // all mutation bits when there is a listener for DOMSubtreeModified event.
5055 return !window || window->HasMutationListeners(aType);
5058 void nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent) {
5059 MOZ_ASSERT(aChild, "Missing child");
5060 MOZ_ASSERT(aChild->GetParentNode() == aParent, "Wrong parent");
5061 MOZ_ASSERT(aChild->OwnerDoc() == aParent->OwnerDoc(), "Wrong owner-doc");
5063 // Having an explicit check here since it's an easy mistake to fall into,
5064 // and there might be existing code with problems. We'd rather be safe
5065 // than fire DOMNodeRemoved in all corner cases. We also rely on it for
5066 // nsAutoScriptBlockerSuppressNodeRemoved.
5067 if (!IsSafeToRunScript()) {
5068 // This checks that IsSafeToRunScript is true since we don't want to fire
5069 // events when that is false. We can't rely on EventDispatcher to assert
5070 // this in this situation since most of the time there are no mutation
5071 // event listeners, in which case we won't even attempt to dispatch events.
5072 // However this also allows for two exceptions. First off, we don't assert
5073 // if the mutation happens to native anonymous content since we never fire
5074 // mutation events on such content anyway.
5075 // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
5076 // that is a know case when we'd normally fire a mutation event, but can't
5077 // make that safe and so we suppress it at this time. Ideally this should
5078 // go away eventually.
5079 if (!aChild->IsInNativeAnonymousSubtree() &&
5080 !sDOMNodeRemovedSuppressCount) {
5081 NS_ERROR("Want to fire DOMNodeRemoved event, but it's not safe");
5082 WarnScriptWasIgnored(aChild->OwnerDoc());
5084 return;
5088 Document* doc = aParent->OwnerDoc();
5089 if (MOZ_UNLIKELY(doc->DevToolsWatchingDOMMutations()) &&
5090 aChild->IsInComposedDoc() && !aChild->ChromeOnlyAccess()) {
5091 DispatchChromeEvent(doc, aChild, u"devtoolschildremoved"_ns,
5092 CanBubble::eNo, Cancelable::eNo);
5096 if (HasMutationListeners(aChild, NS_EVENT_BITS_MUTATION_NODEREMOVED,
5097 aParent)) {
5098 InternalMutationEvent mutation(true, eLegacyNodeRemoved);
5099 mutation.mRelatedNode = aParent;
5101 mozAutoSubtreeModified subtree(aParent->OwnerDoc(), aParent);
5102 EventDispatcher::Dispatch(aChild, nullptr, &mutation);
5106 void nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments() {
5107 if (!sEventListenerManagersHash) {
5108 return;
5111 for (auto i = sEventListenerManagersHash->Iter(); !i.Done(); i.Next()) {
5112 auto entry = static_cast<EventListenerManagerMapEntry*>(i.Get());
5113 nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
5114 if (n && n->IsInComposedDoc() &&
5115 nsCCUncollectableMarker::InGeneration(
5116 n->OwnerDoc()->GetMarkedCCGeneration())) {
5117 entry->mListenerManager->MarkForCC();
5122 /* static */
5123 void nsContentUtils::TraverseListenerManager(
5124 nsINode* aNode, nsCycleCollectionTraversalCallback& cb) {
5125 if (!sEventListenerManagersHash) {
5126 // We're already shut down, just return.
5127 return;
5130 auto entry = static_cast<EventListenerManagerMapEntry*>(
5131 sEventListenerManagersHash->Search(aNode));
5132 if (entry) {
5133 CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
5134 "[via hash] mListenerManager");
5138 EventListenerManager* nsContentUtils::GetListenerManagerForNode(
5139 nsINode* aNode) {
5140 if (!sEventListenerManagersHash) {
5141 // We're already shut down, don't bother creating an event listener
5142 // manager.
5144 return nullptr;
5147 auto entry = static_cast<EventListenerManagerMapEntry*>(
5148 sEventListenerManagersHash->Add(aNode, fallible));
5150 if (!entry) {
5151 return nullptr;
5154 if (!entry->mListenerManager) {
5155 entry->mListenerManager = new EventListenerManager(aNode);
5157 aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
5160 return entry->mListenerManager;
5163 EventListenerManager* nsContentUtils::GetExistingListenerManagerForNode(
5164 const nsINode* aNode) {
5165 if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
5166 return nullptr;
5169 if (!sEventListenerManagersHash) {
5170 // We're already shut down, don't bother creating an event listener
5171 // manager.
5173 return nullptr;
5176 auto entry = static_cast<EventListenerManagerMapEntry*>(
5177 sEventListenerManagersHash->Search(aNode));
5178 if (entry) {
5179 return entry->mListenerManager;
5182 return nullptr;
5185 void nsContentUtils::AddEntryToDOMArenaTable(nsINode* aNode,
5186 DOMArena* aDOMArena) {
5187 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
5188 MOZ_ASSERT_IF(sDOMArenaHashtable, !sDOMArenaHashtable->Contains(aNode));
5189 MOZ_ASSERT(!aNode->HasFlag(NODE_KEEPS_DOMARENA));
5190 if (!sDOMArenaHashtable) {
5191 sDOMArenaHashtable =
5192 new nsRefPtrHashtable<nsPtrHashKey<const nsINode>, dom::DOMArena>();
5194 aNode->SetFlags(NODE_KEEPS_DOMARENA);
5195 sDOMArenaHashtable->InsertOrUpdate(aNode, RefPtr<DOMArena>(aDOMArena));
5198 already_AddRefed<DOMArena> nsContentUtils::TakeEntryFromDOMArenaTable(
5199 const nsINode* aNode) {
5200 MOZ_ASSERT(sDOMArenaHashtable->Contains(aNode));
5201 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
5202 RefPtr<DOMArena> arena;
5203 sDOMArenaHashtable->Remove(aNode, getter_AddRefs(arena));
5204 return arena.forget();
5207 /* static */
5208 void nsContentUtils::RemoveListenerManager(nsINode* aNode) {
5209 if (sEventListenerManagersHash) {
5210 auto entry = static_cast<EventListenerManagerMapEntry*>(
5211 sEventListenerManagersHash->Search(aNode));
5212 if (entry) {
5213 RefPtr<EventListenerManager> listenerManager;
5214 listenerManager.swap(entry->mListenerManager);
5215 // Remove the entry and *then* do operations that could cause further
5216 // modification of sEventListenerManagersHash. See bug 334177.
5217 sEventListenerManagersHash->RawRemove(entry);
5218 if (listenerManager) {
5219 listenerManager->Disconnect();
5225 /* static */
5226 bool nsContentUtils::IsValidNodeName(nsAtom* aLocalName, nsAtom* aPrefix,
5227 int32_t aNamespaceID) {
5228 if (aNamespaceID == kNameSpaceID_Unknown) {
5229 return false;
5232 if (!aPrefix) {
5233 // If the prefix is null, then either the QName must be xmlns or the
5234 // namespace must not be XMLNS.
5235 return (aLocalName == nsGkAtoms::xmlns) ==
5236 (aNamespaceID == kNameSpaceID_XMLNS);
5239 // If the prefix is non-null then the namespace must not be null.
5240 if (aNamespaceID == kNameSpaceID_None) {
5241 return false;
5244 // If the namespace is the XMLNS namespace then the prefix must be xmlns,
5245 // but the localname must not be xmlns.
5246 if (aNamespaceID == kNameSpaceID_XMLNS) {
5247 return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
5250 // If the namespace is not the XMLNS namespace then the prefix must not be
5251 // xmlns.
5252 // If the namespace is the XML namespace then the prefix can be anything.
5253 // If the namespace is not the XML namespace then the prefix must not be xml.
5254 return aPrefix != nsGkAtoms::xmlns &&
5255 (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
5258 already_AddRefed<DocumentFragment> nsContentUtils::CreateContextualFragment(
5259 nsINode* aContextNode, const nsAString& aFragment,
5260 bool aPreventScriptExecution, ErrorResult& aRv) {
5261 if (!aContextNode) {
5262 aRv.Throw(NS_ERROR_INVALID_ARG);
5263 return nullptr;
5266 // If we don't have a document here, we can't get the right security context
5267 // for compiling event handlers... so just bail out.
5268 RefPtr<Document> document = aContextNode->OwnerDoc();
5269 bool isHTML = document->IsHTMLDocument();
5271 if (isHTML) {
5272 RefPtr<DocumentFragment> frag = new (document->NodeInfoManager())
5273 DocumentFragment(document->NodeInfoManager());
5275 Element* element = aContextNode->GetAsElementOrParentElement();
5276 if (element && !element->IsHTMLElement(nsGkAtoms::html)) {
5277 aRv = ParseFragmentHTML(
5278 aFragment, frag, element->NodeInfo()->NameAtom(),
5279 element->GetNameSpaceID(),
5280 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
5281 aPreventScriptExecution);
5282 } else {
5283 aRv = ParseFragmentHTML(
5284 aFragment, frag, nsGkAtoms::body, kNameSpaceID_XHTML,
5285 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
5286 aPreventScriptExecution);
5289 return frag.forget();
5292 AutoTArray<nsString, 32> tagStack;
5293 nsAutoString uriStr, nameStr;
5294 for (Element* element : aContextNode->InclusiveAncestorsOfType<Element>()) {
5295 nsString& tagName = *tagStack.AppendElement();
5296 // It mostly doesn't actually matter what tag name we use here: XML doesn't
5297 // have parsing that depends on the open tag stack, apart from namespace
5298 // declarations. So this whole tagStack bit is just there to get the right
5299 // namespace declarations to the XML parser. That said, the parser _is_
5300 // going to create elements with the tag names we provide here, so we need
5301 // to make sure they are not names that can trigger custom element
5302 // constructors. Just make up a name that is never going to be a valid
5303 // custom element name.
5305 // The principled way to do this would probably be to add a new FromParser
5306 // value and make sure we use it when creating the context elements, then
5307 // make sure we teach all FromParser consumers (and in particular the custom
5308 // element code) about it as needed. But right now the XML parser never
5309 // actually uses FromParser values other than NOT_FROM_PARSER, and changing
5310 // that is pretty complicated.
5311 tagName.AssignLiteral("notacustomelement");
5313 // see if we need to add xmlns declarations
5314 uint32_t count = element->GetAttrCount();
5315 bool setDefaultNamespace = false;
5316 if (count > 0) {
5317 uint32_t index;
5319 for (index = 0; index < count; index++) {
5320 const BorrowedAttrInfo info = element->GetAttrInfoAt(index);
5321 const nsAttrName* name = info.mName;
5322 if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
5323 info.mValue->ToString(uriStr);
5325 // really want something like nsXMLContentSerializer::SerializeAttr
5326 tagName.AppendLiteral(" xmlns"); // space important
5327 if (name->GetPrefix()) {
5328 tagName.Append(char16_t(':'));
5329 name->LocalName()->ToString(nameStr);
5330 tagName.Append(nameStr);
5331 } else {
5332 setDefaultNamespace = true;
5334 tagName.AppendLiteral(R"(=")");
5335 tagName.Append(uriStr);
5336 tagName.Append('"');
5341 if (!setDefaultNamespace) {
5342 mozilla::dom::NodeInfo* info = element->NodeInfo();
5343 if (!info->GetPrefixAtom() && info->NamespaceID() != kNameSpaceID_None) {
5344 // We have no namespace prefix, but have a namespace ID. Push
5345 // default namespace attr in, so that our kids will be in our
5346 // namespace.
5347 info->GetNamespaceURI(uriStr);
5348 tagName.AppendLiteral(R"( xmlns=")");
5349 tagName.Append(uriStr);
5350 tagName.Append('"');
5355 RefPtr<DocumentFragment> frag;
5356 aRv = ParseFragmentXML(aFragment, document, tagStack, aPreventScriptExecution,
5357 -1, getter_AddRefs(frag));
5358 return frag.forget();
5361 /* static */
5362 void nsContentUtils::DropFragmentParsers() {
5363 NS_IF_RELEASE(sHTMLFragmentParser);
5364 NS_IF_RELEASE(sXMLFragmentParser);
5365 NS_IF_RELEASE(sXMLFragmentSink);
5368 /* static */
5369 void nsContentUtils::XPCOMShutdown() { nsContentUtils::DropFragmentParsers(); }
5371 /* Helper function to compuate Sanitization Flags for ParseFramentHTML/XML */
5372 uint32_t computeSanitizationFlags(nsIPrincipal* aPrincipal, int32_t aFlags) {
5373 uint32_t sanitizationFlags = 0;
5374 if (aPrincipal->IsSystemPrincipal()) {
5375 if (aFlags < 0) {
5376 // if this is a chrome-privileged document and no explicit flags
5377 // were passed, then use this sanitization flags.
5378 sanitizationFlags = nsIParserUtils::SanitizerAllowStyle |
5379 nsIParserUtils::SanitizerAllowComments |
5380 nsIParserUtils::SanitizerDropForms |
5381 nsIParserUtils::SanitizerLogRemovals;
5382 } else {
5383 // if the caller explicitly passes flags, then we use those
5384 // flags but additionally drop forms.
5385 sanitizationFlags = aFlags | nsIParserUtils::SanitizerDropForms;
5387 } else if (aFlags >= 0) {
5388 // aFlags by default is -1 and is only ever non equal to -1 if the
5389 // caller of ParseFragmentHTML/ParseFragmentXML is
5390 // ParserUtils::ParseFragment(). Only in that case we should use
5391 // the sanitization flags passed within aFlags.
5392 sanitizationFlags = aFlags;
5394 return sanitizationFlags;
5397 /* static */
5398 bool AllowsUnsanitizedContentForAboutNewTab(nsIPrincipal* aPrincipal) {
5399 if (StaticPrefs::dom_about_newtab_sanitization_enabled() ||
5400 !aPrincipal->SchemeIs("about")) {
5401 return false;
5403 uint32_t aboutModuleFlags = 0;
5404 aPrincipal->GetAboutModuleFlags(&aboutModuleFlags);
5405 return aboutModuleFlags & nsIAboutModule::ALLOW_UNSANITIZED_CONTENT;
5408 /* static */
5409 nsresult nsContentUtils::ParseFragmentHTML(
5410 const nsAString& aSourceBuffer, nsIContent* aTargetNode,
5411 nsAtom* aContextLocalName, int32_t aContextNamespace, bool aQuirks,
5412 bool aPreventScriptExecution, int32_t aFlags) {
5413 AutoTimelineMarker m(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");
5415 if (nsContentUtils::sFragmentParsingActive) {
5416 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5417 return NS_ERROR_DOM_INVALID_STATE_ERR;
5419 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5420 nsContentUtils::sFragmentParsingActive = true;
5421 if (!sHTMLFragmentParser) {
5422 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5423 // Now sHTMLFragmentParser owns the object
5426 nsCOMPtr<nsIPrincipal> nodePrincipal = aTargetNode->NodePrincipal();
5428 #ifdef DEBUG
5429 // aFlags should always be -1 unless the caller of ParseFragmentHTML
5430 // is ParserUtils::ParseFragment() which is the only caller that intends
5431 // sanitization. For all other callers we need to ensure to call
5432 // AuditParsingOfHTMLXMLFragments.
5433 if (aFlags < 0) {
5434 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5435 aSourceBuffer);
5437 #endif
5439 nsIContent* target = aTargetNode;
5441 RefPtr<Document> doc = aTargetNode->OwnerDoc();
5442 RefPtr<DocumentFragment> fragment;
5443 // We sanitize if the fragment occurs in a system privileged
5444 // context, an about: page, or if there are explicit sanitization flags.
5445 // Please note that about:blank and about:srcdoc inherit the security
5446 // context from the embedding context and hence are not loaded using
5447 // an about: scheme principal.
5448 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5449 nodePrincipal->SchemeIs("about") || aFlags >= 0;
5450 if (shouldSanitize &&
5451 !AllowsUnsanitizedContentForAboutNewTab(nodePrincipal)) {
5452 if (!doc->IsLoadedAsData()) {
5453 doc = nsContentUtils::CreateInertHTMLDocument(doc);
5454 if (!doc) {
5455 return NS_ERROR_FAILURE;
5458 fragment =
5459 new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager());
5460 target = fragment;
5463 nsresult rv = sHTMLFragmentParser->ParseFragment(
5464 aSourceBuffer, target, aContextLocalName, aContextNamespace, aQuirks,
5465 aPreventScriptExecution);
5466 NS_ENSURE_SUCCESS(rv, rv);
5468 if (fragment) {
5469 uint32_t sanitizationFlags =
5470 computeSanitizationFlags(nodePrincipal, aFlags);
5471 // Don't fire mutation events for nodes removed by the sanitizer.
5472 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5473 nsTreeSanitizer sanitizer(sanitizationFlags);
5474 sanitizer.Sanitize(fragment);
5476 ErrorResult error;
5477 aTargetNode->AppendChild(*fragment, error);
5478 rv = error.StealNSResult();
5481 return rv;
5484 /* static */
5485 nsresult nsContentUtils::ParseDocumentHTML(
5486 const nsAString& aSourceBuffer, Document* aTargetDocument,
5487 bool aScriptingEnabledForNoscriptParsing) {
5488 AutoTimelineMarker m(aTargetDocument->GetDocShell(), "Parse HTML");
5490 if (nsContentUtils::sFragmentParsingActive) {
5491 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5492 return NS_ERROR_DOM_INVALID_STATE_ERR;
5494 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5495 nsContentUtils::sFragmentParsingActive = true;
5496 if (!sHTMLFragmentParser) {
5497 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5498 // Now sHTMLFragmentParser owns the object
5500 nsresult rv = sHTMLFragmentParser->ParseDocument(
5501 aSourceBuffer, aTargetDocument, aScriptingEnabledForNoscriptParsing);
5502 return rv;
5505 /* static */
5506 nsresult nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
5507 Document* aDocument,
5508 nsTArray<nsString>& aTagStack,
5509 bool aPreventScriptExecution,
5510 int32_t aFlags,
5511 DocumentFragment** aReturn) {
5512 AutoTimelineMarker m(aDocument->GetDocShell(), "Parse XML");
5514 if (nsContentUtils::sFragmentParsingActive) {
5515 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5516 return NS_ERROR_DOM_INVALID_STATE_ERR;
5518 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5519 nsContentUtils::sFragmentParsingActive = true;
5520 if (!sXMLFragmentParser) {
5521 RefPtr<nsParser> parser = new nsParser();
5522 parser.forget(&sXMLFragmentParser);
5523 // sXMLFragmentParser now owns the parser
5525 if (!sXMLFragmentSink) {
5526 NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
5527 // sXMLFragmentSink now owns the sink
5529 nsCOMPtr<nsIContentSink> contentsink = do_QueryInterface(sXMLFragmentSink);
5530 MOZ_ASSERT(contentsink, "Sink doesn't QI to nsIContentSink!");
5531 sXMLFragmentParser->SetContentSink(contentsink);
5533 RefPtr<Document> doc;
5534 nsCOMPtr<nsIPrincipal> nodePrincipal = aDocument->NodePrincipal();
5536 #ifdef DEBUG
5537 // aFlags should always be -1 unless the caller of ParseFragmentXML
5538 // is ParserUtils::ParseFragment() which is the only caller that intends
5539 // sanitization. For all other callers we need to ensure to call
5540 // AuditParsingOfHTMLXMLFragments.
5541 if (aFlags < 0) {
5542 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5543 aSourceBuffer);
5545 #endif
5547 // We sanitize if the fragment occurs in a system privileged
5548 // context, an about: page, or if there are explicit sanitization flags.
5549 // Please note that about:blank and about:srcdoc inherit the security
5550 // context from the embedding context and hence are not loaded using
5551 // an about: scheme principal.
5552 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5553 nodePrincipal->SchemeIs("about") || aFlags >= 0;
5554 if (shouldSanitize && !aDocument->IsLoadedAsData()) {
5555 doc = nsContentUtils::CreateInertXMLDocument(aDocument);
5556 } else {
5557 doc = aDocument;
5560 sXMLFragmentSink->SetTargetDocument(doc);
5561 sXMLFragmentSink->SetPreventScriptExecution(aPreventScriptExecution);
5563 nsresult rv = sXMLFragmentParser->ParseFragment(aSourceBuffer, aTagStack);
5564 if (NS_FAILED(rv)) {
5565 // Drop the fragment parser and sink that might be in an inconsistent state
5566 NS_IF_RELEASE(sXMLFragmentParser);
5567 NS_IF_RELEASE(sXMLFragmentSink);
5568 return rv;
5571 rv = sXMLFragmentSink->FinishFragmentParsing(aReturn);
5573 sXMLFragmentParser->Reset();
5574 NS_ENSURE_SUCCESS(rv, rv);
5576 if (shouldSanitize) {
5577 uint32_t sanitizationFlags =
5578 computeSanitizationFlags(nodePrincipal, aFlags);
5579 // Don't fire mutation events for nodes removed by the sanitizer.
5580 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5581 nsTreeSanitizer sanitizer(sanitizationFlags);
5582 sanitizer.Sanitize(*aReturn);
5585 return rv;
5588 /* static */
5589 nsresult nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
5590 nsAString& aResultBuffer,
5591 uint32_t aFlags,
5592 uint32_t aWrapCol) {
5593 RefPtr<Document> document = nsContentUtils::CreateInertHTMLDocument(nullptr);
5594 if (!document) {
5595 return NS_ERROR_FAILURE;
5598 nsresult rv = nsContentUtils::ParseDocumentHTML(
5599 aSourceBuffer, document,
5600 !(aFlags & nsIDocumentEncoder::OutputNoScriptContent));
5601 NS_ENSURE_SUCCESS(rv, rv);
5603 nsCOMPtr<nsIDocumentEncoder> encoder = do_createDocumentEncoder("text/plain");
5605 rv = encoder->Init(document, u"text/plain"_ns, aFlags);
5606 NS_ENSURE_SUCCESS(rv, rv);
5608 encoder->SetWrapColumn(aWrapCol);
5610 return encoder->EncodeToString(aResultBuffer);
5613 static already_AddRefed<Document> CreateInertDocument(const Document* aTemplate,
5614 DocumentFlavor aFlavor) {
5615 if (aTemplate) {
5616 bool hasHad = true;
5617 nsIScriptGlobalObject* sgo = aTemplate->GetScriptHandlingObject(hasHad);
5618 NS_ENSURE_TRUE(sgo || !hasHad, nullptr);
5620 nsCOMPtr<Document> doc;
5621 nsresult rv = NS_NewDOMDocument(
5622 getter_AddRefs(doc), u""_ns, u""_ns, nullptr,
5623 aTemplate->GetDocumentURI(), aTemplate->GetDocBaseURI(),
5624 aTemplate->NodePrincipal(), true, sgo, aFlavor);
5625 if (NS_FAILED(rv)) {
5626 return nullptr;
5628 return doc.forget();
5630 nsCOMPtr<nsIURI> uri;
5631 NS_NewURI(getter_AddRefs(uri), "about:blank"_ns);
5632 if (!uri) {
5633 return nullptr;
5636 RefPtr<NullPrincipal> nullPrincipal =
5637 NullPrincipal::CreateWithoutOriginAttributes();
5638 if (!nullPrincipal) {
5639 return nullptr;
5642 nsCOMPtr<Document> doc;
5643 nsresult rv =
5644 NS_NewDOMDocument(getter_AddRefs(doc), u""_ns, u""_ns, nullptr, uri, uri,
5645 nullPrincipal, true, nullptr, aFlavor);
5646 if (NS_FAILED(rv)) {
5647 return nullptr;
5649 return doc.forget();
5652 /* static */
5653 already_AddRefed<Document> nsContentUtils::CreateInertXMLDocument(
5654 const Document* aTemplate) {
5655 return CreateInertDocument(aTemplate, DocumentFlavorXML);
5658 /* static */
5659 already_AddRefed<Document> nsContentUtils::CreateInertHTMLDocument(
5660 const Document* aTemplate) {
5661 return CreateInertDocument(aTemplate, DocumentFlavorHTML);
5664 /* static */
5665 nsresult nsContentUtils::SetNodeTextContent(nsIContent* aContent,
5666 const nsAString& aValue,
5667 bool aTryReuse) {
5668 // Fire DOMNodeRemoved mutation events before we do anything else.
5669 nsCOMPtr<nsIContent> owningContent;
5671 // Batch possible DOMSubtreeModified events.
5672 mozAutoSubtreeModified subtree(nullptr, nullptr);
5674 // Scope firing mutation events so that we don't carry any state that
5675 // might be stale
5677 // We're relying on mozAutoSubtreeModified to keep a strong reference if
5678 // needed.
5679 Document* doc = aContent->OwnerDoc();
5681 // Optimize the common case of there being no observers
5682 if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
5683 subtree.UpdateTarget(doc, nullptr);
5684 owningContent = aContent;
5685 nsCOMPtr<nsINode> child;
5686 bool skipFirst = aTryReuse;
5687 for (child = aContent->GetFirstChild();
5688 child && child->GetParentNode() == aContent;
5689 child = child->GetNextSibling()) {
5690 if (skipFirst && child->IsText()) {
5691 skipFirst = false;
5692 continue;
5694 nsContentUtils::MaybeFireNodeRemoved(child, aContent);
5699 // Might as well stick a batch around this since we're performing several
5700 // mutations.
5701 mozAutoDocUpdate updateBatch(aContent->GetComposedDoc(), true);
5702 nsAutoMutationBatch mb;
5704 if (aTryReuse && !aValue.IsEmpty()) {
5705 // Let's remove nodes until we find a eTEXT.
5706 while (aContent->HasChildren()) {
5707 nsIContent* child = aContent->GetFirstChild();
5708 if (child->IsText()) {
5709 break;
5711 aContent->RemoveChildNode(child, true);
5714 // If we have a node, it must be a eTEXT and we reuse it.
5715 if (aContent->HasChildren()) {
5716 nsIContent* child = aContent->GetFirstChild();
5717 nsresult rv = child->AsText()->SetText(aValue, true);
5718 NS_ENSURE_SUCCESS(rv, rv);
5720 // All the following nodes, if they exist, must be deleted.
5721 while (nsIContent* nextChild = child->GetNextSibling()) {
5722 aContent->RemoveChildNode(nextChild, true);
5726 if (aContent->HasChildren()) {
5727 return NS_OK;
5729 } else {
5730 mb.Init(aContent, true, false);
5731 while (aContent->HasChildren()) {
5732 aContent->RemoveChildNode(aContent->GetFirstChild(), true);
5735 mb.RemovalDone();
5737 if (aValue.IsEmpty()) {
5738 return NS_OK;
5741 RefPtr<nsTextNode> textContent = new (aContent->NodeInfo()->NodeInfoManager())
5742 nsTextNode(aContent->NodeInfo()->NodeInfoManager());
5744 textContent->SetText(aValue, true);
5746 ErrorResult rv;
5747 aContent->AppendChildTo(textContent, true, rv);
5748 mb.NodesAdded();
5749 return rv.StealNSResult();
5752 static bool AppendNodeTextContentsRecurse(const nsINode* aNode,
5753 nsAString& aResult,
5754 const fallible_t& aFallible) {
5755 for (nsIContent* child = aNode->GetFirstChild(); child;
5756 child = child->GetNextSibling()) {
5757 if (child->IsElement()) {
5758 bool ok = AppendNodeTextContentsRecurse(child, aResult, aFallible);
5759 if (!ok) {
5760 return false;
5762 } else if (Text* text = child->GetAsText()) {
5763 bool ok = text->AppendTextTo(aResult, aFallible);
5764 if (!ok) {
5765 return false;
5770 return true;
5773 /* static */
5774 bool nsContentUtils::AppendNodeTextContent(const nsINode* aNode, bool aDeep,
5775 nsAString& aResult,
5776 const fallible_t& aFallible) {
5777 if (const Text* text = aNode->GetAsText()) {
5778 return text->AppendTextTo(aResult, aFallible);
5780 if (aDeep) {
5781 return AppendNodeTextContentsRecurse(aNode, aResult, aFallible);
5784 for (nsIContent* child = aNode->GetFirstChild(); child;
5785 child = child->GetNextSibling()) {
5786 if (Text* text = child->GetAsText()) {
5787 bool ok = text->AppendTextTo(aResult, fallible);
5788 if (!ok) {
5789 return false;
5793 return true;
5796 bool nsContentUtils::HasNonEmptyTextContent(
5797 nsINode* aNode, TextContentDiscoverMode aDiscoverMode) {
5798 for (nsIContent* child = aNode->GetFirstChild(); child;
5799 child = child->GetNextSibling()) {
5800 if (child->IsText() && child->TextLength() > 0) {
5801 return true;
5804 if (aDiscoverMode == eRecurseIntoChildren &&
5805 HasNonEmptyTextContent(child, aDiscoverMode)) {
5806 return true;
5810 return false;
5813 /* static */
5814 bool nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
5815 const nsINode* aOtherNode) {
5816 MOZ_ASSERT(aNode, "Must have a node to work with");
5817 MOZ_ASSERT(aOtherNode, "Must have a content to work with");
5819 const bool anon = aNode->IsInNativeAnonymousSubtree();
5820 if (anon != aOtherNode->IsInNativeAnonymousSubtree()) {
5821 return false;
5824 if (anon) {
5825 return aOtherNode->GetClosestNativeAnonymousSubtreeRoot() ==
5826 aNode->GetClosestNativeAnonymousSubtreeRoot();
5829 // FIXME: This doesn't deal with disconnected nodes whatsoever, but it didn't
5830 // use to either. Maybe that's fine.
5831 return aNode->GetContainingShadow() == aOtherNode->GetContainingShadow();
5834 /* static */
5835 bool nsContentUtils::IsInInteractiveHTMLContent(const Element* aElement,
5836 const Element* aStop) {
5837 const Element* element = aElement;
5838 while (element && element != aStop) {
5839 if (element->IsInteractiveHTMLContent()) {
5840 return true;
5842 element = element->GetFlattenedTreeParentElement();
5844 return false;
5847 /* static */
5848 void nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling) {
5849 IMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
5852 /* static */
5853 bool nsContentUtils::SchemeIs(nsIURI* aURI, const char* aScheme) {
5854 nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
5855 NS_ENSURE_TRUE(baseURI, false);
5856 return baseURI->SchemeIs(aScheme);
5859 bool nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal) {
5860 return aPrincipal && aPrincipal->GetIsExpandedPrincipal();
5863 bool nsContentUtils::IsSystemOrExpandedPrincipal(nsIPrincipal* aPrincipal) {
5864 return (aPrincipal && aPrincipal->IsSystemPrincipal()) ||
5865 IsExpandedPrincipal(aPrincipal);
5868 nsIPrincipal* nsContentUtils::GetSystemPrincipal() {
5869 MOZ_ASSERT(IsInitialized());
5870 return sSystemPrincipal;
5873 bool nsContentUtils::CombineResourcePrincipals(
5874 nsCOMPtr<nsIPrincipal>* aResourcePrincipal, nsIPrincipal* aExtraPrincipal) {
5875 if (!aExtraPrincipal) {
5876 return false;
5878 if (!*aResourcePrincipal) {
5879 *aResourcePrincipal = aExtraPrincipal;
5880 return true;
5882 if (*aResourcePrincipal == aExtraPrincipal) {
5883 return false;
5885 bool subsumes;
5886 if (NS_SUCCEEDED(
5887 (*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
5888 subsumes) {
5889 return false;
5891 *aResourcePrincipal = sSystemPrincipal;
5892 return true;
5895 /* static */
5896 void nsContentUtils::TriggerLink(nsIContent* aContent, nsIURI* aLinkURI,
5897 const nsString& aTargetSpec, bool aClick,
5898 bool aIsTrusted) {
5899 MOZ_ASSERT(aLinkURI, "No link URI");
5901 if (aContent->IsEditable() || !aContent->OwnerDoc()->LinkHandlingEnabled()) {
5902 return;
5905 nsCOMPtr<nsIDocShell> docShell = aContent->OwnerDoc()->GetDocShell();
5906 if (!docShell) {
5907 return;
5910 if (!aClick) {
5911 nsDocShell::Cast(docShell)->OnOverLink(aContent, aLinkURI, aTargetSpec);
5912 return;
5915 // Check that this page is allowed to load this URI.
5916 nsresult proceed = NS_OK;
5918 if (sSecurityManager) {
5919 uint32_t flag = static_cast<uint32_t>(nsIScriptSecurityManager::STANDARD);
5920 proceed = sSecurityManager->CheckLoadURIWithPrincipal(
5921 aContent->NodePrincipal(), aLinkURI, flag,
5922 aContent->OwnerDoc()->InnerWindowID());
5925 // Only pass off the click event if the script security manager says it's ok.
5926 // We need to rest aTargetSpec for forced downloads.
5927 if (NS_SUCCEEDED(proceed)) {
5928 // A link/area element with a download attribute is allowed to set
5929 // a pseudo Content-Disposition header.
5930 // For security reasons we only allow websites to declare same-origin
5931 // resources as downloadable. If this check fails we will just do the normal
5932 // thing (i.e. navigate to the resource).
5933 nsAutoString fileName;
5934 if ((!aContent->IsHTMLElement(nsGkAtoms::a) &&
5935 !aContent->IsHTMLElement(nsGkAtoms::area) &&
5936 !aContent->IsSVGElement(nsGkAtoms::a)) ||
5937 !aContent->AsElement()->GetAttr(nsGkAtoms::download, fileName) ||
5938 NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, true))) {
5939 fileName.SetIsVoid(true); // No actionable download attribute was found.
5942 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aContent->NodePrincipal();
5943 nsCOMPtr<nsIContentSecurityPolicy> csp = aContent->GetCsp();
5945 // Sanitize fileNames containing null characters by replacing them with
5946 // underscores.
5947 if (!fileName.IsVoid()) {
5948 fileName.ReplaceChar(char16_t(0), '_');
5950 nsDocShell::Cast(docShell)->OnLinkClick(
5951 aContent, aLinkURI, fileName.IsVoid() ? aTargetSpec : u""_ns, fileName,
5952 nullptr, nullptr, UserActivation::IsHandlingUserInput(), aIsTrusted,
5953 triggeringPrincipal, csp);
5957 /* static */
5958 void nsContentUtils::GetLinkLocation(Element* aElement,
5959 nsString& aLocationString) {
5960 nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
5961 if (hrefURI) {
5962 nsAutoCString specUTF8;
5963 nsresult rv = hrefURI->GetSpec(specUTF8);
5964 if (NS_SUCCEEDED(rv)) CopyUTF8toUTF16(specUTF8, aLocationString);
5968 /* static */
5969 nsIWidget* nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget) {
5970 if (!aWidget) return nullptr;
5972 return aWidget->GetTopLevelWidget();
5975 /* static */
5976 const nsDependentString nsContentUtils::GetLocalizedEllipsis() {
5977 static char16_t sBuf[4] = {0, 0, 0, 0};
5978 if (!sBuf[0]) {
5979 if (!SpoofLocaleEnglish()) {
5980 nsAutoString tmp;
5981 Preferences::GetLocalizedString("intl.ellipsis", tmp);
5982 uint32_t len =
5983 std::min(uint32_t(tmp.Length()), uint32_t(ArrayLength(sBuf) - 1));
5984 CopyUnicodeTo(tmp, 0, sBuf, len);
5986 if (!sBuf[0]) sBuf[0] = char16_t(0x2026);
5988 return nsDependentString(sBuf);
5991 /* static */
5992 void nsContentUtils::AddScriptBlocker() {
5993 MOZ_ASSERT(NS_IsMainThread());
5994 if (!sScriptBlockerCount) {
5995 MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
5996 "Should not already have a count");
5997 sRunnersCountAtFirstBlocker =
5998 sBlockedScriptRunners ? sBlockedScriptRunners->Length() : 0;
6000 ++sScriptBlockerCount;
6003 #ifdef DEBUG
6004 static bool sRemovingScriptBlockers = false;
6005 #endif
6007 /* static */
6008 void nsContentUtils::RemoveScriptBlocker() {
6009 MOZ_ASSERT(NS_IsMainThread());
6010 MOZ_ASSERT(!sRemovingScriptBlockers);
6011 NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
6012 --sScriptBlockerCount;
6013 if (sScriptBlockerCount) {
6014 return;
6017 if (!sBlockedScriptRunners) {
6018 return;
6021 uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
6022 uint32_t lastBlocker = sBlockedScriptRunners->Length();
6023 uint32_t originalFirstBlocker = firstBlocker;
6024 uint32_t blockersCount = lastBlocker - firstBlocker;
6025 sRunnersCountAtFirstBlocker = 0;
6026 NS_ASSERTION(firstBlocker <= lastBlocker, "bad sRunnersCountAtFirstBlocker");
6028 while (firstBlocker < lastBlocker) {
6029 nsCOMPtr<nsIRunnable> runnable;
6030 runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
6031 ++firstBlocker;
6033 // Calling the runnable can reenter us
6035 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
6036 runnable->Run();
6038 // So can dropping the reference to the runnable
6039 runnable = nullptr;
6041 NS_ASSERTION(sRunnersCountAtFirstBlocker == 0, "Bad count");
6042 NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
6044 #ifdef DEBUG
6045 AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
6046 sRemovingScriptBlockers = true;
6047 #endif
6048 sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
6051 /* static */
6052 already_AddRefed<nsPIDOMWindowOuter>
6053 nsContentUtils::GetMostRecentNonPBWindow() {
6054 nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
6056 nsCOMPtr<mozIDOMWindowProxy> window;
6057 wm->GetMostRecentNonPBWindow(u"navigator:browser", getter_AddRefs(window));
6058 nsCOMPtr<nsPIDOMWindowOuter> pwindow;
6059 pwindow = do_QueryInterface(window);
6061 return pwindow.forget();
6064 /* static */
6065 void nsContentUtils::WarnScriptWasIgnored(Document* aDocument) {
6066 nsAutoString msg;
6067 bool privateBrowsing = false;
6068 bool chromeContext = false;
6070 if (aDocument) {
6071 nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
6072 if (uri) {
6073 msg.Append(NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()));
6074 msg.AppendLiteral(" : ");
6076 privateBrowsing =
6077 !!aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
6078 chromeContext = aDocument->NodePrincipal()->IsSystemPrincipal();
6081 msg.AppendLiteral(
6082 "Unable to run script because scripts are blocked internally.");
6083 LogSimpleConsoleError(msg, "DOM"_ns, privateBrowsing, chromeContext);
6086 /* static */
6087 void nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable) {
6088 nsCOMPtr<nsIRunnable> runnable = aRunnable;
6089 if (!runnable) {
6090 return;
6093 if (sScriptBlockerCount) {
6094 sBlockedScriptRunners->AppendElement(runnable.forget());
6095 return;
6098 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
6099 runnable->Run();
6102 /* static */
6103 void nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) {
6104 nsCOMPtr<nsIRunnable> runnable = aRunnable;
6105 AddScriptRunner(runnable.forget());
6108 /* static */ bool nsContentUtils::IsSafeToRunScript() {
6109 MOZ_ASSERT(NS_IsMainThread(),
6110 "This static variable only makes sense on the main thread!");
6111 return sScriptBlockerCount == 0;
6114 /* static */
6115 void nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable) {
6116 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6117 CycleCollectedJSContext::Get()->RunInStableState(std::move(aRunnable));
6120 /* static */
6121 void nsContentUtils::AddPendingIDBTransaction(
6122 already_AddRefed<nsIRunnable> aTransaction) {
6123 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6124 CycleCollectedJSContext::Get()->AddPendingIDBTransaction(
6125 std::move(aTransaction));
6128 /* static */
6129 bool nsContentUtils::IsInStableOrMetaStableState() {
6130 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6131 return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
6134 /* static */
6135 void nsContentUtils::HidePopupsInDocument(Document* aDocument) {
6136 RefPtr<nsXULPopupManager> pm = nsXULPopupManager::GetInstance();
6137 if (!pm || !aDocument) {
6138 return;
6140 nsCOMPtr<nsIDocShellTreeItem> docShellToHide = aDocument->GetDocShell();
6141 if (docShellToHide) {
6142 pm->HidePopupsInDocShell(docShellToHide);
6146 /* static */
6147 already_AddRefed<nsIDragSession> nsContentUtils::GetDragSession() {
6148 nsCOMPtr<nsIDragSession> dragSession;
6149 nsCOMPtr<nsIDragService> dragService =
6150 do_GetService("@mozilla.org/widget/dragservice;1");
6151 if (dragService) dragService->GetCurrentSession(getter_AddRefs(dragSession));
6152 return dragSession.forget();
6155 /* static */
6156 nsresult nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) {
6157 if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) {
6158 return NS_OK;
6161 // For dragstart events, the data transfer object is
6162 // created before the event fires, so it should already be set. For other
6163 // drag events, get the object from the drag session.
6164 NS_ASSERTION(aDragEvent->mMessage != eDragStart,
6165 "draggesture event created without a dataTransfer");
6167 nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
6168 NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
6170 RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
6171 if (!initialDataTransfer) {
6172 // A dataTransfer won't exist when a drag was started by some other
6173 // means, for instance calling the drag service directly, or a drag
6174 // from another application. In either case, a new dataTransfer should
6175 // be created that reflects the data.
6176 initialDataTransfer =
6177 new DataTransfer(aDragEvent->mTarget, aDragEvent->mMessage, true, -1);
6179 // now set it in the drag session so we don't need to create it again
6180 dragSession->SetDataTransfer(initialDataTransfer);
6183 bool isCrossDomainSubFrameDrop = false;
6184 if (aDragEvent->mMessage == eDrop) {
6185 isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
6188 // each event should use a clone of the original dataTransfer.
6189 initialDataTransfer->Clone(
6190 aDragEvent->mTarget, aDragEvent->mMessage, aDragEvent->mUserCancelled,
6191 isCrossDomainSubFrameDrop, getter_AddRefs(aDragEvent->mDataTransfer));
6192 if (NS_WARN_IF(!aDragEvent->mDataTransfer)) {
6193 return NS_ERROR_OUT_OF_MEMORY;
6196 // for the dragenter and dragover events, initialize the drop effect
6197 // from the drop action, which platform specific widget code sets before
6198 // the event is fired based on the keyboard state.
6199 if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
6200 uint32_t action;
6201 dragSession->GetDragAction(&action);
6202 uint32_t effectAllowed = aDragEvent->mDataTransfer->EffectAllowedInt();
6203 aDragEvent->mDataTransfer->SetDropEffectInt(
6204 FilterDropEffect(action, effectAllowed));
6205 } else if (aDragEvent->mMessage == eDrop ||
6206 aDragEvent->mMessage == eDragEnd) {
6207 // For the drop and dragend events, set the drop effect based on the
6208 // last value that the dropEffect had. This will have been set in
6209 // EventStateManager::PostHandleEvent for the last dragenter or
6210 // dragover event.
6211 aDragEvent->mDataTransfer->SetDropEffectInt(
6212 initialDataTransfer->DropEffectInt());
6215 return NS_OK;
6218 /* static */
6219 uint32_t nsContentUtils::FilterDropEffect(uint32_t aAction,
6220 uint32_t aEffectAllowed) {
6221 // It is possible for the drag action to include more than one action, but
6222 // the widget code which sets the action from the keyboard state should only
6223 // be including one. If multiple actions were set, we just consider them in
6224 // the following order:
6225 // copy, link, move
6226 if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
6227 aAction = nsIDragService::DRAGDROP_ACTION_COPY;
6228 else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
6229 aAction = nsIDragService::DRAGDROP_ACTION_LINK;
6230 else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
6231 aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
6233 // Filter the action based on the effectAllowed. If the effectAllowed
6234 // doesn't include the action, then that action cannot be done, so adjust
6235 // the action to something that is allowed. For a copy, adjust to move or
6236 // link. For a move, adjust to copy or link. For a link, adjust to move or
6237 // link. Otherwise, use none.
6238 if (aAction & aEffectAllowed ||
6239 aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
6240 return aAction;
6241 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
6242 return nsIDragService::DRAGDROP_ACTION_MOVE;
6243 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
6244 return nsIDragService::DRAGDROP_ACTION_COPY;
6245 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
6246 return nsIDragService::DRAGDROP_ACTION_LINK;
6247 return nsIDragService::DRAGDROP_ACTION_NONE;
6250 /* static */
6251 bool nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession,
6252 WidgetDragEvent* aDropEvent) {
6253 nsCOMPtr<nsIContent> target =
6254 nsIContent::FromEventTargetOrNull(aDropEvent->mOriginalTarget);
6255 if (!target) {
6256 return true;
6259 // Always allow dropping onto chrome shells.
6260 BrowsingContext* targetBC = target->OwnerDoc()->GetBrowsingContext();
6261 if (targetBC->IsChrome()) {
6262 return false;
6265 WindowContext* targetWC = target->OwnerDoc()->GetWindowContext();
6267 // If there is no source browsing context, then this is a drag from another
6268 // application, which should be allowed.
6269 RefPtr<WindowContext> sourceWC;
6270 aDragSession->GetSourceWindowContext(getter_AddRefs(sourceWC));
6271 if (sourceWC) {
6272 // Get each successive parent of the source document and compare it to
6273 // the drop document. If they match, then this is a drag from a child frame.
6274 for (sourceWC = sourceWC->GetParentWindowContext(); sourceWC;
6275 sourceWC = sourceWC->GetParentWindowContext()) {
6276 // If the source and the target match, then the drag started in a
6277 // descendant frame. If the source is discarded, err on the side of
6278 // caution and treat it as a subframe drag.
6279 if (sourceWC == targetWC || sourceWC->IsDiscarded()) {
6280 return true;
6285 return false;
6288 /* static */
6289 bool nsContentUtils::URIIsLocalFile(nsIURI* aURI) {
6290 bool isFile;
6291 nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
6293 // Important: we do NOT test the entire URI chain here!
6294 return util &&
6295 NS_SUCCEEDED(util->ProtocolHasFlags(
6296 aURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, &isFile)) &&
6297 isFile;
6300 /* static */
6301 JSContext* nsContentUtils::GetCurrentJSContext() {
6302 MOZ_ASSERT(IsInitialized());
6303 if (!IsJSAPIActive()) {
6304 return nullptr;
6306 return danger::GetJSContext();
6309 template <typename StringType, typename CharType>
6310 void _ASCIIToLowerInSitu(StringType& aStr) {
6311 CharType* iter = aStr.BeginWriting();
6312 CharType* end = aStr.EndWriting();
6313 MOZ_ASSERT(iter && end);
6315 while (iter != end) {
6316 CharType c = *iter;
6317 if (c >= 'A' && c <= 'Z') {
6318 *iter = c + ('a' - 'A');
6320 ++iter;
6324 /* static */
6325 void nsContentUtils::ASCIIToLower(nsAString& aStr) {
6326 return _ASCIIToLowerInSitu<nsAString, char16_t>(aStr);
6329 /* static */
6330 void nsContentUtils::ASCIIToLower(nsACString& aStr) {
6331 return _ASCIIToLowerInSitu<nsACString, char>(aStr);
6334 template <typename StringType, typename CharType>
6335 void _ASCIIToLowerCopy(const StringType& aSource, StringType& aDest) {
6336 uint32_t len = aSource.Length();
6337 aDest.SetLength(len);
6338 MOZ_ASSERT(aDest.Length() == len);
6340 CharType* dest = aDest.BeginWriting();
6341 MOZ_ASSERT(dest);
6343 const CharType* iter = aSource.BeginReading();
6344 const CharType* end = aSource.EndReading();
6345 while (iter != end) {
6346 CharType c = *iter;
6347 *dest = (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
6348 ++iter;
6349 ++dest;
6353 /* static */
6354 void nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest) {
6355 return _ASCIIToLowerCopy<nsAString, char16_t>(aSource, aDest);
6358 /* static */
6359 void nsContentUtils::ASCIIToLower(const nsACString& aSource,
6360 nsACString& aDest) {
6361 return _ASCIIToLowerCopy<nsACString, char>(aSource, aDest);
6364 template <typename StringType, typename CharType>
6365 void _ASCIIToUpperInSitu(StringType& aStr) {
6366 CharType* iter = aStr.BeginWriting();
6367 CharType* end = aStr.EndWriting();
6368 MOZ_ASSERT(iter && end);
6370 while (iter != end) {
6371 CharType c = *iter;
6372 if (c >= 'a' && c <= 'z') {
6373 *iter = c + ('A' - 'a');
6375 ++iter;
6379 /* static */
6380 void nsContentUtils::ASCIIToUpper(nsAString& aStr) {
6381 return _ASCIIToUpperInSitu<nsAString, char16_t>(aStr);
6384 /* static */
6385 void nsContentUtils::ASCIIToUpper(nsACString& aStr) {
6386 return _ASCIIToUpperInSitu<nsACString, char>(aStr);
6389 template <typename StringType, typename CharType>
6390 void _ASCIIToUpperCopy(const StringType& aSource, StringType& aDest) {
6391 uint32_t len = aSource.Length();
6392 aDest.SetLength(len);
6393 MOZ_ASSERT(aDest.Length() == len);
6395 CharType* dest = aDest.BeginWriting();
6396 MOZ_ASSERT(dest);
6398 const CharType* iter = aSource.BeginReading();
6399 const CharType* end = aSource.EndReading();
6400 while (iter != end) {
6401 CharType c = *iter;
6402 *dest = (c >= 'a' && c <= 'z') ? c + ('A' - 'a') : c;
6403 ++iter;
6404 ++dest;
6408 /* static */
6409 void nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest) {
6410 return _ASCIIToUpperCopy<nsAString, char16_t>(aSource, aDest);
6413 /* static */
6414 void nsContentUtils::ASCIIToUpper(const nsACString& aSource,
6415 nsACString& aDest) {
6416 return _ASCIIToUpperCopy<nsACString, char>(aSource, aDest);
6419 /* static */
6420 bool nsContentUtils::EqualsIgnoreASCIICase(nsAtom* aAtom1, nsAtom* aAtom2) {
6421 if (aAtom1 == aAtom2) {
6422 return true;
6425 // If both are ascii lowercase already, we know that the slow comparison
6426 // below is going to return false.
6427 if (aAtom1->IsAsciiLowercase() && aAtom2->IsAsciiLowercase()) {
6428 return false;
6431 return EqualsIgnoreASCIICase(nsDependentAtomString(aAtom1),
6432 nsDependentAtomString(aAtom2));
6435 /* static */
6436 bool nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
6437 const nsAString& aStr2) {
6438 uint32_t len = aStr1.Length();
6439 if (len != aStr2.Length()) {
6440 return false;
6443 const char16_t* str1 = aStr1.BeginReading();
6444 const char16_t* str2 = aStr2.BeginReading();
6445 const char16_t* end = str1 + len;
6447 while (str1 < end) {
6448 char16_t c1 = *str1++;
6449 char16_t c2 = *str2++;
6451 // First check if any bits other than the 0x0020 differs
6452 if ((c1 ^ c2) & 0xffdf) {
6453 return false;
6456 // We know they can only differ in the 0x0020 bit.
6457 // Likely the two chars are the same, so check that first
6458 if (c1 != c2) {
6459 // They do differ, but since it's only in the 0x0020 bit, check if it's
6460 // the same ascii char, but just differing in case
6461 char16_t c1Upper = c1 & 0xffdf;
6462 if (!('A' <= c1Upper && c1Upper <= 'Z')) {
6463 return false;
6468 return true;
6471 /* static */
6472 bool nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr) {
6473 const char16_t* iter = aStr.BeginReading();
6474 const char16_t* end = aStr.EndReading();
6475 while (iter != end) {
6476 char16_t c = *iter;
6477 if (c >= 'A' && c <= 'Z') {
6478 return true;
6480 ++iter;
6483 return false;
6486 /* static */
6487 nsIInterfaceRequestor* nsContentUtils::SameOriginChecker() {
6488 if (!sSameOriginChecker) {
6489 sSameOriginChecker = new SameOriginCheckerImpl();
6490 NS_ADDREF(sSameOriginChecker);
6492 return sSameOriginChecker;
6495 /* static */
6496 nsresult nsContentUtils::CheckSameOrigin(nsIChannel* aOldChannel,
6497 nsIChannel* aNewChannel) {
6498 if (!nsContentUtils::GetSecurityManager()) return NS_ERROR_NOT_AVAILABLE;
6500 nsCOMPtr<nsIPrincipal> oldPrincipal;
6501 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
6502 aOldChannel, getter_AddRefs(oldPrincipal));
6504 nsCOMPtr<nsIURI> newURI;
6505 aNewChannel->GetURI(getter_AddRefs(newURI));
6506 nsCOMPtr<nsIURI> newOriginalURI;
6507 aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
6509 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
6511 nsresult rv = oldPrincipal->CheckMayLoad(newURI, false);
6512 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
6513 rv = oldPrincipal->CheckMayLoad(newOriginalURI, false);
6516 return rv;
6519 NS_IMPL_ISUPPORTS(SameOriginCheckerImpl, nsIChannelEventSink,
6520 nsIInterfaceRequestor)
6522 NS_IMETHODIMP
6523 SameOriginCheckerImpl::AsyncOnChannelRedirect(
6524 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
6525 nsIAsyncVerifyRedirectCallback* cb) {
6526 MOZ_ASSERT(aNewChannel, "Redirecting to null channel?");
6528 nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
6529 if (NS_SUCCEEDED(rv)) {
6530 cb->OnRedirectVerifyCallback(NS_OK);
6533 return rv;
6536 NS_IMETHODIMP
6537 SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult) {
6538 return QueryInterface(aIID, aResult);
6541 /* static */
6542 nsresult nsContentUtils::GetWebExposedOriginSerialization(nsIURI* aURI,
6543 nsACString& aOrigin) {
6544 MOZ_ASSERT(aURI, "missing uri");
6546 // For Blob URI, the path is the URL of the owning page.
6547 if (aURI->SchemeIs(BLOBURI_SCHEME)) {
6548 nsAutoCString path;
6549 nsresult rv = aURI->GetPathQueryRef(path);
6550 NS_ENSURE_SUCCESS(rv, rv);
6552 nsCOMPtr<nsIURI> uri;
6553 rv = NS_NewURI(getter_AddRefs(uri), path);
6554 if (NS_FAILED(rv)) {
6555 aOrigin.AssignLiteral("null");
6556 return NS_OK;
6559 return GetWebExposedOriginSerialization(uri, aOrigin);
6562 aOrigin.Truncate();
6564 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
6565 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
6567 nsAutoCString host;
6568 nsresult rv = uri->GetAsciiHost(host);
6570 if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
6571 nsAutoCString userPass;
6572 uri->GetUserPass(userPass);
6574 nsAutoCString prePath;
6575 if (!userPass.IsEmpty()) {
6576 rv = NS_MutateURI(uri).SetUserPass(""_ns).Finalize(uri);
6577 NS_ENSURE_SUCCESS(rv, rv);
6580 rv = uri->GetPrePath(prePath);
6581 NS_ENSURE_SUCCESS(rv, rv);
6583 aOrigin = prePath;
6584 } else {
6585 aOrigin.AssignLiteral("null");
6588 return NS_OK;
6591 /* static */
6592 nsresult nsContentUtils::GetWebExposedOriginSerialization(
6593 nsIPrincipal* aPrincipal, nsAString& aOrigin) {
6594 MOZ_ASSERT(aPrincipal, "missing principal");
6596 aOrigin.Truncate();
6597 nsAutoCString webExposedOriginSerialization;
6599 nsresult rv = aPrincipal->GetWebExposedOriginSerialization(
6600 webExposedOriginSerialization);
6601 if (NS_FAILED(rv)) {
6602 webExposedOriginSerialization.AssignLiteral("null");
6605 CopyUTF8toUTF16(webExposedOriginSerialization, aOrigin);
6606 return NS_OK;
6609 /* static */
6610 nsresult nsContentUtils::GetWebExposedOriginSerialization(nsIURI* aURI,
6611 nsAString& aOrigin) {
6612 MOZ_ASSERT(aURI, "missing uri");
6613 nsresult rv;
6615 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
6616 // Check if either URI has a special origin.
6617 nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin =
6618 do_QueryInterface(aURI);
6619 if (uriWithSpecialOrigin) {
6620 nsCOMPtr<nsIURI> origin;
6621 rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin));
6622 NS_ENSURE_SUCCESS(rv, rv);
6624 return GetWebExposedOriginSerialization(origin, aOrigin);
6626 #endif
6628 nsAutoCString webExposedOriginSerialization;
6629 rv = GetWebExposedOriginSerialization(aURI, webExposedOriginSerialization);
6630 NS_ENSURE_SUCCESS(rv, rv);
6632 CopyUTF8toUTF16(webExposedOriginSerialization, aOrigin);
6633 return NS_OK;
6636 /* static */
6637 bool nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal,
6638 nsIChannel* aChannel,
6639 bool aAllowIfInheritsPrincipal) {
6640 nsCOMPtr<nsIURI> channelURI;
6641 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
6642 NS_ENSURE_SUCCESS(rv, false);
6644 return NS_SUCCEEDED(
6645 aPrincipal->CheckMayLoad(channelURI, aAllowIfInheritsPrincipal));
6648 /* static */
6649 bool nsContentUtils::CanAccessNativeAnon() {
6650 return LegacyIsCallerChromeOrNativeCode();
6653 /* static */
6654 nsresult nsContentUtils::DispatchXULCommand(nsIContent* aTarget, bool aTrusted,
6655 Event* aSourceEvent,
6656 PresShell* aPresShell, bool aCtrl,
6657 bool aAlt, bool aShift, bool aMeta,
6658 uint16_t aInputSource,
6659 int16_t aButton) {
6660 NS_ENSURE_STATE(aTarget);
6661 Document* doc = aTarget->OwnerDoc();
6662 nsPresContext* presContext = doc->GetPresContext();
6664 RefPtr<XULCommandEvent> xulCommand =
6665 new XULCommandEvent(doc, presContext, nullptr);
6666 xulCommand->InitCommandEvent(u"command"_ns, true, true,
6667 nsGlobalWindowInner::Cast(doc->GetInnerWindow()),
6668 0, aCtrl, aAlt, aShift, aMeta, aButton,
6669 aSourceEvent, aInputSource, IgnoreErrors());
6671 if (aPresShell) {
6672 nsEventStatus status = nsEventStatus_eIgnore;
6673 return aPresShell->HandleDOMEventWithTarget(aTarget, xulCommand, &status);
6676 ErrorResult rv;
6677 aTarget->DispatchEvent(*xulCommand, rv);
6678 return rv.StealNSResult();
6681 // static
6682 nsresult nsContentUtils::WrapNative(JSContext* cx, nsISupports* native,
6683 nsWrapperCache* cache, const nsIID* aIID,
6684 JS::MutableHandle<JS::Value> vp,
6685 bool aAllowWrapping) {
6686 MOZ_ASSERT(cx == GetCurrentJSContext());
6688 if (!native) {
6689 vp.setNull();
6691 return NS_OK;
6694 JSObject* wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
6695 if (wrapper) {
6696 return NS_OK;
6699 NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
6701 if (!NS_IsMainThread()) {
6702 MOZ_CRASH();
6705 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
6706 nsresult rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
6707 aAllowWrapping, vp);
6708 return rv;
6711 nsresult nsContentUtils::CreateArrayBuffer(JSContext* aCx,
6712 const nsACString& aData,
6713 JSObject** aResult) {
6714 if (!aCx) {
6715 return NS_ERROR_FAILURE;
6718 size_t dataLen = aData.Length();
6719 *aResult = JS::NewArrayBuffer(aCx, dataLen);
6720 if (!*aResult) {
6721 return NS_ERROR_FAILURE;
6724 if (dataLen > 0) {
6725 NS_ASSERTION(JS::IsArrayBufferObject(*aResult), "What happened?");
6726 JS::AutoCheckCannotGC nogc;
6727 bool isShared;
6728 memcpy(JS::GetArrayBufferData(*aResult, &isShared, nogc),
6729 aData.BeginReading(), dataLen);
6730 MOZ_ASSERT(!isShared);
6733 return NS_OK;
6736 void nsContentUtils::StripNullChars(const nsAString& aInStr,
6737 nsAString& aOutStr) {
6738 // In common cases where we don't have nulls in the
6739 // string we can simple simply bypass the checking code.
6740 int32_t firstNullPos = aInStr.FindChar('\0');
6741 if (firstNullPos == kNotFound) {
6742 aOutStr.Assign(aInStr);
6743 return;
6746 aOutStr.SetCapacity(aInStr.Length() - 1);
6747 nsAString::const_iterator start, end;
6748 aInStr.BeginReading(start);
6749 aInStr.EndReading(end);
6750 while (start != end) {
6751 if (*start != '\0') aOutStr.Append(*start);
6752 ++start;
6756 struct ClassMatchingInfo {
6757 AtomArray mClasses;
6758 nsCaseTreatment mCaseTreatment;
6761 // static
6762 bool nsContentUtils::MatchClassNames(Element* aElement, int32_t aNamespaceID,
6763 nsAtom* aAtom, void* aData) {
6764 // We can't match if there are no class names
6765 const nsAttrValue* classAttr = aElement->GetClasses();
6766 if (!classAttr) {
6767 return false;
6770 // need to match *all* of the classes
6771 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6772 uint32_t length = info->mClasses.Length();
6773 if (!length) {
6774 // If we actually had no classes, don't match.
6775 return false;
6777 uint32_t i;
6778 for (i = 0; i < length; ++i) {
6779 if (!classAttr->Contains(info->mClasses[i], info->mCaseTreatment)) {
6780 return false;
6784 return true;
6787 // static
6788 void nsContentUtils::DestroyClassNameArray(void* aData) {
6789 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6790 delete info;
6793 // static
6794 void* nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
6795 const nsString* aClasses) {
6796 nsAttrValue attrValue;
6797 attrValue.ParseAtomArray(*aClasses);
6798 // nsAttrValue::Equals is sensitive to order, so we'll send an array
6799 auto* info = new ClassMatchingInfo;
6800 if (attrValue.Type() == nsAttrValue::eAtomArray) {
6801 info->mClasses = std::move(attrValue.GetAtomArrayValue()->mArray);
6802 } else if (attrValue.Type() == nsAttrValue::eAtom) {
6803 info->mClasses.AppendElement(attrValue.GetAtomValue());
6806 info->mCaseTreatment =
6807 aRootNode->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks
6808 ? eIgnoreCase
6809 : eCaseMatters;
6810 return info;
6813 // static
6814 bool nsContentUtils::IsFocusedContent(const nsIContent* aContent) {
6815 nsFocusManager* fm = nsFocusManager::GetFocusManager();
6817 return fm && fm->GetFocusedElement() == aContent;
6820 bool nsContentUtils::HasScrollgrab(nsIContent* aContent) {
6821 // If we ever standardize this feature we'll want to hook this up properly
6822 // again. For now we're removing all the DOM-side code related to it but
6823 // leaving the layout and APZ handling for it in place.
6824 return false;
6827 void nsContentUtils::FlushLayoutForTree(nsPIDOMWindowOuter* aWindow) {
6828 if (!aWindow) {
6829 return;
6832 // Note that because FlushPendingNotifications flushes parents, this
6833 // is O(N^2) in docshell tree depth. However, the docshell tree is
6834 // usually pretty shallow.
6836 if (RefPtr<Document> doc = aWindow->GetDoc()) {
6837 doc->FlushPendingNotifications(FlushType::Layout);
6840 if (nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell()) {
6841 int32_t i = 0, i_end;
6842 docShell->GetInProcessChildCount(&i_end);
6843 for (; i < i_end; ++i) {
6844 nsCOMPtr<nsIDocShellTreeItem> item;
6845 if (docShell->GetInProcessChildAt(i, getter_AddRefs(item)) == NS_OK &&
6846 item) {
6847 if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
6848 FlushLayoutForTree(win);
6855 void nsContentUtils::RemoveNewlines(nsString& aString) { aString.StripCRLF(); }
6857 void nsContentUtils::PlatformToDOMLineBreaks(nsString& aString) {
6858 if (!PlatformToDOMLineBreaks(aString, fallible)) {
6859 aString.AllocFailed(aString.Length());
6863 bool nsContentUtils::PlatformToDOMLineBreaks(nsString& aString,
6864 const fallible_t& aFallible) {
6865 if (aString.FindChar(char16_t('\r')) != -1) {
6866 // Windows linebreaks: Map CRLF to LF:
6867 if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
6868 return false;
6871 // Mac linebreaks: Map any remaining CR to LF:
6872 if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
6873 return false;
6877 return true;
6880 void nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
6881 nsAString& aResultString) {
6882 MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
6884 uint32_t stringLen = NS_strlen(static_cast<char16_t*>(aBuf->Data()));
6886 // SANITY CHECK: In case the nsStringBuffer isn't correctly
6887 // null-terminated, let's clamp its length using the allocated size, to be
6888 // sure the resulting string doesn't sample past the end of the the buffer.
6889 // (Note that StorageSize() is in units of bytes, so we have to convert that
6890 // to units of PRUnichars, and subtract 1 for the null-terminator.)
6891 uint32_t allocStringLen = (aBuf->StorageSize() / sizeof(char16_t)) - 1;
6892 MOZ_ASSERT(stringLen <= allocStringLen,
6893 "string buffer lacks null terminator!");
6894 stringLen = std::min(stringLen, allocStringLen);
6896 aBuf->ToString(stringLen, aResultString);
6899 already_AddRefed<nsContentList> nsContentUtils::GetElementsByClassName(
6900 nsINode* aRootNode, const nsAString& aClasses) {
6901 MOZ_ASSERT(aRootNode, "Must have root node");
6903 return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(
6904 aRootNode, MatchClassNames, DestroyClassNameArray, AllocClassMatchingInfo,
6905 aClasses);
6908 PresShell* nsContentUtils::FindPresShellForDocument(const Document* aDocument) {
6909 const Document* doc = aDocument;
6910 Document* displayDoc = doc->GetDisplayDocument();
6911 if (displayDoc) {
6912 doc = displayDoc;
6915 PresShell* presShell = doc->GetPresShell();
6916 if (presShell) {
6917 return presShell;
6920 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
6921 while (docShellTreeItem) {
6922 // We may be in a display:none subdocument, or we may not have a presshell
6923 // created yet.
6924 // Walk the docshell tree to find the nearest container that has a
6925 // presshell, and return that.
6926 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
6927 if (PresShell* presShell = docShell->GetPresShell()) {
6928 return presShell;
6930 nsCOMPtr<nsIDocShellTreeItem> parent;
6931 docShellTreeItem->GetInProcessParent(getter_AddRefs(parent));
6932 docShellTreeItem = parent;
6935 return nullptr;
6938 /* static */
6939 nsPresContext* nsContentUtils::FindPresContextForDocument(
6940 const Document* aDocument) {
6941 if (PresShell* presShell = FindPresShellForDocument(aDocument)) {
6942 return presShell->GetPresContext();
6944 return nullptr;
6947 nsIWidget* nsContentUtils::WidgetForDocument(const Document* aDocument) {
6948 PresShell* presShell = FindPresShellForDocument(aDocument);
6949 if (!presShell) {
6950 return nullptr;
6952 nsViewManager* vm = presShell->GetViewManager();
6953 if (!vm) {
6954 return nullptr;
6956 nsView* rootView = vm->GetRootView();
6957 if (!rootView) {
6958 return nullptr;
6960 nsView* displayRoot = nsViewManager::GetDisplayRootFor(rootView);
6961 if (!displayRoot) {
6962 return nullptr;
6964 return displayRoot->GetNearestWidget(nullptr);
6967 nsIWidget* nsContentUtils::WidgetForContent(const nsIContent* aContent) {
6968 nsIFrame* frame = aContent->GetPrimaryFrame();
6969 if (frame) {
6970 frame = nsLayoutUtils::GetDisplayRootFrame(frame);
6972 nsView* view = frame->GetView();
6973 if (view) {
6974 return view->GetWidget();
6978 return nullptr;
6981 WindowRenderer* nsContentUtils::WindowRendererForContent(
6982 const nsIContent* aContent) {
6983 nsIWidget* widget = nsContentUtils::WidgetForContent(aContent);
6984 if (widget) {
6985 return widget->GetWindowRenderer();
6988 return nullptr;
6991 WindowRenderer* nsContentUtils::WindowRendererForDocument(
6992 const Document* aDoc) {
6993 nsIWidget* widget = nsContentUtils::WidgetForDocument(aDoc);
6994 if (widget) {
6995 return widget->GetWindowRenderer();
6998 return nullptr;
7001 bool nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal) {
7002 if (!aPrincipal) {
7003 return false;
7006 if (aPrincipal->IsSystemPrincipal()) {
7007 return true;
7010 return xpc::IsInAutomation() && IsSitePermAllow(aPrincipal, "allowXULXBL"_ns);
7013 bool nsContentUtils::IsPDFJSEnabled() {
7014 nsCOMPtr<nsIStreamConverter> conv = do_CreateInstance(
7015 "@mozilla.org/streamconv;1?from=application/pdf&to=text/html");
7016 return conv;
7019 bool nsContentUtils::IsPDFJS(nsIPrincipal* aPrincipal) {
7020 if (!aPrincipal) {
7021 return false;
7023 nsAutoCString spec;
7024 nsresult rv = aPrincipal->GetAsciiSpec(spec);
7025 NS_ENSURE_SUCCESS(rv, false);
7026 return spec.EqualsLiteral("resource://pdf.js/web/viewer.html");
7029 bool nsContentUtils::IsSystemOrPDFJS(JSContext* aCx, JSObject*) {
7030 nsIPrincipal* principal = SubjectPrincipal(aCx);
7031 return principal && (principal->IsSystemPrincipal() || IsPDFJS(principal));
7034 already_AddRefed<nsIDocumentLoaderFactory>
7035 nsContentUtils::FindInternalContentViewer(const nsACString& aType,
7036 ContentViewerType* aLoaderType) {
7037 if (aLoaderType) {
7038 *aLoaderType = TYPE_UNSUPPORTED;
7041 // one helper factory, please
7042 nsCOMPtr<nsICategoryManager> catMan(
7043 do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
7044 if (!catMan) return nullptr;
7046 nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
7048 nsCString contractID;
7049 nsresult rv =
7050 catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, contractID);
7051 if (NS_SUCCEEDED(rv)) {
7052 docFactory = do_GetService(contractID.get());
7053 if (docFactory && aLoaderType) {
7054 if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
7055 *aLoaderType = TYPE_CONTENT;
7056 else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
7057 *aLoaderType = TYPE_FALLBACK;
7058 else
7059 *aLoaderType = TYPE_UNKNOWN;
7061 return docFactory.forget();
7064 if (DecoderTraits::IsSupportedInVideoDocument(aType)) {
7065 docFactory =
7066 do_GetService("@mozilla.org/content/document-loader-factory;1");
7067 if (docFactory && aLoaderType) {
7068 *aLoaderType = TYPE_CONTENT;
7070 return docFactory.forget();
7073 return nullptr;
7076 static void ReportPatternCompileFailure(nsAString& aPattern,
7077 const Document* aDocument,
7078 JS::MutableHandle<JS::Value> error,
7079 JSContext* cx) {
7080 JS::AutoSaveExceptionState savedExc(cx);
7081 JS::Rooted<JSObject*> exnObj(cx, &error.toObject());
7082 JS::Rooted<JS::Value> messageVal(cx);
7083 if (!JS_GetProperty(cx, exnObj, "message", &messageVal)) {
7084 return;
7086 JS::Rooted<JSString*> messageStr(cx, messageVal.toString());
7087 MOZ_ASSERT(messageStr);
7089 AutoTArray<nsString, 2> strings;
7090 strings.AppendElement(aPattern);
7091 if (!AssignJSString(cx, *strings.AppendElement(), messageStr)) {
7092 return;
7095 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns,
7096 aDocument, nsContentUtils::eDOM_PROPERTIES,
7097 "PatternAttributeCompileFailure", strings);
7098 savedExc.drop();
7101 // static
7102 Maybe<bool> nsContentUtils::IsPatternMatching(const nsAString& aValue,
7103 nsString&& aPattern,
7104 const Document* aDocument,
7105 bool aHasMultiple,
7106 JS::RegExpFlags aFlags) {
7107 NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
7109 // The fact that we're using a JS regexp under the hood should not be visible
7110 // to things like window onerror handlers, so we don't initialize our JSAPI
7111 // with the document's window (which may not exist anyway).
7112 AutoJSAPI jsapi;
7113 jsapi.Init();
7114 JSContext* cx = jsapi.cx();
7115 AutoDisableJSInterruptCallback disabler(cx);
7117 // We can use the junk scope here, because we're just using it for regexp
7118 // evaluation, not actual script execution, and we disable statics so that the
7119 // evaluation does not interact with the execution global.
7120 JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
7122 // Check if the pattern by itself is valid first, and not that it only becomes
7123 // valid once we add ^(?: and )$.
7124 JS::Rooted<JS::Value> error(cx);
7125 if (!JS::CheckRegExpSyntax(cx, aPattern.BeginReading(), aPattern.Length(),
7126 aFlags, &error)) {
7127 return Nothing();
7130 if (!error.isUndefined()) {
7131 ReportPatternCompileFailure(aPattern, aDocument, &error, cx);
7132 return Some(true);
7135 // The pattern has to match the entire value.
7136 aPattern.InsertLiteral(u"^(?:", 0);
7137 aPattern.AppendLiteral(")$");
7139 JS::Rooted<JSObject*> re(
7140 cx, JS::NewUCRegExpObject(cx, aPattern.BeginReading(), aPattern.Length(),
7141 aFlags));
7142 if (!re) {
7143 return Nothing();
7146 JS::Rooted<JS::Value> rval(cx, JS::NullValue());
7147 if (!aHasMultiple) {
7148 size_t idx = 0;
7149 if (!JS::ExecuteRegExpNoStatics(cx, re, aValue.BeginReading(),
7150 aValue.Length(), &idx, true, &rval)) {
7151 return Nothing();
7153 return Some(!rval.isNull());
7156 HTMLSplitOnSpacesTokenizer tokenizer(aValue, ',');
7157 while (tokenizer.hasMoreTokens()) {
7158 const nsAString& value = tokenizer.nextToken();
7159 size_t idx = 0;
7160 if (!JS::ExecuteRegExpNoStatics(cx, re, value.BeginReading(),
7161 value.Length(), &idx, true, &rval)) {
7162 return Nothing();
7164 if (rval.isNull()) {
7165 return Some(false);
7168 return Some(true);
7171 // static
7172 nsresult nsContentUtils::URIInheritsSecurityContext(nsIURI* aURI,
7173 bool* aResult) {
7174 // Note: about:blank URIs do NOT inherit the security context from the
7175 // current document, which is what this function tests for...
7176 return NS_URIChainHasFlags(
7177 aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, aResult);
7180 // static
7181 bool nsContentUtils::ChannelShouldInheritPrincipal(
7182 nsIPrincipal* aLoadingPrincipal, nsIURI* aURI, bool aInheritForAboutBlank,
7183 bool aForceInherit) {
7184 MOZ_ASSERT(aLoadingPrincipal,
7185 "Can not check inheritance without a principal");
7187 // Only tell the channel to inherit if it can't provide its own security
7188 // context.
7190 // XXX: If this is ever changed, check all callers for what owners
7191 // they're passing in. In particular, see the code and
7192 // comments in nsDocShell::LoadURI where we fall back on
7193 // inheriting the owner if called from chrome. That would be
7194 // very wrong if this code changed anything but channels that
7195 // can't provide their own security context!
7197 // If aForceInherit is true, we will inherit, even for a channel that
7198 // can provide its own security context. This is used for srcdoc loads.
7199 bool inherit = aForceInherit;
7200 if (!inherit) {
7201 bool uriInherits;
7202 // We expect URIInheritsSecurityContext to return success for an
7203 // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
7204 // This condition needs to match the one in nsDocShell::InternalLoad where
7205 // we're checking for things that will use the owner.
7206 inherit =
7207 (NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &uriInherits)) &&
7208 (uriInherits || (aInheritForAboutBlank && NS_IsAboutBlank(aURI)))) ||
7210 // file: uri special-casing
7212 // If this is a file: load opened from another file: then it may need
7213 // to inherit the owner from the referrer so they can script each other.
7214 // If we don't set the owner explicitly then each file: gets an owner
7215 // based on its own codebase later.
7217 (URIIsLocalFile(aURI) &&
7218 NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, false)) &&
7219 // One more check here. CheckMayLoad will always return true for the
7220 // system principal, but we do NOT want to inherit in that case.
7221 !aLoadingPrincipal->IsSystemPrincipal());
7223 return inherit;
7226 /* static */
7227 bool nsContentUtils::IsCutCopyAllowed(Document* aDocument,
7228 nsIPrincipal& aSubjectPrincipal) {
7229 if (StaticPrefs::dom_allow_cut_copy() && aDocument &&
7230 aDocument->HasValidTransientUserGestureActivation()) {
7231 return true;
7234 return PrincipalHasPermission(aSubjectPrincipal, nsGkAtoms::clipboardWrite);
7237 /* static */
7238 bool nsContentUtils::HaveEqualPrincipals(Document* aDoc1, Document* aDoc2) {
7239 if (!aDoc1 || !aDoc2) {
7240 return false;
7242 bool principalsEqual = false;
7243 aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
7244 return principalsEqual;
7247 /* static */
7248 void nsContentUtils::FireMutationEventsForDirectParsing(
7249 Document* aDoc, nsIContent* aDest, int32_t aOldChildCount) {
7250 // Fire mutation events. Optimize for the case when there are no listeners
7251 int32_t newChildCount = aDest->GetChildCount();
7252 if (newChildCount && nsContentUtils::HasMutationListeners(
7253 aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
7254 AutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
7255 NS_ASSERTION(newChildCount - aOldChildCount >= 0,
7256 "What, some unexpected dom mutation has happened?");
7257 childNodes.SetCapacity(newChildCount - aOldChildCount);
7258 for (nsIContent* child = aDest->GetFirstChild(); child;
7259 child = child->GetNextSibling()) {
7260 childNodes.AppendElement(child);
7262 FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
7266 /* static */
7267 const Document* nsContentUtils::GetInProcessSubtreeRootDocument(
7268 const Document* aDoc) {
7269 if (!aDoc) {
7270 return nullptr;
7272 const Document* doc = aDoc;
7273 while (doc->GetInProcessParentDocument()) {
7274 doc = doc->GetInProcessParentDocument();
7276 return doc;
7279 // static
7280 int32_t nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
7281 int32_t aOffset) {
7282 // The structure of the anonymous frames within a text control frame is
7283 // an optional block frame, followed by an optional br frame.
7285 // If the offset frame has a child, then this frame is the block which
7286 // has the text frames (containing the content) as its children. This will
7287 // be the case if we click to the right of any of the text frames, or at the
7288 // bottom of the text area.
7289 nsIFrame* firstChild = aOffsetFrame->PrincipalChildList().FirstChild();
7290 if (firstChild) {
7291 // In this case, the passed-in offset is incorrect, and we want the length
7292 // of the entire content in the text control frame.
7293 return firstChild->GetContent()->Length();
7296 if (aOffsetFrame->GetPrevSibling() && !aOffsetFrame->GetNextSibling()) {
7297 // In this case, we're actually within the last frame, which is a br
7298 // frame. Our offset should therefore be the length of the first child of
7299 // our parent.
7300 int32_t aOutOffset = aOffsetFrame->GetParent()
7301 ->PrincipalChildList()
7302 .FirstChild()
7303 ->GetContent()
7304 ->Length();
7305 return aOutOffset;
7308 // Otherwise, we're within one of the text frames, in which case our offset
7309 // has already been correctly calculated.
7310 return aOffset;
7313 // static
7314 void nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
7315 Element* aRoot,
7316 uint32_t& aOutStartOffset,
7317 uint32_t& aOutEndOffset) {
7318 MOZ_ASSERT(aSelection && aRoot);
7320 // We don't care which end of this selection is anchor and which is focus. In
7321 // fact, we explicitly want to know which is the _start_ and which is the
7322 // _end_, not anchor vs focus.
7323 const nsRange* range = aSelection->GetAnchorFocusRange();
7324 if (!range) {
7325 // Nothing selected
7326 aOutStartOffset = aOutEndOffset = 0;
7327 return;
7330 // All the node pointers here are raw pointers for performance. We shouldn't
7331 // be doing anything in this function that invalidates the node tree.
7332 nsINode* startContainer = range->GetStartContainer();
7333 uint32_t startOffset = range->StartOffset();
7334 nsINode* endContainer = range->GetEndContainer();
7335 uint32_t endOffset = range->EndOffset();
7337 // We have at most two children, consisting of an optional text node followed
7338 // by an optional <br>.
7339 NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
7340 nsIContent* firstChild = aRoot->GetFirstChild();
7341 #ifdef DEBUG
7342 nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
7343 NS_ASSERTION(startContainer == aRoot || startContainer == firstChild ||
7344 startContainer == lastChild,
7345 "Unexpected startContainer");
7346 NS_ASSERTION(endContainer == aRoot || endContainer == firstChild ||
7347 endContainer == lastChild,
7348 "Unexpected endContainer");
7349 // firstChild is either text or a <br> (hence an element).
7350 MOZ_ASSERT_IF(firstChild, firstChild->IsText() || firstChild->IsElement());
7351 #endif
7352 if (!firstChild || firstChild->IsElement()) {
7353 // No text node, so everything is 0
7354 startOffset = endOffset = 0;
7355 } else {
7356 // First child is text. If the start/end is already in the text node,
7357 // or the start of the root node, no change needed. If it's in the root
7358 // node but not the start, or in the trailing <br>, we need to set the
7359 // offset to the end.
7360 if ((startContainer == aRoot && startOffset != 0) ||
7361 (startContainer != aRoot && startContainer != firstChild)) {
7362 startOffset = firstChild->Length();
7364 if ((endContainer == aRoot && endOffset != 0) ||
7365 (endContainer != aRoot && endContainer != firstChild)) {
7366 endOffset = firstChild->Length();
7370 MOZ_ASSERT(startOffset <= endOffset);
7371 aOutStartOffset = startOffset;
7372 aOutEndOffset = endOffset;
7375 // static
7376 HTMLEditor* nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext) {
7377 if (!aPresContext) {
7378 return nullptr;
7380 return GetHTMLEditor(aPresContext->GetDocShell());
7383 // static
7384 HTMLEditor* nsContentUtils::GetHTMLEditor(nsDocShell* aDocShell) {
7385 bool isEditable;
7386 if (!aDocShell || NS_FAILED(aDocShell->GetEditable(&isEditable)) ||
7387 !isEditable) {
7388 return nullptr;
7390 return aDocShell->GetHTMLEditor();
7393 // static
7394 EditorBase* nsContentUtils::GetActiveEditor(nsPresContext* aPresContext) {
7395 if (!aPresContext) {
7396 return nullptr;
7399 return GetActiveEditor(aPresContext->Document()->GetWindow());
7402 // static
7403 EditorBase* nsContentUtils::GetActiveEditor(nsPIDOMWindowOuter* aWindow) {
7404 if (!aWindow || !aWindow->GetExtantDoc()) {
7405 return nullptr;
7408 // If it's in designMode, nobody can have focus. Therefore, the HTMLEditor
7409 // handles all events. I.e., it's focused editor in this case.
7410 if (aWindow->GetExtantDoc()->IsInDesignMode()) {
7411 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7414 // If focused element is associated with TextEditor, it must be <input>
7415 // element or <textarea> element. Let's return it even if it's in a
7416 // contenteditable element.
7417 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
7418 if (Element* focusedElement = nsFocusManager::GetFocusedDescendant(
7419 aWindow, nsFocusManager::SearchRange::eOnlyCurrentWindow,
7420 getter_AddRefs(focusedWindow))) {
7421 if (TextEditor* textEditor = focusedElement->GetTextEditorInternal()) {
7422 return textEditor;
7426 // Otherwise, HTMLEditor may handle inputs even non-editable element has
7427 // focus or nobody has focus.
7428 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7431 // static
7432 TextEditor* nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(
7433 const nsIContent* aAnonymousContent) {
7434 if (!aAnonymousContent) {
7435 return nullptr;
7437 nsIContent* parent = aAnonymousContent->FindFirstNonChromeOnlyAccessContent();
7438 if (!parent || parent == aAnonymousContent) {
7439 return nullptr;
7441 if (HTMLInputElement* inputElement =
7442 HTMLInputElement::FromNodeOrNull(parent)) {
7443 return inputElement->GetTextEditorWithoutCreation();
7445 if (HTMLTextAreaElement* textareaElement =
7446 HTMLTextAreaElement::FromNodeOrNull(parent)) {
7447 return textareaElement->GetTextEditorWithoutCreation();
7449 return nullptr;
7452 // static
7453 bool nsContentUtils::IsNodeInEditableRegion(nsINode* aNode) {
7454 while (aNode) {
7455 if (aNode->IsEditable()) {
7456 return true;
7458 aNode = aNode->GetParent();
7460 return false;
7463 // static
7464 bool nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader,
7465 const nsACString& aValue) {
7466 if (IsForbiddenSystemRequestHeader(aHeader)) {
7467 return true;
7470 if ((nsContentUtils::IsOverrideMethodHeader(aHeader) &&
7471 nsContentUtils::ContainsForbiddenMethod(aValue))) {
7472 return true;
7475 if (StringBeginsWith(aHeader, "proxy-"_ns,
7476 nsCaseInsensitiveCStringComparator) ||
7477 StringBeginsWith(aHeader, "sec-"_ns,
7478 nsCaseInsensitiveCStringComparator)) {
7479 return true;
7482 return false;
7485 // static
7486 bool nsContentUtils::IsForbiddenSystemRequestHeader(const nsACString& aHeader) {
7487 static const char* kInvalidHeaders[] = {"accept-charset",
7488 "accept-encoding",
7489 "access-control-request-headers",
7490 "access-control-request-method",
7491 "connection",
7492 "content-length",
7493 "cookie",
7494 "cookie2",
7495 "date",
7496 "dnt",
7497 "expect",
7498 "host",
7499 "keep-alive",
7500 "origin",
7501 "referer",
7502 "set-cookie",
7503 "te",
7504 "trailer",
7505 "transfer-encoding",
7506 "upgrade",
7507 "via"};
7508 for (auto& kInvalidHeader : kInvalidHeaders) {
7509 if (aHeader.LowerCaseEqualsASCII(kInvalidHeader)) {
7510 return true;
7513 return false;
7516 // static
7517 bool nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader) {
7518 return (aHeader.LowerCaseEqualsASCII("set-cookie") ||
7519 aHeader.LowerCaseEqualsASCII("set-cookie2"));
7522 // static
7523 bool nsContentUtils::IsOverrideMethodHeader(const nsACString& headerName) {
7524 return headerName.EqualsIgnoreCase("x-http-method-override") ||
7525 headerName.EqualsIgnoreCase("x-http-method") ||
7526 headerName.EqualsIgnoreCase("x-method-override");
7529 // static
7530 bool nsContentUtils::ContainsForbiddenMethod(const nsACString& headerValue) {
7531 bool hasInsecureMethod = false;
7532 nsCCharSeparatedTokenizer tokenizer(headerValue, ',');
7534 while (tokenizer.hasMoreTokens()) {
7535 const nsDependentCSubstring& value = tokenizer.nextToken();
7537 if (value.EqualsIgnoreCase("connect") || value.EqualsIgnoreCase("trace") ||
7538 value.EqualsIgnoreCase("track")) {
7539 hasInsecureMethod = true;
7540 break;
7544 return hasInsecureMethod;
7547 Maybe<nsContentUtils::ParsedRange> nsContentUtils::ParseSingleRangeRequest(
7548 const nsACString& aHeaderValue, bool aAllowWhitespace) {
7549 // See https://fetch.spec.whatwg.org/#simple-range-header-value
7550 mozilla::Tokenizer p(aHeaderValue);
7551 Maybe<uint32_t> rangeStart;
7552 Maybe<uint32_t> rangeEnd;
7554 // Step 2 and 3
7555 if (!p.CheckWord("bytes")) {
7556 return Nothing();
7559 // Step 4
7560 if (aAllowWhitespace) {
7561 p.SkipWhites();
7564 // Step 5 and 6
7565 if (!p.CheckChar('=')) {
7566 return Nothing();
7569 // Step 7
7570 if (aAllowWhitespace) {
7571 p.SkipWhites();
7574 // Step 8 and 9
7575 int32_t res;
7576 if (p.ReadInteger(&res)) {
7577 rangeStart = Some(res);
7580 // Step 10
7581 if (aAllowWhitespace) {
7582 p.SkipWhites();
7585 // Step 11
7586 if (!p.CheckChar('-')) {
7587 return Nothing();
7590 // Step 13
7591 if (aAllowWhitespace) {
7592 p.SkipWhites();
7595 // Step 14 and 15
7596 if (p.ReadInteger(&res)) {
7597 rangeEnd = Some(res);
7600 // Step 16
7601 if (!p.CheckEOF()) {
7602 return Nothing();
7605 // Step 17
7606 if (!rangeStart && !rangeEnd) {
7607 return Nothing();
7610 // Step 18
7611 if (rangeStart && rangeEnd && *rangeStart > *rangeEnd) {
7612 return Nothing();
7615 return Some(ParsedRange(rangeStart, rangeEnd));
7618 // static
7619 bool nsContentUtils::IsCorsUnsafeRequestHeaderValue(
7620 const nsACString& aHeaderValue) {
7621 const char* cur = aHeaderValue.BeginReading();
7622 const char* end = aHeaderValue.EndReading();
7624 while (cur != end) {
7625 // Implementation of
7626 // https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte Is less
7627 // than a space but not a horizontal tab
7628 if ((*cur < ' ' && *cur != '\t') || *cur == '"' || *cur == '(' ||
7629 *cur == ')' || *cur == ':' || *cur == '<' || *cur == '>' ||
7630 *cur == '?' || *cur == '@' || *cur == '[' || *cur == '\\' ||
7631 *cur == ']' || *cur == '{' || *cur == '}' ||
7632 *cur == 0x7F) { // 0x75 is DEL
7633 return true;
7635 cur++;
7637 return false;
7640 // static
7641 bool nsContentUtils::IsAllowedNonCorsAccept(const nsACString& aHeaderValue) {
7642 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7643 return false;
7645 return true;
7648 // static
7649 bool nsContentUtils::IsAllowedNonCorsContentType(
7650 const nsACString& aHeaderValue) {
7651 nsAutoCString contentType;
7652 nsAutoCString unused;
7654 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7655 return false;
7658 nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
7659 if (NS_FAILED(rv)) {
7660 return false;
7663 return contentType.LowerCaseEqualsLiteral("text/plain") ||
7664 contentType.LowerCaseEqualsLiteral(
7665 "application/x-www-form-urlencoded") ||
7666 contentType.LowerCaseEqualsLiteral("multipart/form-data");
7669 // static
7670 bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
7671 const char* cur = aHeaderValue.BeginReading();
7672 const char* end = aHeaderValue.EndReading();
7674 while (cur != end) {
7675 if ((*cur >= '0' && *cur <= '9') || (*cur >= 'A' && *cur <= 'Z') ||
7676 (*cur >= 'a' && *cur <= 'z') || *cur == ' ' || *cur == '*' ||
7677 *cur == ',' || *cur == '-' || *cur == '.' || *cur == ';' ||
7678 *cur == '=') {
7679 cur++;
7680 continue;
7682 return false;
7684 return true;
7687 bool nsContentUtils::IsAllowedNonCorsRange(const nsACString& aHeaderValue) {
7688 Maybe<ParsedRange> parsedRange = ParseSingleRangeRequest(aHeaderValue, false);
7689 if (!parsedRange) {
7690 return false;
7693 if (!parsedRange->Start()) {
7694 return false;
7697 return true;
7700 // static
7701 bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
7702 const nsACString& aValue) {
7703 // see https://fetch.spec.whatwg.org/#cors-safelisted-request-header
7704 if (aValue.Length() > 128) {
7705 return false;
7707 return (aName.LowerCaseEqualsLiteral("accept") &&
7708 nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
7709 (aName.LowerCaseEqualsLiteral("accept-language") &&
7710 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7711 (aName.LowerCaseEqualsLiteral("content-language") &&
7712 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7713 (aName.LowerCaseEqualsLiteral("content-type") &&
7714 nsContentUtils::IsAllowedNonCorsContentType(aValue)) ||
7715 (aName.LowerCaseEqualsLiteral("range") &&
7716 nsContentUtils::IsAllowedNonCorsRange(aValue));
7719 mozilla::LogModule* nsContentUtils::ResistFingerprintingLog() {
7720 return gResistFingerprintingLog;
7722 mozilla::LogModule* nsContentUtils::DOMDumpLog() { return sDOMDumpLog; }
7724 bool nsContentUtils::GetNodeTextContent(const nsINode* aNode, bool aDeep,
7725 nsAString& aResult,
7726 const fallible_t& aFallible) {
7727 aResult.Truncate();
7728 return AppendNodeTextContent(aNode, aDeep, aResult, aFallible);
7731 void nsContentUtils::GetNodeTextContent(const nsINode* aNode, bool aDeep,
7732 nsAString& aResult) {
7733 if (!GetNodeTextContent(aNode, aDeep, aResult, fallible)) {
7734 NS_ABORT_OOM(0); // Unfortunately we don't know the allocation size
7738 void nsContentUtils::DestroyMatchString(void* aData) {
7739 if (aData) {
7740 nsString* matchString = static_cast<nsString*>(aData);
7741 delete matchString;
7745 bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
7746 // Table ordered from most to least likely JS MIME types.
7747 static const char* jsTypes[] = {"text/javascript",
7748 "text/ecmascript",
7749 "application/javascript",
7750 "application/ecmascript",
7751 "application/x-javascript",
7752 "application/x-ecmascript",
7753 "text/javascript1.0",
7754 "text/javascript1.1",
7755 "text/javascript1.2",
7756 "text/javascript1.3",
7757 "text/javascript1.4",
7758 "text/javascript1.5",
7759 "text/jscript",
7760 "text/livescript",
7761 "text/x-ecmascript",
7762 "text/x-javascript",
7763 nullptr};
7765 for (uint32_t i = 0; jsTypes[i]; ++i) {
7766 if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
7767 return true;
7771 return false;
7774 bool nsContentUtils::PrefetchPreloadEnabled(nsIDocShell* aDocShell) {
7776 // SECURITY CHECK: disable prefetching and preloading from mailnews!
7778 // walk up the docshell tree to see if any containing
7779 // docshell are of type MAIL.
7782 if (!aDocShell) {
7783 return false;
7786 nsCOMPtr<nsIDocShell> docshell = aDocShell;
7787 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7789 do {
7790 auto appType = docshell->GetAppType();
7791 if (appType == nsIDocShell::APP_TYPE_MAIL) {
7792 return false; // do not prefetch, preload, preconnect from mailnews
7795 docshell->GetInProcessParent(getter_AddRefs(parentItem));
7796 if (parentItem) {
7797 docshell = do_QueryInterface(parentItem);
7798 if (!docshell) {
7799 NS_ERROR("cannot get a docshell from a treeItem!");
7800 return false;
7803 } while (parentItem);
7805 return true;
7808 uint64_t nsContentUtils::GetInnerWindowID(nsIRequest* aRequest) {
7809 // can't do anything if there's no nsIRequest!
7810 if (!aRequest) {
7811 return 0;
7814 nsCOMPtr<nsILoadGroup> loadGroup;
7815 nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
7817 if (NS_FAILED(rv) || !loadGroup) {
7818 return 0;
7821 return GetInnerWindowID(loadGroup);
7824 uint64_t nsContentUtils::GetInnerWindowID(nsILoadGroup* aLoadGroup) {
7825 if (!aLoadGroup) {
7826 return 0;
7829 nsCOMPtr<nsIInterfaceRequestor> callbacks;
7830 nsresult rv = aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
7831 if (NS_FAILED(rv) || !callbacks) {
7832 return 0;
7835 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
7836 if (!loadContext) {
7837 return 0;
7840 nsCOMPtr<mozIDOMWindowProxy> window;
7841 rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
7842 if (NS_FAILED(rv) || !window) {
7843 return 0;
7846 auto* pwindow = nsPIDOMWindowOuter::From(window);
7847 if (!pwindow) {
7848 return 0;
7851 nsPIDOMWindowInner* inner = pwindow->GetCurrentInnerWindow();
7852 return inner ? inner->WindowID() : 0;
7855 // static
7856 void nsContentUtils::MaybeFixIPv6Host(nsACString& aHost) {
7857 if (aHost.FindChar(':') != -1) { // Escape IPv6 address
7858 MOZ_ASSERT(!aHost.Length() ||
7859 (aHost[0] != '[' && aHost[aHost.Length() - 1] != ']'));
7860 aHost.Insert('[', 0);
7861 aHost.Append(']');
7865 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7866 nsACString& aHost) {
7867 aHost.Truncate();
7868 nsresult rv = aURI->GetHost(aHost);
7869 if (NS_FAILED(rv)) { // Some URIs do not have a host
7870 return rv;
7873 MaybeFixIPv6Host(aHost);
7875 return NS_OK;
7878 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7879 nsAString& aHost) {
7880 nsAutoCString hostname;
7881 nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname);
7882 if (NS_FAILED(rv)) {
7883 return rv;
7885 CopyUTF8toUTF16(hostname, aHost);
7886 return NS_OK;
7889 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIPrincipal* aPrincipal,
7890 nsACString& aHost) {
7891 nsresult rv = aPrincipal->GetAsciiHost(aHost);
7892 if (NS_FAILED(rv)) { // Some URIs do not have a host
7893 return rv;
7896 MaybeFixIPv6Host(aHost);
7897 return NS_OK;
7900 CallState nsContentUtils::CallOnAllRemoteChildren(
7901 MessageBroadcaster* aManager,
7902 const std::function<CallState(BrowserParent*)>& aCallback) {
7903 uint32_t browserChildCount = aManager->ChildCount();
7904 for (uint32_t j = 0; j < browserChildCount; ++j) {
7905 RefPtr<MessageListenerManager> childMM = aManager->GetChildAt(j);
7906 if (!childMM) {
7907 continue;
7910 RefPtr<MessageBroadcaster> nonLeafMM = MessageBroadcaster::From(childMM);
7911 if (nonLeafMM) {
7912 if (CallOnAllRemoteChildren(nonLeafMM, aCallback) == CallState::Stop) {
7913 return CallState::Stop;
7915 continue;
7918 mozilla::dom::ipc::MessageManagerCallback* cb = childMM->GetCallback();
7919 if (cb) {
7920 nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
7921 BrowserParent* remote = BrowserParent::GetFrom(fl);
7922 if (remote && aCallback) {
7923 if (aCallback(remote) == CallState::Stop) {
7924 return CallState::Stop;
7930 return CallState::Continue;
7933 void nsContentUtils::CallOnAllRemoteChildren(
7934 nsPIDOMWindowOuter* aWindow,
7935 const std::function<CallState(BrowserParent*)>& aCallback) {
7936 nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(aWindow);
7937 if (window->IsChromeWindow()) {
7938 RefPtr<MessageBroadcaster> windowMM = window->GetMessageManager();
7939 if (windowMM) {
7940 CallOnAllRemoteChildren(windowMM, aCallback);
7945 bool nsContentUtils::IPCTransferableDataItemHasKnownFlavor(
7946 const IPCTransferableDataItem& aItem) {
7947 // Unknown types are converted to kCustomTypesMime.
7948 if (aItem.flavor().EqualsASCII(kCustomTypesMime)) {
7949 return true;
7952 for (const char* format : DataTransfer::kKnownFormats) {
7953 if (aItem.flavor().EqualsASCII(format)) {
7954 return true;
7958 return false;
7961 nsresult nsContentUtils::IPCTransferableDataToTransferable(
7962 const IPCTransferableData& aTransferableData, bool aAddDataFlavor,
7963 nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
7964 nsresult rv;
7965 const nsTArray<IPCTransferableDataItem>& items = aTransferableData.items();
7966 for (const auto& item : items) {
7967 if (aFilterUnknownFlavors && !IPCTransferableDataItemHasKnownFlavor(item)) {
7968 NS_WARNING(
7969 "Ignoring unknown flavor in "
7970 "nsContentUtils::IPCTransferableDataToTransferable");
7971 continue;
7974 if (aAddDataFlavor) {
7975 aTransferable->AddDataFlavor(item.flavor().get());
7978 nsCOMPtr<nsISupports> transferData;
7979 switch (item.data().type()) {
7980 case IPCTransferableDataType::TIPCTransferableDataString: {
7981 const auto& data = item.data().get_IPCTransferableDataString();
7982 nsCOMPtr<nsISupportsString> dataWrapper =
7983 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
7984 NS_ENSURE_SUCCESS(rv, rv);
7985 rv = dataWrapper->SetData(nsDependentSubstring(
7986 reinterpret_cast<const char16_t*>(data.data().Data()),
7987 data.data().Size() / sizeof(char16_t)));
7988 NS_ENSURE_SUCCESS(rv, rv);
7989 transferData = dataWrapper;
7990 break;
7992 case IPCTransferableDataType::TIPCTransferableDataCString: {
7993 const auto& data = item.data().get_IPCTransferableDataCString();
7994 nsCOMPtr<nsISupportsCString> dataWrapper =
7995 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
7996 NS_ENSURE_SUCCESS(rv, rv);
7997 rv = dataWrapper->SetData(nsDependentCSubstring(
7998 reinterpret_cast<const char*>(data.data().Data()),
7999 data.data().Size()));
8000 NS_ENSURE_SUCCESS(rv, rv);
8001 transferData = dataWrapper;
8002 break;
8004 case IPCTransferableDataType::TIPCTransferableDataInputStream: {
8005 const auto& data = item.data().get_IPCTransferableDataInputStream();
8006 nsCOMPtr<nsIInputStream> stream;
8007 rv = NS_NewByteInputStream(getter_AddRefs(stream),
8008 AsChars(data.data().AsSpan()),
8009 NS_ASSIGNMENT_COPY);
8010 NS_ENSURE_SUCCESS(rv, rv);
8011 transferData = stream.forget();
8012 break;
8014 case IPCTransferableDataType::TIPCTransferableDataImageContainer: {
8015 const auto& data = item.data().get_IPCTransferableDataImageContainer();
8016 nsCOMPtr<imgIContainer> container;
8017 rv = DeserializeTransferableDataImageContainer(
8018 data, getter_AddRefs(container));
8019 NS_ENSURE_SUCCESS(rv, rv);
8020 transferData = container;
8021 break;
8023 case IPCTransferableDataType::TIPCTransferableDataBlob: {
8024 const auto& data = item.data().get_IPCTransferableDataBlob();
8025 transferData = IPCBlobUtils::Deserialize(data.blob());
8026 break;
8028 case IPCTransferableDataType::T__None:
8029 MOZ_ASSERT_UNREACHABLE();
8030 return NS_ERROR_FAILURE;
8033 rv = aTransferable->SetTransferData(item.flavor().get(), transferData);
8034 NS_ENSURE_SUCCESS(rv, rv);
8036 return NS_OK;
8039 nsresult nsContentUtils::IPCTransferableDataToTransferable(
8040 const IPCTransferableData& aTransferableData, const bool& aIsPrivateData,
8041 nsIPrincipal* aRequestingPrincipal,
8042 const nsContentPolicyType& aContentPolicyType, bool aAddDataFlavor,
8043 nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
8044 // Note that we need to set privacy status of transferable before adding any
8045 // data into it.
8046 aTransferable->SetIsPrivateData(aIsPrivateData);
8048 nsresult rv = IPCTransferableDataToTransferable(
8049 aTransferableData, aAddDataFlavor, aTransferable, aFilterUnknownFlavors);
8050 NS_ENSURE_SUCCESS(rv, rv);
8052 aTransferable->SetRequestingPrincipal(aRequestingPrincipal);
8053 aTransferable->SetContentPolicyType(aContentPolicyType);
8054 return NS_OK;
8057 nsresult nsContentUtils::IPCTransferableToTransferable(
8058 const IPCTransferable& aIPCTransferable, bool aAddDataFlavor,
8059 nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
8060 nsresult rv = IPCTransferableDataToTransferable(
8061 aIPCTransferable.data(), aIPCTransferable.isPrivateData(),
8062 aIPCTransferable.requestingPrincipal(),
8063 aIPCTransferable.contentPolicyType(), aAddDataFlavor, aTransferable,
8064 aFilterUnknownFlavors);
8065 NS_ENSURE_SUCCESS(rv, rv);
8067 if (aIPCTransferable.cookieJarSettings().isSome()) {
8068 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
8069 net::CookieJarSettings::Deserialize(
8070 aIPCTransferable.cookieJarSettings().ref(),
8071 getter_AddRefs(cookieJarSettings));
8072 aTransferable->SetCookieJarSettings(cookieJarSettings);
8074 aTransferable->SetReferrerInfo(aIPCTransferable.referrerInfo());
8075 return NS_OK;
8078 nsresult nsContentUtils::IPCTransferableDataItemToVariant(
8079 const IPCTransferableDataItem& aItem, nsIWritableVariant* aVariant) {
8080 MOZ_ASSERT(aVariant);
8082 switch (aItem.data().type()) {
8083 case IPCTransferableDataType::TIPCTransferableDataString: {
8084 const auto& data = aItem.data().get_IPCTransferableDataString();
8085 return aVariant->SetAsAString(nsDependentSubstring(
8086 reinterpret_cast<const char16_t*>(data.data().Data()),
8087 data.data().Size() / sizeof(char16_t)));
8089 case IPCTransferableDataType::TIPCTransferableDataCString: {
8090 const auto& data = aItem.data().get_IPCTransferableDataCString();
8091 return aVariant->SetAsACString(nsDependentCSubstring(
8092 reinterpret_cast<const char*>(data.data().Data()),
8093 data.data().Size()));
8095 case IPCTransferableDataType::TIPCTransferableDataInputStream: {
8096 const auto& data = aItem.data().get_IPCTransferableDataInputStream();
8097 nsCOMPtr<nsIInputStream> stream;
8098 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
8099 AsChars(data.data().AsSpan()),
8100 NS_ASSIGNMENT_COPY);
8101 NS_ENSURE_SUCCESS(rv, rv);
8102 return aVariant->SetAsISupports(stream);
8104 case IPCTransferableDataType::TIPCTransferableDataImageContainer: {
8105 const auto& data = aItem.data().get_IPCTransferableDataImageContainer();
8106 nsCOMPtr<imgIContainer> container;
8107 nsresult rv = DeserializeTransferableDataImageContainer(
8108 data, getter_AddRefs(container));
8109 NS_ENSURE_SUCCESS(rv, rv);
8110 return aVariant->SetAsISupports(container);
8112 case IPCTransferableDataType::TIPCTransferableDataBlob: {
8113 const auto& data = aItem.data().get_IPCTransferableDataBlob();
8114 RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(data.blob());
8115 return aVariant->SetAsISupports(blobImpl);
8117 case IPCTransferableDataType::T__None:
8118 break;
8121 MOZ_ASSERT_UNREACHABLE();
8122 return NS_ERROR_UNEXPECTED;
8125 void nsContentUtils::TransferablesToIPCTransferableDatas(
8126 nsIArray* aTransferables, nsTArray<IPCTransferableData>& aIPC,
8127 bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
8128 aIPC.Clear();
8129 if (aTransferables) {
8130 uint32_t transferableCount = 0;
8131 aTransferables->GetLength(&transferableCount);
8132 for (uint32_t i = 0; i < transferableCount; ++i) {
8133 IPCTransferableData* dt = aIPC.AppendElement();
8134 nsCOMPtr<nsITransferable> transferable =
8135 do_QueryElementAt(aTransferables, i);
8136 TransferableToIPCTransferableData(transferable, dt, aInSyncMessage,
8137 aParent);
8142 nsresult nsContentUtils::CalculateBufferSizeForImage(
8143 const uint32_t& aStride, const IntSize& aImageSize,
8144 const SurfaceFormat& aFormat, size_t* aMaxBufferSize,
8145 size_t* aUsedBufferSize) {
8146 CheckedInt32 requiredBytes =
8147 CheckedInt32(aStride) * CheckedInt32(aImageSize.height);
8149 CheckedInt32 usedBytes =
8150 requiredBytes - aStride +
8151 (CheckedInt32(aImageSize.width) * BytesPerPixel(aFormat));
8152 if (!usedBytes.isValid()) {
8153 return NS_ERROR_FAILURE;
8156 MOZ_ASSERT(requiredBytes.isValid(), "usedBytes valid but not required?");
8157 *aMaxBufferSize = requiredBytes.value();
8158 *aUsedBufferSize = usedBytes.value();
8159 return NS_OK;
8162 static already_AddRefed<DataSourceSurface> BigBufferToDataSurface(
8163 BigBuffer& aData, uint32_t aStride, const IntSize& aImageSize,
8164 SurfaceFormat aFormat) {
8165 if (!aData.Size() || !aImageSize.width || !aImageSize.height) {
8166 return nullptr;
8169 // Validate shared memory buffer size
8170 size_t imageBufLen = 0;
8171 size_t maxBufLen = 0;
8172 if (NS_FAILED(nsContentUtils::CalculateBufferSizeForImage(
8173 aStride, aImageSize, aFormat, &maxBufLen, &imageBufLen))) {
8174 return nullptr;
8176 if (imageBufLen > aData.Size()) {
8177 return nullptr;
8179 return CreateDataSourceSurfaceFromData(aImageSize, aFormat, aData.Data(),
8180 aStride);
8183 nsresult nsContentUtils::DeserializeTransferableDataImageContainer(
8184 const IPCTransferableDataImageContainer& aData,
8185 imgIContainer** aContainer) {
8186 const IntSize size(aData.width(), aData.height());
8187 size_t maxBufferSize = 0;
8188 size_t usedBufferSize = 0;
8189 nsresult rv = CalculateBufferSizeForImage(
8190 aData.stride(), size, aData.format(), &maxBufferSize, &usedBufferSize);
8191 NS_ENSURE_SUCCESS(rv, rv);
8192 if (usedBufferSize > aData.data().Size()) {
8193 return NS_ERROR_FAILURE;
8195 RefPtr<DataSourceSurface> surface =
8196 CreateDataSourceSurfaceFromData(size, aData.format(), aData.data().Data(),
8197 static_cast<int32_t>(aData.stride()));
8198 if (!surface) {
8199 return NS_ERROR_FAILURE;
8201 RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size);
8202 nsCOMPtr<imgIContainer> imageContainer =
8203 image::ImageOps::CreateFromDrawable(drawable);
8204 imageContainer.forget(aContainer);
8206 return NS_OK;
8209 bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
8210 return aFlavor.EqualsLiteral(kNativeImageMime) ||
8211 aFlavor.EqualsLiteral(kJPEGImageMime) ||
8212 aFlavor.EqualsLiteral(kJPGImageMime) ||
8213 aFlavor.EqualsLiteral(kPNGImageMime) ||
8214 aFlavor.EqualsLiteral(kGIFImageMime);
8217 // FIXME: This can probably be removed once bug 1783240 lands, as `nsString`
8218 // will be implicitly serialized in shmem when sent over IPDL directly.
8219 static IPCTransferableDataString AsIPCTransferableDataString(
8220 Span<const char16_t> aInput) {
8221 return IPCTransferableDataString{BigBuffer(AsBytes(aInput))};
8224 // FIXME: This can probably be removed once bug 1783240 lands, as `nsCString`
8225 // will be implicitly serialized in shmem when sent over IPDL directly.
8226 static IPCTransferableDataCString AsIPCTransferableDataCString(
8227 Span<const char> aInput) {
8228 return IPCTransferableDataCString{BigBuffer(AsBytes(aInput))};
8231 void nsContentUtils::TransferableToIPCTransferableData(
8232 nsITransferable* aTransferable, IPCTransferableData* aTransferableData,
8233 bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
8234 MOZ_ASSERT_IF(XRE_IsParentProcess(), aParent);
8236 if (aTransferable) {
8237 nsTArray<nsCString> flavorList;
8238 aTransferable->FlavorsTransferableCanExport(flavorList);
8240 for (uint32_t j = 0; j < flavorList.Length(); ++j) {
8241 nsCString& flavorStr = flavorList[j];
8242 if (!flavorStr.Length()) {
8243 continue;
8246 nsCOMPtr<nsISupports> data;
8247 nsresult rv =
8248 aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data));
8250 if (NS_FAILED(rv) || !data) {
8251 if (aInSyncMessage) {
8252 // Can't do anything.
8253 // FIXME: This shouldn't be the case anymore!
8254 continue;
8257 // This is a hack to support kFilePromiseMime.
8258 // On Windows there just needs to be an entry for it,
8259 // and for OSX we need to create
8260 // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
8261 if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
8262 IPCTransferableDataItem* item =
8263 aTransferableData->items().AppendElement();
8264 item->flavor() = flavorStr;
8265 item->data() =
8266 AsIPCTransferableDataString(NS_ConvertUTF8toUTF16(flavorStr));
8267 continue;
8270 // Empty element, transfer only the flavor
8271 IPCTransferableDataItem* item =
8272 aTransferableData->items().AppendElement();
8273 item->flavor() = flavorStr;
8274 item->data() = AsIPCTransferableDataString(EmptyString());
8275 continue;
8278 // We need to handle nsIInputStream before nsISupportsCString, otherwise
8279 // nsStringInputStream would be converted into a wrong type.
8280 if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
8281 IPCTransferableDataItem* item =
8282 aTransferableData->items().AppendElement();
8283 item->flavor() = flavorStr;
8284 nsCString imageData;
8285 DebugOnly<nsresult> rv =
8286 NS_ConsumeStream(stream, UINT32_MAX, imageData);
8287 MOZ_ASSERT(
8288 rv != NS_BASE_STREAM_WOULD_BLOCK,
8289 "cannot use async input streams in nsITransferable right now");
8290 // FIXME: This can probably be simplified once bug 1783240 lands, as
8291 // `nsCString` will be implicitly serialized in shmem when sent over
8292 // IPDL directly.
8293 item->data() =
8294 IPCTransferableDataInputStream(BigBuffer(AsBytes(Span(imageData))));
8295 continue;
8298 if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(data)) {
8299 nsAutoString dataAsString;
8300 MOZ_ALWAYS_SUCCEEDS(text->GetData(dataAsString));
8302 IPCTransferableDataItem* item =
8303 aTransferableData->items().AppendElement();
8304 item->flavor() = flavorStr;
8305 item->data() = AsIPCTransferableDataString(dataAsString);
8306 continue;
8309 if (nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data)) {
8310 nsAutoCString dataAsString;
8311 MOZ_ALWAYS_SUCCEEDS(ctext->GetData(dataAsString));
8313 IPCTransferableDataItem* item =
8314 aTransferableData->items().AppendElement();
8315 item->flavor() = flavorStr;
8316 item->data() = AsIPCTransferableDataCString(dataAsString);
8317 continue;
8320 if (nsCOMPtr<imgIContainer> image = do_QueryInterface(data)) {
8321 // Images to be placed on the clipboard are imgIContainers.
8322 RefPtr<mozilla::gfx::SourceSurface> surface = image->GetFrame(
8323 imgIContainer::FRAME_CURRENT,
8324 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
8325 if (!surface) {
8326 continue;
8328 RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
8329 surface->GetDataSurface();
8330 if (!dataSurface) {
8331 continue;
8333 size_t length;
8334 int32_t stride;
8335 Maybe<BigBuffer> surfaceData =
8336 GetSurfaceData(*dataSurface, &length, &stride);
8338 if (surfaceData.isNothing()) {
8339 continue;
8342 IPCTransferableDataItem* item =
8343 aTransferableData->items().AppendElement();
8344 item->flavor() = flavorStr;
8346 mozilla::gfx::IntSize size = dataSurface->GetSize();
8347 item->data() = IPCTransferableDataImageContainer(
8348 std::move(*surfaceData), size.width, size.height, stride,
8349 dataSurface->GetFormat());
8350 continue;
8353 // Otherwise, handle this as a file.
8354 nsCOMPtr<BlobImpl> blobImpl;
8355 if (nsCOMPtr<nsIFile> file = do_QueryInterface(data)) {
8356 if (aParent) {
8357 bool isDir = false;
8358 if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
8359 nsAutoString path;
8360 if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
8361 continue;
8364 RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
8365 fss->GrantAccessToContentProcess(aParent->ChildID(), path);
8369 blobImpl = new FileBlobImpl(file);
8371 IgnoredErrorResult rv;
8373 // Ensure that file data is cached no that the content process
8374 // has this data available to it when passed over:
8375 blobImpl->GetSize(rv);
8376 if (NS_WARN_IF(rv.Failed())) {
8377 continue;
8380 blobImpl->GetLastModified(rv);
8381 if (NS_WARN_IF(rv.Failed())) {
8382 continue;
8384 } else {
8385 if (aInSyncMessage) {
8386 // Can't do anything.
8387 // FIXME: This shouldn't be the case anymore!
8388 continue;
8391 blobImpl = do_QueryInterface(data);
8394 if (blobImpl) {
8395 // If we failed to create the blob actor, then this blob probably
8396 // can't get the file size for the underlying file, ignore it for
8397 // now. TODO pass this through anyway.
8398 IPCBlob ipcBlob;
8399 nsresult rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);
8400 if (NS_WARN_IF(NS_FAILED(rv))) {
8401 continue;
8404 IPCTransferableDataItem* item =
8405 aTransferableData->items().AppendElement();
8406 item->flavor() = flavorStr;
8407 item->data() = IPCTransferableDataBlob(ipcBlob);
8413 void nsContentUtils::TransferableToIPCTransferable(
8414 nsITransferable* aTransferable, IPCTransferable* aIPCTransferable,
8415 bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
8416 IPCTransferableData ipcTransferableData;
8417 TransferableToIPCTransferableData(aTransferable, &ipcTransferableData,
8418 aInSyncMessage, aParent);
8420 Maybe<net::CookieJarSettingsArgs> cookieJarSettingsArgs;
8421 if (nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
8422 aTransferable->GetCookieJarSettings()) {
8423 net::CookieJarSettingsArgs args;
8424 net::CookieJarSettings::Cast(cookieJarSettings)->Serialize(args);
8425 cookieJarSettingsArgs = Some(std::move(args));
8428 aIPCTransferable->data() = std::move(ipcTransferableData);
8429 aIPCTransferable->isPrivateData() = aTransferable->GetIsPrivateData();
8430 aIPCTransferable->requestingPrincipal() =
8431 aTransferable->GetRequestingPrincipal();
8432 aIPCTransferable->cookieJarSettings() = std::move(cookieJarSettingsArgs);
8433 aIPCTransferable->contentPolicyType() = aTransferable->GetContentPolicyType();
8434 aIPCTransferable->referrerInfo() = aTransferable->GetReferrerInfo();
8437 Maybe<BigBuffer> nsContentUtils::GetSurfaceData(DataSourceSurface& aSurface,
8438 size_t* aLength,
8439 int32_t* aStride) {
8440 mozilla::gfx::DataSourceSurface::MappedSurface map;
8441 if (!aSurface.Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map)) {
8442 return Nothing();
8445 size_t bufLen = 0;
8446 size_t maxBufLen = 0;
8447 nsresult rv = nsContentUtils::CalculateBufferSizeForImage(
8448 map.mStride, aSurface.GetSize(), aSurface.GetFormat(), &maxBufLen,
8449 &bufLen);
8450 if (NS_FAILED(rv)) {
8451 aSurface.Unmap();
8452 return Nothing();
8455 BigBuffer surfaceData(maxBufLen);
8456 memcpy(surfaceData.Data(), map.mData, bufLen);
8457 memset(surfaceData.Data() + bufLen, 0, maxBufLen - bufLen);
8459 *aLength = maxBufLen;
8460 *aStride = map.mStride;
8462 aSurface.Unmap();
8463 return Some(std::move(surfaceData));
8466 Maybe<IPCImage> nsContentUtils::SurfaceToIPCImage(DataSourceSurface& aSurface) {
8467 size_t len = 0;
8468 int32_t stride = 0;
8469 auto mem = GetSurfaceData(aSurface, &len, &stride);
8470 if (!mem) {
8471 return Nothing();
8473 return Some(IPCImage{std::move(*mem), uint32_t(stride), aSurface.GetFormat(),
8474 ImageIntSize::FromUnknownSize(aSurface.GetSize())});
8477 already_AddRefed<DataSourceSurface> nsContentUtils::IPCImageToSurface(
8478 IPCImage&& aImage) {
8479 return BigBufferToDataSurface(aImage.data(), aImage.stride(),
8480 aImage.size().ToUnknownSize(), aImage.format());
8483 Modifiers nsContentUtils::GetWidgetModifiers(int32_t aModifiers) {
8484 Modifiers result = 0;
8485 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SHIFT) {
8486 result |= mozilla::MODIFIER_SHIFT;
8488 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CONTROL) {
8489 result |= mozilla::MODIFIER_CONTROL;
8491 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALT) {
8492 result |= mozilla::MODIFIER_ALT;
8494 if (aModifiers & nsIDOMWindowUtils::MODIFIER_META) {
8495 result |= mozilla::MODIFIER_META;
8497 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALTGRAPH) {
8498 result |= mozilla::MODIFIER_ALTGRAPH;
8500 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CAPSLOCK) {
8501 result |= mozilla::MODIFIER_CAPSLOCK;
8503 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FN) {
8504 result |= mozilla::MODIFIER_FN;
8506 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FNLOCK) {
8507 result |= mozilla::MODIFIER_FNLOCK;
8509 if (aModifiers & nsIDOMWindowUtils::MODIFIER_NUMLOCK) {
8510 result |= mozilla::MODIFIER_NUMLOCK;
8512 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SCROLLLOCK) {
8513 result |= mozilla::MODIFIER_SCROLLLOCK;
8515 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOL) {
8516 result |= mozilla::MODIFIER_SYMBOL;
8518 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK) {
8519 result |= mozilla::MODIFIER_SYMBOLLOCK;
8521 if (aModifiers & nsIDOMWindowUtils::MODIFIER_OS) {
8522 result |= mozilla::MODIFIER_OS;
8524 return result;
8527 nsIWidget* nsContentUtils::GetWidget(PresShell* aPresShell, nsPoint* aOffset) {
8528 if (!aPresShell) {
8529 return nullptr;
8531 nsIFrame* frame = aPresShell->GetRootFrame();
8532 if (!frame) {
8533 return nullptr;
8535 return frame->GetView()->GetNearestWidget(aOffset);
8538 int16_t nsContentUtils::GetButtonsFlagForButton(int32_t aButton) {
8539 switch (aButton) {
8540 case -1:
8541 return MouseButtonsFlag::eNoButtons;
8542 case MouseButton::ePrimary:
8543 return MouseButtonsFlag::ePrimaryFlag;
8544 case MouseButton::eMiddle:
8545 return MouseButtonsFlag::eMiddleFlag;
8546 case MouseButton::eSecondary:
8547 return MouseButtonsFlag::eSecondaryFlag;
8548 case 3:
8549 return MouseButtonsFlag::e4thFlag;
8550 case 4:
8551 return MouseButtonsFlag::e5thFlag;
8552 case MouseButton::eEraser:
8553 return MouseButtonsFlag::eEraserFlag;
8554 default:
8555 NS_ERROR("Button not known.");
8556 return 0;
8560 LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(
8561 const CSSPoint& aPoint, const nsPoint& aOffset,
8562 nsPresContext* aPresContext) {
8563 nsPoint layoutRelative = CSSPoint::ToAppUnits(aPoint) + aOffset;
8564 nsPoint visualRelative =
8565 ViewportUtils::LayoutToVisual(layoutRelative, aPresContext->PresShell());
8566 return LayoutDeviceIntPoint::FromAppUnitsRounded(
8567 visualRelative, aPresContext->AppUnitsPerDevPixel());
8570 nsView* nsContentUtils::GetViewToDispatchEvent(nsPresContext* aPresContext,
8571 PresShell** aPresShell) {
8572 if (!aPresContext || !aPresShell) {
8573 return nullptr;
8575 RefPtr<PresShell> presShell = aPresContext->PresShell();
8576 if (NS_WARN_IF(!presShell)) {
8577 *aPresShell = nullptr;
8578 return nullptr;
8580 nsViewManager* viewManager = presShell->GetViewManager();
8581 if (!viewManager) {
8582 presShell.forget(aPresShell); // XXX Is this intentional?
8583 return nullptr;
8585 presShell.forget(aPresShell);
8586 return viewManager->GetRootView();
8589 nsresult nsContentUtils::SendMouseEvent(
8590 mozilla::PresShell* aPresShell, const nsAString& aType, float aX, float aY,
8591 int32_t aButton, int32_t aButtons, int32_t aClickCount, int32_t aModifiers,
8592 bool aIgnoreRootScrollFrame, float aPressure,
8593 unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
8594 PreventDefaultResult* aPreventDefault, bool aIsDOMEventSynthesized,
8595 bool aIsWidgetEventSynthesized) {
8596 nsPoint offset;
8597 nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
8598 if (!widget) return NS_ERROR_FAILURE;
8600 EventMessage msg;
8601 Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
8602 bool contextMenuKey = false;
8603 if (aType.EqualsLiteral("mousedown")) {
8604 msg = eMouseDown;
8605 } else if (aType.EqualsLiteral("mouseup")) {
8606 msg = eMouseUp;
8607 } else if (aType.EqualsLiteral("mousemove")) {
8608 msg = eMouseMove;
8609 } else if (aType.EqualsLiteral("mouseover")) {
8610 msg = eMouseEnterIntoWidget;
8611 } else if (aType.EqualsLiteral("mouseout")) {
8612 msg = eMouseExitFromWidget;
8613 exitFrom = Some(WidgetMouseEvent::ePlatformChild);
8614 } else if (aType.EqualsLiteral("mousecancel")) {
8615 msg = eMouseExitFromWidget;
8616 exitFrom = Some(XRE_IsParentProcess() ? WidgetMouseEvent::ePlatformTopLevel
8617 : WidgetMouseEvent::ePuppet);
8618 } else if (aType.EqualsLiteral("mouselongtap")) {
8619 msg = eMouseLongTap;
8620 } else if (aType.EqualsLiteral("contextmenu")) {
8621 msg = eContextMenu;
8622 contextMenuKey = (aButton == 0);
8623 } else if (aType.EqualsLiteral("MozMouseHittest")) {
8624 msg = eMouseHitTest;
8625 } else if (aType.EqualsLiteral("MozMouseExploreByTouch")) {
8626 msg = eMouseExploreByTouch;
8627 } else {
8628 return NS_ERROR_FAILURE;
8631 if (aInputSourceArg == MouseEvent_Binding::MOZ_SOURCE_UNKNOWN) {
8632 aInputSourceArg = MouseEvent_Binding::MOZ_SOURCE_MOUSE;
8635 WidgetMouseEvent event(true, msg, widget,
8636 aIsWidgetEventSynthesized
8637 ? WidgetMouseEvent::eSynthesized
8638 : WidgetMouseEvent::eReal,
8639 contextMenuKey ? WidgetMouseEvent::eContextMenuKey
8640 : WidgetMouseEvent::eNormal);
8641 event.pointerId = aIdentifier;
8642 event.mModifiers = GetWidgetModifiers(aModifiers);
8643 event.mButton = aButton;
8644 event.mButtons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED
8645 ? aButtons
8646 : msg == eMouseUp ? 0
8647 : GetButtonsFlagForButton(aButton);
8648 event.mPressure = aPressure;
8649 event.mInputSource = aInputSourceArg;
8650 event.mClickCount = aClickCount;
8651 event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
8652 event.mExitFrom = exitFrom;
8654 nsPresContext* presContext = aPresShell->GetPresContext();
8655 if (!presContext) return NS_ERROR_FAILURE;
8657 event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
8658 event.mIgnoreRootScrollFrame = aIgnoreRootScrollFrame;
8660 nsEventStatus status = nsEventStatus_eIgnore;
8661 if (aToWindow) {
8662 RefPtr<PresShell> presShell;
8663 nsView* view =
8664 GetViewToDispatchEvent(presContext, getter_AddRefs(presShell));
8665 if (!presShell || !view) {
8666 return NS_ERROR_FAILURE;
8668 return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
8670 if (StaticPrefs::test_events_async_enabled()) {
8671 status = widget->DispatchInputEvent(&event).mContentStatus;
8672 } else {
8673 nsresult rv = widget->DispatchEvent(&event, status);
8674 NS_ENSURE_SUCCESS(rv, rv);
8676 if (aPreventDefault) {
8677 if (status == nsEventStatus_eConsumeNoDefault) {
8678 if (event.mFlags.mDefaultPreventedByContent) {
8679 *aPreventDefault = PreventDefaultResult::ByContent;
8680 } else {
8681 *aPreventDefault = PreventDefaultResult::ByChrome;
8683 } else {
8684 *aPreventDefault = PreventDefaultResult::No;
8688 return NS_OK;
8691 /* static */
8692 void nsContentUtils::FirePageHideEventForFrameLoaderSwap(
8693 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8694 bool aOnlySystemGroup) {
8695 MOZ_DIAGNOSTIC_ASSERT(aItem);
8696 MOZ_DIAGNOSTIC_ASSERT(aChromeEventHandler);
8698 RefPtr<Document> doc = aItem->GetDocument();
8699 NS_ASSERTION(doc, "What happened here?");
8700 doc->OnPageHide(true, aChromeEventHandler, aOnlySystemGroup);
8702 int32_t childCount = 0;
8703 aItem->GetInProcessChildCount(&childCount);
8704 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8705 kids.AppendElements(childCount);
8706 for (int32_t i = 0; i < childCount; ++i) {
8707 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8710 for (uint32_t i = 0; i < kids.Length(); ++i) {
8711 if (kids[i]) {
8712 FirePageHideEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8713 aOnlySystemGroup);
8718 // The pageshow event is fired for a given document only if IsShowing() returns
8719 // the same thing as aFireIfShowing. This gives us a way to fire pageshow only
8720 // on documents that are still loading or only on documents that are already
8721 // loaded.
8722 /* static */
8723 void nsContentUtils::FirePageShowEventForFrameLoaderSwap(
8724 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8725 bool aFireIfShowing, bool aOnlySystemGroup) {
8726 int32_t childCount = 0;
8727 aItem->GetInProcessChildCount(&childCount);
8728 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8729 kids.AppendElements(childCount);
8730 for (int32_t i = 0; i < childCount; ++i) {
8731 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8734 for (uint32_t i = 0; i < kids.Length(); ++i) {
8735 if (kids[i]) {
8736 FirePageShowEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8737 aFireIfShowing, aOnlySystemGroup);
8741 RefPtr<Document> doc = aItem->GetDocument();
8742 if (doc && doc->IsShowing() == aFireIfShowing) {
8743 doc->OnPageShow(true, aChromeEventHandler, aOnlySystemGroup);
8747 /* static */
8748 already_AddRefed<nsPIWindowRoot> nsContentUtils::GetWindowRoot(Document* aDoc) {
8749 if (aDoc) {
8750 if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
8751 return win->GetTopWindowRoot();
8754 return nullptr;
8757 /* static */
8758 bool nsContentUtils::LinkContextIsURI(const nsAString& aAnchor,
8759 nsIURI* aDocURI) {
8760 if (aAnchor.IsEmpty()) {
8761 // anchor parameter not present or empty -> same document reference
8762 return true;
8765 // the document URI might contain a fragment identifier ("#...')
8766 // we want to ignore that because it's invisible to the server
8767 // and just affects the local interpretation in the recipient
8768 nsCOMPtr<nsIURI> contextUri;
8769 nsresult rv = NS_GetURIWithoutRef(aDocURI, getter_AddRefs(contextUri));
8771 if (NS_FAILED(rv)) {
8772 // copying failed
8773 return false;
8776 // resolve anchor against context
8777 nsCOMPtr<nsIURI> resolvedUri;
8778 rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor, nullptr, contextUri);
8780 if (NS_FAILED(rv)) {
8781 // resolving failed
8782 return false;
8785 bool same;
8786 rv = contextUri->Equals(resolvedUri, &same);
8787 if (NS_FAILED(rv)) {
8788 // comparison failed
8789 return false;
8792 return same;
8795 /* static */
8796 bool nsContentUtils::IsPreloadType(nsContentPolicyType aType) {
8797 return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
8798 aType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD ||
8799 aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
8800 aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD ||
8801 aType == nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD ||
8802 aType == nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD);
8805 // static
8806 ReferrerPolicy nsContentUtils::GetReferrerPolicyFromChannel(
8807 nsIChannel* aChannel) {
8808 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
8809 if (!httpChannel) {
8810 return ReferrerPolicy::_empty;
8813 nsresult rv;
8814 nsAutoCString headerValue;
8815 rv = httpChannel->GetResponseHeader("referrer-policy"_ns, headerValue);
8816 if (NS_FAILED(rv) || headerValue.IsEmpty()) {
8817 return ReferrerPolicy::_empty;
8820 return ReferrerInfo::ReferrerPolicyFromHeaderString(
8821 NS_ConvertUTF8toUTF16(headerValue));
8824 // static
8825 bool nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel) {
8826 nsLoadFlags loadFlags = 0;
8827 aChannel->GetLoadFlags(&loadFlags);
8828 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
8829 return true;
8832 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
8833 nsContentPolicyType type = loadInfo->InternalContentPolicyType();
8834 return IsNonSubresourceInternalPolicyType(type);
8837 // static
8838 bool nsContentUtils::IsNonSubresourceInternalPolicyType(
8839 nsContentPolicyType aType) {
8840 return aType == nsIContentPolicy::TYPE_DOCUMENT ||
8841 aType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
8842 aType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
8843 aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
8844 aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
8847 // static public
8848 bool nsContentUtils::IsThirdPartyTrackingResourceWindow(
8849 nsPIDOMWindowInner* aWindow) {
8850 MOZ_ASSERT(aWindow);
8852 Document* document = aWindow->GetExtantDoc();
8853 if (!document) {
8854 return false;
8857 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8858 do_QueryInterface(document->GetChannel());
8859 if (!classifiedChannel) {
8860 return false;
8863 return classifiedChannel->IsThirdPartyTrackingResource();
8866 // static public
8867 bool nsContentUtils::IsFirstPartyTrackingResourceWindow(
8868 nsPIDOMWindowInner* aWindow) {
8869 MOZ_ASSERT(aWindow);
8871 Document* document = aWindow->GetExtantDoc();
8872 if (!document) {
8873 return false;
8876 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8877 do_QueryInterface(document->GetChannel());
8878 if (!classifiedChannel) {
8879 return false;
8882 uint32_t classificationFlags =
8883 classifiedChannel->GetFirstPartyClassificationFlags();
8885 return mozilla::net::UrlClassifierCommon::IsTrackingClassificationFlag(
8886 classificationFlags, NS_UsePrivateBrowsing(document->GetChannel()));
8889 namespace {
8891 // We put StringBuilder in the anonymous namespace to prevent anything outside
8892 // this file from accidentally being linked against it.
8893 class BulkAppender {
8894 using size_type = typename nsAString::size_type;
8896 public:
8897 explicit BulkAppender(BulkWriteHandle<char16_t>&& aHandle)
8898 : mHandle(std::move(aHandle)), mPosition(0) {}
8899 ~BulkAppender() = default;
8901 template <int N>
8902 void AppendLiteral(const char16_t (&aStr)[N]) {
8903 size_t len = N - 1;
8904 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8905 memcpy(mHandle.Elements() + mPosition, aStr, len * sizeof(char16_t));
8906 mPosition += len;
8909 void Append(Span<const char16_t> aStr) {
8910 size_t len = aStr.Length();
8911 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8912 // Both mHandle.Elements() and aStr.Elements() are guaranteed
8913 // to be non-null (by the string implementation and by Span,
8914 // respectively), so not checking the pointers for null before
8915 // memcpy does not lead to UB even if len was zero.
8916 memcpy(mHandle.Elements() + mPosition, aStr.Elements(),
8917 len * sizeof(char16_t));
8918 mPosition += len;
8921 void Append(Span<const char> aStr) {
8922 size_t len = aStr.Length();
8923 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8924 ConvertLatin1toUtf16(aStr, mHandle.AsSpan().From(mPosition));
8925 mPosition += len;
8928 void Finish() { mHandle.Finish(mPosition, false); }
8930 private:
8931 BulkWriteHandle<char16_t> mHandle;
8932 size_type mPosition;
8935 class StringBuilder {
8936 private:
8937 class Unit {
8938 public:
8939 Unit() : mAtom(nullptr) { MOZ_COUNT_CTOR(StringBuilder::Unit); }
8940 ~Unit() {
8941 if (mType == Type::String || mType == Type::StringWithEncode) {
8942 mString.~nsString();
8944 MOZ_COUNT_DTOR(StringBuilder::Unit);
8947 enum class Type : uint8_t {
8948 Unknown,
8949 Atom,
8950 String,
8951 StringWithEncode,
8952 Literal,
8953 TextFragment,
8954 TextFragmentWithEncode,
8957 union {
8958 nsAtom* mAtom;
8959 const char16_t* mLiteral;
8960 nsString mString;
8961 const nsTextFragment* mTextFragment;
8963 uint32_t mLength = 0;
8964 Type mType = Type::Unknown;
8967 static_assert(sizeof(void*) != 8 || sizeof(Unit) <= 3 * sizeof(void*),
8968 "Unit should remain small");
8970 public:
8971 // Try to keep the size of StringBuilder close to a jemalloc bucket size (the
8972 // 16kb one in this case).
8973 static constexpr uint32_t TARGET_SIZE = 16 * 1024;
8975 // The number of units we need to remove from the inline buffer so that the
8976 // rest of the builder members fit. A more precise approach would be to
8977 // calculate that extra size and use (TARGET_SIZE - OTHER_SIZE) / sizeof(Unit)
8978 // or so, but this is simpler.
8979 static constexpr uint32_t PADDING_UNITS = sizeof(void*) == 8 ? 1 : 2;
8981 static constexpr uint32_t STRING_BUFFER_UNITS =
8982 TARGET_SIZE / sizeof(Unit) - PADDING_UNITS;
8984 StringBuilder() : mLast(this), mLength(0) { MOZ_COUNT_CTOR(StringBuilder); }
8986 MOZ_COUNTED_DTOR(StringBuilder)
8988 void Append(nsAtom* aAtom) {
8989 Unit* u = AddUnit();
8990 u->mAtom = aAtom;
8991 u->mType = Unit::Type::Atom;
8992 uint32_t len = aAtom->GetLength();
8993 u->mLength = len;
8994 mLength += len;
8997 template <int N>
8998 void Append(const char16_t (&aLiteral)[N]) {
8999 Unit* u = AddUnit();
9000 u->mLiteral = aLiteral;
9001 u->mType = Unit::Type::Literal;
9002 uint32_t len = N - 1;
9003 u->mLength = len;
9004 mLength += len;
9007 void Append(nsString&& aString) {
9008 Unit* u = AddUnit();
9009 uint32_t len = aString.Length();
9010 new (&u->mString) nsString(std::move(aString));
9011 u->mType = Unit::Type::String;
9012 u->mLength = len;
9013 mLength += len;
9016 void AppendWithAttrEncode(nsString&& aString, uint32_t aLen) {
9017 Unit* u = AddUnit();
9018 new (&u->mString) nsString(std::move(aString));
9019 u->mType = Unit::Type::StringWithEncode;
9020 u->mLength = aLen;
9021 mLength += aLen;
9024 void Append(const nsTextFragment* aTextFragment) {
9025 Unit* u = AddUnit();
9026 u->mTextFragment = aTextFragment;
9027 u->mType = Unit::Type::TextFragment;
9028 uint32_t len = aTextFragment->GetLength();
9029 u->mLength = len;
9030 mLength += len;
9033 void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen) {
9034 Unit* u = AddUnit();
9035 u->mTextFragment = aTextFragment;
9036 u->mType = Unit::Type::TextFragmentWithEncode;
9037 u->mLength = aLen;
9038 mLength += aLen;
9041 bool ToString(nsAString& aOut) {
9042 if (!mLength.isValid()) {
9043 return false;
9045 auto appenderOrErr = aOut.BulkWrite(mLength.value(), 0, true);
9046 if (appenderOrErr.isErr()) {
9047 return false;
9050 BulkAppender appender{appenderOrErr.unwrap()};
9052 for (StringBuilder* current = this; current;
9053 current = current->mNext.get()) {
9054 uint32_t len = current->mUnits.Length();
9055 for (uint32_t i = 0; i < len; ++i) {
9056 Unit& u = current->mUnits[i];
9057 switch (u.mType) {
9058 case Unit::Type::Atom:
9059 appender.Append(*(u.mAtom));
9060 break;
9061 case Unit::Type::String:
9062 appender.Append(u.mString);
9063 break;
9064 case Unit::Type::StringWithEncode:
9065 EncodeAttrString(u.mString, appender);
9066 break;
9067 case Unit::Type::Literal:
9068 appender.Append(Span(u.mLiteral, u.mLength));
9069 break;
9070 case Unit::Type::TextFragment:
9071 if (u.mTextFragment->Is2b()) {
9072 appender.Append(
9073 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()));
9074 } else {
9075 appender.Append(
9076 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()));
9078 break;
9079 case Unit::Type::TextFragmentWithEncode:
9080 if (u.mTextFragment->Is2b()) {
9081 EncodeTextFragment(
9082 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()),
9083 appender);
9084 } else {
9085 EncodeTextFragment(
9086 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()),
9087 appender);
9089 break;
9090 default:
9091 MOZ_CRASH("Unknown unit type?");
9095 appender.Finish();
9096 return true;
9099 private:
9100 Unit* AddUnit() {
9101 if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
9102 new StringBuilder(this);
9104 return mLast->mUnits.AppendElement();
9107 explicit StringBuilder(StringBuilder* aFirst) : mLast(nullptr), mLength(0) {
9108 MOZ_COUNT_CTOR(StringBuilder);
9109 aFirst->mLast->mNext = WrapUnique(this);
9110 aFirst->mLast = this;
9113 void EncodeAttrString(Span<const char16_t> aStr, BulkAppender& aAppender) {
9114 size_t flushedUntil = 0;
9115 size_t currentPosition = 0;
9116 for (char16_t c : aStr) {
9117 switch (c) {
9118 case '"':
9119 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9120 aAppender.AppendLiteral(u"&quot;");
9121 flushedUntil = currentPosition + 1;
9122 break;
9123 case '&':
9124 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9125 aAppender.AppendLiteral(u"&amp;");
9126 flushedUntil = currentPosition + 1;
9127 break;
9128 case 0x00A0:
9129 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9130 aAppender.AppendLiteral(u"&nbsp;");
9131 flushedUntil = currentPosition + 1;
9132 break;
9133 default:
9134 break;
9136 currentPosition++;
9138 if (currentPosition > flushedUntil) {
9139 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9143 template <class T>
9144 void EncodeTextFragment(Span<const T> aStr, BulkAppender& aAppender) {
9145 size_t flushedUntil = 0;
9146 size_t currentPosition = 0;
9147 for (T c : aStr) {
9148 switch (c) {
9149 case '<':
9150 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9151 aAppender.AppendLiteral(u"&lt;");
9152 flushedUntil = currentPosition + 1;
9153 break;
9154 case '>':
9155 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9156 aAppender.AppendLiteral(u"&gt;");
9157 flushedUntil = currentPosition + 1;
9158 break;
9159 case '&':
9160 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9161 aAppender.AppendLiteral(u"&amp;");
9162 flushedUntil = currentPosition + 1;
9163 break;
9164 case T(0xA0):
9165 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9166 aAppender.AppendLiteral(u"&nbsp;");
9167 flushedUntil = currentPosition + 1;
9168 break;
9169 default:
9170 break;
9172 currentPosition++;
9174 if (currentPosition > flushedUntil) {
9175 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9179 AutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
9180 UniquePtr<StringBuilder> mNext;
9181 StringBuilder* mLast;
9182 // mLength is used only in the first StringBuilder object in the linked list.
9183 CheckedInt<uint32_t> mLength;
9186 static_assert(sizeof(StringBuilder) <= StringBuilder::TARGET_SIZE,
9187 "StringBuilder should fit in the target bucket");
9189 } // namespace
9191 static void AppendEncodedCharacters(const nsTextFragment* aText,
9192 StringBuilder& aBuilder) {
9193 uint32_t extraSpaceNeeded = 0;
9194 uint32_t len = aText->GetLength();
9195 if (aText->Is2b()) {
9196 const char16_t* data = aText->Get2b();
9197 for (uint32_t i = 0; i < len; ++i) {
9198 const char16_t c = data[i];
9199 switch (c) {
9200 case '<':
9201 extraSpaceNeeded += ArrayLength("&lt;") - 2;
9202 break;
9203 case '>':
9204 extraSpaceNeeded += ArrayLength("&gt;") - 2;
9205 break;
9206 case '&':
9207 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9208 break;
9209 case 0x00A0:
9210 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9211 break;
9212 default:
9213 break;
9216 } else {
9217 const char* data = aText->Get1b();
9218 for (uint32_t i = 0; i < len; ++i) {
9219 const unsigned char c = data[i];
9220 switch (c) {
9221 case '<':
9222 extraSpaceNeeded += ArrayLength("&lt;") - 2;
9223 break;
9224 case '>':
9225 extraSpaceNeeded += ArrayLength("&gt;") - 2;
9226 break;
9227 case '&':
9228 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9229 break;
9230 case 0x00A0:
9231 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9232 break;
9233 default:
9234 break;
9239 if (extraSpaceNeeded) {
9240 aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
9241 } else {
9242 aBuilder.Append(aText);
9246 static uint32_t ExtraSpaceNeededForAttrEncoding(const nsAString& aValue) {
9247 const char16_t* c = aValue.BeginReading();
9248 const char16_t* end = aValue.EndReading();
9250 uint32_t extraSpaceNeeded = 0;
9251 while (c < end) {
9252 switch (*c) {
9253 case '"':
9254 extraSpaceNeeded += ArrayLength("&quot;") - 2;
9255 break;
9256 case '&':
9257 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9258 break;
9259 case 0x00A0:
9260 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9261 break;
9262 default:
9263 break;
9265 ++c;
9268 return extraSpaceNeeded;
9271 static void AppendEncodedAttributeValue(const nsAttrValue& aValue,
9272 StringBuilder& aBuilder) {
9273 if (nsAtom* atom = aValue.GetStoredAtom()) {
9274 nsDependentAtomString atomStr(atom);
9275 uint32_t space = ExtraSpaceNeededForAttrEncoding(atomStr);
9276 if (!space) {
9277 aBuilder.Append(atom);
9278 } else {
9279 aBuilder.AppendWithAttrEncode(nsString(atomStr),
9280 atomStr.Length() + space);
9282 return;
9284 // NOTE(emilio): In most cases this will just be a reference to the stored
9285 // nsStringBuffer.
9286 nsString str;
9287 aValue.ToString(str);
9288 uint32_t space = ExtraSpaceNeededForAttrEncoding(str);
9289 if (space) {
9290 aBuilder.AppendWithAttrEncode(std::move(str), str.Length() + space);
9291 } else {
9292 aBuilder.Append(std::move(str));
9296 static void StartElement(Element* aElement, StringBuilder& aBuilder) {
9297 nsAtom* localName = aElement->NodeInfo()->NameAtom();
9298 const int32_t tagNS = aElement->GetNameSpaceID();
9300 aBuilder.Append(u"<");
9301 if (tagNS == kNameSpaceID_XHTML || tagNS == kNameSpaceID_SVG ||
9302 tagNS == kNameSpaceID_MathML) {
9303 aBuilder.Append(localName);
9304 } else {
9305 aBuilder.Append(nsString(aElement->NodeName()));
9308 if (CustomElementData* ceData = aElement->GetCustomElementData()) {
9309 nsAtom* isAttr = ceData->GetIs(aElement);
9310 if (isAttr && !aElement->HasAttr(nsGkAtoms::is)) {
9311 aBuilder.Append(uR"( is=")");
9312 aBuilder.Append(isAttr);
9313 aBuilder.Append(uR"(")");
9317 uint32_t i = 0;
9318 while (BorrowedAttrInfo info = aElement->GetAttrInfoAt(i++)) {
9319 const nsAttrName* name = info.mName;
9321 int32_t attNs = name->NamespaceID();
9322 nsAtom* attName = name->LocalName();
9324 // Filter out any attribute starting with [-|_]moz
9325 // FIXME(emilio): Do we still need this?
9326 nsDependentAtomString attrNameStr(attName);
9327 if (StringBeginsWith(attrNameStr, u"_moz"_ns) ||
9328 StringBeginsWith(attrNameStr, u"-moz"_ns)) {
9329 continue;
9332 aBuilder.Append(u" ");
9334 if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
9335 (attNs == kNameSpaceID_XMLNS && attName == nsGkAtoms::xmlns)) {
9336 // Nothing else required
9337 } else if (attNs == kNameSpaceID_XML) {
9338 aBuilder.Append(u"xml:");
9339 } else if (attNs == kNameSpaceID_XMLNS) {
9340 aBuilder.Append(u"xmlns:");
9341 } else if (attNs == kNameSpaceID_XLink) {
9342 aBuilder.Append(u"xlink:");
9343 } else if (nsAtom* prefix = name->GetPrefix()) {
9344 aBuilder.Append(prefix);
9345 aBuilder.Append(u":");
9348 aBuilder.Append(attName);
9349 aBuilder.Append(uR"(=")");
9350 AppendEncodedAttributeValue(*info.mValue, aBuilder);
9351 aBuilder.Append(uR"(")");
9354 aBuilder.Append(u">");
9357 // Per HTML spec we should append one \n if the first child of
9358 // pre/textarea/listing is a textnode and starts with a \n.
9359 // But because browsers haven't traditionally had that behavior,
9360 // we're not changing our behavior either - yet.
9361 if (aContent->IsHTMLElement()) {
9362 if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
9363 localName == nsGkAtoms::listing) {
9364 nsIContent* fc = aContent->GetFirstChild();
9365 if (fc &&
9366 (fc->NodeType() == nsINode::TEXT_NODE ||
9367 fc->NodeType() == nsINode::CDATA_SECTION_NODE)) {
9368 const nsTextFragment* text = fc->GetText();
9369 if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
9370 aBuilder.Append("\n");
9377 static inline bool ShouldEscape(nsIContent* aParent) {
9378 if (!aParent || !aParent->IsHTMLElement()) {
9379 return true;
9382 static const nsAtom* nonEscapingElements[] = {
9383 nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
9384 nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
9385 nsGkAtoms::plaintext, nsGkAtoms::noscript};
9386 static mozilla::BitBloomFilter<12, nsAtom> sFilter;
9387 static bool sInitialized = false;
9388 if (!sInitialized) {
9389 sInitialized = true;
9390 for (auto& nonEscapingElement : nonEscapingElements) {
9391 sFilter.add(nonEscapingElement);
9395 nsAtom* tag = aParent->NodeInfo()->NameAtom();
9396 if (sFilter.mightContain(tag)) {
9397 for (auto& nonEscapingElement : nonEscapingElements) {
9398 if (tag == nonEscapingElement) {
9399 if (MOZ_UNLIKELY(tag == nsGkAtoms::noscript) &&
9400 MOZ_UNLIKELY(!aParent->OwnerDoc()->IsScriptEnabled())) {
9401 return true;
9403 return false;
9407 return true;
9410 static inline bool IsVoidTag(Element* aElement) {
9411 if (!aElement->IsHTMLElement()) {
9412 return false;
9414 return FragmentOrElement::IsHTMLVoid(aElement->NodeInfo()->NameAtom());
9417 bool nsContentUtils::SerializeNodeToMarkup(nsINode* aRoot,
9418 bool aDescendantsOnly,
9419 nsAString& aOut) {
9420 // If you pass in a DOCUMENT_NODE, you must pass aDescendentsOnly as true
9421 MOZ_ASSERT(aDescendantsOnly || aRoot->NodeType() != nsINode::DOCUMENT_NODE);
9423 nsINode* current =
9424 aDescendantsOnly ? aRoot->GetFirstChildOfTemplateOrNode() : aRoot;
9426 if (!current) {
9427 return true;
9430 StringBuilder builder;
9431 nsIContent* next;
9432 while (true) {
9433 bool isVoid = false;
9434 switch (current->NodeType()) {
9435 case nsINode::ELEMENT_NODE: {
9436 Element* elem = current->AsElement();
9437 StartElement(elem, builder);
9438 isVoid = IsVoidTag(elem);
9439 if (!isVoid && (next = current->GetFirstChildOfTemplateOrNode())) {
9440 current = next;
9441 continue;
9443 break;
9446 case nsINode::TEXT_NODE:
9447 case nsINode::CDATA_SECTION_NODE: {
9448 const nsTextFragment* text = &current->AsText()->TextFragment();
9449 nsIContent* parent = current->GetParent();
9450 if (ShouldEscape(parent)) {
9451 AppendEncodedCharacters(text, builder);
9452 } else {
9453 builder.Append(text);
9455 break;
9458 case nsINode::COMMENT_NODE: {
9459 builder.Append(u"<!--");
9460 builder.Append(static_cast<nsIContent*>(current)->GetText());
9461 builder.Append(u"-->");
9462 break;
9465 case nsINode::DOCUMENT_TYPE_NODE: {
9466 builder.Append(u"<!DOCTYPE ");
9467 builder.Append(nsString(current->NodeName()));
9468 builder.Append(u">");
9469 break;
9472 case nsINode::PROCESSING_INSTRUCTION_NODE: {
9473 builder.Append(u"<?");
9474 builder.Append(nsString(current->NodeName()));
9475 builder.Append(u" ");
9476 builder.Append(static_cast<nsIContent*>(current)->GetText());
9477 builder.Append(u">");
9478 break;
9482 while (true) {
9483 if (!isVoid && current->NodeType() == nsINode::ELEMENT_NODE) {
9484 builder.Append(u"</");
9485 nsIContent* elem = static_cast<nsIContent*>(current);
9486 if (elem->IsHTMLElement() || elem->IsSVGElement() ||
9487 elem->IsMathMLElement()) {
9488 builder.Append(elem->NodeInfo()->NameAtom());
9489 } else {
9490 builder.Append(nsString(current->NodeName()));
9492 builder.Append(u">");
9494 isVoid = false;
9496 if (current == aRoot) {
9497 return builder.ToString(aOut);
9500 if ((next = current->GetNextSibling())) {
9501 current = next;
9502 break;
9505 current = current->GetParentNode();
9507 // Handle template element. If the parent is a template's content,
9508 // then adjust the parent to be the template element.
9509 if (current != aRoot &&
9510 current->NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
9511 DocumentFragment* frag = static_cast<DocumentFragment*>(current);
9512 nsIContent* fragHost = frag->GetHost();
9513 if (fragHost && fragHost->IsTemplateElement()) {
9514 current = fragHost;
9518 if (aDescendantsOnly && current == aRoot) {
9519 return builder.ToString(aOut);
9525 bool nsContentUtils::IsSpecificAboutPage(JSObject* aGlobal, const char* aUri) {
9526 // aUri must start with about: or this isn't the right function to be using.
9527 MOZ_ASSERT(strncmp(aUri, "about:", 6) == 0);
9529 // Make sure the global is a window
9530 MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
9531 nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
9532 if (!win) {
9533 return false;
9536 nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
9537 NS_ENSURE_TRUE(principal, false);
9539 // First check the scheme to avoid getting long specs in the common case.
9540 if (!principal->SchemeIs("about")) {
9541 return false;
9544 nsAutoCString spec;
9545 principal->GetAsciiSpec(spec);
9547 return spec.EqualsASCII(aUri);
9550 /* static */
9551 void nsContentUtils::SetScrollbarsVisibility(nsIDocShell* aDocShell,
9552 bool aVisible) {
9553 if (!aDocShell) {
9554 return;
9556 auto pref = aVisible ? ScrollbarPreference::Auto : ScrollbarPreference::Never;
9557 nsDocShell::Cast(aDocShell)->SetScrollbarPreference(pref);
9560 /* static */
9561 nsIDocShell* nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget) {
9562 if (!aTarget) {
9563 return nullptr;
9566 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
9567 if (nsCOMPtr<nsINode> node = nsINode::FromEventTarget(aTarget)) {
9568 bool ignore;
9569 innerWindow =
9570 do_QueryInterface(node->OwnerDoc()->GetScriptHandlingObject(ignore));
9571 } else if ((innerWindow = nsPIDOMWindowInner::FromEventTarget(aTarget))) {
9572 // Nothing else to do
9573 } else {
9574 nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(aTarget);
9575 if (helper) {
9576 innerWindow = helper->GetOwner();
9580 if (innerWindow) {
9581 return innerWindow->GetDocShell();
9584 return nullptr;
9588 * Note: this function only relates to figuring out HTTPS state, which is an
9589 * input to the Secure Context algorithm. We are not actually implementing any
9590 * part of the Secure Context algorithm itself here.
9592 * This is a bit of a hack. Ideally we'd propagate HTTPS state through
9593 * nsIChannel as described in the Fetch and HTML specs, but making channels
9594 * know about whether they should inherit HTTPS state, propagating information
9595 * about who the channel's "client" is, exposing GetHttpsState API on channels
9596 * and modifying the various cache implementations to store and retrieve HTTPS
9597 * state involves a huge amount of code (see bug 1220687). We avoid that for
9598 * now using this function.
9600 * This function takes advantage of the observation that we can return true if
9601 * nsIContentSecurityManager::IsOriginPotentiallyTrustworthy returns true for
9602 * the document's origin (e.g. the origin has a scheme of 'https' or host
9603 * 'localhost' etc.). Since we generally propagate a creator document's origin
9604 * onto data:, blob:, etc. documents, this works for them too.
9606 * The scenario where this observation breaks down is sandboxing without the
9607 * 'allow-same-origin' flag, since in this case a document is given a unique
9608 * origin (IsOriginPotentiallyTrustworthy would return false). We handle that
9609 * by using the origin that the document would have had had it not been
9610 * sandboxed.
9612 * DEFICIENCIES: Note that this function uses nsIScriptSecurityManager's
9613 * getChannelResultPrincipalIfNotSandboxed, and that method's ignoring of
9614 * sandboxing is limited to the immediate sandbox. In the case that aDocument
9615 * should inherit its origin (e.g. data: URI) but its parent has ended up
9616 * with a unique origin due to sandboxing further up the parent chain we may
9617 * end up returning false when we would ideally return true (since we will
9618 * examine the parent's origin for 'https' and not finding it.) This means
9619 * that we may restrict the privileges of some pages unnecessarily in this
9620 * edge case.
9622 /* static */
9623 bool nsContentUtils::HttpsStateIsModern(Document* aDocument) {
9624 if (!aDocument) {
9625 return false;
9628 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
9630 if (principal->IsSystemPrincipal()) {
9631 return true;
9634 // If aDocument is sandboxed, try and get the principal that it would have
9635 // been given had it not been sandboxed:
9636 if (principal->GetIsNullPrincipal() &&
9637 (aDocument->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
9638 nsIChannel* channel = aDocument->GetChannel();
9639 if (channel) {
9640 nsCOMPtr<nsIScriptSecurityManager> ssm =
9641 nsContentUtils::GetSecurityManager();
9642 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9643 channel, getter_AddRefs(principal));
9644 if (NS_FAILED(rv)) {
9645 return false;
9647 if (principal->IsSystemPrincipal()) {
9648 // If a document with the system principal is sandboxing a subdocument
9649 // that would normally inherit the embedding element's principal (e.g.
9650 // a srcdoc document) then the embedding document does not trust the
9651 // content that is written to the embedded document. Unlike when the
9652 // embedding document is https, in this case we have no indication as
9653 // to whether the embedded document's contents are delivered securely
9654 // or not, and the sandboxing would possibly indicate that they were
9655 // not. To play it safe we return false here. (See bug 1162772
9656 // comment 73-80.)
9657 return false;
9662 if (principal->GetIsNullPrincipal()) {
9663 return false;
9666 MOZ_ASSERT(principal->GetIsContentPrincipal());
9668 return principal->GetIsOriginPotentiallyTrustworthy();
9671 /* static */
9672 bool nsContentUtils::ComputeIsSecureContext(nsIChannel* aChannel) {
9673 MOZ_ASSERT(aChannel);
9675 nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
9676 nsCOMPtr<nsIPrincipal> principal;
9677 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9678 aChannel, getter_AddRefs(principal));
9679 if (NS_FAILED(rv)) {
9680 return false;
9683 const RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
9685 if (principal->IsSystemPrincipal()) {
9686 // If the load would've been sandboxed, treat this load as an untrusted
9687 // load, as system code considers sandboxed resources insecure.
9688 return !loadInfo->GetLoadingSandboxed();
9691 if (principal->GetIsNullPrincipal()) {
9692 return false;
9695 if (const RefPtr<WindowContext> windowContext =
9696 WindowContext::GetById(loadInfo->GetInnerWindowID())) {
9697 if (!windowContext->GetIsSecureContext()) {
9698 return false;
9702 return principal->GetIsOriginPotentiallyTrustworthy();
9705 /* static */
9706 void nsContentUtils::TryToUpgradeElement(Element* aElement) {
9707 NodeInfo* nodeInfo = aElement->NodeInfo();
9708 RefPtr<nsAtom> typeAtom =
9709 aElement->GetCustomElementData()->GetCustomElementType();
9711 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9712 CustomElementDefinition* definition =
9713 nsContentUtils::LookupCustomElementDefinition(
9714 nodeInfo->GetDocument(), nodeInfo->NameAtom(),
9715 nodeInfo->NamespaceID(), typeAtom);
9716 if (definition) {
9717 nsContentUtils::EnqueueUpgradeReaction(aElement, definition);
9718 } else {
9719 // Add an unresolved custom element that is a candidate for upgrade when a
9720 // custom element is connected to the document.
9721 nsContentUtils::RegisterUnresolvedElement(aElement, typeAtom);
9725 MOZ_CAN_RUN_SCRIPT
9726 static void DoCustomElementCreate(Element** aElement, JSContext* aCx,
9727 Document* aDoc, NodeInfo* aNodeInfo,
9728 CustomElementConstructor* aConstructor,
9729 ErrorResult& aRv, FromParser aFromParser) {
9730 JS::Rooted<JS::Value> constructResult(aCx);
9731 aConstructor->Construct(&constructResult, aRv, "Custom Element Create",
9732 CallbackFunction::eRethrowExceptions);
9733 if (aRv.Failed()) {
9734 return;
9737 RefPtr<Element> element;
9738 // constructResult is an ObjectValue because construction with a callback
9739 // always forms the return value from a JSObject.
9740 UNWRAP_OBJECT(Element, &constructResult, element);
9741 if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9742 if (!element || !element->IsHTMLElement()) {
9743 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9744 "HTMLElement");
9745 return;
9747 } else {
9748 if (!element || !element->IsXULElement()) {
9749 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9750 "XULElement");
9751 return;
9755 nsAtom* localName = aNodeInfo->NameAtom();
9757 if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
9758 element->HasChildren() || element->GetAttrCount() ||
9759 element->NodeInfo()->NameAtom() != localName) {
9760 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
9761 return;
9764 if (element->IsHTMLElement()) {
9765 static_cast<HTMLElement*>(&*element)->InhibitRestoration(
9766 !(aFromParser & FROM_PARSER_NETWORK));
9769 element.forget(aElement);
9772 /* static */
9773 nsresult nsContentUtils::NewXULOrHTMLElement(
9774 Element** aResult, mozilla::dom::NodeInfo* aNodeInfo,
9775 FromParser aFromParser, nsAtom* aIsAtom,
9776 mozilla::dom::CustomElementDefinition* aDefinition) {
9777 RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
9778 MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
9779 nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
9780 "Can only create XUL or XHTML elements.");
9782 nsAtom* name = nodeInfo->NameAtom();
9783 int32_t tag = eHTMLTag_unknown;
9784 bool isCustomElementName = false;
9785 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9786 tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
9787 isCustomElementName =
9788 (tag == eHTMLTag_userdefined &&
9789 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML));
9790 } else { // kNameSpaceID_XUL
9791 if (aIsAtom) {
9792 // Make sure the customized built-in element to be constructed confirms
9793 // to our naming requirement, i.e. [is] must be a dashed name and
9794 // the tag name must not.
9795 // if so, set isCustomElementName to false to kick off all the logics
9796 // that pick up aIsAtom.
9797 if (nsContentUtils::IsNameWithDash(aIsAtom) &&
9798 !nsContentUtils::IsNameWithDash(name)) {
9799 isCustomElementName = false;
9800 } else {
9801 isCustomElementName =
9802 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9804 } else {
9805 isCustomElementName =
9806 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9810 nsAtom* tagAtom = nodeInfo->NameAtom();
9811 nsAtom* typeAtom = nullptr;
9812 bool isCustomElement = isCustomElementName || aIsAtom;
9813 if (isCustomElement) {
9814 typeAtom = isCustomElementName ? tagAtom : aIsAtom;
9817 MOZ_ASSERT_IF(aDefinition, isCustomElement);
9819 // https://dom.spec.whatwg.org/#concept-create-element
9820 // We only handle the "synchronous custom elements flag is set" now.
9821 // For the unset case (e.g. cloning a node), see bug 1319342 for that.
9822 // Step 4.
9823 RefPtr<CustomElementDefinition> definition = aDefinition;
9824 if (isCustomElement && !definition) {
9825 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9826 definition = nsContentUtils::LookupCustomElementDefinition(
9827 nodeInfo->GetDocument(), nodeInfo->NameAtom(), nodeInfo->NamespaceID(),
9828 typeAtom);
9831 // It might be a problem that parser synchronously calls constructor, so filed
9832 // bug 1378079 to figure out what we should do for parser case.
9833 if (definition) {
9835 * Synchronous custom elements flag is determined by 3 places in spec,
9836 * 1) create an element for a token, the flag is determined by
9837 * "will execute script" which is not originally created
9838 * for the HTML fragment parsing algorithm.
9839 * 2) createElement and createElementNS, the flag is the same as
9840 * NOT_FROM_PARSER.
9841 * 3) clone a node, our implementation will not go into this function.
9842 * For the unset case which is non-synchronous only applied for
9843 * inner/outerHTML.
9845 bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT;
9846 // Per discussion in https://github.com/w3c/webcomponents/issues/635,
9847 // use entry global in those places that are called from JS APIs and use the
9848 // node document's global object if it is called from parser.
9849 nsIGlobalObject* global;
9850 if (aFromParser == dom::NOT_FROM_PARSER) {
9851 global = GetEntryGlobal();
9853 // Documents created from the PrototypeDocumentSink always use
9854 // NOT_FROM_PARSER for non-XUL elements. We can get the global from the
9855 // document in that case.
9856 if (!global) {
9857 Document* doc = nodeInfo->GetDocument();
9858 if (doc && doc->LoadedFromPrototype()) {
9859 global = doc->GetScopeObject();
9862 } else {
9863 global = nodeInfo->GetDocument()->GetScopeObject();
9865 if (!global) {
9866 // In browser chrome code, one may have access to a document which doesn't
9867 // have scope object anymore.
9868 return NS_ERROR_FAILURE;
9871 AutoAllowLegacyScriptExecution exemption;
9872 AutoEntryScript aes(global, "create custom elements");
9873 JSContext* cx = aes.cx();
9874 ErrorResult rv;
9876 // Step 5.
9877 if (definition->IsCustomBuiltIn()) {
9878 // SetupCustomElement() should be called with an element that don't have
9879 // CustomElementData setup, if not we will hit the assertion in
9880 // SetCustomElementData().
9881 // Built-in element
9882 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9883 *aResult =
9884 CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9885 } else {
9886 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9888 (*aResult)->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom));
9889 if (synchronousCustomElements) {
9890 CustomElementRegistry::Upgrade(*aResult, definition, rv);
9891 if (rv.MaybeSetPendingException(cx)) {
9892 aes.ReportException();
9894 } else {
9895 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9898 return NS_OK;
9901 // Step 6.1.
9902 if (synchronousCustomElements) {
9903 definition->mPrefixStack.AppendElement(nodeInfo->GetPrefixAtom());
9904 RefPtr<Document> doc = nodeInfo->GetDocument();
9905 DoCustomElementCreate(aResult, cx, doc, nodeInfo,
9906 MOZ_KnownLive(definition->mConstructor), rv,
9907 aFromParser);
9908 if (rv.MaybeSetPendingException(cx)) {
9909 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9910 NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(),
9911 aFromParser));
9912 } else {
9913 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9915 (*aResult)->SetDefined(false);
9917 definition->mPrefixStack.RemoveLastElement();
9918 return NS_OK;
9921 // Step 6.2.
9922 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9923 NS_IF_ADDREF(*aResult =
9924 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9925 } else {
9926 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9928 (*aResult)->SetCustomElementData(
9929 MakeUnique<CustomElementData>(definition->mType));
9930 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9931 return NS_OK;
9934 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9935 // Per the Custom Element specification, unknown tags that are valid custom
9936 // element names should be HTMLElement instead of HTMLUnknownElement.
9937 if (isCustomElementName) {
9938 NS_IF_ADDREF(*aResult =
9939 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9940 } else {
9941 *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9943 } else {
9944 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9947 if (!*aResult) {
9948 return NS_ERROR_OUT_OF_MEMORY;
9951 if (isCustomElement) {
9952 (*aResult)->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom));
9953 nsContentUtils::RegisterCallbackUpgradeElement(*aResult, typeAtom);
9956 return NS_OK;
9959 CustomElementRegistry* nsContentUtils::GetCustomElementRegistry(
9960 Document* aDoc) {
9961 MOZ_ASSERT(aDoc);
9963 if (!aDoc->GetDocShell()) {
9964 return nullptr;
9967 nsPIDOMWindowInner* window = aDoc->GetInnerWindow();
9968 if (!window) {
9969 return nullptr;
9972 return window->CustomElements();
9975 /* static */
9976 CustomElementDefinition* nsContentUtils::LookupCustomElementDefinition(
9977 Document* aDoc, nsAtom* aNameAtom, uint32_t aNameSpaceID,
9978 nsAtom* aTypeAtom) {
9979 if (aNameSpaceID != kNameSpaceID_XUL && aNameSpaceID != kNameSpaceID_XHTML) {
9980 return nullptr;
9983 RefPtr<CustomElementRegistry> registry = GetCustomElementRegistry(aDoc);
9984 if (!registry) {
9985 return nullptr;
9988 return registry->LookupCustomElementDefinition(aNameAtom, aNameSpaceID,
9989 aTypeAtom);
9992 /* static */
9993 void nsContentUtils::RegisterCallbackUpgradeElement(Element* aElement,
9994 nsAtom* aTypeName) {
9995 MOZ_ASSERT(aElement);
9997 Document* doc = aElement->OwnerDoc();
9998 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9999 if (registry) {
10000 registry->RegisterCallbackUpgradeElement(aElement, aTypeName);
10004 /* static */
10005 void nsContentUtils::RegisterUnresolvedElement(Element* aElement,
10006 nsAtom* aTypeName) {
10007 MOZ_ASSERT(aElement);
10009 Document* doc = aElement->OwnerDoc();
10010 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
10011 if (registry) {
10012 registry->RegisterUnresolvedElement(aElement, aTypeName);
10016 /* static */
10017 void nsContentUtils::UnregisterUnresolvedElement(Element* aElement) {
10018 MOZ_ASSERT(aElement);
10020 nsAtom* typeAtom = aElement->GetCustomElementData()->GetCustomElementType();
10021 Document* doc = aElement->OwnerDoc();
10022 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
10023 if (registry) {
10024 registry->UnregisterUnresolvedElement(aElement, typeAtom);
10028 /* static */
10029 void nsContentUtils::EnqueueUpgradeReaction(
10030 Element* aElement, CustomElementDefinition* aDefinition) {
10031 MOZ_ASSERT(aElement);
10033 Document* doc = aElement->OwnerDoc();
10035 // No DocGroup means no custom element reactions stack.
10036 if (!doc->GetDocGroup()) {
10037 return;
10040 CustomElementReactionsStack* stack =
10041 doc->GetDocGroup()->CustomElementReactionsStack();
10042 stack->EnqueueUpgradeReaction(aElement, aDefinition);
10045 /* static */
10046 void nsContentUtils::EnqueueLifecycleCallback(
10047 ElementCallbackType aType, Element* aCustomElement,
10048 const LifecycleCallbackArgs& aArgs, CustomElementDefinition* aDefinition) {
10049 // No DocGroup means no custom element reactions stack.
10050 if (!aCustomElement->OwnerDoc()->GetDocGroup()) {
10051 return;
10054 CustomElementRegistry::EnqueueLifecycleCallback(aType, aCustomElement, aArgs,
10055 aDefinition);
10058 /* static */
10059 CustomElementFormValue nsContentUtils::ConvertToCustomElementFormValue(
10060 const Nullable<OwningFileOrUSVStringOrFormData>& aState) {
10061 if (aState.IsNull()) {
10062 return void_t{};
10064 const auto& state = aState.Value();
10065 if (state.IsFile()) {
10066 RefPtr<BlobImpl> impl = state.GetAsFile()->Impl();
10067 return {std::move(impl)};
10069 if (state.IsUSVString()) {
10070 return state.GetAsUSVString();
10072 return state.GetAsFormData()->ConvertToCustomElementFormValue();
10075 /* static */
10076 Nullable<OwningFileOrUSVStringOrFormData>
10077 nsContentUtils::ExtractFormAssociatedCustomElementValue(
10078 nsIGlobalObject* aGlobal,
10079 const mozilla::dom::CustomElementFormValue& aCEValue) {
10080 MOZ_ASSERT(aGlobal);
10082 OwningFileOrUSVStringOrFormData value;
10083 switch (aCEValue.type()) {
10084 case CustomElementFormValue::TBlobImpl: {
10085 RefPtr<File> file = File::Create(aGlobal, aCEValue.get_BlobImpl());
10086 if (NS_WARN_IF(!file)) {
10087 return {};
10089 value.SetAsFile() = file;
10090 } break;
10092 case CustomElementFormValue::TnsString:
10093 value.SetAsUSVString() = aCEValue.get_nsString();
10094 break;
10096 case CustomElementFormValue::TArrayOfFormDataTuple: {
10097 const auto& array = aCEValue.get_ArrayOfFormDataTuple();
10098 auto formData = MakeRefPtr<FormData>();
10100 for (auto i = 0ul; i < array.Length(); ++i) {
10101 const auto& item = array.ElementAt(i);
10102 switch (item.value().type()) {
10103 case FormDataValue::TnsString:
10104 formData->AddNameValuePair(item.name(),
10105 item.value().get_nsString());
10106 break;
10108 case FormDataValue::TBlobImpl: {
10109 auto blobImpl = item.value().get_BlobImpl();
10110 auto* blob = Blob::Create(aGlobal, blobImpl);
10111 formData->AddNameBlobPair(item.name(), blob);
10112 } break;
10114 default:
10115 continue;
10119 value.SetAsFormData() = formData;
10120 } break;
10121 case CustomElementFormValue::Tvoid_t:
10122 return {};
10123 default:
10124 NS_WARNING("Invalid CustomElementContentData type!");
10125 return {};
10127 return value;
10130 /* static */
10131 void nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
10132 Document* aDocument, nsTArray<nsIContent*>& aElements) {
10133 MOZ_ASSERT(aDocument);
10134 #ifdef DEBUG
10135 size_t oldLength = aElements.Length();
10136 #endif
10138 if (PresShell* presShell = aDocument->GetPresShell()) {
10139 if (nsIFrame* scrollFrame = presShell->GetRootScrollFrame()) {
10140 nsIAnonymousContentCreator* creator = do_QueryFrame(scrollFrame);
10141 MOZ_ASSERT(
10142 creator,
10143 "scroll frame should always implement nsIAnonymousContentCreator");
10144 creator->AppendAnonymousContentTo(aElements, 0);
10146 if (nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame()) {
10147 canvasFrame->AppendAnonymousContentTo(aElements, 0);
10151 #ifdef DEBUG
10152 for (size_t i = oldLength; i < aElements.Length(); i++) {
10153 MOZ_ASSERT(
10154 aElements[i]->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent),
10155 "Someone here has lied, or missed to flag the node");
10157 #endif
10160 static void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame,
10161 nsTArray<nsIContent*>& aKids,
10162 uint32_t aFlags) {
10163 if (nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame)) {
10164 ac->AppendAnonymousContentTo(aKids, aFlags);
10168 /* static */
10169 void nsContentUtils::AppendNativeAnonymousChildren(const nsIContent* aContent,
10170 nsTArray<nsIContent*>& aKids,
10171 uint32_t aFlags) {
10172 if (aContent->MayHaveAnonymousChildren()) {
10173 if (nsIFrame* primaryFrame = aContent->GetPrimaryFrame()) {
10174 // NAC created by the element's primary frame.
10175 AppendNativeAnonymousChildrenFromFrame(primaryFrame, aKids, aFlags);
10177 // NAC created by any other non-primary frames for the element.
10178 AutoTArray<nsIFrame::OwnedAnonBox, 8> ownedAnonBoxes;
10179 primaryFrame->AppendOwnedAnonBoxes(ownedAnonBoxes);
10180 for (nsIFrame::OwnedAnonBox& box : ownedAnonBoxes) {
10181 MOZ_ASSERT(box.mAnonBoxFrame->GetContent() == aContent);
10182 AppendNativeAnonymousChildrenFromFrame(box.mAnonBoxFrame, aKids,
10183 aFlags);
10187 // Get manually created NAC (editor resize handles, etc.).
10188 if (auto nac = static_cast<ManualNACArray*>(
10189 aContent->GetProperty(nsGkAtoms::manualNACProperty))) {
10190 aKids.AppendElements(*nac);
10194 // The root scroll frame is not the primary frame of the root element.
10195 // Detect and handle this case.
10196 if (!(aFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
10197 aContent == aContent->OwnerDoc()->GetRootElement()) {
10198 AppendDocumentLevelNativeAnonymousContentTo(aContent->OwnerDoc(), aKids);
10202 bool nsContentUtils::IsImageAvailable(nsIContent* aLoadingNode, nsIURI* aURI,
10203 nsIPrincipal* aDefaultTriggeringPrincipal,
10204 CORSMode aCORSMode) {
10205 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
10206 QueryTriggeringPrincipal(aLoadingNode, aDefaultTriggeringPrincipal,
10207 getter_AddRefs(triggeringPrincipal));
10208 MOZ_ASSERT(triggeringPrincipal);
10210 Document* doc = aLoadingNode->OwnerDoc();
10211 return IsImageAvailable(aURI, triggeringPrincipal, aCORSMode, doc);
10214 bool nsContentUtils::IsImageAvailable(nsIURI* aURI,
10215 nsIPrincipal* aTriggeringPrincipal,
10216 CORSMode aCORSMode, Document* aDoc) {
10217 imgLoader* imgLoader = GetImgLoaderForDocument(aDoc);
10218 return imgLoader->IsImageAvailable(aURI, aTriggeringPrincipal, aCORSMode,
10219 aDoc);
10222 /* static */
10223 bool nsContentUtils::QueryTriggeringPrincipal(
10224 nsIContent* aLoadingNode, nsIPrincipal* aDefaultPrincipal,
10225 nsIPrincipal** aTriggeringPrincipal) {
10226 MOZ_ASSERT(aLoadingNode);
10227 MOZ_ASSERT(aTriggeringPrincipal);
10229 bool result = false;
10230 nsCOMPtr<nsIPrincipal> loadingPrincipal = aDefaultPrincipal;
10231 if (!loadingPrincipal) {
10232 loadingPrincipal = aLoadingNode->NodePrincipal();
10235 // If aLoadingNode is content, bail out early.
10236 if (!aLoadingNode->NodePrincipal()->IsSystemPrincipal()) {
10237 loadingPrincipal.forget(aTriggeringPrincipal);
10238 return result;
10241 nsAutoString loadingStr;
10242 if (aLoadingNode->IsElement()) {
10243 aLoadingNode->AsElement()->GetAttr(
10244 kNameSpaceID_None, nsGkAtoms::triggeringprincipal, loadingStr);
10247 // Fall back if 'triggeringprincipal' isn't specified,
10248 if (loadingStr.IsEmpty()) {
10249 loadingPrincipal.forget(aTriggeringPrincipal);
10250 return result;
10253 nsCString binary;
10254 nsCOMPtr<nsIPrincipal> serializedPrin =
10255 BasePrincipal::FromJSON(NS_ConvertUTF16toUTF8(loadingStr));
10256 if (serializedPrin) {
10257 result = true;
10258 serializedPrin.forget(aTriggeringPrincipal);
10261 if (!result) {
10262 // Fallback if the deserialization is failed.
10263 loadingPrincipal.forget(aTriggeringPrincipal);
10266 return result;
10269 /* static */
10270 void nsContentUtils::GetContentPolicyTypeForUIImageLoading(
10271 nsIContent* aLoadingNode, nsIPrincipal** aTriggeringPrincipal,
10272 nsContentPolicyType& aContentPolicyType, uint64_t* aRequestContextID) {
10273 MOZ_ASSERT(aRequestContextID);
10275 bool result = QueryTriggeringPrincipal(aLoadingNode, aTriggeringPrincipal);
10276 if (result) {
10277 // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for
10278 // indicating it's a favicon loading.
10279 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
10281 nsAutoString requestContextID;
10282 if (aLoadingNode->IsElement()) {
10283 aLoadingNode->AsElement()->GetAttr(
10284 kNameSpaceID_None, nsGkAtoms::requestcontextid, requestContextID);
10286 nsresult rv;
10287 int64_t val = requestContextID.ToInteger64(&rv);
10288 *aRequestContextID = NS_SUCCEEDED(rv) ? val : 0;
10289 } else {
10290 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
10294 /* static */
10295 nsresult nsContentUtils::CreateJSValueFromSequenceOfObject(
10296 JSContext* aCx, const Sequence<JSObject*>& aTransfer,
10297 JS::MutableHandle<JS::Value> aValue) {
10298 if (aTransfer.IsEmpty()) {
10299 return NS_OK;
10302 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, aTransfer.Length()));
10303 if (!array) {
10304 return NS_ERROR_OUT_OF_MEMORY;
10307 for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
10308 JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
10309 if (!object) {
10310 continue;
10313 if (NS_WARN_IF(
10314 !JS_DefineElement(aCx, array, i, object, JSPROP_ENUMERATE))) {
10315 return NS_ERROR_OUT_OF_MEMORY;
10319 aValue.setObject(*array);
10320 return NS_OK;
10323 /* static */
10324 void nsContentUtils::StructuredClone(JSContext* aCx, nsIGlobalObject* aGlobal,
10325 JS::Handle<JS::Value> aValue,
10326 const StructuredSerializeOptions& aOptions,
10327 JS::MutableHandle<JS::Value> aRetval,
10328 ErrorResult& aError) {
10329 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
10330 aError = nsContentUtils::CreateJSValueFromSequenceOfObject(
10331 aCx, aOptions.mTransfer, &transferArray);
10332 if (NS_WARN_IF(aError.Failed())) {
10333 return;
10336 JS::CloneDataPolicy clonePolicy;
10337 // We are definitely staying in the same agent cluster.
10338 clonePolicy.allowIntraClusterClonableSharedObjects();
10339 if (aGlobal->IsSharedMemoryAllowed()) {
10340 clonePolicy.allowSharedMemoryObjects();
10343 StructuredCloneHolder holder(StructuredCloneHolder::CloningSupported,
10344 StructuredCloneHolder::TransferringSupported,
10345 JS::StructuredCloneScope::SameProcess);
10346 holder.Write(aCx, aValue, transferArray, clonePolicy, aError);
10347 if (NS_WARN_IF(aError.Failed())) {
10348 return;
10351 holder.Read(aGlobal, aCx, aRetval, clonePolicy, aError);
10352 if (NS_WARN_IF(aError.Failed())) {
10353 return;
10356 nsTArray<RefPtr<MessagePort>> ports = holder.TakeTransferredPorts();
10357 Unused << ports;
10360 /* static */
10361 bool nsContentUtils::ShouldBlockReservedKeys(WidgetKeyboardEvent* aKeyEvent) {
10362 nsCOMPtr<nsIPrincipal> principal;
10363 RefPtr<Element> targetElement =
10364 Element::FromEventTargetOrNull(aKeyEvent->mOriginalTarget);
10365 nsCOMPtr<nsIBrowser> targetBrowser;
10366 if (targetElement) {
10367 targetBrowser = targetElement->AsBrowser();
10369 bool isRemoteBrowser = false;
10370 if (targetBrowser) {
10371 targetBrowser->GetIsRemoteBrowser(&isRemoteBrowser);
10374 if (isRemoteBrowser) {
10375 targetBrowser->GetContentPrincipal(getter_AddRefs(principal));
10376 return principal ? nsContentUtils::IsSitePermDeny(principal, "shortcuts"_ns)
10377 : false;
10380 if (targetElement) {
10381 Document* doc = targetElement->GetUncomposedDoc();
10382 if (doc) {
10383 RefPtr<WindowContext> wc = doc->GetWindowContext();
10384 if (wc) {
10385 return wc->TopWindowContext()->GetShortcutsPermission() ==
10386 nsIPermissionManager::DENY_ACTION;
10391 return false;
10395 * Checks whether the given type is a supported document type for
10396 * loading within the nsObjectLoadingContent specified by aContent.
10398 * NOTE Helper method for nsContentUtils::HtmlObjectContentTypeForMIMEType.
10399 * NOTE Does not take content policy or capabilities into account
10401 static bool HtmlObjectContentSupportsDocument(const nsCString& aMimeType) {
10402 nsCOMPtr<nsIWebNavigationInfo> info(
10403 do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID));
10404 if (!info) {
10405 return false;
10408 uint32_t supported;
10409 nsresult rv = info->IsTypeSupported(aMimeType, &supported);
10411 if (NS_FAILED(rv)) {
10412 return false;
10415 if (supported != nsIWebNavigationInfo::UNSUPPORTED) {
10416 // Don't want to support plugins as documents
10417 return supported != nsIWebNavigationInfo::FALLBACK;
10420 // Try a stream converter
10421 // NOTE: We treat any type we can convert from as a supported type. If a
10422 // type is not actually supported, the URI loader will detect that and
10423 // return an error, and we'll fallback.
10424 nsCOMPtr<nsIStreamConverterService> convServ =
10425 do_GetService("@mozilla.org/streamConverters;1");
10426 bool canConvert = false;
10427 if (convServ) {
10428 rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
10430 return NS_SUCCEEDED(rv) && canConvert;
10433 /* static */
10434 already_AddRefed<nsIPluginTag> nsContentUtils::PluginTagForType(
10435 const nsCString& aMIMEType, bool aNoFakePlugin) {
10436 RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
10437 nsCOMPtr<nsIPluginTag> tag;
10438 NS_ENSURE_TRUE(pluginHost, nullptr);
10440 // ShouldPlay will handle the case where the plugin is disabled
10441 pluginHost->GetPluginTagForType(
10442 aMIMEType,
10443 aNoFakePlugin ? nsPluginHost::eExcludeFake : nsPluginHost::eExcludeNone,
10444 getter_AddRefs(tag));
10446 return tag.forget();
10449 /* static */
10450 uint32_t nsContentUtils::HtmlObjectContentTypeForMIMEType(
10451 const nsCString& aMIMEType, bool aNoFakePlugin) {
10452 if (aMIMEType.IsEmpty()) {
10453 return nsIObjectLoadingContent::TYPE_NULL;
10456 if (imgLoader::SupportImageWithMimeType(aMIMEType)) {
10457 return ResolveObjectType(nsIObjectLoadingContent::TYPE_IMAGE);
10460 // Faking support of the PDF content as a document for EMBED tags
10461 // when internal PDF viewer is enabled.
10462 if (aMIMEType.LowerCaseEqualsLiteral("application/pdf") && IsPDFJSEnabled()) {
10463 return nsIObjectLoadingContent::TYPE_DOCUMENT;
10466 if (HtmlObjectContentSupportsDocument(aMIMEType)) {
10467 return nsIObjectLoadingContent::TYPE_DOCUMENT;
10470 bool isSpecialPlugin = nsPluginHost::GetSpecialType(aMIMEType) !=
10471 nsPluginHost::eSpecialType_None;
10472 if (isSpecialPlugin) {
10473 return nsIObjectLoadingContent::TYPE_FALLBACK;
10476 return nsIObjectLoadingContent::TYPE_NULL;
10479 /* static */
10480 already_AddRefed<nsISerialEventTarget> nsContentUtils::GetEventTargetByLoadInfo(
10481 nsILoadInfo* aLoadInfo, TaskCategory aCategory) {
10482 if (NS_WARN_IF(!aLoadInfo)) {
10483 return nullptr;
10486 RefPtr<Document> doc;
10487 aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
10488 nsCOMPtr<nsISerialEventTarget> target;
10489 if (doc) {
10490 if (DocGroup* group = doc->GetDocGroup()) {
10491 target = group->EventTargetFor(aCategory);
10493 } else {
10494 target = GetMainThreadSerialEventTarget();
10497 return target.forget();
10500 /* static */
10501 bool nsContentUtils::IsLocalRefURL(const nsAString& aString) {
10502 return !aString.IsEmpty() && aString[0] == '#';
10505 // We use only 53 bits for the ID so that it can be converted to and from a JS
10506 // value without loss of precision. The upper bits of the ID hold the process
10507 // ID. The lower bits identify the object itself.
10508 static constexpr uint64_t kIdTotalBits = 53;
10509 static constexpr uint64_t kIdProcessBits = 22;
10510 static constexpr uint64_t kIdBits = kIdTotalBits - kIdProcessBits;
10512 /* static */
10513 uint64_t nsContentUtils::GenerateProcessSpecificId(uint64_t aId) {
10514 uint64_t processId = 0;
10515 if (XRE_IsContentProcess()) {
10516 ContentChild* cc = ContentChild::GetSingleton();
10517 processId = cc->GetID();
10520 MOZ_RELEASE_ASSERT(processId < (uint64_t(1) << kIdProcessBits));
10521 uint64_t processBits = processId & ((uint64_t(1) << kIdProcessBits) - 1);
10523 uint64_t id = aId;
10524 MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
10525 uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
10527 return (processBits << kIdBits) | bits;
10530 /* static */
10531 std::tuple<uint64_t, uint64_t> nsContentUtils::SplitProcessSpecificId(
10532 uint64_t aId) {
10533 return {aId >> kIdBits, aId & ((uint64_t(1) << kIdBits) - 1)};
10536 // Next process-local Tab ID.
10537 static uint64_t gNextTabId = 0;
10539 /* static */
10540 uint64_t nsContentUtils::GenerateTabId() {
10541 return GenerateProcessSpecificId(++gNextTabId);
10544 // Next process-local Browser ID.
10545 static uint64_t gNextBrowserId = 0;
10547 /* static */
10548 uint64_t nsContentUtils::GenerateBrowserId() {
10549 return GenerateProcessSpecificId(++gNextBrowserId);
10552 // Next process-local Browsing Context ID.
10553 static uint64_t gNextBrowsingContextId = 0;
10555 /* static */
10556 uint64_t nsContentUtils::GenerateBrowsingContextId() {
10557 return GenerateProcessSpecificId(++gNextBrowsingContextId);
10560 // Next process-local Window ID.
10561 static uint64_t gNextWindowId = 0;
10563 /* static */
10564 uint64_t nsContentUtils::GenerateWindowId() {
10565 return GenerateProcessSpecificId(++gNextWindowId);
10568 // Next process-local load.
10569 static Atomic<uint64_t> gNextLoadIdentifier(0);
10571 /* static */
10572 uint64_t nsContentUtils::GenerateLoadIdentifier() {
10573 return GenerateProcessSpecificId(++gNextLoadIdentifier);
10576 /* static */
10577 bool nsContentUtils::GetUserIsInteracting() {
10578 return UserInteractionObserver::sUserActive;
10581 /* static */
10582 bool nsContentUtils::GetSourceMapURL(nsIHttpChannel* aChannel,
10583 nsACString& aResult) {
10584 nsresult rv = aChannel->GetResponseHeader("SourceMap"_ns, aResult);
10585 if (NS_FAILED(rv)) {
10586 rv = aChannel->GetResponseHeader("X-SourceMap"_ns, aResult);
10588 return NS_SUCCEEDED(rv);
10591 /* static */
10592 bool nsContentUtils::IsMessageInputEvent(const IPC::Message& aMsg) {
10593 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10594 mozilla::dom::PBrowser::PBrowserStart) {
10595 switch (aMsg.type()) {
10596 case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
10597 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10598 case mozilla::dom::PBrowser::Msg_RealMouseEnterExitWidgetEvent__ID:
10599 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10600 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10601 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10602 case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
10603 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10604 case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
10605 return true;
10608 return false;
10611 /* static */
10612 bool nsContentUtils::IsMessageCriticalInputEvent(const IPC::Message& aMsg) {
10613 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10614 mozilla::dom::PBrowser::PBrowserStart) {
10615 switch (aMsg.type()) {
10616 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10617 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10618 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10619 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10620 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10621 return true;
10624 return false;
10627 static const char* kUserInteractionInactive = "user-interaction-inactive";
10628 static const char* kUserInteractionActive = "user-interaction-active";
10630 void nsContentUtils::UserInteractionObserver::Init() {
10631 // Listen for the observer messages from EventStateManager which are telling
10632 // us whether or not the user is interacting.
10633 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10634 obs->AddObserver(this, kUserInteractionInactive, false);
10635 obs->AddObserver(this, kUserInteractionActive, false);
10637 // We can't register ourselves as an annotator yet, as the
10638 // BackgroundHangMonitor hasn't started yet. It will have started by the
10639 // time we have the chance to spin the event loop.
10640 RefPtr<UserInteractionObserver> self = this;
10641 NS_DispatchToMainThread(NS_NewRunnableFunction(
10642 "nsContentUtils::UserInteractionObserver::Init",
10643 [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
10646 void nsContentUtils::UserInteractionObserver::Shutdown() {
10647 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10648 if (obs) {
10649 obs->RemoveObserver(this, kUserInteractionInactive);
10650 obs->RemoveObserver(this, kUserInteractionActive);
10653 BackgroundHangMonitor::UnregisterAnnotator(*this);
10657 * NB: This function is always called by the BackgroundHangMonitor thread.
10658 * Plan accordingly
10660 void nsContentUtils::UserInteractionObserver::AnnotateHang(
10661 BackgroundHangAnnotations& aAnnotations) {
10662 // NOTE: Only annotate the hang report if the user is known to be interacting.
10663 if (sUserActive) {
10664 aAnnotations.AddAnnotation(u"UserInteracting"_ns, true);
10668 NS_IMETHODIMP
10669 nsContentUtils::UserInteractionObserver::Observe(nsISupports* aSubject,
10670 const char* aTopic,
10671 const char16_t* aData) {
10672 if (!strcmp(aTopic, kUserInteractionInactive)) {
10673 if (sUserActive && XRE_IsParentProcess()) {
10674 glean::RecordPowerMetrics();
10676 sUserActive = false;
10677 } else if (!strcmp(aTopic, kUserInteractionActive)) {
10678 if (!sUserActive && XRE_IsParentProcess()) {
10679 glean::RecordPowerMetrics();
10681 nsCOMPtr<nsIUserIdleServiceInternal> idleService =
10682 do_GetService("@mozilla.org/widget/useridleservice;1");
10683 if (idleService) {
10684 idleService->ResetIdleTimeOut(0);
10688 sUserActive = true;
10689 } else {
10690 NS_WARNING("Unexpected observer notification");
10692 return NS_OK;
10695 Atomic<bool> nsContentUtils::UserInteractionObserver::sUserActive(false);
10696 NS_IMPL_ISUPPORTS(nsContentUtils::UserInteractionObserver, nsIObserver)
10698 /* static */
10699 bool nsContentUtils::IsSpecialName(const nsAString& aName) {
10700 return aName.LowerCaseEqualsLiteral("_blank") ||
10701 aName.LowerCaseEqualsLiteral("_top") ||
10702 aName.LowerCaseEqualsLiteral("_parent") ||
10703 aName.LowerCaseEqualsLiteral("_self");
10706 /* static */
10707 bool nsContentUtils::IsOverridingWindowName(const nsAString& aName) {
10708 return !aName.IsEmpty() && !IsSpecialName(aName);
10711 // Unfortunately, we can't unwrap an IDL object using only a concrete type.
10712 // We need to calculate type data based on the IDL typename. Which means
10713 // wrapping our templated function in a macro.
10714 #define EXTRACT_EXN_VALUES(T, ...) \
10715 ExtractExceptionValues<mozilla::dom::prototypes::id::T, \
10716 T##_Binding::NativeType, T>(__VA_ARGS__) \
10717 .isOk()
10719 template <prototypes::ID PrototypeID, class NativeType, typename T>
10720 static Result<Ok, nsresult> ExtractExceptionValues(
10721 JSContext* aCx, JS::Handle<JSObject*> aObj, nsAString& aSourceSpecOut,
10722 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10723 AssertStaticUnwrapOK<PrototypeID>();
10724 RefPtr<T> exn;
10725 MOZ_TRY((UnwrapObject<PrototypeID, NativeType>(aObj, exn, nullptr)));
10727 exn->GetFilename(aCx, aSourceSpecOut);
10728 if (!aSourceSpecOut.IsEmpty()) {
10729 *aLineOut = exn->LineNumber(aCx);
10730 *aColumnOut = exn->ColumnNumber();
10733 exn->GetName(aMessageOut);
10734 aMessageOut.AppendLiteral(": ");
10736 nsAutoString message;
10737 exn->GetMessageMoz(message);
10738 aMessageOut.Append(message);
10739 return Ok();
10742 /* static */
10743 void nsContentUtils::ExtractErrorValues(
10744 JSContext* aCx, JS::Handle<JS::Value> aValue, nsACString& aSourceSpecOut,
10745 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10746 nsAutoString sourceSpec;
10747 ExtractErrorValues(aCx, aValue, sourceSpec, aLineOut, aColumnOut,
10748 aMessageOut);
10749 CopyUTF16toUTF8(sourceSpec, aSourceSpecOut);
10752 /* static */
10753 void nsContentUtils::ExtractErrorValues(
10754 JSContext* aCx, JS::Handle<JS::Value> aValue, nsAString& aSourceSpecOut,
10755 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10756 MOZ_ASSERT(aLineOut);
10757 MOZ_ASSERT(aColumnOut);
10759 if (aValue.isObject()) {
10760 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
10762 // Try to process as an Error object. Use the file/line/column values
10763 // from the Error as they will be more specific to the root cause of
10764 // the problem.
10765 JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
10766 if (err) {
10767 // Use xpc to extract the error message only. We don't actually send
10768 // this report anywhere.
10769 RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
10770 report->Init(err,
10771 nullptr, // toString result
10772 false, // chrome
10773 0); // window ID
10775 if (!report->mFileName.IsEmpty()) {
10776 aSourceSpecOut = report->mFileName;
10777 *aLineOut = report->mLineNumber;
10778 *aColumnOut = report->mColumn;
10780 aMessageOut.Assign(report->mErrorMsg);
10783 // Next, try to unwrap the rejection value as a DOMException.
10784 else if (EXTRACT_EXN_VALUES(DOMException, aCx, obj, aSourceSpecOut,
10785 aLineOut, aColumnOut, aMessageOut)) {
10786 return;
10789 // Next, try to unwrap the rejection value as an XPC Exception.
10790 else if (EXTRACT_EXN_VALUES(Exception, aCx, obj, aSourceSpecOut, aLineOut,
10791 aColumnOut, aMessageOut)) {
10792 return;
10796 // If we could not unwrap a specific error type, then perform default safe
10797 // string conversions on primitives. Objects will result in "[Object]"
10798 // unfortunately.
10799 if (aMessageOut.IsEmpty()) {
10800 nsAutoJSString jsString;
10801 if (jsString.init(aCx, aValue)) {
10802 aMessageOut = jsString;
10803 } else {
10804 JS_ClearPendingException(aCx);
10809 #undef EXTRACT_EXN_VALUES
10811 /* static */
10812 bool nsContentUtils::ContentIsLink(nsIContent* aContent) {
10813 if (!aContent || !aContent->IsElement()) {
10814 return false;
10817 if (aContent->IsHTMLElement(nsGkAtoms::a)) {
10818 return true;
10821 return aContent->AsElement()->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
10822 nsGkAtoms::simple, eCaseMatters);
10825 /* static */
10826 already_AddRefed<ContentFrameMessageManager>
10827 nsContentUtils::TryGetBrowserChildGlobal(nsISupports* aFrom) {
10828 RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(aFrom);
10829 if (!frameLoaderOwner) {
10830 return nullptr;
10833 RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
10834 if (!frameLoader) {
10835 return nullptr;
10838 RefPtr<ContentFrameMessageManager> manager =
10839 frameLoader->GetBrowserChildMessageManager();
10840 return manager.forget();
10843 /* static */
10844 uint32_t nsContentUtils::InnerOrOuterWindowCreated() {
10845 MOZ_ASSERT(NS_IsMainThread());
10846 ++sInnerOrOuterWindowCount;
10847 return ++sInnerOrOuterWindowSerialCounter;
10850 /* static */
10851 void nsContentUtils::InnerOrOuterWindowDestroyed() {
10852 MOZ_ASSERT(NS_IsMainThread());
10853 MOZ_ASSERT(sInnerOrOuterWindowCount > 0);
10854 --sInnerOrOuterWindowCount;
10857 /* static */
10858 nsresult nsContentUtils::AnonymizeURI(nsIURI* aURI, nsCString& aAnonymizedURI) {
10859 MOZ_ASSERT(aURI);
10861 if (aURI->SchemeIs("data")) {
10862 aAnonymizedURI.Assign("data:..."_ns);
10863 return NS_OK;
10865 // Anonymize the URL.
10866 // Strip the URL of any possible username/password and make it ready to be
10867 // presented in the UI.
10868 nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(aURI);
10869 return exposableURI->GetSpec(aAnonymizedURI);
10872 static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
10873 nsAString* result = static_cast<nsAString*>(aData);
10874 result->Append(aBuf, aLen);
10875 return true;
10878 /* static */
10879 bool nsContentUtils::StringifyJSON(JSContext* aCx, JS::Handle<JS::Value> aValue,
10880 nsAString& aOutStr, JSONBehavior aBehavior) {
10881 MOZ_ASSERT(aCx);
10882 switch (aBehavior) {
10883 case UndefinedIsNullStringLiteral: {
10884 aOutStr.Truncate();
10885 JS::Rooted<JS::Value> value(aCx, aValue);
10886 nsAutoString serializedValue;
10887 NS_ENSURE_TRUE(JS_Stringify(aCx, &value, nullptr, JS::NullHandleValue,
10888 JSONCreator, &serializedValue),
10889 false);
10890 aOutStr = serializedValue;
10891 return true;
10893 case UndefinedIsVoidString: {
10894 aOutStr.SetIsVoid(true);
10895 return JS::ToJSON(aCx, aValue, nullptr, JS::NullHandleValue, JSONCreator,
10896 &aOutStr);
10898 default:
10899 MOZ_ASSERT_UNREACHABLE("Invalid value for aBehavior");
10900 return false;
10904 /* static */
10905 bool nsContentUtils::
10906 HighPriorityEventPendingForTopLevelDocumentBeforeContentfulPaint(
10907 Document* aDocument) {
10908 MOZ_ASSERT(XRE_IsContentProcess(),
10909 "This function only makes sense in content processes");
10911 if (aDocument && !aDocument->IsLoadedAsData()) {
10912 if (nsPresContext* presContext = FindPresContextForDocument(aDocument)) {
10913 MOZ_ASSERT(!presContext->IsChrome(),
10914 "Should never have a chrome PresContext in a content process");
10916 return !presContext->GetInProcessRootContentDocumentPresContext()
10917 ->HadFirstContentfulPaint() &&
10918 nsThreadManager::MainThreadHasPendingHighPriorityEvents();
10921 return false;
10924 static nsGlobalWindowInner* GetInnerWindowForGlobal(nsIGlobalObject* aGlobal) {
10925 NS_ENSURE_TRUE(aGlobal, nullptr);
10927 if (auto* window = aGlobal->AsInnerWindow()) {
10928 return nsGlobalWindowInner::Cast(window);
10931 // When Extensions run content scripts inside a sandbox, it uses
10932 // sandboxPrototype to make them appear as though they're running in the
10933 // scope of the page. So when a content script invokes postMessage, it expects
10934 // the |source| of the received message to be the window set as the
10935 // sandboxPrototype. This used to work incidentally for unrelated reasons, but
10936 // now we need to do some special handling to support it.
10937 JS::Rooted<JSObject*> scope(RootingCx(), aGlobal->GetGlobalJSObject());
10938 NS_ENSURE_TRUE(scope, nullptr);
10940 if (xpc::IsSandbox(scope)) {
10941 AutoJSAPI jsapi;
10942 MOZ_ALWAYS_TRUE(jsapi.Init(scope));
10943 JSContext* cx = jsapi.cx();
10944 // Our current Realm on aCx is the sandbox. Using that for unwrapping
10945 // makes sense: if the sandbox can unwrap the window, we can use it.
10946 return xpc::SandboxWindowOrNull(scope, cx);
10949 // The calling window must be holding a reference, so we can return a weak
10950 // pointer.
10951 return nsGlobalWindowInner::Cast(aGlobal->AsInnerWindow());
10954 /* static */
10955 nsGlobalWindowInner* nsContentUtils::IncumbentInnerWindow() {
10956 return GetInnerWindowForGlobal(GetIncumbentGlobal());
10959 /* static */
10960 nsGlobalWindowInner* nsContentUtils::EntryInnerWindow() {
10961 return GetInnerWindowForGlobal(GetEntryGlobal());
10964 /* static */
10965 bool nsContentUtils::IsURIInPrefList(nsIURI* aURI, const char* aPrefName) {
10966 MOZ_ASSERT(aPrefName);
10968 nsAutoCString list;
10969 Preferences::GetCString(aPrefName, list);
10970 ToLowerCase(list);
10971 return IsURIInList(aURI, list);
10974 /* static */
10975 bool nsContentUtils::IsURIInList(nsIURI* aURI, const nsCString& aList) {
10976 #ifdef DEBUG
10977 nsAutoCString listLowerCase(aList);
10978 ToLowerCase(listLowerCase);
10979 MOZ_ASSERT(listLowerCase.Equals(aList),
10980 "The aList argument should be lower-case");
10981 #endif
10983 if (!aURI) {
10984 return false;
10987 nsAutoCString scheme;
10988 aURI->GetScheme(scheme);
10989 if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
10990 return false;
10993 if (aList.IsEmpty()) {
10994 return false;
10997 // The list is comma separated domain list. Each item may start with "*.".
10998 // If starts with "*.", it matches any sub-domains.
11000 nsCCharSeparatedTokenizer tokenizer(aList, ',');
11001 while (tokenizer.hasMoreTokens()) {
11002 const nsCString token(tokenizer.nextToken());
11004 nsAutoCString host;
11005 aURI->GetHost(host);
11006 if (host.IsEmpty()) {
11007 return false;
11009 ToLowerCase(host);
11011 for (;;) {
11012 int32_t index = token.Find(host);
11013 if (index >= 0 &&
11014 static_cast<uint32_t>(index) + host.Length() <= token.Length()) {
11015 // If we found a full match, return true.
11016 size_t indexAfterHost = index + host.Length();
11017 if (index == 0 && indexAfterHost == token.Length()) {
11018 return true;
11020 // If next character is '/', we need to check the path too.
11021 // We assume the path in the list means "/foo" + "*".
11022 if (token[indexAfterHost] == '/') {
11023 nsDependentCSubstring pathInList(
11024 token, indexAfterHost,
11025 static_cast<nsDependentCSubstring::size_type>(-1));
11026 nsAutoCString filePath;
11027 aURI->GetFilePath(filePath);
11028 ToLowerCase(filePath);
11029 if (StringBeginsWith(filePath, pathInList) &&
11030 (filePath.Length() == pathInList.Length() ||
11031 pathInList.EqualsLiteral("/") ||
11032 filePath[pathInList.Length() - 1] == '/' ||
11033 filePath[pathInList.Length() - 1] == '?' ||
11034 filePath[pathInList.Length() - 1] == '#')) {
11035 return true;
11039 int32_t startIndexOfCurrentLevel = host[0] == '*' ? 1 : 0;
11040 int32_t startIndexOfNextLevel =
11041 host.Find(".", startIndexOfCurrentLevel + 1);
11042 if (startIndexOfNextLevel <= 0) {
11043 break;
11045 host = "*"_ns + nsDependentCSubstring(host, startIndexOfNextLevel);
11049 return false;
11052 /* static */
11053 ScreenIntMargin nsContentUtils::GetWindowSafeAreaInsets(
11054 nsIScreen* aScreen, const ScreenIntMargin& aSafeAreaInsets,
11055 const LayoutDeviceIntRect& aWindowRect) {
11056 // This calculates safe area insets of window from screen rectangle, window
11057 // rectangle and safe area insets of screen.
11059 // +----------------------------------------+ <-- screen
11060 // | +-------------------------------+ <------- window
11061 // | | window's safe area inset top) | |
11062 // +--+-------------------------------+--+ |
11063 // | | | |<------ safe area rectangle of
11064 // | | | | | screen
11065 // +--+-------------------------------+--+ |
11066 // | |window's safe area inset bottom| |
11067 // | +-------------------------------+ |
11068 // +----------------------------------------+
11070 ScreenIntMargin windowSafeAreaInsets;
11072 if (windowSafeAreaInsets == aSafeAreaInsets) {
11073 // no safe area insets.
11074 return windowSafeAreaInsets;
11077 int32_t screenLeft, screenTop, screenWidth, screenHeight;
11078 nsresult rv =
11079 aScreen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
11080 if (NS_WARN_IF(NS_FAILED(rv))) {
11081 return windowSafeAreaInsets;
11084 const ScreenIntRect screenRect(screenLeft, screenTop, screenWidth,
11085 screenHeight);
11087 ScreenIntRect safeAreaRect = screenRect;
11088 safeAreaRect.Deflate(aSafeAreaInsets);
11090 ScreenIntRect windowRect = ViewAs<ScreenPixel>(
11091 aWindowRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
11093 // FIXME(bug 1754323): This can trigger because the screen rect is not
11094 // orientation-aware.
11095 // MOZ_ASSERT(screenRect.Contains(windowRect),
11096 // "Screen doesn't contain window rect? Something seems off");
11098 // window's rect of safe area
11099 safeAreaRect = safeAreaRect.Intersect(windowRect);
11101 windowSafeAreaInsets.top = safeAreaRect.y - aWindowRect.y;
11102 windowSafeAreaInsets.left = safeAreaRect.x - aWindowRect.x;
11103 windowSafeAreaInsets.right =
11104 aWindowRect.x + aWindowRect.width - (safeAreaRect.x + safeAreaRect.width);
11105 windowSafeAreaInsets.bottom = aWindowRect.y + aWindowRect.height -
11106 (safeAreaRect.y + safeAreaRect.height);
11108 windowSafeAreaInsets.EnsureAtLeast(ScreenIntMargin());
11109 // This shouldn't be needed, but it wallpapers orientation issues, see bug
11110 // 1754323.
11111 windowSafeAreaInsets.EnsureAtMost(aSafeAreaInsets);
11113 return windowSafeAreaInsets;
11116 /* static */
11117 nsContentUtils::SubresourceCacheValidationInfo
11118 nsContentUtils::GetSubresourceCacheValidationInfo(nsIRequest* aRequest,
11119 nsIURI* aURI) {
11120 SubresourceCacheValidationInfo info;
11121 if (nsCOMPtr<nsICacheInfoChannel> cache = do_QueryInterface(aRequest)) {
11122 uint32_t value = 0;
11123 if (NS_SUCCEEDED(cache->GetCacheTokenExpirationTime(&value))) {
11124 info.mExpirationTime.emplace(value);
11128 // Determine whether the cache entry must be revalidated when we try to use
11129 // it. Currently, only HTTP specifies this information...
11130 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest)) {
11131 Unused << httpChannel->IsNoStoreResponse(&info.mMustRevalidate);
11133 if (!info.mMustRevalidate) {
11134 Unused << httpChannel->IsNoCacheResponse(&info.mMustRevalidate);
11138 // data: URIs are safe to cache across documents under any circumstance, so we
11139 // special-case them here even though the channel itself doesn't have any
11140 // caching policy. Same for chrome:// uris.
11142 // TODO(emilio): Figure out which other schemes that don't have caching
11143 // policies are safe to cache. Blobs should be...
11144 const bool knownCacheable = [&] {
11145 if (!aURI) {
11146 return false;
11148 if (aURI->SchemeIs("data") || aURI->SchemeIs("moz-page-thumb") ||
11149 aURI->SchemeIs("moz-extension")) {
11150 return true;
11152 if (aURI->SchemeIs("chrome") || aURI->SchemeIs("resource")) {
11153 return !StaticPrefs::nglayout_debug_disable_xul_cache();
11155 return false;
11156 }();
11158 if (knownCacheable) {
11159 MOZ_ASSERT(!info.mExpirationTime);
11160 MOZ_ASSERT(!info.mMustRevalidate);
11161 info.mExpirationTime = Some(0); // 0 means "doesn't expire".
11164 return info;
11167 nsCString nsContentUtils::TruncatedURLForDisplay(nsIURI* aURL, size_t aMaxLen) {
11168 nsCString spec;
11169 if (aURL) {
11170 aURL->GetSpec(spec);
11171 spec.Truncate(std::min(aMaxLen, spec.Length()));
11173 return spec;
11176 /* static */
11177 nsresult nsContentUtils::AnonymizeId(nsAString& aId,
11178 const nsACString& aOriginKey,
11179 OriginFormat aFormat) {
11180 MOZ_ASSERT(NS_IsMainThread());
11182 nsresult rv;
11183 nsCString rawKey;
11184 if (aFormat == OriginFormat::Base64) {
11185 rv = Base64Decode(aOriginKey, rawKey);
11186 NS_ENSURE_SUCCESS(rv, rv);
11187 } else {
11188 rawKey = aOriginKey;
11191 HMAC hmac;
11192 rv = hmac.Begin(
11193 SEC_OID_SHA256,
11194 Span(reinterpret_cast<const uint8_t*>(rawKey.get()), rawKey.Length()));
11195 NS_ENSURE_SUCCESS(rv, rv);
11197 NS_ConvertUTF16toUTF8 id(aId);
11198 rv = hmac.Update(reinterpret_cast<const uint8_t*>(id.get()), id.Length());
11199 NS_ENSURE_SUCCESS(rv, rv);
11201 nsTArray<uint8_t> macBytes;
11202 rv = hmac.End(macBytes);
11203 NS_ENSURE_SUCCESS(rv, rv);
11205 nsCString macBase64;
11206 rv = Base64Encode(
11207 nsDependentCSubstring(reinterpret_cast<const char*>(macBytes.Elements()),
11208 macBytes.Length()),
11209 macBase64);
11210 NS_ENSURE_SUCCESS(rv, rv);
11212 CopyUTF8toUTF16(macBase64, aId);
11213 return NS_OK;
11216 /* static */
11217 bool nsContentUtils::ShouldHideObjectOrEmbedImageDocument() {
11218 return StaticPrefs::
11219 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup() &&
11220 StaticPrefs::
11221 browser_opaqueResponseBlocking_syntheticBrowsingContext_filter_AtStartup_DoNotUseDirectly();
11224 /* static */
11225 uint32_t nsContentUtils::ResolveObjectType(uint32_t aType) {
11226 if (!StaticPrefs::
11227 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup()) {
11228 return aType;
11231 if (aType != nsIObjectLoadingContent::TYPE_IMAGE) {
11232 return aType;
11235 return nsIObjectLoadingContent::TYPE_DOCUMENT;
11238 void nsContentUtils::RequestGeckoTaskBurst() {
11239 nsCOMPtr<nsIAppShell> appShell = do_GetService(NS_APPSHELL_CID);
11240 if (appShell) {
11241 appShell->GeckoTaskBurst();
11245 nsIContent* nsContentUtils::GetClosestLinkInFlatTree(nsIContent* aContent) {
11246 for (nsIContent* content = aContent; content;
11247 content = content->GetFlattenedTreeParent()) {
11248 if (nsContentUtils::IsDraggableLink(content)) {
11249 return content;
11252 return nullptr;
11255 namespace mozilla {
11256 std::ostream& operator<<(std::ostream& aOut,
11257 const PreventDefaultResult aPreventDefaultResult) {
11258 switch (aPreventDefaultResult) {
11259 case PreventDefaultResult::No:
11260 aOut << "unhandled";
11261 break;
11262 case PreventDefaultResult::ByContent:
11263 aOut << "handled-by-content";
11264 break;
11265 case PreventDefaultResult::ByChrome:
11266 aOut << "handled-by-chrome";
11267 break;
11269 return aOut;
11271 } // namespace mozilla