Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / dom / base / nsContentUtils.cpp
blobdf095e43783c43062f4445b91f05ba06a7a4e853
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/TextControlState.h"
126 #include "mozilla/TextEditor.h"
127 #include "mozilla/TextEvents.h"
128 #include "mozilla/UniquePtr.h"
129 #include "mozilla/Unused.h"
130 #include "mozilla/Variant.h"
131 #include "mozilla/ViewportUtils.h"
132 #include "mozilla/dom/AncestorIterator.h"
133 #include "mozilla/dom/AutoEntryScript.h"
134 #include "mozilla/dom/AutocompleteInfoBinding.h"
135 #include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
136 #include "mozilla/dom/BindingDeclarations.h"
137 #include "mozilla/dom/BindingUtils.h"
138 #include "mozilla/dom/BlobImpl.h"
139 #include "mozilla/dom/BlobURLProtocolHandler.h"
140 #include "mozilla/dom/BorrowedAttrInfo.h"
141 #include "mozilla/dom/BrowserBridgeParent.h"
142 #include "mozilla/dom/BrowserParent.h"
143 #include "mozilla/dom/BrowsingContext.h"
144 #include "mozilla/dom/BrowsingContextGroup.h"
145 #include "mozilla/dom/CallbackFunction.h"
146 #include "mozilla/dom/CallbackObject.h"
147 #include "mozilla/dom/ChromeMessageBroadcaster.h"
148 #include "mozilla/dom/ContentChild.h"
149 #include "mozilla/dom/ContentFrameMessageManager.h"
150 #include "mozilla/dom/ContentParent.h"
151 #include "mozilla/dom/CustomElementRegistry.h"
152 #include "mozilla/dom/CustomElementRegistryBinding.h"
153 #include "mozilla/dom/CustomElementTypes.h"
154 #include "mozilla/dom/DOMArena.h"
155 #include "mozilla/dom/DOMException.h"
156 #include "mozilla/dom/DOMExceptionBinding.h"
157 #include "mozilla/dom/DOMSecurityMonitor.h"
158 #include "mozilla/dom/DOMTypes.h"
159 #include "mozilla/dom/DataTransfer.h"
160 #include "mozilla/dom/DocGroup.h"
161 #include "mozilla/dom/Document.h"
162 #include "mozilla/dom/DocumentFragment.h"
163 #include "mozilla/dom/DocumentInlines.h"
164 #include "mozilla/dom/Element.h"
165 #include "mozilla/dom/ElementBinding.h"
166 #include "mozilla/dom/ElementInlines.h"
167 #include "mozilla/dom/Event.h"
168 #include "mozilla/dom/EventTarget.h"
169 #include "mozilla/dom/FileBlobImpl.h"
170 #include "mozilla/dom/FileSystemSecurity.h"
171 #include "mozilla/dom/FilteredNodeIterator.h"
172 #include "mozilla/dom/FormData.h"
173 #include "mozilla/dom/FragmentOrElement.h"
174 #include "mozilla/dom/FromParser.h"
175 #include "mozilla/dom/HTMLElement.h"
176 #include "mozilla/dom/HTMLFormElement.h"
177 #include "mozilla/dom/HTMLImageElement.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 "nsAtomHashKeys.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 nsIConsoleService* nsContentUtils::sConsoleService;
428 static nsTHashMap<RefPtr<nsAtom>, EventNameMapping>* sAtomEventTable;
429 static nsTHashMap<nsStringHashKey, EventNameMapping>* sStringEventTable;
430 static nsTArray<RefPtr<nsAtom>>* sUserDefinedEvents;
431 nsIStringBundleService* nsContentUtils::sStringBundleService;
433 static StaticRefPtr<nsIStringBundle>
434 sStringBundles[nsContentUtils::PropertiesFile_COUNT];
436 nsIContentPolicy* nsContentUtils::sContentPolicyService;
437 bool nsContentUtils::sTriedToGetContentPolicy = false;
438 StaticRefPtr<nsIBidiKeyboard> nsContentUtils::sBidiKeyboard;
439 uint32_t nsContentUtils::sScriptBlockerCount = 0;
440 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
441 AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners =
442 nullptr;
443 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
444 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
446 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
448 nsString* nsContentUtils::sShiftText = nullptr;
449 nsString* nsContentUtils::sControlText = nullptr;
450 nsString* nsContentUtils::sCommandOrWinText = nullptr;
451 nsString* nsContentUtils::sAltText = nullptr;
452 nsString* nsContentUtils::sModifierSeparator = nullptr;
454 bool nsContentUtils::sInitialized = false;
455 #ifndef RELEASE_OR_BETA
456 bool nsContentUtils::sBypassCSSOMOriginCheck = false;
457 #endif
459 nsCString* nsContentUtils::sJSScriptBytecodeMimeType = nullptr;
460 nsCString* nsContentUtils::sJSModuleBytecodeMimeType = nullptr;
462 nsContentUtils::UserInteractionObserver*
463 nsContentUtils::sUserInteractionObserver = nullptr;
465 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
466 nsParser* nsContentUtils::sXMLFragmentParser = nullptr;
467 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
468 bool nsContentUtils::sFragmentParsingActive = false;
470 bool nsContentUtils::sMayHaveFormCheckboxStateChangeListeners = false;
471 bool nsContentUtils::sMayHaveFormRadioStateChangeListeners = false;
473 mozilla::LazyLogModule nsContentUtils::gResistFingerprintingLog(
474 "nsResistFingerprinting");
475 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
477 int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
478 uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
480 template Maybe<int32_t> nsContentUtils::ComparePoints(
481 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary);
482 template Maybe<int32_t> nsContentUtils::ComparePoints(
483 const RangeBoundary& aFirstBoundary,
484 const RawRangeBoundary& aSecondBoundary);
485 template Maybe<int32_t> nsContentUtils::ComparePoints(
486 const RawRangeBoundary& aFirstBoundary,
487 const RangeBoundary& aSecondBoundary);
488 template Maybe<int32_t> nsContentUtils::ComparePoints(
489 const RawRangeBoundary& aFirstBoundary,
490 const RawRangeBoundary& aSecondBoundary);
492 template int32_t nsContentUtils::ComparePoints_Deprecated(
493 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
494 bool* aDisconnected);
495 template int32_t nsContentUtils::ComparePoints_Deprecated(
496 const RangeBoundary& aFirstBoundary,
497 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
498 template int32_t nsContentUtils::ComparePoints_Deprecated(
499 const RawRangeBoundary& aFirstBoundary,
500 const RangeBoundary& aSecondBoundary, bool* aDisconnected);
501 template int32_t nsContentUtils::ComparePoints_Deprecated(
502 const RawRangeBoundary& aFirstBoundary,
503 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
505 // Subset of
506 // http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
507 enum AutocompleteUnsupportedFieldName : uint8_t {
508 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
509 eAutocompleteUnsupportedFieldName_##name_,
510 #include "AutocompleteFieldList.h"
511 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
514 enum AutocompleteNoPersistFieldName : uint8_t {
515 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
516 eAutocompleteNoPersistFieldName_##name_,
517 #include "AutocompleteFieldList.h"
518 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
521 enum AutocompleteUnsupportFieldContactHint : uint8_t {
522 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
523 eAutocompleteUnsupportedFieldContactHint_##name_,
524 #include "AutocompleteFieldList.h"
525 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
528 enum AutocompleteFieldName : uint8_t {
529 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) eAutocompleteFieldName_##name_,
530 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
531 AUTOCOMPLETE_FIELD_NAME(name_, value_)
532 #include "AutocompleteFieldList.h"
533 #undef AUTOCOMPLETE_FIELD_NAME
534 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
537 enum AutocompleteFieldHint : uint8_t {
538 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) eAutocompleteFieldHint_##name_,
539 #include "AutocompleteFieldList.h"
540 #undef AUTOCOMPLETE_FIELD_HINT
543 enum AutocompleteFieldContactHint : uint8_t {
544 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
545 eAutocompleteFieldContactHint_##name_,
546 #include "AutocompleteFieldList.h"
547 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
550 enum AutocompleteCategory {
551 #define AUTOCOMPLETE_CATEGORY(name_, value_) eAutocompleteCategory_##name_,
552 #include "AutocompleteFieldList.h"
553 #undef AUTOCOMPLETE_CATEGORY
556 static const nsAttrValue::EnumTable kAutocompleteUnsupportedFieldNameTable[] = {
557 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
558 {value_, eAutocompleteUnsupportedFieldName_##name_},
559 #include "AutocompleteFieldList.h"
560 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
561 {nullptr, 0}};
563 static const nsAttrValue::EnumTable kAutocompleteNoPersistFieldNameTable[] = {
564 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
565 {value_, eAutocompleteNoPersistFieldName_##name_},
566 #include "AutocompleteFieldList.h"
567 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
568 {nullptr, 0}};
570 static const nsAttrValue::EnumTable
571 kAutocompleteUnsupportedContactFieldHintTable[] = {
572 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
573 {value_, eAutocompleteUnsupportedFieldContactHint_##name_},
574 #include "AutocompleteFieldList.h"
575 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
576 {nullptr, 0}};
578 static const nsAttrValue::EnumTable kAutocompleteFieldNameTable[] = {
579 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
580 {value_, eAutocompleteFieldName_##name_},
581 #include "AutocompleteFieldList.h"
582 #undef AUTOCOMPLETE_FIELD_NAME
583 {nullptr, 0}};
585 static const nsAttrValue::EnumTable kAutocompleteContactFieldNameTable[] = {
586 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
587 {value_, eAutocompleteFieldName_##name_},
588 #include "AutocompleteFieldList.h"
589 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
590 {nullptr, 0}};
592 static const nsAttrValue::EnumTable kAutocompleteFieldHintTable[] = {
593 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
594 {value_, eAutocompleteFieldHint_##name_},
595 #include "AutocompleteFieldList.h"
596 #undef AUTOCOMPLETE_FIELD_HINT
597 {nullptr, 0}};
599 static const nsAttrValue::EnumTable kAutocompleteContactFieldHintTable[] = {
600 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
601 {value_, eAutocompleteFieldContactHint_##name_},
602 #include "AutocompleteFieldList.h"
603 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
604 {nullptr, 0}};
606 namespace {
608 static PLDHashTable* sEventListenerManagersHash;
610 // A global hashtable to for keeping the arena alive for cross docGroup node
611 // adoption.
612 static nsRefPtrHashtable<nsPtrHashKey<const nsINode>, mozilla::dom::DOMArena>*
613 sDOMArenaHashtable;
615 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter {
616 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
618 ~DOMEventListenerManagersHashReporter() = default;
620 public:
621 NS_DECL_ISUPPORTS
623 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
624 nsISupports* aData, bool aAnonymize) override {
625 // We don't measure the |EventListenerManager| objects pointed to by the
626 // entries because those references are non-owning.
627 int64_t amount =
628 sEventListenerManagersHash
629 ? sEventListenerManagersHash->ShallowSizeOfIncludingThis(
630 MallocSizeOf)
631 : 0;
633 MOZ_COLLECT_REPORT(
634 "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
635 amount, "Memory used by the event listener manager's hash table.");
637 return NS_OK;
641 NS_IMPL_ISUPPORTS(DOMEventListenerManagersHashReporter, nsIMemoryReporter)
643 class EventListenerManagerMapEntry : public PLDHashEntryHdr {
644 public:
645 explicit EventListenerManagerMapEntry(const void* aKey) : mKey(aKey) {}
647 ~EventListenerManagerMapEntry() {
648 NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
651 protected: // declared protected to silence clang warnings
652 const void* mKey; // must be first, to look like PLDHashEntryStub
654 public:
655 RefPtr<EventListenerManager> mListenerManager;
658 static void EventListenerManagerHashInitEntry(PLDHashEntryHdr* entry,
659 const void* key) {
660 // Initialize the entry with placement new
661 new (entry) EventListenerManagerMapEntry(key);
664 static void EventListenerManagerHashClearEntry(PLDHashTable* table,
665 PLDHashEntryHdr* entry) {
666 EventListenerManagerMapEntry* lm =
667 static_cast<EventListenerManagerMapEntry*>(entry);
669 // Let the EventListenerManagerMapEntry clean itself up...
670 lm->~EventListenerManagerMapEntry();
673 class SameOriginCheckerImpl final : public nsIChannelEventSink,
674 public nsIInterfaceRequestor {
675 ~SameOriginCheckerImpl() = default;
677 NS_DECL_ISUPPORTS
678 NS_DECL_NSICHANNELEVENTSINK
679 NS_DECL_NSIINTERFACEREQUESTOR
682 } // namespace
684 void AutoSuppressEventHandling::SuppressDocument(Document* aDoc) {
685 // Note: Document::SuppressEventHandling will also automatically suppress
686 // event handling for any in-process sub-documents. However, since we need
687 // to deal with cases where remote BrowsingContexts may be interleaved
688 // with in-process ones, we still need to walk the entire tree ourselves.
689 // This may be slightly redundant in some cases, but since event handling
690 // suppressions maintain a count of current blockers, it does not cause
691 // any problems.
692 aDoc->SuppressEventHandling();
695 void AutoSuppressEventHandling::UnsuppressDocument(Document* aDoc) {
696 aDoc->UnsuppressEventHandlingAndFireEvents(true);
699 AutoSuppressEventHandling::~AutoSuppressEventHandling() {
700 UnsuppressDocuments();
703 void AutoSuppressEventHandlingAndSuspend::SuppressDocument(Document* aDoc) {
704 AutoSuppressEventHandling::SuppressDocument(aDoc);
705 if (nsCOMPtr<nsPIDOMWindowInner> win = aDoc->GetInnerWindow()) {
706 win->Suspend();
707 mWindows.AppendElement(win);
711 AutoSuppressEventHandlingAndSuspend::~AutoSuppressEventHandlingAndSuspend() {
712 for (const auto& win : mWindows) {
713 win->Resume();
718 * This class is used to determine whether or not the user is currently
719 * interacting with the browser. It listens to observer events to toggle the
720 * value of the sUserActive static.
722 * This class is an internal implementation detail.
723 * nsContentUtils::GetUserIsInteracting() should be used to access current
724 * user interaction status.
726 class nsContentUtils::UserInteractionObserver final
727 : public nsIObserver,
728 public BackgroundHangAnnotator {
729 public:
730 NS_DECL_ISUPPORTS
731 NS_DECL_NSIOBSERVER
733 void Init();
734 void Shutdown();
735 void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
737 static Atomic<bool> sUserActive;
739 private:
740 ~UserInteractionObserver() = default;
743 static constexpr nsLiteralCString kRfpPrefs[] = {
744 "privacy.resistFingerprinting"_ns,
745 "privacy.resistFingerprinting.pbmode"_ns,
746 "privacy.fingerprintingProtection"_ns,
747 "privacy.fingerprintingProtection.pbmode"_ns,
748 "privacy.fingerprintingProtection.overrides"_ns,
751 static void RecomputeResistFingerprintingAllDocs(const char*, void*) {
752 AutoTArray<RefPtr<BrowsingContextGroup>, 5> bcGroups;
753 BrowsingContextGroup::GetAllGroups(bcGroups);
754 for (auto& bcGroup : bcGroups) {
755 AutoTArray<DocGroup*, 5> docGroups;
756 bcGroup->GetDocGroups(docGroups);
757 for (auto* docGroup : docGroups) {
758 for (Document* doc : *docGroup) {
759 if (doc->RecomputeResistFingerprinting()) {
760 if (auto* pc = doc->GetPresContext()) {
761 pc->MediaFeatureValuesChanged(
762 {MediaFeatureChangeReason::PreferenceChange},
763 MediaFeatureChangePropagation::JustThisDocument);
771 // static
772 nsresult nsContentUtils::Init() {
773 if (sInitialized) {
774 NS_WARNING("Init() called twice");
776 return NS_OK;
779 nsHTMLTags::AddRefTable();
781 sXPConnect = nsXPConnect::XPConnect();
782 // We hold a strong ref to sXPConnect to ensure that it does not go away until
783 // nsLayoutStatics::Shutdown is happening. Otherwise ~nsXPConnect can be
784 // triggered by xpcModuleDtor late in shutdown and cause crashes due to
785 // various stuff already being torn down by then. Note that this means that
786 // we are effectively making sure that if we leak nsLayoutStatics then we also
787 // leak nsXPConnect.
788 NS_ADDREF(sXPConnect);
790 sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
791 if (!sSecurityManager) return NS_ERROR_FAILURE;
792 NS_ADDREF(sSecurityManager);
794 sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
795 MOZ_ASSERT(sSystemPrincipal);
797 RefPtr<NullPrincipal> nullPrincipal =
798 NullPrincipal::CreateWithoutOriginAttributes();
799 if (!nullPrincipal) {
800 return NS_ERROR_FAILURE;
803 nullPrincipal.forget(&sNullSubjectPrincipal);
805 if (!InitializeEventTable()) return NS_ERROR_FAILURE;
807 if (!sEventListenerManagersHash) {
808 static const PLDHashTableOps hash_table_ops = {
809 PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
810 PLDHashTable::MoveEntryStub, EventListenerManagerHashClearEntry,
811 EventListenerManagerHashInitEntry};
813 sEventListenerManagersHash =
814 new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
816 RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
819 sBlockedScriptRunners = new AutoTArray<nsCOMPtr<nsIRunnable>, 8>;
821 #ifndef RELEASE_OR_BETA
822 sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
823 #endif
825 Element::InitCCCallbacks();
827 RefPtr<nsRFPService> rfpService = nsRFPService::GetOrCreate();
828 MOZ_ASSERT(rfpService);
830 if (XRE_IsParentProcess()) {
831 AsyncPrecreateStringBundles();
834 RefPtr<UserInteractionObserver> uio = new UserInteractionObserver();
835 uio->Init();
836 uio.forget(&sUserInteractionObserver);
838 for (const auto& pref : kRfpPrefs) {
839 Preferences::RegisterCallback(RecomputeResistFingerprintingAllDocs, pref);
842 sInitialized = true;
844 return NS_OK;
847 bool nsContentUtils::InitJSBytecodeMimeType() {
848 MOZ_ASSERT(NS_IsMainThread());
849 MOZ_ASSERT(!sJSScriptBytecodeMimeType);
850 MOZ_ASSERT(!sJSModuleBytecodeMimeType);
852 JS::BuildIdCharVector jsBuildId;
853 if (!JS::GetScriptTranscodingBuildId(&jsBuildId)) {
854 return false;
857 nsDependentCSubstring jsBuildIdStr(jsBuildId.begin(), jsBuildId.length());
858 sJSScriptBytecodeMimeType =
859 new nsCString("javascript/moz-script-bytecode-"_ns + jsBuildIdStr);
860 sJSModuleBytecodeMimeType =
861 new nsCString("javascript/moz-module-bytecode-"_ns + jsBuildIdStr);
862 return true;
865 void nsContentUtils::GetShiftText(nsAString& text) {
866 if (!sShiftText) InitializeModifierStrings();
867 text.Assign(*sShiftText);
870 void nsContentUtils::GetControlText(nsAString& text) {
871 if (!sControlText) InitializeModifierStrings();
872 text.Assign(*sControlText);
875 void nsContentUtils::GetCommandOrWinText(nsAString& text) {
876 if (!sCommandOrWinText) {
877 InitializeModifierStrings();
879 text.Assign(*sCommandOrWinText);
882 void nsContentUtils::GetAltText(nsAString& text) {
883 if (!sAltText) InitializeModifierStrings();
884 text.Assign(*sAltText);
887 void nsContentUtils::GetModifierSeparatorText(nsAString& text) {
888 if (!sModifierSeparator) InitializeModifierStrings();
889 text.Assign(*sModifierSeparator);
892 void nsContentUtils::InitializeModifierStrings() {
893 // load the display strings for the keyboard accelerators
894 nsCOMPtr<nsIStringBundleService> bundleService =
895 mozilla::components::StringBundle::Service();
896 nsCOMPtr<nsIStringBundle> bundle;
897 DebugOnly<nsresult> rv = NS_OK;
898 if (bundleService) {
899 rv = bundleService->CreateBundle(
900 "chrome://global-platform/locale/platformKeys.properties",
901 getter_AddRefs(bundle));
904 NS_ASSERTION(
905 NS_SUCCEEDED(rv) && bundle,
906 "chrome://global/locale/platformKeys.properties could not be loaded");
907 nsAutoString shiftModifier;
908 nsAutoString commandOrWinModifier;
909 nsAutoString altModifier;
910 nsAutoString controlModifier;
911 nsAutoString modifierSeparator;
912 if (bundle) {
913 // macs use symbols for each modifier key, so fetch each from the bundle,
914 // which also covers i18n
915 bundle->GetStringFromName("VK_SHIFT", shiftModifier);
916 bundle->GetStringFromName("VK_COMMAND_OR_WIN", commandOrWinModifier);
917 bundle->GetStringFromName("VK_ALT", altModifier);
918 bundle->GetStringFromName("VK_CONTROL", controlModifier);
919 bundle->GetStringFromName("MODIFIER_SEPARATOR", modifierSeparator);
921 // if any of these don't exist, we get an empty string
922 sShiftText = new nsString(shiftModifier);
923 sCommandOrWinText = new nsString(commandOrWinModifier);
924 sAltText = new nsString(altModifier);
925 sControlText = new nsString(controlModifier);
926 sModifierSeparator = new nsString(modifierSeparator);
929 mozilla::EventClassID nsContentUtils::GetEventClassIDFromMessage(
930 EventMessage aEventMessage) {
931 switch (aEventMessage) {
932 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
933 case message_: \
934 return struct_;
935 #include "mozilla/EventNameList.h"
936 #undef MESSAGE_TO_EVENT
937 default:
938 MOZ_ASSERT_UNREACHABLE("Invalid event message?");
939 return eBasicEventClass;
943 bool nsContentUtils::IsExternalProtocol(nsIURI* aURI) {
944 bool doesNotReturnData = false;
945 nsresult rv = NS_URIChainHasFlags(
946 aURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &doesNotReturnData);
947 return NS_SUCCEEDED(rv) && doesNotReturnData;
950 /* static */
951 nsAtom* nsContentUtils::GetEventTypeFromMessage(EventMessage aEventMessage) {
952 switch (aEventMessage) {
953 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
954 case message_: \
955 return nsGkAtoms::on##name_;
956 #include "mozilla/EventNameList.h"
957 #undef MESSAGE_TO_EVENT
958 default:
959 return nullptr;
963 bool nsContentUtils::InitializeEventTable() {
964 NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
965 NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
967 static const EventNameMapping eventArray[] = {
968 #define EVENT(name_, _message, _type, _class) \
969 {nsGkAtoms::on##name_, _type, _message, _class},
970 #define WINDOW_ONLY_EVENT EVENT
971 #define DOCUMENT_ONLY_EVENT EVENT
972 #define NON_IDL_EVENT EVENT
973 #include "mozilla/EventNameList.h"
974 #undef WINDOW_ONLY_EVENT
975 #undef NON_IDL_EVENT
976 #undef EVENT
977 {nullptr}};
979 sAtomEventTable =
980 new nsTHashMap<RefPtr<nsAtom>, EventNameMapping>(ArrayLength(eventArray));
981 sStringEventTable = new nsTHashMap<nsStringHashKey, EventNameMapping>(
982 ArrayLength(eventArray));
983 sUserDefinedEvents = new nsTArray<RefPtr<nsAtom>>(64);
985 // Subtract one from the length because of the trailing null
986 for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) {
987 MOZ_ASSERT(!sAtomEventTable->Contains(eventArray[i].mAtom),
988 "Double-defining event name; fix your EventNameList.h");
989 sAtomEventTable->InsertOrUpdate(eventArray[i].mAtom, eventArray[i]);
990 sStringEventTable->InsertOrUpdate(
991 Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
992 eventArray[i]);
995 return true;
998 void nsContentUtils::InitializeTouchEventTable() {
999 static bool sEventTableInitialized = false;
1000 if (!sEventTableInitialized && sAtomEventTable && sStringEventTable) {
1001 sEventTableInitialized = true;
1002 static const EventNameMapping touchEventArray[] = {
1003 #define EVENT(name_, _message, _type, _class)
1004 #define TOUCH_EVENT(name_, _message, _type, _class) \
1005 {nsGkAtoms::on##name_, _type, _message, _class},
1006 #include "mozilla/EventNameList.h"
1007 #undef TOUCH_EVENT
1008 #undef EVENT
1009 {nullptr}};
1010 // Subtract one from the length because of the trailing null
1011 for (uint32_t i = 0; i < ArrayLength(touchEventArray) - 1; ++i) {
1012 sAtomEventTable->InsertOrUpdate(touchEventArray[i].mAtom,
1013 touchEventArray[i]);
1014 sStringEventTable->InsertOrUpdate(
1015 Substring(nsDependentAtomString(touchEventArray[i].mAtom), 2),
1016 touchEventArray[i]);
1021 static bool Is8bit(const nsAString& aString) {
1022 static const char16_t EIGHT_BIT = char16_t(~0x00FF);
1024 for (nsAString::const_char_iterator start = aString.BeginReading(),
1025 end = aString.EndReading();
1026 start != end; ++start) {
1027 if (*start & EIGHT_BIT) {
1028 return false;
1032 return true;
1035 nsresult nsContentUtils::Btoa(const nsAString& aBinaryData,
1036 nsAString& aAsciiBase64String) {
1037 if (!Is8bit(aBinaryData)) {
1038 aAsciiBase64String.Truncate();
1039 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1042 return Base64Encode(aBinaryData, aAsciiBase64String);
1045 nsresult nsContentUtils::Atob(const nsAString& aAsciiBase64String,
1046 nsAString& aBinaryData) {
1047 if (!Is8bit(aAsciiBase64String)) {
1048 aBinaryData.Truncate();
1049 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1052 const char16_t* start = aAsciiBase64String.BeginReading();
1053 const char16_t* cur = start;
1054 const char16_t* end = aAsciiBase64String.EndReading();
1055 bool hasWhitespace = false;
1057 while (cur < end) {
1058 if (nsContentUtils::IsHTMLWhitespace(*cur)) {
1059 hasWhitespace = true;
1060 break;
1062 cur++;
1065 nsresult rv;
1067 if (hasWhitespace) {
1068 nsString trimmedString;
1070 if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible)) {
1071 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1074 trimmedString.Append(start, cur - start);
1076 while (cur < end) {
1077 if (!nsContentUtils::IsHTMLWhitespace(*cur)) {
1078 trimmedString.Append(*cur);
1080 cur++;
1082 rv = Base64Decode(trimmedString, aBinaryData);
1083 } else {
1084 rv = Base64Decode(aAsciiBase64String, aBinaryData);
1087 if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
1088 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1090 return rv;
1093 bool nsContentUtils::IsAutocompleteEnabled(
1094 mozilla::dom::HTMLInputElement* aInput) {
1095 MOZ_ASSERT(aInput, "aInput should not be null!");
1097 nsAutoString autocomplete;
1098 aInput->GetAutocomplete(autocomplete);
1100 if (autocomplete.IsEmpty()) {
1101 auto* form = aInput->GetForm();
1102 if (!form) {
1103 return true;
1106 form->GetAutocomplete(autocomplete);
1109 return !autocomplete.EqualsLiteral("off");
1112 nsContentUtils::AutocompleteAttrState
1113 nsContentUtils::SerializeAutocompleteAttribute(
1114 const nsAttrValue* aAttr, nsAString& aResult,
1115 AutocompleteAttrState aCachedState) {
1116 if (!aAttr ||
1117 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1118 return aCachedState;
1121 if (aCachedState == nsContentUtils::eAutocompleteAttrState_Valid) {
1122 uint32_t atomCount = aAttr->GetAtomCount();
1123 for (uint32_t i = 0; i < atomCount; i++) {
1124 if (i != 0) {
1125 aResult.Append(' ');
1127 aResult.Append(nsDependentAtomString(aAttr->AtomAt(i)));
1129 nsContentUtils::ASCIIToLower(aResult);
1130 return aCachedState;
1133 aResult.Truncate();
1135 mozilla::dom::AutocompleteInfo info;
1136 AutocompleteAttrState state =
1137 InternalSerializeAutocompleteAttribute(aAttr, info);
1138 if (state == eAutocompleteAttrState_Valid) {
1139 // Concatenate the info fields.
1140 aResult = info.mSection;
1142 if (!info.mAddressType.IsEmpty()) {
1143 if (!aResult.IsEmpty()) {
1144 aResult += ' ';
1146 aResult += info.mAddressType;
1149 if (!info.mContactType.IsEmpty()) {
1150 if (!aResult.IsEmpty()) {
1151 aResult += ' ';
1153 aResult += info.mContactType;
1156 if (!info.mFieldName.IsEmpty()) {
1157 if (!aResult.IsEmpty()) {
1158 aResult += ' ';
1160 aResult += info.mFieldName;
1164 return state;
1167 nsContentUtils::AutocompleteAttrState
1168 nsContentUtils::SerializeAutocompleteAttribute(
1169 const nsAttrValue* aAttr, mozilla::dom::AutocompleteInfo& aInfo,
1170 AutocompleteAttrState aCachedState, bool aGrantAllValidValue) {
1171 if (!aAttr ||
1172 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1173 return aCachedState;
1176 return InternalSerializeAutocompleteAttribute(aAttr, aInfo,
1177 aGrantAllValidValue);
1181 * Helper to validate the @autocomplete tokens.
1183 * @return {AutocompleteAttrState} The state of the attribute (invalid/valid).
1185 nsContentUtils::AutocompleteAttrState
1186 nsContentUtils::InternalSerializeAutocompleteAttribute(
1187 const nsAttrValue* aAttrVal, mozilla::dom::AutocompleteInfo& aInfo,
1188 bool aGrantAllValidValue) {
1189 // No autocomplete attribute so we are done
1190 if (!aAttrVal) {
1191 return eAutocompleteAttrState_Invalid;
1194 uint32_t numTokens = aAttrVal->GetAtomCount();
1195 if (!numTokens) {
1196 return eAutocompleteAttrState_Invalid;
1199 uint32_t index = numTokens - 1;
1200 nsString tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1201 AutocompleteCategory category;
1202 nsAttrValue enumValue;
1204 bool unsupported = false;
1205 if (!aGrantAllValidValue) {
1206 unsupported = enumValue.ParseEnumValue(
1207 tokenString, kAutocompleteUnsupportedFieldNameTable, false);
1208 if (unsupported) {
1209 return eAutocompleteAttrState_Invalid;
1213 nsAutoString str;
1214 bool result =
1215 enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
1216 if (result) {
1217 // Off/Automatic/Normal categories.
1218 if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
1219 enumValue.Equals(u"on"_ns, eIgnoreCase)) {
1220 if (numTokens > 1) {
1221 return eAutocompleteAttrState_Invalid;
1223 enumValue.ToString(str);
1224 ASCIIToLower(str);
1225 aInfo.mFieldName.Assign(str);
1226 aInfo.mCanAutomaticallyPersist =
1227 !enumValue.Equals(u"off"_ns, eIgnoreCase);
1228 return eAutocompleteAttrState_Valid;
1231 // Only allow on/off if form autofill @autocomplete values aren't enabled
1232 // and it doesn't grant all valid values.
1233 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1234 !aGrantAllValidValue) {
1235 return eAutocompleteAttrState_Invalid;
1238 // Normal category
1239 if (numTokens > 3) {
1240 return eAutocompleteAttrState_Invalid;
1242 category = eAutocompleteCategory_NORMAL;
1243 } else { // Check if the last token is of the contact category instead.
1244 // Only allow on/off if form autofill @autocomplete values aren't enabled
1245 // and it doesn't grant all valid values.
1246 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1247 !aGrantAllValidValue) {
1248 return eAutocompleteAttrState_Invalid;
1251 result = enumValue.ParseEnumValue(
1252 tokenString, kAutocompleteContactFieldNameTable, false);
1253 if (!result || numTokens > 4) {
1254 return eAutocompleteAttrState_Invalid;
1257 category = eAutocompleteCategory_CONTACT;
1260 enumValue.ToString(str);
1261 ASCIIToLower(str);
1262 aInfo.mFieldName.Assign(str);
1264 aInfo.mCanAutomaticallyPersist = !enumValue.ParseEnumValue(
1265 tokenString, kAutocompleteNoPersistFieldNameTable, false);
1267 // We are done if this was the only token.
1268 if (numTokens == 1) {
1269 return eAutocompleteAttrState_Valid;
1272 --index;
1273 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1275 if (category == eAutocompleteCategory_CONTACT) {
1276 if (!aGrantAllValidValue) {
1277 unsupported = enumValue.ParseEnumValue(
1278 tokenString, kAutocompleteUnsupportedContactFieldHintTable, false);
1279 if (unsupported) {
1280 return eAutocompleteAttrState_Invalid;
1284 nsAttrValue contactFieldHint;
1285 result = contactFieldHint.ParseEnumValue(
1286 tokenString, kAutocompleteContactFieldHintTable, false);
1287 if (result) {
1288 nsAutoString contactFieldHintString;
1289 contactFieldHint.ToString(contactFieldHintString);
1290 ASCIIToLower(contactFieldHintString);
1291 aInfo.mContactType.Assign(contactFieldHintString);
1292 if (index == 0) {
1293 return eAutocompleteAttrState_Valid;
1295 --index;
1296 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1300 // Check for billing/shipping tokens
1301 nsAttrValue fieldHint;
1302 if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable,
1303 false)) {
1304 nsString fieldHintString;
1305 fieldHint.ToString(fieldHintString);
1306 ASCIIToLower(fieldHintString);
1307 aInfo.mAddressType.Assign(fieldHintString);
1308 if (index == 0) {
1309 return eAutocompleteAttrState_Valid;
1311 --index;
1312 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1315 // Check for section-* token
1316 const nsDependentSubstring& section = Substring(tokenString, 0, 8);
1317 if (section.LowerCaseEqualsASCII("section-")) {
1318 ASCIIToLower(tokenString);
1319 aInfo.mSection.Assign(tokenString);
1320 if (index == 0) {
1321 return eAutocompleteAttrState_Valid;
1325 // Clear the fields as the autocomplete attribute is invalid.
1326 aInfo.mSection.Truncate();
1327 aInfo.mAddressType.Truncate();
1328 aInfo.mContactType.Truncate();
1329 aInfo.mFieldName.Truncate();
1331 return eAutocompleteAttrState_Invalid;
1334 // Parse an integer according to HTML spec
1335 template <class CharT>
1336 int32_t nsContentUtils::ParseHTMLIntegerImpl(
1337 const CharT* aStart, const CharT* aEnd,
1338 ParseHTMLIntegerResultFlags* aResult) {
1339 int result = eParseHTMLInteger_NoFlags;
1341 const CharT* iter = aStart;
1343 while (iter != aEnd && nsContentUtils::IsHTMLWhitespace(*iter)) {
1344 result |= eParseHTMLInteger_NonStandard;
1345 ++iter;
1348 if (iter == aEnd) {
1349 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1350 *aResult = (ParseHTMLIntegerResultFlags)result;
1351 return 0;
1354 int sign = 1;
1355 if (*iter == CharT('-')) {
1356 sign = -1;
1357 result |= eParseHTMLInteger_Negative;
1358 ++iter;
1359 } else if (*iter == CharT('+')) {
1360 result |= eParseHTMLInteger_NonStandard;
1361 ++iter;
1364 bool foundValue = false;
1365 CheckedInt32 value = 0;
1367 // Check for leading zeros first.
1368 uint64_t leadingZeros = 0;
1369 while (iter != aEnd) {
1370 if (*iter != CharT('0')) {
1371 break;
1374 ++leadingZeros;
1375 foundValue = true;
1376 ++iter;
1379 while (iter != aEnd) {
1380 if (*iter >= CharT('0') && *iter <= CharT('9')) {
1381 value = (value * 10) + (*iter - CharT('0')) * sign;
1382 ++iter;
1383 if (!value.isValid()) {
1384 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
1385 break;
1387 foundValue = true;
1388 } else {
1389 break;
1393 if (!foundValue) {
1394 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1397 if (value.isValid() &&
1398 ((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
1399 (sign == -1 && value == 0))) {
1400 result |= eParseHTMLInteger_NonStandard;
1403 if (iter != aEnd) {
1404 result |= eParseHTMLInteger_DidNotConsumeAllInput;
1407 *aResult = (ParseHTMLIntegerResultFlags)result;
1408 return value.isValid() ? value.value() : 0;
1411 // Parse an integer according to HTML spec
1412 int32_t nsContentUtils::ParseHTMLInteger(const char16_t* aStart,
1413 const char16_t* aEnd,
1414 ParseHTMLIntegerResultFlags* aResult) {
1415 return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
1418 int32_t nsContentUtils::ParseHTMLInteger(const char* aStart, const char* aEnd,
1419 ParseHTMLIntegerResultFlags* aResult) {
1420 return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
1423 #define SKIP_WHITESPACE(iter, end_iter, end_res) \
1424 while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
1425 ++(iter); \
1427 if ((iter) == (end_iter)) { \
1428 return (end_res); \
1431 #define SKIP_ATTR_NAME(iter, end_iter) \
1432 while ((iter) != (end_iter) && !nsCRT::IsAsciiSpace(*(iter)) && \
1433 *(iter) != '=') { \
1434 ++(iter); \
1437 bool nsContentUtils::GetPseudoAttributeValue(const nsString& aSource,
1438 nsAtom* aName, nsAString& aValue) {
1439 aValue.Truncate();
1441 const char16_t* start = aSource.get();
1442 const char16_t* end = start + aSource.Length();
1443 const char16_t* iter;
1445 while (start != end) {
1446 SKIP_WHITESPACE(start, end, false)
1447 iter = start;
1448 SKIP_ATTR_NAME(iter, end)
1450 if (start == iter) {
1451 return false;
1454 // Remember the attr name.
1455 const nsDependentSubstring& attrName = Substring(start, iter);
1457 // Now check whether this is a valid name="value" pair.
1458 start = iter;
1459 SKIP_WHITESPACE(start, end, false)
1460 if (*start != '=') {
1461 // No '=', so this is not a name="value" pair. We don't know
1462 // what it is, and we have no way to handle it.
1463 return false;
1466 // Have to skip the value.
1467 ++start;
1468 SKIP_WHITESPACE(start, end, false)
1469 char16_t q = *start;
1470 if (q != kQuote && q != kApostrophe) {
1471 // Not a valid quoted value, so bail.
1472 return false;
1475 ++start; // Point to the first char of the value.
1476 iter = start;
1478 while (iter != end && *iter != q) {
1479 ++iter;
1482 if (iter == end) {
1483 // Oops, unterminated quoted string.
1484 return false;
1487 // At this point attrName holds the name of the "attribute" and
1488 // the value is between start and iter.
1490 if (aName->Equals(attrName)) {
1491 // We'll accumulate as many characters as possible (until we hit either
1492 // the end of the string or the beginning of an entity). Chunks will be
1493 // delimited by start and chunkEnd.
1494 const char16_t* chunkEnd = start;
1495 while (chunkEnd != iter) {
1496 if (*chunkEnd == kLessThan) {
1497 aValue.Truncate();
1499 return false;
1502 if (*chunkEnd == kAmpersand) {
1503 aValue.Append(start, chunkEnd - start);
1505 const char16_t* afterEntity = nullptr;
1506 char16_t result[2];
1507 uint32_t count = MOZ_XMLTranslateEntity(
1508 reinterpret_cast<const char*>(chunkEnd),
1509 reinterpret_cast<const char*>(iter),
1510 reinterpret_cast<const char**>(&afterEntity), result);
1511 if (count == 0) {
1512 aValue.Truncate();
1514 return false;
1517 aValue.Append(result, count);
1519 // Advance to after the entity and begin a new chunk.
1520 start = chunkEnd = afterEntity;
1521 } else {
1522 ++chunkEnd;
1526 // Append remainder.
1527 aValue.Append(start, iter - start);
1529 return true;
1532 // Resume scanning after the end of the attribute value (past the quote
1533 // char).
1534 start = iter + 1;
1537 return false;
1540 bool nsContentUtils::IsJavaScriptLanguage(const nsString& aName) {
1541 // Create MIME type as "text/" + given input
1542 nsAutoString mimeType(u"text/");
1543 mimeType.Append(aName);
1545 return IsJavascriptMIMEType(mimeType);
1548 void nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType,
1549 nsString& aParams) {
1550 aType.Truncate();
1551 aParams.Truncate();
1552 int32_t semiIndex = aValue.FindChar(char16_t(';'));
1553 if (-1 != semiIndex) {
1554 aType = Substring(aValue, 0, semiIndex);
1555 aParams =
1556 Substring(aValue, semiIndex + 1, aValue.Length() - (semiIndex + 1));
1557 aParams.StripWhitespace();
1558 } else {
1559 aType = aValue;
1561 aType.StripWhitespace();
1565 * A helper function that parses a sandbox attribute (of an <iframe> or a CSP
1566 * directive) and converts it to the set of flags used internally.
1568 * @param aSandboxAttr the sandbox attribute
1569 * @return the set of flags (SANDBOXED_NONE if aSandboxAttr is
1570 * null)
1572 uint32_t nsContentUtils::ParseSandboxAttributeToFlags(
1573 const nsAttrValue* aSandboxAttr) {
1574 if (!aSandboxAttr) {
1575 return SANDBOXED_NONE;
1578 uint32_t out = SANDBOX_ALL_FLAGS;
1580 #define SANDBOX_KEYWORD(string, atom, flags) \
1581 if (aSandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { \
1582 out &= ~(flags); \
1584 #include "IframeSandboxKeywordList.h"
1585 #undef SANDBOX_KEYWORD
1587 return out;
1591 * A helper function that checks if a string matches a valid sandbox flag.
1593 * @param aFlag the potential sandbox flag.
1594 * @return true if the flag is a sandbox flag.
1596 bool nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag) {
1597 #define SANDBOX_KEYWORD(string, atom, flags) \
1598 if (EqualsIgnoreASCIICase(nsDependentAtomString(nsGkAtoms::atom), aFlag)) { \
1599 return true; \
1601 #include "IframeSandboxKeywordList.h"
1602 #undef SANDBOX_KEYWORD
1603 return false;
1607 * A helper function that returns a string attribute corresponding to the
1608 * sandbox flags.
1610 * @param aFlags the sandbox flags
1611 * @param aString the attribute corresponding to the flags (null if aFlags
1612 * is zero)
1614 void nsContentUtils::SandboxFlagsToString(uint32_t aFlags, nsAString& aString) {
1615 if (!aFlags) {
1616 SetDOMStringToNull(aString);
1617 return;
1620 aString.Truncate();
1622 #define SANDBOX_KEYWORD(string, atom, flags) \
1623 if (!(aFlags & (flags))) { \
1624 if (!aString.IsEmpty()) { \
1625 aString.AppendLiteral(u" "); \
1627 aString.Append(nsDependentAtomString(nsGkAtoms::atom)); \
1629 #include "IframeSandboxKeywordList.h"
1630 #undef SANDBOX_KEYWORD
1633 nsIBidiKeyboard* nsContentUtils::GetBidiKeyboard() {
1634 if (!sBidiKeyboard) {
1635 sBidiKeyboard = nsIWidget::CreateBidiKeyboard();
1637 return sBidiKeyboard;
1641 * This is used to determine whether a character is in one of the classes
1642 * which CSS says should be part of the first-letter. Currently, that is
1643 * all punctuation classes (P*). Note that this is a change from CSS2
1644 * which excluded Pc and Pd.
1646 * https://www.w3.org/TR/css-pseudo-4/#first-letter-pseudo
1647 * "Punctuation (i.e, characters that belong to the Punctuation (P*) Unicode
1648 * general category [UAX44]) [...]"
1651 // static
1652 bool nsContentUtils::IsFirstLetterPunctuation(uint32_t aChar) {
1653 switch (mozilla::unicode::GetGeneralCategory(aChar)) {
1654 case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
1655 case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION: /* Pd */
1656 case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION: /* Pe */
1657 case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION: /* Pf */
1658 case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
1659 case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION: /* Po */
1660 case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION: /* Ps */
1661 return true;
1662 default:
1663 return false;
1667 // static
1668 bool nsContentUtils::IsAlphanumeric(uint32_t aChar) {
1669 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1671 return (cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber);
1674 // static
1675 bool nsContentUtils::IsAlphanumericOrSymbol(uint32_t aChar) {
1676 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1678 return cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber ||
1679 cat == nsUGenCategory::kSymbol;
1682 /* static */
1683 bool nsContentUtils::IsHTMLWhitespace(char16_t aChar) {
1684 return aChar == char16_t(0x0009) || aChar == char16_t(0x000A) ||
1685 aChar == char16_t(0x000C) || aChar == char16_t(0x000D) ||
1686 aChar == char16_t(0x0020);
1689 /* static */
1690 bool nsContentUtils::IsHTMLWhitespaceOrNBSP(char16_t aChar) {
1691 return IsHTMLWhitespace(aChar) || aChar == char16_t(0xA0);
1694 /* static */
1695 bool nsContentUtils::IsHTMLBlockLevelElement(nsIContent* aContent) {
1696 return aContent->IsAnyOfHTMLElements(
1697 nsGkAtoms::address, nsGkAtoms::article, nsGkAtoms::aside,
1698 nsGkAtoms::blockquote, nsGkAtoms::center, nsGkAtoms::dir, nsGkAtoms::div,
1699 nsGkAtoms::dl, // XXX why not dt and dd?
1700 nsGkAtoms::fieldset,
1701 nsGkAtoms::figure, // XXX shouldn't figcaption be on this list
1702 nsGkAtoms::footer, nsGkAtoms::form, nsGkAtoms::h1, nsGkAtoms::h2,
1703 nsGkAtoms::h3, nsGkAtoms::h4, nsGkAtoms::h5, nsGkAtoms::h6,
1704 nsGkAtoms::header, nsGkAtoms::hgroup, nsGkAtoms::hr, nsGkAtoms::li,
1705 nsGkAtoms::listing, nsGkAtoms::menu, nsGkAtoms::nav, nsGkAtoms::ol,
1706 nsGkAtoms::p, nsGkAtoms::pre, nsGkAtoms::section, nsGkAtoms::table,
1707 nsGkAtoms::ul, nsGkAtoms::xmp);
1710 /* static */
1711 bool nsContentUtils::ParseIntMarginValue(const nsAString& aString,
1712 nsIntMargin& result) {
1713 nsAutoString marginStr(aString);
1714 marginStr.CompressWhitespace(true, true);
1715 if (marginStr.IsEmpty()) {
1716 return false;
1719 int32_t start = 0, end = 0;
1720 for (int count = 0; count < 4; count++) {
1721 if ((uint32_t)end >= marginStr.Length()) return false;
1723 // top, right, bottom, left
1724 if (count < 3)
1725 end = Substring(marginStr, start).FindChar(',');
1726 else
1727 end = Substring(marginStr, start).Length();
1729 if (end <= 0) return false;
1731 nsresult ec;
1732 int32_t val = nsString(Substring(marginStr, start, end)).ToInteger(&ec);
1733 if (NS_FAILED(ec)) return false;
1735 switch (count) {
1736 case 0:
1737 result.top = val;
1738 break;
1739 case 1:
1740 result.right = val;
1741 break;
1742 case 2:
1743 result.bottom = val;
1744 break;
1745 case 3:
1746 result.left = val;
1747 break;
1749 start += end + 1;
1751 return true;
1754 // static
1755 int32_t nsContentUtils::ParseLegacyFontSize(const nsAString& aValue) {
1756 nsAString::const_iterator iter, end;
1757 aValue.BeginReading(iter);
1758 aValue.EndReading(end);
1760 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1761 ++iter;
1764 if (iter == end) {
1765 return 0;
1768 bool relative = false;
1769 bool negate = false;
1770 if (*iter == char16_t('-')) {
1771 relative = true;
1772 negate = true;
1773 ++iter;
1774 } else if (*iter == char16_t('+')) {
1775 relative = true;
1776 ++iter;
1779 if (iter == end || *iter < char16_t('0') || *iter > char16_t('9')) {
1780 return 0;
1783 // We don't have to worry about overflow, since we can bail out as soon as
1784 // we're bigger than 7.
1785 int32_t value = 0;
1786 while (iter != end && *iter >= char16_t('0') && *iter <= char16_t('9')) {
1787 value = 10 * value + (*iter - char16_t('0'));
1788 if (value >= 7) {
1789 break;
1791 ++iter;
1794 if (relative) {
1795 if (negate) {
1796 value = 3 - value;
1797 } else {
1798 value = 3 + value;
1802 return clamped(value, 1, 7);
1805 /* static */
1806 void nsContentUtils::GetOfflineAppManifest(Document* aDocument, nsIURI** aURI) {
1807 MOZ_ASSERT(NS_IsMainThread());
1808 MOZ_ASSERT(aDocument);
1809 *aURI = nullptr;
1811 if (aDocument->GetController().isSome()) {
1812 return;
1815 Element* docElement = aDocument->GetRootElement();
1816 if (!docElement) {
1817 return;
1820 nsAutoString manifestSpec;
1821 docElement->GetAttr(nsGkAtoms::manifest, manifestSpec);
1823 // Manifest URIs can't have fragment identifiers.
1824 if (manifestSpec.IsEmpty() || manifestSpec.Contains('#')) {
1825 return;
1828 nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec, aDocument,
1829 aDocument->GetDocBaseURI());
1832 /* static */
1833 bool nsContentUtils::OfflineAppAllowed(nsIURI* aURI) { return false; }
1835 /* static */
1836 bool nsContentUtils::OfflineAppAllowed(nsIPrincipal* aPrincipal) {
1837 return false;
1839 // Static
1840 bool nsContentUtils::IsErrorPage(nsIURI* aURI) {
1841 if (!aURI) {
1842 return false;
1845 if (!aURI->SchemeIs("about")) {
1846 return false;
1849 nsAutoCString name;
1850 nsresult rv = NS_GetAboutModuleName(aURI, name);
1851 NS_ENSURE_SUCCESS(rv, false);
1853 return name.EqualsLiteral("certerror") || name.EqualsLiteral("neterror") ||
1854 name.EqualsLiteral("blocked");
1857 // static
1858 void nsContentUtils::Shutdown() {
1859 sInitialized = false;
1861 nsHTMLTags::ReleaseTable();
1863 NS_IF_RELEASE(sContentPolicyService);
1864 sTriedToGetContentPolicy = false;
1865 for (StaticRefPtr<nsIStringBundle>& bundle : sStringBundles) {
1866 bundle = nullptr;
1869 NS_IF_RELEASE(sStringBundleService);
1870 NS_IF_RELEASE(sConsoleService);
1871 NS_IF_RELEASE(sXPConnect);
1872 NS_IF_RELEASE(sSecurityManager);
1873 NS_IF_RELEASE(sSystemPrincipal);
1874 NS_IF_RELEASE(sNullSubjectPrincipal);
1876 sBidiKeyboard = nullptr;
1878 delete sAtomEventTable;
1879 sAtomEventTable = nullptr;
1880 delete sStringEventTable;
1881 sStringEventTable = nullptr;
1882 delete sUserDefinedEvents;
1883 sUserDefinedEvents = nullptr;
1885 if (sEventListenerManagersHash) {
1886 NS_ASSERTION(sEventListenerManagersHash->EntryCount() == 0,
1887 "Event listener manager hash not empty at shutdown!");
1889 // See comment above.
1891 // However, we have to handle this table differently. If it still
1892 // has entries, we want to leak it too, so that we can keep it alive
1893 // in case any elements are destroyed. Because if they are, we need
1894 // their event listener managers to be destroyed too, or otherwise
1895 // it could leave dangling references in DOMClassInfo's preserved
1896 // wrapper table.
1898 if (sEventListenerManagersHash->EntryCount() == 0) {
1899 delete sEventListenerManagersHash;
1900 sEventListenerManagersHash = nullptr;
1904 if (sDOMArenaHashtable) {
1905 MOZ_ASSERT(sDOMArenaHashtable->Count() == 0);
1906 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
1907 delete sDOMArenaHashtable;
1908 sDOMArenaHashtable = nullptr;
1911 NS_ASSERTION(!sBlockedScriptRunners || sBlockedScriptRunners->Length() == 0,
1912 "How'd this happen?");
1913 delete sBlockedScriptRunners;
1914 sBlockedScriptRunners = nullptr;
1916 delete sShiftText;
1917 sShiftText = nullptr;
1918 delete sControlText;
1919 sControlText = nullptr;
1920 delete sCommandOrWinText;
1921 sCommandOrWinText = nullptr;
1922 delete sAltText;
1923 sAltText = nullptr;
1924 delete sModifierSeparator;
1925 sModifierSeparator = nullptr;
1927 delete sJSScriptBytecodeMimeType;
1928 sJSScriptBytecodeMimeType = nullptr;
1930 delete sJSModuleBytecodeMimeType;
1931 sJSModuleBytecodeMimeType = nullptr;
1933 NS_IF_RELEASE(sSameOriginChecker);
1935 if (sUserInteractionObserver) {
1936 sUserInteractionObserver->Shutdown();
1937 NS_RELEASE(sUserInteractionObserver);
1940 for (const auto& pref : kRfpPrefs) {
1941 Preferences::UnregisterCallback(RecomputeResistFingerprintingAllDocs, pref);
1944 TextControlState::Shutdown();
1948 * Checks whether two nodes come from the same origin. aTrustedNode is
1949 * considered 'safe' in that a user can operate on it.
1951 // static
1952 nsresult nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
1953 const nsINode* unTrustedNode) {
1954 MOZ_ASSERT(aTrustedNode);
1955 MOZ_ASSERT(unTrustedNode);
1958 * Get hold of each node's principal
1961 nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
1962 nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
1964 if (trustedPrincipal == unTrustedPrincipal) {
1965 return NS_OK;
1968 bool equal;
1969 // XXXbz should we actually have a Subsumes() check here instead? Or perhaps
1970 // a separate method for that, with callers using one or the other?
1971 if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
1972 !equal) {
1973 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
1976 return NS_OK;
1979 // static
1980 bool nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
1981 nsIPrincipal* aPrincipal) {
1982 bool subsumes;
1983 nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
1984 NS_ENSURE_SUCCESS(rv, false);
1986 if (subsumes) {
1987 return true;
1990 // The subject doesn't subsume aPrincipal. Allow access only if the subject
1991 // is chrome.
1992 return IsCallerChrome();
1995 // static
1996 bool nsContentUtils::CanCallerAccess(const nsINode* aNode) {
1997 nsIPrincipal* subject = SubjectPrincipal();
1998 if (subject->IsSystemPrincipal()) {
1999 return true;
2002 if (aNode->ChromeOnlyAccess()) {
2003 return false;
2006 return CanCallerAccess(subject, aNode->NodePrincipal());
2009 // static
2010 bool nsContentUtils::CanCallerAccess(nsPIDOMWindowInner* aWindow) {
2011 nsCOMPtr<nsIScriptObjectPrincipal> scriptObject = do_QueryInterface(aWindow);
2012 NS_ENSURE_TRUE(scriptObject, false);
2014 return CanCallerAccess(SubjectPrincipal(), scriptObject->GetPrincipal());
2017 // static
2018 bool nsContentUtils::PrincipalHasPermission(nsIPrincipal& aPrincipal,
2019 const nsAtom* aPerm) {
2020 // Chrome gets access by default.
2021 if (aPrincipal.IsSystemPrincipal()) {
2022 return true;
2025 // Otherwise, only allow if caller is an addon with the permission.
2026 return BasePrincipal::Cast(aPrincipal).AddonHasPermission(aPerm);
2029 // static
2030 bool nsContentUtils::CallerHasPermission(JSContext* aCx, const nsAtom* aPerm) {
2031 return PrincipalHasPermission(*SubjectPrincipal(aCx), aPerm);
2034 // static
2035 nsIPrincipal* nsContentUtils::GetAttrTriggeringPrincipal(
2036 nsIContent* aContent, const nsAString& aAttrValue,
2037 nsIPrincipal* aSubjectPrincipal) {
2038 nsIPrincipal* contentPrin = aContent ? aContent->NodePrincipal() : nullptr;
2040 // If the subject principal is the same as the content principal, or no
2041 // explicit subject principal was provided, we don't need to do any further
2042 // checks. Just return the content principal.
2043 if (contentPrin == aSubjectPrincipal || !aSubjectPrincipal) {
2044 return contentPrin;
2047 // Only use the subject principal if the URL string we are going to end up
2048 // fetching is under the control of that principal, which is never the case
2049 // for relative URLs.
2050 if (aAttrValue.IsEmpty() ||
2051 !IsAbsoluteURL(NS_ConvertUTF16toUTF8(aAttrValue))) {
2052 return contentPrin;
2055 // Only use the subject principal as the attr triggering principal if it
2056 // should override the CSP of the node's principal.
2057 if (BasePrincipal::Cast(aSubjectPrincipal)->OverridesCSP(contentPrin)) {
2058 return aSubjectPrincipal;
2061 return contentPrin;
2064 // static
2065 bool nsContentUtils::IsAbsoluteURL(const nsACString& aURL) {
2066 nsAutoCString scheme;
2067 if (NS_FAILED(net_ExtractURLScheme(aURL, scheme))) {
2068 // If we can't extract a scheme, it's not an absolute URL.
2069 return false;
2072 // If it parses as an absolute StandardURL, it's definitely an absolute URL,
2073 // so no need to check with the IO service.
2074 if (net_IsAbsoluteURL(aURL)) {
2075 return true;
2078 nsresult rv = NS_OK;
2079 nsCOMPtr<nsIIOService> io = mozilla::components::IO::Service(&rv);
2080 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
2081 if (NS_FAILED(rv)) {
2082 return false;
2085 uint32_t flags;
2086 if (NS_SUCCEEDED(io->GetProtocolFlags(scheme.get(), &flags))) {
2087 return flags & nsIProtocolHandler::URI_NORELATIVE;
2090 return false;
2093 // static
2094 bool nsContentUtils::InProlog(nsINode* aNode) {
2095 MOZ_ASSERT(aNode, "missing node to nsContentUtils::InProlog");
2097 nsINode* parent = aNode->GetParentNode();
2098 if (!parent || !parent->IsDocument()) {
2099 return false;
2102 const Document* doc = parent->AsDocument();
2103 const nsIContent* root = doc->GetRootElement();
2104 if (!root) {
2105 return true;
2107 const Maybe<uint32_t> indexOfNode = doc->ComputeIndexOf(aNode);
2108 const Maybe<uint32_t> indexOfRoot = doc->ComputeIndexOf(root);
2109 if (MOZ_LIKELY(indexOfNode.isSome() && indexOfRoot.isSome())) {
2110 return *indexOfNode < *indexOfRoot;
2112 // XXX Keep the odd traditional behavior for now.
2113 return indexOfNode.isNothing() && indexOfRoot.isSome();
2116 bool nsContentUtils::IsCallerChrome() {
2117 MOZ_ASSERT(NS_IsMainThread());
2118 return SubjectPrincipal() == sSystemPrincipal;
2121 #ifdef FUZZING
2122 bool nsContentUtils::IsFuzzingEnabled() {
2123 return StaticPrefs::fuzzing_enabled();
2125 #endif
2127 /* static */
2128 bool nsContentUtils::IsCallerChromeOrElementTransformGettersEnabled(
2129 JSContext* aCx, JSObject*) {
2130 return ThreadsafeIsSystemCaller(aCx) ||
2131 StaticPrefs::dom_element_transform_getters_enabled();
2134 // Older Should RFP Functions ----------------------------------
2136 /* static */
2137 bool nsContentUtils::ShouldResistFingerprinting(RFPTarget aTarget) {
2138 return nsRFPService::IsRFPEnabledFor(aTarget);
2141 /* static */
2142 bool nsContentUtils::ShouldResistFingerprinting(nsIGlobalObject* aGlobalObject,
2143 RFPTarget aTarget) {
2144 if (!aGlobalObject) {
2145 return ShouldResistFingerprinting("Null Object", aTarget);
2147 return aGlobalObject->ShouldResistFingerprinting(aTarget);
2150 // Newer Should RFP Functions ----------------------------------
2151 // Utilities ---------------------------------------------------
2153 inline void LogDomainAndPrefList(const char* urlType,
2154 const char* exemptedDomainsPrefName,
2155 nsAutoCString& url, bool isExemptDomain) {
2156 nsAutoCString list;
2157 Preferences::GetCString(exemptedDomainsPrefName, list);
2158 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2159 ("%s \"%s\" is %s the exempt list \"%s\"", urlType,
2160 PromiseFlatCString(url).get(), isExemptDomain ? "in" : "NOT in",
2161 PromiseFlatCString(list).get()));
2164 inline already_AddRefed<nsICookieJarSettings> GetCookieJarSettings(
2165 nsILoadInfo* aLoadInfo) {
2166 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
2167 nsresult rv =
2168 aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
2169 if (rv == NS_ERROR_NOT_IMPLEMENTED) {
2170 // The TRRLoadInfo in particular does not implement this method
2171 // In that instance. We will return false and let other code decide if
2172 // we shouldRFP for this connection
2173 return nullptr;
2175 if (NS_WARN_IF(NS_FAILED(rv))) {
2176 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2177 ("Called CookieJarSettingsSaysShouldResistFingerprinting but the "
2178 "loadinfo's CookieJarSettings couldn't be retrieved"));
2179 return nullptr;
2182 MOZ_ASSERT(cookieJarSettings);
2183 return cookieJarSettings.forget();
2186 bool ETPSaysShouldNotResistFingerprinting(nsIChannel* aChannel,
2187 nsILoadInfo* aLoadInfo) {
2188 // A positive return from this function should always be obeyed.
2189 // A negative return means we should keep checking things.
2191 // We do not want this check to apply to RFP, only to FPP
2192 // There is one problematic combination of prefs; however:
2193 // If RFP is enabled in PBMode only and FPP is enabled globally
2194 // (so, in non-PBM mode) - we need to know if we're in PBMode or not.
2195 // But that's kind of expensive and we'd like to avoid it if we
2196 // don't have to, so special-case that scenario
2197 if (StaticPrefs::privacy_fingerprintingProtection_DoNotUseDirectly() &&
2198 !StaticPrefs::privacy_resistFingerprinting_DoNotUseDirectly() &&
2199 StaticPrefs::privacy_resistFingerprinting_pbmode_DoNotUseDirectly()) {
2200 if (NS_UsePrivateBrowsing(aChannel)) {
2201 // In PBM (where RFP is enabled) do not exempt based on the ETP toggle
2202 return false;
2204 } else if (StaticPrefs::privacy_resistFingerprinting_DoNotUseDirectly() ||
2205 StaticPrefs::
2206 privacy_resistFingerprinting_pbmode_DoNotUseDirectly()) {
2207 // In RFP, never use the ETP toggle to exempt.
2208 // We can safely return false here even if we are not in PBM mode
2209 // and RFP_pbmode is enabled because we will later see that and
2210 // return false from the ShouldRFP function entirely.
2211 return false;
2214 nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
2215 GetCookieJarSettings(aLoadInfo);
2216 if (!cookieJarSettings) {
2217 return false;
2220 return ContentBlockingAllowList::Check(cookieJarSettings);
2223 inline bool CookieJarSettingsSaysShouldResistFingerprinting(
2224 nsILoadInfo* aLoadInfo) {
2225 // A positive return from this function should always be obeyed.
2226 // A negative return means we should keep checking things.
2228 nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
2229 GetCookieJarSettings(aLoadInfo);
2230 if (!cookieJarSettings) {
2231 return false;
2233 return cookieJarSettings->GetShouldResistFingerprinting();
2236 inline bool SchemeSaysShouldNotResistFingerprinting(nsIURI* aURI) {
2237 return aURI->SchemeIs("chrome") || aURI->SchemeIs("resource") ||
2238 aURI->SchemeIs("view-source") || aURI->SchemeIs("moz-extension") ||
2239 (aURI->SchemeIs("about") && !NS_IsContentAccessibleAboutURI(aURI));
2242 inline bool SchemeSaysShouldNotResistFingerprinting(nsIPrincipal* aPrincipal) {
2243 if (aPrincipal->SchemeIs("chrome") || aPrincipal->SchemeIs("resource") ||
2244 aPrincipal->SchemeIs("view-source") ||
2245 aPrincipal->SchemeIs("moz-extension")) {
2246 return true;
2249 if (!aPrincipal->SchemeIs("about")) {
2250 return false;
2253 bool isContentAccessibleAboutURI;
2254 Unused << aPrincipal->IsContentAccessibleAboutURI(
2255 &isContentAccessibleAboutURI);
2256 return !isContentAccessibleAboutURI;
2259 const char* kExemptedDomainsPrefName =
2260 "privacy.resistFingerprinting.exemptedDomains";
2262 inline bool PartionKeyIsAlsoExempted(
2263 const mozilla::OriginAttributes& aOriginAttributes) {
2264 // If we've gotten here we have (probably) passed the CookieJarSettings
2265 // check that would tell us that if we _are_ a subdocument, then we are on
2266 // an exempted top-level domain and we should see if we ourselves are
2267 // exempted. But we may have gotten here because we directly called the
2268 // _dangerous function and we haven't done that check, but we _were_
2269 // instatiated from a state where we could have been partitioned.
2270 // So perform this last-ditch check for that scenario.
2271 // We arbitrarily use https as the scheme, but it doesn't matter.
2272 nsresult rv = NS_ERROR_NOT_INITIALIZED;
2273 nsCOMPtr<nsIURI> uri;
2274 if (StaticPrefs::privacy_firstparty_isolate() &&
2275 !aOriginAttributes.mFirstPartyDomain.IsEmpty()) {
2276 rv = NS_NewURI(getter_AddRefs(uri),
2277 u"https://"_ns + aOriginAttributes.mFirstPartyDomain);
2278 } else if (!aOriginAttributes.mPartitionKey.IsEmpty()) {
2279 rv = NS_NewURI(getter_AddRefs(uri),
2280 u"https://"_ns + aOriginAttributes.mPartitionKey);
2283 if (!NS_FAILED(rv)) {
2284 bool isExemptPartitionKey =
2285 nsContentUtils::IsURIInPrefList(uri, kExemptedDomainsPrefName);
2286 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2287 mozilla::LogLevel::Debug)) {
2288 nsAutoCString url;
2289 uri->GetHost(url);
2290 LogDomainAndPrefList("Partition Key", kExemptedDomainsPrefName, url,
2291 isExemptPartitionKey);
2293 return isExemptPartitionKey;
2295 return true;
2298 // Functions ---------------------------------------------------
2300 /* static */
2301 bool nsContentUtils::ShouldResistFingerprinting(const char* aJustification,
2302 RFPTarget aTarget) {
2303 // See comment in header file for information about usage
2304 return ShouldResistFingerprinting(aTarget);
2307 /* static */
2308 bool nsContentUtils::ShouldResistFingerprinting(CallerType aCallerType,
2309 nsIGlobalObject* aGlobalObject,
2310 RFPTarget aTarget) {
2311 if (aCallerType == CallerType::System) {
2312 return false;
2314 return ShouldResistFingerprinting(aGlobalObject, aTarget);
2317 bool nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell,
2318 RFPTarget aTarget) {
2319 if (!aDocShell) {
2320 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2321 ("Called nsContentUtils::ShouldResistFingerprinting(nsIDocShell*) "
2322 "with NULL docshell"));
2323 return ShouldResistFingerprinting("Null Object", aTarget);
2325 Document* doc = aDocShell->GetDocument();
2326 if (!doc) {
2327 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2328 ("Called nsContentUtils::ShouldResistFingerprinting(nsIDocShell*) "
2329 "with NULL doc"));
2330 return ShouldResistFingerprinting(aTarget);
2332 return doc->ShouldResistFingerprinting(aTarget);
2335 /* static */
2336 bool nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel,
2337 RFPTarget aTarget) {
2338 if (!aChannel) {
2339 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2340 ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2341 "aChannel) with NULL channel"));
2342 return ShouldResistFingerprinting("Null Object", aTarget);
2345 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
2346 if (!loadInfo) {
2347 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2348 ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2349 "aChannel) but the channel's loadinfo was NULL"));
2350 return ShouldResistFingerprinting("Null Object", aTarget);
2353 // With this check, we can ensure that the prefs and target say yes, so only
2354 // an exemption would cause us to return false.
2355 if (!ShouldResistFingerprinting("Positive return check", aTarget)) {
2356 return false;
2359 if (ETPSaysShouldNotResistFingerprinting(aChannel, loadInfo)) {
2360 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2361 ("Inside ShouldResistFingerprinting(nsIChannel*)"
2362 " ETPSaysShouldNotResistFingerprinting said false"));
2363 return false;
2366 if (CookieJarSettingsSaysShouldResistFingerprinting(loadInfo)) {
2367 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2368 ("Inside ShouldResistFingerprinting(nsIChannel*)"
2369 " CookieJarSettingsSaysShouldResistFingerprinting said true"));
2370 return true;
2373 // Document types have no loading principal. Subdocument types do have a
2374 // loading principal, but it is the loading principal of the parent
2375 // document; not the subdocument.
2376 auto contentType = loadInfo->GetExternalContentPolicyType();
2377 // Case 1: Document or Subdocument load
2378 if (contentType == ExtContentPolicy::TYPE_DOCUMENT ||
2379 contentType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
2380 nsCOMPtr<nsIURI> channelURI;
2381 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
2382 MOZ_ASSERT(
2383 NS_SUCCEEDED(rv),
2384 "Failed to get URI in "
2385 "nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel)");
2386 // this check is to ensure that we do not crash in non-debug builds.
2387 if (NS_FAILED(rv)) {
2388 return true;
2391 #if 0
2392 if (loadInfo->GetExternalContentPolicyType() == ExtContentPolicy::TYPE_SUBDOCUMENT) {
2393 nsCOMPtr<nsIURI> channelURI;
2394 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
2395 nsAutoCString channelSpec;
2396 channelURI->GetSpec(channelSpec);
2398 if (!loadInfo->GetLoadingPrincipal()) {
2399 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2400 ("Sub Document Type. FinalChannelURI is %s, Loading Principal is NULL\n",
2401 channelSpec.get()));
2403 } else {
2404 nsAutoCString loadingPrincipalSpec;
2405 loadInfo->GetLoadingPrincipal()->GetOrigin(loadingPrincipalSpec);
2407 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2408 ("Sub Document Type. FinalChannelURI is %s, Loading Principal Origin is %s\n",
2409 channelSpec.get(), loadingPrincipalSpec.get()));
2413 #endif
2415 return ShouldResistFingerprinting_dangerous(
2416 channelURI, loadInfo->GetOriginAttributes(), "Internal Call", aTarget);
2419 // Case 2: Subresource Load
2420 // Because this code is only used for subresource loads, this
2421 // will check the parent's principal
2422 nsIPrincipal* principal = loadInfo->GetLoadingPrincipal();
2424 MOZ_ASSERT_IF(principal && !principal->IsSystemPrincipal() &&
2425 !principal->GetIsAddonOrExpandedAddonPrincipal(),
2426 BasePrincipal::Cast(principal)->OriginAttributesRef() ==
2427 loadInfo->GetOriginAttributes());
2428 return ShouldResistFingerprinting_dangerous(principal, "Internal Call",
2429 aTarget);
2432 /* static */
2433 bool nsContentUtils::ShouldResistFingerprinting_dangerous(
2434 nsIURI* aURI, const mozilla::OriginAttributes& aOriginAttributes,
2435 const char* aJustification, RFPTarget aTarget) {
2436 // With this check, we can ensure that the prefs and target say yes, so only
2437 // an exemption would cause us to return false.
2438 if (!ShouldResistFingerprinting("Positive return check", aTarget)) {
2439 return false;
2442 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2443 ("Inside ShouldResistFingerprinting_dangerous(nsIURI*,"
2444 " OriginAttributes) and the URI is %s",
2445 aURI->GetSpecOrDefault().get()));
2447 if (!StaticPrefs::privacy_resistFingerprinting_DoNotUseDirectly() &&
2448 !StaticPrefs::privacy_fingerprintingProtection_DoNotUseDirectly()) {
2449 // If neither of the 'regular' RFP prefs are set, then one (or both)
2450 // of the PBM-Only prefs are set (or we would have failed the
2451 // Positive return check.) Therefore, if we are not in PBM, return false
2452 if (aOriginAttributes.mPrivateBrowsingId == 0) {
2453 return false;
2457 // Exclude internal schemes and web extensions
2458 if (SchemeSaysShouldNotResistFingerprinting(aURI)) {
2459 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2460 ("Inside ShouldResistFingerprinting(nsIURI*)"
2461 " SchemeSaysShouldNotResistFingerprinting said false"));
2462 return false;
2465 bool isExemptDomain = false;
2466 nsAutoCString list;
2467 Preferences::GetCString(kExemptedDomainsPrefName, list);
2468 ToLowerCase(list);
2469 isExemptDomain = IsURIInList(aURI, list);
2471 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2472 mozilla::LogLevel::Debug)) {
2473 nsAutoCString url;
2474 aURI->GetHost(url);
2475 LogDomainAndPrefList("URI", kExemptedDomainsPrefName, url, isExemptDomain);
2478 if (isExemptDomain) {
2479 isExemptDomain &= PartionKeyIsAlsoExempted(aOriginAttributes);
2482 return !isExemptDomain;
2485 /* static */
2486 bool nsContentUtils::ShouldResistFingerprinting_dangerous(
2487 nsIPrincipal* aPrincipal, const char* aJustification, RFPTarget aTarget) {
2488 if (!aPrincipal) {
2489 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2490 ("Called nsContentUtils::ShouldResistFingerprinting(nsILoadInfo* "
2491 "aChannel) but the loadinfo's loadingprincipal was NULL"));
2492 return ShouldResistFingerprinting("Null object", aTarget);
2495 // With this check, we can ensure that the prefs and target say yes, so only
2496 // an exemption would cause us to return false.
2497 if (!ShouldResistFingerprinting("Positive return check", aTarget)) {
2498 return false;
2501 if (aPrincipal->IsSystemPrincipal()) {
2502 return false;
2505 auto originAttributes =
2506 BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
2507 if (!StaticPrefs::privacy_resistFingerprinting_DoNotUseDirectly() &&
2508 !StaticPrefs::privacy_fingerprintingProtection_DoNotUseDirectly()) {
2509 // If neither of the 'regular' RFP prefs are set, then one (or both)
2510 // of the PBM-Only prefs are set (or we would have failed the
2511 // Positive return check.) Therefore, if we are not in PBM, return false
2512 if (originAttributes.mPrivateBrowsingId == 0) {
2513 return false;
2517 // Exclude internal schemes and web extensions
2518 if (SchemeSaysShouldNotResistFingerprinting(aPrincipal)) {
2519 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2520 ("Inside ShouldResistFingerprinting(nsIPrincipal*)"
2521 " SchemeSaysShouldNotResistFingerprinting said false"));
2522 return false;
2525 // Web extension principals are also excluded
2526 if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
2527 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2528 ("Inside ShouldResistFingerprinting_dangerous(nsIPrincipal*)"
2529 " and AddonPolicy said false"));
2530 return false;
2533 bool isExemptDomain = false;
2534 aPrincipal->IsURIInPrefList(kExemptedDomainsPrefName, &isExemptDomain);
2536 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2537 mozilla::LogLevel::Debug)) {
2538 nsAutoCString origin;
2539 aPrincipal->GetOrigin(origin);
2540 LogDomainAndPrefList("URI", kExemptedDomainsPrefName, origin,
2541 isExemptDomain);
2544 if (isExemptDomain) {
2545 isExemptDomain &= PartionKeyIsAlsoExempted(originAttributes);
2548 return !isExemptDomain;
2551 // --------------------------------------------------------------------
2553 /* static */
2554 void nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
2555 int32_t aChromeWidth, int32_t aChromeHeight, int32_t aScreenWidth,
2556 int32_t aScreenHeight, int32_t aInputWidth, int32_t aInputHeight,
2557 bool aSetOuterWidth, bool aSetOuterHeight, int32_t* aOutputWidth,
2558 int32_t* aOutputHeight) {
2559 MOZ_ASSERT(aOutputWidth);
2560 MOZ_ASSERT(aOutputHeight);
2562 int32_t availContentWidth = 0;
2563 int32_t availContentHeight = 0;
2565 availContentWidth = std::min(StaticPrefs::privacy_window_maxInnerWidth(),
2566 aScreenWidth - aChromeWidth);
2567 #ifdef MOZ_WIDGET_GTK
2568 // In the GTK window, it will not report outside system decorations
2569 // when we get available window size, see Bug 581863. So, we leave a
2570 // 40 pixels space for them when calculating the available content
2571 // height. It is not necessary for the width since the content width
2572 // is usually pretty much the same as the chrome width.
2573 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2574 (-40 + aScreenHeight) - aChromeHeight);
2575 #else
2576 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2577 aScreenHeight - aChromeHeight);
2578 #endif
2580 // Ideally, we'd like to round window size to 1000x1000, but the
2581 // screen space could be too small to accommodate this size in some
2582 // cases. If it happens, we would round the window size to the nearest
2583 // 200x100.
2584 availContentWidth = availContentWidth - (availContentWidth % 200);
2585 availContentHeight = availContentHeight - (availContentHeight % 100);
2587 // If aIsOuter is true, we are setting the outer window. So we
2588 // have to consider the chrome UI.
2589 int32_t chromeOffsetWidth = aSetOuterWidth ? aChromeWidth : 0;
2590 int32_t chromeOffsetHeight = aSetOuterHeight ? aChromeHeight : 0;
2591 int32_t resultWidth = 0, resultHeight = 0;
2593 // if the original size is greater than the maximum available size, we set
2594 // it to the maximum size. And if the original value is less than the
2595 // minimum rounded size, we set it to the minimum 200x100.
2596 if (aInputWidth > (availContentWidth + chromeOffsetWidth)) {
2597 resultWidth = availContentWidth + chromeOffsetWidth;
2598 } else if (aInputWidth < (200 + chromeOffsetWidth)) {
2599 resultWidth = 200 + chromeOffsetWidth;
2600 } else {
2601 // Otherwise, we round the window to the nearest upper rounded 200x100.
2602 resultWidth = NSToIntCeil((aInputWidth - chromeOffsetWidth) / 200.0) * 200 +
2603 chromeOffsetWidth;
2606 if (aInputHeight > (availContentHeight + chromeOffsetHeight)) {
2607 resultHeight = availContentHeight + chromeOffsetHeight;
2608 } else if (aInputHeight < (100 + chromeOffsetHeight)) {
2609 resultHeight = 100 + chromeOffsetHeight;
2610 } else {
2611 resultHeight =
2612 NSToIntCeil((aInputHeight - chromeOffsetHeight) / 100.0) * 100 +
2613 chromeOffsetHeight;
2616 *aOutputWidth = resultWidth;
2617 *aOutputHeight = resultHeight;
2620 bool nsContentUtils::ThreadsafeIsCallerChrome() {
2621 return NS_IsMainThread() ? IsCallerChrome()
2622 : IsCurrentThreadRunningChromeWorker();
2625 bool nsContentUtils::IsCallerUAWidget() {
2626 JSContext* cx = GetCurrentJSContext();
2627 if (!cx) {
2628 return false;
2631 JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
2632 if (!realm) {
2633 return false;
2636 return xpc::IsUAWidgetScope(realm);
2639 bool nsContentUtils::IsSystemCaller(JSContext* aCx) {
2640 // Note that SubjectPrincipal() assumes we are in a compartment here.
2641 return SubjectPrincipal(aCx) == sSystemPrincipal;
2644 bool nsContentUtils::ThreadsafeIsSystemCaller(JSContext* aCx) {
2645 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
2646 MOZ_ASSERT(ccjscx->Context() == aCx);
2648 return ccjscx->IsSystemCaller();
2651 // static
2652 bool nsContentUtils::LookupBindingMember(
2653 JSContext* aCx, nsIContent* aContent, JS::Handle<jsid> aId,
2654 JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
2655 return true;
2658 nsINode* nsContentUtils::GetNearestInProcessCrossDocParentNode(
2659 nsINode* aChild) {
2660 if (aChild->IsDocument()) {
2661 for (BrowsingContext* bc = aChild->AsDocument()->GetBrowsingContext(); bc;
2662 bc = bc->GetParent()) {
2663 if (bc->GetEmbedderElement()) {
2664 return bc->GetEmbedderElement();
2667 return nullptr;
2670 nsINode* parent = aChild->GetParentNode();
2671 if (parent && parent->IsContent() && aChild->IsContent()) {
2672 parent = aChild->AsContent()->GetFlattenedTreeParent();
2675 return parent;
2678 bool nsContentUtils::ContentIsHostIncludingDescendantOf(
2679 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2680 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2681 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2683 do {
2684 if (aPossibleDescendant == aPossibleAncestor) return true;
2685 if (aPossibleDescendant->IsDocumentFragment()) {
2686 aPossibleDescendant =
2687 aPossibleDescendant->AsDocumentFragment()->GetHost();
2688 } else {
2689 aPossibleDescendant = aPossibleDescendant->GetParentNode();
2691 } while (aPossibleDescendant);
2693 return false;
2696 // static
2697 bool nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
2698 nsINode* aPossibleAncestor) {
2699 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2700 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2702 do {
2703 if (aPossibleDescendant == aPossibleAncestor) {
2704 return true;
2707 aPossibleDescendant =
2708 GetNearestInProcessCrossDocParentNode(aPossibleDescendant);
2709 } while (aPossibleDescendant);
2711 return false;
2714 // static
2715 bool nsContentUtils::ContentIsFlattenedTreeDescendantOf(
2716 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2717 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2718 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2720 do {
2721 if (aPossibleDescendant == aPossibleAncestor) {
2722 return true;
2724 aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
2725 } while (aPossibleDescendant);
2727 return false;
2730 // static
2731 bool nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
2732 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2733 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2734 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2736 do {
2737 if (aPossibleDescendant == aPossibleAncestor) {
2738 return true;
2740 aPossibleDescendant =
2741 aPossibleDescendant->GetFlattenedTreeParentNodeForStyle();
2742 } while (aPossibleDescendant);
2744 return false;
2747 // static
2748 nsINode* nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB) {
2749 while (true && aTargetA) {
2750 // If A's root is not a shadow root...
2751 nsINode* root = aTargetA->SubtreeRoot();
2752 if (!root->IsShadowRoot()) {
2753 // ...then return A.
2754 return aTargetA;
2757 // or A's root is a shadow-including inclusive ancestor of B...
2758 if (aTargetB->IsShadowIncludingInclusiveDescendantOf(root)) {
2759 // ...then return A.
2760 return aTargetA;
2763 aTargetA = ShadowRoot::FromNode(root)->GetHost();
2766 return nullptr;
2769 // static
2770 Element* nsContentUtils::GetAnElementForTiming(Element* aTarget,
2771 const Document* aDocument,
2772 nsIGlobalObject* aGlobal) {
2773 if (!aTarget->IsInComposedDoc()) {
2774 return nullptr;
2777 if (!aDocument) {
2778 nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(aGlobal);
2779 if (!inner) {
2780 return nullptr;
2782 aDocument = inner->GetExtantDoc();
2785 MOZ_ASSERT(aDocument);
2787 if (aTarget->GetUncomposedDocOrConnectedShadowRoot() != aDocument ||
2788 !aDocument->IsCurrentActiveDocument()) {
2789 return nullptr;
2792 return aTarget;
2795 // static
2796 nsresult nsContentUtils::GetInclusiveAncestors(nsINode* aNode,
2797 nsTArray<nsINode*>& aArray) {
2798 while (aNode) {
2799 aArray.AppendElement(aNode);
2800 aNode = aNode->GetParentNode();
2802 return NS_OK;
2805 // static
2806 nsresult nsContentUtils::GetInclusiveAncestorsAndOffsets(
2807 nsINode* aNode, uint32_t aOffset, nsTArray<nsIContent*>* aAncestorNodes,
2808 nsTArray<Maybe<uint32_t>>* aAncestorOffsets) {
2809 NS_ENSURE_ARG_POINTER(aNode);
2811 if (!aNode->IsContent()) {
2812 return NS_ERROR_FAILURE;
2814 nsIContent* content = aNode->AsContent();
2816 if (!aAncestorNodes->IsEmpty()) {
2817 NS_WARNING("aAncestorNodes is not empty");
2818 aAncestorNodes->Clear();
2821 if (!aAncestorOffsets->IsEmpty()) {
2822 NS_WARNING("aAncestorOffsets is not empty");
2823 aAncestorOffsets->Clear();
2826 // insert the node itself
2827 aAncestorNodes->AppendElement(content);
2828 aAncestorOffsets->AppendElement(Some(aOffset));
2830 // insert all the ancestors
2831 nsIContent* child = content;
2832 nsIContent* parent = child->GetParent();
2833 while (parent) {
2834 aAncestorNodes->AppendElement(parent);
2835 aAncestorOffsets->AppendElement(parent->ComputeIndexOf(child));
2836 child = parent;
2837 parent = parent->GetParent();
2840 return NS_OK;
2843 template <typename Node, typename GetParentFunc>
2844 static Node* GetCommonAncestorInternal(Node* aNode1, Node* aNode2,
2845 GetParentFunc aGetParentFunc) {
2846 MOZ_ASSERT(aNode1 != aNode2);
2848 // Build the chain of parents
2849 AutoTArray<Node*, 30> parents1, parents2;
2850 do {
2851 parents1.AppendElement(aNode1);
2852 aNode1 = aGetParentFunc(aNode1);
2853 } while (aNode1);
2854 do {
2855 parents2.AppendElement(aNode2);
2856 aNode2 = aGetParentFunc(aNode2);
2857 } while (aNode2);
2859 // Find where the parent chain differs
2860 uint32_t pos1 = parents1.Length();
2861 uint32_t pos2 = parents2.Length();
2862 Node** data1 = parents1.Elements();
2863 Node** data2 = parents2.Elements();
2864 Node* parent = nullptr;
2865 uint32_t len;
2866 for (len = std::min(pos1, pos2); len > 0; --len) {
2867 Node* child1 = data1[--pos1];
2868 Node* child2 = data2[--pos2];
2869 if (child1 != child2) {
2870 break;
2872 parent = child1;
2875 return parent;
2878 /* static */
2879 nsINode* nsContentUtils::GetCommonAncestorHelper(nsINode* aNode1,
2880 nsINode* aNode2) {
2881 return GetCommonAncestorInternal(
2882 aNode1, aNode2, [](nsINode* aNode) { return aNode->GetParentNode(); });
2885 /* static */
2886 nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorHelper(
2887 nsIContent* aContent1, nsIContent* aContent2) {
2888 return GetCommonAncestorInternal(
2889 aContent1, aContent2,
2890 [](nsIContent* aContent) { return aContent->GetFlattenedTreeParent(); });
2893 /* static */
2894 Element* nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
2895 Element* aElement1, Element* aElement2) {
2896 return GetCommonAncestorInternal(aElement1, aElement2, [](Element* aElement) {
2897 return aElement->GetFlattenedTreeParentElementForStyle();
2901 /* static */
2902 bool nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2,
2903 Maybe<uint32_t>* aNode1Index,
2904 Maybe<uint32_t>* aNode2Index) {
2905 // Note, CompareDocumentPosition takes the latter params in different order.
2906 return (aNode2->CompareDocumentPosition(*aNode1, aNode2Index, aNode1Index) &
2907 (Node_Binding::DOCUMENT_POSITION_PRECEDING |
2908 Node_Binding::DOCUMENT_POSITION_DISCONNECTED)) ==
2909 Node_Binding::DOCUMENT_POSITION_PRECEDING;
2912 /* static */
2913 Maybe<int32_t> nsContentUtils::ComparePoints(
2914 const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
2915 uint32_t aOffset2, ComparePointsCache* aParent1Cache) {
2916 bool disconnected{false};
2918 const int32_t order = ComparePoints_Deprecated(
2919 aParent1, aOffset1, aParent2, aOffset2, &disconnected, aParent1Cache);
2920 if (disconnected) {
2921 return Nothing();
2924 return Some(order);
2927 /* static */
2928 int32_t nsContentUtils::ComparePoints_Deprecated(
2929 const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
2930 uint32_t aOffset2, bool* aDisconnected, ComparePointsCache* aParent1Cache) {
2931 if (aParent1 == aParent2) {
2932 return aOffset1 < aOffset2 ? -1 : aOffset1 > aOffset2 ? 1 : 0;
2935 AutoTArray<const nsINode*, 32> parents1, parents2;
2936 const nsINode* node1 = aParent1;
2937 const nsINode* node2 = aParent2;
2938 do {
2939 parents1.AppendElement(node1);
2940 node1 = node1->GetParentOrShadowHostNode();
2941 } while (node1);
2942 do {
2943 parents2.AppendElement(node2);
2944 node2 = node2->GetParentOrShadowHostNode();
2945 } while (node2);
2947 uint32_t pos1 = parents1.Length() - 1;
2948 uint32_t pos2 = parents2.Length() - 1;
2950 bool disconnected = parents1.ElementAt(pos1) != parents2.ElementAt(pos2);
2951 if (aDisconnected) {
2952 *aDisconnected = disconnected;
2954 if (disconnected) {
2955 NS_ASSERTION(aDisconnected, "unexpected disconnected nodes");
2956 return 1;
2959 // Find where the parent chains differ
2960 const nsINode* parent = parents1.ElementAt(pos1);
2961 uint32_t len;
2962 for (len = std::min(pos1, pos2); len > 0; --len) {
2963 const nsINode* child1 = parents1.ElementAt(--pos1);
2964 const nsINode* child2 = parents2.ElementAt(--pos2);
2965 if (child1 != child2) {
2966 if (MOZ_UNLIKELY(child1->IsShadowRoot())) {
2967 // Shadow roots come before light DOM per
2968 // https://dom.spec.whatwg.org/#concept-shadow-including-tree-order
2969 MOZ_ASSERT(!child2->IsShadowRoot(), "Two shadow roots?");
2970 return -1;
2972 if (MOZ_UNLIKELY(child2->IsShadowRoot())) {
2973 return 1;
2975 const Maybe<uint32_t> child1Index =
2976 aParent1Cache ? aParent1Cache->ComputeIndexOf(parent, child1)
2977 : parent->ComputeIndexOf(child1);
2978 const Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2);
2979 if (MOZ_LIKELY(child1Index.isSome() && child2Index.isSome())) {
2980 return *child1Index < *child2Index ? -1 : 1;
2982 // XXX Keep the odd traditional behavior for now.
2983 return child1Index.isNothing() && child2Index.isSome() ? -1 : 1;
2985 parent = child1;
2988 // The parent chains never differed, so one of the nodes is an ancestor of
2989 // the other
2991 NS_ASSERTION(!pos1 || !pos2,
2992 "should have run out of parent chain for one of the nodes");
2994 if (!pos1) {
2995 const nsINode* child2 = parents2.ElementAt(--pos2);
2996 const Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2);
2997 if (MOZ_UNLIKELY(NS_WARN_IF(child2Index.isNothing()))) {
2998 return 1;
3000 return aOffset1 <= *child2Index ? -1 : 1;
3003 const nsINode* child1 = parents1.ElementAt(--pos1);
3004 const Maybe<uint32_t> child1Index =
3005 aParent1Cache ? aParent1Cache->ComputeIndexOf(parent, child1)
3006 : parent->ComputeIndexOf(child1);
3007 if (MOZ_UNLIKELY(NS_WARN_IF(child1Index.isNothing()))) {
3008 return -1;
3010 return *child1Index < aOffset2 ? -1 : 1;
3013 /* static */
3014 BrowserParent* nsContentUtils::GetCommonBrowserParentAncestor(
3015 BrowserParent* aBrowserParent1, BrowserParent* aBrowserParent2) {
3016 return GetCommonAncestorInternal(
3017 aBrowserParent1, aBrowserParent2, [](BrowserParent* aBrowserParent) {
3018 return aBrowserParent->GetBrowserBridgeParent()
3019 ? aBrowserParent->GetBrowserBridgeParent()->Manager()
3020 : nullptr;
3024 /* static */
3025 Element* nsContentUtils::GetTargetElement(Document* aDocument,
3026 const nsAString& aAnchorName) {
3027 MOZ_ASSERT(aDocument);
3029 if (aAnchorName.IsEmpty()) {
3030 return nullptr;
3032 // 1. If there is an element in the document tree that has an ID equal to
3033 // fragment, then return the first such element in tree order.
3034 if (Element* el = aDocument->GetElementById(aAnchorName)) {
3035 return el;
3038 // 2. If there is an a element in the document tree that has a name
3039 // attribute whose value is equal to fragment, then return the first such
3040 // element in tree order.
3042 // FIXME(emilio): Why the different code-paths for HTML and non-HTML docs?
3043 if (aDocument->IsHTMLDocument()) {
3044 nsCOMPtr<nsINodeList> list = aDocument->GetElementsByName(aAnchorName);
3045 // Loop through the named nodes looking for the first anchor
3046 uint32_t length = list->Length();
3047 for (uint32_t i = 0; i < length; i++) {
3048 nsIContent* node = list->Item(i);
3049 if (node->IsHTMLElement(nsGkAtoms::a)) {
3050 return node->AsElement();
3053 } else {
3054 constexpr auto nameSpace = u"http://www.w3.org/1999/xhtml"_ns;
3055 // Get the list of anchor elements
3056 nsCOMPtr<nsINodeList> list =
3057 aDocument->GetElementsByTagNameNS(nameSpace, u"a"_ns);
3058 // Loop through the anchors looking for the first one with the given name.
3059 for (uint32_t i = 0; true; i++) {
3060 nsIContent* node = list->Item(i);
3061 if (!node) { // End of list
3062 break;
3065 // Compare the name attribute
3066 if (node->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
3067 aAnchorName, eCaseMatters)) {
3068 return node->AsElement();
3073 // 3. Return null.
3074 return nullptr;
3077 /* static */
3078 template <typename FPT, typename FRT, typename SPT, typename SRT>
3079 Maybe<int32_t> nsContentUtils::ComparePoints(
3080 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
3081 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary) {
3082 if (!aFirstBoundary.IsSet() || !aSecondBoundary.IsSet()) {
3083 return Nothing{};
3086 bool disconnected{false};
3087 const int32_t order =
3088 ComparePoints_Deprecated(aFirstBoundary, aSecondBoundary, &disconnected);
3090 if (disconnected) {
3091 return Nothing{};
3094 return Some(order);
3097 /* static */
3098 template <typename FPT, typename FRT, typename SPT, typename SRT>
3099 int32_t nsContentUtils::ComparePoints_Deprecated(
3100 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
3101 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary, bool* aDisconnected) {
3102 if (NS_WARN_IF(!aFirstBoundary.IsSet()) ||
3103 NS_WARN_IF(!aSecondBoundary.IsSet())) {
3104 return -1;
3106 // XXX Re-implement this without calling `Offset()` as far as possible,
3107 // and the other overload should be an alias of this.
3108 return ComparePoints_Deprecated(
3109 aFirstBoundary.Container(),
3110 *aFirstBoundary.Offset(
3111 RangeBoundaryBase<FPT, FRT>::OffsetFilter::kValidOrInvalidOffsets),
3112 aSecondBoundary.Container(),
3113 *aSecondBoundary.Offset(
3114 RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOrInvalidOffsets),
3115 aDisconnected);
3118 inline bool IsCharInSet(const char* aSet, const char16_t aChar) {
3119 char16_t ch;
3120 while ((ch = *aSet)) {
3121 if (aChar == char16_t(ch)) {
3122 return true;
3124 ++aSet;
3126 return false;
3130 * This method strips leading/trailing chars, in given set, from string.
3133 // static
3134 const nsDependentSubstring nsContentUtils::TrimCharsInSet(
3135 const char* aSet, const nsAString& aValue) {
3136 nsAString::const_iterator valueCurrent, valueEnd;
3138 aValue.BeginReading(valueCurrent);
3139 aValue.EndReading(valueEnd);
3141 // Skip characters in the beginning
3142 while (valueCurrent != valueEnd) {
3143 if (!IsCharInSet(aSet, *valueCurrent)) {
3144 break;
3146 ++valueCurrent;
3149 if (valueCurrent != valueEnd) {
3150 for (;;) {
3151 --valueEnd;
3152 if (!IsCharInSet(aSet, *valueEnd)) {
3153 break;
3156 ++valueEnd; // Step beyond the last character we want in the value.
3159 // valueEnd should point to the char after the last to copy
3160 return Substring(valueCurrent, valueEnd);
3164 * This method strips leading and trailing whitespace from a string.
3167 // static
3168 template <bool IsWhitespace(char16_t)>
3169 const nsDependentSubstring nsContentUtils::TrimWhitespace(const nsAString& aStr,
3170 bool aTrimTrailing) {
3171 nsAString::const_iterator start, end;
3173 aStr.BeginReading(start);
3174 aStr.EndReading(end);
3176 // Skip whitespace characters in the beginning
3177 while (start != end && IsWhitespace(*start)) {
3178 ++start;
3181 if (aTrimTrailing) {
3182 // Skip whitespace characters in the end.
3183 while (end != start) {
3184 --end;
3186 if (!IsWhitespace(*end)) {
3187 // Step back to the last non-whitespace character.
3188 ++end;
3190 break;
3195 // Return a substring for the string w/o leading and/or trailing
3196 // whitespace
3198 return Substring(start, end);
3201 // Declaring the templates we are going to use avoid linking issues without
3202 // inlining the method. Considering there is not so much spaces checking
3203 // methods we can consider this to be better than inlining.
3204 template const nsDependentSubstring
3205 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, bool);
3206 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
3207 nsContentUtils::IsHTMLWhitespace>(const nsAString&, bool);
3208 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
3209 nsContentUtils::IsHTMLWhitespaceOrNBSP>(const nsAString&, bool);
3211 static inline void KeyAppendSep(nsACString& aKey) {
3212 if (!aKey.IsEmpty()) {
3213 aKey.Append('>');
3217 static inline void KeyAppendString(const nsAString& aString, nsACString& aKey) {
3218 KeyAppendSep(aKey);
3220 // Could escape separator here if collisions happen. > is not a legal char
3221 // for a name or type attribute, so we should be safe avoiding that extra
3222 // work.
3224 AppendUTF16toUTF8(aString, aKey);
3227 static inline void KeyAppendString(const nsACString& aString,
3228 nsACString& aKey) {
3229 KeyAppendSep(aKey);
3231 // Could escape separator here if collisions happen. > is not a legal char
3232 // for a name or type attribute, so we should be safe avoiding that extra
3233 // work.
3235 aKey.Append(aString);
3238 static inline void KeyAppendInt(int32_t aInt, nsACString& aKey) {
3239 KeyAppendSep(aKey);
3241 aKey.AppendInt(aInt);
3244 static inline bool IsAutocompleteOff(const nsIContent* aContent) {
3245 return aContent->IsElement() &&
3246 aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
3247 nsGkAtoms::autocomplete, u"off"_ns,
3248 eIgnoreCase);
3251 /*static*/
3252 void nsContentUtils::GenerateStateKey(nsIContent* aContent, Document* aDocument,
3253 nsACString& aKey) {
3254 MOZ_ASSERT(aContent);
3256 aKey.Truncate();
3258 uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
3260 // Don't capture state for anonymous content
3261 if (aContent->IsInNativeAnonymousSubtree()) {
3262 return;
3265 if (IsAutocompleteOff(aContent)) {
3266 return;
3269 RefPtr<Document> doc = aContent->GetUncomposedDoc();
3271 KeyAppendInt(partID, aKey); // first append a partID
3272 bool generatedUniqueKey = false;
3274 if (doc && doc->IsHTMLOrXHTML()) {
3275 nsHTMLDocument* htmlDoc = doc->AsHTMLDocument();
3277 // If we have a form control and can calculate form information, use that
3278 // as the key - it is more reliable than just recording position in the
3279 // DOM.
3280 // XXXbz Is it, really? We have bugs on this, I think...
3281 // Important to have a unique key, and tag/type/name may not be.
3283 // The format of the key depends on whether the control has a form,
3284 // and whether the element was parser inserted:
3286 // [Has Form, Parser Inserted]:
3287 // fp>type>FormNum>IndOfControlInForm>FormName>name
3289 // [No Form, Parser Inserted]:
3290 // dp>type>ControlNum>name
3292 // [Has Form, Not Parser Inserted]:
3293 // fn>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
3295 // [No Form, Not Parser Inserted]:
3296 // dn>type>IndOfControlInDoc>name
3298 // XXX We don't need to use index if name is there
3299 // XXXbz We don't? Why not? I don't follow.
3301 nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
3302 if (control) {
3303 // Get the control number if this was a parser inserted element from the
3304 // network.
3305 int32_t controlNumber =
3306 control->GetParserInsertedControlNumberForStateKey();
3307 bool parserInserted = controlNumber != -1;
3309 RefPtr<nsContentList> htmlForms;
3310 RefPtr<nsContentList> htmlFormControls;
3311 if (!parserInserted) {
3312 // Getting these lists is expensive, as we need to keep them up to date
3313 // as the document loads, so we avoid it if we don't need them.
3314 htmlDoc->GetFormsAndFormControls(getter_AddRefs(htmlForms),
3315 getter_AddRefs(htmlFormControls));
3318 // Append the control type
3319 KeyAppendInt(int32_t(control->ControlType()), aKey);
3321 // If in a form, add form name / index of form / index in form
3322 HTMLFormElement* formElement = control->GetForm();
3323 if (formElement) {
3324 if (IsAutocompleteOff(formElement)) {
3325 aKey.Truncate();
3326 return;
3329 // Append the form number, if this is a parser inserted control, or
3330 // the index of the form in the document otherwise.
3331 bool appendedForm = false;
3332 if (parserInserted) {
3333 MOZ_ASSERT(formElement->GetFormNumberForStateKey() != -1,
3334 "when generating a state key for a parser inserted form "
3335 "control we should have a parser inserted <form> element");
3336 KeyAppendString("fp"_ns, aKey);
3337 KeyAppendInt(formElement->GetFormNumberForStateKey(), aKey);
3338 appendedForm = true;
3339 } else {
3340 KeyAppendString("fn"_ns, aKey);
3341 int32_t index = htmlForms->IndexOf(formElement, false);
3342 if (index <= -1) {
3344 // XXX HACK this uses some state that was dumped into the document
3345 // specifically to fix bug 138892. What we are trying to do is
3346 // *guess* which form this control's state is found in, with the
3347 // highly likely guess that the highest form parsed so far is the
3348 // one. This code should not be on trunk, only branch.
3350 index = htmlDoc->GetNumFormsSynchronous() - 1;
3352 if (index > -1) {
3353 KeyAppendInt(index, aKey);
3354 appendedForm = true;
3358 if (appendedForm) {
3359 // Append the index of the control in the form
3360 int32_t index = formElement->IndexOfContent(aContent);
3362 if (index > -1) {
3363 KeyAppendInt(index, aKey);
3364 generatedUniqueKey = true;
3368 // Append the form name
3369 nsAutoString formName;
3370 formElement->GetAttr(nsGkAtoms::name, formName);
3371 KeyAppendString(formName, aKey);
3372 } else {
3373 // Not in a form. Append the control number, if this is a parser
3374 // inserted control, or the index of the control in the document
3375 // otherwise.
3376 if (parserInserted) {
3377 KeyAppendString("dp"_ns, aKey);
3378 KeyAppendInt(control->GetParserInsertedControlNumberForStateKey(),
3379 aKey);
3380 generatedUniqueKey = true;
3381 } else {
3382 KeyAppendString("dn"_ns, aKey);
3383 int32_t index = htmlFormControls->IndexOf(aContent, true);
3384 if (index > -1) {
3385 KeyAppendInt(index, aKey);
3386 generatedUniqueKey = true;
3390 // Append the control name
3391 nsAutoString name;
3392 aContent->AsElement()->GetAttr(nsGkAtoms::name, name);
3393 KeyAppendString(name, aKey);
3398 if (!generatedUniqueKey) {
3399 // Either we didn't have a form control or we aren't in an HTML document so
3400 // we can't figure out form info. Append the tag name if it's an element
3401 // to avoid restoring state for one type of element on another type.
3402 if (aContent->IsElement()) {
3403 KeyAppendString(nsDependentAtomString(aContent->NodeInfo()->NameAtom()),
3404 aKey);
3405 } else {
3406 // Append a character that is not "d" or "f" to disambiguate from
3407 // the case when we were a form control in an HTML document.
3408 KeyAppendString("o"_ns, aKey);
3411 // Now start at aContent and append the indices of it and all its ancestors
3412 // in their containers. That should at least pin down its position in the
3413 // DOM...
3414 nsINode* parent = aContent->GetParentNode();
3415 nsINode* content = aContent;
3416 while (parent) {
3417 KeyAppendInt(parent->ComputeIndexOf_Deprecated(content), aKey);
3418 content = parent;
3419 parent = content->GetParentNode();
3424 // static
3425 nsIPrincipal* nsContentUtils::SubjectPrincipal(JSContext* aCx) {
3426 MOZ_ASSERT(NS_IsMainThread());
3428 // As opposed to SubjectPrincipal(), we do in fact assume that
3429 // we're in a realm here; anyone who calls this function in
3430 // situations where that's not the case is doing it wrong.
3431 JS::Realm* realm = js::GetContextRealm(aCx);
3432 MOZ_ASSERT(realm);
3434 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3435 return nsJSPrincipals::get(principals);
3438 // static
3439 nsIPrincipal* nsContentUtils::SubjectPrincipal() {
3440 MOZ_ASSERT(IsInitialized());
3441 MOZ_ASSERT(NS_IsMainThread());
3442 JSContext* cx = GetCurrentJSContext();
3443 if (!cx) {
3444 MOZ_CRASH(
3445 "Accessing the Subject Principal without an AutoJSAPI on the stack is "
3446 "forbidden");
3449 JS::Realm* realm = js::GetContextRealm(cx);
3451 // When an AutoJSAPI is instantiated, we are in a null realm until the
3452 // first JSAutoRealm, which is kind of a purgatory as far as permissions
3453 // go. It would be nice to just hard-abort if somebody does a security check
3454 // in this purgatory zone, but that would be too fragile, since it could be
3455 // triggered by random IsCallerChrome() checks 20-levels deep.
3457 // So we want to return _something_ here - and definitely not the System
3458 // Principal, since that would make an AutoJSAPI a very dangerous thing to
3459 // instantiate.
3461 // The natural thing to return is a null principal. Ideally, we'd return a
3462 // different null principal each time, to avoid any unexpected interactions
3463 // when the principal accidentally gets inherited somewhere. But
3464 // SubjectPrincipal doesn't return strong references, so there's no way to
3465 // sanely manage the lifetime of multiple null principals.
3467 // So we use a singleton null principal. To avoid it being accidentally
3468 // inherited and becoming a "real" subject or object principal, we do a
3469 // release-mode assert during realm creation against using this principal on
3470 // an actual global.
3471 if (!realm) {
3472 return sNullSubjectPrincipal;
3475 return SubjectPrincipal(cx);
3478 // static
3479 nsIPrincipal* nsContentUtils::ObjectPrincipal(JSObject* aObj) {
3480 #ifdef DEBUG
3481 JS::AssertObjectBelongsToCurrentThread(aObj);
3482 #endif
3484 MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(aObj));
3486 JS::Realm* realm = js::GetNonCCWObjectRealm(aObj);
3487 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3488 return nsJSPrincipals::get(principals);
3491 // static
3492 nsresult nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
3493 const nsAString& aSpec,
3494 Document* aDocument,
3495 nsIURI* aBaseURI) {
3496 if (aDocument) {
3497 return NS_NewURI(aResult, aSpec, aDocument->GetDocumentCharacterSet(),
3498 aBaseURI);
3500 return NS_NewURI(aResult, aSpec, nullptr, aBaseURI);
3503 // static
3504 bool nsContentUtils::IsNameWithDash(nsAtom* aName) {
3505 // A valid custom element name is a sequence of characters name which
3506 // must match the PotentialCustomElementName production:
3507 // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
3508 const char16_t* name = aName->GetUTF16String();
3509 uint32_t len = aName->GetLength();
3510 bool hasDash = false;
3512 if (!len || name[0] < 'a' || name[0] > 'z') {
3513 return false;
3516 uint32_t i = 1;
3517 while (i < len) {
3518 if (i + 1 < len && NS_IS_SURROGATE_PAIR(name[i], name[i + 1])) {
3519 // Merged two 16-bit surrogate pairs into code point.
3520 char32_t code = SURROGATE_TO_UCS4(name[i], name[i + 1]);
3522 if (code < 0x10000 || code > 0xEFFFF) {
3523 return false;
3526 i += 2;
3527 } else {
3528 if (name[i] == '-') {
3529 hasDash = true;
3532 if (name[i] != '-' && name[i] != '.' && name[i] != '_' &&
3533 name[i] != 0xB7 && (name[i] < '0' || name[i] > '9') &&
3534 (name[i] < 'a' || name[i] > 'z') &&
3535 (name[i] < 0xC0 || name[i] > 0xD6) &&
3536 (name[i] < 0xF8 || name[i] > 0x37D) &&
3537 (name[i] < 0x37F || name[i] > 0x1FFF) &&
3538 (name[i] < 0x200C || name[i] > 0x200D) &&
3539 (name[i] < 0x203F || name[i] > 0x2040) &&
3540 (name[i] < 0x2070 || name[i] > 0x218F) &&
3541 (name[i] < 0x2C00 || name[i] > 0x2FEF) &&
3542 (name[i] < 0x3001 || name[i] > 0xD7FF) &&
3543 (name[i] < 0xF900 || name[i] > 0xFDCF) &&
3544 (name[i] < 0xFDF0 || name[i] > 0xFFFD)) {
3545 return false;
3548 i++;
3552 return hasDash;
3555 // static
3556 bool nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID) {
3557 // Allow non-dashed names in XUL for XBL to Custom Element migrations.
3558 if (aNameSpaceID == kNameSpaceID_XUL) {
3559 return true;
3562 bool hasDash = IsNameWithDash(aName);
3563 if (!hasDash) {
3564 return false;
3567 // The custom element name must not be one of the following values:
3568 // annotation-xml
3569 // color-profile
3570 // font-face
3571 // font-face-src
3572 // font-face-uri
3573 // font-face-format
3574 // font-face-name
3575 // missing-glyph
3576 return aName != nsGkAtoms::annotation_xml_ &&
3577 aName != nsGkAtoms::colorProfile && aName != nsGkAtoms::font_face &&
3578 aName != nsGkAtoms::font_face_src &&
3579 aName != nsGkAtoms::font_face_uri &&
3580 aName != nsGkAtoms::font_face_format &&
3581 aName != nsGkAtoms::font_face_name && aName != nsGkAtoms::missingGlyph;
3584 // static
3585 nsresult nsContentUtils::CheckQName(const nsAString& aQualifiedName,
3586 bool aNamespaceAware,
3587 const char16_t** aColon) {
3588 const char* colon = nullptr;
3589 const char16_t* begin = aQualifiedName.BeginReading();
3590 const char16_t* end = aQualifiedName.EndReading();
3592 int result = MOZ_XMLCheckQName(reinterpret_cast<const char*>(begin),
3593 reinterpret_cast<const char*>(end),
3594 aNamespaceAware, &colon);
3596 if (!result) {
3597 if (aColon) {
3598 *aColon = reinterpret_cast<const char16_t*>(colon);
3601 return NS_OK;
3604 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
3607 // static
3608 nsresult nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
3609 const nsString& aQName, int32_t* aNamespace,
3610 nsAtom** aLocalName) {
3611 const char16_t* colon;
3612 nsresult rv = nsContentUtils::CheckQName(aQName, true, &colon);
3613 NS_ENSURE_SUCCESS(rv, rv);
3615 if (colon) {
3616 const char16_t* end;
3617 aQName.EndReading(end);
3618 nsAutoString nameSpace;
3619 rv = aNamespaceResolver->LookupNamespaceURIInternal(
3620 Substring(aQName.get(), colon), nameSpace);
3621 NS_ENSURE_SUCCESS(rv, rv);
3623 *aNamespace = nsNameSpaceManager::GetInstance()->GetNameSpaceID(
3624 nameSpace, nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
3625 if (*aNamespace == kNameSpaceID_Unknown) return NS_ERROR_FAILURE;
3627 *aLocalName = NS_AtomizeMainThread(Substring(colon + 1, end)).take();
3628 } else {
3629 *aNamespace = kNameSpaceID_None;
3630 *aLocalName = NS_AtomizeMainThread(aQName).take();
3632 NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
3633 return NS_OK;
3636 // static
3637 nsresult nsContentUtils::GetNodeInfoFromQName(
3638 const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
3639 nsNodeInfoManager* aNodeInfoManager, uint16_t aNodeType,
3640 mozilla::dom::NodeInfo** aNodeInfo) {
3641 const nsString& qName = PromiseFlatString(aQualifiedName);
3642 const char16_t* colon;
3643 nsresult rv = nsContentUtils::CheckQName(qName, true, &colon);
3644 NS_ENSURE_SUCCESS(rv, rv);
3646 int32_t nsID;
3647 nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, nsID);
3648 if (colon) {
3649 const char16_t* end;
3650 qName.EndReading(end);
3652 RefPtr<nsAtom> prefix = NS_AtomizeMainThread(Substring(qName.get(), colon));
3654 rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix, nsID,
3655 aNodeType, aNodeInfo);
3656 } else {
3657 rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID, aNodeType,
3658 aNodeInfo);
3660 NS_ENSURE_SUCCESS(rv, rv);
3662 return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
3663 (*aNodeInfo)->GetPrefixAtom(),
3664 (*aNodeInfo)->NamespaceID())
3665 ? NS_OK
3666 : NS_ERROR_DOM_NAMESPACE_ERR;
3669 // static
3670 void nsContentUtils::SplitExpatName(const char16_t* aExpatName,
3671 nsAtom** aPrefix, nsAtom** aLocalName,
3672 int32_t* aNameSpaceID) {
3674 * Expat can send the following:
3675 * localName
3676 * namespaceURI<separator>localName
3677 * namespaceURI<separator>localName<separator>prefix
3679 * and we use 0xFFFF for the <separator>.
3683 const char16_t* uriEnd = nullptr;
3684 const char16_t* nameEnd = nullptr;
3685 const char16_t* pos;
3686 for (pos = aExpatName; *pos; ++pos) {
3687 if (*pos == 0xFFFF) {
3688 if (uriEnd) {
3689 nameEnd = pos;
3690 } else {
3691 uriEnd = pos;
3696 const char16_t* nameStart;
3697 if (uriEnd) {
3698 nsNameSpaceManager::GetInstance()->RegisterNameSpace(
3699 nsDependentSubstring(aExpatName, uriEnd), *aNameSpaceID);
3701 nameStart = (uriEnd + 1);
3702 if (nameEnd) {
3703 const char16_t* prefixStart = nameEnd + 1;
3704 *aPrefix = NS_AtomizeMainThread(Substring(prefixStart, pos)).take();
3705 } else {
3706 nameEnd = pos;
3707 *aPrefix = nullptr;
3709 } else {
3710 *aNameSpaceID = kNameSpaceID_None;
3711 nameStart = aExpatName;
3712 nameEnd = pos;
3713 *aPrefix = nullptr;
3715 *aLocalName = NS_AtomizeMainThread(Substring(nameStart, nameEnd)).take();
3718 // static
3719 PresShell* nsContentUtils::GetPresShellForContent(const nsIContent* aContent) {
3720 Document* doc = aContent->GetComposedDoc();
3721 if (!doc) {
3722 return nullptr;
3724 return doc->GetPresShell();
3727 // static
3728 nsPresContext* nsContentUtils::GetContextForContent(
3729 const nsIContent* aContent) {
3730 PresShell* presShell = GetPresShellForContent(aContent);
3731 if (!presShell) {
3732 return nullptr;
3734 return presShell->GetPresContext();
3737 // static
3738 bool nsContentUtils::CanLoadImage(nsIURI* aURI, nsINode* aNode,
3739 Document* aLoadingDocument,
3740 nsIPrincipal* aLoadingPrincipal) {
3741 MOZ_ASSERT(aURI, "Must have a URI");
3742 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3743 MOZ_ASSERT(aLoadingPrincipal, "Must have a loading principal");
3745 nsresult rv;
3747 auto appType = nsIDocShell::APP_TYPE_UNKNOWN;
3750 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
3751 aLoadingDocument->GetDocShell();
3752 if (docShellTreeItem) {
3753 nsCOMPtr<nsIDocShellTreeItem> root;
3754 docShellTreeItem->GetInProcessRootTreeItem(getter_AddRefs(root));
3756 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
3758 if (docShell) {
3759 appType = docShell->GetAppType();
3764 if (appType != nsIDocShell::APP_TYPE_EDITOR) {
3765 // Editor apps get special treatment here, editors can load images
3766 // from anywhere. This allows editor to insert images from file://
3767 // into documents that are being edited.
3768 rv = sSecurityManager->CheckLoadURIWithPrincipal(
3769 aLoadingPrincipal, aURI, nsIScriptSecurityManager::ALLOW_CHROME,
3770 aLoadingDocument->InnerWindowID());
3771 if (NS_FAILED(rv)) {
3772 return false;
3776 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new mozilla::net::LoadInfo(
3777 aLoadingPrincipal,
3778 aLoadingPrincipal, // triggering principal
3779 aNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
3780 nsIContentPolicy::TYPE_INTERNAL_IMAGE);
3782 int16_t decision = nsIContentPolicy::ACCEPT;
3784 rv = NS_CheckContentLoadPolicy(aURI, secCheckLoadInfo,
3785 ""_ns, // mime guess
3786 &decision, GetContentPolicy());
3788 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(decision);
3791 // static
3792 bool nsContentUtils::IsInPrivateBrowsing(Document* aDoc) {
3793 if (!aDoc) {
3794 return false;
3797 nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
3798 if (loadGroup) {
3799 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3800 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3801 if (callbacks) {
3802 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3803 if (loadContext) {
3804 return loadContext->UsePrivateBrowsing();
3809 nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
3810 return channel && NS_UsePrivateBrowsing(channel);
3813 // static
3814 bool nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup) {
3815 if (!aLoadGroup) {
3816 return false;
3818 bool isPrivate = false;
3819 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3820 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3821 if (callbacks) {
3822 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3823 isPrivate = loadContext && loadContext->UsePrivateBrowsing();
3825 return isPrivate;
3828 // FIXME(emilio): This is (effectively) almost but not quite the same as
3829 // Document::ShouldLoadImages(), which one is right?
3830 bool nsContentUtils::DocumentInactiveForImageLoads(Document* aDocument) {
3831 if (!aDocument) {
3832 return false;
3834 if (IsChromeDoc(aDocument) || aDocument->IsResourceDoc() ||
3835 aDocument->IsStaticDocument()) {
3836 return false;
3838 nsCOMPtr<nsPIDOMWindowInner> win =
3839 do_QueryInterface(aDocument->GetScopeObject());
3840 return !win || !win->GetDocShell();
3843 imgLoader* nsContentUtils::GetImgLoaderForDocument(Document* aDoc) {
3844 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
3846 if (!aDoc) {
3847 return imgLoader::NormalLoader();
3849 bool isPrivate = IsInPrivateBrowsing(aDoc);
3850 return isPrivate ? imgLoader::PrivateBrowsingLoader()
3851 : imgLoader::NormalLoader();
3854 // static
3855 imgLoader* nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
3856 Document* aContext) {
3857 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
3859 if (!aChannel) {
3860 return imgLoader::NormalLoader();
3862 nsCOMPtr<nsILoadContext> context;
3863 NS_QueryNotificationCallbacks(aChannel, context);
3864 return context && context->UsePrivateBrowsing()
3865 ? imgLoader::PrivateBrowsingLoader()
3866 : imgLoader::NormalLoader();
3869 // static
3870 int32_t nsContentUtils::CORSModeToLoadImageFlags(mozilla::CORSMode aMode) {
3871 switch (aMode) {
3872 case CORS_ANONYMOUS:
3873 return imgILoader::LOAD_CORS_ANONYMOUS;
3874 case CORS_USE_CREDENTIALS:
3875 return imgILoader::LOAD_CORS_USE_CREDENTIALS;
3876 default:
3877 return 0;
3881 // static
3882 nsresult nsContentUtils::LoadImage(
3883 nsIURI* aURI, nsINode* aContext, Document* aLoadingDocument,
3884 nsIPrincipal* aLoadingPrincipal, uint64_t aRequestContextID,
3885 nsIReferrerInfo* aReferrerInfo, imgINotificationObserver* aObserver,
3886 int32_t aLoadFlags, const nsAString& initiatorType,
3887 imgRequestProxy** aRequest, nsContentPolicyType aContentPolicyType,
3888 bool aUseUrgentStartForChannel, bool aLinkPreload,
3889 uint64_t aEarlyHintPreloaderId) {
3890 MOZ_ASSERT(aURI, "Must have a URI");
3891 MOZ_ASSERT(aContext, "Must have a context");
3892 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3893 MOZ_ASSERT(aLoadingPrincipal, "Must have a principal");
3894 MOZ_ASSERT(aRequest, "Null out param");
3896 imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
3897 if (!imgLoader) {
3898 // nothing we can do here
3899 return NS_ERROR_FAILURE;
3902 nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
3904 nsIURI* documentURI = aLoadingDocument->GetDocumentURI();
3906 NS_ASSERTION(loadGroup || aLoadingDocument->IsSVGGlyphsDocument(),
3907 "Could not get loadgroup; onload may fire too early");
3909 // XXXbz using "documentURI" for the initialDocumentURI is not quite
3910 // right, but the best we can do here...
3911 return imgLoader->LoadImage(aURI, /* uri to load */
3912 documentURI, /* initialDocumentURI */
3913 aReferrerInfo, /* referrerInfo */
3914 aLoadingPrincipal, /* loading principal */
3915 aRequestContextID, /* request context ID */
3916 loadGroup, /* loadgroup */
3917 aObserver, /* imgINotificationObserver */
3918 aContext, /* loading context */
3919 aLoadingDocument, /* uniquification key */
3920 aLoadFlags, /* load flags */
3921 nullptr, /* cache key */
3922 aContentPolicyType, /* content policy type */
3923 initiatorType, /* the load initiator */
3924 aUseUrgentStartForChannel, /* urgent-start flag */
3925 aLinkPreload, /* <link preload> initiator */
3926 aEarlyHintPreloaderId, aRequest);
3929 // static
3930 already_AddRefed<imgIContainer> nsContentUtils::GetImageFromContent(
3931 nsIImageLoadingContent* aContent, imgIRequest** aRequest) {
3932 if (aRequest) {
3933 *aRequest = nullptr;
3936 NS_ENSURE_TRUE(aContent, nullptr);
3938 nsCOMPtr<imgIRequest> imgRequest;
3939 aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3940 getter_AddRefs(imgRequest));
3941 if (!imgRequest) {
3942 return nullptr;
3945 nsCOMPtr<imgIContainer> imgContainer;
3946 imgRequest->GetImage(getter_AddRefs(imgContainer));
3948 if (!imgContainer) {
3949 return nullptr;
3952 if (aRequest) {
3953 // If the consumer wants the request, verify it has actually loaded
3954 // successfully.
3955 uint32_t imgStatus;
3956 imgRequest->GetImageStatus(&imgStatus);
3957 if (imgStatus & imgIRequest::STATUS_FRAME_COMPLETE &&
3958 !(imgStatus & imgIRequest::STATUS_ERROR)) {
3959 imgRequest.swap(*aRequest);
3963 return imgContainer.forget();
3966 static bool IsLinkWithURI(const nsIContent& aContent) {
3967 const auto* element = Element::FromNode(aContent);
3968 if (!element || !element->IsLink()) {
3969 return false;
3971 nsCOMPtr<nsIURI> absURI = element->GetHrefURI();
3972 return !!absURI;
3975 static bool HasImageRequest(nsIContent& aContent) {
3976 nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(&aContent));
3977 if (!imageContent) {
3978 return false;
3981 nsCOMPtr<imgIRequest> imgRequest;
3982 imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3983 getter_AddRefs(imgRequest));
3985 // XXXbz It may be draggable even if the request resulted in an error. Why?
3986 // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
3987 return !!imgRequest;
3990 static Maybe<bool> DraggableOverride(const nsIContent& aContent) {
3991 if (auto* el = nsGenericHTMLElement::FromNode(aContent)) {
3992 if (el->Draggable()) {
3993 return Some(true);
3996 if (el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
3997 nsGkAtoms::_false, eIgnoreCase)) {
3998 return Some(false);
4001 if (aContent.IsSVGElement()) {
4002 return Some(false);
4004 return Nothing();
4007 // static
4008 bool nsContentUtils::ContentIsDraggable(nsIContent* aContent) {
4009 MOZ_ASSERT(aContent);
4011 if (auto draggable = DraggableOverride(*aContent)) {
4012 return *draggable;
4015 // special handling for content area image and link dragging
4016 return HasImageRequest(*aContent) || IsLinkWithURI(*aContent);
4019 // static
4020 bool nsContentUtils::IsDraggableImage(nsIContent* aContent) {
4021 MOZ_ASSERT(aContent);
4022 return HasImageRequest(*aContent) &&
4023 DraggableOverride(*aContent).valueOr(true);
4026 // static
4027 bool nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
4028 MOZ_ASSERT(aContent);
4029 return IsLinkWithURI(*aContent) && DraggableOverride(*aContent).valueOr(true);
4032 // static
4033 nsresult nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo,
4034 nsAtom* aName,
4035 mozilla::dom::NodeInfo** aResult) {
4036 nsNodeInfoManager* niMgr = aNodeInfo->NodeInfoManager();
4038 *aResult = niMgr
4039 ->GetNodeInfo(aName, nullptr, aNodeInfo->NamespaceID(),
4040 aNodeInfo->NodeType(), aNodeInfo->GetExtraName())
4041 .take();
4042 return NS_OK;
4045 static bool TestSitePerm(nsIPrincipal* aPrincipal, const nsACString& aType,
4046 uint32_t aPerm, bool aExactHostMatch) {
4047 if (!aPrincipal) {
4048 // We always deny (i.e. don't allow) the permission if we don't have a
4049 // principal.
4050 return aPerm != nsIPermissionManager::ALLOW_ACTION;
4053 nsCOMPtr<nsIPermissionManager> permMgr =
4054 components::PermissionManager::Service();
4055 NS_ENSURE_TRUE(permMgr, false);
4057 uint32_t perm;
4058 nsresult rv;
4059 if (aExactHostMatch) {
4060 rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
4061 } else {
4062 rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
4064 NS_ENSURE_SUCCESS(rv, false);
4066 return perm == aPerm;
4069 bool nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal,
4070 const nsACString& aType) {
4071 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
4072 false);
4075 bool nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal,
4076 const nsACString& aType) {
4077 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
4078 false);
4081 bool nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal,
4082 const nsACString& aType) {
4083 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
4084 true);
4087 bool nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal,
4088 const nsACString& aType) {
4089 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
4090 true);
4093 bool nsContentUtils::HasSitePerm(nsIPrincipal* aPrincipal,
4094 const nsACString& aType) {
4095 if (!aPrincipal) {
4096 return false;
4099 nsCOMPtr<nsIPermissionManager> permMgr =
4100 components::PermissionManager::Service();
4101 NS_ENSURE_TRUE(permMgr, false);
4103 uint32_t perm;
4104 nsresult rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
4105 NS_ENSURE_SUCCESS(rv, false);
4107 return perm != nsIPermissionManager::UNKNOWN_ACTION;
4110 static const char* gEventNames[] = {"event"};
4111 static const char* gSVGEventNames[] = {"evt"};
4112 // for b/w compat, the first name to onerror is still 'event', even though it
4113 // is actually the error message
4114 static const char* gOnErrorNames[] = {"event", "source", "lineno", "colno",
4115 "error"};
4117 // static
4118 void nsContentUtils::GetEventArgNames(int32_t aNameSpaceID, nsAtom* aEventName,
4119 bool aIsForWindow, uint32_t* aArgCount,
4120 const char*** aArgArray) {
4121 #define SET_EVENT_ARG_NAMES(names) \
4122 *aArgCount = sizeof(names) / sizeof(names[0]); \
4123 *aArgArray = names;
4125 // JSEventHandler is what does the arg magic for onerror, and it does
4126 // not seem to take the namespace into account. So we let onerror in all
4127 // namespaces get the 3 arg names.
4128 if (aEventName == nsGkAtoms::onerror && aIsForWindow) {
4129 SET_EVENT_ARG_NAMES(gOnErrorNames);
4130 } else if (aNameSpaceID == kNameSpaceID_SVG) {
4131 SET_EVENT_ARG_NAMES(gSVGEventNames);
4132 } else {
4133 SET_EVENT_ARG_NAMES(gEventNames);
4137 // Note: The list of content bundles in nsStringBundle.cpp should be updated
4138 // whenever entries are added or removed from this list.
4139 static const char* gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT] = {
4140 // Must line up with the enum values in |PropertiesFile| enum.
4141 "chrome://global/locale/css.properties",
4142 "chrome://global/locale/xul.properties",
4143 "chrome://global/locale/layout_errors.properties",
4144 "chrome://global/locale/layout/HtmlForm.properties",
4145 "chrome://global/locale/printing.properties",
4146 "chrome://global/locale/dom/dom.properties",
4147 "chrome://global/locale/layout/htmlparser.properties",
4148 "chrome://global/locale/svg/svg.properties",
4149 "chrome://branding/locale/brand.properties",
4150 "chrome://global/locale/commonDialogs.properties",
4151 "chrome://global/locale/mathml/mathml.properties",
4152 "chrome://global/locale/security/security.properties",
4153 "chrome://necko/locale/necko.properties",
4154 "resource://gre/res/locale/layout/HtmlForm.properties",
4155 "resource://gre/res/locale/dom/dom.properties"};
4157 /* static */
4158 nsresult nsContentUtils::EnsureStringBundle(PropertiesFile aFile) {
4159 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(),
4160 "Should not create bundles off main thread.");
4161 if (!sStringBundles[aFile]) {
4162 if (!sStringBundleService) {
4163 nsresult rv =
4164 CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
4165 NS_ENSURE_SUCCESS(rv, rv);
4167 RefPtr<nsIStringBundle> bundle;
4168 MOZ_TRY(sStringBundleService->CreateBundle(gPropertiesFiles[aFile],
4169 getter_AddRefs(bundle)));
4170 sStringBundles[aFile] = bundle.forget();
4172 return NS_OK;
4175 /* static */
4176 void nsContentUtils::AsyncPrecreateStringBundles() {
4177 // We only ever want to pre-create bundles in the parent process.
4179 // All nsContentUtils bundles are shared between the parent and child
4180 // precesses, and the shared memory regions that back them *must* be created
4181 // in the parent, and then sent to all children.
4183 // If we attempt to create a bundle in the child before its memory region is
4184 // available, we need to create a temporary non-shared bundle, and later
4185 // replace that with the shared memory copy. So attempting to pre-load in the
4186 // child is wasteful and unnecessary.
4187 MOZ_ASSERT(XRE_IsParentProcess());
4189 for (uint32_t bundleIndex = 0; bundleIndex < PropertiesFile_COUNT;
4190 ++bundleIndex) {
4191 nsresult rv = NS_DispatchToCurrentThreadQueue(
4192 NS_NewRunnableFunction("AsyncPrecreateStringBundles",
4193 [bundleIndex]() {
4194 PropertiesFile file =
4195 static_cast<PropertiesFile>(bundleIndex);
4196 EnsureStringBundle(file);
4197 nsIStringBundle* bundle = sStringBundles[file];
4198 bundle->AsyncPreload();
4200 EventQueuePriority::Idle);
4201 Unused << NS_WARN_IF(NS_FAILED(rv));
4205 /* static */
4206 bool nsContentUtils::SpoofLocaleEnglish() {
4207 // 0 - will prompt
4208 // 1 - don't spoof
4209 // 2 - spoof
4210 return StaticPrefs::privacy_spoof_english() == 2;
4213 static nsContentUtils::PropertiesFile GetMaybeSpoofedPropertiesFile(
4214 nsContentUtils::PropertiesFile aFile, const char* aKey,
4215 Document* aDocument) {
4216 // When we spoof English, use en-US properties in strings that are accessible
4217 // by content.
4218 bool spoofLocale = nsContentUtils::SpoofLocaleEnglish() &&
4219 (!aDocument || !aDocument->AllowsL10n());
4220 if (spoofLocale) {
4221 switch (aFile) {
4222 case nsContentUtils::eFORMS_PROPERTIES:
4223 return nsContentUtils::eFORMS_PROPERTIES_en_US;
4224 case nsContentUtils::eDOM_PROPERTIES:
4225 return nsContentUtils::eDOM_PROPERTIES_en_US;
4226 default:
4227 break;
4230 return aFile;
4233 /* static */
4234 nsresult nsContentUtils::GetMaybeLocalizedString(PropertiesFile aFile,
4235 const char* aKey,
4236 Document* aDocument,
4237 nsAString& aResult) {
4238 return GetLocalizedString(
4239 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aResult);
4242 /* static */
4243 nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
4244 const char* aKey,
4245 nsAString& aResult) {
4246 return FormatLocalizedString(aFile, aKey, {}, aResult);
4249 /* static */
4250 nsresult nsContentUtils::FormatMaybeLocalizedString(
4251 PropertiesFile aFile, const char* aKey, Document* aDocument,
4252 const nsTArray<nsString>& aParams, nsAString& aResult) {
4253 return FormatLocalizedString(
4254 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aParams,
4255 aResult);
4258 class FormatLocalizedStringRunnable final : public WorkerMainThreadRunnable {
4259 public:
4260 FormatLocalizedStringRunnable(WorkerPrivate* aWorkerPrivate,
4261 nsContentUtils::PropertiesFile aFile,
4262 const char* aKey,
4263 const nsTArray<nsString>& aParams,
4264 nsAString& aLocalizedString)
4265 : WorkerMainThreadRunnable(aWorkerPrivate,
4266 "FormatLocalizedStringRunnable"_ns),
4267 mFile(aFile),
4268 mKey(aKey),
4269 mParams(aParams),
4270 mLocalizedString(aLocalizedString) {
4271 MOZ_ASSERT(aWorkerPrivate);
4272 aWorkerPrivate->AssertIsOnWorkerThread();
4275 bool MainThreadRun() override {
4276 AssertIsOnMainThread();
4278 mResult = nsContentUtils::FormatLocalizedString(mFile, mKey, mParams,
4279 mLocalizedString);
4280 Unused << NS_WARN_IF(NS_FAILED(mResult));
4281 return true;
4284 nsresult GetResult() const { return mResult; }
4286 private:
4287 const nsContentUtils::PropertiesFile mFile;
4288 const char* mKey;
4289 const nsTArray<nsString>& mParams;
4290 nsresult mResult = NS_ERROR_FAILURE;
4291 nsAString& mLocalizedString;
4294 /* static */
4295 nsresult nsContentUtils::FormatLocalizedString(
4296 PropertiesFile aFile, const char* aKey, const nsTArray<nsString>& aParams,
4297 nsAString& aResult) {
4298 if (!NS_IsMainThread()) {
4299 // nsIStringBundle is thread-safe but its creation is not, and in particular
4300 // we don't create and store nsIStringBundle objects in a thread-safe way.
4302 // TODO(emilio): Maybe if we already have the right bundle created we could
4303 // just call into it, but we should make sure that Shutdown() doesn't get
4304 // called on the main thread when that happens which is a bit tricky to
4305 // prove?
4306 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
4307 if (NS_WARN_IF(!workerPrivate)) {
4308 return NS_ERROR_UNEXPECTED;
4311 auto runnable = MakeRefPtr<FormatLocalizedStringRunnable>(
4312 workerPrivate, aFile, aKey, aParams, aResult);
4314 runnable->Dispatch(Canceling, IgnoreErrors());
4315 return runnable->GetResult();
4318 MOZ_TRY(EnsureStringBundle(aFile));
4319 nsIStringBundle* bundle = sStringBundles[aFile];
4320 if (aParams.IsEmpty()) {
4321 return bundle->GetStringFromName(aKey, aResult);
4323 return bundle->FormatStringFromName(aKey, aParams, aResult);
4326 /* static */
4327 void nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
4328 const nsACString& aCategory,
4329 bool aFromPrivateWindow,
4330 bool aFromChromeContext,
4331 uint32_t aErrorFlags) {
4332 nsCOMPtr<nsIScriptError> scriptError =
4333 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
4334 if (scriptError) {
4335 nsCOMPtr<nsIConsoleService> console =
4336 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
4337 if (console && NS_SUCCEEDED(scriptError->Init(
4338 aErrorText, u""_ns, u""_ns, 0, 0, aErrorFlags, aCategory,
4339 aFromPrivateWindow, aFromChromeContext))) {
4340 console->LogMessage(scriptError);
4345 /* static */
4346 nsresult nsContentUtils::ReportToConsole(
4347 uint32_t aErrorFlags, const nsACString& aCategory,
4348 const Document* aDocument, PropertiesFile aFile, const char* aMessageName,
4349 const nsTArray<nsString>& aParams, nsIURI* aURI,
4350 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber) {
4351 nsresult rv;
4352 nsAutoString errorText;
4353 if (!aParams.IsEmpty()) {
4354 rv = FormatLocalizedString(aFile, aMessageName, aParams, errorText);
4355 } else {
4356 rv = GetLocalizedString(aFile, aMessageName, errorText);
4358 NS_ENSURE_SUCCESS(rv, rv);
4360 return ReportToConsoleNonLocalized(errorText, aErrorFlags, aCategory,
4361 aDocument, aURI, aSourceLine, aLineNumber,
4362 aColumnNumber);
4365 /* static */
4366 void nsContentUtils::ReportEmptyGetElementByIdArg(const Document* aDoc) {
4367 ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, aDoc,
4368 nsContentUtils::eDOM_PROPERTIES, "EmptyGetElementByIdParam");
4371 /* static */
4372 nsresult nsContentUtils::ReportToConsoleNonLocalized(
4373 const nsAString& aErrorText, uint32_t aErrorFlags,
4374 const nsACString& aCategory, const Document* aDocument, nsIURI* aURI,
4375 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4376 MissingErrorLocationMode aLocationMode) {
4377 uint64_t innerWindowID = 0;
4378 if (aDocument) {
4379 if (!aURI) {
4380 aURI = aDocument->GetDocumentURI();
4382 innerWindowID = aDocument->InnerWindowID();
4385 return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
4386 innerWindowID, aURI, aSourceLine,
4387 aLineNumber, aColumnNumber, aLocationMode);
4390 /* static */
4391 nsresult nsContentUtils::ReportToConsoleByWindowID(
4392 const nsAString& aErrorText, uint32_t aErrorFlags,
4393 const nsACString& aCategory, uint64_t aInnerWindowID, nsIURI* aURI,
4394 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4395 MissingErrorLocationMode aLocationMode) {
4396 nsresult rv;
4397 if (!sConsoleService) { // only need to bother null-checking here
4398 rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4399 NS_ENSURE_SUCCESS(rv, rv);
4402 nsAutoString spec;
4403 if (!aLineNumber && aLocationMode == eUSE_CALLING_LOCATION) {
4404 JSContext* cx = GetCurrentJSContext();
4405 if (cx) {
4406 nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber, &aColumnNumber);
4410 nsCOMPtr<nsIScriptError> errorObject =
4411 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
4412 NS_ENSURE_SUCCESS(rv, rv);
4414 if (!spec.IsEmpty()) {
4415 rv = errorObject->InitWithWindowID(aErrorText,
4416 spec, // file name
4417 aSourceLine, aLineNumber, aColumnNumber,
4418 aErrorFlags, aCategory, aInnerWindowID);
4419 } else {
4420 rv = errorObject->InitWithSourceURI(aErrorText, aURI, aSourceLine,
4421 aLineNumber, aColumnNumber, aErrorFlags,
4422 aCategory, aInnerWindowID);
4424 NS_ENSURE_SUCCESS(rv, rv);
4426 return sConsoleService->LogMessage(errorObject);
4429 void nsContentUtils::LogMessageToConsole(const char* aMsg) {
4430 if (!sConsoleService) { // only need to bother null-checking here
4431 CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4432 if (!sConsoleService) {
4433 return;
4436 sConsoleService->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
4439 bool nsContentUtils::IsChromeDoc(const Document* aDocument) {
4440 return aDocument && aDocument->NodePrincipal() == sSystemPrincipal;
4443 bool nsContentUtils::IsChildOfSameType(Document* aDoc) {
4444 if (BrowsingContext* bc = aDoc->GetBrowsingContext()) {
4445 return bc->GetParent();
4447 return false;
4450 bool nsContentUtils::IsPlainTextType(const nsACString& aContentType) {
4451 // NOTE: if you add a type here, add it to the CONTENTDLF_CATEGORIES
4452 // define in nsContentDLF.h as well.
4453 return aContentType.EqualsLiteral(TEXT_PLAIN) ||
4454 aContentType.EqualsLiteral(TEXT_CSS) ||
4455 aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4456 aContentType.EqualsLiteral(TEXT_VTT) ||
4457 aContentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
4458 aContentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
4459 aContentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
4460 aContentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
4461 aContentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
4462 aContentType.EqualsLiteral(APPLICATION_JSON) ||
4463 aContentType.EqualsLiteral(TEXT_JSON);
4466 bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) {
4467 // NOTE: This must be a subset of the list in IsPlainTextType().
4468 return aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4469 aContentType.EqualsLiteral(APPLICATION_JSON) ||
4470 aContentType.EqualsLiteral(TEXT_JSON) ||
4471 aContentType.EqualsLiteral(TEXT_VTT);
4474 bool nsContentUtils::IsInChromeDocshell(const Document* aDocument) {
4475 return aDocument && aDocument->IsInChromeDocShell();
4478 // static
4479 nsIContentPolicy* nsContentUtils::GetContentPolicy() {
4480 if (!sTriedToGetContentPolicy) {
4481 CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
4482 // It's OK to not have a content policy service
4483 sTriedToGetContentPolicy = true;
4486 return sContentPolicyService;
4489 // static
4490 bool nsContentUtils::IsEventAttributeName(nsAtom* aName, int32_t aType) {
4491 const char16_t* name = aName->GetUTF16String();
4492 if (name[0] != 'o' || name[1] != 'n') {
4493 return false;
4496 EventNameMapping mapping;
4497 return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
4500 // static
4501 EventMessage nsContentUtils::GetEventMessage(nsAtom* aName) {
4502 MOZ_ASSERT(NS_IsMainThread(), "sAtomEventTable is not threadsafe");
4503 if (aName) {
4504 EventNameMapping mapping;
4505 if (sAtomEventTable->Get(aName, &mapping)) {
4506 return mapping.mMessage;
4510 return eUnidentifiedEvent;
4513 // static
4514 mozilla::EventClassID nsContentUtils::GetEventClassID(const nsAString& aName) {
4515 EventNameMapping mapping;
4516 if (sStringEventTable->Get(aName, &mapping)) return mapping.mEventClassID;
4518 return eBasicEventClass;
4521 nsAtom* nsContentUtils::GetEventMessageAndAtom(
4522 const nsAString& aName, mozilla::EventClassID aEventClassID,
4523 EventMessage* aEventMessage) {
4524 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4525 EventNameMapping mapping;
4526 if (sStringEventTable->Get(aName, &mapping)) {
4527 *aEventMessage = mapping.mEventClassID == aEventClassID
4528 ? mapping.mMessage
4529 : eUnidentifiedEvent;
4530 return mapping.mAtom;
4533 // If we have cached lots of user defined event names, clear some of them.
4534 if (sUserDefinedEvents->Length() > 127) {
4535 while (sUserDefinedEvents->Length() > 64) {
4536 nsAtom* first = sUserDefinedEvents->ElementAt(0);
4537 sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
4538 sUserDefinedEvents->RemoveElementAt(0);
4542 *aEventMessage = eUnidentifiedEvent;
4543 RefPtr<nsAtom> atom = NS_AtomizeMainThread(u"on"_ns + aName);
4544 sUserDefinedEvents->AppendElement(atom);
4545 mapping.mAtom = atom;
4546 mapping.mMessage = eUnidentifiedEvent;
4547 mapping.mType = EventNameType_None;
4548 mapping.mEventClassID = eBasicEventClass;
4549 sStringEventTable->InsertOrUpdate(aName, mapping);
4550 return mapping.mAtom;
4553 // static
4554 EventMessage nsContentUtils::GetEventMessageAndAtomForListener(
4555 const nsAString& aName, nsAtom** aOnName) {
4556 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4558 // Check sStringEventTable for a matching entry. This will only fail for
4559 // user-defined event types.
4560 EventNameMapping mapping;
4561 if (sStringEventTable->Get(aName, &mapping)) {
4562 RefPtr<nsAtom> atom = mapping.mAtom;
4563 atom.forget(aOnName);
4564 return mapping.mMessage;
4567 // sStringEventTable did not contain an entry for this event type string.
4568 // Call GetEventMessageAndAtom, which will create an event type atom and
4569 // cache it in sStringEventTable for future calls.
4570 EventMessage msg = eUnidentifiedEvent;
4571 RefPtr<nsAtom> atom = GetEventMessageAndAtom(aName, eBasicEventClass, &msg);
4572 atom.forget(aOnName);
4573 return msg;
4576 static already_AddRefed<Event> GetEventWithTarget(
4577 Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
4578 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4579 Trusted aTrusted, ErrorResult& aErrorResult) {
4580 RefPtr<Event> event =
4581 aDoc->CreateEvent(u"Events"_ns, CallerType::System, aErrorResult);
4582 if (aErrorResult.Failed()) {
4583 return nullptr;
4586 event->InitEvent(aEventName, aCanBubble, aCancelable, aComposed);
4587 event->SetTrusted(aTrusted == Trusted::eYes);
4589 event->SetTarget(aTarget);
4591 return event.forget();
4594 // static
4595 nsresult nsContentUtils::DispatchTrustedEvent(
4596 Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
4597 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4598 bool* aDefaultAction) {
4599 MOZ_ASSERT(!aEventName.EqualsLiteral("input") &&
4600 !aEventName.EqualsLiteral("beforeinput"),
4601 "Use DispatchInputEvent() instead");
4602 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4603 aComposed, Trusted::eYes, aDefaultAction);
4606 // static
4607 nsresult nsContentUtils::DispatchUntrustedEvent(
4608 Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
4609 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4610 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4611 Composed::eDefault, Trusted::eNo, aDefaultAction);
4614 // static
4615 nsresult nsContentUtils::DispatchEvent(Document* aDoc, EventTarget* aTarget,
4616 const nsAString& aEventName,
4617 CanBubble aCanBubble,
4618 Cancelable aCancelable,
4619 Composed aComposed, Trusted aTrusted,
4620 bool* aDefaultAction,
4621 ChromeOnlyDispatch aOnlyChromeDispatch) {
4622 if (!aDoc || !aTarget) {
4623 return NS_ERROR_INVALID_ARG;
4626 ErrorResult err;
4627 RefPtr<Event> event =
4628 GetEventWithTarget(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4629 aComposed, aTrusted, err);
4630 if (err.Failed()) {
4631 return err.StealNSResult();
4633 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch =
4634 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4636 bool doDefault = aTarget->DispatchEvent(*event, CallerType::System, err);
4637 if (aDefaultAction) {
4638 *aDefaultAction = doDefault;
4640 return err.StealNSResult();
4643 // static
4644 nsresult nsContentUtils::DispatchEvent(Document* aDoc, EventTarget* aTarget,
4645 WidgetEvent& aEvent,
4646 EventMessage aEventMessage,
4647 CanBubble aCanBubble,
4648 Cancelable aCancelable, Trusted aTrusted,
4649 bool* aDefaultAction,
4650 ChromeOnlyDispatch aOnlyChromeDispatch) {
4651 MOZ_ASSERT_IF(aOnlyChromeDispatch == ChromeOnlyDispatch::eYes,
4652 aTrusted == Trusted::eYes);
4654 aEvent.mSpecifiedEventType = GetEventTypeFromMessage(aEventMessage);
4655 aEvent.SetDefaultComposed();
4656 aEvent.SetDefaultComposedInNativeAnonymousContent();
4658 aEvent.mFlags.mBubbles = aCanBubble == CanBubble::eYes;
4659 aEvent.mFlags.mCancelable = aCancelable == Cancelable::eYes;
4660 aEvent.mFlags.mOnlyChromeDispatch =
4661 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4663 aEvent.mTarget = aTarget;
4665 nsEventStatus status = nsEventStatus_eIgnore;
4666 nsresult rv = EventDispatcher::DispatchDOMEvent(aTarget, &aEvent, nullptr,
4667 nullptr, &status);
4668 if (aDefaultAction) {
4669 *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
4671 return rv;
4674 // static
4675 nsresult nsContentUtils::DispatchInputEvent(Element* aEventTarget) {
4676 return DispatchInputEvent(aEventTarget, mozilla::eEditorInput,
4677 mozilla::EditorInputType::eUnknown, nullptr,
4678 InputEventOptions());
4681 // static
4682 nsresult nsContentUtils::DispatchInputEvent(
4683 Element* aEventTargetElement, EventMessage aEventMessage,
4684 EditorInputType aEditorInputType, EditorBase* aEditorBase,
4685 InputEventOptions&& aOptions, nsEventStatus* aEventStatus /* = nullptr */) {
4686 MOZ_ASSERT(aEventMessage == eEditorInput ||
4687 aEventMessage == eEditorBeforeInput);
4689 if (NS_WARN_IF(!aEventTargetElement)) {
4690 return NS_ERROR_INVALID_ARG;
4693 // If this is called from editor, the instance should be set to aEditorBase.
4694 // Otherwise, we need to look for an editor for aEventTargetElement.
4695 // However, we don't need to do it for HTMLEditor since nobody shouldn't
4696 // dispatch "beforeinput" nor "input" event for HTMLEditor except HTMLEditor
4697 // itself.
4698 bool useInputEvent = false;
4699 if (aEditorBase) {
4700 useInputEvent = true;
4701 } else if (HTMLTextAreaElement* textAreaElement =
4702 HTMLTextAreaElement::FromNode(aEventTargetElement)) {
4703 aEditorBase = textAreaElement->GetTextEditorWithoutCreation();
4704 useInputEvent = true;
4705 } else if (HTMLInputElement* inputElement =
4706 HTMLInputElement::FromNode(aEventTargetElement)) {
4707 if (inputElement->IsInputEventTarget()) {
4708 aEditorBase = inputElement->GetTextEditorWithoutCreation();
4709 useInputEvent = true;
4712 #ifdef DEBUG
4713 else {
4714 MOZ_ASSERT(!aEventTargetElement->IsTextControlElement(),
4715 "The event target may have editor, but we've not known it yet.");
4717 #endif // #ifdef DEBUG
4719 if (!useInputEvent) {
4720 MOZ_ASSERT(aEventMessage == eEditorInput);
4721 MOZ_ASSERT(aEditorInputType == EditorInputType::eUnknown);
4722 MOZ_ASSERT(!aOptions.mNeverCancelable);
4723 // Dispatch "input" event with Event instance.
4724 WidgetEvent widgetEvent(true, eUnidentifiedEvent);
4725 widgetEvent.mSpecifiedEventType = nsGkAtoms::oninput;
4726 widgetEvent.mFlags.mCancelable = false;
4727 widgetEvent.mFlags.mComposed = true;
4728 return AsyncEventDispatcher::RunDOMEventWhenSafe(*aEventTargetElement,
4729 widgetEvent, aEventStatus);
4732 MOZ_ASSERT_IF(aEventMessage != eEditorBeforeInput,
4733 !aOptions.mNeverCancelable);
4734 MOZ_ASSERT_IF(
4735 aEventMessage == eEditorBeforeInput && aOptions.mNeverCancelable,
4736 aEditorInputType == EditorInputType::eInsertReplacementText);
4738 nsCOMPtr<nsIWidget> widget;
4739 if (aEditorBase) {
4740 widget = aEditorBase->GetWidget();
4741 if (NS_WARN_IF(!widget)) {
4742 return NS_ERROR_FAILURE;
4744 } else {
4745 Document* document = aEventTargetElement->OwnerDoc();
4746 if (NS_WARN_IF(!document)) {
4747 return NS_ERROR_FAILURE;
4749 // If we're running xpcshell tests, we fail to get presShell here.
4750 // Even in such case, we need to dispatch "input" event without widget.
4751 PresShell* presShell = document->GetPresShell();
4752 if (presShell) {
4753 nsPresContext* presContext = presShell->GetPresContext();
4754 if (NS_WARN_IF(!presContext)) {
4755 return NS_ERROR_FAILURE;
4757 widget = presContext->GetRootWidget();
4758 if (NS_WARN_IF(!widget)) {
4759 return NS_ERROR_FAILURE;
4764 // Dispatch "input" event with InputEvent instance.
4765 InternalEditorInputEvent inputEvent(true, aEventMessage, widget);
4767 inputEvent.mFlags.mCancelable =
4768 !aOptions.mNeverCancelable && aEventMessage == eEditorBeforeInput &&
4769 IsCancelableBeforeInputEvent(aEditorInputType);
4770 MOZ_ASSERT(!inputEvent.mFlags.mCancelable || aEventStatus);
4772 // If there is an editor, set isComposing to true when it has composition.
4773 // Note that EditorBase::IsIMEComposing() may return false even when we
4774 // need to set it to true.
4775 // Otherwise, i.e., editor hasn't been created for the element yet,
4776 // we should set isComposing to false since the element can never has
4777 // composition without editor.
4778 inputEvent.mIsComposing = aEditorBase && aEditorBase->GetComposition();
4780 if (!aEditorBase || aEditorBase->IsTextEditor()) {
4781 if (IsDataAvailableOnTextEditor(aEditorInputType)) {
4782 inputEvent.mData = std::move(aOptions.mData);
4783 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4784 "inputEvent.mData shouldn't be void");
4786 #ifdef DEBUG
4787 else {
4788 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4790 #endif // #ifdef DEBUG
4791 MOZ_ASSERT(
4792 aOptions.mTargetRanges.IsEmpty(),
4793 "Target ranges for <input> and <textarea> should always be empty");
4794 } else {
4795 MOZ_ASSERT(aEditorBase->IsHTMLEditor());
4796 if (IsDataAvailableOnHTMLEditor(aEditorInputType)) {
4797 inputEvent.mData = std::move(aOptions.mData);
4798 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4799 "inputEvent.mData shouldn't be void");
4800 } else {
4801 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4802 if (IsDataTransferAvailableOnHTMLEditor(aEditorInputType)) {
4803 inputEvent.mDataTransfer = std::move(aOptions.mDataTransfer);
4804 MOZ_ASSERT(inputEvent.mDataTransfer,
4805 "inputEvent.mDataTransfer shouldn't be nullptr");
4806 MOZ_ASSERT(inputEvent.mDataTransfer->IsReadOnly(),
4807 "inputEvent.mDataTransfer should be read only");
4809 #ifdef DEBUG
4810 else {
4811 MOZ_ASSERT(!inputEvent.mDataTransfer,
4812 "inputEvent.mDataTransfer should be nullptr");
4814 #endif // #ifdef DEBUG
4816 if (aEventMessage == eEditorBeforeInput &&
4817 MayHaveTargetRangesOnHTMLEditor(aEditorInputType)) {
4818 inputEvent.mTargetRanges = std::move(aOptions.mTargetRanges);
4820 #ifdef DEBUG
4821 else {
4822 MOZ_ASSERT(aOptions.mTargetRanges.IsEmpty(),
4823 "Target ranges shouldn't be set for the dispatching event");
4825 #endif // #ifdef DEBUG
4828 inputEvent.mInputType = aEditorInputType;
4830 // If we cannot dispatch an event right now, we cannot make it cancelable.
4831 if (!nsContentUtils::IsSafeToRunScript()) {
4832 NS_ASSERTION(
4833 !inputEvent.mFlags.mCancelable,
4834 "Cancelable beforeinput event dispatcher should run when it's safe");
4835 inputEvent.mFlags.mCancelable = false;
4837 return AsyncEventDispatcher::RunDOMEventWhenSafe(*aEventTargetElement,
4838 inputEvent, aEventStatus);
4841 nsresult nsContentUtils::DispatchChromeEvent(
4842 Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
4843 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4844 if (!aDoc || !aTarget) {
4845 return NS_ERROR_INVALID_ARG;
4848 if (!aDoc->GetWindow()) {
4849 return NS_ERROR_INVALID_ARG;
4852 EventTarget* piTarget = aDoc->GetWindow()->GetParentTarget();
4853 if (!piTarget) {
4854 return NS_ERROR_INVALID_ARG;
4857 ErrorResult err;
4858 RefPtr<Event> event =
4859 GetEventWithTarget(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4860 Composed::eDefault, Trusted::eYes, err);
4861 if (err.Failed()) {
4862 return err.StealNSResult();
4865 bool defaultActionEnabled =
4866 piTarget->DispatchEvent(*event, CallerType::System, err);
4867 if (aDefaultAction) {
4868 *aDefaultAction = defaultActionEnabled;
4870 return err.StealNSResult();
4873 void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise,
4874 CallerType aCallerType) {
4875 RefPtr<Element> target = &aFrameElement;
4876 bool defaultAction = true;
4877 if (aCanRaise) {
4878 DispatchEventOnlyToChrome(target->OwnerDoc(), target,
4879 u"framefocusrequested"_ns, CanBubble::eYes,
4880 Cancelable::eYes, &defaultAction);
4882 if (!defaultAction) {
4883 return;
4886 RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
4887 if (!fm) {
4888 return;
4891 uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
4892 if (aCanRaise) {
4893 flags |= nsIFocusManager::FLAG_RAISE;
4896 if (aCallerType == CallerType::NonSystem) {
4897 flags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
4900 fm->SetFocus(target, flags);
4903 nsresult nsContentUtils::DispatchEventOnlyToChrome(
4904 Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
4905 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4906 bool* aDefaultAction) {
4907 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4908 aComposed, Trusted::eYes, aDefaultAction,
4909 ChromeOnlyDispatch::eYes);
4912 /* static */
4913 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4914 const nsAtom* aId) {
4915 for (nsIContent* cur = aContent; cur; cur = cur->GetNextNode(aContent)) {
4916 if (aId == cur->GetID()) {
4917 return cur->AsElement();
4921 return nullptr;
4924 /* static */
4925 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4926 const nsAString& aId) {
4927 MOZ_ASSERT(!aId.IsEmpty(), "Will match random elements");
4929 // ID attrs are generally stored as atoms, so just atomize this up front
4930 RefPtr<nsAtom> id(NS_Atomize(aId));
4931 if (!id) {
4932 // OOM, so just bail
4933 return nullptr;
4936 return MatchElementId(aContent, id);
4939 /* static */
4940 void nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver) {
4941 nsCOMPtr<nsIObserverService> observerService =
4942 mozilla::services::GetObserverService();
4943 if (observerService) {
4944 observerService->AddObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
4945 false);
4949 /* static */
4950 void nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver) {
4951 nsCOMPtr<nsIObserverService> observerService =
4952 mozilla::services::GetObserverService();
4953 if (observerService) {
4954 observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
4958 /* static */
4959 bool nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent,
4960 int32_t aNameSpaceID, nsAtom* aName) {
4961 static AttrArray::AttrValuesArray strings[] = {nsGkAtoms::_empty, nullptr};
4962 return aContent->IsElement() &&
4963 aContent->AsElement()->FindAttrValueIn(aNameSpaceID, aName, strings,
4964 eCaseMatters) ==
4965 AttrArray::ATTR_VALUE_NO_MATCH;
4968 /* static */
4969 bool nsContentUtils::HasMutationListeners(nsINode* aNode, uint32_t aType,
4970 nsINode* aTargetForSubtreeModified) {
4971 Document* doc = aNode->OwnerDoc();
4973 // global object will be null for documents that don't have windows.
4974 nsPIDOMWindowInner* window = doc->GetInnerWindow();
4975 // This relies on EventListenerManager::AddEventListener, which sets
4976 // all mutation bits when there is a listener for DOMSubtreeModified event.
4977 if (window && !window->HasMutationListeners(aType)) {
4978 return false;
4981 if (aNode->ChromeOnlyAccess() || aNode->IsInShadowTree()) {
4982 return false;
4985 doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
4987 // If we have a window, we can check it for mutation listeners now.
4988 if (aNode->IsInUncomposedDoc()) {
4989 nsCOMPtr<EventTarget> piTarget(do_QueryInterface(window));
4990 if (piTarget) {
4991 EventListenerManager* manager = piTarget->GetExistingListenerManager();
4992 if (manager && manager->HasMutationListeners()) {
4993 return true;
4998 // If we have a window, we know a mutation listener is registered, but it
4999 // might not be in our chain. If we don't have a window, we might have a
5000 // mutation listener. Check quickly to see.
5001 while (aNode) {
5002 EventListenerManager* manager = aNode->GetExistingListenerManager();
5003 if (manager && manager->HasMutationListeners()) {
5004 return true;
5007 aNode = aNode->GetParentNode();
5010 return false;
5013 /* static */
5014 bool nsContentUtils::HasMutationListeners(Document* aDocument, uint32_t aType) {
5015 nsPIDOMWindowInner* window =
5016 aDocument ? aDocument->GetInnerWindow() : nullptr;
5018 // This relies on EventListenerManager::AddEventListener, which sets
5019 // all mutation bits when there is a listener for DOMSubtreeModified event.
5020 return !window || window->HasMutationListeners(aType);
5023 void nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent) {
5024 MOZ_ASSERT(aChild, "Missing child");
5025 MOZ_ASSERT(aChild->GetParentNode() == aParent, "Wrong parent");
5026 MOZ_ASSERT(aChild->OwnerDoc() == aParent->OwnerDoc(), "Wrong owner-doc");
5028 // Having an explicit check here since it's an easy mistake to fall into,
5029 // and there might be existing code with problems. We'd rather be safe
5030 // than fire DOMNodeRemoved in all corner cases. We also rely on it for
5031 // nsAutoScriptBlockerSuppressNodeRemoved.
5032 if (!IsSafeToRunScript()) {
5033 // This checks that IsSafeToRunScript is true since we don't want to fire
5034 // events when that is false. We can't rely on EventDispatcher to assert
5035 // this in this situation since most of the time there are no mutation
5036 // event listeners, in which case we won't even attempt to dispatch events.
5037 // However this also allows for two exceptions. First off, we don't assert
5038 // if the mutation happens to native anonymous content since we never fire
5039 // mutation events on such content anyway.
5040 // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
5041 // that is a know case when we'd normally fire a mutation event, but can't
5042 // make that safe and so we suppress it at this time. Ideally this should
5043 // go away eventually.
5044 if (!aChild->IsInNativeAnonymousSubtree() &&
5045 !sDOMNodeRemovedSuppressCount) {
5046 NS_ERROR("Want to fire DOMNodeRemoved event, but it's not safe");
5047 WarnScriptWasIgnored(aChild->OwnerDoc());
5049 return;
5053 Document* doc = aParent->OwnerDoc();
5054 if (MOZ_UNLIKELY(doc->DevToolsWatchingDOMMutations()) &&
5055 aChild->IsInComposedDoc() && !aChild->ChromeOnlyAccess()) {
5056 DispatchChromeEvent(doc, aChild, u"devtoolschildremoved"_ns,
5057 CanBubble::eNo, Cancelable::eNo);
5061 if (HasMutationListeners(aChild, NS_EVENT_BITS_MUTATION_NODEREMOVED,
5062 aParent)) {
5063 InternalMutationEvent mutation(true, eLegacyNodeRemoved);
5064 mutation.mRelatedNode = aParent;
5066 mozAutoSubtreeModified subtree(aParent->OwnerDoc(), aParent);
5067 EventDispatcher::Dispatch(aChild, nullptr, &mutation);
5071 void nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments() {
5072 if (!sEventListenerManagersHash) {
5073 return;
5076 for (auto i = sEventListenerManagersHash->Iter(); !i.Done(); i.Next()) {
5077 auto entry = static_cast<EventListenerManagerMapEntry*>(i.Get());
5078 nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
5079 if (n && n->IsInComposedDoc() &&
5080 nsCCUncollectableMarker::InGeneration(
5081 n->OwnerDoc()->GetMarkedCCGeneration())) {
5082 entry->mListenerManager->MarkForCC();
5087 /* static */
5088 void nsContentUtils::TraverseListenerManager(
5089 nsINode* aNode, nsCycleCollectionTraversalCallback& cb) {
5090 if (!sEventListenerManagersHash) {
5091 // We're already shut down, just return.
5092 return;
5095 auto entry = static_cast<EventListenerManagerMapEntry*>(
5096 sEventListenerManagersHash->Search(aNode));
5097 if (entry) {
5098 CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
5099 "[via hash] mListenerManager");
5103 EventListenerManager* nsContentUtils::GetListenerManagerForNode(
5104 nsINode* aNode) {
5105 if (!sEventListenerManagersHash) {
5106 // We're already shut down, don't bother creating an event listener
5107 // manager.
5109 return nullptr;
5112 auto entry = static_cast<EventListenerManagerMapEntry*>(
5113 sEventListenerManagersHash->Add(aNode, fallible));
5115 if (!entry) {
5116 return nullptr;
5119 if (!entry->mListenerManager) {
5120 entry->mListenerManager = new EventListenerManager(aNode);
5122 aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
5125 return entry->mListenerManager;
5128 EventListenerManager* nsContentUtils::GetExistingListenerManagerForNode(
5129 const nsINode* aNode) {
5130 if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
5131 return nullptr;
5134 if (!sEventListenerManagersHash) {
5135 // We're already shut down, don't bother creating an event listener
5136 // manager.
5138 return nullptr;
5141 auto entry = static_cast<EventListenerManagerMapEntry*>(
5142 sEventListenerManagersHash->Search(aNode));
5143 if (entry) {
5144 return entry->mListenerManager;
5147 return nullptr;
5150 void nsContentUtils::AddEntryToDOMArenaTable(nsINode* aNode,
5151 DOMArena* aDOMArena) {
5152 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
5153 MOZ_ASSERT_IF(sDOMArenaHashtable, !sDOMArenaHashtable->Contains(aNode));
5154 MOZ_ASSERT(!aNode->HasFlag(NODE_KEEPS_DOMARENA));
5155 if (!sDOMArenaHashtable) {
5156 sDOMArenaHashtable =
5157 new nsRefPtrHashtable<nsPtrHashKey<const nsINode>, dom::DOMArena>();
5159 aNode->SetFlags(NODE_KEEPS_DOMARENA);
5160 sDOMArenaHashtable->InsertOrUpdate(aNode, RefPtr<DOMArena>(aDOMArena));
5163 already_AddRefed<DOMArena> nsContentUtils::TakeEntryFromDOMArenaTable(
5164 const nsINode* aNode) {
5165 MOZ_ASSERT(sDOMArenaHashtable->Contains(aNode));
5166 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
5167 RefPtr<DOMArena> arena;
5168 sDOMArenaHashtable->Remove(aNode, getter_AddRefs(arena));
5169 return arena.forget();
5172 /* static */
5173 void nsContentUtils::RemoveListenerManager(nsINode* aNode) {
5174 if (sEventListenerManagersHash) {
5175 auto entry = static_cast<EventListenerManagerMapEntry*>(
5176 sEventListenerManagersHash->Search(aNode));
5177 if (entry) {
5178 RefPtr<EventListenerManager> listenerManager;
5179 listenerManager.swap(entry->mListenerManager);
5180 // Remove the entry and *then* do operations that could cause further
5181 // modification of sEventListenerManagersHash. See bug 334177.
5182 sEventListenerManagersHash->RawRemove(entry);
5183 if (listenerManager) {
5184 listenerManager->Disconnect();
5190 /* static */
5191 bool nsContentUtils::IsValidNodeName(nsAtom* aLocalName, nsAtom* aPrefix,
5192 int32_t aNamespaceID) {
5193 if (aNamespaceID == kNameSpaceID_Unknown) {
5194 return false;
5197 if (!aPrefix) {
5198 // If the prefix is null, then either the QName must be xmlns or the
5199 // namespace must not be XMLNS.
5200 return (aLocalName == nsGkAtoms::xmlns) ==
5201 (aNamespaceID == kNameSpaceID_XMLNS);
5204 // If the prefix is non-null then the namespace must not be null.
5205 if (aNamespaceID == kNameSpaceID_None) {
5206 return false;
5209 // If the namespace is the XMLNS namespace then the prefix must be xmlns,
5210 // but the localname must not be xmlns.
5211 if (aNamespaceID == kNameSpaceID_XMLNS) {
5212 return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
5215 // If the namespace is not the XMLNS namespace then the prefix must not be
5216 // xmlns.
5217 // If the namespace is the XML namespace then the prefix can be anything.
5218 // If the namespace is not the XML namespace then the prefix must not be xml.
5219 return aPrefix != nsGkAtoms::xmlns &&
5220 (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
5223 already_AddRefed<DocumentFragment> nsContentUtils::CreateContextualFragment(
5224 nsINode* aContextNode, const nsAString& aFragment,
5225 bool aPreventScriptExecution, ErrorResult& aRv) {
5226 if (!aContextNode) {
5227 aRv.Throw(NS_ERROR_INVALID_ARG);
5228 return nullptr;
5231 // If we don't have a document here, we can't get the right security context
5232 // for compiling event handlers... so just bail out.
5233 RefPtr<Document> document = aContextNode->OwnerDoc();
5234 bool isHTML = document->IsHTMLDocument();
5236 if (isHTML) {
5237 RefPtr<DocumentFragment> frag = new (document->NodeInfoManager())
5238 DocumentFragment(document->NodeInfoManager());
5240 Element* element = aContextNode->GetAsElementOrParentElement();
5241 if (element && !element->IsHTMLElement(nsGkAtoms::html)) {
5242 aRv = ParseFragmentHTML(
5243 aFragment, frag, element->NodeInfo()->NameAtom(),
5244 element->GetNameSpaceID(),
5245 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
5246 aPreventScriptExecution);
5247 } else {
5248 aRv = ParseFragmentHTML(
5249 aFragment, frag, nsGkAtoms::body, kNameSpaceID_XHTML,
5250 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
5251 aPreventScriptExecution);
5254 return frag.forget();
5257 AutoTArray<nsString, 32> tagStack;
5258 nsAutoString uriStr, nameStr;
5259 for (Element* element : aContextNode->InclusiveAncestorsOfType<Element>()) {
5260 nsString& tagName = *tagStack.AppendElement();
5261 // It mostly doesn't actually matter what tag name we use here: XML doesn't
5262 // have parsing that depends on the open tag stack, apart from namespace
5263 // declarations. So this whole tagStack bit is just there to get the right
5264 // namespace declarations to the XML parser. That said, the parser _is_
5265 // going to create elements with the tag names we provide here, so we need
5266 // to make sure they are not names that can trigger custom element
5267 // constructors. Just make up a name that is never going to be a valid
5268 // custom element name.
5270 // The principled way to do this would probably be to add a new FromParser
5271 // value and make sure we use it when creating the context elements, then
5272 // make sure we teach all FromParser consumers (and in particular the custom
5273 // element code) about it as needed. But right now the XML parser never
5274 // actually uses FromParser values other than NOT_FROM_PARSER, and changing
5275 // that is pretty complicated.
5276 tagName.AssignLiteral("notacustomelement");
5278 // see if we need to add xmlns declarations
5279 uint32_t count = element->GetAttrCount();
5280 bool setDefaultNamespace = false;
5281 if (count > 0) {
5282 uint32_t index;
5284 for (index = 0; index < count; index++) {
5285 const BorrowedAttrInfo info = element->GetAttrInfoAt(index);
5286 const nsAttrName* name = info.mName;
5287 if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
5288 info.mValue->ToString(uriStr);
5290 // really want something like nsXMLContentSerializer::SerializeAttr
5291 tagName.AppendLiteral(" xmlns"); // space important
5292 if (name->GetPrefix()) {
5293 tagName.Append(char16_t(':'));
5294 name->LocalName()->ToString(nameStr);
5295 tagName.Append(nameStr);
5296 } else {
5297 setDefaultNamespace = true;
5299 tagName.AppendLiteral(R"(=")");
5300 tagName.Append(uriStr);
5301 tagName.Append('"');
5306 if (!setDefaultNamespace) {
5307 mozilla::dom::NodeInfo* info = element->NodeInfo();
5308 if (!info->GetPrefixAtom() && info->NamespaceID() != kNameSpaceID_None) {
5309 // We have no namespace prefix, but have a namespace ID. Push
5310 // default namespace attr in, so that our kids will be in our
5311 // namespace.
5312 info->GetNamespaceURI(uriStr);
5313 tagName.AppendLiteral(R"( xmlns=")");
5314 tagName.Append(uriStr);
5315 tagName.Append('"');
5320 RefPtr<DocumentFragment> frag;
5321 aRv = ParseFragmentXML(aFragment, document, tagStack, aPreventScriptExecution,
5322 -1, getter_AddRefs(frag));
5323 return frag.forget();
5326 /* static */
5327 void nsContentUtils::DropFragmentParsers() {
5328 NS_IF_RELEASE(sHTMLFragmentParser);
5329 NS_IF_RELEASE(sXMLFragmentParser);
5330 NS_IF_RELEASE(sXMLFragmentSink);
5333 /* static */
5334 void nsContentUtils::XPCOMShutdown() { nsContentUtils::DropFragmentParsers(); }
5336 /* Helper function to compuate Sanitization Flags for ParseFramentHTML/XML */
5337 uint32_t computeSanitizationFlags(nsIPrincipal* aPrincipal, int32_t aFlags) {
5338 uint32_t sanitizationFlags = 0;
5339 if (aPrincipal->IsSystemPrincipal()) {
5340 if (aFlags < 0) {
5341 // if this is a chrome-privileged document and no explicit flags
5342 // were passed, then use this sanitization flags.
5343 sanitizationFlags = nsIParserUtils::SanitizerAllowStyle |
5344 nsIParserUtils::SanitizerAllowComments |
5345 nsIParserUtils::SanitizerDropForms |
5346 nsIParserUtils::SanitizerLogRemovals;
5347 } else {
5348 // if the caller explicitly passes flags, then we use those
5349 // flags but additionally drop forms.
5350 sanitizationFlags = aFlags | nsIParserUtils::SanitizerDropForms;
5352 } else if (aFlags >= 0) {
5353 // aFlags by default is -1 and is only ever non equal to -1 if the
5354 // caller of ParseFragmentHTML/ParseFragmentXML is
5355 // ParserUtils::ParseFragment(). Only in that case we should use
5356 // the sanitization flags passed within aFlags.
5357 sanitizationFlags = aFlags;
5359 return sanitizationFlags;
5362 /* static */
5363 bool AllowsUnsanitizedContentForAboutNewTab(nsIPrincipal* aPrincipal) {
5364 if (StaticPrefs::dom_about_newtab_sanitization_enabled() ||
5365 !aPrincipal->SchemeIs("about")) {
5366 return false;
5368 uint32_t aboutModuleFlags = 0;
5369 aPrincipal->GetAboutModuleFlags(&aboutModuleFlags);
5370 return aboutModuleFlags & nsIAboutModule::ALLOW_UNSANITIZED_CONTENT;
5373 /* static */
5374 nsresult nsContentUtils::ParseFragmentHTML(
5375 const nsAString& aSourceBuffer, nsIContent* aTargetNode,
5376 nsAtom* aContextLocalName, int32_t aContextNamespace, bool aQuirks,
5377 bool aPreventScriptExecution, int32_t aFlags) {
5378 AutoTimelineMarker m(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");
5380 if (nsContentUtils::sFragmentParsingActive) {
5381 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5382 return NS_ERROR_DOM_INVALID_STATE_ERR;
5384 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5385 nsContentUtils::sFragmentParsingActive = true;
5386 if (!sHTMLFragmentParser) {
5387 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5388 // Now sHTMLFragmentParser owns the object
5391 nsCOMPtr<nsIPrincipal> nodePrincipal = aTargetNode->NodePrincipal();
5393 #ifdef DEBUG
5394 // aFlags should always be -1 unless the caller of ParseFragmentHTML
5395 // is ParserUtils::ParseFragment() which is the only caller that intends
5396 // sanitization. For all other callers we need to ensure to call
5397 // AuditParsingOfHTMLXMLFragments.
5398 if (aFlags < 0) {
5399 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5400 aSourceBuffer);
5402 #endif
5404 nsIContent* target = aTargetNode;
5406 RefPtr<Document> doc = aTargetNode->OwnerDoc();
5407 RefPtr<DocumentFragment> fragment;
5408 // We sanitize if the fragment occurs in a system privileged
5409 // context, an about: page, or if there are explicit sanitization flags.
5410 // Please note that about:blank and about:srcdoc inherit the security
5411 // context from the embedding context and hence are not loaded using
5412 // an about: scheme principal.
5413 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5414 nodePrincipal->SchemeIs("about") || aFlags >= 0;
5415 if (shouldSanitize &&
5416 !AllowsUnsanitizedContentForAboutNewTab(nodePrincipal)) {
5417 if (!doc->IsLoadedAsData()) {
5418 doc = nsContentUtils::CreateInertHTMLDocument(doc);
5419 if (!doc) {
5420 return NS_ERROR_FAILURE;
5423 fragment =
5424 new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager());
5425 target = fragment;
5428 nsresult rv = sHTMLFragmentParser->ParseFragment(
5429 aSourceBuffer, target, aContextLocalName, aContextNamespace, aQuirks,
5430 aPreventScriptExecution);
5431 NS_ENSURE_SUCCESS(rv, rv);
5433 if (fragment) {
5434 uint32_t sanitizationFlags =
5435 computeSanitizationFlags(nodePrincipal, aFlags);
5436 // Don't fire mutation events for nodes removed by the sanitizer.
5437 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5438 nsTreeSanitizer sanitizer(sanitizationFlags);
5439 sanitizer.Sanitize(fragment);
5441 ErrorResult error;
5442 aTargetNode->AppendChild(*fragment, error);
5443 rv = error.StealNSResult();
5446 return rv;
5449 /* static */
5450 nsresult nsContentUtils::ParseDocumentHTML(
5451 const nsAString& aSourceBuffer, Document* aTargetDocument,
5452 bool aScriptingEnabledForNoscriptParsing) {
5453 AutoTimelineMarker m(aTargetDocument->GetDocShell(), "Parse HTML");
5455 if (nsContentUtils::sFragmentParsingActive) {
5456 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5457 return NS_ERROR_DOM_INVALID_STATE_ERR;
5459 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5460 nsContentUtils::sFragmentParsingActive = true;
5461 if (!sHTMLFragmentParser) {
5462 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5463 // Now sHTMLFragmentParser owns the object
5465 nsresult rv = sHTMLFragmentParser->ParseDocument(
5466 aSourceBuffer, aTargetDocument, aScriptingEnabledForNoscriptParsing);
5467 return rv;
5470 /* static */
5471 nsresult nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
5472 Document* aDocument,
5473 nsTArray<nsString>& aTagStack,
5474 bool aPreventScriptExecution,
5475 int32_t aFlags,
5476 DocumentFragment** aReturn) {
5477 AutoTimelineMarker m(aDocument->GetDocShell(), "Parse XML");
5479 if (nsContentUtils::sFragmentParsingActive) {
5480 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5481 return NS_ERROR_DOM_INVALID_STATE_ERR;
5483 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5484 nsContentUtils::sFragmentParsingActive = true;
5485 if (!sXMLFragmentParser) {
5486 RefPtr<nsParser> parser = new nsParser();
5487 parser.forget(&sXMLFragmentParser);
5488 // sXMLFragmentParser now owns the parser
5490 if (!sXMLFragmentSink) {
5491 NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
5492 // sXMLFragmentSink now owns the sink
5494 nsCOMPtr<nsIContentSink> contentsink = do_QueryInterface(sXMLFragmentSink);
5495 MOZ_ASSERT(contentsink, "Sink doesn't QI to nsIContentSink!");
5496 sXMLFragmentParser->SetContentSink(contentsink);
5498 RefPtr<Document> doc;
5499 nsCOMPtr<nsIPrincipal> nodePrincipal = aDocument->NodePrincipal();
5501 #ifdef DEBUG
5502 // aFlags should always be -1 unless the caller of ParseFragmentXML
5503 // is ParserUtils::ParseFragment() which is the only caller that intends
5504 // sanitization. For all other callers we need to ensure to call
5505 // AuditParsingOfHTMLXMLFragments.
5506 if (aFlags < 0) {
5507 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5508 aSourceBuffer);
5510 #endif
5512 // We sanitize if the fragment occurs in a system privileged
5513 // context, an about: page, or if there are explicit sanitization flags.
5514 // Please note that about:blank and about:srcdoc inherit the security
5515 // context from the embedding context and hence are not loaded using
5516 // an about: scheme principal.
5517 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5518 nodePrincipal->SchemeIs("about") || aFlags >= 0;
5519 if (shouldSanitize && !aDocument->IsLoadedAsData()) {
5520 doc = nsContentUtils::CreateInertXMLDocument(aDocument);
5521 } else {
5522 doc = aDocument;
5525 sXMLFragmentSink->SetTargetDocument(doc);
5526 sXMLFragmentSink->SetPreventScriptExecution(aPreventScriptExecution);
5528 nsresult rv = sXMLFragmentParser->ParseFragment(aSourceBuffer, aTagStack);
5529 if (NS_FAILED(rv)) {
5530 // Drop the fragment parser and sink that might be in an inconsistent state
5531 NS_IF_RELEASE(sXMLFragmentParser);
5532 NS_IF_RELEASE(sXMLFragmentSink);
5533 return rv;
5536 rv = sXMLFragmentSink->FinishFragmentParsing(aReturn);
5538 sXMLFragmentParser->Reset();
5539 NS_ENSURE_SUCCESS(rv, rv);
5541 if (shouldSanitize) {
5542 uint32_t sanitizationFlags =
5543 computeSanitizationFlags(nodePrincipal, aFlags);
5544 // Don't fire mutation events for nodes removed by the sanitizer.
5545 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5546 nsTreeSanitizer sanitizer(sanitizationFlags);
5547 sanitizer.Sanitize(*aReturn);
5550 return rv;
5553 /* static */
5554 nsresult nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
5555 nsAString& aResultBuffer,
5556 uint32_t aFlags,
5557 uint32_t aWrapCol) {
5558 RefPtr<Document> document = nsContentUtils::CreateInertHTMLDocument(nullptr);
5559 if (!document) {
5560 return NS_ERROR_FAILURE;
5563 nsresult rv = nsContentUtils::ParseDocumentHTML(
5564 aSourceBuffer, document,
5565 !(aFlags & nsIDocumentEncoder::OutputNoScriptContent));
5566 NS_ENSURE_SUCCESS(rv, rv);
5568 nsCOMPtr<nsIDocumentEncoder> encoder = do_createDocumentEncoder("text/plain");
5570 rv = encoder->Init(document, u"text/plain"_ns, aFlags);
5571 NS_ENSURE_SUCCESS(rv, rv);
5573 encoder->SetWrapColumn(aWrapCol);
5575 return encoder->EncodeToString(aResultBuffer);
5578 static already_AddRefed<Document> CreateInertDocument(const Document* aTemplate,
5579 DocumentFlavor aFlavor) {
5580 if (aTemplate) {
5581 bool hasHad = true;
5582 nsIScriptGlobalObject* sgo = aTemplate->GetScriptHandlingObject(hasHad);
5583 NS_ENSURE_TRUE(sgo || !hasHad, nullptr);
5585 nsCOMPtr<Document> doc;
5586 nsresult rv = NS_NewDOMDocument(
5587 getter_AddRefs(doc), u""_ns, u""_ns, nullptr,
5588 aTemplate->GetDocumentURI(), aTemplate->GetDocBaseURI(),
5589 aTemplate->NodePrincipal(), true, sgo, aFlavor);
5590 if (NS_FAILED(rv)) {
5591 return nullptr;
5593 return doc.forget();
5595 nsCOMPtr<nsIURI> uri;
5596 NS_NewURI(getter_AddRefs(uri), "about:blank"_ns);
5597 if (!uri) {
5598 return nullptr;
5601 RefPtr<NullPrincipal> nullPrincipal =
5602 NullPrincipal::CreateWithoutOriginAttributes();
5603 if (!nullPrincipal) {
5604 return nullptr;
5607 nsCOMPtr<Document> doc;
5608 nsresult rv =
5609 NS_NewDOMDocument(getter_AddRefs(doc), u""_ns, u""_ns, nullptr, uri, uri,
5610 nullPrincipal, true, nullptr, aFlavor);
5611 if (NS_FAILED(rv)) {
5612 return nullptr;
5614 return doc.forget();
5617 /* static */
5618 already_AddRefed<Document> nsContentUtils::CreateInertXMLDocument(
5619 const Document* aTemplate) {
5620 return CreateInertDocument(aTemplate, DocumentFlavorXML);
5623 /* static */
5624 already_AddRefed<Document> nsContentUtils::CreateInertHTMLDocument(
5625 const Document* aTemplate) {
5626 return CreateInertDocument(aTemplate, DocumentFlavorHTML);
5629 /* static */
5630 nsresult nsContentUtils::SetNodeTextContent(nsIContent* aContent,
5631 const nsAString& aValue,
5632 bool aTryReuse) {
5633 // Fire DOMNodeRemoved mutation events before we do anything else.
5634 nsCOMPtr<nsIContent> owningContent;
5636 // Batch possible DOMSubtreeModified events.
5637 mozAutoSubtreeModified subtree(nullptr, nullptr);
5639 // Scope firing mutation events so that we don't carry any state that
5640 // might be stale
5642 // We're relying on mozAutoSubtreeModified to keep a strong reference if
5643 // needed.
5644 Document* doc = aContent->OwnerDoc();
5646 // Optimize the common case of there being no observers
5647 if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
5648 subtree.UpdateTarget(doc, nullptr);
5649 owningContent = aContent;
5650 nsCOMPtr<nsINode> child;
5651 bool skipFirst = aTryReuse;
5652 for (child = aContent->GetFirstChild();
5653 child && child->GetParentNode() == aContent;
5654 child = child->GetNextSibling()) {
5655 if (skipFirst && child->IsText()) {
5656 skipFirst = false;
5657 continue;
5659 nsContentUtils::MaybeFireNodeRemoved(child, aContent);
5664 // Might as well stick a batch around this since we're performing several
5665 // mutations.
5666 mozAutoDocUpdate updateBatch(aContent->GetComposedDoc(), true);
5667 nsAutoMutationBatch mb;
5669 if (aTryReuse && !aValue.IsEmpty()) {
5670 // Let's remove nodes until we find a eTEXT.
5671 while (aContent->HasChildren()) {
5672 nsIContent* child = aContent->GetFirstChild();
5673 if (child->IsText()) {
5674 break;
5676 aContent->RemoveChildNode(child, true);
5679 // If we have a node, it must be a eTEXT and we reuse it.
5680 if (aContent->HasChildren()) {
5681 nsIContent* child = aContent->GetFirstChild();
5682 nsresult rv = child->AsText()->SetText(aValue, true);
5683 NS_ENSURE_SUCCESS(rv, rv);
5685 // All the following nodes, if they exist, must be deleted.
5686 while (nsIContent* nextChild = child->GetNextSibling()) {
5687 aContent->RemoveChildNode(nextChild, true);
5691 if (aContent->HasChildren()) {
5692 return NS_OK;
5694 } else {
5695 mb.Init(aContent, true, false);
5696 while (aContent->HasChildren()) {
5697 aContent->RemoveChildNode(aContent->GetFirstChild(), true);
5700 mb.RemovalDone();
5702 if (aValue.IsEmpty()) {
5703 return NS_OK;
5706 RefPtr<nsTextNode> textContent = new (aContent->NodeInfo()->NodeInfoManager())
5707 nsTextNode(aContent->NodeInfo()->NodeInfoManager());
5709 textContent->SetText(aValue, true);
5711 ErrorResult rv;
5712 aContent->AppendChildTo(textContent, true, rv);
5713 mb.NodesAdded();
5714 return rv.StealNSResult();
5717 static bool AppendNodeTextContentsRecurse(const nsINode* aNode,
5718 nsAString& aResult,
5719 const fallible_t& aFallible) {
5720 for (nsIContent* child = aNode->GetFirstChild(); child;
5721 child = child->GetNextSibling()) {
5722 if (child->IsElement()) {
5723 bool ok = AppendNodeTextContentsRecurse(child, aResult, aFallible);
5724 if (!ok) {
5725 return false;
5727 } else if (Text* text = child->GetAsText()) {
5728 bool ok = text->AppendTextTo(aResult, aFallible);
5729 if (!ok) {
5730 return false;
5735 return true;
5738 /* static */
5739 bool nsContentUtils::AppendNodeTextContent(const nsINode* aNode, bool aDeep,
5740 nsAString& aResult,
5741 const fallible_t& aFallible) {
5742 if (const Text* text = aNode->GetAsText()) {
5743 return text->AppendTextTo(aResult, aFallible);
5745 if (aDeep) {
5746 return AppendNodeTextContentsRecurse(aNode, aResult, aFallible);
5749 for (nsIContent* child = aNode->GetFirstChild(); child;
5750 child = child->GetNextSibling()) {
5751 if (Text* text = child->GetAsText()) {
5752 bool ok = text->AppendTextTo(aResult, fallible);
5753 if (!ok) {
5754 return false;
5758 return true;
5761 bool nsContentUtils::HasNonEmptyTextContent(
5762 nsINode* aNode, TextContentDiscoverMode aDiscoverMode) {
5763 for (nsIContent* child = aNode->GetFirstChild(); child;
5764 child = child->GetNextSibling()) {
5765 if (child->IsText() && child->TextLength() > 0) {
5766 return true;
5769 if (aDiscoverMode == eRecurseIntoChildren &&
5770 HasNonEmptyTextContent(child, aDiscoverMode)) {
5771 return true;
5775 return false;
5778 /* static */
5779 bool nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
5780 const nsINode* aOtherNode) {
5781 MOZ_ASSERT(aNode, "Must have a node to work with");
5782 MOZ_ASSERT(aOtherNode, "Must have a content to work with");
5784 const bool anon = aNode->IsInNativeAnonymousSubtree();
5785 if (anon != aOtherNode->IsInNativeAnonymousSubtree()) {
5786 return false;
5789 if (anon) {
5790 return aOtherNode->GetClosestNativeAnonymousSubtreeRoot() ==
5791 aNode->GetClosestNativeAnonymousSubtreeRoot();
5794 // FIXME: This doesn't deal with disconnected nodes whatsoever, but it didn't
5795 // use to either. Maybe that's fine.
5796 return aNode->GetContainingShadow() == aOtherNode->GetContainingShadow();
5799 /* static */
5800 bool nsContentUtils::IsInInteractiveHTMLContent(const Element* aElement,
5801 const Element* aStop) {
5802 const Element* element = aElement;
5803 while (element && element != aStop) {
5804 if (element->IsInteractiveHTMLContent()) {
5805 return true;
5807 element = element->GetFlattenedTreeParentElement();
5809 return false;
5812 /* static */
5813 void nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling) {
5814 IMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
5817 /* static */
5818 bool nsContentUtils::SchemeIs(nsIURI* aURI, const char* aScheme) {
5819 nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
5820 NS_ENSURE_TRUE(baseURI, false);
5821 return baseURI->SchemeIs(aScheme);
5824 bool nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal) {
5825 return aPrincipal && aPrincipal->GetIsExpandedPrincipal();
5828 bool nsContentUtils::IsSystemOrExpandedPrincipal(nsIPrincipal* aPrincipal) {
5829 return (aPrincipal && aPrincipal->IsSystemPrincipal()) ||
5830 IsExpandedPrincipal(aPrincipal);
5833 nsIPrincipal* nsContentUtils::GetSystemPrincipal() {
5834 MOZ_ASSERT(IsInitialized());
5835 return sSystemPrincipal;
5838 bool nsContentUtils::CombineResourcePrincipals(
5839 nsCOMPtr<nsIPrincipal>* aResourcePrincipal, nsIPrincipal* aExtraPrincipal) {
5840 if (!aExtraPrincipal) {
5841 return false;
5843 if (!*aResourcePrincipal) {
5844 *aResourcePrincipal = aExtraPrincipal;
5845 return true;
5847 if (*aResourcePrincipal == aExtraPrincipal) {
5848 return false;
5850 bool subsumes;
5851 if (NS_SUCCEEDED(
5852 (*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
5853 subsumes) {
5854 return false;
5856 *aResourcePrincipal = sSystemPrincipal;
5857 return true;
5860 /* static */
5861 void nsContentUtils::TriggerLink(nsIContent* aContent, nsIURI* aLinkURI,
5862 const nsString& aTargetSpec, bool aClick,
5863 bool aIsTrusted) {
5864 MOZ_ASSERT(aLinkURI, "No link URI");
5866 if (aContent->IsEditable() || !aContent->OwnerDoc()->LinkHandlingEnabled()) {
5867 return;
5870 nsCOMPtr<nsIDocShell> docShell = aContent->OwnerDoc()->GetDocShell();
5871 if (!docShell) {
5872 return;
5875 if (!aClick) {
5876 nsDocShell::Cast(docShell)->OnOverLink(aContent, aLinkURI, aTargetSpec);
5877 return;
5880 // Check that this page is allowed to load this URI.
5881 nsresult proceed = NS_OK;
5883 if (sSecurityManager) {
5884 uint32_t flag = static_cast<uint32_t>(nsIScriptSecurityManager::STANDARD);
5885 proceed = sSecurityManager->CheckLoadURIWithPrincipal(
5886 aContent->NodePrincipal(), aLinkURI, flag,
5887 aContent->OwnerDoc()->InnerWindowID());
5890 // Only pass off the click event if the script security manager says it's ok.
5891 // We need to rest aTargetSpec for forced downloads.
5892 if (NS_SUCCEEDED(proceed)) {
5893 // A link/area element with a download attribute is allowed to set
5894 // a pseudo Content-Disposition header.
5895 // For security reasons we only allow websites to declare same-origin
5896 // resources as downloadable. If this check fails we will just do the normal
5897 // thing (i.e. navigate to the resource).
5898 nsAutoString fileName;
5899 if ((!aContent->IsHTMLElement(nsGkAtoms::a) &&
5900 !aContent->IsHTMLElement(nsGkAtoms::area) &&
5901 !aContent->IsSVGElement(nsGkAtoms::a)) ||
5902 !aContent->AsElement()->GetAttr(nsGkAtoms::download, fileName) ||
5903 NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, true))) {
5904 fileName.SetIsVoid(true); // No actionable download attribute was found.
5907 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aContent->NodePrincipal();
5908 nsCOMPtr<nsIContentSecurityPolicy> csp = aContent->GetCsp();
5910 // Sanitize fileNames containing null characters by replacing them with
5911 // underscores.
5912 if (!fileName.IsVoid()) {
5913 fileName.ReplaceChar(char16_t(0), '_');
5915 nsDocShell::Cast(docShell)->OnLinkClick(
5916 aContent, aLinkURI, fileName.IsVoid() ? aTargetSpec : u""_ns, fileName,
5917 nullptr, nullptr, UserActivation::IsHandlingUserInput(), aIsTrusted,
5918 triggeringPrincipal, csp);
5922 /* static */
5923 void nsContentUtils::GetLinkLocation(Element* aElement,
5924 nsString& aLocationString) {
5925 nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
5926 if (hrefURI) {
5927 nsAutoCString specUTF8;
5928 nsresult rv = hrefURI->GetSpec(specUTF8);
5929 if (NS_SUCCEEDED(rv)) CopyUTF8toUTF16(specUTF8, aLocationString);
5933 /* static */
5934 nsIWidget* nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget) {
5935 if (!aWidget) return nullptr;
5937 return aWidget->GetTopLevelWidget();
5940 /* static */
5941 const nsDependentString nsContentUtils::GetLocalizedEllipsis() {
5942 static char16_t sBuf[4] = {0, 0, 0, 0};
5943 if (!sBuf[0]) {
5944 if (!SpoofLocaleEnglish()) {
5945 nsAutoString tmp;
5946 Preferences::GetLocalizedString("intl.ellipsis", tmp);
5947 uint32_t len =
5948 std::min(uint32_t(tmp.Length()), uint32_t(ArrayLength(sBuf) - 1));
5949 CopyUnicodeTo(tmp, 0, sBuf, len);
5951 if (!sBuf[0]) sBuf[0] = char16_t(0x2026);
5953 return nsDependentString(sBuf);
5956 /* static */
5957 void nsContentUtils::AddScriptBlocker() {
5958 MOZ_ASSERT(NS_IsMainThread());
5959 if (!sScriptBlockerCount) {
5960 MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
5961 "Should not already have a count");
5962 sRunnersCountAtFirstBlocker =
5963 sBlockedScriptRunners ? sBlockedScriptRunners->Length() : 0;
5965 ++sScriptBlockerCount;
5968 #ifdef DEBUG
5969 static bool sRemovingScriptBlockers = false;
5970 #endif
5972 /* static */
5973 void nsContentUtils::RemoveScriptBlocker() {
5974 MOZ_ASSERT(NS_IsMainThread());
5975 MOZ_ASSERT(!sRemovingScriptBlockers);
5976 NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
5977 --sScriptBlockerCount;
5978 if (sScriptBlockerCount) {
5979 return;
5982 if (!sBlockedScriptRunners) {
5983 return;
5986 uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
5987 uint32_t lastBlocker = sBlockedScriptRunners->Length();
5988 uint32_t originalFirstBlocker = firstBlocker;
5989 uint32_t blockersCount = lastBlocker - firstBlocker;
5990 sRunnersCountAtFirstBlocker = 0;
5991 NS_ASSERTION(firstBlocker <= lastBlocker, "bad sRunnersCountAtFirstBlocker");
5993 while (firstBlocker < lastBlocker) {
5994 nsCOMPtr<nsIRunnable> runnable;
5995 runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
5996 ++firstBlocker;
5998 // Calling the runnable can reenter us
6000 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
6001 runnable->Run();
6003 // So can dropping the reference to the runnable
6004 runnable = nullptr;
6006 NS_ASSERTION(sRunnersCountAtFirstBlocker == 0, "Bad count");
6007 NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
6009 #ifdef DEBUG
6010 AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
6011 sRemovingScriptBlockers = true;
6012 #endif
6013 sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
6016 /* static */
6017 already_AddRefed<nsPIDOMWindowOuter>
6018 nsContentUtils::GetMostRecentNonPBWindow() {
6019 nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
6021 nsCOMPtr<mozIDOMWindowProxy> window;
6022 wm->GetMostRecentNonPBWindow(u"navigator:browser", getter_AddRefs(window));
6023 nsCOMPtr<nsPIDOMWindowOuter> pwindow;
6024 pwindow = do_QueryInterface(window);
6026 return pwindow.forget();
6029 /* static */
6030 void nsContentUtils::WarnScriptWasIgnored(Document* aDocument) {
6031 nsAutoString msg;
6032 bool privateBrowsing = false;
6033 bool chromeContext = false;
6035 if (aDocument) {
6036 nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
6037 if (uri) {
6038 msg.Append(NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()));
6039 msg.AppendLiteral(" : ");
6041 privateBrowsing =
6042 !!aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
6043 chromeContext = aDocument->NodePrincipal()->IsSystemPrincipal();
6046 msg.AppendLiteral(
6047 "Unable to run script because scripts are blocked internally.");
6048 LogSimpleConsoleError(msg, "DOM"_ns, privateBrowsing, chromeContext);
6051 /* static */
6052 void nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable) {
6053 nsCOMPtr<nsIRunnable> runnable = aRunnable;
6054 if (!runnable) {
6055 return;
6058 if (sScriptBlockerCount) {
6059 sBlockedScriptRunners->AppendElement(runnable.forget());
6060 return;
6063 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
6064 runnable->Run();
6067 /* static */
6068 void nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) {
6069 nsCOMPtr<nsIRunnable> runnable = aRunnable;
6070 AddScriptRunner(runnable.forget());
6073 /* static */ bool nsContentUtils::IsSafeToRunScript() {
6074 MOZ_ASSERT(NS_IsMainThread(),
6075 "This static variable only makes sense on the main thread!");
6076 return sScriptBlockerCount == 0;
6079 /* static */
6080 void nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable) {
6081 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6082 CycleCollectedJSContext::Get()->RunInStableState(std::move(aRunnable));
6085 /* static */
6086 void nsContentUtils::AddPendingIDBTransaction(
6087 already_AddRefed<nsIRunnable> aTransaction) {
6088 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6089 CycleCollectedJSContext::Get()->AddPendingIDBTransaction(
6090 std::move(aTransaction));
6093 /* static */
6094 bool nsContentUtils::IsInStableOrMetaStableState() {
6095 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6096 return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
6099 /* static */
6100 void nsContentUtils::HidePopupsInDocument(Document* aDocument) {
6101 RefPtr<nsXULPopupManager> pm = nsXULPopupManager::GetInstance();
6102 if (!pm || !aDocument) {
6103 return;
6105 nsCOMPtr<nsIDocShellTreeItem> docShellToHide = aDocument->GetDocShell();
6106 if (docShellToHide) {
6107 pm->HidePopupsInDocShell(docShellToHide);
6111 /* static */
6112 already_AddRefed<nsIDragSession> nsContentUtils::GetDragSession() {
6113 nsCOMPtr<nsIDragSession> dragSession;
6114 nsCOMPtr<nsIDragService> dragService =
6115 do_GetService("@mozilla.org/widget/dragservice;1");
6116 if (dragService) dragService->GetCurrentSession(getter_AddRefs(dragSession));
6117 return dragSession.forget();
6120 /* static */
6121 nsresult nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) {
6122 if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) {
6123 return NS_OK;
6126 // For dragstart events, the data transfer object is
6127 // created before the event fires, so it should already be set. For other
6128 // drag events, get the object from the drag session.
6129 NS_ASSERTION(aDragEvent->mMessage != eDragStart,
6130 "draggesture event created without a dataTransfer");
6132 nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
6133 NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
6135 RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
6136 if (!initialDataTransfer) {
6137 // A dataTransfer won't exist when a drag was started by some other
6138 // means, for instance calling the drag service directly, or a drag
6139 // from another application. In either case, a new dataTransfer should
6140 // be created that reflects the data.
6141 initialDataTransfer =
6142 new DataTransfer(aDragEvent->mTarget, aDragEvent->mMessage, true, -1);
6144 // now set it in the drag session so we don't need to create it again
6145 dragSession->SetDataTransfer(initialDataTransfer);
6148 bool isCrossDomainSubFrameDrop = false;
6149 if (aDragEvent->mMessage == eDrop) {
6150 isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
6153 // each event should use a clone of the original dataTransfer.
6154 initialDataTransfer->Clone(
6155 aDragEvent->mTarget, aDragEvent->mMessage, aDragEvent->mUserCancelled,
6156 isCrossDomainSubFrameDrop, getter_AddRefs(aDragEvent->mDataTransfer));
6157 if (NS_WARN_IF(!aDragEvent->mDataTransfer)) {
6158 return NS_ERROR_OUT_OF_MEMORY;
6161 // for the dragenter and dragover events, initialize the drop effect
6162 // from the drop action, which platform specific widget code sets before
6163 // the event is fired based on the keyboard state.
6164 if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
6165 uint32_t action;
6166 dragSession->GetDragAction(&action);
6167 uint32_t effectAllowed = aDragEvent->mDataTransfer->EffectAllowedInt();
6168 aDragEvent->mDataTransfer->SetDropEffectInt(
6169 FilterDropEffect(action, effectAllowed));
6170 } else if (aDragEvent->mMessage == eDrop ||
6171 aDragEvent->mMessage == eDragEnd) {
6172 // For the drop and dragend events, set the drop effect based on the
6173 // last value that the dropEffect had. This will have been set in
6174 // EventStateManager::PostHandleEvent for the last dragenter or
6175 // dragover event.
6176 aDragEvent->mDataTransfer->SetDropEffectInt(
6177 initialDataTransfer->DropEffectInt());
6180 return NS_OK;
6183 /* static */
6184 uint32_t nsContentUtils::FilterDropEffect(uint32_t aAction,
6185 uint32_t aEffectAllowed) {
6186 // It is possible for the drag action to include more than one action, but
6187 // the widget code which sets the action from the keyboard state should only
6188 // be including one. If multiple actions were set, we just consider them in
6189 // the following order:
6190 // copy, link, move
6191 if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
6192 aAction = nsIDragService::DRAGDROP_ACTION_COPY;
6193 else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
6194 aAction = nsIDragService::DRAGDROP_ACTION_LINK;
6195 else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
6196 aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
6198 // Filter the action based on the effectAllowed. If the effectAllowed
6199 // doesn't include the action, then that action cannot be done, so adjust
6200 // the action to something that is allowed. For a copy, adjust to move or
6201 // link. For a move, adjust to copy or link. For a link, adjust to move or
6202 // link. Otherwise, use none.
6203 if (aAction & aEffectAllowed ||
6204 aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
6205 return aAction;
6206 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
6207 return nsIDragService::DRAGDROP_ACTION_MOVE;
6208 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
6209 return nsIDragService::DRAGDROP_ACTION_COPY;
6210 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
6211 return nsIDragService::DRAGDROP_ACTION_LINK;
6212 return nsIDragService::DRAGDROP_ACTION_NONE;
6215 /* static */
6216 bool nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession,
6217 WidgetDragEvent* aDropEvent) {
6218 nsCOMPtr<nsIContent> target =
6219 nsIContent::FromEventTargetOrNull(aDropEvent->mOriginalTarget);
6220 if (!target) {
6221 return true;
6224 // Always allow dropping onto chrome shells.
6225 BrowsingContext* targetBC = target->OwnerDoc()->GetBrowsingContext();
6226 if (targetBC->IsChrome()) {
6227 return false;
6230 WindowContext* targetWC = target->OwnerDoc()->GetWindowContext();
6232 // If there is no source browsing context, then this is a drag from another
6233 // application, which should be allowed.
6234 RefPtr<WindowContext> sourceWC;
6235 aDragSession->GetSourceWindowContext(getter_AddRefs(sourceWC));
6236 if (sourceWC) {
6237 // Get each successive parent of the source document and compare it to
6238 // the drop document. If they match, then this is a drag from a child frame.
6239 for (sourceWC = sourceWC->GetParentWindowContext(); sourceWC;
6240 sourceWC = sourceWC->GetParentWindowContext()) {
6241 // If the source and the target match, then the drag started in a
6242 // descendant frame. If the source is discarded, err on the side of
6243 // caution and treat it as a subframe drag.
6244 if (sourceWC == targetWC || sourceWC->IsDiscarded()) {
6245 return true;
6250 return false;
6253 /* static */
6254 bool nsContentUtils::URIIsLocalFile(nsIURI* aURI) {
6255 bool isFile;
6256 nsCOMPtr<nsINetUtil> util = mozilla::components::IO::Service();
6258 // Important: we do NOT test the entire URI chain here!
6259 return util &&
6260 NS_SUCCEEDED(util->ProtocolHasFlags(
6261 aURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, &isFile)) &&
6262 isFile;
6265 /* static */
6266 JSContext* nsContentUtils::GetCurrentJSContext() {
6267 MOZ_ASSERT(IsInitialized());
6268 if (!IsJSAPIActive()) {
6269 return nullptr;
6271 return danger::GetJSContext();
6274 template <typename StringType, typename CharType>
6275 void _ASCIIToLowerInSitu(StringType& aStr) {
6276 CharType* iter = aStr.BeginWriting();
6277 CharType* end = aStr.EndWriting();
6278 MOZ_ASSERT(iter && end);
6280 while (iter != end) {
6281 CharType c = *iter;
6282 if (c >= 'A' && c <= 'Z') {
6283 *iter = c + ('a' - 'A');
6285 ++iter;
6289 /* static */
6290 void nsContentUtils::ASCIIToLower(nsAString& aStr) {
6291 return _ASCIIToLowerInSitu<nsAString, char16_t>(aStr);
6294 /* static */
6295 void nsContentUtils::ASCIIToLower(nsACString& aStr) {
6296 return _ASCIIToLowerInSitu<nsACString, char>(aStr);
6299 template <typename StringType, typename CharType>
6300 void _ASCIIToLowerCopy(const StringType& aSource, StringType& aDest) {
6301 uint32_t len = aSource.Length();
6302 aDest.SetLength(len);
6303 MOZ_ASSERT(aDest.Length() == len);
6305 CharType* dest = aDest.BeginWriting();
6306 MOZ_ASSERT(dest);
6308 const CharType* iter = aSource.BeginReading();
6309 const CharType* end = aSource.EndReading();
6310 while (iter != end) {
6311 CharType c = *iter;
6312 *dest = (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
6313 ++iter;
6314 ++dest;
6318 /* static */
6319 void nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest) {
6320 return _ASCIIToLowerCopy<nsAString, char16_t>(aSource, aDest);
6323 /* static */
6324 void nsContentUtils::ASCIIToLower(const nsACString& aSource,
6325 nsACString& aDest) {
6326 return _ASCIIToLowerCopy<nsACString, char>(aSource, aDest);
6329 template <typename StringType, typename CharType>
6330 void _ASCIIToUpperInSitu(StringType& aStr) {
6331 CharType* iter = aStr.BeginWriting();
6332 CharType* end = aStr.EndWriting();
6333 MOZ_ASSERT(iter && end);
6335 while (iter != end) {
6336 CharType c = *iter;
6337 if (c >= 'a' && c <= 'z') {
6338 *iter = c + ('A' - 'a');
6340 ++iter;
6344 /* static */
6345 void nsContentUtils::ASCIIToUpper(nsAString& aStr) {
6346 return _ASCIIToUpperInSitu<nsAString, char16_t>(aStr);
6349 /* static */
6350 void nsContentUtils::ASCIIToUpper(nsACString& aStr) {
6351 return _ASCIIToUpperInSitu<nsACString, char>(aStr);
6354 template <typename StringType, typename CharType>
6355 void _ASCIIToUpperCopy(const StringType& aSource, StringType& aDest) {
6356 uint32_t len = aSource.Length();
6357 aDest.SetLength(len);
6358 MOZ_ASSERT(aDest.Length() == len);
6360 CharType* dest = aDest.BeginWriting();
6361 MOZ_ASSERT(dest);
6363 const CharType* iter = aSource.BeginReading();
6364 const CharType* end = aSource.EndReading();
6365 while (iter != end) {
6366 CharType c = *iter;
6367 *dest = (c >= 'a' && c <= 'z') ? c + ('A' - 'a') : c;
6368 ++iter;
6369 ++dest;
6373 /* static */
6374 void nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest) {
6375 return _ASCIIToUpperCopy<nsAString, char16_t>(aSource, aDest);
6378 /* static */
6379 void nsContentUtils::ASCIIToUpper(const nsACString& aSource,
6380 nsACString& aDest) {
6381 return _ASCIIToUpperCopy<nsACString, char>(aSource, aDest);
6384 /* static */
6385 bool nsContentUtils::EqualsIgnoreASCIICase(nsAtom* aAtom1, nsAtom* aAtom2) {
6386 if (aAtom1 == aAtom2) {
6387 return true;
6390 // If both are ascii lowercase already, we know that the slow comparison
6391 // below is going to return false.
6392 if (aAtom1->IsAsciiLowercase() && aAtom2->IsAsciiLowercase()) {
6393 return false;
6396 return EqualsIgnoreASCIICase(nsDependentAtomString(aAtom1),
6397 nsDependentAtomString(aAtom2));
6400 /* static */
6401 bool nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
6402 const nsAString& aStr2) {
6403 uint32_t len = aStr1.Length();
6404 if (len != aStr2.Length()) {
6405 return false;
6408 const char16_t* str1 = aStr1.BeginReading();
6409 const char16_t* str2 = aStr2.BeginReading();
6410 const char16_t* end = str1 + len;
6412 while (str1 < end) {
6413 char16_t c1 = *str1++;
6414 char16_t c2 = *str2++;
6416 // First check if any bits other than the 0x0020 differs
6417 if ((c1 ^ c2) & 0xffdf) {
6418 return false;
6421 // We know they can only differ in the 0x0020 bit.
6422 // Likely the two chars are the same, so check that first
6423 if (c1 != c2) {
6424 // They do differ, but since it's only in the 0x0020 bit, check if it's
6425 // the same ascii char, but just differing in case
6426 char16_t c1Upper = c1 & 0xffdf;
6427 if (!('A' <= c1Upper && c1Upper <= 'Z')) {
6428 return false;
6433 return true;
6436 /* static */
6437 bool nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr) {
6438 const char16_t* iter = aStr.BeginReading();
6439 const char16_t* end = aStr.EndReading();
6440 while (iter != end) {
6441 char16_t c = *iter;
6442 if (c >= 'A' && c <= 'Z') {
6443 return true;
6445 ++iter;
6448 return false;
6451 /* static */
6452 nsIInterfaceRequestor* nsContentUtils::SameOriginChecker() {
6453 if (!sSameOriginChecker) {
6454 sSameOriginChecker = new SameOriginCheckerImpl();
6455 NS_ADDREF(sSameOriginChecker);
6457 return sSameOriginChecker;
6460 /* static */
6461 nsresult nsContentUtils::CheckSameOrigin(nsIChannel* aOldChannel,
6462 nsIChannel* aNewChannel) {
6463 if (!nsContentUtils::GetSecurityManager()) return NS_ERROR_NOT_AVAILABLE;
6465 nsCOMPtr<nsIPrincipal> oldPrincipal;
6466 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
6467 aOldChannel, getter_AddRefs(oldPrincipal));
6469 nsCOMPtr<nsIURI> newURI;
6470 aNewChannel->GetURI(getter_AddRefs(newURI));
6471 nsCOMPtr<nsIURI> newOriginalURI;
6472 aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
6474 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
6476 nsresult rv = oldPrincipal->CheckMayLoad(newURI, false);
6477 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
6478 rv = oldPrincipal->CheckMayLoad(newOriginalURI, false);
6481 return rv;
6484 NS_IMPL_ISUPPORTS(SameOriginCheckerImpl, nsIChannelEventSink,
6485 nsIInterfaceRequestor)
6487 NS_IMETHODIMP
6488 SameOriginCheckerImpl::AsyncOnChannelRedirect(
6489 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
6490 nsIAsyncVerifyRedirectCallback* cb) {
6491 MOZ_ASSERT(aNewChannel, "Redirecting to null channel?");
6493 nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
6494 if (NS_SUCCEEDED(rv)) {
6495 cb->OnRedirectVerifyCallback(NS_OK);
6498 return rv;
6501 NS_IMETHODIMP
6502 SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult) {
6503 return QueryInterface(aIID, aResult);
6506 /* static */
6507 nsresult nsContentUtils::GetWebExposedOriginSerialization(nsIURI* aURI,
6508 nsACString& aOrigin) {
6509 nsresult rv;
6510 MOZ_ASSERT(aURI, "missing uri");
6512 // For Blob URI, the path is the URL of the owning page.
6513 if (aURI->SchemeIs(BLOBURI_SCHEME)) {
6514 nsAutoCString path;
6515 rv = aURI->GetPathQueryRef(path);
6516 NS_ENSURE_SUCCESS(rv, rv);
6518 nsCOMPtr<nsIURI> uri;
6519 rv = NS_NewURI(getter_AddRefs(uri), path);
6520 if (NS_FAILED(rv)) {
6521 aOrigin.AssignLiteral("null");
6522 return NS_OK;
6525 return GetWebExposedOriginSerialization(uri, aOrigin);
6528 nsAutoCString scheme;
6529 aURI->GetScheme(scheme);
6531 // If the protocol doesn't have URI_HAS_WEB_EXPOSED_ORIGIN, then
6532 // return "null" as the origin serialization.
6533 // We make an exception for "ftp" since we don't have a protocol handler
6534 // for this scheme
6535 uint32_t flags = 0;
6536 nsCOMPtr<nsIIOService> io = mozilla::components::IO::Service(&rv);
6537 if (!scheme.Equals("ftp") && NS_SUCCEEDED(rv) &&
6538 NS_SUCCEEDED(io->GetProtocolFlags(scheme.get(), &flags))) {
6539 if (!(flags & nsIProtocolHandler::URI_HAS_WEB_EXPOSED_ORIGIN)) {
6540 aOrigin.AssignLiteral("null");
6541 return NS_OK;
6545 aOrigin.Truncate();
6547 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
6548 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
6550 nsAutoCString host;
6551 rv = uri->GetAsciiHost(host);
6553 if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
6554 nsAutoCString userPass;
6555 uri->GetUserPass(userPass);
6557 nsAutoCString prePath;
6558 if (!userPass.IsEmpty()) {
6559 rv = NS_MutateURI(uri).SetUserPass(""_ns).Finalize(uri);
6560 NS_ENSURE_SUCCESS(rv, rv);
6563 rv = uri->GetPrePath(prePath);
6564 NS_ENSURE_SUCCESS(rv, rv);
6566 aOrigin = prePath;
6567 } else {
6568 aOrigin.AssignLiteral("null");
6571 return NS_OK;
6574 /* static */
6575 nsresult nsContentUtils::GetWebExposedOriginSerialization(
6576 nsIPrincipal* aPrincipal, nsAString& aOrigin) {
6577 MOZ_ASSERT(aPrincipal, "missing principal");
6579 aOrigin.Truncate();
6580 nsAutoCString webExposedOriginSerialization;
6582 nsresult rv = aPrincipal->GetWebExposedOriginSerialization(
6583 webExposedOriginSerialization);
6584 if (NS_FAILED(rv)) {
6585 webExposedOriginSerialization.AssignLiteral("null");
6588 CopyUTF8toUTF16(webExposedOriginSerialization, aOrigin);
6589 return NS_OK;
6592 /* static */
6593 nsresult nsContentUtils::GetWebExposedOriginSerialization(nsIURI* aURI,
6594 nsAString& aOrigin) {
6595 MOZ_ASSERT(aURI, "missing uri");
6596 nsresult rv;
6598 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
6599 // Check if either URI has a special origin.
6600 nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin =
6601 do_QueryInterface(aURI);
6602 if (uriWithSpecialOrigin) {
6603 nsCOMPtr<nsIURI> origin;
6604 rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin));
6605 NS_ENSURE_SUCCESS(rv, rv);
6607 return GetWebExposedOriginSerialization(origin, aOrigin);
6609 #endif
6611 nsAutoCString webExposedOriginSerialization;
6612 rv = GetWebExposedOriginSerialization(aURI, webExposedOriginSerialization);
6613 NS_ENSURE_SUCCESS(rv, rv);
6615 CopyUTF8toUTF16(webExposedOriginSerialization, aOrigin);
6616 return NS_OK;
6619 /* static */
6620 bool nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal,
6621 nsIChannel* aChannel,
6622 bool aAllowIfInheritsPrincipal) {
6623 nsCOMPtr<nsIURI> channelURI;
6624 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
6625 NS_ENSURE_SUCCESS(rv, false);
6627 return NS_SUCCEEDED(
6628 aPrincipal->CheckMayLoad(channelURI, aAllowIfInheritsPrincipal));
6631 /* static */
6632 bool nsContentUtils::CanAccessNativeAnon() {
6633 return LegacyIsCallerChromeOrNativeCode();
6636 /* static */
6637 nsresult nsContentUtils::DispatchXULCommand(nsIContent* aTarget, bool aTrusted,
6638 Event* aSourceEvent,
6639 PresShell* aPresShell, bool aCtrl,
6640 bool aAlt, bool aShift, bool aMeta,
6641 uint16_t aInputSource,
6642 int16_t aButton) {
6643 NS_ENSURE_STATE(aTarget);
6644 Document* doc = aTarget->OwnerDoc();
6645 nsPresContext* presContext = doc->GetPresContext();
6647 RefPtr<XULCommandEvent> xulCommand =
6648 new XULCommandEvent(doc, presContext, nullptr);
6649 xulCommand->InitCommandEvent(u"command"_ns, true, true,
6650 nsGlobalWindowInner::Cast(doc->GetInnerWindow()),
6651 0, aCtrl, aAlt, aShift, aMeta, aButton,
6652 aSourceEvent, aInputSource, IgnoreErrors());
6654 if (aPresShell) {
6655 nsEventStatus status = nsEventStatus_eIgnore;
6656 return aPresShell->HandleDOMEventWithTarget(aTarget, xulCommand, &status);
6659 ErrorResult rv;
6660 aTarget->DispatchEvent(*xulCommand, rv);
6661 return rv.StealNSResult();
6664 // static
6665 nsresult nsContentUtils::WrapNative(JSContext* cx, nsISupports* native,
6666 nsWrapperCache* cache, const nsIID* aIID,
6667 JS::MutableHandle<JS::Value> vp,
6668 bool aAllowWrapping) {
6669 MOZ_ASSERT(cx == GetCurrentJSContext());
6671 if (!native) {
6672 vp.setNull();
6674 return NS_OK;
6677 JSObject* wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
6678 if (wrapper) {
6679 return NS_OK;
6682 NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
6684 if (!NS_IsMainThread()) {
6685 MOZ_CRASH();
6688 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
6689 nsresult rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
6690 aAllowWrapping, vp);
6691 return rv;
6694 nsresult nsContentUtils::CreateArrayBuffer(JSContext* aCx,
6695 const nsACString& aData,
6696 JSObject** aResult) {
6697 if (!aCx) {
6698 return NS_ERROR_FAILURE;
6701 size_t dataLen = aData.Length();
6702 *aResult = JS::NewArrayBuffer(aCx, dataLen);
6703 if (!*aResult) {
6704 return NS_ERROR_FAILURE;
6707 if (dataLen > 0) {
6708 NS_ASSERTION(JS::IsArrayBufferObject(*aResult), "What happened?");
6709 JS::AutoCheckCannotGC nogc;
6710 bool isShared;
6711 memcpy(JS::GetArrayBufferData(*aResult, &isShared, nogc),
6712 aData.BeginReading(), dataLen);
6713 MOZ_ASSERT(!isShared);
6716 return NS_OK;
6719 void nsContentUtils::StripNullChars(const nsAString& aInStr,
6720 nsAString& aOutStr) {
6721 // In common cases where we don't have nulls in the
6722 // string we can simple simply bypass the checking code.
6723 int32_t firstNullPos = aInStr.FindChar('\0');
6724 if (firstNullPos == kNotFound) {
6725 aOutStr.Assign(aInStr);
6726 return;
6729 aOutStr.SetCapacity(aInStr.Length() - 1);
6730 nsAString::const_iterator start, end;
6731 aInStr.BeginReading(start);
6732 aInStr.EndReading(end);
6733 while (start != end) {
6734 if (*start != '\0') aOutStr.Append(*start);
6735 ++start;
6739 struct ClassMatchingInfo {
6740 AtomArray mClasses;
6741 nsCaseTreatment mCaseTreatment;
6744 // static
6745 bool nsContentUtils::MatchClassNames(Element* aElement, int32_t aNamespaceID,
6746 nsAtom* aAtom, void* aData) {
6747 // We can't match if there are no class names
6748 const nsAttrValue* classAttr = aElement->GetClasses();
6749 if (!classAttr) {
6750 return false;
6753 // need to match *all* of the classes
6754 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6755 uint32_t length = info->mClasses.Length();
6756 if (!length) {
6757 // If we actually had no classes, don't match.
6758 return false;
6760 uint32_t i;
6761 for (i = 0; i < length; ++i) {
6762 if (!classAttr->Contains(info->mClasses[i], info->mCaseTreatment)) {
6763 return false;
6767 return true;
6770 // static
6771 void nsContentUtils::DestroyClassNameArray(void* aData) {
6772 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6773 delete info;
6776 // static
6777 void* nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
6778 const nsString* aClasses) {
6779 nsAttrValue attrValue;
6780 attrValue.ParseAtomArray(*aClasses);
6781 // nsAttrValue::Equals is sensitive to order, so we'll send an array
6782 auto* info = new ClassMatchingInfo;
6783 if (attrValue.Type() == nsAttrValue::eAtomArray) {
6784 info->mClasses = attrValue.GetAtomArrayValue()->mArray.Clone();
6785 } else if (attrValue.Type() == nsAttrValue::eAtom) {
6786 info->mClasses.AppendElement(attrValue.GetAtomValue());
6789 info->mCaseTreatment =
6790 aRootNode->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks
6791 ? eIgnoreCase
6792 : eCaseMatters;
6793 return info;
6796 // static
6797 bool nsContentUtils::IsFocusedContent(const nsIContent* aContent) {
6798 nsFocusManager* fm = nsFocusManager::GetFocusManager();
6800 return fm && fm->GetFocusedElement() == aContent;
6803 bool nsContentUtils::HasScrollgrab(nsIContent* aContent) {
6804 // If we ever standardize this feature we'll want to hook this up properly
6805 // again. For now we're removing all the DOM-side code related to it but
6806 // leaving the layout and APZ handling for it in place.
6807 return false;
6810 void nsContentUtils::FlushLayoutForTree(nsPIDOMWindowOuter* aWindow) {
6811 if (!aWindow) {
6812 return;
6815 // Note that because FlushPendingNotifications flushes parents, this
6816 // is O(N^2) in docshell tree depth. However, the docshell tree is
6817 // usually pretty shallow.
6819 if (RefPtr<Document> doc = aWindow->GetDoc()) {
6820 doc->FlushPendingNotifications(FlushType::Layout);
6823 if (nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell()) {
6824 int32_t i = 0, i_end;
6825 docShell->GetInProcessChildCount(&i_end);
6826 for (; i < i_end; ++i) {
6827 nsCOMPtr<nsIDocShellTreeItem> item;
6828 if (docShell->GetInProcessChildAt(i, getter_AddRefs(item)) == NS_OK &&
6829 item) {
6830 if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
6831 FlushLayoutForTree(win);
6838 void nsContentUtils::RemoveNewlines(nsString& aString) { aString.StripCRLF(); }
6840 void nsContentUtils::PlatformToDOMLineBreaks(nsString& aString) {
6841 if (!PlatformToDOMLineBreaks(aString, fallible)) {
6842 aString.AllocFailed(aString.Length());
6846 bool nsContentUtils::PlatformToDOMLineBreaks(nsString& aString,
6847 const fallible_t& aFallible) {
6848 if (aString.FindChar(char16_t('\r')) != -1) {
6849 // Windows linebreaks: Map CRLF to LF:
6850 if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
6851 return false;
6854 // Mac linebreaks: Map any remaining CR to LF:
6855 if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
6856 return false;
6860 return true;
6863 void nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
6864 nsAString& aResultString) {
6865 MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
6867 uint32_t stringLen = NS_strlen(static_cast<char16_t*>(aBuf->Data()));
6869 // SANITY CHECK: In case the nsStringBuffer isn't correctly
6870 // null-terminated, let's clamp its length using the allocated size, to be
6871 // sure the resulting string doesn't sample past the end of the the buffer.
6872 // (Note that StorageSize() is in units of bytes, so we have to convert that
6873 // to units of PRUnichars, and subtract 1 for the null-terminator.)
6874 uint32_t allocStringLen = (aBuf->StorageSize() / sizeof(char16_t)) - 1;
6875 MOZ_ASSERT(stringLen <= allocStringLen,
6876 "string buffer lacks null terminator!");
6877 stringLen = std::min(stringLen, allocStringLen);
6879 aBuf->ToString(stringLen, aResultString);
6882 already_AddRefed<nsContentList> nsContentUtils::GetElementsByClassName(
6883 nsINode* aRootNode, const nsAString& aClasses) {
6884 MOZ_ASSERT(aRootNode, "Must have root node");
6886 return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(
6887 aRootNode, MatchClassNames, DestroyClassNameArray, AllocClassMatchingInfo,
6888 aClasses);
6891 PresShell* nsContentUtils::FindPresShellForDocument(const Document* aDocument) {
6892 const Document* doc = aDocument;
6893 Document* displayDoc = doc->GetDisplayDocument();
6894 if (displayDoc) {
6895 doc = displayDoc;
6898 PresShell* presShell = doc->GetPresShell();
6899 if (presShell) {
6900 return presShell;
6903 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
6904 while (docShellTreeItem) {
6905 // We may be in a display:none subdocument, or we may not have a presshell
6906 // created yet.
6907 // Walk the docshell tree to find the nearest container that has a
6908 // presshell, and return that.
6909 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
6910 if (PresShell* presShell = docShell->GetPresShell()) {
6911 return presShell;
6913 nsCOMPtr<nsIDocShellTreeItem> parent;
6914 docShellTreeItem->GetInProcessParent(getter_AddRefs(parent));
6915 docShellTreeItem = parent;
6918 return nullptr;
6921 /* static */
6922 nsPresContext* nsContentUtils::FindPresContextForDocument(
6923 const Document* aDocument) {
6924 if (PresShell* presShell = FindPresShellForDocument(aDocument)) {
6925 return presShell->GetPresContext();
6927 return nullptr;
6930 nsIWidget* nsContentUtils::WidgetForDocument(const Document* aDocument) {
6931 PresShell* presShell = FindPresShellForDocument(aDocument);
6932 if (!presShell) {
6933 return nullptr;
6935 nsViewManager* vm = presShell->GetViewManager();
6936 if (!vm) {
6937 return nullptr;
6939 nsView* rootView = vm->GetRootView();
6940 if (!rootView) {
6941 return nullptr;
6943 nsView* displayRoot = nsViewManager::GetDisplayRootFor(rootView);
6944 if (!displayRoot) {
6945 return nullptr;
6947 return displayRoot->GetNearestWidget(nullptr);
6950 nsIWidget* nsContentUtils::WidgetForContent(const nsIContent* aContent) {
6951 nsIFrame* frame = aContent->GetPrimaryFrame();
6952 if (frame) {
6953 frame = nsLayoutUtils::GetDisplayRootFrame(frame);
6955 nsView* view = frame->GetView();
6956 if (view) {
6957 return view->GetWidget();
6961 return nullptr;
6964 WindowRenderer* nsContentUtils::WindowRendererForContent(
6965 const nsIContent* aContent) {
6966 nsIWidget* widget = nsContentUtils::WidgetForContent(aContent);
6967 if (widget) {
6968 return widget->GetWindowRenderer();
6971 return nullptr;
6974 WindowRenderer* nsContentUtils::WindowRendererForDocument(
6975 const Document* aDoc) {
6976 nsIWidget* widget = nsContentUtils::WidgetForDocument(aDoc);
6977 if (widget) {
6978 return widget->GetWindowRenderer();
6981 return nullptr;
6984 bool nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal) {
6985 if (!aPrincipal) {
6986 return false;
6989 if (aPrincipal->IsSystemPrincipal()) {
6990 return true;
6993 return xpc::IsInAutomation() && IsSitePermAllow(aPrincipal, "allowXULXBL"_ns);
6996 bool nsContentUtils::IsPDFJSEnabled() {
6997 nsCOMPtr<nsIStreamConverter> conv = do_CreateInstance(
6998 "@mozilla.org/streamconv;1?from=application/pdf&to=text/html");
6999 return conv;
7002 bool nsContentUtils::IsPDFJS(nsIPrincipal* aPrincipal) {
7003 if (!aPrincipal || !aPrincipal->SchemeIs("resource")) {
7004 return false;
7006 nsAutoCString spec;
7007 nsresult rv = aPrincipal->GetAsciiSpec(spec);
7008 NS_ENSURE_SUCCESS(rv, false);
7009 return spec.EqualsLiteral("resource://pdf.js/web/viewer.html");
7012 bool nsContentUtils::IsSystemOrPDFJS(JSContext* aCx, JSObject*) {
7013 nsIPrincipal* principal = SubjectPrincipal(aCx);
7014 return principal && (principal->IsSystemPrincipal() || IsPDFJS(principal));
7017 already_AddRefed<nsIDocumentLoaderFactory>
7018 nsContentUtils::FindInternalContentViewer(const nsACString& aType,
7019 ContentViewerType* aLoaderType) {
7020 if (aLoaderType) {
7021 *aLoaderType = TYPE_UNSUPPORTED;
7024 // one helper factory, please
7025 nsCOMPtr<nsICategoryManager> catMan(
7026 do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
7027 if (!catMan) return nullptr;
7029 nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
7031 nsCString contractID;
7032 nsresult rv =
7033 catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, contractID);
7034 if (NS_SUCCEEDED(rv)) {
7035 docFactory = do_GetService(contractID.get());
7036 if (docFactory && aLoaderType) {
7037 if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
7038 *aLoaderType = TYPE_CONTENT;
7039 else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
7040 *aLoaderType = TYPE_FALLBACK;
7041 else
7042 *aLoaderType = TYPE_UNKNOWN;
7044 return docFactory.forget();
7047 if (DecoderTraits::IsSupportedInVideoDocument(aType)) {
7048 docFactory =
7049 do_GetService("@mozilla.org/content/document-loader-factory;1");
7050 if (docFactory && aLoaderType) {
7051 *aLoaderType = TYPE_CONTENT;
7053 return docFactory.forget();
7056 return nullptr;
7059 static void ReportPatternCompileFailure(nsAString& aPattern,
7060 const Document* aDocument,
7061 JS::MutableHandle<JS::Value> error,
7062 JSContext* cx) {
7063 JS::AutoSaveExceptionState savedExc(cx);
7064 JS::Rooted<JSObject*> exnObj(cx, &error.toObject());
7065 JS::Rooted<JS::Value> messageVal(cx);
7066 if (!JS_GetProperty(cx, exnObj, "message", &messageVal)) {
7067 return;
7069 JS::Rooted<JSString*> messageStr(cx, messageVal.toString());
7070 MOZ_ASSERT(messageStr);
7072 AutoTArray<nsString, 2> strings;
7073 strings.AppendElement(aPattern);
7074 if (!AssignJSString(cx, *strings.AppendElement(), messageStr)) {
7075 return;
7078 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns,
7079 aDocument, nsContentUtils::eDOM_PROPERTIES,
7080 "PatternAttributeCompileFailure", strings);
7081 savedExc.drop();
7084 // static
7085 Maybe<bool> nsContentUtils::IsPatternMatching(const nsAString& aValue,
7086 nsString&& aPattern,
7087 const Document* aDocument,
7088 bool aHasMultiple,
7089 JS::RegExpFlags aFlags) {
7090 NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
7092 // The fact that we're using a JS regexp under the hood should not be visible
7093 // to things like window onerror handlers, so we don't initialize our JSAPI
7094 // with the document's window (which may not exist anyway).
7095 AutoJSAPI jsapi;
7096 jsapi.Init();
7097 JSContext* cx = jsapi.cx();
7098 AutoDisableJSInterruptCallback disabler(cx);
7100 // We can use the junk scope here, because we're just using it for regexp
7101 // evaluation, not actual script execution, and we disable statics so that the
7102 // evaluation does not interact with the execution global.
7103 JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
7105 // Check if the pattern by itself is valid first, and not that it only becomes
7106 // valid once we add ^(?: and )$.
7107 JS::Rooted<JS::Value> error(cx);
7108 if (!JS::CheckRegExpSyntax(cx, aPattern.BeginReading(), aPattern.Length(),
7109 aFlags, &error)) {
7110 return Nothing();
7113 if (!error.isUndefined()) {
7114 ReportPatternCompileFailure(aPattern, aDocument, &error, cx);
7115 return Some(true);
7118 // The pattern has to match the entire value.
7119 aPattern.InsertLiteral(u"^(?:", 0);
7120 aPattern.AppendLiteral(")$");
7122 JS::Rooted<JSObject*> re(
7123 cx, JS::NewUCRegExpObject(cx, aPattern.BeginReading(), aPattern.Length(),
7124 aFlags));
7125 if (!re) {
7126 return Nothing();
7129 JS::Rooted<JS::Value> rval(cx, JS::NullValue());
7130 if (!aHasMultiple) {
7131 size_t idx = 0;
7132 if (!JS::ExecuteRegExpNoStatics(cx, re, aValue.BeginReading(),
7133 aValue.Length(), &idx, true, &rval)) {
7134 return Nothing();
7136 return Some(!rval.isNull());
7139 HTMLSplitOnSpacesTokenizer tokenizer(aValue, ',');
7140 while (tokenizer.hasMoreTokens()) {
7141 const nsAString& value = tokenizer.nextToken();
7142 size_t idx = 0;
7143 if (!JS::ExecuteRegExpNoStatics(cx, re, value.BeginReading(),
7144 value.Length(), &idx, true, &rval)) {
7145 return Nothing();
7147 if (rval.isNull()) {
7148 return Some(false);
7151 return Some(true);
7154 // static
7155 nsresult nsContentUtils::URIInheritsSecurityContext(nsIURI* aURI,
7156 bool* aResult) {
7157 // Note: about:blank URIs do NOT inherit the security context from the
7158 // current document, which is what this function tests for...
7159 return NS_URIChainHasFlags(
7160 aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, aResult);
7163 // static
7164 bool nsContentUtils::ChannelShouldInheritPrincipal(
7165 nsIPrincipal* aLoadingPrincipal, nsIURI* aURI, bool aInheritForAboutBlank,
7166 bool aForceInherit) {
7167 MOZ_ASSERT(aLoadingPrincipal,
7168 "Can not check inheritance without a principal");
7170 // Only tell the channel to inherit if it can't provide its own security
7171 // context.
7173 // XXX: If this is ever changed, check all callers for what owners
7174 // they're passing in. In particular, see the code and
7175 // comments in nsDocShell::LoadURI where we fall back on
7176 // inheriting the owner if called from chrome. That would be
7177 // very wrong if this code changed anything but channels that
7178 // can't provide their own security context!
7180 // If aForceInherit is true, we will inherit, even for a channel that
7181 // can provide its own security context. This is used for srcdoc loads.
7182 bool inherit = aForceInherit;
7183 if (!inherit) {
7184 bool uriInherits;
7185 // We expect URIInheritsSecurityContext to return success for an
7186 // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
7187 // This condition needs to match the one in nsDocShell::InternalLoad where
7188 // we're checking for things that will use the owner.
7189 inherit =
7190 (NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &uriInherits)) &&
7191 (uriInherits || (aInheritForAboutBlank && NS_IsAboutBlank(aURI)))) ||
7193 // file: uri special-casing
7195 // If this is a file: load opened from another file: then it may need
7196 // to inherit the owner from the referrer so they can script each other.
7197 // If we don't set the owner explicitly then each file: gets an owner
7198 // based on its own codebase later.
7200 (URIIsLocalFile(aURI) &&
7201 NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, false)) &&
7202 // One more check here. CheckMayLoad will always return true for the
7203 // system principal, but we do NOT want to inherit in that case.
7204 !aLoadingPrincipal->IsSystemPrincipal());
7206 return inherit;
7209 /* static */
7210 bool nsContentUtils::IsCutCopyAllowed(Document* aDocument,
7211 nsIPrincipal& aSubjectPrincipal) {
7212 if (StaticPrefs::dom_allow_cut_copy() && aDocument &&
7213 aDocument->HasValidTransientUserGestureActivation()) {
7214 return true;
7217 return PrincipalHasPermission(aSubjectPrincipal, nsGkAtoms::clipboardWrite);
7220 /* static */
7221 bool nsContentUtils::HaveEqualPrincipals(Document* aDoc1, Document* aDoc2) {
7222 if (!aDoc1 || !aDoc2) {
7223 return false;
7225 bool principalsEqual = false;
7226 aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
7227 return principalsEqual;
7230 /* static */
7231 void nsContentUtils::FireMutationEventsForDirectParsing(
7232 Document* aDoc, nsIContent* aDest, int32_t aOldChildCount) {
7233 // Fire mutation events. Optimize for the case when there are no listeners
7234 int32_t newChildCount = aDest->GetChildCount();
7235 if (newChildCount && nsContentUtils::HasMutationListeners(
7236 aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
7237 AutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
7238 NS_ASSERTION(newChildCount - aOldChildCount >= 0,
7239 "What, some unexpected dom mutation has happened?");
7240 childNodes.SetCapacity(newChildCount - aOldChildCount);
7241 for (nsIContent* child = aDest->GetFirstChild(); child;
7242 child = child->GetNextSibling()) {
7243 childNodes.AppendElement(child);
7245 FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
7249 /* static */
7250 const Document* nsContentUtils::GetInProcessSubtreeRootDocument(
7251 const Document* aDoc) {
7252 if (!aDoc) {
7253 return nullptr;
7255 const Document* doc = aDoc;
7256 while (doc->GetInProcessParentDocument()) {
7257 doc = doc->GetInProcessParentDocument();
7259 return doc;
7262 // static
7263 int32_t nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
7264 int32_t aOffset) {
7265 // The structure of the anonymous frames within a text control frame is
7266 // an optional block frame, followed by an optional br frame.
7268 // If the offset frame has a child, then this frame is the block which
7269 // has the text frames (containing the content) as its children. This will
7270 // be the case if we click to the right of any of the text frames, or at the
7271 // bottom of the text area.
7272 nsIFrame* firstChild = aOffsetFrame->PrincipalChildList().FirstChild();
7273 if (firstChild) {
7274 // In this case, the passed-in offset is incorrect, and we want the length
7275 // of the entire content in the text control frame.
7276 return firstChild->GetContent()->Length();
7279 if (aOffsetFrame->GetPrevSibling() && !aOffsetFrame->GetNextSibling()) {
7280 // In this case, we're actually within the last frame, which is a br
7281 // frame. Our offset should therefore be the length of the first child of
7282 // our parent.
7283 int32_t aOutOffset = aOffsetFrame->GetParent()
7284 ->PrincipalChildList()
7285 .FirstChild()
7286 ->GetContent()
7287 ->Length();
7288 return aOutOffset;
7291 // Otherwise, we're within one of the text frames, in which case our offset
7292 // has already been correctly calculated.
7293 return aOffset;
7296 // static
7297 void nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
7298 Element* aRoot,
7299 uint32_t& aOutStartOffset,
7300 uint32_t& aOutEndOffset) {
7301 MOZ_ASSERT(aSelection && aRoot);
7303 // We don't care which end of this selection is anchor and which is focus. In
7304 // fact, we explicitly want to know which is the _start_ and which is the
7305 // _end_, not anchor vs focus.
7306 const nsRange* range = aSelection->GetAnchorFocusRange();
7307 if (!range) {
7308 // Nothing selected
7309 aOutStartOffset = aOutEndOffset = 0;
7310 return;
7313 // All the node pointers here are raw pointers for performance. We shouldn't
7314 // be doing anything in this function that invalidates the node tree.
7315 nsINode* startContainer = range->GetStartContainer();
7316 uint32_t startOffset = range->StartOffset();
7317 nsINode* endContainer = range->GetEndContainer();
7318 uint32_t endOffset = range->EndOffset();
7320 // We have at most two children, consisting of an optional text node followed
7321 // by an optional <br>.
7322 NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
7323 nsIContent* firstChild = aRoot->GetFirstChild();
7324 #ifdef DEBUG
7325 nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
7326 NS_ASSERTION(startContainer == aRoot || startContainer == firstChild ||
7327 startContainer == lastChild,
7328 "Unexpected startContainer");
7329 NS_ASSERTION(endContainer == aRoot || endContainer == firstChild ||
7330 endContainer == lastChild,
7331 "Unexpected endContainer");
7332 // firstChild is either text or a <br> (hence an element).
7333 MOZ_ASSERT_IF(firstChild, firstChild->IsText() || firstChild->IsElement());
7334 #endif
7335 if (!firstChild || firstChild->IsElement()) {
7336 // No text node, so everything is 0
7337 startOffset = endOffset = 0;
7338 } else {
7339 // First child is text. If the start/end is already in the text node,
7340 // or the start of the root node, no change needed. If it's in the root
7341 // node but not the start, or in the trailing <br>, we need to set the
7342 // offset to the end.
7343 if ((startContainer == aRoot && startOffset != 0) ||
7344 (startContainer != aRoot && startContainer != firstChild)) {
7345 startOffset = firstChild->Length();
7347 if ((endContainer == aRoot && endOffset != 0) ||
7348 (endContainer != aRoot && endContainer != firstChild)) {
7349 endOffset = firstChild->Length();
7353 MOZ_ASSERT(startOffset <= endOffset);
7354 aOutStartOffset = startOffset;
7355 aOutEndOffset = endOffset;
7358 // static
7359 HTMLEditor* nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext) {
7360 if (!aPresContext) {
7361 return nullptr;
7363 return GetHTMLEditor(aPresContext->GetDocShell());
7366 // static
7367 HTMLEditor* nsContentUtils::GetHTMLEditor(nsDocShell* aDocShell) {
7368 bool isEditable;
7369 if (!aDocShell || NS_FAILED(aDocShell->GetEditable(&isEditable)) ||
7370 !isEditable) {
7371 return nullptr;
7373 return aDocShell->GetHTMLEditor();
7376 // static
7377 EditorBase* nsContentUtils::GetActiveEditor(nsPresContext* aPresContext) {
7378 if (!aPresContext) {
7379 return nullptr;
7382 return GetActiveEditor(aPresContext->Document()->GetWindow());
7385 // static
7386 EditorBase* nsContentUtils::GetActiveEditor(nsPIDOMWindowOuter* aWindow) {
7387 if (!aWindow || !aWindow->GetExtantDoc()) {
7388 return nullptr;
7391 // If it's in designMode, nobody can have focus. Therefore, the HTMLEditor
7392 // handles all events. I.e., it's focused editor in this case.
7393 if (aWindow->GetExtantDoc()->IsInDesignMode()) {
7394 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7397 // If focused element is associated with TextEditor, it must be <input>
7398 // element or <textarea> element. Let's return it even if it's in a
7399 // contenteditable element.
7400 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
7401 if (Element* focusedElement = nsFocusManager::GetFocusedDescendant(
7402 aWindow, nsFocusManager::SearchRange::eOnlyCurrentWindow,
7403 getter_AddRefs(focusedWindow))) {
7404 if (TextEditor* textEditor = focusedElement->GetTextEditorInternal()) {
7405 return textEditor;
7409 // Otherwise, HTMLEditor may handle inputs even non-editable element has
7410 // focus or nobody has focus.
7411 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7414 // static
7415 TextEditor* nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(
7416 const nsIContent* aAnonymousContent) {
7417 if (!aAnonymousContent) {
7418 return nullptr;
7420 nsIContent* parent = aAnonymousContent->FindFirstNonChromeOnlyAccessContent();
7421 if (!parent || parent == aAnonymousContent) {
7422 return nullptr;
7424 if (HTMLInputElement* inputElement =
7425 HTMLInputElement::FromNodeOrNull(parent)) {
7426 return inputElement->GetTextEditorWithoutCreation();
7428 if (HTMLTextAreaElement* textareaElement =
7429 HTMLTextAreaElement::FromNodeOrNull(parent)) {
7430 return textareaElement->GetTextEditorWithoutCreation();
7432 return nullptr;
7435 // static
7436 bool nsContentUtils::IsNodeInEditableRegion(nsINode* aNode) {
7437 while (aNode) {
7438 if (aNode->IsEditable()) {
7439 return true;
7441 aNode = aNode->GetParent();
7443 return false;
7446 // static
7447 bool nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader,
7448 const nsACString& aValue) {
7449 if (IsForbiddenSystemRequestHeader(aHeader)) {
7450 return true;
7453 if ((nsContentUtils::IsOverrideMethodHeader(aHeader) &&
7454 nsContentUtils::ContainsForbiddenMethod(aValue))) {
7455 return true;
7458 if (StringBeginsWith(aHeader, "proxy-"_ns,
7459 nsCaseInsensitiveCStringComparator) ||
7460 StringBeginsWith(aHeader, "sec-"_ns,
7461 nsCaseInsensitiveCStringComparator)) {
7462 return true;
7465 return false;
7468 // static
7469 bool nsContentUtils::IsForbiddenSystemRequestHeader(const nsACString& aHeader) {
7470 static const char* kInvalidHeaders[] = {"accept-charset",
7471 "accept-encoding",
7472 "access-control-request-headers",
7473 "access-control-request-method",
7474 "connection",
7475 "content-length",
7476 "cookie",
7477 "cookie2",
7478 "date",
7479 "dnt",
7480 "expect",
7481 "host",
7482 "keep-alive",
7483 "origin",
7484 "referer",
7485 "set-cookie",
7486 "te",
7487 "trailer",
7488 "transfer-encoding",
7489 "upgrade",
7490 "via"};
7491 for (auto& kInvalidHeader : kInvalidHeaders) {
7492 if (aHeader.LowerCaseEqualsASCII(kInvalidHeader)) {
7493 return true;
7496 return false;
7499 // static
7500 bool nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader) {
7501 return (aHeader.LowerCaseEqualsASCII("set-cookie") ||
7502 aHeader.LowerCaseEqualsASCII("set-cookie2"));
7505 // static
7506 bool nsContentUtils::IsOverrideMethodHeader(const nsACString& headerName) {
7507 return headerName.EqualsIgnoreCase("x-http-method-override") ||
7508 headerName.EqualsIgnoreCase("x-http-method") ||
7509 headerName.EqualsIgnoreCase("x-method-override");
7512 // static
7513 bool nsContentUtils::ContainsForbiddenMethod(const nsACString& headerValue) {
7514 bool hasInsecureMethod = false;
7515 nsCCharSeparatedTokenizer tokenizer(headerValue, ',');
7517 while (tokenizer.hasMoreTokens()) {
7518 const nsDependentCSubstring& value = tokenizer.nextToken();
7520 if (value.EqualsIgnoreCase("connect") || value.EqualsIgnoreCase("trace") ||
7521 value.EqualsIgnoreCase("track")) {
7522 hasInsecureMethod = true;
7523 break;
7527 return hasInsecureMethod;
7530 Maybe<nsContentUtils::ParsedRange> nsContentUtils::ParseSingleRangeRequest(
7531 const nsACString& aHeaderValue, bool aAllowWhitespace) {
7532 // See https://fetch.spec.whatwg.org/#simple-range-header-value
7533 mozilla::Tokenizer p(aHeaderValue);
7534 Maybe<uint64_t> rangeStart;
7535 Maybe<uint64_t> rangeEnd;
7537 // Step 2 and 3
7538 if (!p.CheckWord("bytes")) {
7539 return Nothing();
7542 // Step 4
7543 if (aAllowWhitespace) {
7544 p.SkipWhites();
7547 // Step 5 and 6
7548 if (!p.CheckChar('=')) {
7549 return Nothing();
7552 // Step 7
7553 if (aAllowWhitespace) {
7554 p.SkipWhites();
7557 // Step 8 and 9
7558 uint64_t res;
7559 if (p.ReadInteger(&res)) {
7560 rangeStart = Some(res);
7563 // Step 10
7564 if (aAllowWhitespace) {
7565 p.SkipWhites();
7568 // Step 11
7569 if (!p.CheckChar('-')) {
7570 return Nothing();
7573 // Step 13
7574 if (aAllowWhitespace) {
7575 p.SkipWhites();
7578 // Step 14 and 15
7579 if (p.ReadInteger(&res)) {
7580 rangeEnd = Some(res);
7583 // Step 16
7584 if (!p.CheckEOF()) {
7585 return Nothing();
7588 // Step 17
7589 if (!rangeStart && !rangeEnd) {
7590 return Nothing();
7593 // Step 18
7594 if (rangeStart && rangeEnd && *rangeStart > *rangeEnd) {
7595 return Nothing();
7598 return Some(ParsedRange(rangeStart, rangeEnd));
7601 // static
7602 bool nsContentUtils::IsCorsUnsafeRequestHeaderValue(
7603 const nsACString& aHeaderValue) {
7604 const char* cur = aHeaderValue.BeginReading();
7605 const char* end = aHeaderValue.EndReading();
7607 while (cur != end) {
7608 // Implementation of
7609 // https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte Is less
7610 // than a space but not a horizontal tab
7611 if ((*cur < ' ' && *cur != '\t') || *cur == '"' || *cur == '(' ||
7612 *cur == ')' || *cur == ':' || *cur == '<' || *cur == '>' ||
7613 *cur == '?' || *cur == '@' || *cur == '[' || *cur == '\\' ||
7614 *cur == ']' || *cur == '{' || *cur == '}' ||
7615 *cur == 0x7F) { // 0x75 is DEL
7616 return true;
7618 cur++;
7620 return false;
7623 // static
7624 bool nsContentUtils::IsAllowedNonCorsAccept(const nsACString& aHeaderValue) {
7625 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7626 return false;
7628 return true;
7631 // static
7632 bool nsContentUtils::IsAllowedNonCorsContentType(
7633 const nsACString& aHeaderValue) {
7634 nsAutoCString contentType;
7635 nsAutoCString unused;
7637 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7638 return false;
7641 nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
7642 if (NS_FAILED(rv)) {
7643 return false;
7646 return contentType.LowerCaseEqualsLiteral("text/plain") ||
7647 contentType.LowerCaseEqualsLiteral(
7648 "application/x-www-form-urlencoded") ||
7649 contentType.LowerCaseEqualsLiteral("multipart/form-data");
7652 // static
7653 bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
7654 const char* cur = aHeaderValue.BeginReading();
7655 const char* end = aHeaderValue.EndReading();
7657 while (cur != end) {
7658 if ((*cur >= '0' && *cur <= '9') || (*cur >= 'A' && *cur <= 'Z') ||
7659 (*cur >= 'a' && *cur <= 'z') || *cur == ' ' || *cur == '*' ||
7660 *cur == ',' || *cur == '-' || *cur == '.' || *cur == ';' ||
7661 *cur == '=') {
7662 cur++;
7663 continue;
7665 return false;
7667 return true;
7670 bool nsContentUtils::IsAllowedNonCorsRange(const nsACString& aHeaderValue) {
7671 Maybe<ParsedRange> parsedRange = ParseSingleRangeRequest(aHeaderValue, false);
7672 if (!parsedRange) {
7673 return false;
7676 if (!parsedRange->Start()) {
7677 return false;
7680 return true;
7683 // static
7684 bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
7685 const nsACString& aValue) {
7686 // see https://fetch.spec.whatwg.org/#cors-safelisted-request-header
7687 if (aValue.Length() > 128) {
7688 return false;
7690 return (aName.LowerCaseEqualsLiteral("accept") &&
7691 nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
7692 (aName.LowerCaseEqualsLiteral("accept-language") &&
7693 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7694 (aName.LowerCaseEqualsLiteral("content-language") &&
7695 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7696 (aName.LowerCaseEqualsLiteral("content-type") &&
7697 nsContentUtils::IsAllowedNonCorsContentType(aValue)) ||
7698 (aName.LowerCaseEqualsLiteral("range") &&
7699 nsContentUtils::IsAllowedNonCorsRange(aValue));
7702 mozilla::LogModule* nsContentUtils::ResistFingerprintingLog() {
7703 return gResistFingerprintingLog;
7705 mozilla::LogModule* nsContentUtils::DOMDumpLog() { return sDOMDumpLog; }
7707 bool nsContentUtils::GetNodeTextContent(const nsINode* aNode, bool aDeep,
7708 nsAString& aResult,
7709 const fallible_t& aFallible) {
7710 aResult.Truncate();
7711 return AppendNodeTextContent(aNode, aDeep, aResult, aFallible);
7714 void nsContentUtils::GetNodeTextContent(const nsINode* aNode, bool aDeep,
7715 nsAString& aResult) {
7716 if (!GetNodeTextContent(aNode, aDeep, aResult, fallible)) {
7717 NS_ABORT_OOM(0); // Unfortunately we don't know the allocation size
7721 void nsContentUtils::DestroyMatchString(void* aData) {
7722 if (aData) {
7723 nsString* matchString = static_cast<nsString*>(aData);
7724 delete matchString;
7728 bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
7729 // Table ordered from most to least likely JS MIME types.
7730 static const char* jsTypes[] = {"text/javascript",
7731 "text/ecmascript",
7732 "application/javascript",
7733 "application/ecmascript",
7734 "application/x-javascript",
7735 "application/x-ecmascript",
7736 "text/javascript1.0",
7737 "text/javascript1.1",
7738 "text/javascript1.2",
7739 "text/javascript1.3",
7740 "text/javascript1.4",
7741 "text/javascript1.5",
7742 "text/jscript",
7743 "text/livescript",
7744 "text/x-ecmascript",
7745 "text/x-javascript",
7746 nullptr};
7748 for (uint32_t i = 0; jsTypes[i]; ++i) {
7749 if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
7750 return true;
7754 return false;
7757 bool nsContentUtils::PrefetchPreloadEnabled(nsIDocShell* aDocShell) {
7759 // SECURITY CHECK: disable prefetching and preloading from mailnews!
7761 // walk up the docshell tree to see if any containing
7762 // docshell are of type MAIL.
7765 if (!aDocShell) {
7766 return false;
7769 nsCOMPtr<nsIDocShell> docshell = aDocShell;
7770 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7772 do {
7773 auto appType = docshell->GetAppType();
7774 if (appType == nsIDocShell::APP_TYPE_MAIL) {
7775 return false; // do not prefetch, preload, preconnect from mailnews
7778 docshell->GetInProcessParent(getter_AddRefs(parentItem));
7779 if (parentItem) {
7780 docshell = do_QueryInterface(parentItem);
7781 if (!docshell) {
7782 NS_ERROR("cannot get a docshell from a treeItem!");
7783 return false;
7786 } while (parentItem);
7788 return true;
7791 uint64_t nsContentUtils::GetInnerWindowID(nsIRequest* aRequest) {
7792 // can't do anything if there's no nsIRequest!
7793 if (!aRequest) {
7794 return 0;
7797 nsCOMPtr<nsILoadGroup> loadGroup;
7798 nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
7800 if (NS_FAILED(rv) || !loadGroup) {
7801 return 0;
7804 return GetInnerWindowID(loadGroup);
7807 uint64_t nsContentUtils::GetInnerWindowID(nsILoadGroup* aLoadGroup) {
7808 if (!aLoadGroup) {
7809 return 0;
7812 nsCOMPtr<nsIInterfaceRequestor> callbacks;
7813 nsresult rv = aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
7814 if (NS_FAILED(rv) || !callbacks) {
7815 return 0;
7818 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
7819 if (!loadContext) {
7820 return 0;
7823 nsCOMPtr<mozIDOMWindowProxy> window;
7824 rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
7825 if (NS_FAILED(rv) || !window) {
7826 return 0;
7829 auto* pwindow = nsPIDOMWindowOuter::From(window);
7830 if (!pwindow) {
7831 return 0;
7834 nsPIDOMWindowInner* inner = pwindow->GetCurrentInnerWindow();
7835 return inner ? inner->WindowID() : 0;
7838 // static
7839 void nsContentUtils::MaybeFixIPv6Host(nsACString& aHost) {
7840 if (aHost.FindChar(':') != -1) { // Escape IPv6 address
7841 MOZ_ASSERT(!aHost.Length() ||
7842 (aHost[0] != '[' && aHost[aHost.Length() - 1] != ']'));
7843 aHost.Insert('[', 0);
7844 aHost.Append(']');
7848 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7849 nsACString& aHost) {
7850 aHost.Truncate();
7851 nsresult rv = aURI->GetHost(aHost);
7852 if (NS_FAILED(rv)) { // Some URIs do not have a host
7853 return rv;
7856 MaybeFixIPv6Host(aHost);
7858 return NS_OK;
7861 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7862 nsAString& aHost) {
7863 nsAutoCString hostname;
7864 nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname);
7865 if (NS_FAILED(rv)) {
7866 return rv;
7868 CopyUTF8toUTF16(hostname, aHost);
7869 return NS_OK;
7872 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIPrincipal* aPrincipal,
7873 nsACString& aHost) {
7874 nsresult rv = aPrincipal->GetAsciiHost(aHost);
7875 if (NS_FAILED(rv)) { // Some URIs do not have a host
7876 return rv;
7879 MaybeFixIPv6Host(aHost);
7880 return NS_OK;
7883 CallState nsContentUtils::CallOnAllRemoteChildren(
7884 MessageBroadcaster* aManager,
7885 const std::function<CallState(BrowserParent*)>& aCallback) {
7886 uint32_t browserChildCount = aManager->ChildCount();
7887 for (uint32_t j = 0; j < browserChildCount; ++j) {
7888 RefPtr<MessageListenerManager> childMM = aManager->GetChildAt(j);
7889 if (!childMM) {
7890 continue;
7893 RefPtr<MessageBroadcaster> nonLeafMM = MessageBroadcaster::From(childMM);
7894 if (nonLeafMM) {
7895 if (CallOnAllRemoteChildren(nonLeafMM, aCallback) == CallState::Stop) {
7896 return CallState::Stop;
7898 continue;
7901 mozilla::dom::ipc::MessageManagerCallback* cb = childMM->GetCallback();
7902 if (cb) {
7903 nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
7904 BrowserParent* remote = BrowserParent::GetFrom(fl);
7905 if (remote && aCallback) {
7906 if (aCallback(remote) == CallState::Stop) {
7907 return CallState::Stop;
7913 return CallState::Continue;
7916 void nsContentUtils::CallOnAllRemoteChildren(
7917 nsPIDOMWindowOuter* aWindow,
7918 const std::function<CallState(BrowserParent*)>& aCallback) {
7919 nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(aWindow);
7920 if (window->IsChromeWindow()) {
7921 RefPtr<MessageBroadcaster> windowMM = window->GetMessageManager();
7922 if (windowMM) {
7923 CallOnAllRemoteChildren(windowMM, aCallback);
7928 bool nsContentUtils::IPCTransferableDataItemHasKnownFlavor(
7929 const IPCTransferableDataItem& aItem) {
7930 // Unknown types are converted to kCustomTypesMime.
7931 if (aItem.flavor().EqualsASCII(kCustomTypesMime)) {
7932 return true;
7935 for (const char* format : DataTransfer::kKnownFormats) {
7936 if (aItem.flavor().EqualsASCII(format)) {
7937 return true;
7941 return false;
7944 nsresult nsContentUtils::IPCTransferableDataToTransferable(
7945 const IPCTransferableData& aTransferableData, bool aAddDataFlavor,
7946 nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
7947 nsresult rv;
7948 const nsTArray<IPCTransferableDataItem>& items = aTransferableData.items();
7949 for (const auto& item : items) {
7950 if (aFilterUnknownFlavors && !IPCTransferableDataItemHasKnownFlavor(item)) {
7951 NS_WARNING(
7952 "Ignoring unknown flavor in "
7953 "nsContentUtils::IPCTransferableDataToTransferable");
7954 continue;
7957 if (aAddDataFlavor) {
7958 aTransferable->AddDataFlavor(item.flavor().get());
7961 nsCOMPtr<nsISupports> transferData;
7962 switch (item.data().type()) {
7963 case IPCTransferableDataType::TIPCTransferableDataString: {
7964 const auto& data = item.data().get_IPCTransferableDataString();
7965 nsCOMPtr<nsISupportsString> dataWrapper =
7966 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
7967 NS_ENSURE_SUCCESS(rv, rv);
7968 rv = dataWrapper->SetData(nsDependentSubstring(
7969 reinterpret_cast<const char16_t*>(data.data().Data()),
7970 data.data().Size() / sizeof(char16_t)));
7971 NS_ENSURE_SUCCESS(rv, rv);
7972 transferData = dataWrapper;
7973 break;
7975 case IPCTransferableDataType::TIPCTransferableDataCString: {
7976 const auto& data = item.data().get_IPCTransferableDataCString();
7977 nsCOMPtr<nsISupportsCString> dataWrapper =
7978 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
7979 NS_ENSURE_SUCCESS(rv, rv);
7980 rv = dataWrapper->SetData(nsDependentCSubstring(
7981 reinterpret_cast<const char*>(data.data().Data()),
7982 data.data().Size()));
7983 NS_ENSURE_SUCCESS(rv, rv);
7984 transferData = dataWrapper;
7985 break;
7987 case IPCTransferableDataType::TIPCTransferableDataInputStream: {
7988 const auto& data = item.data().get_IPCTransferableDataInputStream();
7989 nsCOMPtr<nsIInputStream> stream;
7990 rv = NS_NewByteInputStream(getter_AddRefs(stream),
7991 AsChars(data.data().AsSpan()),
7992 NS_ASSIGNMENT_COPY);
7993 NS_ENSURE_SUCCESS(rv, rv);
7994 transferData = stream.forget();
7995 break;
7997 case IPCTransferableDataType::TIPCTransferableDataImageContainer: {
7998 const auto& data = item.data().get_IPCTransferableDataImageContainer();
7999 nsCOMPtr<imgIContainer> container;
8000 rv = DeserializeTransferableDataImageContainer(
8001 data, getter_AddRefs(container));
8002 NS_ENSURE_SUCCESS(rv, rv);
8003 transferData = container;
8004 break;
8006 case IPCTransferableDataType::TIPCTransferableDataBlob: {
8007 const auto& data = item.data().get_IPCTransferableDataBlob();
8008 transferData = IPCBlobUtils::Deserialize(data.blob());
8009 break;
8011 case IPCTransferableDataType::T__None:
8012 MOZ_ASSERT_UNREACHABLE();
8013 return NS_ERROR_FAILURE;
8016 rv = aTransferable->SetTransferData(item.flavor().get(), transferData);
8017 NS_ENSURE_SUCCESS(rv, rv);
8019 return NS_OK;
8022 nsresult nsContentUtils::IPCTransferableDataToTransferable(
8023 const IPCTransferableData& aTransferableData, const bool& aIsPrivateData,
8024 nsIPrincipal* aRequestingPrincipal,
8025 const nsContentPolicyType& aContentPolicyType, bool aAddDataFlavor,
8026 nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
8027 // Note that we need to set privacy status of transferable before adding any
8028 // data into it.
8029 aTransferable->SetIsPrivateData(aIsPrivateData);
8031 nsresult rv = IPCTransferableDataToTransferable(
8032 aTransferableData, aAddDataFlavor, aTransferable, aFilterUnknownFlavors);
8033 NS_ENSURE_SUCCESS(rv, rv);
8035 aTransferable->SetRequestingPrincipal(aRequestingPrincipal);
8036 aTransferable->SetContentPolicyType(aContentPolicyType);
8037 return NS_OK;
8040 nsresult nsContentUtils::IPCTransferableToTransferable(
8041 const IPCTransferable& aIPCTransferable, bool aAddDataFlavor,
8042 nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
8043 nsresult rv = IPCTransferableDataToTransferable(
8044 aIPCTransferable.data(), aIPCTransferable.isPrivateData(),
8045 aIPCTransferable.requestingPrincipal(),
8046 aIPCTransferable.contentPolicyType(), aAddDataFlavor, aTransferable,
8047 aFilterUnknownFlavors);
8048 NS_ENSURE_SUCCESS(rv, rv);
8050 if (aIPCTransferable.cookieJarSettings().isSome()) {
8051 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
8052 net::CookieJarSettings::Deserialize(
8053 aIPCTransferable.cookieJarSettings().ref(),
8054 getter_AddRefs(cookieJarSettings));
8055 aTransferable->SetCookieJarSettings(cookieJarSettings);
8057 aTransferable->SetReferrerInfo(aIPCTransferable.referrerInfo());
8058 return NS_OK;
8061 nsresult nsContentUtils::IPCTransferableDataItemToVariant(
8062 const IPCTransferableDataItem& aItem, nsIWritableVariant* aVariant) {
8063 MOZ_ASSERT(aVariant);
8065 switch (aItem.data().type()) {
8066 case IPCTransferableDataType::TIPCTransferableDataString: {
8067 const auto& data = aItem.data().get_IPCTransferableDataString();
8068 return aVariant->SetAsAString(nsDependentSubstring(
8069 reinterpret_cast<const char16_t*>(data.data().Data()),
8070 data.data().Size() / sizeof(char16_t)));
8072 case IPCTransferableDataType::TIPCTransferableDataCString: {
8073 const auto& data = aItem.data().get_IPCTransferableDataCString();
8074 return aVariant->SetAsACString(nsDependentCSubstring(
8075 reinterpret_cast<const char*>(data.data().Data()),
8076 data.data().Size()));
8078 case IPCTransferableDataType::TIPCTransferableDataInputStream: {
8079 const auto& data = aItem.data().get_IPCTransferableDataInputStream();
8080 nsCOMPtr<nsIInputStream> stream;
8081 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
8082 AsChars(data.data().AsSpan()),
8083 NS_ASSIGNMENT_COPY);
8084 NS_ENSURE_SUCCESS(rv, rv);
8085 return aVariant->SetAsISupports(stream);
8087 case IPCTransferableDataType::TIPCTransferableDataImageContainer: {
8088 const auto& data = aItem.data().get_IPCTransferableDataImageContainer();
8089 nsCOMPtr<imgIContainer> container;
8090 nsresult rv = DeserializeTransferableDataImageContainer(
8091 data, getter_AddRefs(container));
8092 NS_ENSURE_SUCCESS(rv, rv);
8093 return aVariant->SetAsISupports(container);
8095 case IPCTransferableDataType::TIPCTransferableDataBlob: {
8096 const auto& data = aItem.data().get_IPCTransferableDataBlob();
8097 RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(data.blob());
8098 return aVariant->SetAsISupports(blobImpl);
8100 case IPCTransferableDataType::T__None:
8101 break;
8104 MOZ_ASSERT_UNREACHABLE();
8105 return NS_ERROR_UNEXPECTED;
8108 void nsContentUtils::TransferablesToIPCTransferableDatas(
8109 nsIArray* aTransferables, nsTArray<IPCTransferableData>& aIPC,
8110 bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
8111 aIPC.Clear();
8112 if (aTransferables) {
8113 uint32_t transferableCount = 0;
8114 aTransferables->GetLength(&transferableCount);
8115 for (uint32_t i = 0; i < transferableCount; ++i) {
8116 IPCTransferableData* dt = aIPC.AppendElement();
8117 nsCOMPtr<nsITransferable> transferable =
8118 do_QueryElementAt(aTransferables, i);
8119 TransferableToIPCTransferableData(transferable, dt, aInSyncMessage,
8120 aParent);
8125 nsresult nsContentUtils::CalculateBufferSizeForImage(
8126 const uint32_t& aStride, const IntSize& aImageSize,
8127 const SurfaceFormat& aFormat, size_t* aMaxBufferSize,
8128 size_t* aUsedBufferSize) {
8129 CheckedInt32 requiredBytes =
8130 CheckedInt32(aStride) * CheckedInt32(aImageSize.height);
8132 CheckedInt32 usedBytes =
8133 requiredBytes - aStride +
8134 (CheckedInt32(aImageSize.width) * BytesPerPixel(aFormat));
8135 if (!usedBytes.isValid()) {
8136 return NS_ERROR_FAILURE;
8139 MOZ_ASSERT(requiredBytes.isValid(), "usedBytes valid but not required?");
8140 *aMaxBufferSize = requiredBytes.value();
8141 *aUsedBufferSize = usedBytes.value();
8142 return NS_OK;
8145 static already_AddRefed<DataSourceSurface> BigBufferToDataSurface(
8146 BigBuffer& aData, uint32_t aStride, const IntSize& aImageSize,
8147 SurfaceFormat aFormat) {
8148 if (!aData.Size() || !aImageSize.width || !aImageSize.height) {
8149 return nullptr;
8152 // Validate shared memory buffer size
8153 size_t imageBufLen = 0;
8154 size_t maxBufLen = 0;
8155 if (NS_FAILED(nsContentUtils::CalculateBufferSizeForImage(
8156 aStride, aImageSize, aFormat, &maxBufLen, &imageBufLen))) {
8157 return nullptr;
8159 if (imageBufLen > aData.Size()) {
8160 return nullptr;
8162 return CreateDataSourceSurfaceFromData(aImageSize, aFormat, aData.Data(),
8163 aStride);
8166 nsresult nsContentUtils::DeserializeTransferableDataImageContainer(
8167 const IPCTransferableDataImageContainer& aData,
8168 imgIContainer** aContainer) {
8169 const IntSize size(aData.width(), aData.height());
8170 size_t maxBufferSize = 0;
8171 size_t usedBufferSize = 0;
8172 nsresult rv = CalculateBufferSizeForImage(
8173 aData.stride(), size, aData.format(), &maxBufferSize, &usedBufferSize);
8174 NS_ENSURE_SUCCESS(rv, rv);
8175 if (usedBufferSize > aData.data().Size()) {
8176 return NS_ERROR_FAILURE;
8178 RefPtr<DataSourceSurface> surface =
8179 CreateDataSourceSurfaceFromData(size, aData.format(), aData.data().Data(),
8180 static_cast<int32_t>(aData.stride()));
8181 if (!surface) {
8182 return NS_ERROR_FAILURE;
8184 RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size);
8185 nsCOMPtr<imgIContainer> imageContainer =
8186 image::ImageOps::CreateFromDrawable(drawable);
8187 imageContainer.forget(aContainer);
8189 return NS_OK;
8192 bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
8193 return aFlavor.EqualsLiteral(kNativeImageMime) ||
8194 aFlavor.EqualsLiteral(kJPEGImageMime) ||
8195 aFlavor.EqualsLiteral(kJPGImageMime) ||
8196 aFlavor.EqualsLiteral(kPNGImageMime) ||
8197 aFlavor.EqualsLiteral(kGIFImageMime);
8200 // FIXME: This can probably be removed once bug 1783240 lands, as `nsString`
8201 // will be implicitly serialized in shmem when sent over IPDL directly.
8202 static IPCTransferableDataString AsIPCTransferableDataString(
8203 Span<const char16_t> aInput) {
8204 return IPCTransferableDataString{BigBuffer(AsBytes(aInput))};
8207 // FIXME: This can probably be removed once bug 1783240 lands, as `nsCString`
8208 // will be implicitly serialized in shmem when sent over IPDL directly.
8209 static IPCTransferableDataCString AsIPCTransferableDataCString(
8210 Span<const char> aInput) {
8211 return IPCTransferableDataCString{BigBuffer(AsBytes(aInput))};
8214 void nsContentUtils::TransferableToIPCTransferableData(
8215 nsITransferable* aTransferable, IPCTransferableData* aTransferableData,
8216 bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
8217 MOZ_ASSERT_IF(XRE_IsParentProcess(), aParent);
8219 if (aTransferable) {
8220 nsTArray<nsCString> flavorList;
8221 aTransferable->FlavorsTransferableCanExport(flavorList);
8223 for (uint32_t j = 0; j < flavorList.Length(); ++j) {
8224 nsCString& flavorStr = flavorList[j];
8225 if (!flavorStr.Length()) {
8226 continue;
8229 nsCOMPtr<nsISupports> data;
8230 nsresult rv =
8231 aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data));
8233 if (NS_FAILED(rv) || !data) {
8234 if (aInSyncMessage) {
8235 // Can't do anything.
8236 // FIXME: This shouldn't be the case anymore!
8237 continue;
8240 // This is a hack to support kFilePromiseMime.
8241 // On Windows there just needs to be an entry for it,
8242 // and for OSX we need to create
8243 // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
8244 if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
8245 IPCTransferableDataItem* item =
8246 aTransferableData->items().AppendElement();
8247 item->flavor() = flavorStr;
8248 item->data() =
8249 AsIPCTransferableDataString(NS_ConvertUTF8toUTF16(flavorStr));
8250 continue;
8253 // Empty element, transfer only the flavor
8254 IPCTransferableDataItem* item =
8255 aTransferableData->items().AppendElement();
8256 item->flavor() = flavorStr;
8257 item->data() = AsIPCTransferableDataString(EmptyString());
8258 continue;
8261 // We need to handle nsIInputStream before nsISupportsCString, otherwise
8262 // nsStringInputStream would be converted into a wrong type.
8263 if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
8264 IPCTransferableDataItem* item =
8265 aTransferableData->items().AppendElement();
8266 item->flavor() = flavorStr;
8267 nsCString imageData;
8268 DebugOnly<nsresult> rv =
8269 NS_ConsumeStream(stream, UINT32_MAX, imageData);
8270 MOZ_ASSERT(
8271 rv != NS_BASE_STREAM_WOULD_BLOCK,
8272 "cannot use async input streams in nsITransferable right now");
8273 // FIXME: This can probably be simplified once bug 1783240 lands, as
8274 // `nsCString` will be implicitly serialized in shmem when sent over
8275 // IPDL directly.
8276 item->data() =
8277 IPCTransferableDataInputStream(BigBuffer(AsBytes(Span(imageData))));
8278 continue;
8281 if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(data)) {
8282 nsAutoString dataAsString;
8283 MOZ_ALWAYS_SUCCEEDS(text->GetData(dataAsString));
8285 IPCTransferableDataItem* item =
8286 aTransferableData->items().AppendElement();
8287 item->flavor() = flavorStr;
8288 item->data() = AsIPCTransferableDataString(dataAsString);
8289 continue;
8292 if (nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data)) {
8293 nsAutoCString dataAsString;
8294 MOZ_ALWAYS_SUCCEEDS(ctext->GetData(dataAsString));
8296 IPCTransferableDataItem* item =
8297 aTransferableData->items().AppendElement();
8298 item->flavor() = flavorStr;
8299 item->data() = AsIPCTransferableDataCString(dataAsString);
8300 continue;
8303 if (nsCOMPtr<imgIContainer> image = do_QueryInterface(data)) {
8304 // Images to be placed on the clipboard are imgIContainers.
8305 RefPtr<mozilla::gfx::SourceSurface> surface = image->GetFrame(
8306 imgIContainer::FRAME_CURRENT,
8307 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
8308 if (!surface) {
8309 continue;
8311 RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
8312 surface->GetDataSurface();
8313 if (!dataSurface) {
8314 continue;
8316 size_t length;
8317 int32_t stride;
8318 Maybe<BigBuffer> surfaceData =
8319 GetSurfaceData(*dataSurface, &length, &stride);
8321 if (surfaceData.isNothing()) {
8322 continue;
8325 IPCTransferableDataItem* item =
8326 aTransferableData->items().AppendElement();
8327 item->flavor() = flavorStr;
8329 mozilla::gfx::IntSize size = dataSurface->GetSize();
8330 item->data() = IPCTransferableDataImageContainer(
8331 std::move(*surfaceData), size.width, size.height, stride,
8332 dataSurface->GetFormat());
8333 continue;
8336 // Otherwise, handle this as a file.
8337 nsCOMPtr<BlobImpl> blobImpl;
8338 if (nsCOMPtr<nsIFile> file = do_QueryInterface(data)) {
8339 if (aParent) {
8340 bool isDir = false;
8341 if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
8342 nsAutoString path;
8343 if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
8344 continue;
8347 RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
8348 fss->GrantAccessToContentProcess(aParent->ChildID(), path);
8352 blobImpl = new FileBlobImpl(file);
8354 IgnoredErrorResult rv;
8356 // Ensure that file data is cached no that the content process
8357 // has this data available to it when passed over:
8358 blobImpl->GetSize(rv);
8359 if (NS_WARN_IF(rv.Failed())) {
8360 continue;
8363 blobImpl->GetLastModified(rv);
8364 if (NS_WARN_IF(rv.Failed())) {
8365 continue;
8367 } else {
8368 if (aInSyncMessage) {
8369 // Can't do anything.
8370 // FIXME: This shouldn't be the case anymore!
8371 continue;
8374 blobImpl = do_QueryInterface(data);
8377 if (blobImpl) {
8378 // If we failed to create the blob actor, then this blob probably
8379 // can't get the file size for the underlying file, ignore it for
8380 // now. TODO pass this through anyway.
8381 IPCBlob ipcBlob;
8382 nsresult rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);
8383 if (NS_WARN_IF(NS_FAILED(rv))) {
8384 continue;
8387 IPCTransferableDataItem* item =
8388 aTransferableData->items().AppendElement();
8389 item->flavor() = flavorStr;
8390 item->data() = IPCTransferableDataBlob(ipcBlob);
8396 void nsContentUtils::TransferableToIPCTransferable(
8397 nsITransferable* aTransferable, IPCTransferable* aIPCTransferable,
8398 bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
8399 IPCTransferableData ipcTransferableData;
8400 TransferableToIPCTransferableData(aTransferable, &ipcTransferableData,
8401 aInSyncMessage, aParent);
8403 Maybe<net::CookieJarSettingsArgs> cookieJarSettingsArgs;
8404 if (nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
8405 aTransferable->GetCookieJarSettings()) {
8406 net::CookieJarSettingsArgs args;
8407 net::CookieJarSettings::Cast(cookieJarSettings)->Serialize(args);
8408 cookieJarSettingsArgs = Some(std::move(args));
8411 aIPCTransferable->data() = std::move(ipcTransferableData);
8412 aIPCTransferable->isPrivateData() = aTransferable->GetIsPrivateData();
8413 aIPCTransferable->requestingPrincipal() =
8414 aTransferable->GetRequestingPrincipal();
8415 aIPCTransferable->cookieJarSettings() = std::move(cookieJarSettingsArgs);
8416 aIPCTransferable->contentPolicyType() = aTransferable->GetContentPolicyType();
8417 aIPCTransferable->referrerInfo() = aTransferable->GetReferrerInfo();
8420 Maybe<BigBuffer> nsContentUtils::GetSurfaceData(DataSourceSurface& aSurface,
8421 size_t* aLength,
8422 int32_t* aStride) {
8423 mozilla::gfx::DataSourceSurface::MappedSurface map;
8424 if (!aSurface.Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map)) {
8425 return Nothing();
8428 size_t bufLen = 0;
8429 size_t maxBufLen = 0;
8430 nsresult rv = nsContentUtils::CalculateBufferSizeForImage(
8431 map.mStride, aSurface.GetSize(), aSurface.GetFormat(), &maxBufLen,
8432 &bufLen);
8433 if (NS_FAILED(rv)) {
8434 aSurface.Unmap();
8435 return Nothing();
8438 BigBuffer surfaceData(maxBufLen);
8439 memcpy(surfaceData.Data(), map.mData, bufLen);
8440 memset(surfaceData.Data() + bufLen, 0, maxBufLen - bufLen);
8442 *aLength = maxBufLen;
8443 *aStride = map.mStride;
8445 aSurface.Unmap();
8446 return Some(std::move(surfaceData));
8449 Maybe<IPCImage> nsContentUtils::SurfaceToIPCImage(DataSourceSurface& aSurface) {
8450 size_t len = 0;
8451 int32_t stride = 0;
8452 auto mem = GetSurfaceData(aSurface, &len, &stride);
8453 if (!mem) {
8454 return Nothing();
8456 return Some(IPCImage{std::move(*mem), uint32_t(stride), aSurface.GetFormat(),
8457 ImageIntSize::FromUnknownSize(aSurface.GetSize())});
8460 already_AddRefed<DataSourceSurface> nsContentUtils::IPCImageToSurface(
8461 IPCImage&& aImage) {
8462 return BigBufferToDataSurface(aImage.data(), aImage.stride(),
8463 aImage.size().ToUnknownSize(), aImage.format());
8466 Modifiers nsContentUtils::GetWidgetModifiers(int32_t aModifiers) {
8467 Modifiers result = 0;
8468 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SHIFT) {
8469 result |= mozilla::MODIFIER_SHIFT;
8471 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CONTROL) {
8472 result |= mozilla::MODIFIER_CONTROL;
8474 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALT) {
8475 result |= mozilla::MODIFIER_ALT;
8477 if (aModifiers & nsIDOMWindowUtils::MODIFIER_META) {
8478 result |= mozilla::MODIFIER_META;
8480 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALTGRAPH) {
8481 result |= mozilla::MODIFIER_ALTGRAPH;
8483 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CAPSLOCK) {
8484 result |= mozilla::MODIFIER_CAPSLOCK;
8486 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FN) {
8487 result |= mozilla::MODIFIER_FN;
8489 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FNLOCK) {
8490 result |= mozilla::MODIFIER_FNLOCK;
8492 if (aModifiers & nsIDOMWindowUtils::MODIFIER_NUMLOCK) {
8493 result |= mozilla::MODIFIER_NUMLOCK;
8495 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SCROLLLOCK) {
8496 result |= mozilla::MODIFIER_SCROLLLOCK;
8498 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOL) {
8499 result |= mozilla::MODIFIER_SYMBOL;
8501 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK) {
8502 result |= mozilla::MODIFIER_SYMBOLLOCK;
8504 return result;
8507 nsIWidget* nsContentUtils::GetWidget(PresShell* aPresShell, nsPoint* aOffset) {
8508 if (!aPresShell) {
8509 return nullptr;
8511 nsIFrame* frame = aPresShell->GetRootFrame();
8512 if (!frame) {
8513 return nullptr;
8515 return frame->GetView()->GetNearestWidget(aOffset);
8518 int16_t nsContentUtils::GetButtonsFlagForButton(int32_t aButton) {
8519 switch (aButton) {
8520 case -1:
8521 return MouseButtonsFlag::eNoButtons;
8522 case MouseButton::ePrimary:
8523 return MouseButtonsFlag::ePrimaryFlag;
8524 case MouseButton::eMiddle:
8525 return MouseButtonsFlag::eMiddleFlag;
8526 case MouseButton::eSecondary:
8527 return MouseButtonsFlag::eSecondaryFlag;
8528 case 3:
8529 return MouseButtonsFlag::e4thFlag;
8530 case 4:
8531 return MouseButtonsFlag::e5thFlag;
8532 case MouseButton::eEraser:
8533 return MouseButtonsFlag::eEraserFlag;
8534 default:
8535 NS_ERROR("Button not known.");
8536 return 0;
8540 LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(
8541 const CSSPoint& aPoint, const nsPoint& aOffset,
8542 nsPresContext* aPresContext) {
8543 nsPoint layoutRelative = CSSPoint::ToAppUnits(aPoint) + aOffset;
8544 nsPoint visualRelative =
8545 ViewportUtils::LayoutToVisual(layoutRelative, aPresContext->PresShell());
8546 return LayoutDeviceIntPoint::FromAppUnitsRounded(
8547 visualRelative, aPresContext->AppUnitsPerDevPixel());
8550 nsView* nsContentUtils::GetViewToDispatchEvent(nsPresContext* aPresContext,
8551 PresShell** aPresShell) {
8552 if (!aPresContext || !aPresShell) {
8553 return nullptr;
8555 RefPtr<PresShell> presShell = aPresContext->PresShell();
8556 if (NS_WARN_IF(!presShell)) {
8557 *aPresShell = nullptr;
8558 return nullptr;
8560 nsViewManager* viewManager = presShell->GetViewManager();
8561 if (!viewManager) {
8562 presShell.forget(aPresShell); // XXX Is this intentional?
8563 return nullptr;
8565 presShell.forget(aPresShell);
8566 return viewManager->GetRootView();
8569 nsresult nsContentUtils::SendMouseEvent(
8570 mozilla::PresShell* aPresShell, const nsAString& aType, float aX, float aY,
8571 int32_t aButton, int32_t aButtons, int32_t aClickCount, int32_t aModifiers,
8572 bool aIgnoreRootScrollFrame, float aPressure,
8573 unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
8574 PreventDefaultResult* aPreventDefault, bool aIsDOMEventSynthesized,
8575 bool aIsWidgetEventSynthesized) {
8576 nsPoint offset;
8577 nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
8578 if (!widget) return NS_ERROR_FAILURE;
8580 EventMessage msg;
8581 Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
8582 bool contextMenuKey = false;
8583 if (aType.EqualsLiteral("mousedown")) {
8584 msg = eMouseDown;
8585 } else if (aType.EqualsLiteral("mouseup")) {
8586 msg = eMouseUp;
8587 } else if (aType.EqualsLiteral("mousemove")) {
8588 msg = eMouseMove;
8589 } else if (aType.EqualsLiteral("mouseover")) {
8590 msg = eMouseEnterIntoWidget;
8591 } else if (aType.EqualsLiteral("mouseout")) {
8592 msg = eMouseExitFromWidget;
8593 exitFrom = Some(WidgetMouseEvent::ePlatformChild);
8594 } else if (aType.EqualsLiteral("mousecancel")) {
8595 msg = eMouseExitFromWidget;
8596 exitFrom = Some(XRE_IsParentProcess() ? WidgetMouseEvent::ePlatformTopLevel
8597 : WidgetMouseEvent::ePuppet);
8598 } else if (aType.EqualsLiteral("mouselongtap")) {
8599 msg = eMouseLongTap;
8600 } else if (aType.EqualsLiteral("contextmenu")) {
8601 msg = eContextMenu;
8602 contextMenuKey = (aButton == 0);
8603 } else if (aType.EqualsLiteral("MozMouseHittest")) {
8604 msg = eMouseHitTest;
8605 } else if (aType.EqualsLiteral("MozMouseExploreByTouch")) {
8606 msg = eMouseExploreByTouch;
8607 } else {
8608 return NS_ERROR_FAILURE;
8611 if (aInputSourceArg == MouseEvent_Binding::MOZ_SOURCE_UNKNOWN) {
8612 aInputSourceArg = MouseEvent_Binding::MOZ_SOURCE_MOUSE;
8615 WidgetMouseEvent event(true, msg, widget,
8616 aIsWidgetEventSynthesized
8617 ? WidgetMouseEvent::eSynthesized
8618 : WidgetMouseEvent::eReal,
8619 contextMenuKey ? WidgetMouseEvent::eContextMenuKey
8620 : WidgetMouseEvent::eNormal);
8621 event.pointerId = aIdentifier;
8622 event.mModifiers = GetWidgetModifiers(aModifiers);
8623 event.mButton = aButton;
8624 event.mButtons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED
8625 ? aButtons
8626 : msg == eMouseUp ? 0
8627 : GetButtonsFlagForButton(aButton);
8628 event.mPressure = aPressure;
8629 event.mInputSource = aInputSourceArg;
8630 event.mClickCount = aClickCount;
8631 event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
8632 event.mExitFrom = exitFrom;
8634 nsPresContext* presContext = aPresShell->GetPresContext();
8635 if (!presContext) return NS_ERROR_FAILURE;
8637 event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
8638 event.mIgnoreRootScrollFrame = aIgnoreRootScrollFrame;
8640 nsEventStatus status = nsEventStatus_eIgnore;
8641 if (aToWindow) {
8642 RefPtr<PresShell> presShell;
8643 nsView* view =
8644 GetViewToDispatchEvent(presContext, getter_AddRefs(presShell));
8645 if (!presShell || !view) {
8646 return NS_ERROR_FAILURE;
8648 return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
8650 if (StaticPrefs::test_events_async_enabled() &&
8651 StaticPrefs::test_events_async_mouse_enabled()) {
8652 status = widget->DispatchInputEvent(&event).mContentStatus;
8653 } else {
8654 nsresult rv = widget->DispatchEvent(&event, status);
8655 NS_ENSURE_SUCCESS(rv, rv);
8657 if (aPreventDefault) {
8658 if (status == nsEventStatus_eConsumeNoDefault) {
8659 if (event.mFlags.mDefaultPreventedByContent) {
8660 *aPreventDefault = PreventDefaultResult::ByContent;
8661 } else {
8662 *aPreventDefault = PreventDefaultResult::ByChrome;
8664 } else {
8665 *aPreventDefault = PreventDefaultResult::No;
8669 return NS_OK;
8672 /* static */
8673 void nsContentUtils::FirePageHideEventForFrameLoaderSwap(
8674 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8675 bool aOnlySystemGroup) {
8676 MOZ_DIAGNOSTIC_ASSERT(aItem);
8677 MOZ_DIAGNOSTIC_ASSERT(aChromeEventHandler);
8679 RefPtr<Document> doc = aItem->GetDocument();
8680 NS_ASSERTION(doc, "What happened here?");
8681 doc->OnPageHide(true, aChromeEventHandler, aOnlySystemGroup);
8683 int32_t childCount = 0;
8684 aItem->GetInProcessChildCount(&childCount);
8685 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8686 kids.AppendElements(childCount);
8687 for (int32_t i = 0; i < childCount; ++i) {
8688 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8691 for (uint32_t i = 0; i < kids.Length(); ++i) {
8692 if (kids[i]) {
8693 FirePageHideEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8694 aOnlySystemGroup);
8699 // The pageshow event is fired for a given document only if IsShowing() returns
8700 // the same thing as aFireIfShowing. This gives us a way to fire pageshow only
8701 // on documents that are still loading or only on documents that are already
8702 // loaded.
8703 /* static */
8704 void nsContentUtils::FirePageShowEventForFrameLoaderSwap(
8705 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8706 bool aFireIfShowing, bool aOnlySystemGroup) {
8707 int32_t childCount = 0;
8708 aItem->GetInProcessChildCount(&childCount);
8709 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8710 kids.AppendElements(childCount);
8711 for (int32_t i = 0; i < childCount; ++i) {
8712 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8715 for (uint32_t i = 0; i < kids.Length(); ++i) {
8716 if (kids[i]) {
8717 FirePageShowEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8718 aFireIfShowing, aOnlySystemGroup);
8722 RefPtr<Document> doc = aItem->GetDocument();
8723 if (doc && doc->IsShowing() == aFireIfShowing) {
8724 doc->OnPageShow(true, aChromeEventHandler, aOnlySystemGroup);
8728 /* static */
8729 already_AddRefed<nsPIWindowRoot> nsContentUtils::GetWindowRoot(Document* aDoc) {
8730 if (aDoc) {
8731 if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
8732 return win->GetTopWindowRoot();
8735 return nullptr;
8738 /* static */
8739 bool nsContentUtils::LinkContextIsURI(const nsAString& aAnchor,
8740 nsIURI* aDocURI) {
8741 if (aAnchor.IsEmpty()) {
8742 // anchor parameter not present or empty -> same document reference
8743 return true;
8746 // the document URI might contain a fragment identifier ("#...')
8747 // we want to ignore that because it's invisible to the server
8748 // and just affects the local interpretation in the recipient
8749 nsCOMPtr<nsIURI> contextUri;
8750 nsresult rv = NS_GetURIWithoutRef(aDocURI, getter_AddRefs(contextUri));
8752 if (NS_FAILED(rv)) {
8753 // copying failed
8754 return false;
8757 // resolve anchor against context
8758 nsCOMPtr<nsIURI> resolvedUri;
8759 rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor, nullptr, contextUri);
8761 if (NS_FAILED(rv)) {
8762 // resolving failed
8763 return false;
8766 bool same;
8767 rv = contextUri->Equals(resolvedUri, &same);
8768 if (NS_FAILED(rv)) {
8769 // comparison failed
8770 return false;
8773 return same;
8776 /* static */
8777 bool nsContentUtils::IsPreloadType(nsContentPolicyType aType) {
8778 return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
8779 aType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD ||
8780 aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
8781 aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD ||
8782 aType == nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD ||
8783 aType == nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD);
8786 // static
8787 ReferrerPolicy nsContentUtils::GetReferrerPolicyFromChannel(
8788 nsIChannel* aChannel) {
8789 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
8790 if (!httpChannel) {
8791 return ReferrerPolicy::_empty;
8794 nsresult rv;
8795 nsAutoCString headerValue;
8796 rv = httpChannel->GetResponseHeader("referrer-policy"_ns, headerValue);
8797 if (NS_FAILED(rv) || headerValue.IsEmpty()) {
8798 return ReferrerPolicy::_empty;
8801 return ReferrerInfo::ReferrerPolicyFromHeaderString(
8802 NS_ConvertUTF8toUTF16(headerValue));
8805 // static
8806 bool nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel) {
8807 nsLoadFlags loadFlags = 0;
8808 aChannel->GetLoadFlags(&loadFlags);
8809 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
8810 return true;
8813 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
8814 nsContentPolicyType type = loadInfo->InternalContentPolicyType();
8815 return IsNonSubresourceInternalPolicyType(type);
8818 // static
8819 bool nsContentUtils::IsNonSubresourceInternalPolicyType(
8820 nsContentPolicyType aType) {
8821 return aType == nsIContentPolicy::TYPE_DOCUMENT ||
8822 aType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
8823 aType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
8824 aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
8825 aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
8828 // static public
8829 bool nsContentUtils::IsThirdPartyTrackingResourceWindow(
8830 nsPIDOMWindowInner* aWindow) {
8831 MOZ_ASSERT(aWindow);
8833 Document* document = aWindow->GetExtantDoc();
8834 if (!document) {
8835 return false;
8838 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8839 do_QueryInterface(document->GetChannel());
8840 if (!classifiedChannel) {
8841 return false;
8844 return classifiedChannel->IsThirdPartyTrackingResource();
8847 // static public
8848 bool nsContentUtils::IsFirstPartyTrackingResourceWindow(
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 uint32_t classificationFlags =
8864 classifiedChannel->GetFirstPartyClassificationFlags();
8866 return mozilla::net::UrlClassifierCommon::IsTrackingClassificationFlag(
8867 classificationFlags, NS_UsePrivateBrowsing(document->GetChannel()));
8870 namespace {
8872 // We put StringBuilder in the anonymous namespace to prevent anything outside
8873 // this file from accidentally being linked against it.
8874 class BulkAppender {
8875 using size_type = typename nsAString::size_type;
8877 public:
8878 explicit BulkAppender(BulkWriteHandle<char16_t>&& aHandle)
8879 : mHandle(std::move(aHandle)), mPosition(0) {}
8880 ~BulkAppender() = default;
8882 template <int N>
8883 void AppendLiteral(const char16_t (&aStr)[N]) {
8884 size_t len = N - 1;
8885 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8886 memcpy(mHandle.Elements() + mPosition, aStr, len * sizeof(char16_t));
8887 mPosition += len;
8890 void Append(Span<const char16_t> aStr) {
8891 size_t len = aStr.Length();
8892 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8893 // Both mHandle.Elements() and aStr.Elements() are guaranteed
8894 // to be non-null (by the string implementation and by Span,
8895 // respectively), so not checking the pointers for null before
8896 // memcpy does not lead to UB even if len was zero.
8897 memcpy(mHandle.Elements() + mPosition, aStr.Elements(),
8898 len * sizeof(char16_t));
8899 mPosition += len;
8902 void Append(Span<const char> aStr) {
8903 size_t len = aStr.Length();
8904 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8905 ConvertLatin1toUtf16(aStr, mHandle.AsSpan().From(mPosition));
8906 mPosition += len;
8909 void Finish() { mHandle.Finish(mPosition, false); }
8911 private:
8912 BulkWriteHandle<char16_t> mHandle;
8913 size_type mPosition;
8916 class StringBuilder {
8917 private:
8918 class Unit {
8919 public:
8920 Unit() : mAtom(nullptr) { MOZ_COUNT_CTOR(StringBuilder::Unit); }
8921 ~Unit() {
8922 if (mType == Type::String || mType == Type::StringWithEncode) {
8923 mString.~nsString();
8925 MOZ_COUNT_DTOR(StringBuilder::Unit);
8928 enum class Type : uint8_t {
8929 Unknown,
8930 Atom,
8931 String,
8932 StringWithEncode,
8933 Literal,
8934 TextFragment,
8935 TextFragmentWithEncode,
8938 union {
8939 nsAtom* mAtom;
8940 const char16_t* mLiteral;
8941 nsString mString;
8942 const nsTextFragment* mTextFragment;
8944 uint32_t mLength = 0;
8945 Type mType = Type::Unknown;
8948 static_assert(sizeof(void*) != 8 || sizeof(Unit) <= 3 * sizeof(void*),
8949 "Unit should remain small");
8951 public:
8952 // Try to keep the size of StringBuilder close to a jemalloc bucket size (the
8953 // 16kb one in this case).
8954 static constexpr uint32_t TARGET_SIZE = 16 * 1024;
8956 // The number of units we need to remove from the inline buffer so that the
8957 // rest of the builder members fit. A more precise approach would be to
8958 // calculate that extra size and use (TARGET_SIZE - OTHER_SIZE) / sizeof(Unit)
8959 // or so, but this is simpler.
8960 static constexpr uint32_t PADDING_UNITS = sizeof(void*) == 8 ? 1 : 2;
8962 static constexpr uint32_t STRING_BUFFER_UNITS =
8963 TARGET_SIZE / sizeof(Unit) - PADDING_UNITS;
8965 StringBuilder() : mLast(this), mLength(0) { MOZ_COUNT_CTOR(StringBuilder); }
8967 MOZ_COUNTED_DTOR(StringBuilder)
8969 void Append(nsAtom* aAtom) {
8970 Unit* u = AddUnit();
8971 u->mAtom = aAtom;
8972 u->mType = Unit::Type::Atom;
8973 uint32_t len = aAtom->GetLength();
8974 u->mLength = len;
8975 mLength += len;
8978 template <int N>
8979 void Append(const char16_t (&aLiteral)[N]) {
8980 Unit* u = AddUnit();
8981 u->mLiteral = aLiteral;
8982 u->mType = Unit::Type::Literal;
8983 uint32_t len = N - 1;
8984 u->mLength = len;
8985 mLength += len;
8988 void Append(nsString&& aString) {
8989 Unit* u = AddUnit();
8990 uint32_t len = aString.Length();
8991 new (&u->mString) nsString(std::move(aString));
8992 u->mType = Unit::Type::String;
8993 u->mLength = len;
8994 mLength += len;
8997 void AppendWithAttrEncode(nsString&& aString, uint32_t aLen) {
8998 Unit* u = AddUnit();
8999 new (&u->mString) nsString(std::move(aString));
9000 u->mType = Unit::Type::StringWithEncode;
9001 u->mLength = aLen;
9002 mLength += aLen;
9005 void Append(const nsTextFragment* aTextFragment) {
9006 Unit* u = AddUnit();
9007 u->mTextFragment = aTextFragment;
9008 u->mType = Unit::Type::TextFragment;
9009 uint32_t len = aTextFragment->GetLength();
9010 u->mLength = len;
9011 mLength += len;
9014 void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen) {
9015 Unit* u = AddUnit();
9016 u->mTextFragment = aTextFragment;
9017 u->mType = Unit::Type::TextFragmentWithEncode;
9018 u->mLength = aLen;
9019 mLength += aLen;
9022 bool ToString(nsAString& aOut) {
9023 if (!mLength.isValid()) {
9024 return false;
9026 auto appenderOrErr = aOut.BulkWrite(mLength.value(), 0, true);
9027 if (appenderOrErr.isErr()) {
9028 return false;
9031 BulkAppender appender{appenderOrErr.unwrap()};
9033 for (StringBuilder* current = this; current;
9034 current = current->mNext.get()) {
9035 uint32_t len = current->mUnits.Length();
9036 for (uint32_t i = 0; i < len; ++i) {
9037 Unit& u = current->mUnits[i];
9038 switch (u.mType) {
9039 case Unit::Type::Atom:
9040 appender.Append(*(u.mAtom));
9041 break;
9042 case Unit::Type::String:
9043 appender.Append(u.mString);
9044 break;
9045 case Unit::Type::StringWithEncode:
9046 EncodeAttrString(u.mString, appender);
9047 break;
9048 case Unit::Type::Literal:
9049 appender.Append(Span(u.mLiteral, u.mLength));
9050 break;
9051 case Unit::Type::TextFragment:
9052 if (u.mTextFragment->Is2b()) {
9053 appender.Append(
9054 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()));
9055 } else {
9056 appender.Append(
9057 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()));
9059 break;
9060 case Unit::Type::TextFragmentWithEncode:
9061 if (u.mTextFragment->Is2b()) {
9062 EncodeTextFragment(
9063 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()),
9064 appender);
9065 } else {
9066 EncodeTextFragment(
9067 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()),
9068 appender);
9070 break;
9071 default:
9072 MOZ_CRASH("Unknown unit type?");
9076 appender.Finish();
9077 return true;
9080 private:
9081 Unit* AddUnit() {
9082 if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
9083 new StringBuilder(this);
9085 return mLast->mUnits.AppendElement();
9088 explicit StringBuilder(StringBuilder* aFirst) : mLast(nullptr), mLength(0) {
9089 MOZ_COUNT_CTOR(StringBuilder);
9090 aFirst->mLast->mNext = WrapUnique(this);
9091 aFirst->mLast = this;
9094 void EncodeAttrString(Span<const char16_t> aStr, BulkAppender& aAppender) {
9095 size_t flushedUntil = 0;
9096 size_t currentPosition = 0;
9097 for (char16_t c : aStr) {
9098 switch (c) {
9099 case '"':
9100 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9101 aAppender.AppendLiteral(u"&quot;");
9102 flushedUntil = currentPosition + 1;
9103 break;
9104 case '&':
9105 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9106 aAppender.AppendLiteral(u"&amp;");
9107 flushedUntil = currentPosition + 1;
9108 break;
9109 case 0x00A0:
9110 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9111 aAppender.AppendLiteral(u"&nbsp;");
9112 flushedUntil = currentPosition + 1;
9113 break;
9114 default:
9115 break;
9117 currentPosition++;
9119 if (currentPosition > flushedUntil) {
9120 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9124 template <class T>
9125 void EncodeTextFragment(Span<const T> aStr, BulkAppender& aAppender) {
9126 size_t flushedUntil = 0;
9127 size_t currentPosition = 0;
9128 for (T c : aStr) {
9129 switch (c) {
9130 case '<':
9131 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9132 aAppender.AppendLiteral(u"&lt;");
9133 flushedUntil = currentPosition + 1;
9134 break;
9135 case '>':
9136 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9137 aAppender.AppendLiteral(u"&gt;");
9138 flushedUntil = currentPosition + 1;
9139 break;
9140 case '&':
9141 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9142 aAppender.AppendLiteral(u"&amp;");
9143 flushedUntil = currentPosition + 1;
9144 break;
9145 case T(0xA0):
9146 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9147 aAppender.AppendLiteral(u"&nbsp;");
9148 flushedUntil = currentPosition + 1;
9149 break;
9150 default:
9151 break;
9153 currentPosition++;
9155 if (currentPosition > flushedUntil) {
9156 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9160 AutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
9161 UniquePtr<StringBuilder> mNext;
9162 StringBuilder* mLast;
9163 // mLength is used only in the first StringBuilder object in the linked list.
9164 CheckedInt<uint32_t> mLength;
9167 static_assert(sizeof(StringBuilder) <= StringBuilder::TARGET_SIZE,
9168 "StringBuilder should fit in the target bucket");
9170 } // namespace
9172 static void AppendEncodedCharacters(const nsTextFragment* aText,
9173 StringBuilder& aBuilder) {
9174 uint32_t extraSpaceNeeded = 0;
9175 uint32_t len = aText->GetLength();
9176 if (aText->Is2b()) {
9177 const char16_t* data = aText->Get2b();
9178 for (uint32_t i = 0; i < len; ++i) {
9179 const char16_t c = data[i];
9180 switch (c) {
9181 case '<':
9182 extraSpaceNeeded += ArrayLength("&lt;") - 2;
9183 break;
9184 case '>':
9185 extraSpaceNeeded += ArrayLength("&gt;") - 2;
9186 break;
9187 case '&':
9188 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9189 break;
9190 case 0x00A0:
9191 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9192 break;
9193 default:
9194 break;
9197 } else {
9198 const char* data = aText->Get1b();
9199 for (uint32_t i = 0; i < len; ++i) {
9200 const unsigned char c = data[i];
9201 switch (c) {
9202 case '<':
9203 extraSpaceNeeded += ArrayLength("&lt;") - 2;
9204 break;
9205 case '>':
9206 extraSpaceNeeded += ArrayLength("&gt;") - 2;
9207 break;
9208 case '&':
9209 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9210 break;
9211 case 0x00A0:
9212 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9213 break;
9214 default:
9215 break;
9220 if (extraSpaceNeeded) {
9221 aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
9222 } else {
9223 aBuilder.Append(aText);
9227 static uint32_t ExtraSpaceNeededForAttrEncoding(const nsAString& aValue) {
9228 const char16_t* c = aValue.BeginReading();
9229 const char16_t* end = aValue.EndReading();
9231 uint32_t extraSpaceNeeded = 0;
9232 while (c < end) {
9233 switch (*c) {
9234 case '"':
9235 extraSpaceNeeded += ArrayLength("&quot;") - 2;
9236 break;
9237 case '&':
9238 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9239 break;
9240 case 0x00A0:
9241 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9242 break;
9243 default:
9244 break;
9246 ++c;
9249 return extraSpaceNeeded;
9252 static void AppendEncodedAttributeValue(const nsAttrValue& aValue,
9253 StringBuilder& aBuilder) {
9254 if (nsAtom* atom = aValue.GetStoredAtom()) {
9255 nsDependentAtomString atomStr(atom);
9256 uint32_t space = ExtraSpaceNeededForAttrEncoding(atomStr);
9257 if (!space) {
9258 aBuilder.Append(atom);
9259 } else {
9260 aBuilder.AppendWithAttrEncode(nsString(atomStr),
9261 atomStr.Length() + space);
9263 return;
9265 // NOTE(emilio): In most cases this will just be a reference to the stored
9266 // nsStringBuffer.
9267 nsString str;
9268 aValue.ToString(str);
9269 uint32_t space = ExtraSpaceNeededForAttrEncoding(str);
9270 if (space) {
9271 aBuilder.AppendWithAttrEncode(std::move(str), str.Length() + space);
9272 } else {
9273 aBuilder.Append(std::move(str));
9277 static void StartElement(Element* aElement, StringBuilder& aBuilder) {
9278 nsAtom* localName = aElement->NodeInfo()->NameAtom();
9279 const int32_t tagNS = aElement->GetNameSpaceID();
9281 aBuilder.Append(u"<");
9282 if (tagNS == kNameSpaceID_XHTML || tagNS == kNameSpaceID_SVG ||
9283 tagNS == kNameSpaceID_MathML) {
9284 aBuilder.Append(localName);
9285 } else {
9286 aBuilder.Append(nsString(aElement->NodeName()));
9289 if (CustomElementData* ceData = aElement->GetCustomElementData()) {
9290 nsAtom* isAttr = ceData->GetIs(aElement);
9291 if (isAttr && !aElement->HasAttr(nsGkAtoms::is)) {
9292 aBuilder.Append(uR"( is=")");
9293 aBuilder.Append(isAttr);
9294 aBuilder.Append(uR"(")");
9298 uint32_t i = 0;
9299 while (BorrowedAttrInfo info = aElement->GetAttrInfoAt(i++)) {
9300 const nsAttrName* name = info.mName;
9302 int32_t attNs = name->NamespaceID();
9303 nsAtom* attName = name->LocalName();
9305 // Filter out any attribute starting with [-|_]moz
9306 // FIXME(emilio): Do we still need this?
9307 nsDependentAtomString attrNameStr(attName);
9308 if (StringBeginsWith(attrNameStr, u"_moz"_ns) ||
9309 StringBeginsWith(attrNameStr, u"-moz"_ns)) {
9310 continue;
9313 aBuilder.Append(u" ");
9315 if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
9316 (attNs == kNameSpaceID_XMLNS && attName == nsGkAtoms::xmlns)) {
9317 // Nothing else required
9318 } else if (attNs == kNameSpaceID_XML) {
9319 aBuilder.Append(u"xml:");
9320 } else if (attNs == kNameSpaceID_XMLNS) {
9321 aBuilder.Append(u"xmlns:");
9322 } else if (attNs == kNameSpaceID_XLink) {
9323 aBuilder.Append(u"xlink:");
9324 } else if (nsAtom* prefix = name->GetPrefix()) {
9325 aBuilder.Append(prefix);
9326 aBuilder.Append(u":");
9329 aBuilder.Append(attName);
9330 aBuilder.Append(uR"(=")");
9331 AppendEncodedAttributeValue(*info.mValue, aBuilder);
9332 aBuilder.Append(uR"(")");
9335 aBuilder.Append(u">");
9338 // Per HTML spec we should append one \n if the first child of
9339 // pre/textarea/listing is a textnode and starts with a \n.
9340 // But because browsers haven't traditionally had that behavior,
9341 // we're not changing our behavior either - yet.
9342 if (aContent->IsHTMLElement()) {
9343 if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
9344 localName == nsGkAtoms::listing) {
9345 nsIContent* fc = aContent->GetFirstChild();
9346 if (fc &&
9347 (fc->NodeType() == nsINode::TEXT_NODE ||
9348 fc->NodeType() == nsINode::CDATA_SECTION_NODE)) {
9349 const nsTextFragment* text = fc->GetText();
9350 if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
9351 aBuilder.Append("\n");
9358 static inline bool ShouldEscape(nsIContent* aParent) {
9359 if (!aParent || !aParent->IsHTMLElement()) {
9360 return true;
9363 static const nsAtom* nonEscapingElements[] = {
9364 nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
9365 nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
9366 nsGkAtoms::plaintext, nsGkAtoms::noscript};
9367 static mozilla::BitBloomFilter<12, nsAtom> sFilter;
9368 static bool sInitialized = false;
9369 if (!sInitialized) {
9370 sInitialized = true;
9371 for (auto& nonEscapingElement : nonEscapingElements) {
9372 sFilter.add(nonEscapingElement);
9376 nsAtom* tag = aParent->NodeInfo()->NameAtom();
9377 if (sFilter.mightContain(tag)) {
9378 for (auto& nonEscapingElement : nonEscapingElements) {
9379 if (tag == nonEscapingElement) {
9380 if (MOZ_UNLIKELY(tag == nsGkAtoms::noscript) &&
9381 MOZ_UNLIKELY(!aParent->OwnerDoc()->IsScriptEnabled())) {
9382 return true;
9384 return false;
9388 return true;
9391 static inline bool IsVoidTag(Element* aElement) {
9392 if (!aElement->IsHTMLElement()) {
9393 return false;
9395 return FragmentOrElement::IsHTMLVoid(aElement->NodeInfo()->NameAtom());
9398 bool nsContentUtils::SerializeNodeToMarkup(nsINode* aRoot,
9399 bool aDescendantsOnly,
9400 nsAString& aOut) {
9401 // If you pass in a DOCUMENT_NODE, you must pass aDescendentsOnly as true
9402 MOZ_ASSERT(aDescendantsOnly || aRoot->NodeType() != nsINode::DOCUMENT_NODE);
9404 nsINode* current =
9405 aDescendantsOnly ? aRoot->GetFirstChildOfTemplateOrNode() : aRoot;
9407 if (!current) {
9408 return true;
9411 StringBuilder builder;
9412 nsIContent* next;
9413 while (true) {
9414 bool isVoid = false;
9415 switch (current->NodeType()) {
9416 case nsINode::ELEMENT_NODE: {
9417 Element* elem = current->AsElement();
9418 StartElement(elem, builder);
9419 isVoid = IsVoidTag(elem);
9420 if (!isVoid && (next = current->GetFirstChildOfTemplateOrNode())) {
9421 current = next;
9422 continue;
9424 break;
9427 case nsINode::TEXT_NODE:
9428 case nsINode::CDATA_SECTION_NODE: {
9429 const nsTextFragment* text = &current->AsText()->TextFragment();
9430 nsIContent* parent = current->GetParent();
9431 if (ShouldEscape(parent)) {
9432 AppendEncodedCharacters(text, builder);
9433 } else {
9434 builder.Append(text);
9436 break;
9439 case nsINode::COMMENT_NODE: {
9440 builder.Append(u"<!--");
9441 builder.Append(static_cast<nsIContent*>(current)->GetText());
9442 builder.Append(u"-->");
9443 break;
9446 case nsINode::DOCUMENT_TYPE_NODE: {
9447 builder.Append(u"<!DOCTYPE ");
9448 builder.Append(nsString(current->NodeName()));
9449 builder.Append(u">");
9450 break;
9453 case nsINode::PROCESSING_INSTRUCTION_NODE: {
9454 builder.Append(u"<?");
9455 builder.Append(nsString(current->NodeName()));
9456 builder.Append(u" ");
9457 builder.Append(static_cast<nsIContent*>(current)->GetText());
9458 builder.Append(u">");
9459 break;
9463 while (true) {
9464 if (!isVoid && current->NodeType() == nsINode::ELEMENT_NODE) {
9465 builder.Append(u"</");
9466 nsIContent* elem = static_cast<nsIContent*>(current);
9467 if (elem->IsHTMLElement() || elem->IsSVGElement() ||
9468 elem->IsMathMLElement()) {
9469 builder.Append(elem->NodeInfo()->NameAtom());
9470 } else {
9471 builder.Append(nsString(current->NodeName()));
9473 builder.Append(u">");
9475 isVoid = false;
9477 if (current == aRoot) {
9478 return builder.ToString(aOut);
9481 if ((next = current->GetNextSibling())) {
9482 current = next;
9483 break;
9486 current = current->GetParentNode();
9488 // Handle template element. If the parent is a template's content,
9489 // then adjust the parent to be the template element.
9490 if (current != aRoot &&
9491 current->NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
9492 DocumentFragment* frag = static_cast<DocumentFragment*>(current);
9493 nsIContent* fragHost = frag->GetHost();
9494 if (fragHost && fragHost->IsTemplateElement()) {
9495 current = fragHost;
9499 if (aDescendantsOnly && current == aRoot) {
9500 return builder.ToString(aOut);
9506 bool nsContentUtils::IsSpecificAboutPage(JSObject* aGlobal, const char* aUri) {
9507 // aUri must start with about: or this isn't the right function to be using.
9508 MOZ_ASSERT(strncmp(aUri, "about:", 6) == 0);
9510 // Make sure the global is a window
9511 MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
9512 nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
9513 if (!win) {
9514 return false;
9517 nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
9518 NS_ENSURE_TRUE(principal, false);
9520 // First check the scheme to avoid getting long specs in the common case.
9521 if (!principal->SchemeIs("about")) {
9522 return false;
9525 nsAutoCString spec;
9526 principal->GetAsciiSpec(spec);
9528 return spec.EqualsASCII(aUri);
9531 /* static */
9532 void nsContentUtils::SetScrollbarsVisibility(nsIDocShell* aDocShell,
9533 bool aVisible) {
9534 if (!aDocShell) {
9535 return;
9537 auto pref = aVisible ? ScrollbarPreference::Auto : ScrollbarPreference::Never;
9538 nsDocShell::Cast(aDocShell)->SetScrollbarPreference(pref);
9541 /* static */
9542 nsIDocShell* nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget) {
9543 if (!aTarget) {
9544 return nullptr;
9547 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
9548 if (nsCOMPtr<nsINode> node = nsINode::FromEventTarget(aTarget)) {
9549 bool ignore;
9550 innerWindow =
9551 do_QueryInterface(node->OwnerDoc()->GetScriptHandlingObject(ignore));
9552 } else if ((innerWindow = nsPIDOMWindowInner::FromEventTarget(aTarget))) {
9553 // Nothing else to do
9554 } else {
9555 nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(aTarget);
9556 if (helper) {
9557 innerWindow = helper->GetOwner();
9561 if (innerWindow) {
9562 return innerWindow->GetDocShell();
9565 return nullptr;
9569 * Note: this function only relates to figuring out HTTPS state, which is an
9570 * input to the Secure Context algorithm. We are not actually implementing any
9571 * part of the Secure Context algorithm itself here.
9573 * This is a bit of a hack. Ideally we'd propagate HTTPS state through
9574 * nsIChannel as described in the Fetch and HTML specs, but making channels
9575 * know about whether they should inherit HTTPS state, propagating information
9576 * about who the channel's "client" is, exposing GetHttpsState API on channels
9577 * and modifying the various cache implementations to store and retrieve HTTPS
9578 * state involves a huge amount of code (see bug 1220687). We avoid that for
9579 * now using this function.
9581 * This function takes advantage of the observation that we can return true if
9582 * nsIContentSecurityManager::IsOriginPotentiallyTrustworthy returns true for
9583 * the document's origin (e.g. the origin has a scheme of 'https' or host
9584 * 'localhost' etc.). Since we generally propagate a creator document's origin
9585 * onto data:, blob:, etc. documents, this works for them too.
9587 * The scenario where this observation breaks down is sandboxing without the
9588 * 'allow-same-origin' flag, since in this case a document is given a unique
9589 * origin (IsOriginPotentiallyTrustworthy would return false). We handle that
9590 * by using the origin that the document would have had had it not been
9591 * sandboxed.
9593 * DEFICIENCIES: Note that this function uses nsIScriptSecurityManager's
9594 * getChannelResultPrincipalIfNotSandboxed, and that method's ignoring of
9595 * sandboxing is limited to the immediate sandbox. In the case that aDocument
9596 * should inherit its origin (e.g. data: URI) but its parent has ended up
9597 * with a unique origin due to sandboxing further up the parent chain we may
9598 * end up returning false when we would ideally return true (since we will
9599 * examine the parent's origin for 'https' and not finding it.) This means
9600 * that we may restrict the privileges of some pages unnecessarily in this
9601 * edge case.
9603 /* static */
9604 bool nsContentUtils::HttpsStateIsModern(Document* aDocument) {
9605 if (!aDocument) {
9606 return false;
9609 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
9611 if (principal->IsSystemPrincipal()) {
9612 return true;
9615 // If aDocument is sandboxed, try and get the principal that it would have
9616 // been given had it not been sandboxed:
9617 if (principal->GetIsNullPrincipal() &&
9618 (aDocument->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
9619 nsIChannel* channel = aDocument->GetChannel();
9620 if (channel) {
9621 nsCOMPtr<nsIScriptSecurityManager> ssm =
9622 nsContentUtils::GetSecurityManager();
9623 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9624 channel, getter_AddRefs(principal));
9625 if (NS_FAILED(rv)) {
9626 return false;
9628 if (principal->IsSystemPrincipal()) {
9629 // If a document with the system principal is sandboxing a subdocument
9630 // that would normally inherit the embedding element's principal (e.g.
9631 // a srcdoc document) then the embedding document does not trust the
9632 // content that is written to the embedded document. Unlike when the
9633 // embedding document is https, in this case we have no indication as
9634 // to whether the embedded document's contents are delivered securely
9635 // or not, and the sandboxing would possibly indicate that they were
9636 // not. To play it safe we return false here. (See bug 1162772
9637 // comment 73-80.)
9638 return false;
9643 if (principal->GetIsNullPrincipal()) {
9644 return false;
9647 MOZ_ASSERT(principal->GetIsContentPrincipal());
9649 return principal->GetIsOriginPotentiallyTrustworthy();
9652 /* static */
9653 bool nsContentUtils::ComputeIsSecureContext(nsIChannel* aChannel) {
9654 MOZ_ASSERT(aChannel);
9656 nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
9657 nsCOMPtr<nsIPrincipal> principal;
9658 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9659 aChannel, getter_AddRefs(principal));
9660 if (NS_FAILED(rv)) {
9661 return false;
9664 const RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
9666 if (principal->IsSystemPrincipal()) {
9667 // If the load would've been sandboxed, treat this load as an untrusted
9668 // load, as system code considers sandboxed resources insecure.
9669 return !loadInfo->GetLoadingSandboxed();
9672 if (principal->GetIsNullPrincipal()) {
9673 return false;
9676 if (const RefPtr<WindowContext> windowContext =
9677 WindowContext::GetById(loadInfo->GetInnerWindowID())) {
9678 if (!windowContext->GetIsSecureContext()) {
9679 return false;
9683 return principal->GetIsOriginPotentiallyTrustworthy();
9686 /* static */
9687 void nsContentUtils::TryToUpgradeElement(Element* aElement) {
9688 NodeInfo* nodeInfo = aElement->NodeInfo();
9689 RefPtr<nsAtom> typeAtom =
9690 aElement->GetCustomElementData()->GetCustomElementType();
9692 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9693 CustomElementDefinition* definition =
9694 nsContentUtils::LookupCustomElementDefinition(
9695 nodeInfo->GetDocument(), nodeInfo->NameAtom(),
9696 nodeInfo->NamespaceID(), typeAtom);
9697 if (definition) {
9698 nsContentUtils::EnqueueUpgradeReaction(aElement, definition);
9699 } else {
9700 // Add an unresolved custom element that is a candidate for upgrade when a
9701 // custom element is connected to the document.
9702 nsContentUtils::RegisterUnresolvedElement(aElement, typeAtom);
9706 MOZ_CAN_RUN_SCRIPT
9707 static void DoCustomElementCreate(Element** aElement, JSContext* aCx,
9708 Document* aDoc, NodeInfo* aNodeInfo,
9709 CustomElementConstructor* aConstructor,
9710 ErrorResult& aRv, FromParser aFromParser) {
9711 JS::Rooted<JS::Value> constructResult(aCx);
9712 aConstructor->Construct(&constructResult, aRv, "Custom Element Create",
9713 CallbackFunction::eRethrowExceptions);
9714 if (aRv.Failed()) {
9715 return;
9718 RefPtr<Element> element;
9719 // constructResult is an ObjectValue because construction with a callback
9720 // always forms the return value from a JSObject.
9721 UNWRAP_OBJECT(Element, &constructResult, element);
9722 if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9723 if (!element || !element->IsHTMLElement()) {
9724 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9725 "HTMLElement");
9726 return;
9728 } else {
9729 if (!element || !element->IsXULElement()) {
9730 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9731 "XULElement");
9732 return;
9736 nsAtom* localName = aNodeInfo->NameAtom();
9738 if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
9739 element->HasChildren() || element->GetAttrCount() ||
9740 element->NodeInfo()->NameAtom() != localName) {
9741 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
9742 return;
9745 if (element->IsHTMLElement()) {
9746 static_cast<HTMLElement*>(&*element)->InhibitRestoration(
9747 !(aFromParser & FROM_PARSER_NETWORK));
9750 element.forget(aElement);
9753 /* static */
9754 nsresult nsContentUtils::NewXULOrHTMLElement(
9755 Element** aResult, mozilla::dom::NodeInfo* aNodeInfo,
9756 FromParser aFromParser, nsAtom* aIsAtom,
9757 mozilla::dom::CustomElementDefinition* aDefinition) {
9758 RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
9759 MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
9760 nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
9761 "Can only create XUL or XHTML elements.");
9763 nsAtom* name = nodeInfo->NameAtom();
9764 int32_t tag = eHTMLTag_unknown;
9765 bool isCustomElementName = false;
9766 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9767 tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
9768 isCustomElementName =
9769 (tag == eHTMLTag_userdefined &&
9770 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML));
9771 } else { // kNameSpaceID_XUL
9772 if (aIsAtom) {
9773 // Make sure the customized built-in element to be constructed confirms
9774 // to our naming requirement, i.e. [is] must be a dashed name and
9775 // the tag name must not.
9776 // if so, set isCustomElementName to false to kick off all the logics
9777 // that pick up aIsAtom.
9778 if (nsContentUtils::IsNameWithDash(aIsAtom) &&
9779 !nsContentUtils::IsNameWithDash(name)) {
9780 isCustomElementName = false;
9781 } else {
9782 isCustomElementName =
9783 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9785 } else {
9786 isCustomElementName =
9787 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9791 nsAtom* tagAtom = nodeInfo->NameAtom();
9792 nsAtom* typeAtom = nullptr;
9793 bool isCustomElement = isCustomElementName || aIsAtom;
9794 if (isCustomElement) {
9795 typeAtom = isCustomElementName ? tagAtom : aIsAtom;
9798 MOZ_ASSERT_IF(aDefinition, isCustomElement);
9800 // https://dom.spec.whatwg.org/#concept-create-element
9801 // We only handle the "synchronous custom elements flag is set" now.
9802 // For the unset case (e.g. cloning a node), see bug 1319342 for that.
9803 // Step 4.
9804 RefPtr<CustomElementDefinition> definition = aDefinition;
9805 if (isCustomElement && !definition) {
9806 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9807 definition = nsContentUtils::LookupCustomElementDefinition(
9808 nodeInfo->GetDocument(), nodeInfo->NameAtom(), nodeInfo->NamespaceID(),
9809 typeAtom);
9812 // It might be a problem that parser synchronously calls constructor, so filed
9813 // bug 1378079 to figure out what we should do for parser case.
9814 if (definition) {
9816 * Synchronous custom elements flag is determined by 3 places in spec,
9817 * 1) create an element for a token, the flag is determined by
9818 * "will execute script" which is not originally created
9819 * for the HTML fragment parsing algorithm.
9820 * 2) createElement and createElementNS, the flag is the same as
9821 * NOT_FROM_PARSER.
9822 * 3) clone a node, our implementation will not go into this function.
9823 * For the unset case which is non-synchronous only applied for
9824 * inner/outerHTML.
9826 bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT;
9827 // Per discussion in https://github.com/w3c/webcomponents/issues/635,
9828 // use entry global in those places that are called from JS APIs and use the
9829 // node document's global object if it is called from parser.
9830 nsIGlobalObject* global;
9831 if (aFromParser == dom::NOT_FROM_PARSER) {
9832 global = GetEntryGlobal();
9834 // Documents created from the PrototypeDocumentSink always use
9835 // NOT_FROM_PARSER for non-XUL elements. We can get the global from the
9836 // document in that case.
9837 if (!global) {
9838 Document* doc = nodeInfo->GetDocument();
9839 if (doc && doc->LoadedFromPrototype()) {
9840 global = doc->GetScopeObject();
9843 } else {
9844 global = nodeInfo->GetDocument()->GetScopeObject();
9846 if (!global) {
9847 // In browser chrome code, one may have access to a document which doesn't
9848 // have scope object anymore.
9849 return NS_ERROR_FAILURE;
9852 AutoAllowLegacyScriptExecution exemption;
9853 AutoEntryScript aes(global, "create custom elements");
9854 JSContext* cx = aes.cx();
9855 ErrorResult rv;
9857 // Step 5.
9858 if (definition->IsCustomBuiltIn()) {
9859 // SetupCustomElement() should be called with an element that don't have
9860 // CustomElementData setup, if not we will hit the assertion in
9861 // SetCustomElementData().
9862 // Built-in element
9863 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9864 *aResult =
9865 CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9866 } else {
9867 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9869 (*aResult)->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom));
9870 if (synchronousCustomElements) {
9871 CustomElementRegistry::Upgrade(*aResult, definition, rv);
9872 if (rv.MaybeSetPendingException(cx)) {
9873 aes.ReportException();
9875 } else {
9876 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9879 return NS_OK;
9882 // Step 6.1.
9883 if (synchronousCustomElements) {
9884 definition->mPrefixStack.AppendElement(nodeInfo->GetPrefixAtom());
9885 RefPtr<Document> doc = nodeInfo->GetDocument();
9886 DoCustomElementCreate(aResult, cx, doc, nodeInfo,
9887 MOZ_KnownLive(definition->mConstructor), rv,
9888 aFromParser);
9889 if (rv.MaybeSetPendingException(cx)) {
9890 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9891 NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(),
9892 aFromParser));
9893 } else {
9894 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9896 (*aResult)->SetDefined(false);
9898 definition->mPrefixStack.RemoveLastElement();
9899 return NS_OK;
9902 // Step 6.2.
9903 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9904 NS_IF_ADDREF(*aResult =
9905 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9906 } else {
9907 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9909 (*aResult)->SetCustomElementData(
9910 MakeUnique<CustomElementData>(definition->mType));
9911 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9912 return NS_OK;
9915 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9916 // Per the Custom Element specification, unknown tags that are valid custom
9917 // element names should be HTMLElement instead of HTMLUnknownElement.
9918 if (isCustomElementName) {
9919 NS_IF_ADDREF(*aResult =
9920 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9921 } else {
9922 *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9924 } else {
9925 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9928 if (!*aResult) {
9929 return NS_ERROR_OUT_OF_MEMORY;
9932 if (isCustomElement) {
9933 (*aResult)->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom));
9934 nsContentUtils::RegisterCallbackUpgradeElement(*aResult, typeAtom);
9937 return NS_OK;
9940 CustomElementRegistry* nsContentUtils::GetCustomElementRegistry(
9941 Document* aDoc) {
9942 MOZ_ASSERT(aDoc);
9944 if (!aDoc->GetDocShell()) {
9945 return nullptr;
9948 nsPIDOMWindowInner* window = aDoc->GetInnerWindow();
9949 if (!window) {
9950 return nullptr;
9953 return window->CustomElements();
9956 /* static */
9957 CustomElementDefinition* nsContentUtils::LookupCustomElementDefinition(
9958 Document* aDoc, nsAtom* aNameAtom, uint32_t aNameSpaceID,
9959 nsAtom* aTypeAtom) {
9960 if (aNameSpaceID != kNameSpaceID_XUL && aNameSpaceID != kNameSpaceID_XHTML) {
9961 return nullptr;
9964 RefPtr<CustomElementRegistry> registry = GetCustomElementRegistry(aDoc);
9965 if (!registry) {
9966 return nullptr;
9969 return registry->LookupCustomElementDefinition(aNameAtom, aNameSpaceID,
9970 aTypeAtom);
9973 /* static */
9974 void nsContentUtils::RegisterCallbackUpgradeElement(Element* aElement,
9975 nsAtom* aTypeName) {
9976 MOZ_ASSERT(aElement);
9978 Document* doc = aElement->OwnerDoc();
9979 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9980 if (registry) {
9981 registry->RegisterCallbackUpgradeElement(aElement, aTypeName);
9985 /* static */
9986 void nsContentUtils::RegisterUnresolvedElement(Element* aElement,
9987 nsAtom* aTypeName) {
9988 MOZ_ASSERT(aElement);
9990 Document* doc = aElement->OwnerDoc();
9991 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9992 if (registry) {
9993 registry->RegisterUnresolvedElement(aElement, aTypeName);
9997 /* static */
9998 void nsContentUtils::UnregisterUnresolvedElement(Element* aElement) {
9999 MOZ_ASSERT(aElement);
10001 nsAtom* typeAtom = aElement->GetCustomElementData()->GetCustomElementType();
10002 Document* doc = aElement->OwnerDoc();
10003 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
10004 if (registry) {
10005 registry->UnregisterUnresolvedElement(aElement, typeAtom);
10009 /* static */
10010 void nsContentUtils::EnqueueUpgradeReaction(
10011 Element* aElement, CustomElementDefinition* aDefinition) {
10012 MOZ_ASSERT(aElement);
10014 Document* doc = aElement->OwnerDoc();
10016 // No DocGroup means no custom element reactions stack.
10017 if (!doc->GetDocGroup()) {
10018 return;
10021 CustomElementReactionsStack* stack =
10022 doc->GetDocGroup()->CustomElementReactionsStack();
10023 stack->EnqueueUpgradeReaction(aElement, aDefinition);
10026 /* static */
10027 void nsContentUtils::EnqueueLifecycleCallback(
10028 ElementCallbackType aType, Element* aCustomElement,
10029 const LifecycleCallbackArgs& aArgs, CustomElementDefinition* aDefinition) {
10030 // No DocGroup means no custom element reactions stack.
10031 if (!aCustomElement->OwnerDoc()->GetDocGroup()) {
10032 return;
10035 CustomElementRegistry::EnqueueLifecycleCallback(aType, aCustomElement, aArgs,
10036 aDefinition);
10039 /* static */
10040 CustomElementFormValue nsContentUtils::ConvertToCustomElementFormValue(
10041 const Nullable<OwningFileOrUSVStringOrFormData>& aState) {
10042 if (aState.IsNull()) {
10043 return void_t{};
10045 const auto& state = aState.Value();
10046 if (state.IsFile()) {
10047 RefPtr<BlobImpl> impl = state.GetAsFile()->Impl();
10048 return {std::move(impl)};
10050 if (state.IsUSVString()) {
10051 return state.GetAsUSVString();
10053 return state.GetAsFormData()->ConvertToCustomElementFormValue();
10056 /* static */
10057 Nullable<OwningFileOrUSVStringOrFormData>
10058 nsContentUtils::ExtractFormAssociatedCustomElementValue(
10059 nsIGlobalObject* aGlobal,
10060 const mozilla::dom::CustomElementFormValue& aCEValue) {
10061 MOZ_ASSERT(aGlobal);
10063 OwningFileOrUSVStringOrFormData value;
10064 switch (aCEValue.type()) {
10065 case CustomElementFormValue::TBlobImpl: {
10066 RefPtr<File> file = File::Create(aGlobal, aCEValue.get_BlobImpl());
10067 if (NS_WARN_IF(!file)) {
10068 return {};
10070 value.SetAsFile() = file;
10071 } break;
10073 case CustomElementFormValue::TnsString:
10074 value.SetAsUSVString() = aCEValue.get_nsString();
10075 break;
10077 case CustomElementFormValue::TArrayOfFormDataTuple: {
10078 const auto& array = aCEValue.get_ArrayOfFormDataTuple();
10079 auto formData = MakeRefPtr<FormData>();
10081 for (auto i = 0ul; i < array.Length(); ++i) {
10082 const auto& item = array.ElementAt(i);
10083 switch (item.value().type()) {
10084 case FormDataValue::TnsString:
10085 formData->AddNameValuePair(item.name(),
10086 item.value().get_nsString());
10087 break;
10089 case FormDataValue::TBlobImpl: {
10090 auto blobImpl = item.value().get_BlobImpl();
10091 auto* blob = Blob::Create(aGlobal, blobImpl);
10092 formData->AddNameBlobPair(item.name(), blob);
10093 } break;
10095 default:
10096 continue;
10100 value.SetAsFormData() = formData;
10101 } break;
10102 case CustomElementFormValue::Tvoid_t:
10103 return {};
10104 default:
10105 NS_WARNING("Invalid CustomElementContentData type!");
10106 return {};
10108 return value;
10111 /* static */
10112 void nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
10113 Document* aDocument, nsTArray<nsIContent*>& aElements) {
10114 MOZ_ASSERT(aDocument);
10115 #ifdef DEBUG
10116 size_t oldLength = aElements.Length();
10117 #endif
10119 if (PresShell* presShell = aDocument->GetPresShell()) {
10120 if (nsIFrame* scrollFrame = presShell->GetRootScrollFrame()) {
10121 nsIAnonymousContentCreator* creator = do_QueryFrame(scrollFrame);
10122 MOZ_ASSERT(
10123 creator,
10124 "scroll frame should always implement nsIAnonymousContentCreator");
10125 creator->AppendAnonymousContentTo(aElements, 0);
10127 if (nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame()) {
10128 canvasFrame->AppendAnonymousContentTo(aElements, 0);
10132 #ifdef DEBUG
10133 for (size_t i = oldLength; i < aElements.Length(); i++) {
10134 MOZ_ASSERT(
10135 aElements[i]->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent),
10136 "Someone here has lied, or missed to flag the node");
10138 #endif
10141 static void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame,
10142 nsTArray<nsIContent*>& aKids,
10143 uint32_t aFlags) {
10144 if (nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame)) {
10145 ac->AppendAnonymousContentTo(aKids, aFlags);
10149 /* static */
10150 void nsContentUtils::AppendNativeAnonymousChildren(const nsIContent* aContent,
10151 nsTArray<nsIContent*>& aKids,
10152 uint32_t aFlags) {
10153 if (aContent->MayHaveAnonymousChildren()) {
10154 if (nsIFrame* primaryFrame = aContent->GetPrimaryFrame()) {
10155 // NAC created by the element's primary frame.
10156 AppendNativeAnonymousChildrenFromFrame(primaryFrame, aKids, aFlags);
10158 // NAC created by any other non-primary frames for the element.
10159 AutoTArray<nsIFrame::OwnedAnonBox, 8> ownedAnonBoxes;
10160 primaryFrame->AppendOwnedAnonBoxes(ownedAnonBoxes);
10161 for (nsIFrame::OwnedAnonBox& box : ownedAnonBoxes) {
10162 MOZ_ASSERT(box.mAnonBoxFrame->GetContent() == aContent);
10163 AppendNativeAnonymousChildrenFromFrame(box.mAnonBoxFrame, aKids,
10164 aFlags);
10168 // Get manually created NAC (editor resize handles, etc.).
10169 if (auto nac = static_cast<ManualNACArray*>(
10170 aContent->GetProperty(nsGkAtoms::manualNACProperty))) {
10171 aKids.AppendElements(*nac);
10175 // The root scroll frame is not the primary frame of the root element.
10176 // Detect and handle this case.
10177 if (!(aFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
10178 aContent == aContent->OwnerDoc()->GetRootElement()) {
10179 AppendDocumentLevelNativeAnonymousContentTo(aContent->OwnerDoc(), aKids);
10183 bool nsContentUtils::IsImageAvailable(nsIContent* aLoadingNode, nsIURI* aURI,
10184 nsIPrincipal* aDefaultTriggeringPrincipal,
10185 CORSMode aCORSMode) {
10186 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
10187 QueryTriggeringPrincipal(aLoadingNode, aDefaultTriggeringPrincipal,
10188 getter_AddRefs(triggeringPrincipal));
10189 MOZ_ASSERT(triggeringPrincipal);
10191 Document* doc = aLoadingNode->OwnerDoc();
10192 return IsImageAvailable(aURI, triggeringPrincipal, aCORSMode, doc);
10195 bool nsContentUtils::IsImageAvailable(nsIURI* aURI,
10196 nsIPrincipal* aTriggeringPrincipal,
10197 CORSMode aCORSMode, Document* aDoc) {
10198 imgLoader* imgLoader = GetImgLoaderForDocument(aDoc);
10199 return imgLoader->IsImageAvailable(aURI, aTriggeringPrincipal, aCORSMode,
10200 aDoc);
10203 /* static */
10204 bool nsContentUtils::QueryTriggeringPrincipal(
10205 nsIContent* aLoadingNode, nsIPrincipal* aDefaultPrincipal,
10206 nsIPrincipal** aTriggeringPrincipal) {
10207 MOZ_ASSERT(aLoadingNode);
10208 MOZ_ASSERT(aTriggeringPrincipal);
10210 bool result = false;
10211 nsCOMPtr<nsIPrincipal> loadingPrincipal = aDefaultPrincipal;
10212 if (!loadingPrincipal) {
10213 loadingPrincipal = aLoadingNode->NodePrincipal();
10216 // If aLoadingNode is content, bail out early.
10217 if (!aLoadingNode->NodePrincipal()->IsSystemPrincipal()) {
10218 loadingPrincipal.forget(aTriggeringPrincipal);
10219 return result;
10222 nsAutoString loadingStr;
10223 if (aLoadingNode->IsElement()) {
10224 aLoadingNode->AsElement()->GetAttr(
10225 kNameSpaceID_None, nsGkAtoms::triggeringprincipal, loadingStr);
10228 // Fall back if 'triggeringprincipal' isn't specified,
10229 if (loadingStr.IsEmpty()) {
10230 loadingPrincipal.forget(aTriggeringPrincipal);
10231 return result;
10234 nsCString binary;
10235 nsCOMPtr<nsIPrincipal> serializedPrin =
10236 BasePrincipal::FromJSON(NS_ConvertUTF16toUTF8(loadingStr));
10237 if (serializedPrin) {
10238 result = true;
10239 serializedPrin.forget(aTriggeringPrincipal);
10242 if (!result) {
10243 // Fallback if the deserialization is failed.
10244 loadingPrincipal.forget(aTriggeringPrincipal);
10247 return result;
10250 /* static */
10251 void nsContentUtils::GetContentPolicyTypeForUIImageLoading(
10252 nsIContent* aLoadingNode, nsIPrincipal** aTriggeringPrincipal,
10253 nsContentPolicyType& aContentPolicyType, uint64_t* aRequestContextID) {
10254 MOZ_ASSERT(aRequestContextID);
10256 bool result = QueryTriggeringPrincipal(aLoadingNode, aTriggeringPrincipal);
10257 if (result) {
10258 // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for
10259 // indicating it's a favicon loading.
10260 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
10262 nsAutoString requestContextID;
10263 if (aLoadingNode->IsElement()) {
10264 aLoadingNode->AsElement()->GetAttr(
10265 kNameSpaceID_None, nsGkAtoms::requestcontextid, requestContextID);
10267 nsresult rv;
10268 int64_t val = requestContextID.ToInteger64(&rv);
10269 *aRequestContextID = NS_SUCCEEDED(rv) ? val : 0;
10270 } else {
10271 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
10275 /* static */
10276 nsresult nsContentUtils::CreateJSValueFromSequenceOfObject(
10277 JSContext* aCx, const Sequence<JSObject*>& aTransfer,
10278 JS::MutableHandle<JS::Value> aValue) {
10279 if (aTransfer.IsEmpty()) {
10280 return NS_OK;
10283 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, aTransfer.Length()));
10284 if (!array) {
10285 return NS_ERROR_OUT_OF_MEMORY;
10288 for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
10289 JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
10290 if (!object) {
10291 continue;
10294 if (NS_WARN_IF(
10295 !JS_DefineElement(aCx, array, i, object, JSPROP_ENUMERATE))) {
10296 return NS_ERROR_OUT_OF_MEMORY;
10300 aValue.setObject(*array);
10301 return NS_OK;
10304 /* static */
10305 void nsContentUtils::StructuredClone(JSContext* aCx, nsIGlobalObject* aGlobal,
10306 JS::Handle<JS::Value> aValue,
10307 const StructuredSerializeOptions& aOptions,
10308 JS::MutableHandle<JS::Value> aRetval,
10309 ErrorResult& aError) {
10310 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
10311 aError = nsContentUtils::CreateJSValueFromSequenceOfObject(
10312 aCx, aOptions.mTransfer, &transferArray);
10313 if (NS_WARN_IF(aError.Failed())) {
10314 return;
10317 JS::CloneDataPolicy clonePolicy;
10318 // We are definitely staying in the same agent cluster.
10319 clonePolicy.allowIntraClusterClonableSharedObjects();
10320 if (aGlobal->IsSharedMemoryAllowed()) {
10321 clonePolicy.allowSharedMemoryObjects();
10324 StructuredCloneHolder holder(StructuredCloneHolder::CloningSupported,
10325 StructuredCloneHolder::TransferringSupported,
10326 JS::StructuredCloneScope::SameProcess);
10327 holder.Write(aCx, aValue, transferArray, clonePolicy, aError);
10328 if (NS_WARN_IF(aError.Failed())) {
10329 return;
10332 holder.Read(aGlobal, aCx, aRetval, clonePolicy, aError);
10333 if (NS_WARN_IF(aError.Failed())) {
10334 return;
10337 nsTArray<RefPtr<MessagePort>> ports = holder.TakeTransferredPorts();
10338 Unused << ports;
10341 /* static */
10342 bool nsContentUtils::ShouldBlockReservedKeys(WidgetKeyboardEvent* aKeyEvent) {
10343 nsCOMPtr<nsIPrincipal> principal;
10344 RefPtr<Element> targetElement =
10345 Element::FromEventTargetOrNull(aKeyEvent->mOriginalTarget);
10346 nsCOMPtr<nsIBrowser> targetBrowser;
10347 if (targetElement) {
10348 targetBrowser = targetElement->AsBrowser();
10350 bool isRemoteBrowser = false;
10351 if (targetBrowser) {
10352 targetBrowser->GetIsRemoteBrowser(&isRemoteBrowser);
10355 if (isRemoteBrowser) {
10356 targetBrowser->GetContentPrincipal(getter_AddRefs(principal));
10357 return principal ? nsContentUtils::IsSitePermDeny(principal, "shortcuts"_ns)
10358 : false;
10361 if (targetElement) {
10362 Document* doc = targetElement->GetUncomposedDoc();
10363 if (doc) {
10364 RefPtr<WindowContext> wc = doc->GetWindowContext();
10365 if (wc) {
10366 return wc->TopWindowContext()->GetShortcutsPermission() ==
10367 nsIPermissionManager::DENY_ACTION;
10372 return false;
10376 * Checks whether the given type is a supported document type for
10377 * loading within the nsObjectLoadingContent specified by aContent.
10379 * NOTE Helper method for nsContentUtils::HtmlObjectContentTypeForMIMEType.
10380 * NOTE Does not take content policy or capabilities into account
10382 static bool HtmlObjectContentSupportsDocument(const nsCString& aMimeType) {
10383 nsCOMPtr<nsIWebNavigationInfo> info(
10384 do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID));
10385 if (!info) {
10386 return false;
10389 uint32_t supported;
10390 nsresult rv = info->IsTypeSupported(aMimeType, &supported);
10392 if (NS_FAILED(rv)) {
10393 return false;
10396 if (supported != nsIWebNavigationInfo::UNSUPPORTED) {
10397 // Don't want to support plugins as documents
10398 return supported != nsIWebNavigationInfo::FALLBACK;
10401 // Try a stream converter
10402 // NOTE: We treat any type we can convert from as a supported type. If a
10403 // type is not actually supported, the URI loader will detect that and
10404 // return an error, and we'll fallback.
10405 nsCOMPtr<nsIStreamConverterService> convServ =
10406 do_GetService("@mozilla.org/streamConverters;1");
10407 bool canConvert = false;
10408 if (convServ) {
10409 rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
10411 return NS_SUCCEEDED(rv) && canConvert;
10414 /* static */
10415 already_AddRefed<nsIPluginTag> nsContentUtils::PluginTagForType(
10416 const nsCString& aMIMEType, bool aNoFakePlugin) {
10417 RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
10418 nsCOMPtr<nsIPluginTag> tag;
10419 NS_ENSURE_TRUE(pluginHost, nullptr);
10421 // ShouldPlay will handle the case where the plugin is disabled
10422 pluginHost->GetPluginTagForType(
10423 aMIMEType,
10424 aNoFakePlugin ? nsPluginHost::eExcludeFake : nsPluginHost::eExcludeNone,
10425 getter_AddRefs(tag));
10427 return tag.forget();
10430 /* static */
10431 uint32_t nsContentUtils::HtmlObjectContentTypeForMIMEType(
10432 const nsCString& aMIMEType, bool aNoFakePlugin) {
10433 if (aMIMEType.IsEmpty()) {
10434 return nsIObjectLoadingContent::TYPE_NULL;
10437 if (imgLoader::SupportImageWithMimeType(aMIMEType)) {
10438 return ResolveObjectType(nsIObjectLoadingContent::TYPE_IMAGE);
10441 // Faking support of the PDF content as a document for EMBED tags
10442 // when internal PDF viewer is enabled.
10443 if (aMIMEType.LowerCaseEqualsLiteral("application/pdf") && IsPDFJSEnabled()) {
10444 return nsIObjectLoadingContent::TYPE_DOCUMENT;
10447 if (HtmlObjectContentSupportsDocument(aMIMEType)) {
10448 return nsIObjectLoadingContent::TYPE_DOCUMENT;
10451 bool isSpecialPlugin = nsPluginHost::GetSpecialType(aMIMEType) !=
10452 nsPluginHost::eSpecialType_None;
10453 if (isSpecialPlugin) {
10454 return nsIObjectLoadingContent::TYPE_FALLBACK;
10457 return nsIObjectLoadingContent::TYPE_NULL;
10460 /* static */
10461 bool nsContentUtils::IsLocalRefURL(const nsAString& aString) {
10462 return !aString.IsEmpty() && aString[0] == '#';
10465 // We use only 53 bits for the ID so that it can be converted to and from a JS
10466 // value without loss of precision. The upper bits of the ID hold the process
10467 // ID. The lower bits identify the object itself.
10468 static constexpr uint64_t kIdTotalBits = 53;
10469 static constexpr uint64_t kIdProcessBits = 22;
10470 static constexpr uint64_t kIdBits = kIdTotalBits - kIdProcessBits;
10472 /* static */
10473 uint64_t nsContentUtils::GenerateProcessSpecificId(uint64_t aId) {
10474 uint64_t processId = 0;
10475 if (XRE_IsContentProcess()) {
10476 ContentChild* cc = ContentChild::GetSingleton();
10477 processId = cc->GetID();
10480 MOZ_RELEASE_ASSERT(processId < (uint64_t(1) << kIdProcessBits));
10481 uint64_t processBits = processId & ((uint64_t(1) << kIdProcessBits) - 1);
10483 uint64_t id = aId;
10484 MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
10485 uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
10487 return (processBits << kIdBits) | bits;
10490 /* static */
10491 std::tuple<uint64_t, uint64_t> nsContentUtils::SplitProcessSpecificId(
10492 uint64_t aId) {
10493 return {aId >> kIdBits, aId & ((uint64_t(1) << kIdBits) - 1)};
10496 // Next process-local Tab ID.
10497 static uint64_t gNextTabId = 0;
10499 /* static */
10500 uint64_t nsContentUtils::GenerateTabId() {
10501 return GenerateProcessSpecificId(++gNextTabId);
10504 // Next process-local Browser ID.
10505 static uint64_t gNextBrowserId = 0;
10507 /* static */
10508 uint64_t nsContentUtils::GenerateBrowserId() {
10509 return GenerateProcessSpecificId(++gNextBrowserId);
10512 // Next process-local Browsing Context ID.
10513 static uint64_t gNextBrowsingContextId = 0;
10515 /* static */
10516 uint64_t nsContentUtils::GenerateBrowsingContextId() {
10517 return GenerateProcessSpecificId(++gNextBrowsingContextId);
10520 // Next process-local Window ID.
10521 static uint64_t gNextWindowId = 0;
10523 /* static */
10524 uint64_t nsContentUtils::GenerateWindowId() {
10525 return GenerateProcessSpecificId(++gNextWindowId);
10528 // Next process-local load.
10529 static Atomic<uint64_t> gNextLoadIdentifier(0);
10531 /* static */
10532 uint64_t nsContentUtils::GenerateLoadIdentifier() {
10533 return GenerateProcessSpecificId(++gNextLoadIdentifier);
10536 /* static */
10537 bool nsContentUtils::GetUserIsInteracting() {
10538 return UserInteractionObserver::sUserActive;
10541 /* static */
10542 bool nsContentUtils::GetSourceMapURL(nsIHttpChannel* aChannel,
10543 nsACString& aResult) {
10544 nsresult rv = aChannel->GetResponseHeader("SourceMap"_ns, aResult);
10545 if (NS_FAILED(rv)) {
10546 rv = aChannel->GetResponseHeader("X-SourceMap"_ns, aResult);
10548 return NS_SUCCEEDED(rv);
10551 /* static */
10552 bool nsContentUtils::IsMessageInputEvent(const IPC::Message& aMsg) {
10553 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10554 mozilla::dom::PBrowser::PBrowserStart) {
10555 switch (aMsg.type()) {
10556 case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
10557 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10558 case mozilla::dom::PBrowser::Msg_RealMouseEnterExitWidgetEvent__ID:
10559 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10560 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10561 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10562 case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
10563 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10564 case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
10565 return true;
10568 return false;
10571 /* static */
10572 bool nsContentUtils::IsMessageCriticalInputEvent(const IPC::Message& aMsg) {
10573 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10574 mozilla::dom::PBrowser::PBrowserStart) {
10575 switch (aMsg.type()) {
10576 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10577 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10578 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10579 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10580 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10581 return true;
10584 return false;
10587 static const char* kUserInteractionInactive = "user-interaction-inactive";
10588 static const char* kUserInteractionActive = "user-interaction-active";
10590 void nsContentUtils::UserInteractionObserver::Init() {
10591 // Listen for the observer messages from EventStateManager which are telling
10592 // us whether or not the user is interacting.
10593 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10594 obs->AddObserver(this, kUserInteractionInactive, false);
10595 obs->AddObserver(this, kUserInteractionActive, false);
10597 // We can't register ourselves as an annotator yet, as the
10598 // BackgroundHangMonitor hasn't started yet. It will have started by the
10599 // time we have the chance to spin the event loop.
10600 RefPtr<UserInteractionObserver> self = this;
10601 NS_DispatchToMainThread(NS_NewRunnableFunction(
10602 "nsContentUtils::UserInteractionObserver::Init",
10603 [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
10606 void nsContentUtils::UserInteractionObserver::Shutdown() {
10607 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10608 if (obs) {
10609 obs->RemoveObserver(this, kUserInteractionInactive);
10610 obs->RemoveObserver(this, kUserInteractionActive);
10613 BackgroundHangMonitor::UnregisterAnnotator(*this);
10617 * NB: This function is always called by the BackgroundHangMonitor thread.
10618 * Plan accordingly
10620 void nsContentUtils::UserInteractionObserver::AnnotateHang(
10621 BackgroundHangAnnotations& aAnnotations) {
10622 // NOTE: Only annotate the hang report if the user is known to be interacting.
10623 if (sUserActive) {
10624 aAnnotations.AddAnnotation(u"UserInteracting"_ns, true);
10628 NS_IMETHODIMP
10629 nsContentUtils::UserInteractionObserver::Observe(nsISupports* aSubject,
10630 const char* aTopic,
10631 const char16_t* aData) {
10632 if (!strcmp(aTopic, kUserInteractionInactive)) {
10633 if (sUserActive && XRE_IsParentProcess()) {
10634 glean::RecordPowerMetrics();
10636 sUserActive = false;
10637 } else if (!strcmp(aTopic, kUserInteractionActive)) {
10638 if (!sUserActive && XRE_IsParentProcess()) {
10639 glean::RecordPowerMetrics();
10641 nsCOMPtr<nsIUserIdleServiceInternal> idleService =
10642 do_GetService("@mozilla.org/widget/useridleservice;1");
10643 if (idleService) {
10644 idleService->ResetIdleTimeOut(0);
10648 sUserActive = true;
10649 } else {
10650 NS_WARNING("Unexpected observer notification");
10652 return NS_OK;
10655 Atomic<bool> nsContentUtils::UserInteractionObserver::sUserActive(false);
10656 NS_IMPL_ISUPPORTS(nsContentUtils::UserInteractionObserver, nsIObserver)
10658 /* static */
10659 bool nsContentUtils::IsSpecialName(const nsAString& aName) {
10660 return aName.LowerCaseEqualsLiteral("_blank") ||
10661 aName.LowerCaseEqualsLiteral("_top") ||
10662 aName.LowerCaseEqualsLiteral("_parent") ||
10663 aName.LowerCaseEqualsLiteral("_self");
10666 /* static */
10667 bool nsContentUtils::IsOverridingWindowName(const nsAString& aName) {
10668 return !aName.IsEmpty() && !IsSpecialName(aName);
10671 // Unfortunately, we can't unwrap an IDL object using only a concrete type.
10672 // We need to calculate type data based on the IDL typename. Which means
10673 // wrapping our templated function in a macro.
10674 #define EXTRACT_EXN_VALUES(T, ...) \
10675 ExtractExceptionValues<mozilla::dom::prototypes::id::T, \
10676 T##_Binding::NativeType, T>(__VA_ARGS__) \
10677 .isOk()
10679 template <prototypes::ID PrototypeID, class NativeType, typename T>
10680 static Result<Ok, nsresult> ExtractExceptionValues(
10681 JSContext* aCx, JS::Handle<JSObject*> aObj, nsAString& aSourceSpecOut,
10682 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10683 AssertStaticUnwrapOK<PrototypeID>();
10684 RefPtr<T> exn;
10685 MOZ_TRY((UnwrapObject<PrototypeID, NativeType>(aObj, exn, nullptr)));
10687 exn->GetFilename(aCx, aSourceSpecOut);
10688 if (!aSourceSpecOut.IsEmpty()) {
10689 *aLineOut = exn->LineNumber(aCx);
10690 *aColumnOut = exn->ColumnNumber();
10693 exn->GetName(aMessageOut);
10694 aMessageOut.AppendLiteral(": ");
10696 nsAutoString message;
10697 exn->GetMessageMoz(message);
10698 aMessageOut.Append(message);
10699 return Ok();
10702 /* static */
10703 void nsContentUtils::ExtractErrorValues(
10704 JSContext* aCx, JS::Handle<JS::Value> aValue, nsACString& aSourceSpecOut,
10705 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10706 nsAutoString sourceSpec;
10707 ExtractErrorValues(aCx, aValue, sourceSpec, aLineOut, aColumnOut,
10708 aMessageOut);
10709 CopyUTF16toUTF8(sourceSpec, aSourceSpecOut);
10712 /* static */
10713 void nsContentUtils::ExtractErrorValues(
10714 JSContext* aCx, JS::Handle<JS::Value> aValue, nsAString& aSourceSpecOut,
10715 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10716 MOZ_ASSERT(aLineOut);
10717 MOZ_ASSERT(aColumnOut);
10719 if (aValue.isObject()) {
10720 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
10722 // Try to process as an Error object. Use the file/line/column values
10723 // from the Error as they will be more specific to the root cause of
10724 // the problem.
10725 JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
10726 if (err) {
10727 // Use xpc to extract the error message only. We don't actually send
10728 // this report anywhere.
10729 RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
10730 report->Init(err,
10731 nullptr, // toString result
10732 false, // chrome
10733 0); // window ID
10735 if (!report->mFileName.IsEmpty()) {
10736 aSourceSpecOut = report->mFileName;
10737 *aLineOut = report->mLineNumber;
10738 *aColumnOut = report->mColumn;
10740 aMessageOut.Assign(report->mErrorMsg);
10743 // Next, try to unwrap the rejection value as a DOMException.
10744 else if (EXTRACT_EXN_VALUES(DOMException, aCx, obj, aSourceSpecOut,
10745 aLineOut, aColumnOut, aMessageOut)) {
10746 return;
10749 // Next, try to unwrap the rejection value as an XPC Exception.
10750 else if (EXTRACT_EXN_VALUES(Exception, aCx, obj, aSourceSpecOut, aLineOut,
10751 aColumnOut, aMessageOut)) {
10752 return;
10756 // If we could not unwrap a specific error type, then perform default safe
10757 // string conversions on primitives. Objects will result in "[Object]"
10758 // unfortunately.
10759 if (aMessageOut.IsEmpty()) {
10760 nsAutoJSString jsString;
10761 if (jsString.init(aCx, aValue)) {
10762 aMessageOut = jsString;
10763 } else {
10764 JS_ClearPendingException(aCx);
10769 #undef EXTRACT_EXN_VALUES
10771 /* static */
10772 bool nsContentUtils::ContentIsLink(nsIContent* aContent) {
10773 if (!aContent || !aContent->IsElement()) {
10774 return false;
10777 if (aContent->IsHTMLElement(nsGkAtoms::a)) {
10778 return true;
10781 return aContent->AsElement()->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
10782 nsGkAtoms::simple, eCaseMatters);
10785 /* static */
10786 already_AddRefed<ContentFrameMessageManager>
10787 nsContentUtils::TryGetBrowserChildGlobal(nsISupports* aFrom) {
10788 RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(aFrom);
10789 if (!frameLoaderOwner) {
10790 return nullptr;
10793 RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
10794 if (!frameLoader) {
10795 return nullptr;
10798 RefPtr<ContentFrameMessageManager> manager =
10799 frameLoader->GetBrowserChildMessageManager();
10800 return manager.forget();
10803 /* static */
10804 uint32_t nsContentUtils::InnerOrOuterWindowCreated() {
10805 MOZ_ASSERT(NS_IsMainThread());
10806 ++sInnerOrOuterWindowCount;
10807 return ++sInnerOrOuterWindowSerialCounter;
10810 /* static */
10811 void nsContentUtils::InnerOrOuterWindowDestroyed() {
10812 MOZ_ASSERT(NS_IsMainThread());
10813 MOZ_ASSERT(sInnerOrOuterWindowCount > 0);
10814 --sInnerOrOuterWindowCount;
10817 /* static */
10818 nsresult nsContentUtils::AnonymizeURI(nsIURI* aURI, nsCString& aAnonymizedURI) {
10819 MOZ_ASSERT(aURI);
10821 if (aURI->SchemeIs("data")) {
10822 aAnonymizedURI.Assign("data:..."_ns);
10823 return NS_OK;
10825 // Anonymize the URL.
10826 // Strip the URL of any possible username/password and make it ready to be
10827 // presented in the UI.
10828 nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(aURI);
10829 return exposableURI->GetSpec(aAnonymizedURI);
10832 static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
10833 nsAString* result = static_cast<nsAString*>(aData);
10834 result->Append(aBuf, aLen);
10835 return true;
10838 /* static */
10839 bool nsContentUtils::StringifyJSON(JSContext* aCx, JS::Handle<JS::Value> aValue,
10840 nsAString& aOutStr, JSONBehavior aBehavior) {
10841 MOZ_ASSERT(aCx);
10842 switch (aBehavior) {
10843 case UndefinedIsNullStringLiteral: {
10844 aOutStr.Truncate();
10845 JS::Rooted<JS::Value> value(aCx, aValue);
10846 nsAutoString serializedValue;
10847 NS_ENSURE_TRUE(JS_Stringify(aCx, &value, nullptr, JS::NullHandleValue,
10848 JSONCreator, &serializedValue),
10849 false);
10850 aOutStr = serializedValue;
10851 return true;
10853 case UndefinedIsVoidString: {
10854 aOutStr.SetIsVoid(true);
10855 return JS::ToJSON(aCx, aValue, nullptr, JS::NullHandleValue, JSONCreator,
10856 &aOutStr);
10858 default:
10859 MOZ_ASSERT_UNREACHABLE("Invalid value for aBehavior");
10860 return false;
10864 /* static */
10865 bool nsContentUtils::
10866 HighPriorityEventPendingForTopLevelDocumentBeforeContentfulPaint(
10867 Document* aDocument) {
10868 MOZ_ASSERT(XRE_IsContentProcess(),
10869 "This function only makes sense in content processes");
10871 if (aDocument && !aDocument->IsLoadedAsData()) {
10872 if (nsPresContext* presContext = FindPresContextForDocument(aDocument)) {
10873 MOZ_ASSERT(!presContext->IsChrome(),
10874 "Should never have a chrome PresContext in a content process");
10876 return !presContext->GetInProcessRootContentDocumentPresContext()
10877 ->HadFirstContentfulPaint() &&
10878 nsThreadManager::MainThreadHasPendingHighPriorityEvents();
10881 return false;
10884 static nsGlobalWindowInner* GetInnerWindowForGlobal(nsIGlobalObject* aGlobal) {
10885 NS_ENSURE_TRUE(aGlobal, nullptr);
10887 if (auto* window = aGlobal->GetAsInnerWindow()) {
10888 return nsGlobalWindowInner::Cast(window);
10891 // When Extensions run content scripts inside a sandbox, it uses
10892 // sandboxPrototype to make them appear as though they're running in the
10893 // scope of the page. So when a content script invokes postMessage, it expects
10894 // the |source| of the received message to be the window set as the
10895 // sandboxPrototype. This used to work incidentally for unrelated reasons, but
10896 // now we need to do some special handling to support it.
10897 JS::Rooted<JSObject*> scope(RootingCx(), aGlobal->GetGlobalJSObject());
10898 NS_ENSURE_TRUE(scope, nullptr);
10900 if (xpc::IsSandbox(scope)) {
10901 AutoJSAPI jsapi;
10902 MOZ_ALWAYS_TRUE(jsapi.Init(scope));
10903 JSContext* cx = jsapi.cx();
10904 // Our current Realm on aCx is the sandbox. Using that for unwrapping
10905 // makes sense: if the sandbox can unwrap the window, we can use it.
10906 return xpc::SandboxWindowOrNull(scope, cx);
10909 // The calling window must be holding a reference, so we can return a weak
10910 // pointer.
10911 return nsGlobalWindowInner::Cast(aGlobal->GetAsInnerWindow());
10914 /* static */
10915 nsGlobalWindowInner* nsContentUtils::IncumbentInnerWindow() {
10916 return GetInnerWindowForGlobal(GetIncumbentGlobal());
10919 /* static */
10920 nsGlobalWindowInner* nsContentUtils::EntryInnerWindow() {
10921 return GetInnerWindowForGlobal(GetEntryGlobal());
10924 /* static */
10925 bool nsContentUtils::IsURIInPrefList(nsIURI* aURI, const char* aPrefName) {
10926 MOZ_ASSERT(aPrefName);
10928 nsAutoCString list;
10929 Preferences::GetCString(aPrefName, list);
10930 ToLowerCase(list);
10931 return IsURIInList(aURI, list);
10934 /* static */
10935 bool nsContentUtils::IsURIInList(nsIURI* aURI, const nsCString& aList) {
10936 #ifdef DEBUG
10937 nsAutoCString listLowerCase(aList);
10938 ToLowerCase(listLowerCase);
10939 MOZ_ASSERT(listLowerCase.Equals(aList),
10940 "The aList argument should be lower-case");
10941 #endif
10943 if (!aURI) {
10944 return false;
10947 nsAutoCString scheme;
10948 aURI->GetScheme(scheme);
10949 if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
10950 return false;
10953 if (aList.IsEmpty()) {
10954 return false;
10957 // The list is comma separated domain list. Each item may start with "*.".
10958 // If starts with "*.", it matches any sub-domains.
10960 nsCCharSeparatedTokenizer tokenizer(aList, ',');
10961 while (tokenizer.hasMoreTokens()) {
10962 const nsCString token(tokenizer.nextToken());
10964 nsAutoCString host;
10965 aURI->GetHost(host);
10966 if (host.IsEmpty()) {
10967 return false;
10969 ToLowerCase(host);
10971 for (;;) {
10972 int32_t index = token.Find(host);
10973 if (index >= 0 &&
10974 static_cast<uint32_t>(index) + host.Length() <= token.Length()) {
10975 // If we found a full match, return true.
10976 size_t indexAfterHost = index + host.Length();
10977 if (index == 0 && indexAfterHost == token.Length()) {
10978 return true;
10980 // If next character is '/', we need to check the path too.
10981 // We assume the path in the list means "/foo" + "*".
10982 if (token[indexAfterHost] == '/') {
10983 nsDependentCSubstring pathInList(
10984 token, indexAfterHost,
10985 static_cast<nsDependentCSubstring::size_type>(-1));
10986 nsAutoCString filePath;
10987 aURI->GetFilePath(filePath);
10988 ToLowerCase(filePath);
10989 if (StringBeginsWith(filePath, pathInList) &&
10990 (filePath.Length() == pathInList.Length() ||
10991 pathInList.EqualsLiteral("/") ||
10992 filePath[pathInList.Length() - 1] == '/' ||
10993 filePath[pathInList.Length() - 1] == '?' ||
10994 filePath[pathInList.Length() - 1] == '#')) {
10995 return true;
10999 int32_t startIndexOfCurrentLevel = host[0] == '*' ? 1 : 0;
11000 int32_t startIndexOfNextLevel =
11001 host.Find(".", startIndexOfCurrentLevel + 1);
11002 if (startIndexOfNextLevel <= 0) {
11003 break;
11005 host = "*"_ns + nsDependentCSubstring(host, startIndexOfNextLevel);
11009 return false;
11012 /* static */
11013 ScreenIntMargin nsContentUtils::GetWindowSafeAreaInsets(
11014 nsIScreen* aScreen, const ScreenIntMargin& aSafeAreaInsets,
11015 const LayoutDeviceIntRect& aWindowRect) {
11016 // This calculates safe area insets of window from screen rectangle, window
11017 // rectangle and safe area insets of screen.
11019 // +----------------------------------------+ <-- screen
11020 // | +-------------------------------+ <------- window
11021 // | | window's safe area inset top) | |
11022 // +--+-------------------------------+--+ |
11023 // | | | |<------ safe area rectangle of
11024 // | | | | | screen
11025 // +--+-------------------------------+--+ |
11026 // | |window's safe area inset bottom| |
11027 // | +-------------------------------+ |
11028 // +----------------------------------------+
11030 ScreenIntMargin windowSafeAreaInsets;
11032 if (windowSafeAreaInsets == aSafeAreaInsets) {
11033 // no safe area insets.
11034 return windowSafeAreaInsets;
11037 int32_t screenLeft, screenTop, screenWidth, screenHeight;
11038 nsresult rv =
11039 aScreen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
11040 if (NS_WARN_IF(NS_FAILED(rv))) {
11041 return windowSafeAreaInsets;
11044 const ScreenIntRect screenRect(screenLeft, screenTop, screenWidth,
11045 screenHeight);
11047 ScreenIntRect safeAreaRect = screenRect;
11048 safeAreaRect.Deflate(aSafeAreaInsets);
11050 ScreenIntRect windowRect = ViewAs<ScreenPixel>(
11051 aWindowRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
11053 // FIXME(bug 1754323): This can trigger because the screen rect is not
11054 // orientation-aware.
11055 // MOZ_ASSERT(screenRect.Contains(windowRect),
11056 // "Screen doesn't contain window rect? Something seems off");
11058 // window's rect of safe area
11059 safeAreaRect = safeAreaRect.Intersect(windowRect);
11061 windowSafeAreaInsets.top = safeAreaRect.y - aWindowRect.y;
11062 windowSafeAreaInsets.left = safeAreaRect.x - aWindowRect.x;
11063 windowSafeAreaInsets.right =
11064 aWindowRect.x + aWindowRect.width - (safeAreaRect.x + safeAreaRect.width);
11065 windowSafeAreaInsets.bottom = aWindowRect.y + aWindowRect.height -
11066 (safeAreaRect.y + safeAreaRect.height);
11068 windowSafeAreaInsets.EnsureAtLeast(ScreenIntMargin());
11069 // This shouldn't be needed, but it wallpapers orientation issues, see bug
11070 // 1754323.
11071 windowSafeAreaInsets.EnsureAtMost(aSafeAreaInsets);
11073 return windowSafeAreaInsets;
11076 /* static */
11077 nsContentUtils::SubresourceCacheValidationInfo
11078 nsContentUtils::GetSubresourceCacheValidationInfo(nsIRequest* aRequest,
11079 nsIURI* aURI) {
11080 SubresourceCacheValidationInfo info;
11081 if (nsCOMPtr<nsICacheInfoChannel> cache = do_QueryInterface(aRequest)) {
11082 uint32_t value = 0;
11083 if (NS_SUCCEEDED(cache->GetCacheTokenExpirationTime(&value))) {
11084 info.mExpirationTime.emplace(value);
11088 // Determine whether the cache entry must be revalidated when we try to use
11089 // it. Currently, only HTTP specifies this information...
11090 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest)) {
11091 Unused << httpChannel->IsNoStoreResponse(&info.mMustRevalidate);
11093 if (!info.mMustRevalidate) {
11094 Unused << httpChannel->IsNoCacheResponse(&info.mMustRevalidate);
11098 // data: URIs are safe to cache across documents under any circumstance, so we
11099 // special-case them here even though the channel itself doesn't have any
11100 // caching policy. Same for chrome:// uris.
11102 // TODO(emilio): Figure out which other schemes that don't have caching
11103 // policies are safe to cache. Blobs should be...
11104 const bool knownCacheable = [&] {
11105 if (!aURI) {
11106 return false;
11108 if (aURI->SchemeIs("data") || aURI->SchemeIs("moz-page-thumb") ||
11109 aURI->SchemeIs("moz-extension")) {
11110 return true;
11112 if (aURI->SchemeIs("chrome") || aURI->SchemeIs("resource")) {
11113 return !StaticPrefs::nglayout_debug_disable_xul_cache();
11115 return false;
11116 }();
11118 if (knownCacheable) {
11119 MOZ_ASSERT(!info.mExpirationTime);
11120 MOZ_ASSERT(!info.mMustRevalidate);
11121 info.mExpirationTime = Some(0); // 0 means "doesn't expire".
11124 return info;
11127 nsCString nsContentUtils::TruncatedURLForDisplay(nsIURI* aURL, size_t aMaxLen) {
11128 nsCString spec;
11129 if (aURL) {
11130 aURL->GetSpec(spec);
11131 spec.Truncate(std::min(aMaxLen, spec.Length()));
11133 return spec;
11136 /* static */
11137 nsresult nsContentUtils::AnonymizeId(nsAString& aId,
11138 const nsACString& aOriginKey,
11139 OriginFormat aFormat) {
11140 MOZ_ASSERT(NS_IsMainThread());
11142 nsresult rv;
11143 nsCString rawKey;
11144 if (aFormat == OriginFormat::Base64) {
11145 rv = Base64Decode(aOriginKey, rawKey);
11146 NS_ENSURE_SUCCESS(rv, rv);
11147 } else {
11148 rawKey = aOriginKey;
11151 HMAC hmac;
11152 rv = hmac.Begin(
11153 SEC_OID_SHA256,
11154 Span(reinterpret_cast<const uint8_t*>(rawKey.get()), rawKey.Length()));
11155 NS_ENSURE_SUCCESS(rv, rv);
11157 NS_ConvertUTF16toUTF8 id(aId);
11158 rv = hmac.Update(reinterpret_cast<const uint8_t*>(id.get()), id.Length());
11159 NS_ENSURE_SUCCESS(rv, rv);
11161 nsTArray<uint8_t> macBytes;
11162 rv = hmac.End(macBytes);
11163 NS_ENSURE_SUCCESS(rv, rv);
11165 nsCString macBase64;
11166 rv = Base64Encode(
11167 nsDependentCSubstring(reinterpret_cast<const char*>(macBytes.Elements()),
11168 macBytes.Length()),
11169 macBase64);
11170 NS_ENSURE_SUCCESS(rv, rv);
11172 CopyUTF8toUTF16(macBase64, aId);
11173 return NS_OK;
11176 /* static */
11177 bool nsContentUtils::ShouldHideObjectOrEmbedImageDocument() {
11178 return StaticPrefs::
11179 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup() &&
11180 StaticPrefs::
11181 browser_opaqueResponseBlocking_syntheticBrowsingContext_filter_AtStartup_DoNotUseDirectly();
11184 /* static */
11185 uint32_t nsContentUtils::ResolveObjectType(uint32_t aType) {
11186 if (!StaticPrefs::
11187 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup()) {
11188 return aType;
11191 if (aType != nsIObjectLoadingContent::TYPE_IMAGE) {
11192 return aType;
11195 return nsIObjectLoadingContent::TYPE_DOCUMENT;
11198 void nsContentUtils::RequestGeckoTaskBurst() {
11199 nsCOMPtr<nsIAppShell> appShell = do_GetService(NS_APPSHELL_CID);
11200 if (appShell) {
11201 appShell->GeckoTaskBurst();
11205 nsIContent* nsContentUtils::GetClosestLinkInFlatTree(nsIContent* aContent) {
11206 for (nsIContent* content = aContent; content;
11207 content = content->GetFlattenedTreeParent()) {
11208 if (nsContentUtils::IsDraggableLink(content)) {
11209 return content;
11212 return nullptr;
11215 namespace {
11217 struct TreePositionComparator {
11218 Element* const mChild;
11219 nsIContent* const mAncestor;
11220 TreePositionComparator(Element* aChild, nsIContent* aAncestor)
11221 : mChild(aChild), mAncestor(aAncestor) {}
11222 int operator()(Element* aElement) const {
11223 return nsLayoutUtils::CompareTreePosition(mChild, aElement, mAncestor);
11227 } // namespace
11229 /* static */
11230 int32_t nsContentUtils::CompareTreePosition(nsIContent* aContent1,
11231 nsIContent* aContent2,
11232 const nsIContent* aCommonAncestor) {
11233 NS_ASSERTION(aContent1 != aContent2, "Comparing content to itself");
11235 // TODO: remove the prevent asserts fix, see bug 598468.
11236 #ifdef DEBUG
11237 nsLayoutUtils::gPreventAssertInCompareTreePosition = true;
11238 int32_t rVal =
11239 nsLayoutUtils::CompareTreePosition(aContent1, aContent2, aCommonAncestor);
11240 nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
11242 return rVal;
11243 #else // DEBUG
11244 return nsLayoutUtils::CompareTreePosition(aContent1, aContent2,
11245 aCommonAncestor);
11246 #endif // DEBUG
11249 /* static */
11250 template <typename ElementType, typename ElementPtr>
11251 bool nsContentUtils::AddElementToListByTreeOrder(nsTArray<ElementType>& aList,
11252 ElementPtr aChild,
11253 nsIContent* aCommonAncestor) {
11254 NS_ASSERTION(aList.IndexOf(aChild) == aList.NoIndex,
11255 "aChild already in aList");
11257 const uint32_t count = aList.Length();
11258 ElementType element;
11260 // Optimize most common case where we insert at the end.
11261 int32_t position = -1;
11262 if (count > 0) {
11263 element = aList[count - 1];
11264 position = CompareTreePosition(aChild, element, aCommonAncestor);
11267 // If this item comes after the last element, or the elements array is
11268 // empty, we append to the end. Otherwise, we do a binary search to
11269 // determine where the element should go.
11270 if (position >= 0 || count == 0) {
11271 aList.AppendElement(aChild);
11272 return true;
11275 size_t idx;
11276 BinarySearchIf(aList, 0, count,
11277 TreePositionComparator(aChild, aCommonAncestor), &idx);
11279 aList.InsertElementAt(idx, aChild);
11280 return false;
11283 template bool nsContentUtils::AddElementToListByTreeOrder(
11284 nsTArray<nsGenericHTMLFormElement*>& aList,
11285 nsGenericHTMLFormElement* aChild, nsIContent* aAncestor);
11286 template bool nsContentUtils::AddElementToListByTreeOrder(
11287 nsTArray<HTMLImageElement*>& aList, HTMLImageElement* aChild,
11288 nsIContent* aAncestor);
11289 template bool nsContentUtils::AddElementToListByTreeOrder(
11290 nsTArray<RefPtr<HTMLInputElement>>& aList, HTMLInputElement* aChild,
11291 nsIContent* aAncestor);
11293 namespace mozilla {
11294 std::ostream& operator<<(std::ostream& aOut,
11295 const PreventDefaultResult aPreventDefaultResult) {
11296 switch (aPreventDefaultResult) {
11297 case PreventDefaultResult::No:
11298 aOut << "unhandled";
11299 break;
11300 case PreventDefaultResult::ByContent:
11301 aOut << "handled-by-content";
11302 break;
11303 case PreventDefaultResult::ByChrome:
11304 aOut << "handled-by-chrome";
11305 break;
11307 return aOut;
11309 } // namespace mozilla