Bug 1835710 - Cancel off-thread JIT compilation before changing nursery allocation...
[gecko.git] / dom / base / nsContentUtils.cpp
blobde4209eaa7ac7b7a8a8eb8a651779e949675a806
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/CycleCollectedJSContext.h"
78 #include "mozilla/DOMEventTargetHelper.h"
79 #include "mozilla/DebugOnly.h"
80 #include "mozilla/ErrorResult.h"
81 #include "mozilla/EventDispatcher.h"
82 #include "mozilla/EventListenerManager.h"
83 #include "mozilla/EventQueue.h"
84 #include "mozilla/EventStateManager.h"
85 #include "mozilla/FlushType.h"
86 #include "mozilla/FOGIPC.h"
87 #include "mozilla/HTMLEditor.h"
88 #include "mozilla/HangAnnotations.h"
89 #include "mozilla/IMEStateManager.h"
90 #include "mozilla/InputEventOptions.h"
91 #include "mozilla/InternalMutationEvent.h"
92 #include "mozilla/Latin1.h"
93 #include "mozilla/Likely.h"
94 #include "mozilla/LoadInfo.h"
95 #include "mozilla/Logging.h"
96 #include "mozilla/MacroForEach.h"
97 #include "mozilla/ManualNAC.h"
98 #include "mozilla/Maybe.h"
99 #include "mozilla/MediaFeatureChange.h"
100 #include "mozilla/MouseEvents.h"
101 #include "mozilla/NotNull.h"
102 #include "mozilla/NullPrincipal.h"
103 #include "mozilla/OriginAttributes.h"
104 #include "mozilla/Preferences.h"
105 #include "mozilla/PresShell.h"
106 #include "mozilla/ProfilerRunnable.h"
107 #include "mozilla/RangeBoundary.h"
108 #include "mozilla/RefPtr.h"
109 #include "mozilla/Result.h"
110 #include "mozilla/ResultExtensions.h"
111 #include "mozilla/ScrollbarPreferences.h"
112 #include "mozilla/Span.h"
113 #include "mozilla/StaticAnalysisFunctions.h"
114 #include "mozilla/StaticPrefs_browser.h"
115 #include "mozilla/StaticPrefs_dom.h"
116 #ifdef FUZZING
117 # include "mozilla/StaticPrefs_fuzzing.h"
118 #endif
119 #include "mozilla/StaticPrefs_nglayout.h"
120 #include "mozilla/StaticPrefs_privacy.h"
121 #include "mozilla/StaticPrefs_test.h"
122 #include "mozilla/StaticPrefs_ui.h"
123 #include "mozilla/StaticPtr.h"
124 #include "mozilla/TaskCategory.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/DOMArena.h"
154 #include "mozilla/dom/DOMException.h"
155 #include "mozilla/dom/DOMExceptionBinding.h"
156 #include "mozilla/dom/DOMSecurityMonitor.h"
157 #include "mozilla/dom/DOMTypes.h"
158 #include "mozilla/dom/DataTransfer.h"
159 #include "mozilla/dom/DocGroup.h"
160 #include "mozilla/dom/Document.h"
161 #include "mozilla/dom/DocumentFragment.h"
162 #include "mozilla/dom/DocumentInlines.h"
163 #include "mozilla/dom/Element.h"
164 #include "mozilla/dom/ElementBinding.h"
165 #include "mozilla/dom/ElementInlines.h"
166 #include "mozilla/dom/Event.h"
167 #include "mozilla/dom/EventTarget.h"
168 #include "mozilla/dom/FileBlobImpl.h"
169 #include "mozilla/dom/FileSystemSecurity.h"
170 #include "mozilla/dom/FilteredNodeIterator.h"
171 #include "mozilla/dom/FragmentOrElement.h"
172 #include "mozilla/dom/FromParser.h"
173 #include "mozilla/dom/HTMLFormElement.h"
174 #include "mozilla/dom/HTMLInputElement.h"
175 #include "mozilla/dom/HTMLTextAreaElement.h"
176 #include "mozilla/dom/IPCBlob.h"
177 #include "mozilla/dom/IPCBlobUtils.h"
178 #include "mozilla/dom/MessageBroadcaster.h"
179 #include "mozilla/dom/MessageListenerManager.h"
180 #include "mozilla/dom/MessagePort.h"
181 #include "mozilla/dom/MouseEventBinding.h"
182 #include "mozilla/dom/NameSpaceConstants.h"
183 #include "mozilla/dom/NodeBinding.h"
184 #include "mozilla/dom/NodeInfo.h"
185 #include "mozilla/dom/PBrowser.h"
186 #include "mozilla/dom/PContentChild.h"
187 #include "mozilla/dom/PrototypeList.h"
188 #include "mozilla/dom/ReferrerPolicyBinding.h"
189 #include "mozilla/dom/ScriptSettings.h"
190 #include "mozilla/dom/Selection.h"
191 #include "mozilla/dom/ShadowRoot.h"
192 #include "mozilla/dom/Text.h"
193 #include "mozilla/dom/UserActivation.h"
194 #include "mozilla/dom/WindowContext.h"
195 #include "mozilla/dom/WorkerCommon.h"
196 #include "mozilla/dom/WorkerPrivate.h"
197 #include "mozilla/dom/WorkerRunnable.h"
198 #include "mozilla/dom/XULCommandEvent.h"
199 #include "mozilla/fallible.h"
200 #include "mozilla/gfx/2D.h"
201 #include "mozilla/gfx/BaseMargin.h"
202 #include "mozilla/gfx/BasePoint.h"
203 #include "mozilla/gfx/BaseSize.h"
204 #include "mozilla/gfx/DataSurfaceHelpers.h"
205 #include "mozilla/gfx/Point.h"
206 #include "mozilla/gfx/Rect.h"
207 #include "mozilla/gfx/Types.h"
208 #include "mozilla/ipc/ProtocolUtils.h"
209 #include "mozilla/ipc/SharedMemory.h"
210 #include "mozilla/net/UrlClassifierCommon.h"
211 #include "mozilla/widget/IMEData.h"
212 #include "nsAboutProtocolUtils.h"
213 #include "nsAlgorithm.h"
214 #include "nsArrayUtils.h"
215 #include "nsAtom.h"
216 #include "nsAttrName.h"
217 #include "nsAttrValue.h"
218 #include "nsAttrValueInlines.h"
219 #include "nsBaseHashtable.h"
220 #include "nsCCUncollectableMarker.h"
221 #include "nsCOMPtr.h"
222 #include "nsCRT.h"
223 #include "nsCRTGlue.h"
224 #include "nsCanvasFrame.h"
225 #include "nsCaseTreatment.h"
226 #include "nsCharSeparatedTokenizer.h"
227 #include "nsCharTraits.h"
228 #include "nsCompatibility.h"
229 #include "nsComponentManagerUtils.h"
230 #include "nsContainerFrame.h"
231 #include "nsContentCreatorFunctions.h"
232 #include "nsContentDLF.h"
233 #include "nsContentList.h"
234 #include "nsContentListDeclarations.h"
235 #include "nsContentPolicyUtils.h"
236 #include "nsCoord.h"
237 #include "nsCycleCollectionNoteChild.h"
238 #include "nsDOMMutationObserver.h"
239 #include "nsDOMString.h"
240 #include "nsTHashMap.h"
241 #include "nsDebug.h"
242 #include "nsDocShell.h"
243 #include "nsDocShellCID.h"
244 #include "nsError.h"
245 #include "nsFocusManager.h"
246 #include "nsFrameList.h"
247 #include "nsFrameLoader.h"
248 #include "nsFrameLoaderOwner.h"
249 #include "nsGenericHTMLElement.h"
250 #include "nsGkAtoms.h"
251 #include "nsGlobalWindowInner.h"
252 #include "nsGlobalWindowOuter.h"
253 #include "nsHTMLDocument.h"
254 #include "nsHTMLTags.h"
255 #include "nsHashKeys.h"
256 #include "nsHtml5StringParser.h"
257 #include "nsIAboutModule.h"
258 #include "nsIAnonymousContentCreator.h"
259 #include "nsIAppShell.h"
260 #include "nsIArray.h"
261 #include "nsIAsyncVerifyRedirectCallback.h"
262 #include "nsIBidiKeyboard.h"
263 #include "nsIBrowser.h"
264 #include "nsICacheInfoChannel.h"
265 #include "nsICategoryManager.h"
266 #include "nsIChannel.h"
267 #include "nsIChannelEventSink.h"
268 #include "nsIClassifiedChannel.h"
269 #include "nsIConsoleService.h"
270 #include "nsIContent.h"
271 #include "nsIContentInlines.h"
272 #include "nsIContentPolicy.h"
273 #include "nsIContentSecurityPolicy.h"
274 #include "nsIContentSink.h"
275 #include "nsIContentViewer.h"
276 #include "nsIDOMWindowUtils.h"
277 #include "nsIDocShell.h"
278 #include "nsIDocShellTreeItem.h"
279 #include "nsIDocumentEncoder.h"
280 #include "nsIDocumentLoaderFactory.h"
281 #include "nsIDragService.h"
282 #include "nsIDragSession.h"
283 #include "nsIFile.h"
284 #include "nsIFocusManager.h"
285 #include "nsIFormControl.h"
286 #include "nsIFragmentContentSink.h"
287 #include "nsIFrame.h"
288 #include "nsIGlobalObject.h"
289 #include "nsIHttpChannel.h"
290 #include "nsIHttpChannelInternal.h"
291 #include "nsIIOService.h"
292 #include "nsIImageLoadingContent.h"
293 #include "nsIInputStream.h"
294 #include "nsIInterfaceRequestor.h"
295 #include "nsIInterfaceRequestorUtils.h"
296 #include "nsILoadContext.h"
297 #include "nsILoadGroup.h"
298 #include "nsILoadInfo.h"
299 #include "nsIMIMEService.h"
300 #include "nsIMemoryReporter.h"
301 #include "nsINetUtil.h"
302 #include "nsINode.h"
303 #include "nsIObjectLoadingContent.h"
304 #include "nsIObserver.h"
305 #include "nsIObserverService.h"
306 #include "nsIParserUtils.h"
307 #include "nsIPermissionManager.h"
308 #include "nsIPluginTag.h"
309 #include "nsIPrincipal.h"
310 #include "nsIProperties.h"
311 #include "nsIProtocolHandler.h"
312 #include "nsIRequest.h"
313 #include "nsIRunnable.h"
314 #include "nsIScreen.h"
315 #include "nsIScriptError.h"
316 #include "nsIScriptGlobalObject.h"
317 #include "nsIScriptObjectPrincipal.h"
318 #include "nsIScriptSecurityManager.h"
319 #include "nsISerialEventTarget.h"
320 #include "nsIStreamConverter.h"
321 #include "nsIStreamConverterService.h"
322 #include "nsIStringBundle.h"
323 #include "nsISupports.h"
324 #include "nsISupportsPrimitives.h"
325 #include "nsISupportsUtils.h"
326 #include "nsITransferable.h"
327 #include "nsIURI.h"
328 #include "nsIURIMutator.h"
329 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
330 # include "nsIURIWithSpecialOrigin.h"
331 #endif
332 #include "nsIUserIdleServiceInternal.h"
333 #include "nsIWeakReferenceUtils.h"
334 #include "nsIWebNavigation.h"
335 #include "nsIWebNavigationInfo.h"
336 #include "nsIWidget.h"
337 #include "nsIWindowMediator.h"
338 #include "nsIXPConnect.h"
339 #include "nsJSPrincipals.h"
340 #include "nsJSUtils.h"
341 #include "nsLayoutUtils.h"
342 #include "nsLiteralString.h"
343 #include "nsMappedAttributes.h"
344 #include "nsMargin.h"
345 #include "nsMimeTypes.h"
346 #include "nsNameSpaceManager.h"
347 #include "nsNetCID.h"
348 #include "nsNetUtil.h"
349 #include "nsNodeInfoManager.h"
350 #include "nsPIDOMWindow.h"
351 #include "nsPIDOMWindowInlines.h"
352 #include "nsParser.h"
353 #include "nsParserConstants.h"
354 #include "nsPluginHost.h"
355 #include "nsPoint.h"
356 #include "nsPointerHashKeys.h"
357 #include "nsPresContext.h"
358 #include "nsQueryFrame.h"
359 #include "nsQueryObject.h"
360 #include "nsRange.h"
361 #include "nsRefPtrHashtable.h"
362 #include "nsSandboxFlags.h"
363 #include "nsScriptSecurityManager.h"
364 #include "nsServiceManagerUtils.h"
365 #include "nsStreamUtils.h"
366 #include "nsString.h"
367 #include "nsStringBuffer.h"
368 #include "nsStringBundle.h"
369 #include "nsStringFlags.h"
370 #include "nsStringFwd.h"
371 #include "nsStringIterator.h"
372 #include "nsStringStream.h"
373 #include "nsTArray.h"
374 #include "nsTLiteralString.h"
375 #include "nsTPromiseFlatString.h"
376 #include "nsTStringRepr.h"
377 #include "nsTextFragment.h"
378 #include "nsTextNode.h"
379 #include "nsThreadManager.h"
380 #include "nsThreadUtils.h"
381 #include "nsTreeSanitizer.h"
382 #include "nsUGenCategory.h"
383 #include "nsURLHelper.h"
384 #include "nsUnicodeProperties.h"
385 #include "nsVariant.h"
386 #include "nsWidgetsCID.h"
387 #include "nsView.h"
388 #include "nsViewManager.h"
389 #include "nsXPCOM.h"
390 #include "nsXPCOMCID.h"
391 #include "nsXULAppAPI.h"
392 #include "nsXULElement.h"
393 #include "nsXULPopupManager.h"
394 #include "nscore.h"
395 #include "prinrval.h"
396 #include "xpcprivate.h"
397 #include "xpcpublic.h"
399 #if defined(XP_WIN)
400 // Undefine LoadImage to prevent naming conflict with Windows.
401 # undef LoadImage
402 #endif
404 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
405 const char** next, char16_t* result);
406 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end, int ns_aware,
407 const char** colon);
409 using namespace mozilla::dom;
410 using namespace mozilla::ipc;
411 using namespace mozilla::gfx;
412 using namespace mozilla::layers;
413 using namespace mozilla::widget;
414 using namespace mozilla;
416 const char kLoadAsData[] = "loadAsData";
418 nsIXPConnect* nsContentUtils::sXPConnect;
419 nsIScriptSecurityManager* nsContentUtils::sSecurityManager;
420 nsIPrincipal* nsContentUtils::sSystemPrincipal;
421 nsIPrincipal* nsContentUtils::sNullSubjectPrincipal;
422 nsIIOService* nsContentUtils::sIOService;
423 nsIConsoleService* nsContentUtils::sConsoleService;
424 nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>*
425 nsContentUtils::sAtomEventTable = nullptr;
426 nsTHashMap<nsStringHashKey, EventNameMapping>*
427 nsContentUtils::sStringEventTable = nullptr;
428 nsTArray<RefPtr<nsAtom>>* nsContentUtils::sUserDefinedEvents = nullptr;
429 nsIStringBundleService* nsContentUtils::sStringBundleService;
431 static StaticRefPtr<nsIStringBundle>
432 sStringBundles[nsContentUtils::PropertiesFile_COUNT];
434 nsIContentPolicy* nsContentUtils::sContentPolicyService;
435 bool nsContentUtils::sTriedToGetContentPolicy = false;
436 StaticRefPtr<nsIBidiKeyboard> nsContentUtils::sBidiKeyboard;
437 uint32_t nsContentUtils::sScriptBlockerCount = 0;
438 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
439 AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners =
440 nullptr;
441 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
442 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
444 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
446 nsString* nsContentUtils::sShiftText = nullptr;
447 nsString* nsContentUtils::sControlText = nullptr;
448 nsString* nsContentUtils::sMetaText = nullptr;
449 nsString* nsContentUtils::sOSText = nullptr;
450 nsString* nsContentUtils::sAltText = nullptr;
451 nsString* nsContentUtils::sModifierSeparator = nullptr;
453 bool nsContentUtils::sInitialized = false;
454 #ifndef RELEASE_OR_BETA
455 bool nsContentUtils::sBypassCSSOMOriginCheck = false;
456 #endif
458 nsCString* nsContentUtils::sJSScriptBytecodeMimeType = nullptr;
459 nsCString* nsContentUtils::sJSModuleBytecodeMimeType = nullptr;
461 nsContentUtils::UserInteractionObserver*
462 nsContentUtils::sUserInteractionObserver = nullptr;
464 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
465 nsParser* nsContentUtils::sXMLFragmentParser = nullptr;
466 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
467 bool nsContentUtils::sFragmentParsingActive = false;
469 bool nsContentUtils::sMayHaveFormCheckboxStateChangeListeners = false;
470 bool nsContentUtils::sMayHaveFormRadioStateChangeListeners = false;
472 mozilla::LazyLogModule nsContentUtils::gResistFingerprintingLog(
473 "nsResistFingerprinting");
474 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
476 int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
477 uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
479 template Maybe<int32_t> nsContentUtils::ComparePoints(
480 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary);
481 template Maybe<int32_t> nsContentUtils::ComparePoints(
482 const RangeBoundary& aFirstBoundary,
483 const RawRangeBoundary& aSecondBoundary);
484 template Maybe<int32_t> nsContentUtils::ComparePoints(
485 const RawRangeBoundary& aFirstBoundary,
486 const RangeBoundary& aSecondBoundary);
487 template Maybe<int32_t> nsContentUtils::ComparePoints(
488 const RawRangeBoundary& aFirstBoundary,
489 const RawRangeBoundary& aSecondBoundary);
491 template int32_t nsContentUtils::ComparePoints_Deprecated(
492 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
493 bool* aDisconnected);
494 template int32_t nsContentUtils::ComparePoints_Deprecated(
495 const RangeBoundary& aFirstBoundary,
496 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
497 template int32_t nsContentUtils::ComparePoints_Deprecated(
498 const RawRangeBoundary& aFirstBoundary,
499 const RangeBoundary& aSecondBoundary, bool* aDisconnected);
500 template int32_t nsContentUtils::ComparePoints_Deprecated(
501 const RawRangeBoundary& aFirstBoundary,
502 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
504 // Subset of
505 // http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
506 enum AutocompleteUnsupportedFieldName : uint8_t {
507 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
508 eAutocompleteUnsupportedFieldName_##name_,
509 #include "AutocompleteFieldList.h"
510 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
513 enum AutocompleteNoPersistFieldName : uint8_t {
514 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
515 eAutocompleteNoPersistFieldName_##name_,
516 #include "AutocompleteFieldList.h"
517 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
520 enum AutocompleteUnsupportFieldContactHint : uint8_t {
521 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
522 eAutocompleteUnsupportedFieldContactHint_##name_,
523 #include "AutocompleteFieldList.h"
524 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
527 enum AutocompleteFieldName : uint8_t {
528 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) eAutocompleteFieldName_##name_,
529 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
530 AUTOCOMPLETE_FIELD_NAME(name_, value_)
531 #include "AutocompleteFieldList.h"
532 #undef AUTOCOMPLETE_FIELD_NAME
533 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
536 enum AutocompleteFieldHint : uint8_t {
537 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) eAutocompleteFieldHint_##name_,
538 #include "AutocompleteFieldList.h"
539 #undef AUTOCOMPLETE_FIELD_HINT
542 enum AutocompleteFieldContactHint : uint8_t {
543 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
544 eAutocompleteFieldContactHint_##name_,
545 #include "AutocompleteFieldList.h"
546 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
549 enum AutocompleteCategory {
550 #define AUTOCOMPLETE_CATEGORY(name_, value_) eAutocompleteCategory_##name_,
551 #include "AutocompleteFieldList.h"
552 #undef AUTOCOMPLETE_CATEGORY
555 static const nsAttrValue::EnumTable kAutocompleteUnsupportedFieldNameTable[] = {
556 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
557 {value_, eAutocompleteUnsupportedFieldName_##name_},
558 #include "AutocompleteFieldList.h"
559 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
560 {nullptr, 0}};
562 static const nsAttrValue::EnumTable kAutocompleteNoPersistFieldNameTable[] = {
563 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
564 {value_, eAutocompleteNoPersistFieldName_##name_},
565 #include "AutocompleteFieldList.h"
566 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
567 {nullptr, 0}};
569 static const nsAttrValue::EnumTable
570 kAutocompleteUnsupportedContactFieldHintTable[] = {
571 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
572 {value_, eAutocompleteUnsupportedFieldContactHint_##name_},
573 #include "AutocompleteFieldList.h"
574 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
575 {nullptr, 0}};
577 static const nsAttrValue::EnumTable kAutocompleteFieldNameTable[] = {
578 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
579 {value_, eAutocompleteFieldName_##name_},
580 #include "AutocompleteFieldList.h"
581 #undef AUTOCOMPLETE_FIELD_NAME
582 {nullptr, 0}};
584 static const nsAttrValue::EnumTable kAutocompleteContactFieldNameTable[] = {
585 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
586 {value_, eAutocompleteFieldName_##name_},
587 #include "AutocompleteFieldList.h"
588 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
589 {nullptr, 0}};
591 static const nsAttrValue::EnumTable kAutocompleteFieldHintTable[] = {
592 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
593 {value_, eAutocompleteFieldHint_##name_},
594 #include "AutocompleteFieldList.h"
595 #undef AUTOCOMPLETE_FIELD_HINT
596 {nullptr, 0}};
598 static const nsAttrValue::EnumTable kAutocompleteContactFieldHintTable[] = {
599 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
600 {value_, eAutocompleteFieldContactHint_##name_},
601 #include "AutocompleteFieldList.h"
602 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
603 {nullptr, 0}};
605 namespace {
607 static PLDHashTable* sEventListenerManagersHash;
609 // A global hashtable to for keeping the arena alive for cross docGroup node
610 // adoption.
611 static nsRefPtrHashtable<nsPtrHashKey<const nsINode>, mozilla::dom::DOMArena>*
612 sDOMArenaHashtable;
614 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter {
615 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
617 ~DOMEventListenerManagersHashReporter() = default;
619 public:
620 NS_DECL_ISUPPORTS
622 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
623 nsISupports* aData, bool aAnonymize) override {
624 // We don't measure the |EventListenerManager| objects pointed to by the
625 // entries because those references are non-owning.
626 int64_t amount =
627 sEventListenerManagersHash
628 ? sEventListenerManagersHash->ShallowSizeOfIncludingThis(
629 MallocSizeOf)
630 : 0;
632 MOZ_COLLECT_REPORT(
633 "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
634 amount, "Memory used by the event listener manager's hash table.");
636 return NS_OK;
640 NS_IMPL_ISUPPORTS(DOMEventListenerManagersHashReporter, nsIMemoryReporter)
642 class EventListenerManagerMapEntry : public PLDHashEntryHdr {
643 public:
644 explicit EventListenerManagerMapEntry(const void* aKey) : mKey(aKey) {}
646 ~EventListenerManagerMapEntry() {
647 NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
650 protected: // declared protected to silence clang warnings
651 const void* mKey; // must be first, to look like PLDHashEntryStub
653 public:
654 RefPtr<EventListenerManager> mListenerManager;
657 static void EventListenerManagerHashInitEntry(PLDHashEntryHdr* entry,
658 const void* key) {
659 // Initialize the entry with placement new
660 new (entry) EventListenerManagerMapEntry(key);
663 static void EventListenerManagerHashClearEntry(PLDHashTable* table,
664 PLDHashEntryHdr* entry) {
665 EventListenerManagerMapEntry* lm =
666 static_cast<EventListenerManagerMapEntry*>(entry);
668 // Let the EventListenerManagerMapEntry clean itself up...
669 lm->~EventListenerManagerMapEntry();
672 class SameOriginCheckerImpl final : public nsIChannelEventSink,
673 public nsIInterfaceRequestor {
674 ~SameOriginCheckerImpl() = default;
676 NS_DECL_ISUPPORTS
677 NS_DECL_NSICHANNELEVENTSINK
678 NS_DECL_NSIINTERFACEREQUESTOR
681 } // namespace
683 void AutoSuppressEventHandling::SuppressDocument(Document* aDoc) {
684 // Note: Document::SuppressEventHandling will also automatically suppress
685 // event handling for any in-process sub-documents. However, since we need
686 // to deal with cases where remote BrowsingContexts may be interleaved
687 // with in-process ones, we still need to walk the entire tree ourselves.
688 // This may be slightly redundant in some cases, but since event handling
689 // suppressions maintain a count of current blockers, it does not cause
690 // any problems.
691 aDoc->SuppressEventHandling();
694 void AutoSuppressEventHandling::UnsuppressDocument(Document* aDoc) {
695 aDoc->UnsuppressEventHandlingAndFireEvents(true);
698 AutoSuppressEventHandling::~AutoSuppressEventHandling() {
699 UnsuppressDocuments();
702 void AutoSuppressEventHandlingAndSuspend::SuppressDocument(Document* aDoc) {
703 AutoSuppressEventHandling::SuppressDocument(aDoc);
704 if (nsCOMPtr<nsPIDOMWindowInner> win = aDoc->GetInnerWindow()) {
705 win->Suspend();
706 mWindows.AppendElement(win);
710 AutoSuppressEventHandlingAndSuspend::~AutoSuppressEventHandlingAndSuspend() {
711 for (const auto& win : mWindows) {
712 win->Resume();
717 * This class is used to determine whether or not the user is currently
718 * interacting with the browser. It listens to observer events to toggle the
719 * value of the sUserActive static.
721 * This class is an internal implementation detail.
722 * nsContentUtils::GetUserIsInteracting() should be used to access current
723 * user interaction status.
725 class nsContentUtils::UserInteractionObserver final
726 : public nsIObserver,
727 public BackgroundHangAnnotator {
728 public:
729 NS_DECL_ISUPPORTS
730 NS_DECL_NSIOBSERVER
732 void Init();
733 void Shutdown();
734 void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
736 static Atomic<bool> sUserActive;
738 private:
739 ~UserInteractionObserver() = default;
742 static constexpr nsLiteralCString kRfpPrefs[] = {
743 "privacy.resistFingerprinting"_ns,
744 "privacy.resistFingerprinting.pbmode"_ns,
745 "privacy.fingerprintingProtection"_ns,
746 "privacy.fingerprintingProtection.pbmode"_ns,
747 "privacy.fingerprintingProtection.overrides"_ns,
750 static void RecomputeResistFingerprintingAllDocs(const char*, void*) {
751 AutoTArray<RefPtr<BrowsingContextGroup>, 5> bcGroups;
752 BrowsingContextGroup::GetAllGroups(bcGroups);
753 for (auto& bcGroup : bcGroups) {
754 AutoTArray<DocGroup*, 5> docGroups;
755 bcGroup->GetDocGroups(docGroups);
756 for (auto* docGroup : docGroups) {
757 for (Document* doc : *docGroup) {
758 if (doc->RecomputeResistFingerprinting()) {
759 if (auto* pc = doc->GetPresContext()) {
760 pc->MediaFeatureValuesChanged(
761 {MediaFeatureChangeReason::PreferenceChange},
762 MediaFeatureChangePropagation::JustThisDocument);
770 // static
771 nsresult nsContentUtils::Init() {
772 if (sInitialized) {
773 NS_WARNING("Init() called twice");
775 return NS_OK;
778 nsHTMLTags::AddRefTable();
780 sXPConnect = nsXPConnect::XPConnect();
781 // We hold a strong ref to sXPConnect to ensure that it does not go away until
782 // nsLayoutStatics::Shutdown is happening. Otherwise ~nsXPConnect can be
783 // triggered by xpcModuleDtor late in shutdown and cause crashes due to
784 // various stuff already being torn down by then. Note that this means that
785 // we are effectively making sure that if we leak nsLayoutStatics then we also
786 // leak nsXPConnect.
787 NS_ADDREF(sXPConnect);
789 sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
790 if (!sSecurityManager) return NS_ERROR_FAILURE;
791 NS_ADDREF(sSecurityManager);
793 sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
794 MOZ_ASSERT(sSystemPrincipal);
796 RefPtr<NullPrincipal> nullPrincipal =
797 NullPrincipal::CreateWithoutOriginAttributes();
798 if (!nullPrincipal) {
799 return NS_ERROR_FAILURE;
802 nullPrincipal.forget(&sNullSubjectPrincipal);
804 nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
805 if (NS_FAILED(rv)) {
806 // This makes life easier, but we can live without it.
808 sIOService = nullptr;
811 if (!InitializeEventTable()) return NS_ERROR_FAILURE;
813 if (!sEventListenerManagersHash) {
814 static const PLDHashTableOps hash_table_ops = {
815 PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
816 PLDHashTable::MoveEntryStub, EventListenerManagerHashClearEntry,
817 EventListenerManagerHashInitEntry};
819 sEventListenerManagersHash =
820 new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
822 RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
825 sBlockedScriptRunners = new AutoTArray<nsCOMPtr<nsIRunnable>, 8>;
827 #ifndef RELEASE_OR_BETA
828 sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
829 #endif
831 Element::InitCCCallbacks();
833 Unused << nsRFPService::GetOrCreate();
835 if (XRE_IsParentProcess()) {
836 AsyncPrecreateStringBundles();
839 RefPtr<UserInteractionObserver> uio = new UserInteractionObserver();
840 uio->Init();
841 uio.forget(&sUserInteractionObserver);
843 for (const auto& pref : kRfpPrefs) {
844 Preferences::RegisterCallback(RecomputeResistFingerprintingAllDocs, pref);
847 sInitialized = true;
849 return NS_OK;
852 bool nsContentUtils::InitJSBytecodeMimeType() {
853 MOZ_ASSERT(NS_IsMainThread());
854 MOZ_ASSERT(!sJSScriptBytecodeMimeType);
855 MOZ_ASSERT(!sJSModuleBytecodeMimeType);
857 JS::BuildIdCharVector jsBuildId;
858 if (!JS::GetScriptTranscodingBuildId(&jsBuildId)) {
859 return false;
862 nsDependentCSubstring jsBuildIdStr(jsBuildId.begin(), jsBuildId.length());
863 sJSScriptBytecodeMimeType =
864 new nsCString("javascript/moz-script-bytecode-"_ns + jsBuildIdStr);
865 sJSModuleBytecodeMimeType =
866 new nsCString("javascript/moz-module-bytecode-"_ns + jsBuildIdStr);
867 return true;
870 void nsContentUtils::GetShiftText(nsAString& text) {
871 if (!sShiftText) InitializeModifierStrings();
872 text.Assign(*sShiftText);
875 void nsContentUtils::GetControlText(nsAString& text) {
876 if (!sControlText) InitializeModifierStrings();
877 text.Assign(*sControlText);
880 void nsContentUtils::GetMetaText(nsAString& text) {
881 if (!sMetaText) InitializeModifierStrings();
882 text.Assign(*sMetaText);
885 void nsContentUtils::GetOSText(nsAString& text) {
886 if (!sOSText) {
887 InitializeModifierStrings();
889 text.Assign(*sOSText);
892 void nsContentUtils::GetAltText(nsAString& text) {
893 if (!sAltText) InitializeModifierStrings();
894 text.Assign(*sAltText);
897 void nsContentUtils::GetModifierSeparatorText(nsAString& text) {
898 if (!sModifierSeparator) InitializeModifierStrings();
899 text.Assign(*sModifierSeparator);
902 void nsContentUtils::InitializeModifierStrings() {
903 // load the display strings for the keyboard accelerators
904 nsCOMPtr<nsIStringBundleService> bundleService =
905 mozilla::components::StringBundle::Service();
906 nsCOMPtr<nsIStringBundle> bundle;
907 DebugOnly<nsresult> rv = NS_OK;
908 if (bundleService) {
909 rv = bundleService->CreateBundle(
910 "chrome://global-platform/locale/platformKeys.properties",
911 getter_AddRefs(bundle));
914 NS_ASSERTION(
915 NS_SUCCEEDED(rv) && bundle,
916 "chrome://global/locale/platformKeys.properties could not be loaded");
917 nsAutoString shiftModifier;
918 nsAutoString metaModifier;
919 nsAutoString osModifier;
920 nsAutoString altModifier;
921 nsAutoString controlModifier;
922 nsAutoString modifierSeparator;
923 if (bundle) {
924 // macs use symbols for each modifier key, so fetch each from the bundle,
925 // which also covers i18n
926 bundle->GetStringFromName("VK_SHIFT", shiftModifier);
927 bundle->GetStringFromName("VK_META", metaModifier);
928 bundle->GetStringFromName("VK_WIN", osModifier);
929 bundle->GetStringFromName("VK_ALT", altModifier);
930 bundle->GetStringFromName("VK_CONTROL", controlModifier);
931 bundle->GetStringFromName("MODIFIER_SEPARATOR", modifierSeparator);
933 // if any of these don't exist, we get an empty string
934 sShiftText = new nsString(shiftModifier);
935 sMetaText = new nsString(metaModifier);
936 sOSText = new nsString(osModifier);
937 sAltText = new nsString(altModifier);
938 sControlText = new nsString(controlModifier);
939 sModifierSeparator = new nsString(modifierSeparator);
942 mozilla::EventClassID nsContentUtils::GetEventClassIDFromMessage(
943 EventMessage aEventMessage) {
944 switch (aEventMessage) {
945 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
946 case message_: \
947 return struct_;
948 #include "mozilla/EventNameList.h"
949 #undef MESSAGE_TO_EVENT
950 default:
951 MOZ_ASSERT_UNREACHABLE("Invalid event message?");
952 return eBasicEventClass;
956 bool nsContentUtils::IsExternalProtocol(nsIURI* aURI) {
957 bool doesNotReturnData = false;
958 nsresult rv = NS_URIChainHasFlags(
959 aURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &doesNotReturnData);
960 return NS_SUCCEEDED(rv) && doesNotReturnData;
963 static nsAtom* GetEventTypeFromMessage(EventMessage aEventMessage) {
964 switch (aEventMessage) {
965 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
966 case message_: \
967 return nsGkAtoms::on##name_;
968 #include "mozilla/EventNameList.h"
969 #undef MESSAGE_TO_EVENT
970 default:
971 return nullptr;
975 // Because of SVG/SMIL we have several atoms mapped to the same
976 // id, but we can rely on MESSAGE_TO_EVENT to map id to only one atom.
977 static bool ShouldAddEventToStringEventTable(const EventNameMapping& aMapping) {
978 MOZ_ASSERT(aMapping.mAtom);
979 return GetEventTypeFromMessage(aMapping.mMessage) == aMapping.mAtom;
982 bool nsContentUtils::InitializeEventTable() {
983 NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
984 NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
986 static const EventNameMapping eventArray[] = {
987 #define EVENT(name_, _message, _type, _class) \
988 {nsGkAtoms::on##name_, _type, _message, _class, false},
989 #define WINDOW_ONLY_EVENT EVENT
990 #define DOCUMENT_ONLY_EVENT EVENT
991 #define NON_IDL_EVENT EVENT
992 #include "mozilla/EventNameList.h"
993 #undef WINDOW_ONLY_EVENT
994 #undef NON_IDL_EVENT
995 #undef EVENT
996 {nullptr}};
998 sAtomEventTable = new nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>(
999 ArrayLength(eventArray));
1000 sStringEventTable = new nsTHashMap<nsStringHashKey, EventNameMapping>(
1001 ArrayLength(eventArray));
1002 sUserDefinedEvents = new nsTArray<RefPtr<nsAtom>>(64);
1004 // Subtract one from the length because of the trailing null
1005 for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) {
1006 MOZ_ASSERT(!sAtomEventTable->Contains(eventArray[i].mAtom),
1007 "Double-defining event name; fix your EventNameList.h");
1008 sAtomEventTable->InsertOrUpdate(eventArray[i].mAtom, eventArray[i]);
1009 if (ShouldAddEventToStringEventTable(eventArray[i])) {
1010 sStringEventTable->InsertOrUpdate(
1011 Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
1012 eventArray[i]);
1016 return true;
1019 void nsContentUtils::InitializeTouchEventTable() {
1020 static bool sEventTableInitialized = false;
1021 if (!sEventTableInitialized && sAtomEventTable && sStringEventTable) {
1022 sEventTableInitialized = true;
1023 static const EventNameMapping touchEventArray[] = {
1024 #define EVENT(name_, _message, _type, _class)
1025 #define TOUCH_EVENT(name_, _message, _type, _class) \
1026 {nsGkAtoms::on##name_, _type, _message, _class},
1027 #include "mozilla/EventNameList.h"
1028 #undef TOUCH_EVENT
1029 #undef EVENT
1030 {nullptr}};
1031 // Subtract one from the length because of the trailing null
1032 for (uint32_t i = 0; i < ArrayLength(touchEventArray) - 1; ++i) {
1033 sAtomEventTable->InsertOrUpdate(touchEventArray[i].mAtom,
1034 touchEventArray[i]);
1035 sStringEventTable->InsertOrUpdate(
1036 Substring(nsDependentAtomString(touchEventArray[i].mAtom), 2),
1037 touchEventArray[i]);
1042 static bool Is8bit(const nsAString& aString) {
1043 static const char16_t EIGHT_BIT = char16_t(~0x00FF);
1045 for (nsAString::const_char_iterator start = aString.BeginReading(),
1046 end = aString.EndReading();
1047 start != end; ++start) {
1048 if (*start & EIGHT_BIT) {
1049 return false;
1053 return true;
1056 nsresult nsContentUtils::Btoa(const nsAString& aBinaryData,
1057 nsAString& aAsciiBase64String) {
1058 if (!Is8bit(aBinaryData)) {
1059 aAsciiBase64String.Truncate();
1060 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1063 return Base64Encode(aBinaryData, aAsciiBase64String);
1066 nsresult nsContentUtils::Atob(const nsAString& aAsciiBase64String,
1067 nsAString& aBinaryData) {
1068 if (!Is8bit(aAsciiBase64String)) {
1069 aBinaryData.Truncate();
1070 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1073 const char16_t* start = aAsciiBase64String.BeginReading();
1074 const char16_t* cur = start;
1075 const char16_t* end = aAsciiBase64String.EndReading();
1076 bool hasWhitespace = false;
1078 while (cur < end) {
1079 if (nsContentUtils::IsHTMLWhitespace(*cur)) {
1080 hasWhitespace = true;
1081 break;
1083 cur++;
1086 nsresult rv;
1088 if (hasWhitespace) {
1089 nsString trimmedString;
1091 if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible)) {
1092 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1095 trimmedString.Append(start, cur - start);
1097 while (cur < end) {
1098 if (!nsContentUtils::IsHTMLWhitespace(*cur)) {
1099 trimmedString.Append(*cur);
1101 cur++;
1103 rv = Base64Decode(trimmedString, aBinaryData);
1104 } else {
1105 rv = Base64Decode(aAsciiBase64String, aBinaryData);
1108 if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
1109 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1111 return rv;
1114 bool nsContentUtils::IsAutocompleteEnabled(
1115 mozilla::dom::HTMLInputElement* aInput) {
1116 MOZ_ASSERT(aInput, "aInput should not be null!");
1118 nsAutoString autocomplete;
1119 aInput->GetAutocomplete(autocomplete);
1121 if (autocomplete.IsEmpty()) {
1122 auto* form = aInput->GetForm();
1123 if (!form) {
1124 return true;
1127 form->GetAutocomplete(autocomplete);
1130 return !autocomplete.EqualsLiteral("off");
1133 nsContentUtils::AutocompleteAttrState
1134 nsContentUtils::SerializeAutocompleteAttribute(
1135 const nsAttrValue* aAttr, nsAString& aResult,
1136 AutocompleteAttrState aCachedState) {
1137 if (!aAttr ||
1138 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1139 return aCachedState;
1142 if (aCachedState == nsContentUtils::eAutocompleteAttrState_Valid) {
1143 uint32_t atomCount = aAttr->GetAtomCount();
1144 for (uint32_t i = 0; i < atomCount; i++) {
1145 if (i != 0) {
1146 aResult.Append(' ');
1148 aResult.Append(nsDependentAtomString(aAttr->AtomAt(i)));
1150 nsContentUtils::ASCIIToLower(aResult);
1151 return aCachedState;
1154 aResult.Truncate();
1156 mozilla::dom::AutocompleteInfo info;
1157 AutocompleteAttrState state =
1158 InternalSerializeAutocompleteAttribute(aAttr, info);
1159 if (state == eAutocompleteAttrState_Valid) {
1160 // Concatenate the info fields.
1161 aResult = info.mSection;
1163 if (!info.mAddressType.IsEmpty()) {
1164 if (!aResult.IsEmpty()) {
1165 aResult += ' ';
1167 aResult += info.mAddressType;
1170 if (!info.mContactType.IsEmpty()) {
1171 if (!aResult.IsEmpty()) {
1172 aResult += ' ';
1174 aResult += info.mContactType;
1177 if (!info.mFieldName.IsEmpty()) {
1178 if (!aResult.IsEmpty()) {
1179 aResult += ' ';
1181 aResult += info.mFieldName;
1185 return state;
1188 nsContentUtils::AutocompleteAttrState
1189 nsContentUtils::SerializeAutocompleteAttribute(
1190 const nsAttrValue* aAttr, mozilla::dom::AutocompleteInfo& aInfo,
1191 AutocompleteAttrState aCachedState, bool aGrantAllValidValue) {
1192 if (!aAttr ||
1193 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1194 return aCachedState;
1197 return InternalSerializeAutocompleteAttribute(aAttr, aInfo,
1198 aGrantAllValidValue);
1202 * Helper to validate the @autocomplete tokens.
1204 * @return {AutocompleteAttrState} The state of the attribute (invalid/valid).
1206 nsContentUtils::AutocompleteAttrState
1207 nsContentUtils::InternalSerializeAutocompleteAttribute(
1208 const nsAttrValue* aAttrVal, mozilla::dom::AutocompleteInfo& aInfo,
1209 bool aGrantAllValidValue) {
1210 // No autocomplete attribute so we are done
1211 if (!aAttrVal) {
1212 return eAutocompleteAttrState_Invalid;
1215 uint32_t numTokens = aAttrVal->GetAtomCount();
1216 if (!numTokens) {
1217 return eAutocompleteAttrState_Invalid;
1220 uint32_t index = numTokens - 1;
1221 nsString tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1222 AutocompleteCategory category;
1223 nsAttrValue enumValue;
1225 bool unsupported = false;
1226 if (!aGrantAllValidValue) {
1227 unsupported = enumValue.ParseEnumValue(
1228 tokenString, kAutocompleteUnsupportedFieldNameTable, false);
1229 if (unsupported) {
1230 return eAutocompleteAttrState_Invalid;
1234 nsAutoString str;
1235 bool result =
1236 enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
1237 if (result) {
1238 // Off/Automatic/Normal categories.
1239 if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
1240 enumValue.Equals(u"on"_ns, eIgnoreCase)) {
1241 if (numTokens > 1) {
1242 return eAutocompleteAttrState_Invalid;
1244 enumValue.ToString(str);
1245 ASCIIToLower(str);
1246 aInfo.mFieldName.Assign(str);
1247 aInfo.mCanAutomaticallyPersist =
1248 !enumValue.Equals(u"off"_ns, eIgnoreCase);
1249 return eAutocompleteAttrState_Valid;
1252 // Only allow on/off if form autofill @autocomplete values aren't enabled
1253 // and it doesn't grant all valid values.
1254 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1255 !aGrantAllValidValue) {
1256 return eAutocompleteAttrState_Invalid;
1259 // Normal category
1260 if (numTokens > 3) {
1261 return eAutocompleteAttrState_Invalid;
1263 category = eAutocompleteCategory_NORMAL;
1264 } else { // Check if the last token is of the contact category instead.
1265 // Only allow on/off if form autofill @autocomplete values aren't enabled
1266 // and it doesn't grant all valid values.
1267 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1268 !aGrantAllValidValue) {
1269 return eAutocompleteAttrState_Invalid;
1272 result = enumValue.ParseEnumValue(
1273 tokenString, kAutocompleteContactFieldNameTable, false);
1274 if (!result || numTokens > 4) {
1275 return eAutocompleteAttrState_Invalid;
1278 category = eAutocompleteCategory_CONTACT;
1281 enumValue.ToString(str);
1282 ASCIIToLower(str);
1283 aInfo.mFieldName.Assign(str);
1285 aInfo.mCanAutomaticallyPersist = !enumValue.ParseEnumValue(
1286 tokenString, kAutocompleteNoPersistFieldNameTable, false);
1288 // We are done if this was the only token.
1289 if (numTokens == 1) {
1290 return eAutocompleteAttrState_Valid;
1293 --index;
1294 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1296 if (category == eAutocompleteCategory_CONTACT) {
1297 if (!aGrantAllValidValue) {
1298 unsupported = enumValue.ParseEnumValue(
1299 tokenString, kAutocompleteUnsupportedContactFieldHintTable, false);
1300 if (unsupported) {
1301 return eAutocompleteAttrState_Invalid;
1305 nsAttrValue contactFieldHint;
1306 result = contactFieldHint.ParseEnumValue(
1307 tokenString, kAutocompleteContactFieldHintTable, false);
1308 if (result) {
1309 nsAutoString contactFieldHintString;
1310 contactFieldHint.ToString(contactFieldHintString);
1311 ASCIIToLower(contactFieldHintString);
1312 aInfo.mContactType.Assign(contactFieldHintString);
1313 if (index == 0) {
1314 return eAutocompleteAttrState_Valid;
1316 --index;
1317 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1321 // Check for billing/shipping tokens
1322 nsAttrValue fieldHint;
1323 if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable,
1324 false)) {
1325 nsString fieldHintString;
1326 fieldHint.ToString(fieldHintString);
1327 ASCIIToLower(fieldHintString);
1328 aInfo.mAddressType.Assign(fieldHintString);
1329 if (index == 0) {
1330 return eAutocompleteAttrState_Valid;
1332 --index;
1333 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1336 // Check for section-* token
1337 const nsDependentSubstring& section = Substring(tokenString, 0, 8);
1338 if (section.LowerCaseEqualsASCII("section-")) {
1339 ASCIIToLower(tokenString);
1340 aInfo.mSection.Assign(tokenString);
1341 if (index == 0) {
1342 return eAutocompleteAttrState_Valid;
1346 // Clear the fields as the autocomplete attribute is invalid.
1347 aInfo.mSection.Truncate();
1348 aInfo.mAddressType.Truncate();
1349 aInfo.mContactType.Truncate();
1350 aInfo.mFieldName.Truncate();
1352 return eAutocompleteAttrState_Invalid;
1355 // Parse an integer according to HTML spec
1356 template <class CharT>
1357 int32_t nsContentUtils::ParseHTMLIntegerImpl(
1358 const CharT* aStart, const CharT* aEnd,
1359 ParseHTMLIntegerResultFlags* aResult) {
1360 int result = eParseHTMLInteger_NoFlags;
1362 const CharT* iter = aStart;
1364 while (iter != aEnd && nsContentUtils::IsHTMLWhitespace(*iter)) {
1365 result |= eParseHTMLInteger_NonStandard;
1366 ++iter;
1369 if (iter == aEnd) {
1370 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1371 *aResult = (ParseHTMLIntegerResultFlags)result;
1372 return 0;
1375 int sign = 1;
1376 if (*iter == CharT('-')) {
1377 sign = -1;
1378 result |= eParseHTMLInteger_Negative;
1379 ++iter;
1380 } else if (*iter == CharT('+')) {
1381 result |= eParseHTMLInteger_NonStandard;
1382 ++iter;
1385 bool foundValue = false;
1386 CheckedInt32 value = 0;
1388 // Check for leading zeros first.
1389 uint64_t leadingZeros = 0;
1390 while (iter != aEnd) {
1391 if (*iter != CharT('0')) {
1392 break;
1395 ++leadingZeros;
1396 foundValue = true;
1397 ++iter;
1400 while (iter != aEnd) {
1401 if (*iter >= CharT('0') && *iter <= CharT('9')) {
1402 value = (value * 10) + (*iter - CharT('0')) * sign;
1403 ++iter;
1404 if (!value.isValid()) {
1405 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
1406 break;
1408 foundValue = true;
1409 } else {
1410 break;
1414 if (!foundValue) {
1415 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1418 if (value.isValid() &&
1419 ((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
1420 (sign == -1 && value == 0))) {
1421 result |= eParseHTMLInteger_NonStandard;
1424 if (iter != aEnd) {
1425 result |= eParseHTMLInteger_DidNotConsumeAllInput;
1428 *aResult = (ParseHTMLIntegerResultFlags)result;
1429 return value.isValid() ? value.value() : 0;
1432 // Parse an integer according to HTML spec
1433 int32_t nsContentUtils::ParseHTMLInteger(const char16_t* aStart,
1434 const char16_t* aEnd,
1435 ParseHTMLIntegerResultFlags* aResult) {
1436 return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
1439 int32_t nsContentUtils::ParseHTMLInteger(const char* aStart, const char* aEnd,
1440 ParseHTMLIntegerResultFlags* aResult) {
1441 return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
1444 #define SKIP_WHITESPACE(iter, end_iter, end_res) \
1445 while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
1446 ++(iter); \
1448 if ((iter) == (end_iter)) { \
1449 return (end_res); \
1452 #define SKIP_ATTR_NAME(iter, end_iter) \
1453 while ((iter) != (end_iter) && !nsCRT::IsAsciiSpace(*(iter)) && \
1454 *(iter) != '=') { \
1455 ++(iter); \
1458 bool nsContentUtils::GetPseudoAttributeValue(const nsString& aSource,
1459 nsAtom* aName, nsAString& aValue) {
1460 aValue.Truncate();
1462 const char16_t* start = aSource.get();
1463 const char16_t* end = start + aSource.Length();
1464 const char16_t* iter;
1466 while (start != end) {
1467 SKIP_WHITESPACE(start, end, false)
1468 iter = start;
1469 SKIP_ATTR_NAME(iter, end)
1471 if (start == iter) {
1472 return false;
1475 // Remember the attr name.
1476 const nsDependentSubstring& attrName = Substring(start, iter);
1478 // Now check whether this is a valid name="value" pair.
1479 start = iter;
1480 SKIP_WHITESPACE(start, end, false)
1481 if (*start != '=') {
1482 // No '=', so this is not a name="value" pair. We don't know
1483 // what it is, and we have no way to handle it.
1484 return false;
1487 // Have to skip the value.
1488 ++start;
1489 SKIP_WHITESPACE(start, end, false)
1490 char16_t q = *start;
1491 if (q != kQuote && q != kApostrophe) {
1492 // Not a valid quoted value, so bail.
1493 return false;
1496 ++start; // Point to the first char of the value.
1497 iter = start;
1499 while (iter != end && *iter != q) {
1500 ++iter;
1503 if (iter == end) {
1504 // Oops, unterminated quoted string.
1505 return false;
1508 // At this point attrName holds the name of the "attribute" and
1509 // the value is between start and iter.
1511 if (aName->Equals(attrName)) {
1512 // We'll accumulate as many characters as possible (until we hit either
1513 // the end of the string or the beginning of an entity). Chunks will be
1514 // delimited by start and chunkEnd.
1515 const char16_t* chunkEnd = start;
1516 while (chunkEnd != iter) {
1517 if (*chunkEnd == kLessThan) {
1518 aValue.Truncate();
1520 return false;
1523 if (*chunkEnd == kAmpersand) {
1524 aValue.Append(start, chunkEnd - start);
1526 const char16_t* afterEntity = nullptr;
1527 char16_t result[2];
1528 uint32_t count = MOZ_XMLTranslateEntity(
1529 reinterpret_cast<const char*>(chunkEnd),
1530 reinterpret_cast<const char*>(iter),
1531 reinterpret_cast<const char**>(&afterEntity), result);
1532 if (count == 0) {
1533 aValue.Truncate();
1535 return false;
1538 aValue.Append(result, count);
1540 // Advance to after the entity and begin a new chunk.
1541 start = chunkEnd = afterEntity;
1542 } else {
1543 ++chunkEnd;
1547 // Append remainder.
1548 aValue.Append(start, iter - start);
1550 return true;
1553 // Resume scanning after the end of the attribute value (past the quote
1554 // char).
1555 start = iter + 1;
1558 return false;
1561 bool nsContentUtils::IsJavaScriptLanguage(const nsString& aName) {
1562 // Create MIME type as "text/" + given input
1563 nsAutoString mimeType(u"text/");
1564 mimeType.Append(aName);
1566 return IsJavascriptMIMEType(mimeType);
1569 void nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType,
1570 nsString& aParams) {
1571 aType.Truncate();
1572 aParams.Truncate();
1573 int32_t semiIndex = aValue.FindChar(char16_t(';'));
1574 if (-1 != semiIndex) {
1575 aType = Substring(aValue, 0, semiIndex);
1576 aParams =
1577 Substring(aValue, semiIndex + 1, aValue.Length() - (semiIndex + 1));
1578 aParams.StripWhitespace();
1579 } else {
1580 aType = aValue;
1582 aType.StripWhitespace();
1586 * A helper function that parses a sandbox attribute (of an <iframe> or a CSP
1587 * directive) and converts it to the set of flags used internally.
1589 * @param aSandboxAttr the sandbox attribute
1590 * @return the set of flags (SANDBOXED_NONE if aSandboxAttr is
1591 * null)
1593 uint32_t nsContentUtils::ParseSandboxAttributeToFlags(
1594 const nsAttrValue* aSandboxAttr) {
1595 if (!aSandboxAttr) {
1596 return SANDBOXED_NONE;
1599 uint32_t out = SANDBOX_ALL_FLAGS;
1601 #define SANDBOX_KEYWORD(string, atom, flags) \
1602 if (aSandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { \
1603 out &= ~(flags); \
1605 #include "IframeSandboxKeywordList.h"
1606 #undef SANDBOX_KEYWORD
1608 return out;
1612 * A helper function that checks if a string matches a valid sandbox flag.
1614 * @param aFlag the potential sandbox flag.
1615 * @return true if the flag is a sandbox flag.
1617 bool nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag) {
1618 #define SANDBOX_KEYWORD(string, atom, flags) \
1619 if (EqualsIgnoreASCIICase(nsDependentAtomString(nsGkAtoms::atom), aFlag)) { \
1620 return true; \
1622 #include "IframeSandboxKeywordList.h"
1623 #undef SANDBOX_KEYWORD
1624 return false;
1628 * A helper function that returns a string attribute corresponding to the
1629 * sandbox flags.
1631 * @param aFlags the sandbox flags
1632 * @param aString the attribute corresponding to the flags (null if aFlags
1633 * is zero)
1635 void nsContentUtils::SandboxFlagsToString(uint32_t aFlags, nsAString& aString) {
1636 if (!aFlags) {
1637 SetDOMStringToNull(aString);
1638 return;
1641 aString.Truncate();
1643 #define SANDBOX_KEYWORD(string, atom, flags) \
1644 if (!(aFlags & (flags))) { \
1645 if (!aString.IsEmpty()) { \
1646 aString.AppendLiteral(u" "); \
1648 aString.Append(nsDependentAtomString(nsGkAtoms::atom)); \
1650 #include "IframeSandboxKeywordList.h"
1651 #undef SANDBOX_KEYWORD
1654 nsIBidiKeyboard* nsContentUtils::GetBidiKeyboard() {
1655 if (!sBidiKeyboard) {
1656 sBidiKeyboard = nsIWidget::CreateBidiKeyboard();
1658 return sBidiKeyboard;
1662 * This is used to determine whether a character is in one of the classes
1663 * which CSS says should be part of the first-letter. Currently, that is
1664 * all punctuation classes (P*). Note that this is a change from CSS2
1665 * which excluded Pc and Pd.
1667 * https://www.w3.org/TR/css-pseudo-4/#first-letter-pseudo
1668 * "Punctuation (i.e, characters that belong to the Punctuation (P*) Unicode
1669 * general category [UAX44]) [...]"
1672 // static
1673 bool nsContentUtils::IsFirstLetterPunctuation(uint32_t aChar) {
1674 switch (mozilla::unicode::GetGeneralCategory(aChar)) {
1675 case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
1676 case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION: /* Pd */
1677 case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION: /* Pe */
1678 case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION: /* Pf */
1679 case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
1680 case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION: /* Po */
1681 case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION: /* Ps */
1682 return true;
1683 default:
1684 return false;
1688 // static
1689 bool nsContentUtils::IsAlphanumeric(uint32_t aChar) {
1690 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1692 return (cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber);
1695 // static
1696 bool nsContentUtils::IsAlphanumericOrSymbol(uint32_t aChar) {
1697 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1699 return cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber ||
1700 cat == nsUGenCategory::kSymbol;
1703 /* static */
1704 bool nsContentUtils::IsHTMLWhitespace(char16_t aChar) {
1705 return aChar == char16_t(0x0009) || aChar == char16_t(0x000A) ||
1706 aChar == char16_t(0x000C) || aChar == char16_t(0x000D) ||
1707 aChar == char16_t(0x0020);
1710 /* static */
1711 bool nsContentUtils::IsHTMLWhitespaceOrNBSP(char16_t aChar) {
1712 return IsHTMLWhitespace(aChar) || aChar == char16_t(0xA0);
1715 /* static */
1716 bool nsContentUtils::IsHTMLBlockLevelElement(nsIContent* aContent) {
1717 return aContent->IsAnyOfHTMLElements(
1718 nsGkAtoms::address, nsGkAtoms::article, nsGkAtoms::aside,
1719 nsGkAtoms::blockquote, nsGkAtoms::center, nsGkAtoms::dir, nsGkAtoms::div,
1720 nsGkAtoms::dl, // XXX why not dt and dd?
1721 nsGkAtoms::fieldset,
1722 nsGkAtoms::figure, // XXX shouldn't figcaption be on this list
1723 nsGkAtoms::footer, nsGkAtoms::form, nsGkAtoms::h1, nsGkAtoms::h2,
1724 nsGkAtoms::h3, nsGkAtoms::h4, nsGkAtoms::h5, nsGkAtoms::h6,
1725 nsGkAtoms::header, nsGkAtoms::hgroup, nsGkAtoms::hr, nsGkAtoms::li,
1726 nsGkAtoms::listing, nsGkAtoms::menu, nsGkAtoms::nav, nsGkAtoms::ol,
1727 nsGkAtoms::p, nsGkAtoms::pre, nsGkAtoms::section, nsGkAtoms::table,
1728 nsGkAtoms::ul, nsGkAtoms::xmp);
1731 /* static */
1732 bool nsContentUtils::ParseIntMarginValue(const nsAString& aString,
1733 nsIntMargin& result) {
1734 nsAutoString marginStr(aString);
1735 marginStr.CompressWhitespace(true, true);
1736 if (marginStr.IsEmpty()) {
1737 return false;
1740 int32_t start = 0, end = 0;
1741 for (int count = 0; count < 4; count++) {
1742 if ((uint32_t)end >= marginStr.Length()) return false;
1744 // top, right, bottom, left
1745 if (count < 3)
1746 end = Substring(marginStr, start).FindChar(',');
1747 else
1748 end = Substring(marginStr, start).Length();
1750 if (end <= 0) return false;
1752 nsresult ec;
1753 int32_t val = nsString(Substring(marginStr, start, end)).ToInteger(&ec);
1754 if (NS_FAILED(ec)) return false;
1756 switch (count) {
1757 case 0:
1758 result.top = val;
1759 break;
1760 case 1:
1761 result.right = val;
1762 break;
1763 case 2:
1764 result.bottom = val;
1765 break;
1766 case 3:
1767 result.left = val;
1768 break;
1770 start += end + 1;
1772 return true;
1775 // static
1776 int32_t nsContentUtils::ParseLegacyFontSize(const nsAString& aValue) {
1777 nsAString::const_iterator iter, end;
1778 aValue.BeginReading(iter);
1779 aValue.EndReading(end);
1781 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1782 ++iter;
1785 if (iter == end) {
1786 return 0;
1789 bool relative = false;
1790 bool negate = false;
1791 if (*iter == char16_t('-')) {
1792 relative = true;
1793 negate = true;
1794 ++iter;
1795 } else if (*iter == char16_t('+')) {
1796 relative = true;
1797 ++iter;
1800 if (iter == end || *iter < char16_t('0') || *iter > char16_t('9')) {
1801 return 0;
1804 // We don't have to worry about overflow, since we can bail out as soon as
1805 // we're bigger than 7.
1806 int32_t value = 0;
1807 while (iter != end && *iter >= char16_t('0') && *iter <= char16_t('9')) {
1808 value = 10 * value + (*iter - char16_t('0'));
1809 if (value >= 7) {
1810 break;
1812 ++iter;
1815 if (relative) {
1816 if (negate) {
1817 value = 3 - value;
1818 } else {
1819 value = 3 + value;
1823 return clamped(value, 1, 7);
1826 /* static */
1827 void nsContentUtils::GetOfflineAppManifest(Document* aDocument, nsIURI** aURI) {
1828 MOZ_ASSERT(NS_IsMainThread());
1829 MOZ_ASSERT(aDocument);
1830 *aURI = nullptr;
1832 if (aDocument->GetController().isSome()) {
1833 return;
1836 Element* docElement = aDocument->GetRootElement();
1837 if (!docElement) {
1838 return;
1841 nsAutoString manifestSpec;
1842 docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
1844 // Manifest URIs can't have fragment identifiers.
1845 if (manifestSpec.IsEmpty() || manifestSpec.Contains('#')) {
1846 return;
1849 nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec, aDocument,
1850 aDocument->GetDocBaseURI());
1853 /* static */
1854 bool nsContentUtils::OfflineAppAllowed(nsIURI* aURI) { return false; }
1856 /* static */
1857 bool nsContentUtils::OfflineAppAllowed(nsIPrincipal* aPrincipal) {
1858 return false;
1860 // Static
1861 bool nsContentUtils::IsErrorPage(nsIURI* aURI) {
1862 if (!aURI) {
1863 return false;
1866 if (!aURI->SchemeIs("about")) {
1867 return false;
1870 nsAutoCString name;
1871 nsresult rv = NS_GetAboutModuleName(aURI, name);
1872 NS_ENSURE_SUCCESS(rv, false);
1874 return name.EqualsLiteral("certerror") || name.EqualsLiteral("neterror") ||
1875 name.EqualsLiteral("blocked");
1878 // static
1879 void nsContentUtils::Shutdown() {
1880 sInitialized = false;
1882 nsHTMLTags::ReleaseTable();
1884 NS_IF_RELEASE(sContentPolicyService);
1885 sTriedToGetContentPolicy = false;
1886 for (StaticRefPtr<nsIStringBundle>& bundle : sStringBundles) {
1887 bundle = nullptr;
1890 NS_IF_RELEASE(sStringBundleService);
1891 NS_IF_RELEASE(sConsoleService);
1892 NS_IF_RELEASE(sXPConnect);
1893 NS_IF_RELEASE(sSecurityManager);
1894 NS_IF_RELEASE(sSystemPrincipal);
1895 NS_IF_RELEASE(sNullSubjectPrincipal);
1896 NS_IF_RELEASE(sIOService);
1898 sBidiKeyboard = nullptr;
1900 delete sAtomEventTable;
1901 sAtomEventTable = nullptr;
1902 delete sStringEventTable;
1903 sStringEventTable = nullptr;
1904 delete sUserDefinedEvents;
1905 sUserDefinedEvents = nullptr;
1907 if (sEventListenerManagersHash) {
1908 NS_ASSERTION(sEventListenerManagersHash->EntryCount() == 0,
1909 "Event listener manager hash not empty at shutdown!");
1911 // See comment above.
1913 // However, we have to handle this table differently. If it still
1914 // has entries, we want to leak it too, so that we can keep it alive
1915 // in case any elements are destroyed. Because if they are, we need
1916 // their event listener managers to be destroyed too, or otherwise
1917 // it could leave dangling references in DOMClassInfo's preserved
1918 // wrapper table.
1920 if (sEventListenerManagersHash->EntryCount() == 0) {
1921 delete sEventListenerManagersHash;
1922 sEventListenerManagersHash = nullptr;
1926 if (sDOMArenaHashtable) {
1927 MOZ_ASSERT(sDOMArenaHashtable->Count() == 0);
1928 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
1929 delete sDOMArenaHashtable;
1930 sDOMArenaHashtable = nullptr;
1933 NS_ASSERTION(!sBlockedScriptRunners || sBlockedScriptRunners->Length() == 0,
1934 "How'd this happen?");
1935 delete sBlockedScriptRunners;
1936 sBlockedScriptRunners = nullptr;
1938 delete sShiftText;
1939 sShiftText = nullptr;
1940 delete sControlText;
1941 sControlText = nullptr;
1942 delete sMetaText;
1943 sMetaText = nullptr;
1944 delete sOSText;
1945 sOSText = nullptr;
1946 delete sAltText;
1947 sAltText = nullptr;
1948 delete sModifierSeparator;
1949 sModifierSeparator = nullptr;
1951 delete sJSScriptBytecodeMimeType;
1952 sJSScriptBytecodeMimeType = nullptr;
1954 delete sJSModuleBytecodeMimeType;
1955 sJSModuleBytecodeMimeType = nullptr;
1957 NS_IF_RELEASE(sSameOriginChecker);
1959 if (sUserInteractionObserver) {
1960 sUserInteractionObserver->Shutdown();
1961 NS_RELEASE(sUserInteractionObserver);
1964 for (const auto& pref : kRfpPrefs) {
1965 Preferences::UnregisterCallback(RecomputeResistFingerprintingAllDocs, pref);
1968 TextControlState::Shutdown();
1969 nsMappedAttributes::Shutdown();
1973 * Checks whether two nodes come from the same origin. aTrustedNode is
1974 * considered 'safe' in that a user can operate on it.
1976 // static
1977 nsresult nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
1978 const nsINode* unTrustedNode) {
1979 MOZ_ASSERT(aTrustedNode);
1980 MOZ_ASSERT(unTrustedNode);
1983 * Get hold of each node's principal
1986 nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
1987 nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
1989 if (trustedPrincipal == unTrustedPrincipal) {
1990 return NS_OK;
1993 bool equal;
1994 // XXXbz should we actually have a Subsumes() check here instead? Or perhaps
1995 // a separate method for that, with callers using one or the other?
1996 if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
1997 !equal) {
1998 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
2001 return NS_OK;
2004 // static
2005 bool nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
2006 nsIPrincipal* aPrincipal) {
2007 bool subsumes;
2008 nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
2009 NS_ENSURE_SUCCESS(rv, false);
2011 if (subsumes) {
2012 return true;
2015 // The subject doesn't subsume aPrincipal. Allow access only if the subject
2016 // is chrome.
2017 return IsCallerChrome();
2020 // static
2021 bool nsContentUtils::CanCallerAccess(const nsINode* aNode) {
2022 nsIPrincipal* subject = SubjectPrincipal();
2023 if (subject->IsSystemPrincipal()) {
2024 return true;
2027 if (aNode->ChromeOnlyAccess()) {
2028 return false;
2031 return CanCallerAccess(subject, aNode->NodePrincipal());
2034 // static
2035 bool nsContentUtils::CanCallerAccess(nsPIDOMWindowInner* aWindow) {
2036 nsCOMPtr<nsIScriptObjectPrincipal> scriptObject = do_QueryInterface(aWindow);
2037 NS_ENSURE_TRUE(scriptObject, false);
2039 return CanCallerAccess(SubjectPrincipal(), scriptObject->GetPrincipal());
2042 // static
2043 bool nsContentUtils::PrincipalHasPermission(nsIPrincipal& aPrincipal,
2044 const nsAtom* aPerm) {
2045 // Chrome gets access by default.
2046 if (aPrincipal.IsSystemPrincipal()) {
2047 return true;
2050 // Otherwise, only allow if caller is an addon with the permission.
2051 return BasePrincipal::Cast(aPrincipal).AddonHasPermission(aPerm);
2054 // static
2055 bool nsContentUtils::CallerHasPermission(JSContext* aCx, const nsAtom* aPerm) {
2056 return PrincipalHasPermission(*SubjectPrincipal(aCx), aPerm);
2059 // static
2060 nsIPrincipal* nsContentUtils::GetAttrTriggeringPrincipal(
2061 nsIContent* aContent, const nsAString& aAttrValue,
2062 nsIPrincipal* aSubjectPrincipal) {
2063 nsIPrincipal* contentPrin = aContent ? aContent->NodePrincipal() : nullptr;
2065 // If the subject principal is the same as the content principal, or no
2066 // explicit subject principal was provided, we don't need to do any further
2067 // checks. Just return the content principal.
2068 if (contentPrin == aSubjectPrincipal || !aSubjectPrincipal) {
2069 return contentPrin;
2072 // Only use the subject principal if the URL string we are going to end up
2073 // fetching is under the control of that principal, which is never the case
2074 // for relative URLs.
2075 if (aAttrValue.IsEmpty() ||
2076 !IsAbsoluteURL(NS_ConvertUTF16toUTF8(aAttrValue))) {
2077 return contentPrin;
2080 // Only use the subject principal as the attr triggering principal if it
2081 // should override the CSP of the node's principal.
2082 if (BasePrincipal::Cast(aSubjectPrincipal)->OverridesCSP(contentPrin)) {
2083 return aSubjectPrincipal;
2086 return contentPrin;
2089 // static
2090 bool nsContentUtils::IsAbsoluteURL(const nsACString& aURL) {
2091 nsAutoCString scheme;
2092 if (NS_FAILED(net_ExtractURLScheme(aURL, scheme))) {
2093 // If we can't extract a scheme, it's not an absolute URL.
2094 return false;
2097 // If it parses as an absolute StandardURL, it's definitely an absolute URL,
2098 // so no need to check with the IO service.
2099 if (net_IsAbsoluteURL(aURL)) {
2100 return true;
2103 uint32_t flags;
2104 if (NS_SUCCEEDED(sIOService->GetProtocolFlags(scheme.get(), &flags))) {
2105 return flags & nsIProtocolHandler::URI_NORELATIVE;
2108 return false;
2111 // static
2112 bool nsContentUtils::InProlog(nsINode* aNode) {
2113 MOZ_ASSERT(aNode, "missing node to nsContentUtils::InProlog");
2115 nsINode* parent = aNode->GetParentNode();
2116 if (!parent || !parent->IsDocument()) {
2117 return false;
2120 const Document* doc = parent->AsDocument();
2121 const nsIContent* root = doc->GetRootElement();
2122 if (!root) {
2123 return true;
2125 const Maybe<uint32_t> indexOfNode = doc->ComputeIndexOf(aNode);
2126 const Maybe<uint32_t> indexOfRoot = doc->ComputeIndexOf(root);
2127 if (MOZ_LIKELY(indexOfNode.isSome() && indexOfRoot.isSome())) {
2128 return *indexOfNode < *indexOfRoot;
2130 // XXX Keep the odd traditional behavior for now.
2131 return indexOfNode.isNothing() && indexOfRoot.isSome();
2134 bool nsContentUtils::IsCallerChrome() {
2135 MOZ_ASSERT(NS_IsMainThread());
2136 return SubjectPrincipal() == sSystemPrincipal;
2139 #ifdef FUZZING
2140 bool nsContentUtils::IsFuzzingEnabled() {
2141 return StaticPrefs::fuzzing_enabled();
2143 #endif
2145 /* static */
2146 bool nsContentUtils::IsCallerChromeOrElementTransformGettersEnabled(
2147 JSContext* aCx, JSObject*) {
2148 return ThreadsafeIsSystemCaller(aCx) ||
2149 StaticPrefs::dom_element_transform_getters_enabled();
2152 // Older Should RFP Functions ----------------------------------
2154 /* static */
2155 bool nsContentUtils::ShouldResistFingerprinting(
2156 RFPTarget aTarget /* = RFPTarget::Unknown */) {
2157 return nsRFPService::IsRFPEnabledFor(aTarget);
2160 /* static */
2161 bool nsContentUtils::ShouldResistFingerprinting(nsIGlobalObject* aGlobalObject,
2162 RFPTarget aTarget) {
2163 if (!aGlobalObject) {
2164 return ShouldResistFingerprinting("Null Object", aTarget);
2166 return aGlobalObject->ShouldResistFingerprinting(aTarget);
2169 // Newer Should RFP Functions ----------------------------------
2171 inline void LogDomainAndPrefList(const char* exemptedDomainsPrefName,
2172 nsAutoCString& url, bool isExemptDomain) {
2173 nsAutoCString list;
2174 Preferences::GetCString(exemptedDomainsPrefName, list);
2175 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2176 ("Domain \"%s\" is %s the exempt list \"%s\"",
2177 PromiseFlatCString(url).get(), isExemptDomain ? "in" : "NOT in",
2178 PromiseFlatCString(list).get()));
2181 inline bool CookieJarSettingsSaysShouldResistFingerprinting(
2182 nsILoadInfo* aLoadInfo) {
2183 // If the loadinfo's CookieJarSettings says that we _should_ resist
2184 // fingerprinting we can always believe it. (This is the (*) rule from
2185 // CookieJarSettings.h)
2186 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
2187 nsresult rv =
2188 aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
2189 if (rv == NS_ERROR_NOT_IMPLEMENTED) {
2190 // The TRRLoadInfo in particular does not implement this method
2191 // In that instance. We will return false and let other code decide if
2192 // we shouldRFP for this connection
2193 return false;
2195 if (NS_WARN_IF(NS_FAILED(rv))) {
2196 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2197 ("Called CookieJarSettingsSaysShouldResistFingerprinting but the "
2198 "loadinfo's CookieJarSettings couldn't be retrieved"));
2199 return false;
2201 return cookieJarSettings->GetShouldResistFingerprinting();
2204 const char* kExemptedDomainsPrefName =
2205 "privacy.resistFingerprinting.exemptedDomains";
2207 /* static */
2208 bool nsContentUtils::ShouldResistFingerprinting(
2209 const char* aJustification, RFPTarget aTarget /* = RFPTarget::Unknown */) {
2210 // See comment in header file for information about usage
2211 return ShouldResistFingerprinting(aTarget);
2214 /* static */
2215 bool nsContentUtils::ShouldResistFingerprinting(
2216 CallerType aCallerType, nsIGlobalObject* aGlobalObject,
2217 RFPTarget aTarget /* = RFPTarget::Unknown */) {
2218 if (aCallerType == CallerType::System) {
2219 return false;
2221 return ShouldResistFingerprinting(aGlobalObject, aTarget);
2224 bool nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell,
2225 RFPTarget aTarget) {
2226 if (!aDocShell) {
2227 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2228 ("Called nsContentUtils::ShouldResistFingerprinting(nsIDocShell*) "
2229 "with NULL docshell"));
2230 return ShouldResistFingerprinting("Null Object", aTarget);
2232 Document* doc = aDocShell->GetDocument();
2233 if (!doc) {
2234 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2235 ("Called nsContentUtils::ShouldResistFingerprinting(nsIDocShell*) "
2236 "with NULL doc"));
2237 return ShouldResistFingerprinting(aTarget);
2239 return doc->ShouldResistFingerprinting(aTarget);
2242 /* static */
2243 bool nsContentUtils::ShouldResistFingerprinting(
2244 nsIChannel* aChannel, RFPTarget aTarget /* = RFPTarget::Unknown */) {
2245 if (!aChannel) {
2246 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2247 ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2248 "aChannel) with NULL channel"));
2249 return ShouldResistFingerprinting("Null Object", aTarget);
2252 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
2253 if (!loadInfo) {
2254 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2255 ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2256 "aChannel) but the channel's loadinfo was NULL"));
2257 return ShouldResistFingerprinting("Null Object", aTarget);
2260 // With this check, we can ensure that the prefs and target say yes, so only
2261 // an exemption would cause us to return false.
2262 if (!ShouldResistFingerprinting("Positive return check", aTarget)) {
2263 return false;
2266 // Document types have no loading principal. Subdocument types do have a
2267 // loading principal, but it is the loading principal of the parent document;
2268 // not the subdocument.
2269 auto contentType = loadInfo->GetExternalContentPolicyType();
2270 if (contentType == ExtContentPolicy::TYPE_DOCUMENT ||
2271 contentType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
2272 // This cookie jar check is relevant to both document and non-document
2273 // cases. but it will be performed inside the ShouldRFP(nsILoadInfo) as
2274 // well, so we put into this conditional to avoid doing it twice in that
2275 // case.
2276 if (CookieJarSettingsSaysShouldResistFingerprinting(loadInfo)) {
2277 return true;
2280 nsCOMPtr<nsIURI> channelURI;
2281 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
2282 MOZ_ASSERT(
2283 NS_SUCCEEDED(rv),
2284 "Failed to get URI in "
2285 "nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel)");
2286 // this check is to ensure that we do not crash in non-debug builds.
2287 if (NS_FAILED(rv)) {
2288 return true;
2291 #if 0
2292 if (loadInfo->GetExternalContentPolicyType() == ExtContentPolicy::TYPE_SUBDOCUMENT) {
2293 nsCOMPtr<nsIURI> channelURI;
2294 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
2295 nsAutoCString channelSpec;
2296 channelURI->GetSpec(channelSpec);
2298 if (!loadInfo->GetLoadingPrincipal()) {
2299 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2300 ("Sub Document Type. FinalChannelURI is %s, Loading Principal is NULL\n",
2301 channelSpec.get()));
2303 } else {
2304 nsAutoCString loadingPrincipalSpec;
2305 loadInfo->GetLoadingPrincipal()->GetOrigin(loadingPrincipalSpec);
2307 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2308 ("Sub Document Type. FinalChannelURI is %s, Loading Principal Origin is %s\n",
2309 channelSpec.get(), loadingPrincipalSpec.get()));
2313 #endif
2315 return ShouldResistFingerprinting_dangerous(
2316 channelURI, loadInfo->GetOriginAttributes(), "Internal Call", aTarget);
2319 // Case 2: Subresource Load
2320 return ShouldResistFingerprinting(loadInfo, aTarget);
2323 /* static */
2324 bool nsContentUtils::ShouldResistFingerprinting_dangerous(
2325 nsIURI* aURI, const mozilla::OriginAttributes& aOriginAttributes,
2326 const char* aJustification, RFPTarget aTarget /* = RFPTarget::Unknown */) {
2327 // With this check, we can ensure that the prefs and target say yes, so only
2328 // an exemption would cause us to return false.
2329 if (!ShouldResistFingerprinting("Positive return check", aTarget)) {
2330 return false;
2333 if (!StaticPrefs::privacy_resistFingerprinting_DoNotUseDirectly() &&
2334 !StaticPrefs::privacy_fingerprintingProtection_DoNotUseDirectly()) {
2335 // If neither of the 'regular' RFP prefs are set, then one (or both)
2336 // of the PBM-Only prefs are set (or we would have failed the
2337 // Positive return check.) Therefore, if we are not in PBM, return false
2338 if (aOriginAttributes.mPrivateBrowsingId == 0) {
2339 return false;
2343 // Exclude internal schemes and web extensions
2344 if (aURI->SchemeIs("about") || aURI->SchemeIs("chrome") ||
2345 aURI->SchemeIs("resource") || aURI->SchemeIs("view-source") ||
2346 aURI->SchemeIs("moz-extension")) {
2347 return false;
2350 bool isExemptDomain = false;
2351 nsAutoCString list;
2352 Preferences::GetCString(kExemptedDomainsPrefName, list);
2353 ToLowerCase(list);
2354 isExemptDomain = IsURIInList(aURI, list);
2356 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2357 mozilla::LogLevel::Debug)) {
2358 nsAutoCString url;
2359 aURI->GetHost(url);
2360 LogDomainAndPrefList(kExemptedDomainsPrefName, url, isExemptDomain);
2363 return !isExemptDomain;
2366 /* static */
2367 bool nsContentUtils::ShouldResistFingerprinting(
2368 nsILoadInfo* aLoadInfo, RFPTarget aTarget /* = RFPTarget::Unknown */) {
2369 MOZ_ASSERT(aLoadInfo->GetExternalContentPolicyType() !=
2370 ExtContentPolicy::TYPE_DOCUMENT &&
2371 aLoadInfo->GetExternalContentPolicyType() !=
2372 ExtContentPolicy::TYPE_SUBDOCUMENT);
2374 // With this check, we can ensure that the prefs and target say yes, so only
2375 // an exemption would cause us to return false.
2376 if (!ShouldResistFingerprinting("Positive return check", aTarget)) {
2377 return false;
2380 if (CookieJarSettingsSaysShouldResistFingerprinting(aLoadInfo)) {
2381 return true;
2384 // Because this function is only used for subresource loads, this
2385 // will check the parent's principal
2386 nsIPrincipal* principal = aLoadInfo->GetLoadingPrincipal();
2388 MOZ_ASSERT_IF(principal && !principal->IsSystemPrincipal(),
2389 BasePrincipal::Cast(principal)->OriginAttributesRef() ==
2390 aLoadInfo->GetOriginAttributes());
2391 return ShouldResistFingerprinting_dangerous(principal, "Internal Call",
2392 aTarget);
2395 /* static */
2396 bool nsContentUtils::ShouldResistFingerprinting_dangerous(
2397 nsIPrincipal* aPrincipal, const char* aJustification,
2398 RFPTarget aTarget /* = RFPTarget::Unknown */) {
2399 if (!aPrincipal) {
2400 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2401 ("Called nsContentUtils::ShouldResistFingerprinting(nsILoadInfo* "
2402 "aChannel) but the loadinfo's loadingprincipal was NULL"));
2403 return ShouldResistFingerprinting("Null object", aTarget);
2406 // With this check, we can ensure that the prefs and target say yes, so only
2407 // an exemption would cause us to return false.
2408 if (!ShouldResistFingerprinting("Positive return check", aTarget)) {
2409 return false;
2412 if (aPrincipal->IsSystemPrincipal()) {
2413 return false;
2416 auto originAttributes =
2417 BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
2418 if (!StaticPrefs::privacy_resistFingerprinting_DoNotUseDirectly() &&
2419 !StaticPrefs::privacy_fingerprintingProtection_DoNotUseDirectly()) {
2420 // If neither of the 'regular' RFP prefs are set, then one (or both)
2421 // of the PBM-Only prefs are set (or we would have failed the
2422 // Positive return check.) Therefore, if we are not in PBM, return false
2423 if (originAttributes.mPrivateBrowsingId == 0) {
2424 return false;
2428 // Exclude internal schemes
2429 if (aPrincipal->SchemeIs("about") || aPrincipal->SchemeIs("chrome") ||
2430 aPrincipal->SchemeIs("resource") || aPrincipal->SchemeIs("view-source")) {
2431 return false;
2434 // Web extension principals are also excluded
2435 if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
2436 return false;
2439 bool isExemptDomain = false;
2440 aPrincipal->IsURIInPrefList(kExemptedDomainsPrefName, &isExemptDomain);
2442 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2443 mozilla::LogLevel::Debug)) {
2444 nsAutoCString origin;
2445 aPrincipal->GetAsciiOrigin(origin);
2446 LogDomainAndPrefList(kExemptedDomainsPrefName, origin, isExemptDomain);
2449 // If we've gotten here we have (probably) passed the CookieJarSettings
2450 // check that would tell us that if we _are_ a subdocument, then we are on
2451 // an exempted top-level domain and we should see if we ourselves are
2452 // exempted. But we may have gotten here because we directly called the
2453 // _dangerous function and we haven't done that check, but we _were_
2454 // instatiated from a state where we could have been partitioned.
2455 // So perform this last-ditch check for that scenario.
2456 // We arbitrarily use https as the scheme, but it doesn't matter.
2457 nsCOMPtr<nsIURI> uri;
2458 nsresult rv;
2459 if (isExemptDomain && StaticPrefs::privacy_firstparty_isolate() &&
2460 !originAttributes.mFirstPartyDomain.IsEmpty()) {
2461 rv = NS_NewURI(getter_AddRefs(uri),
2462 u"https://"_ns + originAttributes.mFirstPartyDomain);
2463 if (!NS_FAILED(rv)) {
2464 isExemptDomain =
2465 nsContentUtils::IsURIInPrefList(uri, kExemptedDomainsPrefName);
2467 } else if (isExemptDomain && !originAttributes.mPartitionKey.IsEmpty()) {
2468 rv = NS_NewURI(getter_AddRefs(uri),
2469 u"https://"_ns + originAttributes.mPartitionKey);
2470 if (!NS_FAILED(rv)) {
2471 isExemptDomain =
2472 nsContentUtils::IsURIInPrefList(uri, kExemptedDomainsPrefName);
2476 return !isExemptDomain;
2479 /* static */
2480 void nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
2481 int32_t aChromeWidth, int32_t aChromeHeight, int32_t aScreenWidth,
2482 int32_t aScreenHeight, int32_t aInputWidth, int32_t aInputHeight,
2483 bool aSetOuterWidth, bool aSetOuterHeight, int32_t* aOutputWidth,
2484 int32_t* aOutputHeight) {
2485 MOZ_ASSERT(aOutputWidth);
2486 MOZ_ASSERT(aOutputHeight);
2488 int32_t availContentWidth = 0;
2489 int32_t availContentHeight = 0;
2491 availContentWidth = std::min(StaticPrefs::privacy_window_maxInnerWidth(),
2492 aScreenWidth - aChromeWidth);
2493 #ifdef MOZ_WIDGET_GTK
2494 // In the GTK window, it will not report outside system decorations
2495 // when we get available window size, see Bug 581863. So, we leave a
2496 // 40 pixels space for them when calculating the available content
2497 // height. It is not necessary for the width since the content width
2498 // is usually pretty much the same as the chrome width.
2499 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2500 (-40 + aScreenHeight) - aChromeHeight);
2501 #else
2502 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2503 aScreenHeight - aChromeHeight);
2504 #endif
2506 // Ideally, we'd like to round window size to 1000x1000, but the
2507 // screen space could be too small to accommodate this size in some
2508 // cases. If it happens, we would round the window size to the nearest
2509 // 200x100.
2510 availContentWidth = availContentWidth - (availContentWidth % 200);
2511 availContentHeight = availContentHeight - (availContentHeight % 100);
2513 // If aIsOuter is true, we are setting the outer window. So we
2514 // have to consider the chrome UI.
2515 int32_t chromeOffsetWidth = aSetOuterWidth ? aChromeWidth : 0;
2516 int32_t chromeOffsetHeight = aSetOuterHeight ? aChromeHeight : 0;
2517 int32_t resultWidth = 0, resultHeight = 0;
2519 // if the original size is greater than the maximum available size, we set
2520 // it to the maximum size. And if the original value is less than the
2521 // minimum rounded size, we set it to the minimum 200x100.
2522 if (aInputWidth > (availContentWidth + chromeOffsetWidth)) {
2523 resultWidth = availContentWidth + chromeOffsetWidth;
2524 } else if (aInputWidth < (200 + chromeOffsetWidth)) {
2525 resultWidth = 200 + chromeOffsetWidth;
2526 } else {
2527 // Otherwise, we round the window to the nearest upper rounded 200x100.
2528 resultWidth = NSToIntCeil((aInputWidth - chromeOffsetWidth) / 200.0) * 200 +
2529 chromeOffsetWidth;
2532 if (aInputHeight > (availContentHeight + chromeOffsetHeight)) {
2533 resultHeight = availContentHeight + chromeOffsetHeight;
2534 } else if (aInputHeight < (100 + chromeOffsetHeight)) {
2535 resultHeight = 100 + chromeOffsetHeight;
2536 } else {
2537 resultHeight =
2538 NSToIntCeil((aInputHeight - chromeOffsetHeight) / 100.0) * 100 +
2539 chromeOffsetHeight;
2542 *aOutputWidth = resultWidth;
2543 *aOutputHeight = resultHeight;
2546 bool nsContentUtils::ThreadsafeIsCallerChrome() {
2547 return NS_IsMainThread() ? IsCallerChrome()
2548 : IsCurrentThreadRunningChromeWorker();
2551 bool nsContentUtils::IsCallerUAWidget() {
2552 JSContext* cx = GetCurrentJSContext();
2553 if (!cx) {
2554 return false;
2557 JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
2558 if (!realm) {
2559 return false;
2562 return xpc::IsUAWidgetScope(realm);
2565 bool nsContentUtils::IsSystemCaller(JSContext* aCx) {
2566 // Note that SubjectPrincipal() assumes we are in a compartment here.
2567 return SubjectPrincipal(aCx) == sSystemPrincipal;
2570 bool nsContentUtils::ThreadsafeIsSystemCaller(JSContext* aCx) {
2571 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
2572 MOZ_ASSERT(ccjscx->Context() == aCx);
2574 return ccjscx->IsSystemCaller();
2577 // static
2578 bool nsContentUtils::LookupBindingMember(
2579 JSContext* aCx, nsIContent* aContent, JS::Handle<jsid> aId,
2580 JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
2581 return true;
2584 nsINode* nsContentUtils::GetNearestInProcessCrossDocParentNode(
2585 nsINode* aChild) {
2586 if (aChild->IsDocument()) {
2587 for (BrowsingContext* bc = aChild->AsDocument()->GetBrowsingContext(); bc;
2588 bc = bc->GetParent()) {
2589 if (bc->GetEmbedderElement()) {
2590 return bc->GetEmbedderElement();
2593 return nullptr;
2596 nsINode* parent = aChild->GetParentNode();
2597 if (parent && parent->IsContent() && aChild->IsContent()) {
2598 parent = aChild->AsContent()->GetFlattenedTreeParent();
2601 return parent;
2604 bool nsContentUtils::ContentIsHostIncludingDescendantOf(
2605 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2606 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2607 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2609 do {
2610 if (aPossibleDescendant == aPossibleAncestor) return true;
2611 if (aPossibleDescendant->IsDocumentFragment()) {
2612 aPossibleDescendant =
2613 aPossibleDescendant->AsDocumentFragment()->GetHost();
2614 } else {
2615 aPossibleDescendant = aPossibleDescendant->GetParentNode();
2617 } while (aPossibleDescendant);
2619 return false;
2622 // static
2623 bool nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
2624 nsINode* aPossibleAncestor) {
2625 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2626 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2628 do {
2629 if (aPossibleDescendant == aPossibleAncestor) {
2630 return true;
2633 aPossibleDescendant =
2634 GetNearestInProcessCrossDocParentNode(aPossibleDescendant);
2635 } while (aPossibleDescendant);
2637 return false;
2640 // static
2641 bool nsContentUtils::ContentIsFlattenedTreeDescendantOf(
2642 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2643 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2644 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2646 do {
2647 if (aPossibleDescendant == aPossibleAncestor) {
2648 return true;
2650 aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
2651 } while (aPossibleDescendant);
2653 return false;
2656 // static
2657 bool nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
2658 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2659 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2660 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2662 do {
2663 if (aPossibleDescendant == aPossibleAncestor) {
2664 return true;
2666 aPossibleDescendant =
2667 aPossibleDescendant->GetFlattenedTreeParentNodeForStyle();
2668 } while (aPossibleDescendant);
2670 return false;
2673 // static
2674 nsINode* nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB) {
2675 while (true && aTargetA) {
2676 // If A's root is not a shadow root...
2677 nsINode* root = aTargetA->SubtreeRoot();
2678 if (!root->IsShadowRoot()) {
2679 // ...then return A.
2680 return aTargetA;
2683 // or A's root is a shadow-including inclusive ancestor of B...
2684 if (aTargetB->IsShadowIncludingInclusiveDescendantOf(root)) {
2685 // ...then return A.
2686 return aTargetA;
2689 aTargetA = ShadowRoot::FromNode(root)->GetHost();
2692 return nullptr;
2695 // static
2696 Element* nsContentUtils::GetAnElementForTiming(Element* aTarget,
2697 const Document* aDocument,
2698 nsIGlobalObject* aGlobal) {
2699 if (!aTarget->IsInComposedDoc()) {
2700 return nullptr;
2703 if (!aDocument) {
2704 nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(aGlobal);
2705 if (!inner) {
2706 return nullptr;
2708 aDocument = inner->GetExtantDoc();
2711 MOZ_ASSERT(aDocument);
2713 if (aTarget->GetUncomposedDocOrConnectedShadowRoot() != aDocument ||
2714 !aDocument->IsCurrentActiveDocument()) {
2715 return nullptr;
2718 return aTarget;
2721 // static
2722 nsresult nsContentUtils::GetInclusiveAncestors(nsINode* aNode,
2723 nsTArray<nsINode*>& aArray) {
2724 while (aNode) {
2725 aArray.AppendElement(aNode);
2726 aNode = aNode->GetParentNode();
2728 return NS_OK;
2731 // static
2732 nsresult nsContentUtils::GetInclusiveAncestorsAndOffsets(
2733 nsINode* aNode, uint32_t aOffset, nsTArray<nsIContent*>* aAncestorNodes,
2734 nsTArray<Maybe<uint32_t>>* aAncestorOffsets) {
2735 NS_ENSURE_ARG_POINTER(aNode);
2737 if (!aNode->IsContent()) {
2738 return NS_ERROR_FAILURE;
2740 nsIContent* content = aNode->AsContent();
2742 if (!aAncestorNodes->IsEmpty()) {
2743 NS_WARNING("aAncestorNodes is not empty");
2744 aAncestorNodes->Clear();
2747 if (!aAncestorOffsets->IsEmpty()) {
2748 NS_WARNING("aAncestorOffsets is not empty");
2749 aAncestorOffsets->Clear();
2752 // insert the node itself
2753 aAncestorNodes->AppendElement(content);
2754 aAncestorOffsets->AppendElement(Some(aOffset));
2756 // insert all the ancestors
2757 nsIContent* child = content;
2758 nsIContent* parent = child->GetParent();
2759 while (parent) {
2760 aAncestorNodes->AppendElement(parent);
2761 aAncestorOffsets->AppendElement(parent->ComputeIndexOf(child));
2762 child = parent;
2763 parent = parent->GetParent();
2766 return NS_OK;
2769 template <typename Node, typename GetParentFunc>
2770 static Node* GetCommonAncestorInternal(Node* aNode1, Node* aNode2,
2771 GetParentFunc aGetParentFunc) {
2772 MOZ_ASSERT(aNode1 != aNode2);
2774 // Build the chain of parents
2775 AutoTArray<Node*, 30> parents1, parents2;
2776 do {
2777 parents1.AppendElement(aNode1);
2778 aNode1 = aGetParentFunc(aNode1);
2779 } while (aNode1);
2780 do {
2781 parents2.AppendElement(aNode2);
2782 aNode2 = aGetParentFunc(aNode2);
2783 } while (aNode2);
2785 // Find where the parent chain differs
2786 uint32_t pos1 = parents1.Length();
2787 uint32_t pos2 = parents2.Length();
2788 Node* parent = nullptr;
2789 uint32_t len;
2790 for (len = std::min(pos1, pos2); len > 0; --len) {
2791 Node* child1 = parents1.ElementAt(--pos1);
2792 Node* child2 = parents2.ElementAt(--pos2);
2793 if (child1 != child2) {
2794 break;
2796 parent = child1;
2799 return parent;
2802 /* static */
2803 nsINode* nsContentUtils::GetCommonAncestorHelper(nsINode* aNode1,
2804 nsINode* aNode2) {
2805 return GetCommonAncestorInternal(
2806 aNode1, aNode2, [](nsINode* aNode) { return aNode->GetParentNode(); });
2809 /* static */
2810 nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorHelper(
2811 nsIContent* aContent1, nsIContent* aContent2) {
2812 return GetCommonAncestorInternal(
2813 aContent1, aContent2,
2814 [](nsIContent* aContent) { return aContent->GetFlattenedTreeParent(); });
2817 /* static */
2818 Element* nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
2819 Element* aElement1, Element* aElement2) {
2820 return GetCommonAncestorInternal(aElement1, aElement2, [](Element* aElement) {
2821 return aElement->GetFlattenedTreeParentElementForStyle();
2825 /* static */
2826 bool nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2,
2827 Maybe<uint32_t>* aNode1Index,
2828 Maybe<uint32_t>* aNode2Index) {
2829 // Note, CompareDocumentPosition takes the latter params in different order.
2830 return (aNode2->CompareDocumentPosition(*aNode1, aNode2Index, aNode1Index) &
2831 (Node_Binding::DOCUMENT_POSITION_PRECEDING |
2832 Node_Binding::DOCUMENT_POSITION_DISCONNECTED)) ==
2833 Node_Binding::DOCUMENT_POSITION_PRECEDING;
2836 /* static */
2837 Maybe<int32_t> nsContentUtils::ComparePoints(
2838 const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
2839 uint32_t aOffset2, ComparePointsCache* aParent1Cache) {
2840 bool disconnected{false};
2842 const int32_t order = ComparePoints_Deprecated(
2843 aParent1, aOffset1, aParent2, aOffset2, &disconnected, aParent1Cache);
2844 if (disconnected) {
2845 return Nothing();
2848 return Some(order);
2851 /* static */
2852 int32_t nsContentUtils::ComparePoints_Deprecated(
2853 const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
2854 uint32_t aOffset2, bool* aDisconnected, ComparePointsCache* aParent1Cache) {
2855 if (aParent1 == aParent2) {
2856 return aOffset1 < aOffset2 ? -1 : aOffset1 > aOffset2 ? 1 : 0;
2859 AutoTArray<const nsINode*, 32> parents1, parents2;
2860 const nsINode* node1 = aParent1;
2861 const nsINode* node2 = aParent2;
2862 do {
2863 parents1.AppendElement(node1);
2864 node1 = node1->GetParentOrShadowHostNode();
2865 } while (node1);
2866 do {
2867 parents2.AppendElement(node2);
2868 node2 = node2->GetParentOrShadowHostNode();
2869 } while (node2);
2871 uint32_t pos1 = parents1.Length() - 1;
2872 uint32_t pos2 = parents2.Length() - 1;
2874 bool disconnected = parents1.ElementAt(pos1) != parents2.ElementAt(pos2);
2875 if (aDisconnected) {
2876 *aDisconnected = disconnected;
2878 if (disconnected) {
2879 NS_ASSERTION(aDisconnected, "unexpected disconnected nodes");
2880 return 1;
2883 // Find where the parent chains differ
2884 const nsINode* parent = parents1.ElementAt(pos1);
2885 uint32_t len;
2886 for (len = std::min(pos1, pos2); len > 0; --len) {
2887 const nsINode* child1 = parents1.ElementAt(--pos1);
2888 const nsINode* child2 = parents2.ElementAt(--pos2);
2889 if (child1 != child2) {
2890 if (MOZ_UNLIKELY(child1->IsShadowRoot())) {
2891 // Shadow roots come before light DOM per
2892 // https://dom.spec.whatwg.org/#concept-shadow-including-tree-order
2893 MOZ_ASSERT(!child2->IsShadowRoot(), "Two shadow roots?");
2894 return -1;
2896 if (MOZ_UNLIKELY(child2->IsShadowRoot())) {
2897 return 1;
2899 const Maybe<uint32_t> child1Index =
2900 aParent1Cache ? aParent1Cache->ComputeIndexOf(parent, child1)
2901 : parent->ComputeIndexOf(child1);
2902 const Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2);
2903 if (MOZ_LIKELY(child1Index.isSome() && child2Index.isSome())) {
2904 return *child1Index < *child2Index ? -1 : 1;
2906 // XXX Keep the odd traditional behavior for now.
2907 return child1Index.isNothing() && child2Index.isSome() ? -1 : 1;
2909 parent = child1;
2912 // The parent chains never differed, so one of the nodes is an ancestor of
2913 // the other
2915 NS_ASSERTION(!pos1 || !pos2,
2916 "should have run out of parent chain for one of the nodes");
2918 if (!pos1) {
2919 const nsINode* child2 = parents2.ElementAt(--pos2);
2920 const Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2);
2921 if (MOZ_UNLIKELY(NS_WARN_IF(child2Index.isNothing()))) {
2922 return 1;
2924 return aOffset1 <= *child2Index ? -1 : 1;
2927 const nsINode* child1 = parents1.ElementAt(--pos1);
2928 const Maybe<uint32_t> child1Index =
2929 aParent1Cache ? aParent1Cache->ComputeIndexOf(parent, child1)
2930 : parent->ComputeIndexOf(child1);
2931 if (MOZ_UNLIKELY(NS_WARN_IF(child1Index.isNothing()))) {
2932 return -1;
2934 return *child1Index < aOffset2 ? -1 : 1;
2937 // static
2938 nsINode* nsContentUtils::GetCommonAncestorUnderInteractiveContent(
2939 nsINode* aNode1, nsINode* aNode2) {
2940 if (!aNode1 || !aNode2) {
2941 return nullptr;
2944 if (aNode1 == aNode2) {
2945 return aNode1;
2948 // Build the chain of parents
2949 AutoTArray<nsINode*, 30> parents1;
2950 do {
2951 parents1.AppendElement(aNode1);
2952 if (aNode1->IsElement() &&
2953 aNode1->AsElement()->IsInteractiveHTMLContent()) {
2954 break;
2956 aNode1 = aNode1->GetFlattenedTreeParentNode();
2957 } while (aNode1);
2959 AutoTArray<nsINode*, 30> parents2;
2960 do {
2961 parents2.AppendElement(aNode2);
2962 if (aNode2->IsElement() &&
2963 aNode2->AsElement()->IsInteractiveHTMLContent()) {
2964 break;
2966 aNode2 = aNode2->GetFlattenedTreeParentNode();
2967 } while (aNode2);
2969 // Find where the parent chain differs
2970 uint32_t pos1 = parents1.Length();
2971 uint32_t pos2 = parents2.Length();
2972 nsINode* parent = nullptr;
2973 for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
2974 nsINode* child1 = parents1.ElementAt(--pos1);
2975 nsINode* child2 = parents2.ElementAt(--pos2);
2976 if (child1 != child2) {
2977 break;
2979 parent = child1;
2982 return parent;
2985 /* static */
2986 BrowserParent* nsContentUtils::GetCommonBrowserParentAncestor(
2987 BrowserParent* aBrowserParent1, BrowserParent* aBrowserParent2) {
2988 return GetCommonAncestorInternal(
2989 aBrowserParent1, aBrowserParent2, [](BrowserParent* aBrowserParent) {
2990 return aBrowserParent->GetBrowserBridgeParent()
2991 ? aBrowserParent->GetBrowserBridgeParent()->Manager()
2992 : nullptr;
2996 /* static */
2997 Element* nsContentUtils::GetTargetElement(Document* aDocument,
2998 const nsAString& aAnchorName) {
2999 MOZ_ASSERT(aDocument);
3001 if (aAnchorName.IsEmpty()) {
3002 return nullptr;
3004 // 1. If there is an element in the document tree that has an ID equal to
3005 // fragment, then return the first such element in tree order.
3006 if (Element* el = aDocument->GetElementById(aAnchorName)) {
3007 return el;
3010 // 2. If there is an a element in the document tree that has a name
3011 // attribute whose value is equal to fragment, then return the first such
3012 // element in tree order.
3014 // FIXME(emilio): Why the different code-paths for HTML and non-HTML docs?
3015 if (aDocument->IsHTMLDocument()) {
3016 nsCOMPtr<nsINodeList> list = aDocument->GetElementsByName(aAnchorName);
3017 // Loop through the named nodes looking for the first anchor
3018 uint32_t length = list->Length();
3019 for (uint32_t i = 0; i < length; i++) {
3020 nsIContent* node = list->Item(i);
3021 if (node->IsHTMLElement(nsGkAtoms::a)) {
3022 return node->AsElement();
3025 } else {
3026 constexpr auto nameSpace = u"http://www.w3.org/1999/xhtml"_ns;
3027 // Get the list of anchor elements
3028 nsCOMPtr<nsINodeList> list =
3029 aDocument->GetElementsByTagNameNS(nameSpace, u"a"_ns);
3030 // Loop through the anchors looking for the first one with the given name.
3031 for (uint32_t i = 0; true; i++) {
3032 nsIContent* node = list->Item(i);
3033 if (!node) { // End of list
3034 break;
3037 // Compare the name attribute
3038 if (node->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
3039 aAnchorName, eCaseMatters)) {
3040 return node->AsElement();
3045 // 3. Return null.
3046 return nullptr;
3049 /* static */
3050 template <typename FPT, typename FRT, typename SPT, typename SRT>
3051 Maybe<int32_t> nsContentUtils::ComparePoints(
3052 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
3053 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary) {
3054 if (!aFirstBoundary.IsSet() || !aSecondBoundary.IsSet()) {
3055 return Nothing{};
3058 bool disconnected{false};
3059 const int32_t order =
3060 ComparePoints_Deprecated(aFirstBoundary, aSecondBoundary, &disconnected);
3062 if (disconnected) {
3063 return Nothing{};
3066 return Some(order);
3069 /* static */
3070 template <typename FPT, typename FRT, typename SPT, typename SRT>
3071 int32_t nsContentUtils::ComparePoints_Deprecated(
3072 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
3073 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary, bool* aDisconnected) {
3074 if (NS_WARN_IF(!aFirstBoundary.IsSet()) ||
3075 NS_WARN_IF(!aSecondBoundary.IsSet())) {
3076 return -1;
3078 // XXX Re-implement this without calling `Offset()` as far as possible,
3079 // and the other overload should be an alias of this.
3080 return ComparePoints_Deprecated(
3081 aFirstBoundary.Container(),
3082 *aFirstBoundary.Offset(
3083 RangeBoundaryBase<FPT, FRT>::OffsetFilter::kValidOrInvalidOffsets),
3084 aSecondBoundary.Container(),
3085 *aSecondBoundary.Offset(
3086 RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOrInvalidOffsets),
3087 aDisconnected);
3090 inline bool IsCharInSet(const char* aSet, const char16_t aChar) {
3091 char16_t ch;
3092 while ((ch = *aSet)) {
3093 if (aChar == char16_t(ch)) {
3094 return true;
3096 ++aSet;
3098 return false;
3102 * This method strips leading/trailing chars, in given set, from string.
3105 // static
3106 const nsDependentSubstring nsContentUtils::TrimCharsInSet(
3107 const char* aSet, const nsAString& aValue) {
3108 nsAString::const_iterator valueCurrent, valueEnd;
3110 aValue.BeginReading(valueCurrent);
3111 aValue.EndReading(valueEnd);
3113 // Skip characters in the beginning
3114 while (valueCurrent != valueEnd) {
3115 if (!IsCharInSet(aSet, *valueCurrent)) {
3116 break;
3118 ++valueCurrent;
3121 if (valueCurrent != valueEnd) {
3122 for (;;) {
3123 --valueEnd;
3124 if (!IsCharInSet(aSet, *valueEnd)) {
3125 break;
3128 ++valueEnd; // Step beyond the last character we want in the value.
3131 // valueEnd should point to the char after the last to copy
3132 return Substring(valueCurrent, valueEnd);
3136 * This method strips leading and trailing whitespace from a string.
3139 // static
3140 template <bool IsWhitespace(char16_t)>
3141 const nsDependentSubstring nsContentUtils::TrimWhitespace(const nsAString& aStr,
3142 bool aTrimTrailing) {
3143 nsAString::const_iterator start, end;
3145 aStr.BeginReading(start);
3146 aStr.EndReading(end);
3148 // Skip whitespace characters in the beginning
3149 while (start != end && IsWhitespace(*start)) {
3150 ++start;
3153 if (aTrimTrailing) {
3154 // Skip whitespace characters in the end.
3155 while (end != start) {
3156 --end;
3158 if (!IsWhitespace(*end)) {
3159 // Step back to the last non-whitespace character.
3160 ++end;
3162 break;
3167 // Return a substring for the string w/o leading and/or trailing
3168 // whitespace
3170 return Substring(start, end);
3173 // Declaring the templates we are going to use avoid linking issues without
3174 // inlining the method. Considering there is not so much spaces checking
3175 // methods we can consider this to be better than inlining.
3176 template const nsDependentSubstring
3177 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, bool);
3178 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
3179 nsContentUtils::IsHTMLWhitespace>(const nsAString&, bool);
3180 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
3181 nsContentUtils::IsHTMLWhitespaceOrNBSP>(const nsAString&, bool);
3183 static inline void KeyAppendSep(nsACString& aKey) {
3184 if (!aKey.IsEmpty()) {
3185 aKey.Append('>');
3189 static inline void KeyAppendString(const nsAString& aString, nsACString& aKey) {
3190 KeyAppendSep(aKey);
3192 // Could escape separator here if collisions happen. > is not a legal char
3193 // for a name or type attribute, so we should be safe avoiding that extra
3194 // work.
3196 AppendUTF16toUTF8(aString, aKey);
3199 static inline void KeyAppendString(const nsACString& aString,
3200 nsACString& aKey) {
3201 KeyAppendSep(aKey);
3203 // Could escape separator here if collisions happen. > is not a legal char
3204 // for a name or type attribute, so we should be safe avoiding that extra
3205 // work.
3207 aKey.Append(aString);
3210 static inline void KeyAppendInt(int32_t aInt, nsACString& aKey) {
3211 KeyAppendSep(aKey);
3213 aKey.AppendInt(aInt);
3216 static inline bool IsAutocompleteOff(const nsIContent* aContent) {
3217 return aContent->IsElement() &&
3218 aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
3219 nsGkAtoms::autocomplete, u"off"_ns,
3220 eIgnoreCase);
3223 /*static*/
3224 void nsContentUtils::GenerateStateKey(nsIContent* aContent, Document* aDocument,
3225 nsACString& aKey) {
3226 MOZ_ASSERT(aContent);
3228 aKey.Truncate();
3230 uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
3232 // Don't capture state for anonymous content
3233 if (aContent->IsInNativeAnonymousSubtree()) {
3234 return;
3237 if (IsAutocompleteOff(aContent)) {
3238 return;
3241 RefPtr<Document> doc = aContent->GetUncomposedDoc();
3243 KeyAppendInt(partID, aKey); // first append a partID
3244 bool generatedUniqueKey = false;
3246 if (doc && doc->IsHTMLOrXHTML()) {
3247 nsHTMLDocument* htmlDoc = doc->AsHTMLDocument();
3249 // If we have a form control and can calculate form information, use that
3250 // as the key - it is more reliable than just recording position in the
3251 // DOM.
3252 // XXXbz Is it, really? We have bugs on this, I think...
3253 // Important to have a unique key, and tag/type/name may not be.
3255 // The format of the key depends on whether the control has a form,
3256 // and whether the element was parser inserted:
3258 // [Has Form, Parser Inserted]:
3259 // fp>type>FormNum>IndOfControlInForm>FormName>name
3261 // [No Form, Parser Inserted]:
3262 // dp>type>ControlNum>name
3264 // [Has Form, Not Parser Inserted]:
3265 // fn>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
3267 // [No Form, Not Parser Inserted]:
3268 // dn>type>IndOfControlInDoc>name
3270 // XXX We don't need to use index if name is there
3271 // XXXbz We don't? Why not? I don't follow.
3273 nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
3274 if (control) {
3275 // Get the control number if this was a parser inserted element from the
3276 // network.
3277 int32_t controlNumber =
3278 control->GetParserInsertedControlNumberForStateKey();
3279 bool parserInserted = controlNumber != -1;
3281 RefPtr<nsContentList> htmlForms;
3282 RefPtr<nsContentList> htmlFormControls;
3283 if (!parserInserted) {
3284 // Getting these lists is expensive, as we need to keep them up to date
3285 // as the document loads, so we avoid it if we don't need them.
3286 htmlDoc->GetFormsAndFormControls(getter_AddRefs(htmlForms),
3287 getter_AddRefs(htmlFormControls));
3290 // Append the control type
3291 KeyAppendInt(int32_t(control->ControlType()), aKey);
3293 // If in a form, add form name / index of form / index in form
3294 HTMLFormElement* formElement = control->GetForm();
3295 if (formElement) {
3296 if (IsAutocompleteOff(formElement)) {
3297 aKey.Truncate();
3298 return;
3301 // Append the form number, if this is a parser inserted control, or
3302 // the index of the form in the document otherwise.
3303 bool appendedForm = false;
3304 if (parserInserted) {
3305 MOZ_ASSERT(formElement->GetFormNumberForStateKey() != -1,
3306 "when generating a state key for a parser inserted form "
3307 "control we should have a parser inserted <form> element");
3308 KeyAppendString("fp"_ns, aKey);
3309 KeyAppendInt(formElement->GetFormNumberForStateKey(), aKey);
3310 appendedForm = true;
3311 } else {
3312 KeyAppendString("fn"_ns, aKey);
3313 int32_t index = htmlForms->IndexOf(formElement, false);
3314 if (index <= -1) {
3316 // XXX HACK this uses some state that was dumped into the document
3317 // specifically to fix bug 138892. What we are trying to do is
3318 // *guess* which form this control's state is found in, with the
3319 // highly likely guess that the highest form parsed so far is the
3320 // one. This code should not be on trunk, only branch.
3322 index = htmlDoc->GetNumFormsSynchronous() - 1;
3324 if (index > -1) {
3325 KeyAppendInt(index, aKey);
3326 appendedForm = true;
3330 if (appendedForm) {
3331 // Append the index of the control in the form
3332 int32_t index = formElement->IndexOfContent(aContent);
3334 if (index > -1) {
3335 KeyAppendInt(index, aKey);
3336 generatedUniqueKey = true;
3340 // Append the form name
3341 nsAutoString formName;
3342 formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
3343 KeyAppendString(formName, aKey);
3344 } else {
3345 // Not in a form. Append the control number, if this is a parser
3346 // inserted control, or the index of the control in the document
3347 // otherwise.
3348 if (parserInserted) {
3349 KeyAppendString("dp"_ns, aKey);
3350 KeyAppendInt(control->GetParserInsertedControlNumberForStateKey(),
3351 aKey);
3352 generatedUniqueKey = true;
3353 } else {
3354 KeyAppendString("dn"_ns, aKey);
3355 int32_t index = htmlFormControls->IndexOf(aContent, true);
3356 if (index > -1) {
3357 KeyAppendInt(index, aKey);
3358 generatedUniqueKey = true;
3362 // Append the control name
3363 nsAutoString name;
3364 aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name,
3365 name);
3366 KeyAppendString(name, aKey);
3371 if (!generatedUniqueKey) {
3372 // Either we didn't have a form control or we aren't in an HTML document so
3373 // we can't figure out form info. Append the tag name if it's an element
3374 // to avoid restoring state for one type of element on another type.
3375 if (aContent->IsElement()) {
3376 KeyAppendString(nsDependentAtomString(aContent->NodeInfo()->NameAtom()),
3377 aKey);
3378 } else {
3379 // Append a character that is not "d" or "f" to disambiguate from
3380 // the case when we were a form control in an HTML document.
3381 KeyAppendString("o"_ns, aKey);
3384 // Now start at aContent and append the indices of it and all its ancestors
3385 // in their containers. That should at least pin down its position in the
3386 // DOM...
3387 nsINode* parent = aContent->GetParentNode();
3388 nsINode* content = aContent;
3389 while (parent) {
3390 KeyAppendInt(parent->ComputeIndexOf_Deprecated(content), aKey);
3391 content = parent;
3392 parent = content->GetParentNode();
3397 // static
3398 nsIPrincipal* nsContentUtils::SubjectPrincipal(JSContext* aCx) {
3399 MOZ_ASSERT(NS_IsMainThread());
3401 // As opposed to SubjectPrincipal(), we do in fact assume that
3402 // we're in a realm here; anyone who calls this function in
3403 // situations where that's not the case is doing it wrong.
3404 JS::Realm* realm = js::GetContextRealm(aCx);
3405 MOZ_ASSERT(realm);
3407 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3408 return nsJSPrincipals::get(principals);
3411 // static
3412 nsIPrincipal* nsContentUtils::SubjectPrincipal() {
3413 MOZ_ASSERT(IsInitialized());
3414 MOZ_ASSERT(NS_IsMainThread());
3415 JSContext* cx = GetCurrentJSContext();
3416 if (!cx) {
3417 MOZ_CRASH(
3418 "Accessing the Subject Principal without an AutoJSAPI on the stack is "
3419 "forbidden");
3422 JS::Realm* realm = js::GetContextRealm(cx);
3424 // When an AutoJSAPI is instantiated, we are in a null realm until the
3425 // first JSAutoRealm, which is kind of a purgatory as far as permissions
3426 // go. It would be nice to just hard-abort if somebody does a security check
3427 // in this purgatory zone, but that would be too fragile, since it could be
3428 // triggered by random IsCallerChrome() checks 20-levels deep.
3430 // So we want to return _something_ here - and definitely not the System
3431 // Principal, since that would make an AutoJSAPI a very dangerous thing to
3432 // instantiate.
3434 // The natural thing to return is a null principal. Ideally, we'd return a
3435 // different null principal each time, to avoid any unexpected interactions
3436 // when the principal accidentally gets inherited somewhere. But
3437 // SubjectPrincipal doesn't return strong references, so there's no way to
3438 // sanely manage the lifetime of multiple null principals.
3440 // So we use a singleton null principal. To avoid it being accidentally
3441 // inherited and becoming a "real" subject or object principal, we do a
3442 // release-mode assert during realm creation against using this principal on
3443 // an actual global.
3444 if (!realm) {
3445 return sNullSubjectPrincipal;
3448 return SubjectPrincipal(cx);
3451 // static
3452 nsIPrincipal* nsContentUtils::ObjectPrincipal(JSObject* aObj) {
3453 #ifdef DEBUG
3454 JS::AssertObjectBelongsToCurrentThread(aObj);
3455 #endif
3457 MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(aObj));
3459 JS::Realm* realm = js::GetNonCCWObjectRealm(aObj);
3460 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3461 return nsJSPrincipals::get(principals);
3464 // static
3465 nsresult nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
3466 const nsAString& aSpec,
3467 Document* aDocument,
3468 nsIURI* aBaseURI) {
3469 if (aDocument) {
3470 return NS_NewURI(aResult, aSpec, aDocument->GetDocumentCharacterSet(),
3471 aBaseURI);
3473 return NS_NewURI(aResult, aSpec, nullptr, aBaseURI);
3476 // static
3477 bool nsContentUtils::IsNameWithDash(nsAtom* aName) {
3478 // A valid custom element name is a sequence of characters name which
3479 // must match the PotentialCustomElementName production:
3480 // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
3481 const char16_t* name = aName->GetUTF16String();
3482 uint32_t len = aName->GetLength();
3483 bool hasDash = false;
3485 if (!len || name[0] < 'a' || name[0] > 'z') {
3486 return false;
3489 uint32_t i = 1;
3490 while (i < len) {
3491 if (i + 1 < len && NS_IS_SURROGATE_PAIR(name[i], name[i + 1])) {
3492 // Merged two 16-bit surrogate pairs into code point.
3493 char32_t code = SURROGATE_TO_UCS4(name[i], name[i + 1]);
3495 if (code < 0x10000 || code > 0xEFFFF) {
3496 return false;
3499 i += 2;
3500 } else {
3501 if (name[i] == '-') {
3502 hasDash = true;
3505 if (name[i] != '-' && name[i] != '.' && name[i] != '_' &&
3506 name[i] != 0xB7 && (name[i] < '0' || name[i] > '9') &&
3507 (name[i] < 'a' || name[i] > 'z') &&
3508 (name[i] < 0xC0 || name[i] > 0xD6) &&
3509 (name[i] < 0xF8 || name[i] > 0x37D) &&
3510 (name[i] < 0x37F || name[i] > 0x1FFF) &&
3511 (name[i] < 0x200C || name[i] > 0x200D) &&
3512 (name[i] < 0x203F || name[i] > 0x2040) &&
3513 (name[i] < 0x2070 || name[i] > 0x218F) &&
3514 (name[i] < 0x2C00 || name[i] > 0x2FEF) &&
3515 (name[i] < 0x3001 || name[i] > 0xD7FF) &&
3516 (name[i] < 0xF900 || name[i] > 0xFDCF) &&
3517 (name[i] < 0xFDF0 || name[i] > 0xFFFD)) {
3518 return false;
3521 i++;
3525 return hasDash;
3528 // static
3529 bool nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID) {
3530 // Allow non-dashed names in XUL for XBL to Custom Element migrations.
3531 if (aNameSpaceID == kNameSpaceID_XUL) {
3532 return true;
3535 bool hasDash = IsNameWithDash(aName);
3536 if (!hasDash) {
3537 return false;
3540 // The custom element name must not be one of the following values:
3541 // annotation-xml
3542 // color-profile
3543 // font-face
3544 // font-face-src
3545 // font-face-uri
3546 // font-face-format
3547 // font-face-name
3548 // missing-glyph
3549 return aName != nsGkAtoms::annotation_xml_ &&
3550 aName != nsGkAtoms::colorProfile && aName != nsGkAtoms::font_face &&
3551 aName != nsGkAtoms::font_face_src &&
3552 aName != nsGkAtoms::font_face_uri &&
3553 aName != nsGkAtoms::font_face_format &&
3554 aName != nsGkAtoms::font_face_name && aName != nsGkAtoms::missingGlyph;
3557 // static
3558 nsresult nsContentUtils::CheckQName(const nsAString& aQualifiedName,
3559 bool aNamespaceAware,
3560 const char16_t** aColon) {
3561 const char* colon = nullptr;
3562 const char16_t* begin = aQualifiedName.BeginReading();
3563 const char16_t* end = aQualifiedName.EndReading();
3565 int result = MOZ_XMLCheckQName(reinterpret_cast<const char*>(begin),
3566 reinterpret_cast<const char*>(end),
3567 aNamespaceAware, &colon);
3569 if (!result) {
3570 if (aColon) {
3571 *aColon = reinterpret_cast<const char16_t*>(colon);
3574 return NS_OK;
3577 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
3580 // static
3581 nsresult nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
3582 const nsString& aQName, int32_t* aNamespace,
3583 nsAtom** aLocalName) {
3584 const char16_t* colon;
3585 nsresult rv = nsContentUtils::CheckQName(aQName, true, &colon);
3586 NS_ENSURE_SUCCESS(rv, rv);
3588 if (colon) {
3589 const char16_t* end;
3590 aQName.EndReading(end);
3591 nsAutoString nameSpace;
3592 rv = aNamespaceResolver->LookupNamespaceURIInternal(
3593 Substring(aQName.get(), colon), nameSpace);
3594 NS_ENSURE_SUCCESS(rv, rv);
3596 *aNamespace = nsNameSpaceManager::GetInstance()->GetNameSpaceID(
3597 nameSpace, nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
3598 if (*aNamespace == kNameSpaceID_Unknown) return NS_ERROR_FAILURE;
3600 *aLocalName = NS_AtomizeMainThread(Substring(colon + 1, end)).take();
3601 } else {
3602 *aNamespace = kNameSpaceID_None;
3603 *aLocalName = NS_AtomizeMainThread(aQName).take();
3605 NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
3606 return NS_OK;
3609 // static
3610 nsresult nsContentUtils::GetNodeInfoFromQName(
3611 const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
3612 nsNodeInfoManager* aNodeInfoManager, uint16_t aNodeType,
3613 mozilla::dom::NodeInfo** aNodeInfo) {
3614 const nsString& qName = PromiseFlatString(aQualifiedName);
3615 const char16_t* colon;
3616 nsresult rv = nsContentUtils::CheckQName(qName, true, &colon);
3617 NS_ENSURE_SUCCESS(rv, rv);
3619 int32_t nsID;
3620 nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, nsID);
3621 if (colon) {
3622 const char16_t* end;
3623 qName.EndReading(end);
3625 RefPtr<nsAtom> prefix = NS_AtomizeMainThread(Substring(qName.get(), colon));
3627 rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix, nsID,
3628 aNodeType, aNodeInfo);
3629 } else {
3630 rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID, aNodeType,
3631 aNodeInfo);
3633 NS_ENSURE_SUCCESS(rv, rv);
3635 return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
3636 (*aNodeInfo)->GetPrefixAtom(),
3637 (*aNodeInfo)->NamespaceID())
3638 ? NS_OK
3639 : NS_ERROR_DOM_NAMESPACE_ERR;
3642 // static
3643 void nsContentUtils::SplitExpatName(const char16_t* aExpatName,
3644 nsAtom** aPrefix, nsAtom** aLocalName,
3645 int32_t* aNameSpaceID) {
3647 * Expat can send the following:
3648 * localName
3649 * namespaceURI<separator>localName
3650 * namespaceURI<separator>localName<separator>prefix
3652 * and we use 0xFFFF for the <separator>.
3656 const char16_t* uriEnd = nullptr;
3657 const char16_t* nameEnd = nullptr;
3658 const char16_t* pos;
3659 for (pos = aExpatName; *pos; ++pos) {
3660 if (*pos == 0xFFFF) {
3661 if (uriEnd) {
3662 nameEnd = pos;
3663 } else {
3664 uriEnd = pos;
3669 const char16_t* nameStart;
3670 if (uriEnd) {
3671 nsNameSpaceManager::GetInstance()->RegisterNameSpace(
3672 nsDependentSubstring(aExpatName, uriEnd), *aNameSpaceID);
3674 nameStart = (uriEnd + 1);
3675 if (nameEnd) {
3676 const char16_t* prefixStart = nameEnd + 1;
3677 *aPrefix = NS_AtomizeMainThread(Substring(prefixStart, pos)).take();
3678 } else {
3679 nameEnd = pos;
3680 *aPrefix = nullptr;
3682 } else {
3683 *aNameSpaceID = kNameSpaceID_None;
3684 nameStart = aExpatName;
3685 nameEnd = pos;
3686 *aPrefix = nullptr;
3688 *aLocalName = NS_AtomizeMainThread(Substring(nameStart, nameEnd)).take();
3691 // static
3692 PresShell* nsContentUtils::GetPresShellForContent(const nsIContent* aContent) {
3693 Document* doc = aContent->GetComposedDoc();
3694 if (!doc) {
3695 return nullptr;
3697 return doc->GetPresShell();
3700 // static
3701 nsPresContext* nsContentUtils::GetContextForContent(
3702 const nsIContent* aContent) {
3703 PresShell* presShell = GetPresShellForContent(aContent);
3704 if (!presShell) {
3705 return nullptr;
3707 return presShell->GetPresContext();
3710 // static
3711 bool nsContentUtils::CanLoadImage(nsIURI* aURI, nsINode* aNode,
3712 Document* aLoadingDocument,
3713 nsIPrincipal* aLoadingPrincipal) {
3714 MOZ_ASSERT(aURI, "Must have a URI");
3715 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3716 MOZ_ASSERT(aLoadingPrincipal, "Must have a loading principal");
3718 nsresult rv;
3720 auto appType = nsIDocShell::APP_TYPE_UNKNOWN;
3723 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
3724 aLoadingDocument->GetDocShell();
3725 if (docShellTreeItem) {
3726 nsCOMPtr<nsIDocShellTreeItem> root;
3727 docShellTreeItem->GetInProcessRootTreeItem(getter_AddRefs(root));
3729 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
3731 if (docShell) {
3732 appType = docShell->GetAppType();
3737 if (appType != nsIDocShell::APP_TYPE_EDITOR) {
3738 // Editor apps get special treatment here, editors can load images
3739 // from anywhere. This allows editor to insert images from file://
3740 // into documents that are being edited.
3741 rv = sSecurityManager->CheckLoadURIWithPrincipal(
3742 aLoadingPrincipal, aURI, nsIScriptSecurityManager::ALLOW_CHROME,
3743 aLoadingDocument->InnerWindowID());
3744 if (NS_FAILED(rv)) {
3745 return false;
3749 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new mozilla::net::LoadInfo(
3750 aLoadingPrincipal,
3751 aLoadingPrincipal, // triggering principal
3752 aNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
3753 nsIContentPolicy::TYPE_INTERNAL_IMAGE);
3755 int16_t decision = nsIContentPolicy::ACCEPT;
3757 rv = NS_CheckContentLoadPolicy(aURI, secCheckLoadInfo,
3758 ""_ns, // mime guess
3759 &decision, GetContentPolicy());
3761 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(decision);
3764 // static
3765 bool nsContentUtils::IsInPrivateBrowsing(Document* aDoc) {
3766 if (!aDoc) {
3767 return false;
3770 nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
3771 if (loadGroup) {
3772 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3773 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3774 if (callbacks) {
3775 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3776 if (loadContext) {
3777 return loadContext->UsePrivateBrowsing();
3782 nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
3783 return channel && NS_UsePrivateBrowsing(channel);
3786 // static
3787 bool nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup) {
3788 if (!aLoadGroup) {
3789 return false;
3791 bool isPrivate = false;
3792 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3793 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3794 if (callbacks) {
3795 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3796 isPrivate = loadContext && loadContext->UsePrivateBrowsing();
3798 return isPrivate;
3801 // FIXME(emilio): This is (effectively) almost but not quite the same as
3802 // Document::ShouldLoadImages(), which one is right?
3803 bool nsContentUtils::DocumentInactiveForImageLoads(Document* aDocument) {
3804 if (!aDocument) {
3805 return false;
3807 if (IsChromeDoc(aDocument) || aDocument->IsResourceDoc() ||
3808 aDocument->IsStaticDocument()) {
3809 return false;
3811 nsCOMPtr<nsPIDOMWindowInner> win =
3812 do_QueryInterface(aDocument->GetScopeObject());
3813 return !win || !win->GetDocShell();
3816 imgLoader* nsContentUtils::GetImgLoaderForDocument(Document* aDoc) {
3817 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
3819 if (!aDoc) {
3820 return imgLoader::NormalLoader();
3822 bool isPrivate = IsInPrivateBrowsing(aDoc);
3823 return isPrivate ? imgLoader::PrivateBrowsingLoader()
3824 : imgLoader::NormalLoader();
3827 // static
3828 imgLoader* nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
3829 Document* aContext) {
3830 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
3832 if (!aChannel) {
3833 return imgLoader::NormalLoader();
3835 nsCOMPtr<nsILoadContext> context;
3836 NS_QueryNotificationCallbacks(aChannel, context);
3837 return context && context->UsePrivateBrowsing()
3838 ? imgLoader::PrivateBrowsingLoader()
3839 : imgLoader::NormalLoader();
3842 // static
3843 int32_t nsContentUtils::CORSModeToLoadImageFlags(mozilla::CORSMode aMode) {
3844 switch (aMode) {
3845 case CORS_ANONYMOUS:
3846 return imgILoader::LOAD_CORS_ANONYMOUS;
3847 case CORS_USE_CREDENTIALS:
3848 return imgILoader::LOAD_CORS_USE_CREDENTIALS;
3849 default:
3850 return 0;
3854 // static
3855 nsresult nsContentUtils::LoadImage(
3856 nsIURI* aURI, nsINode* aContext, Document* aLoadingDocument,
3857 nsIPrincipal* aLoadingPrincipal, uint64_t aRequestContextID,
3858 nsIReferrerInfo* aReferrerInfo, imgINotificationObserver* aObserver,
3859 int32_t aLoadFlags, const nsAString& initiatorType,
3860 imgRequestProxy** aRequest, nsContentPolicyType aContentPolicyType,
3861 bool aUseUrgentStartForChannel, bool aLinkPreload,
3862 uint64_t aEarlyHintPreloaderId) {
3863 MOZ_ASSERT(aURI, "Must have a URI");
3864 MOZ_ASSERT(aContext, "Must have a context");
3865 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3866 MOZ_ASSERT(aLoadingPrincipal, "Must have a principal");
3867 MOZ_ASSERT(aRequest, "Null out param");
3869 imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
3870 if (!imgLoader) {
3871 // nothing we can do here
3872 return NS_ERROR_FAILURE;
3875 nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
3877 nsIURI* documentURI = aLoadingDocument->GetDocumentURI();
3879 NS_ASSERTION(loadGroup || aLoadingDocument->IsSVGGlyphsDocument(),
3880 "Could not get loadgroup; onload may fire too early");
3882 // XXXbz using "documentURI" for the initialDocumentURI is not quite
3883 // right, but the best we can do here...
3884 return imgLoader->LoadImage(aURI, /* uri to load */
3885 documentURI, /* initialDocumentURI */
3886 aReferrerInfo, /* referrerInfo */
3887 aLoadingPrincipal, /* loading principal */
3888 aRequestContextID, /* request context ID */
3889 loadGroup, /* loadgroup */
3890 aObserver, /* imgINotificationObserver */
3891 aContext, /* loading context */
3892 aLoadingDocument, /* uniquification key */
3893 aLoadFlags, /* load flags */
3894 nullptr, /* cache key */
3895 aContentPolicyType, /* content policy type */
3896 initiatorType, /* the load initiator */
3897 aUseUrgentStartForChannel, /* urgent-start flag */
3898 aLinkPreload, /* <link preload> initiator */
3899 aEarlyHintPreloaderId, aRequest);
3902 // static
3903 already_AddRefed<imgIContainer> nsContentUtils::GetImageFromContent(
3904 nsIImageLoadingContent* aContent, imgIRequest** aRequest) {
3905 if (aRequest) {
3906 *aRequest = nullptr;
3909 NS_ENSURE_TRUE(aContent, nullptr);
3911 nsCOMPtr<imgIRequest> imgRequest;
3912 aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3913 getter_AddRefs(imgRequest));
3914 if (!imgRequest) {
3915 return nullptr;
3918 nsCOMPtr<imgIContainer> imgContainer;
3919 imgRequest->GetImage(getter_AddRefs(imgContainer));
3921 if (!imgContainer) {
3922 return nullptr;
3925 if (aRequest) {
3926 // If the consumer wants the request, verify it has actually loaded
3927 // successfully.
3928 uint32_t imgStatus;
3929 imgRequest->GetImageStatus(&imgStatus);
3930 if (imgStatus & imgIRequest::STATUS_FRAME_COMPLETE &&
3931 !(imgStatus & imgIRequest::STATUS_ERROR)) {
3932 imgRequest.swap(*aRequest);
3936 return imgContainer.forget();
3939 static bool IsLinkWithURI(const nsIContent& aContent) {
3940 const auto* element = Element::FromNode(aContent);
3941 if (!element || !element->IsLink()) {
3942 return false;
3944 nsCOMPtr<nsIURI> absURI = element->GetHrefURI();
3945 return !!absURI;
3948 static bool HasImageRequest(nsIContent& aContent) {
3949 nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(&aContent));
3950 if (!imageContent) {
3951 return false;
3954 nsCOMPtr<imgIRequest> imgRequest;
3955 imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3956 getter_AddRefs(imgRequest));
3958 // XXXbz It may be draggable even if the request resulted in an error. Why?
3959 // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
3960 return !!imgRequest;
3963 static Maybe<bool> DraggableOverride(const nsIContent& aContent) {
3964 if (auto* el = nsGenericHTMLElement::FromNode(aContent)) {
3965 if (el->Draggable()) {
3966 return Some(true);
3969 if (el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
3970 nsGkAtoms::_false, eIgnoreCase)) {
3971 return Some(false);
3974 if (aContent.IsSVGElement()) {
3975 return Some(false);
3977 return Nothing();
3980 // static
3981 bool nsContentUtils::ContentIsDraggable(nsIContent* aContent) {
3982 MOZ_ASSERT(aContent);
3984 if (auto draggable = DraggableOverride(*aContent)) {
3985 return *draggable;
3988 // special handling for content area image and link dragging
3989 return HasImageRequest(*aContent) || IsLinkWithURI(*aContent);
3992 // static
3993 bool nsContentUtils::IsDraggableImage(nsIContent* aContent) {
3994 MOZ_ASSERT(aContent);
3995 return HasImageRequest(*aContent) &&
3996 DraggableOverride(*aContent).valueOr(true);
3999 // static
4000 bool nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
4001 MOZ_ASSERT(aContent);
4002 return IsLinkWithURI(*aContent) && DraggableOverride(*aContent).valueOr(true);
4005 // static
4006 nsresult nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo,
4007 nsAtom* aName,
4008 mozilla::dom::NodeInfo** aResult) {
4009 nsNodeInfoManager* niMgr = aNodeInfo->NodeInfoManager();
4011 *aResult = niMgr
4012 ->GetNodeInfo(aName, nullptr, aNodeInfo->NamespaceID(),
4013 aNodeInfo->NodeType(), aNodeInfo->GetExtraName())
4014 .take();
4015 return NS_OK;
4018 static bool TestSitePerm(nsIPrincipal* aPrincipal, const nsACString& aType,
4019 uint32_t aPerm, bool aExactHostMatch) {
4020 if (!aPrincipal) {
4021 // We always deny (i.e. don't allow) the permission if we don't have a
4022 // principal.
4023 return aPerm != nsIPermissionManager::ALLOW_ACTION;
4026 nsCOMPtr<nsIPermissionManager> permMgr =
4027 components::PermissionManager::Service();
4028 NS_ENSURE_TRUE(permMgr, false);
4030 uint32_t perm;
4031 nsresult rv;
4032 if (aExactHostMatch) {
4033 rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
4034 } else {
4035 rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
4037 NS_ENSURE_SUCCESS(rv, false);
4039 return perm == aPerm;
4042 bool nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal,
4043 const nsACString& aType) {
4044 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
4045 false);
4048 bool nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal,
4049 const nsACString& aType) {
4050 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
4051 false);
4054 bool nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal,
4055 const nsACString& aType) {
4056 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
4057 true);
4060 bool nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal,
4061 const nsACString& aType) {
4062 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
4063 true);
4066 bool nsContentUtils::HasSitePerm(nsIPrincipal* aPrincipal,
4067 const nsACString& aType) {
4068 if (!aPrincipal) {
4069 return false;
4072 nsCOMPtr<nsIPermissionManager> permMgr =
4073 components::PermissionManager::Service();
4074 NS_ENSURE_TRUE(permMgr, false);
4076 uint32_t perm;
4077 nsresult rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
4078 NS_ENSURE_SUCCESS(rv, false);
4080 return perm != nsIPermissionManager::UNKNOWN_ACTION;
4083 static const char* gEventNames[] = {"event"};
4084 static const char* gSVGEventNames[] = {"evt"};
4085 // for b/w compat, the first name to onerror is still 'event', even though it
4086 // is actually the error message
4087 static const char* gOnErrorNames[] = {"event", "source", "lineno", "colno",
4088 "error"};
4090 // static
4091 void nsContentUtils::GetEventArgNames(int32_t aNameSpaceID, nsAtom* aEventName,
4092 bool aIsForWindow, uint32_t* aArgCount,
4093 const char*** aArgArray) {
4094 #define SET_EVENT_ARG_NAMES(names) \
4095 *aArgCount = sizeof(names) / sizeof(names[0]); \
4096 *aArgArray = names;
4098 // JSEventHandler is what does the arg magic for onerror, and it does
4099 // not seem to take the namespace into account. So we let onerror in all
4100 // namespaces get the 3 arg names.
4101 if (aEventName == nsGkAtoms::onerror && aIsForWindow) {
4102 SET_EVENT_ARG_NAMES(gOnErrorNames);
4103 } else if (aNameSpaceID == kNameSpaceID_SVG) {
4104 SET_EVENT_ARG_NAMES(gSVGEventNames);
4105 } else {
4106 SET_EVENT_ARG_NAMES(gEventNames);
4110 // Note: The list of content bundles in nsStringBundle.cpp should be updated
4111 // whenever entries are added or removed from this list.
4112 static const char* gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT] = {
4113 // Must line up with the enum values in |PropertiesFile| enum.
4114 "chrome://global/locale/css.properties",
4115 "chrome://global/locale/xul.properties",
4116 "chrome://global/locale/layout_errors.properties",
4117 "chrome://global/locale/layout/HtmlForm.properties",
4118 "chrome://global/locale/printing.properties",
4119 "chrome://global/locale/dom/dom.properties",
4120 "chrome://global/locale/layout/htmlparser.properties",
4121 "chrome://global/locale/svg/svg.properties",
4122 "chrome://branding/locale/brand.properties",
4123 "chrome://global/locale/commonDialogs.properties",
4124 "chrome://global/locale/mathml/mathml.properties",
4125 "chrome://global/locale/security/security.properties",
4126 "chrome://necko/locale/necko.properties",
4127 "resource://gre/res/locale/layout/HtmlForm.properties",
4128 "resource://gre/res/locale/dom/dom.properties"};
4130 /* static */
4131 nsresult nsContentUtils::EnsureStringBundle(PropertiesFile aFile) {
4132 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(),
4133 "Should not create bundles off main thread.");
4134 if (!sStringBundles[aFile]) {
4135 if (!sStringBundleService) {
4136 nsresult rv =
4137 CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
4138 NS_ENSURE_SUCCESS(rv, rv);
4140 RefPtr<nsIStringBundle> bundle;
4141 MOZ_TRY(sStringBundleService->CreateBundle(gPropertiesFiles[aFile],
4142 getter_AddRefs(bundle)));
4143 sStringBundles[aFile] = bundle.forget();
4145 return NS_OK;
4148 /* static */
4149 void nsContentUtils::AsyncPrecreateStringBundles() {
4150 // We only ever want to pre-create bundles in the parent process.
4152 // All nsContentUtils bundles are shared between the parent and child
4153 // precesses, and the shared memory regions that back them *must* be created
4154 // in the parent, and then sent to all children.
4156 // If we attempt to create a bundle in the child before its memory region is
4157 // available, we need to create a temporary non-shared bundle, and later
4158 // replace that with the shared memory copy. So attempting to pre-load in the
4159 // child is wasteful and unnecessary.
4160 MOZ_ASSERT(XRE_IsParentProcess());
4162 for (uint32_t bundleIndex = 0; bundleIndex < PropertiesFile_COUNT;
4163 ++bundleIndex) {
4164 nsresult rv = NS_DispatchToCurrentThreadQueue(
4165 NS_NewRunnableFunction("AsyncPrecreateStringBundles",
4166 [bundleIndex]() {
4167 PropertiesFile file =
4168 static_cast<PropertiesFile>(bundleIndex);
4169 EnsureStringBundle(file);
4170 nsIStringBundle* bundle = sStringBundles[file];
4171 bundle->AsyncPreload();
4173 EventQueuePriority::Idle);
4174 Unused << NS_WARN_IF(NS_FAILED(rv));
4178 /* static */
4179 bool nsContentUtils::SpoofLocaleEnglish() {
4180 // 0 - will prompt
4181 // 1 - don't spoof
4182 // 2 - spoof
4183 return StaticPrefs::privacy_spoof_english() == 2;
4186 static nsContentUtils::PropertiesFile GetMaybeSpoofedPropertiesFile(
4187 nsContentUtils::PropertiesFile aFile, const char* aKey,
4188 Document* aDocument) {
4189 // When we spoof English, use en-US properties in strings that are accessible
4190 // by content.
4191 bool spoofLocale = nsContentUtils::SpoofLocaleEnglish() &&
4192 (!aDocument || !aDocument->AllowsL10n());
4193 if (spoofLocale) {
4194 switch (aFile) {
4195 case nsContentUtils::eFORMS_PROPERTIES:
4196 return nsContentUtils::eFORMS_PROPERTIES_en_US;
4197 case nsContentUtils::eDOM_PROPERTIES:
4198 return nsContentUtils::eDOM_PROPERTIES_en_US;
4199 default:
4200 break;
4203 return aFile;
4206 /* static */
4207 nsresult nsContentUtils::GetMaybeLocalizedString(PropertiesFile aFile,
4208 const char* aKey,
4209 Document* aDocument,
4210 nsAString& aResult) {
4211 return GetLocalizedString(
4212 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aResult);
4215 /* static */
4216 nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
4217 const char* aKey,
4218 nsAString& aResult) {
4219 return FormatLocalizedString(aFile, aKey, {}, aResult);
4222 /* static */
4223 nsresult nsContentUtils::FormatMaybeLocalizedString(
4224 PropertiesFile aFile, const char* aKey, Document* aDocument,
4225 const nsTArray<nsString>& aParams, nsAString& aResult) {
4226 return FormatLocalizedString(
4227 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aParams,
4228 aResult);
4231 class FormatLocalizedStringRunnable final : public WorkerMainThreadRunnable {
4232 public:
4233 FormatLocalizedStringRunnable(WorkerPrivate* aWorkerPrivate,
4234 nsContentUtils::PropertiesFile aFile,
4235 const char* aKey,
4236 const nsTArray<nsString>& aParams,
4237 nsAString& aLocalizedString)
4238 : WorkerMainThreadRunnable(aWorkerPrivate,
4239 "FormatLocalizedStringRunnable"_ns),
4240 mFile(aFile),
4241 mKey(aKey),
4242 mParams(aParams),
4243 mLocalizedString(aLocalizedString) {
4244 MOZ_ASSERT(aWorkerPrivate);
4245 aWorkerPrivate->AssertIsOnWorkerThread();
4248 bool MainThreadRun() override {
4249 AssertIsOnMainThread();
4251 mResult = nsContentUtils::FormatLocalizedString(mFile, mKey, mParams,
4252 mLocalizedString);
4253 Unused << NS_WARN_IF(NS_FAILED(mResult));
4254 return true;
4257 nsresult GetResult() const { return mResult; }
4259 private:
4260 const nsContentUtils::PropertiesFile mFile;
4261 const char* mKey;
4262 const nsTArray<nsString>& mParams;
4263 nsresult mResult = NS_ERROR_FAILURE;
4264 nsAString& mLocalizedString;
4267 /* static */
4268 nsresult nsContentUtils::FormatLocalizedString(
4269 PropertiesFile aFile, const char* aKey, const nsTArray<nsString>& aParams,
4270 nsAString& aResult) {
4271 if (!NS_IsMainThread()) {
4272 // nsIStringBundle is thread-safe but its creation is not, and in particular
4273 // we don't create and store nsIStringBundle objects in a thread-safe way.
4275 // TODO(emilio): Maybe if we already have the right bundle created we could
4276 // just call into it, but we should make sure that Shutdown() doesn't get
4277 // called on the main thread when that happens which is a bit tricky to
4278 // prove?
4279 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
4280 if (NS_WARN_IF(!workerPrivate)) {
4281 return NS_ERROR_UNEXPECTED;
4284 auto runnable = MakeRefPtr<FormatLocalizedStringRunnable>(
4285 workerPrivate, aFile, aKey, aParams, aResult);
4287 runnable->Dispatch(Canceling, IgnoreErrors());
4288 return runnable->GetResult();
4291 MOZ_TRY(EnsureStringBundle(aFile));
4292 nsIStringBundle* bundle = sStringBundles[aFile];
4293 if (aParams.IsEmpty()) {
4294 return bundle->GetStringFromName(aKey, aResult);
4296 return bundle->FormatStringFromName(aKey, aParams, aResult);
4299 /* static */
4300 void nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
4301 const nsACString& aCategory,
4302 bool aFromPrivateWindow,
4303 bool aFromChromeContext,
4304 uint32_t aErrorFlags) {
4305 nsCOMPtr<nsIScriptError> scriptError =
4306 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
4307 if (scriptError) {
4308 nsCOMPtr<nsIConsoleService> console =
4309 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
4310 if (console && NS_SUCCEEDED(scriptError->Init(
4311 aErrorText, u""_ns, u""_ns, 0, 0, aErrorFlags, aCategory,
4312 aFromPrivateWindow, aFromChromeContext))) {
4313 console->LogMessage(scriptError);
4318 /* static */
4319 nsresult nsContentUtils::ReportToConsole(
4320 uint32_t aErrorFlags, const nsACString& aCategory,
4321 const Document* aDocument, PropertiesFile aFile, const char* aMessageName,
4322 const nsTArray<nsString>& aParams, nsIURI* aURI,
4323 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber) {
4324 nsresult rv;
4325 nsAutoString errorText;
4326 if (!aParams.IsEmpty()) {
4327 rv = FormatLocalizedString(aFile, aMessageName, aParams, errorText);
4328 } else {
4329 rv = GetLocalizedString(aFile, aMessageName, errorText);
4331 NS_ENSURE_SUCCESS(rv, rv);
4333 return ReportToConsoleNonLocalized(errorText, aErrorFlags, aCategory,
4334 aDocument, aURI, aSourceLine, aLineNumber,
4335 aColumnNumber);
4338 /* static */
4339 void nsContentUtils::ReportEmptyGetElementByIdArg(const Document* aDoc) {
4340 ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, aDoc,
4341 nsContentUtils::eDOM_PROPERTIES, "EmptyGetElementByIdParam");
4344 /* static */
4345 nsresult nsContentUtils::ReportToConsoleNonLocalized(
4346 const nsAString& aErrorText, uint32_t aErrorFlags,
4347 const nsACString& aCategory, const Document* aDocument, nsIURI* aURI,
4348 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4349 MissingErrorLocationMode aLocationMode) {
4350 uint64_t innerWindowID = 0;
4351 if (aDocument) {
4352 if (!aURI) {
4353 aURI = aDocument->GetDocumentURI();
4355 innerWindowID = aDocument->InnerWindowID();
4358 return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
4359 innerWindowID, aURI, aSourceLine,
4360 aLineNumber, aColumnNumber, aLocationMode);
4363 /* static */
4364 nsresult nsContentUtils::ReportToConsoleByWindowID(
4365 const nsAString& aErrorText, uint32_t aErrorFlags,
4366 const nsACString& aCategory, uint64_t aInnerWindowID, nsIURI* aURI,
4367 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4368 MissingErrorLocationMode aLocationMode) {
4369 nsresult rv;
4370 if (!sConsoleService) { // only need to bother null-checking here
4371 rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4372 NS_ENSURE_SUCCESS(rv, rv);
4375 nsAutoString spec;
4376 if (!aLineNumber && aLocationMode == eUSE_CALLING_LOCATION) {
4377 JSContext* cx = GetCurrentJSContext();
4378 if (cx) {
4379 nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber, &aColumnNumber);
4383 nsCOMPtr<nsIScriptError> errorObject =
4384 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
4385 NS_ENSURE_SUCCESS(rv, rv);
4387 if (!spec.IsEmpty()) {
4388 rv = errorObject->InitWithWindowID(aErrorText,
4389 spec, // file name
4390 aSourceLine, aLineNumber, aColumnNumber,
4391 aErrorFlags, aCategory, aInnerWindowID);
4392 } else {
4393 rv = errorObject->InitWithSourceURI(aErrorText, aURI, aSourceLine,
4394 aLineNumber, aColumnNumber, aErrorFlags,
4395 aCategory, aInnerWindowID);
4397 NS_ENSURE_SUCCESS(rv, rv);
4399 return sConsoleService->LogMessage(errorObject);
4402 void nsContentUtils::LogMessageToConsole(const char* aMsg) {
4403 if (!sConsoleService) { // only need to bother null-checking here
4404 CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4405 if (!sConsoleService) {
4406 return;
4409 sConsoleService->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
4412 bool nsContentUtils::IsChromeDoc(const Document* aDocument) {
4413 return aDocument && aDocument->NodePrincipal() == sSystemPrincipal;
4416 bool nsContentUtils::IsChildOfSameType(Document* aDoc) {
4417 if (BrowsingContext* bc = aDoc->GetBrowsingContext()) {
4418 return bc->GetParent();
4420 return false;
4423 bool nsContentUtils::IsPlainTextType(const nsACString& aContentType) {
4424 // NOTE: if you add a type here, add it to the CONTENTDLF_CATEGORIES
4425 // define in nsContentDLF.h as well.
4426 return aContentType.EqualsLiteral(TEXT_PLAIN) ||
4427 aContentType.EqualsLiteral(TEXT_CSS) ||
4428 aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4429 aContentType.EqualsLiteral(TEXT_VTT) ||
4430 aContentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
4431 aContentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
4432 aContentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
4433 aContentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
4434 aContentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
4435 aContentType.EqualsLiteral(APPLICATION_JSON) ||
4436 aContentType.EqualsLiteral(TEXT_JSON);
4439 bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) {
4440 // NOTE: This must be a subset of the list in IsPlainTextType().
4441 return aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4442 aContentType.EqualsLiteral(APPLICATION_JSON) ||
4443 aContentType.EqualsLiteral(TEXT_JSON) ||
4444 aContentType.EqualsLiteral(TEXT_VTT);
4447 bool nsContentUtils::IsInChromeDocshell(const Document* aDocument) {
4448 return aDocument && aDocument->IsInChromeDocShell();
4451 // static
4452 nsIContentPolicy* nsContentUtils::GetContentPolicy() {
4453 if (!sTriedToGetContentPolicy) {
4454 CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
4455 // It's OK to not have a content policy service
4456 sTriedToGetContentPolicy = true;
4459 return sContentPolicyService;
4462 // static
4463 bool nsContentUtils::IsEventAttributeName(nsAtom* aName, int32_t aType) {
4464 const char16_t* name = aName->GetUTF16String();
4465 if (name[0] != 'o' || name[1] != 'n') {
4466 return false;
4469 EventNameMapping mapping;
4470 return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
4473 // static
4474 EventMessage nsContentUtils::GetEventMessage(nsAtom* aName) {
4475 MOZ_ASSERT(NS_IsMainThread(), "sAtomEventTable is not threadsafe");
4476 if (aName) {
4477 EventNameMapping mapping;
4478 if (sAtomEventTable->Get(aName, &mapping)) {
4479 return mapping.mMessage;
4483 return eUnidentifiedEvent;
4486 // static
4487 mozilla::EventClassID nsContentUtils::GetEventClassID(const nsAString& aName) {
4488 EventNameMapping mapping;
4489 if (sStringEventTable->Get(aName, &mapping)) return mapping.mEventClassID;
4491 return eBasicEventClass;
4494 nsAtom* nsContentUtils::GetEventMessageAndAtom(
4495 const nsAString& aName, mozilla::EventClassID aEventClassID,
4496 EventMessage* aEventMessage) {
4497 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4498 EventNameMapping mapping;
4499 if (sStringEventTable->Get(aName, &mapping)) {
4500 *aEventMessage = mapping.mEventClassID == aEventClassID
4501 ? mapping.mMessage
4502 : eUnidentifiedEvent;
4503 return mapping.mAtom;
4506 // If we have cached lots of user defined event names, clear some of them.
4507 if (sUserDefinedEvents->Length() > 127) {
4508 while (sUserDefinedEvents->Length() > 64) {
4509 nsAtom* first = sUserDefinedEvents->ElementAt(0);
4510 sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
4511 sUserDefinedEvents->RemoveElementAt(0);
4515 *aEventMessage = eUnidentifiedEvent;
4516 RefPtr<nsAtom> atom = NS_AtomizeMainThread(u"on"_ns + aName);
4517 sUserDefinedEvents->AppendElement(atom);
4518 mapping.mAtom = atom;
4519 mapping.mMessage = eUnidentifiedEvent;
4520 mapping.mType = EventNameType_None;
4521 mapping.mEventClassID = eBasicEventClass;
4522 // This is a slow hashtable call, but at least we cache the result for the
4523 // following calls. Because GetEventMessageAndAtomForListener utilizes
4524 // sStringEventTable, it needs to know in which cases sStringEventTable
4525 // doesn't contain the information it needs so that it can use
4526 // sAtomEventTable instead.
4527 mapping.mMaybeSpecialSVGorSMILEvent =
4528 GetEventMessage(atom) != eUnidentifiedEvent;
4529 sStringEventTable->InsertOrUpdate(aName, mapping);
4530 return mapping.mAtom;
4533 // static
4534 EventMessage nsContentUtils::GetEventMessageAndAtomForListener(
4535 const nsAString& aName, nsAtom** aOnName) {
4536 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4538 // Because of SVG/SMIL sStringEventTable contains a subset of the event names
4539 // comparing to the sAtomEventTable. However, usually sStringEventTable
4540 // contains the information we need, so in order to reduce hashtable
4541 // lookups, start from it.
4542 EventNameMapping mapping;
4543 EventMessage msg = eUnidentifiedEvent;
4544 RefPtr<nsAtom> atom;
4545 if (sStringEventTable->Get(aName, &mapping)) {
4546 if (mapping.mMaybeSpecialSVGorSMILEvent) {
4547 // Try the atom version so that we should get the right message for
4548 // SVG/SMIL.
4549 atom = NS_AtomizeMainThread(u"on"_ns + aName);
4550 msg = GetEventMessage(atom);
4551 } else {
4552 atom = mapping.mAtom;
4553 msg = mapping.mMessage;
4555 atom.forget(aOnName);
4556 return msg;
4559 // GetEventMessageAndAtom will cache the event type for the future usage...
4560 GetEventMessageAndAtom(aName, eBasicEventClass, &msg);
4562 // ...and then call this method recursively to get the message and atom from
4563 // now updated sStringEventTable.
4564 return GetEventMessageAndAtomForListener(aName, aOnName);
4567 static nsresult GetEventAndTarget(Document* aDoc, nsISupports* aTarget,
4568 const nsAString& aEventName,
4569 CanBubble aCanBubble, Cancelable aCancelable,
4570 Composed aComposed, Trusted aTrusted,
4571 Event** aEvent, EventTarget** aTargetOut) {
4572 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4573 NS_ENSURE_TRUE(aDoc && target, NS_ERROR_INVALID_ARG);
4575 ErrorResult err;
4576 RefPtr<Event> event =
4577 aDoc->CreateEvent(u"Events"_ns, CallerType::System, err);
4578 if (NS_WARN_IF(err.Failed())) {
4579 return err.StealNSResult();
4582 event->InitEvent(aEventName, aCanBubble, aCancelable, aComposed);
4583 event->SetTrusted(aTrusted == Trusted::eYes);
4585 event->SetTarget(target);
4587 event.forget(aEvent);
4588 target.forget(aTargetOut);
4589 return NS_OK;
4592 // static
4593 nsresult nsContentUtils::DispatchTrustedEvent(
4594 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4595 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4596 bool* aDefaultAction) {
4597 MOZ_ASSERT(!aEventName.EqualsLiteral("input") &&
4598 !aEventName.EqualsLiteral("beforeinput"),
4599 "Use DispatchInputEvent() instead");
4600 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4601 aComposed, Trusted::eYes, aDefaultAction);
4604 // static
4605 nsresult nsContentUtils::DispatchUntrustedEvent(
4606 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4607 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4608 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4609 Composed::eDefault, Trusted::eNo, aDefaultAction);
4612 // static
4613 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4614 const nsAString& aEventName,
4615 CanBubble aCanBubble,
4616 Cancelable aCancelable,
4617 Composed aComposed, Trusted aTrusted,
4618 bool* aDefaultAction,
4619 ChromeOnlyDispatch aOnlyChromeDispatch) {
4620 RefPtr<Event> event;
4621 nsCOMPtr<EventTarget> target;
4622 nsresult rv = GetEventAndTarget(
4623 aDoc, aTarget, aEventName, aCanBubble, aCancelable, aComposed, aTrusted,
4624 getter_AddRefs(event), getter_AddRefs(target));
4625 NS_ENSURE_SUCCESS(rv, rv);
4626 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch =
4627 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4629 ErrorResult err;
4630 bool doDefault = target->DispatchEvent(*event, CallerType::System, err);
4631 if (aDefaultAction) {
4632 *aDefaultAction = doDefault;
4634 return err.StealNSResult();
4637 // static
4638 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4639 WidgetEvent& aEvent,
4640 EventMessage aEventMessage,
4641 CanBubble aCanBubble,
4642 Cancelable aCancelable, Trusted aTrusted,
4643 bool* aDefaultAction,
4644 ChromeOnlyDispatch aOnlyChromeDispatch) {
4645 MOZ_ASSERT_IF(aOnlyChromeDispatch == ChromeOnlyDispatch::eYes,
4646 aTrusted == Trusted::eYes);
4648 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4650 aEvent.mSpecifiedEventType = GetEventTypeFromMessage(aEventMessage);
4651 aEvent.SetDefaultComposed();
4652 aEvent.SetDefaultComposedInNativeAnonymousContent();
4654 aEvent.mFlags.mBubbles = aCanBubble == CanBubble::eYes;
4655 aEvent.mFlags.mCancelable = aCancelable == Cancelable::eYes;
4656 aEvent.mFlags.mOnlyChromeDispatch =
4657 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4659 aEvent.mTarget = target;
4661 nsEventStatus status = nsEventStatus_eIgnore;
4662 nsresult rv = EventDispatcher::DispatchDOMEvent(target, &aEvent, nullptr,
4663 nullptr, &status);
4664 if (aDefaultAction) {
4665 *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
4667 return rv;
4670 // static
4671 nsresult nsContentUtils::DispatchInputEvent(Element* aEventTarget) {
4672 return DispatchInputEvent(aEventTarget, mozilla::eEditorInput,
4673 mozilla::EditorInputType::eUnknown, nullptr,
4674 InputEventOptions());
4677 // static
4678 nsresult nsContentUtils::DispatchInputEvent(
4679 Element* aEventTargetElement, EventMessage aEventMessage,
4680 EditorInputType aEditorInputType, EditorBase* aEditorBase,
4681 InputEventOptions&& aOptions, nsEventStatus* aEventStatus /* = nullptr */) {
4682 MOZ_ASSERT(aEventMessage == eEditorInput ||
4683 aEventMessage == eEditorBeforeInput);
4685 if (NS_WARN_IF(!aEventTargetElement)) {
4686 return NS_ERROR_INVALID_ARG;
4689 // If this is called from editor, the instance should be set to aEditorBase.
4690 // Otherwise, we need to look for an editor for aEventTargetElement.
4691 // However, we don't need to do it for HTMLEditor since nobody shouldn't
4692 // dispatch "beforeinput" nor "input" event for HTMLEditor except HTMLEditor
4693 // itself.
4694 bool useInputEvent = false;
4695 if (aEditorBase) {
4696 useInputEvent = true;
4697 } else if (HTMLTextAreaElement* textAreaElement =
4698 HTMLTextAreaElement::FromNode(aEventTargetElement)) {
4699 aEditorBase = textAreaElement->GetTextEditorWithoutCreation();
4700 useInputEvent = true;
4701 } else if (HTMLInputElement* inputElement =
4702 HTMLInputElement::FromNode(aEventTargetElement)) {
4703 if (inputElement->IsInputEventTarget()) {
4704 aEditorBase = inputElement->GetTextEditorWithoutCreation();
4705 useInputEvent = true;
4708 #ifdef DEBUG
4709 else {
4710 MOZ_ASSERT(!aEventTargetElement->IsTextControlElement(),
4711 "The event target may have editor, but we've not known it yet.");
4713 #endif // #ifdef DEBUG
4715 if (!useInputEvent) {
4716 MOZ_ASSERT(aEventMessage == eEditorInput);
4717 MOZ_ASSERT(aEditorInputType == EditorInputType::eUnknown);
4718 MOZ_ASSERT(!aOptions.mNeverCancelable);
4719 // Dispatch "input" event with Event instance.
4720 WidgetEvent widgetEvent(true, eUnidentifiedEvent);
4721 widgetEvent.mSpecifiedEventType = nsGkAtoms::oninput;
4722 widgetEvent.mFlags.mCancelable = false;
4723 widgetEvent.mFlags.mComposed = true;
4724 return AsyncEventDispatcher::RunDOMEventWhenSafe(*aEventTargetElement,
4725 widgetEvent, aEventStatus);
4728 MOZ_ASSERT_IF(aEventMessage != eEditorBeforeInput,
4729 !aOptions.mNeverCancelable);
4730 MOZ_ASSERT_IF(
4731 aEventMessage == eEditorBeforeInput && aOptions.mNeverCancelable,
4732 aEditorInputType == EditorInputType::eInsertReplacementText);
4734 nsCOMPtr<nsIWidget> widget;
4735 if (aEditorBase) {
4736 widget = aEditorBase->GetWidget();
4737 if (NS_WARN_IF(!widget)) {
4738 return NS_ERROR_FAILURE;
4740 } else {
4741 Document* document = aEventTargetElement->OwnerDoc();
4742 if (NS_WARN_IF(!document)) {
4743 return NS_ERROR_FAILURE;
4745 // If we're running xpcshell tests, we fail to get presShell here.
4746 // Even in such case, we need to dispatch "input" event without widget.
4747 PresShell* presShell = document->GetPresShell();
4748 if (presShell) {
4749 nsPresContext* presContext = presShell->GetPresContext();
4750 if (NS_WARN_IF(!presContext)) {
4751 return NS_ERROR_FAILURE;
4753 widget = presContext->GetRootWidget();
4754 if (NS_WARN_IF(!widget)) {
4755 return NS_ERROR_FAILURE;
4760 // Dispatch "input" event with InputEvent instance.
4761 InternalEditorInputEvent inputEvent(true, aEventMessage, widget);
4763 inputEvent.mFlags.mCancelable =
4764 !aOptions.mNeverCancelable && aEventMessage == eEditorBeforeInput &&
4765 IsCancelableBeforeInputEvent(aEditorInputType);
4766 MOZ_ASSERT(!inputEvent.mFlags.mCancelable || aEventStatus);
4768 // If there is an editor, set isComposing to true when it has composition.
4769 // Note that EditorBase::IsIMEComposing() may return false even when we
4770 // need to set it to true.
4771 // Otherwise, i.e., editor hasn't been created for the element yet,
4772 // we should set isComposing to false since the element can never has
4773 // composition without editor.
4774 inputEvent.mIsComposing = aEditorBase && aEditorBase->GetComposition();
4776 if (!aEditorBase || aEditorBase->IsTextEditor()) {
4777 if (IsDataAvailableOnTextEditor(aEditorInputType)) {
4778 inputEvent.mData = std::move(aOptions.mData);
4779 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4780 "inputEvent.mData shouldn't be void");
4782 #ifdef DEBUG
4783 else {
4784 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4786 #endif // #ifdef DEBUG
4787 MOZ_ASSERT(
4788 aOptions.mTargetRanges.IsEmpty(),
4789 "Target ranges for <input> and <textarea> should always be empty");
4790 } else {
4791 MOZ_ASSERT(aEditorBase->IsHTMLEditor());
4792 if (IsDataAvailableOnHTMLEditor(aEditorInputType)) {
4793 inputEvent.mData = std::move(aOptions.mData);
4794 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4795 "inputEvent.mData shouldn't be void");
4796 } else {
4797 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4798 if (IsDataTransferAvailableOnHTMLEditor(aEditorInputType)) {
4799 inputEvent.mDataTransfer = std::move(aOptions.mDataTransfer);
4800 MOZ_ASSERT(inputEvent.mDataTransfer,
4801 "inputEvent.mDataTransfer shouldn't be nullptr");
4802 MOZ_ASSERT(inputEvent.mDataTransfer->IsReadOnly(),
4803 "inputEvent.mDataTransfer should be read only");
4805 #ifdef DEBUG
4806 else {
4807 MOZ_ASSERT(!inputEvent.mDataTransfer,
4808 "inputEvent.mDataTransfer should be nullptr");
4810 #endif // #ifdef DEBUG
4812 if (aEventMessage == eEditorBeforeInput &&
4813 MayHaveTargetRangesOnHTMLEditor(aEditorInputType)) {
4814 inputEvent.mTargetRanges = std::move(aOptions.mTargetRanges);
4816 #ifdef DEBUG
4817 else {
4818 MOZ_ASSERT(aOptions.mTargetRanges.IsEmpty(),
4819 "Target ranges shouldn't be set for the dispatching event");
4821 #endif // #ifdef DEBUG
4824 inputEvent.mInputType = aEditorInputType;
4826 // If we cannot dispatch an event right now, we cannot make it cancelable.
4827 if (!nsContentUtils::IsSafeToRunScript()) {
4828 NS_ASSERTION(
4829 !inputEvent.mFlags.mCancelable,
4830 "Cancelable beforeinput event dispatcher should run when it's safe");
4831 inputEvent.mFlags.mCancelable = false;
4833 return AsyncEventDispatcher::RunDOMEventWhenSafe(*aEventTargetElement,
4834 inputEvent, aEventStatus);
4837 nsresult nsContentUtils::DispatchChromeEvent(
4838 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4839 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4840 RefPtr<Event> event;
4841 nsCOMPtr<EventTarget> target;
4842 nsresult rv = GetEventAndTarget(
4843 aDoc, aTarget, aEventName, aCanBubble, aCancelable, Composed::eDefault,
4844 Trusted::eYes, getter_AddRefs(event), getter_AddRefs(target));
4845 NS_ENSURE_SUCCESS(rv, rv);
4847 NS_ASSERTION(aDoc, "GetEventAndTarget lied?");
4848 if (!aDoc->GetWindow()) return NS_ERROR_INVALID_ARG;
4850 EventTarget* piTarget = aDoc->GetWindow()->GetParentTarget();
4851 if (!piTarget) return NS_ERROR_INVALID_ARG;
4853 ErrorResult err;
4854 bool defaultActionEnabled =
4855 piTarget->DispatchEvent(*event, CallerType::System, err);
4856 if (aDefaultAction) {
4857 *aDefaultAction = defaultActionEnabled;
4859 return err.StealNSResult();
4862 void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise,
4863 CallerType aCallerType) {
4864 RefPtr<Element> target = &aFrameElement;
4865 bool defaultAction = true;
4866 if (aCanRaise) {
4867 DispatchEventOnlyToChrome(target->OwnerDoc(), target,
4868 u"framefocusrequested"_ns, CanBubble::eYes,
4869 Cancelable::eYes, &defaultAction);
4871 if (!defaultAction) {
4872 return;
4875 RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
4876 if (!fm) {
4877 return;
4880 uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
4881 if (aCanRaise) {
4882 flags |= nsIFocusManager::FLAG_RAISE;
4885 if (aCallerType == CallerType::NonSystem) {
4886 flags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
4889 fm->SetFocus(target, flags);
4892 nsresult nsContentUtils::DispatchEventOnlyToChrome(
4893 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4894 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4895 bool* aDefaultAction) {
4896 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4897 aComposed, Trusted::eYes, aDefaultAction,
4898 ChromeOnlyDispatch::eYes);
4901 /* static */
4902 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4903 const nsAtom* aId) {
4904 for (nsIContent* cur = aContent; cur; cur = cur->GetNextNode(aContent)) {
4905 if (aId == cur->GetID()) {
4906 return cur->AsElement();
4910 return nullptr;
4913 /* static */
4914 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4915 const nsAString& aId) {
4916 MOZ_ASSERT(!aId.IsEmpty(), "Will match random elements");
4918 // ID attrs are generally stored as atoms, so just atomize this up front
4919 RefPtr<nsAtom> id(NS_Atomize(aId));
4920 if (!id) {
4921 // OOM, so just bail
4922 return nullptr;
4925 return MatchElementId(aContent, id);
4928 /* static */
4929 void nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver) {
4930 nsCOMPtr<nsIObserverService> observerService =
4931 mozilla::services::GetObserverService();
4932 if (observerService) {
4933 observerService->AddObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
4934 false);
4938 /* static */
4939 void nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver) {
4940 nsCOMPtr<nsIObserverService> observerService =
4941 mozilla::services::GetObserverService();
4942 if (observerService) {
4943 observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
4947 /* static */
4948 bool nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent,
4949 int32_t aNameSpaceID, nsAtom* aName) {
4950 static AttrArray::AttrValuesArray strings[] = {nsGkAtoms::_empty, nullptr};
4951 return aContent->IsElement() &&
4952 aContent->AsElement()->FindAttrValueIn(aNameSpaceID, aName, strings,
4953 eCaseMatters) ==
4954 AttrArray::ATTR_VALUE_NO_MATCH;
4957 /* static */
4958 bool nsContentUtils::HasMutationListeners(nsINode* aNode, uint32_t aType,
4959 nsINode* aTargetForSubtreeModified) {
4960 Document* doc = aNode->OwnerDoc();
4962 // global object will be null for documents that don't have windows.
4963 nsPIDOMWindowInner* window = doc->GetInnerWindow();
4964 // This relies on EventListenerManager::AddEventListener, which sets
4965 // all mutation bits when there is a listener for DOMSubtreeModified event.
4966 if (window && !window->HasMutationListeners(aType)) {
4967 return false;
4970 if (aNode->ChromeOnlyAccess() || aNode->IsInShadowTree()) {
4971 return false;
4974 doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
4976 // If we have a window, we can check it for mutation listeners now.
4977 if (aNode->IsInUncomposedDoc()) {
4978 nsCOMPtr<EventTarget> piTarget(do_QueryInterface(window));
4979 if (piTarget) {
4980 EventListenerManager* manager = piTarget->GetExistingListenerManager();
4981 if (manager && manager->HasMutationListeners()) {
4982 return true;
4987 // If we have a window, we know a mutation listener is registered, but it
4988 // might not be in our chain. If we don't have a window, we might have a
4989 // mutation listener. Check quickly to see.
4990 while (aNode) {
4991 EventListenerManager* manager = aNode->GetExistingListenerManager();
4992 if (manager && manager->HasMutationListeners()) {
4993 return true;
4996 aNode = aNode->GetParentNode();
4999 return false;
5002 /* static */
5003 bool nsContentUtils::HasMutationListeners(Document* aDocument, uint32_t aType) {
5004 nsPIDOMWindowInner* window =
5005 aDocument ? aDocument->GetInnerWindow() : nullptr;
5007 // This relies on EventListenerManager::AddEventListener, which sets
5008 // all mutation bits when there is a listener for DOMSubtreeModified event.
5009 return !window || window->HasMutationListeners(aType);
5012 void nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent) {
5013 MOZ_ASSERT(aChild, "Missing child");
5014 MOZ_ASSERT(aChild->GetParentNode() == aParent, "Wrong parent");
5015 MOZ_ASSERT(aChild->OwnerDoc() == aParent->OwnerDoc(), "Wrong owner-doc");
5017 // Having an explicit check here since it's an easy mistake to fall into,
5018 // and there might be existing code with problems. We'd rather be safe
5019 // than fire DOMNodeRemoved in all corner cases. We also rely on it for
5020 // nsAutoScriptBlockerSuppressNodeRemoved.
5021 if (!IsSafeToRunScript()) {
5022 // This checks that IsSafeToRunScript is true since we don't want to fire
5023 // events when that is false. We can't rely on EventDispatcher to assert
5024 // this in this situation since most of the time there are no mutation
5025 // event listeners, in which case we won't even attempt to dispatch events.
5026 // However this also allows for two exceptions. First off, we don't assert
5027 // if the mutation happens to native anonymous content since we never fire
5028 // mutation events on such content anyway.
5029 // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
5030 // that is a know case when we'd normally fire a mutation event, but can't
5031 // make that safe and so we suppress it at this time. Ideally this should
5032 // go away eventually.
5033 if (!aChild->IsInNativeAnonymousSubtree() &&
5034 !sDOMNodeRemovedSuppressCount) {
5035 NS_ERROR("Want to fire DOMNodeRemoved event, but it's not safe");
5036 WarnScriptWasIgnored(aChild->OwnerDoc());
5038 return;
5042 Document* doc = aParent->OwnerDoc();
5043 if (MOZ_UNLIKELY(doc->DevToolsWatchingDOMMutations()) &&
5044 aChild->IsInComposedDoc() && !aChild->ChromeOnlyAccess()) {
5045 DispatchChromeEvent(doc, aChild, u"devtoolschildremoved"_ns,
5046 CanBubble::eNo, Cancelable::eNo);
5050 if (HasMutationListeners(aChild, NS_EVENT_BITS_MUTATION_NODEREMOVED,
5051 aParent)) {
5052 InternalMutationEvent mutation(true, eLegacyNodeRemoved);
5053 mutation.mRelatedNode = aParent;
5055 mozAutoSubtreeModified subtree(aParent->OwnerDoc(), aParent);
5056 EventDispatcher::Dispatch(aChild, nullptr, &mutation);
5060 void nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments() {
5061 if (!sEventListenerManagersHash) {
5062 return;
5065 for (auto i = sEventListenerManagersHash->Iter(); !i.Done(); i.Next()) {
5066 auto entry = static_cast<EventListenerManagerMapEntry*>(i.Get());
5067 nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
5068 if (n && n->IsInComposedDoc() &&
5069 nsCCUncollectableMarker::InGeneration(
5070 n->OwnerDoc()->GetMarkedCCGeneration())) {
5071 entry->mListenerManager->MarkForCC();
5076 /* static */
5077 void nsContentUtils::TraverseListenerManager(
5078 nsINode* aNode, nsCycleCollectionTraversalCallback& cb) {
5079 if (!sEventListenerManagersHash) {
5080 // We're already shut down, just return.
5081 return;
5084 auto entry = static_cast<EventListenerManagerMapEntry*>(
5085 sEventListenerManagersHash->Search(aNode));
5086 if (entry) {
5087 CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
5088 "[via hash] mListenerManager");
5092 EventListenerManager* nsContentUtils::GetListenerManagerForNode(
5093 nsINode* aNode) {
5094 if (!sEventListenerManagersHash) {
5095 // We're already shut down, don't bother creating an event listener
5096 // manager.
5098 return nullptr;
5101 auto entry = static_cast<EventListenerManagerMapEntry*>(
5102 sEventListenerManagersHash->Add(aNode, fallible));
5104 if (!entry) {
5105 return nullptr;
5108 if (!entry->mListenerManager) {
5109 entry->mListenerManager = new EventListenerManager(aNode);
5111 aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
5114 return entry->mListenerManager;
5117 EventListenerManager* nsContentUtils::GetExistingListenerManagerForNode(
5118 const nsINode* aNode) {
5119 if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
5120 return nullptr;
5123 if (!sEventListenerManagersHash) {
5124 // We're already shut down, don't bother creating an event listener
5125 // manager.
5127 return nullptr;
5130 auto entry = static_cast<EventListenerManagerMapEntry*>(
5131 sEventListenerManagersHash->Search(aNode));
5132 if (entry) {
5133 return entry->mListenerManager;
5136 return nullptr;
5139 void nsContentUtils::AddEntryToDOMArenaTable(nsINode* aNode,
5140 DOMArena* aDOMArena) {
5141 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
5142 MOZ_ASSERT_IF(sDOMArenaHashtable, !sDOMArenaHashtable->Contains(aNode));
5143 MOZ_ASSERT(!aNode->HasFlag(NODE_KEEPS_DOMARENA));
5144 if (!sDOMArenaHashtable) {
5145 sDOMArenaHashtable =
5146 new nsRefPtrHashtable<nsPtrHashKey<const nsINode>, dom::DOMArena>();
5148 aNode->SetFlags(NODE_KEEPS_DOMARENA);
5149 sDOMArenaHashtable->InsertOrUpdate(aNode, RefPtr<DOMArena>(aDOMArena));
5152 already_AddRefed<DOMArena> nsContentUtils::TakeEntryFromDOMArenaTable(
5153 const nsINode* aNode) {
5154 MOZ_ASSERT(sDOMArenaHashtable->Contains(aNode));
5155 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
5156 RefPtr<DOMArena> arena;
5157 sDOMArenaHashtable->Remove(aNode, getter_AddRefs(arena));
5158 return arena.forget();
5161 /* static */
5162 void nsContentUtils::RemoveListenerManager(nsINode* aNode) {
5163 if (sEventListenerManagersHash) {
5164 auto entry = static_cast<EventListenerManagerMapEntry*>(
5165 sEventListenerManagersHash->Search(aNode));
5166 if (entry) {
5167 RefPtr<EventListenerManager> listenerManager;
5168 listenerManager.swap(entry->mListenerManager);
5169 // Remove the entry and *then* do operations that could cause further
5170 // modification of sEventListenerManagersHash. See bug 334177.
5171 sEventListenerManagersHash->RawRemove(entry);
5172 if (listenerManager) {
5173 listenerManager->Disconnect();
5179 /* static */
5180 bool nsContentUtils::IsValidNodeName(nsAtom* aLocalName, nsAtom* aPrefix,
5181 int32_t aNamespaceID) {
5182 if (aNamespaceID == kNameSpaceID_Unknown) {
5183 return false;
5186 if (!aPrefix) {
5187 // If the prefix is null, then either the QName must be xmlns or the
5188 // namespace must not be XMLNS.
5189 return (aLocalName == nsGkAtoms::xmlns) ==
5190 (aNamespaceID == kNameSpaceID_XMLNS);
5193 // If the prefix is non-null then the namespace must not be null.
5194 if (aNamespaceID == kNameSpaceID_None) {
5195 return false;
5198 // If the namespace is the XMLNS namespace then the prefix must be xmlns,
5199 // but the localname must not be xmlns.
5200 if (aNamespaceID == kNameSpaceID_XMLNS) {
5201 return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
5204 // If the namespace is not the XMLNS namespace then the prefix must not be
5205 // xmlns.
5206 // If the namespace is the XML namespace then the prefix can be anything.
5207 // If the namespace is not the XML namespace then the prefix must not be xml.
5208 return aPrefix != nsGkAtoms::xmlns &&
5209 (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
5212 already_AddRefed<DocumentFragment> nsContentUtils::CreateContextualFragment(
5213 nsINode* aContextNode, const nsAString& aFragment,
5214 bool aPreventScriptExecution, ErrorResult& aRv) {
5215 if (!aContextNode) {
5216 aRv.Throw(NS_ERROR_INVALID_ARG);
5217 return nullptr;
5220 // If we don't have a document here, we can't get the right security context
5221 // for compiling event handlers... so just bail out.
5222 RefPtr<Document> document = aContextNode->OwnerDoc();
5223 bool isHTML = document->IsHTMLDocument();
5225 if (isHTML) {
5226 RefPtr<DocumentFragment> frag = new (document->NodeInfoManager())
5227 DocumentFragment(document->NodeInfoManager());
5229 Element* element = aContextNode->GetAsElementOrParentElement();
5230 if (element && !element->IsHTMLElement(nsGkAtoms::html)) {
5231 aRv = ParseFragmentHTML(
5232 aFragment, frag, element->NodeInfo()->NameAtom(),
5233 element->GetNameSpaceID(),
5234 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
5235 aPreventScriptExecution);
5236 } else {
5237 aRv = ParseFragmentHTML(
5238 aFragment, frag, nsGkAtoms::body, kNameSpaceID_XHTML,
5239 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
5240 aPreventScriptExecution);
5243 return frag.forget();
5246 AutoTArray<nsString, 32> tagStack;
5247 nsAutoString uriStr, nameStr;
5248 for (Element* element : aContextNode->InclusiveAncestorsOfType<Element>()) {
5249 nsString& tagName = *tagStack.AppendElement();
5250 // It mostly doesn't actually matter what tag name we use here: XML doesn't
5251 // have parsing that depends on the open tag stack, apart from namespace
5252 // declarations. So this whole tagStack bit is just there to get the right
5253 // namespace declarations to the XML parser. That said, the parser _is_
5254 // going to create elements with the tag names we provide here, so we need
5255 // to make sure they are not names that can trigger custom element
5256 // constructors. Just make up a name that is never going to be a valid
5257 // custom element name.
5259 // The principled way to do this would probably be to add a new FromParser
5260 // value and make sure we use it when creating the context elements, then
5261 // make sure we teach all FromParser consumers (and in particular the custom
5262 // element code) about it as needed. But right now the XML parser never
5263 // actually uses FromParser values other than NOT_FROM_PARSER, and changing
5264 // that is pretty complicated.
5265 tagName.AssignLiteral("notacustomelement");
5267 // see if we need to add xmlns declarations
5268 uint32_t count = element->GetAttrCount();
5269 bool setDefaultNamespace = false;
5270 if (count > 0) {
5271 uint32_t index;
5273 for (index = 0; index < count; index++) {
5274 const BorrowedAttrInfo info = element->GetAttrInfoAt(index);
5275 const nsAttrName* name = info.mName;
5276 if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
5277 info.mValue->ToString(uriStr);
5279 // really want something like nsXMLContentSerializer::SerializeAttr
5280 tagName.AppendLiteral(" xmlns"); // space important
5281 if (name->GetPrefix()) {
5282 tagName.Append(char16_t(':'));
5283 name->LocalName()->ToString(nameStr);
5284 tagName.Append(nameStr);
5285 } else {
5286 setDefaultNamespace = true;
5288 tagName.AppendLiteral(R"(=")");
5289 tagName.Append(uriStr);
5290 tagName.Append('"');
5295 if (!setDefaultNamespace) {
5296 mozilla::dom::NodeInfo* info = element->NodeInfo();
5297 if (!info->GetPrefixAtom() && info->NamespaceID() != kNameSpaceID_None) {
5298 // We have no namespace prefix, but have a namespace ID. Push
5299 // default namespace attr in, so that our kids will be in our
5300 // namespace.
5301 info->GetNamespaceURI(uriStr);
5302 tagName.AppendLiteral(R"( xmlns=")");
5303 tagName.Append(uriStr);
5304 tagName.Append('"');
5309 RefPtr<DocumentFragment> frag;
5310 aRv = ParseFragmentXML(aFragment, document, tagStack, aPreventScriptExecution,
5311 -1, getter_AddRefs(frag));
5312 return frag.forget();
5315 /* static */
5316 void nsContentUtils::DropFragmentParsers() {
5317 NS_IF_RELEASE(sHTMLFragmentParser);
5318 NS_IF_RELEASE(sXMLFragmentParser);
5319 NS_IF_RELEASE(sXMLFragmentSink);
5322 /* static */
5323 void nsContentUtils::XPCOMShutdown() { nsContentUtils::DropFragmentParsers(); }
5325 /* Helper function to compuate Sanitization Flags for ParseFramentHTML/XML */
5326 uint32_t computeSanitizationFlags(nsIPrincipal* aPrincipal, int32_t aFlags) {
5327 uint32_t sanitizationFlags = 0;
5328 if (aPrincipal->IsSystemPrincipal()) {
5329 if (aFlags < 0) {
5330 // if this is a chrome-privileged document and no explicit flags
5331 // were passed, then use this sanitization flags.
5332 sanitizationFlags = nsIParserUtils::SanitizerAllowStyle |
5333 nsIParserUtils::SanitizerAllowComments |
5334 nsIParserUtils::SanitizerDropForms |
5335 nsIParserUtils::SanitizerLogRemovals;
5336 } else {
5337 // if the caller explicitly passes flags, then we use those
5338 // flags but additionally drop forms.
5339 sanitizationFlags = aFlags | nsIParserUtils::SanitizerDropForms;
5341 } else if (aFlags >= 0) {
5342 // aFlags by default is -1 and is only ever non equal to -1 if the
5343 // caller of ParseFragmentHTML/ParseFragmentXML is
5344 // ParserUtils::ParseFragment(). Only in that case we should use
5345 // the sanitization flags passed within aFlags.
5346 sanitizationFlags = aFlags;
5348 return sanitizationFlags;
5351 /* static */
5352 bool AllowsUnsanitizedContentForAboutNewTab(nsIPrincipal* aPrincipal) {
5353 if (StaticPrefs::dom_about_newtab_sanitization_enabled() ||
5354 !aPrincipal->SchemeIs("about")) {
5355 return false;
5357 uint32_t aboutModuleFlags = 0;
5358 aPrincipal->GetAboutModuleFlags(&aboutModuleFlags);
5359 return aboutModuleFlags & nsIAboutModule::ALLOW_UNSANITIZED_CONTENT;
5362 /* static */
5363 nsresult nsContentUtils::ParseFragmentHTML(
5364 const nsAString& aSourceBuffer, nsIContent* aTargetNode,
5365 nsAtom* aContextLocalName, int32_t aContextNamespace, bool aQuirks,
5366 bool aPreventScriptExecution, int32_t aFlags) {
5367 AutoTimelineMarker m(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");
5369 if (nsContentUtils::sFragmentParsingActive) {
5370 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5371 return NS_ERROR_DOM_INVALID_STATE_ERR;
5373 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5374 nsContentUtils::sFragmentParsingActive = true;
5375 if (!sHTMLFragmentParser) {
5376 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5377 // Now sHTMLFragmentParser owns the object
5380 nsCOMPtr<nsIPrincipal> nodePrincipal = aTargetNode->NodePrincipal();
5382 #ifdef DEBUG
5383 // aFlags should always be -1 unless the caller of ParseFragmentHTML
5384 // is ParserUtils::ParseFragment() which is the only caller that intends
5385 // sanitization. For all other callers we need to ensure to call
5386 // AuditParsingOfHTMLXMLFragments.
5387 if (aFlags < 0) {
5388 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5389 aSourceBuffer);
5391 #endif
5393 nsIContent* target = aTargetNode;
5395 RefPtr<Document> doc = aTargetNode->OwnerDoc();
5396 RefPtr<DocumentFragment> fragment;
5397 // We sanitize if the fragment occurs in a system privileged
5398 // context, an about: page, or if there are explicit sanitization flags.
5399 // Please note that about:blank and about:srcdoc inherit the security
5400 // context from the embedding context and hence are not loaded using
5401 // an about: scheme principal.
5402 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5403 nodePrincipal->SchemeIs("about") || aFlags >= 0;
5404 if (shouldSanitize &&
5405 !AllowsUnsanitizedContentForAboutNewTab(nodePrincipal)) {
5406 if (!doc->IsLoadedAsData()) {
5407 doc = nsContentUtils::CreateInertHTMLDocument(doc);
5408 if (!doc) {
5409 return NS_ERROR_FAILURE;
5412 fragment =
5413 new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager());
5414 target = fragment;
5417 nsresult rv = sHTMLFragmentParser->ParseFragment(
5418 aSourceBuffer, target, aContextLocalName, aContextNamespace, aQuirks,
5419 aPreventScriptExecution);
5420 NS_ENSURE_SUCCESS(rv, rv);
5422 if (fragment) {
5423 uint32_t sanitizationFlags =
5424 computeSanitizationFlags(nodePrincipal, aFlags);
5425 // Don't fire mutation events for nodes removed by the sanitizer.
5426 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5427 nsTreeSanitizer sanitizer(sanitizationFlags);
5428 sanitizer.Sanitize(fragment);
5430 ErrorResult error;
5431 aTargetNode->AppendChild(*fragment, error);
5432 rv = error.StealNSResult();
5435 return rv;
5438 /* static */
5439 nsresult nsContentUtils::ParseDocumentHTML(
5440 const nsAString& aSourceBuffer, Document* aTargetDocument,
5441 bool aScriptingEnabledForNoscriptParsing) {
5442 AutoTimelineMarker m(aTargetDocument->GetDocShell(), "Parse HTML");
5444 if (nsContentUtils::sFragmentParsingActive) {
5445 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5446 return NS_ERROR_DOM_INVALID_STATE_ERR;
5448 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5449 nsContentUtils::sFragmentParsingActive = true;
5450 if (!sHTMLFragmentParser) {
5451 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5452 // Now sHTMLFragmentParser owns the object
5454 nsresult rv = sHTMLFragmentParser->ParseDocument(
5455 aSourceBuffer, aTargetDocument, aScriptingEnabledForNoscriptParsing);
5456 return rv;
5459 /* static */
5460 nsresult nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
5461 Document* aDocument,
5462 nsTArray<nsString>& aTagStack,
5463 bool aPreventScriptExecution,
5464 int32_t aFlags,
5465 DocumentFragment** aReturn) {
5466 AutoTimelineMarker m(aDocument->GetDocShell(), "Parse XML");
5468 if (nsContentUtils::sFragmentParsingActive) {
5469 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5470 return NS_ERROR_DOM_INVALID_STATE_ERR;
5472 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5473 nsContentUtils::sFragmentParsingActive = true;
5474 if (!sXMLFragmentParser) {
5475 RefPtr<nsParser> parser = new nsParser();
5476 parser.forget(&sXMLFragmentParser);
5477 // sXMLFragmentParser now owns the parser
5479 if (!sXMLFragmentSink) {
5480 NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
5481 // sXMLFragmentSink now owns the sink
5483 nsCOMPtr<nsIContentSink> contentsink = do_QueryInterface(sXMLFragmentSink);
5484 MOZ_ASSERT(contentsink, "Sink doesn't QI to nsIContentSink!");
5485 sXMLFragmentParser->SetContentSink(contentsink);
5487 RefPtr<Document> doc;
5488 nsCOMPtr<nsIPrincipal> nodePrincipal = aDocument->NodePrincipal();
5490 #ifdef DEBUG
5491 // aFlags should always be -1 unless the caller of ParseFragmentXML
5492 // is ParserUtils::ParseFragment() which is the only caller that intends
5493 // sanitization. For all other callers we need to ensure to call
5494 // AuditParsingOfHTMLXMLFragments.
5495 if (aFlags < 0) {
5496 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5497 aSourceBuffer);
5499 #endif
5501 // We sanitize if the fragment occurs in a system privileged
5502 // context, an about: page, or if there are explicit sanitization flags.
5503 // Please note that about:blank and about:srcdoc inherit the security
5504 // context from the embedding context and hence are not loaded using
5505 // an about: scheme principal.
5506 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5507 nodePrincipal->SchemeIs("about") || aFlags >= 0;
5508 if (shouldSanitize && !aDocument->IsLoadedAsData()) {
5509 doc = nsContentUtils::CreateInertXMLDocument(aDocument);
5510 } else {
5511 doc = aDocument;
5514 sXMLFragmentSink->SetTargetDocument(doc);
5515 sXMLFragmentSink->SetPreventScriptExecution(aPreventScriptExecution);
5517 nsresult rv = sXMLFragmentParser->ParseFragment(aSourceBuffer, aTagStack);
5518 if (NS_FAILED(rv)) {
5519 // Drop the fragment parser and sink that might be in an inconsistent state
5520 NS_IF_RELEASE(sXMLFragmentParser);
5521 NS_IF_RELEASE(sXMLFragmentSink);
5522 return rv;
5525 rv = sXMLFragmentSink->FinishFragmentParsing(aReturn);
5527 sXMLFragmentParser->Reset();
5528 NS_ENSURE_SUCCESS(rv, rv);
5530 if (shouldSanitize) {
5531 uint32_t sanitizationFlags =
5532 computeSanitizationFlags(nodePrincipal, aFlags);
5533 // Don't fire mutation events for nodes removed by the sanitizer.
5534 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5535 nsTreeSanitizer sanitizer(sanitizationFlags);
5536 sanitizer.Sanitize(*aReturn);
5539 return rv;
5542 /* static */
5543 nsresult nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
5544 nsAString& aResultBuffer,
5545 uint32_t aFlags,
5546 uint32_t aWrapCol) {
5547 RefPtr<Document> document = nsContentUtils::CreateInertHTMLDocument(nullptr);
5548 if (!document) {
5549 return NS_ERROR_FAILURE;
5552 nsresult rv = nsContentUtils::ParseDocumentHTML(
5553 aSourceBuffer, document,
5554 !(aFlags & nsIDocumentEncoder::OutputNoScriptContent));
5555 NS_ENSURE_SUCCESS(rv, rv);
5557 nsCOMPtr<nsIDocumentEncoder> encoder = do_createDocumentEncoder("text/plain");
5559 rv = encoder->Init(document, u"text/plain"_ns, aFlags);
5560 NS_ENSURE_SUCCESS(rv, rv);
5562 encoder->SetWrapColumn(aWrapCol);
5564 return encoder->EncodeToString(aResultBuffer);
5567 static already_AddRefed<Document> CreateInertDocument(const Document* aTemplate,
5568 DocumentFlavor aFlavor) {
5569 if (aTemplate) {
5570 bool hasHad = true;
5571 nsIScriptGlobalObject* sgo = aTemplate->GetScriptHandlingObject(hasHad);
5572 NS_ENSURE_TRUE(sgo || !hasHad, nullptr);
5574 nsCOMPtr<Document> doc;
5575 nsresult rv = NS_NewDOMDocument(
5576 getter_AddRefs(doc), u""_ns, u""_ns, nullptr,
5577 aTemplate->GetDocumentURI(), aTemplate->GetDocBaseURI(),
5578 aTemplate->NodePrincipal(), true, sgo, aFlavor);
5579 if (NS_FAILED(rv)) {
5580 return nullptr;
5582 return doc.forget();
5584 nsCOMPtr<nsIURI> uri;
5585 NS_NewURI(getter_AddRefs(uri), "about:blank"_ns);
5586 if (!uri) {
5587 return nullptr;
5590 RefPtr<NullPrincipal> nullPrincipal =
5591 NullPrincipal::CreateWithoutOriginAttributes();
5592 if (!nullPrincipal) {
5593 return nullptr;
5596 nsCOMPtr<Document> doc;
5597 nsresult rv =
5598 NS_NewDOMDocument(getter_AddRefs(doc), u""_ns, u""_ns, nullptr, uri, uri,
5599 nullPrincipal, true, nullptr, aFlavor);
5600 if (NS_FAILED(rv)) {
5601 return nullptr;
5603 return doc.forget();
5606 /* static */
5607 already_AddRefed<Document> nsContentUtils::CreateInertXMLDocument(
5608 const Document* aTemplate) {
5609 return CreateInertDocument(aTemplate, DocumentFlavorXML);
5612 /* static */
5613 already_AddRefed<Document> nsContentUtils::CreateInertHTMLDocument(
5614 const Document* aTemplate) {
5615 return CreateInertDocument(aTemplate, DocumentFlavorHTML);
5618 /* static */
5619 nsresult nsContentUtils::SetNodeTextContent(nsIContent* aContent,
5620 const nsAString& aValue,
5621 bool aTryReuse) {
5622 // Fire DOMNodeRemoved mutation events before we do anything else.
5623 nsCOMPtr<nsIContent> owningContent;
5625 // Batch possible DOMSubtreeModified events.
5626 mozAutoSubtreeModified subtree(nullptr, nullptr);
5628 // Scope firing mutation events so that we don't carry any state that
5629 // might be stale
5631 // We're relying on mozAutoSubtreeModified to keep a strong reference if
5632 // needed.
5633 Document* doc = aContent->OwnerDoc();
5635 // Optimize the common case of there being no observers
5636 if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
5637 subtree.UpdateTarget(doc, nullptr);
5638 owningContent = aContent;
5639 nsCOMPtr<nsINode> child;
5640 bool skipFirst = aTryReuse;
5641 for (child = aContent->GetFirstChild();
5642 child && child->GetParentNode() == aContent;
5643 child = child->GetNextSibling()) {
5644 if (skipFirst && child->IsText()) {
5645 skipFirst = false;
5646 continue;
5648 nsContentUtils::MaybeFireNodeRemoved(child, aContent);
5653 // Might as well stick a batch around this since we're performing several
5654 // mutations.
5655 mozAutoDocUpdate updateBatch(aContent->GetComposedDoc(), true);
5656 nsAutoMutationBatch mb;
5658 if (aTryReuse && !aValue.IsEmpty()) {
5659 // Let's remove nodes until we find a eTEXT.
5660 while (aContent->HasChildren()) {
5661 nsIContent* child = aContent->GetFirstChild();
5662 if (child->IsText()) {
5663 break;
5665 aContent->RemoveChildNode(child, true);
5668 // If we have a node, it must be a eTEXT and we reuse it.
5669 if (aContent->HasChildren()) {
5670 nsIContent* child = aContent->GetFirstChild();
5671 nsresult rv = child->AsText()->SetText(aValue, true);
5672 NS_ENSURE_SUCCESS(rv, rv);
5674 // All the following nodes, if they exist, must be deleted.
5675 while (nsIContent* nextChild = child->GetNextSibling()) {
5676 aContent->RemoveChildNode(nextChild, true);
5680 if (aContent->HasChildren()) {
5681 return NS_OK;
5683 } else {
5684 mb.Init(aContent, true, false);
5685 while (aContent->HasChildren()) {
5686 aContent->RemoveChildNode(aContent->GetFirstChild(), true);
5689 mb.RemovalDone();
5691 if (aValue.IsEmpty()) {
5692 return NS_OK;
5695 RefPtr<nsTextNode> textContent = new (aContent->NodeInfo()->NodeInfoManager())
5696 nsTextNode(aContent->NodeInfo()->NodeInfoManager());
5698 textContent->SetText(aValue, true);
5700 ErrorResult rv;
5701 aContent->AppendChildTo(textContent, true, rv);
5702 mb.NodesAdded();
5703 return rv.StealNSResult();
5706 static bool AppendNodeTextContentsRecurse(const nsINode* aNode,
5707 nsAString& aResult,
5708 const fallible_t& aFallible) {
5709 for (nsIContent* child = aNode->GetFirstChild(); child;
5710 child = child->GetNextSibling()) {
5711 if (child->IsElement()) {
5712 bool ok = AppendNodeTextContentsRecurse(child, aResult, aFallible);
5713 if (!ok) {
5714 return false;
5716 } else if (Text* text = child->GetAsText()) {
5717 bool ok = text->AppendTextTo(aResult, aFallible);
5718 if (!ok) {
5719 return false;
5724 return true;
5727 /* static */
5728 bool nsContentUtils::AppendNodeTextContent(const nsINode* aNode, bool aDeep,
5729 nsAString& aResult,
5730 const fallible_t& aFallible) {
5731 if (const Text* text = aNode->GetAsText()) {
5732 return text->AppendTextTo(aResult, aFallible);
5734 if (aDeep) {
5735 return AppendNodeTextContentsRecurse(aNode, aResult, aFallible);
5738 for (nsIContent* child = aNode->GetFirstChild(); child;
5739 child = child->GetNextSibling()) {
5740 if (Text* text = child->GetAsText()) {
5741 bool ok = text->AppendTextTo(aResult, fallible);
5742 if (!ok) {
5743 return false;
5747 return true;
5750 bool nsContentUtils::HasNonEmptyTextContent(
5751 nsINode* aNode, TextContentDiscoverMode aDiscoverMode) {
5752 for (nsIContent* child = aNode->GetFirstChild(); child;
5753 child = child->GetNextSibling()) {
5754 if (child->IsText() && child->TextLength() > 0) {
5755 return true;
5758 if (aDiscoverMode == eRecurseIntoChildren &&
5759 HasNonEmptyTextContent(child, aDiscoverMode)) {
5760 return true;
5764 return false;
5767 /* static */
5768 bool nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
5769 const nsINode* aOtherNode) {
5770 MOZ_ASSERT(aNode, "Must have a node to work with");
5771 MOZ_ASSERT(aOtherNode, "Must have a content to work with");
5773 const bool anon = aNode->IsInNativeAnonymousSubtree();
5774 if (anon != aOtherNode->IsInNativeAnonymousSubtree()) {
5775 return false;
5778 if (anon) {
5779 return aOtherNode->GetClosestNativeAnonymousSubtreeRoot() ==
5780 aNode->GetClosestNativeAnonymousSubtreeRoot();
5783 // FIXME: This doesn't deal with disconnected nodes whatsoever, but it didn't
5784 // use to either. Maybe that's fine.
5785 return aNode->GetContainingShadow() == aOtherNode->GetContainingShadow();
5788 /* static */
5789 bool nsContentUtils::IsInInteractiveHTMLContent(const Element* aElement,
5790 const Element* aStop) {
5791 const Element* element = aElement;
5792 while (element && element != aStop) {
5793 if (element->IsInteractiveHTMLContent()) {
5794 return true;
5796 element = element->GetFlattenedTreeParentElement();
5798 return false;
5801 /* static */
5802 void nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling) {
5803 IMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
5806 /* static */
5807 bool nsContentUtils::SchemeIs(nsIURI* aURI, const char* aScheme) {
5808 nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
5809 NS_ENSURE_TRUE(baseURI, false);
5810 return baseURI->SchemeIs(aScheme);
5813 bool nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal) {
5814 return aPrincipal && aPrincipal->GetIsExpandedPrincipal();
5817 bool nsContentUtils::IsSystemOrExpandedPrincipal(nsIPrincipal* aPrincipal) {
5818 return (aPrincipal && aPrincipal->IsSystemPrincipal()) ||
5819 IsExpandedPrincipal(aPrincipal);
5822 nsIPrincipal* nsContentUtils::GetSystemPrincipal() {
5823 MOZ_ASSERT(IsInitialized());
5824 return sSystemPrincipal;
5827 bool nsContentUtils::CombineResourcePrincipals(
5828 nsCOMPtr<nsIPrincipal>* aResourcePrincipal, nsIPrincipal* aExtraPrincipal) {
5829 if (!aExtraPrincipal) {
5830 return false;
5832 if (!*aResourcePrincipal) {
5833 *aResourcePrincipal = aExtraPrincipal;
5834 return true;
5836 if (*aResourcePrincipal == aExtraPrincipal) {
5837 return false;
5839 bool subsumes;
5840 if (NS_SUCCEEDED(
5841 (*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
5842 subsumes) {
5843 return false;
5845 *aResourcePrincipal = sSystemPrincipal;
5846 return true;
5849 /* static */
5850 void nsContentUtils::TriggerLink(nsIContent* aContent, nsIURI* aLinkURI,
5851 const nsString& aTargetSpec, bool aClick,
5852 bool aIsTrusted) {
5853 MOZ_ASSERT(aLinkURI, "No link URI");
5855 if (aContent->IsEditable() || !aContent->OwnerDoc()->LinkHandlingEnabled()) {
5856 return;
5859 nsCOMPtr<nsIDocShell> docShell = aContent->OwnerDoc()->GetDocShell();
5860 if (!docShell) {
5861 return;
5864 if (!aClick) {
5865 nsDocShell::Cast(docShell)->OnOverLink(aContent, aLinkURI, aTargetSpec);
5866 return;
5869 // Check that this page is allowed to load this URI.
5870 nsresult proceed = NS_OK;
5872 if (sSecurityManager) {
5873 uint32_t flag = static_cast<uint32_t>(nsIScriptSecurityManager::STANDARD);
5874 proceed = sSecurityManager->CheckLoadURIWithPrincipal(
5875 aContent->NodePrincipal(), aLinkURI, flag,
5876 aContent->OwnerDoc()->InnerWindowID());
5879 // Only pass off the click event if the script security manager says it's ok.
5880 // We need to rest aTargetSpec for forced downloads.
5881 if (NS_SUCCEEDED(proceed)) {
5882 // A link/area element with a download attribute is allowed to set
5883 // a pseudo Content-Disposition header.
5884 // For security reasons we only allow websites to declare same-origin
5885 // resources as downloadable. If this check fails we will just do the normal
5886 // thing (i.e. navigate to the resource).
5887 nsAutoString fileName;
5888 if ((!aContent->IsHTMLElement(nsGkAtoms::a) &&
5889 !aContent->IsHTMLElement(nsGkAtoms::area) &&
5890 !aContent->IsSVGElement(nsGkAtoms::a)) ||
5891 !aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::download,
5892 fileName) ||
5893 NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, true))) {
5894 fileName.SetIsVoid(true); // No actionable download attribute was found.
5897 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aContent->NodePrincipal();
5898 nsCOMPtr<nsIContentSecurityPolicy> csp = aContent->GetCsp();
5900 // Sanitize fileNames containing null characters by replacing them with
5901 // underscores.
5902 if (!fileName.IsVoid()) {
5903 fileName.ReplaceChar(char16_t(0), '_');
5905 nsDocShell::Cast(docShell)->OnLinkClick(
5906 aContent, aLinkURI, fileName.IsVoid() ? aTargetSpec : u""_ns, fileName,
5907 nullptr, nullptr, UserActivation::IsHandlingUserInput(), aIsTrusted,
5908 triggeringPrincipal, csp);
5912 /* static */
5913 void nsContentUtils::GetLinkLocation(Element* aElement,
5914 nsString& aLocationString) {
5915 nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
5916 if (hrefURI) {
5917 nsAutoCString specUTF8;
5918 nsresult rv = hrefURI->GetSpec(specUTF8);
5919 if (NS_SUCCEEDED(rv)) CopyUTF8toUTF16(specUTF8, aLocationString);
5923 /* static */
5924 nsIWidget* nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget) {
5925 if (!aWidget) return nullptr;
5927 return aWidget->GetTopLevelWidget();
5930 /* static */
5931 const nsDependentString nsContentUtils::GetLocalizedEllipsis() {
5932 static char16_t sBuf[4] = {0, 0, 0, 0};
5933 if (!sBuf[0]) {
5934 if (!SpoofLocaleEnglish()) {
5935 nsAutoString tmp;
5936 Preferences::GetLocalizedString("intl.ellipsis", tmp);
5937 uint32_t len =
5938 std::min(uint32_t(tmp.Length()), uint32_t(ArrayLength(sBuf) - 1));
5939 CopyUnicodeTo(tmp, 0, sBuf, len);
5941 if (!sBuf[0]) sBuf[0] = char16_t(0x2026);
5943 return nsDependentString(sBuf);
5946 /* static */
5947 void nsContentUtils::AddScriptBlocker() {
5948 MOZ_ASSERT(NS_IsMainThread());
5949 if (!sScriptBlockerCount) {
5950 MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
5951 "Should not already have a count");
5952 sRunnersCountAtFirstBlocker =
5953 sBlockedScriptRunners ? sBlockedScriptRunners->Length() : 0;
5955 ++sScriptBlockerCount;
5958 #ifdef DEBUG
5959 static bool sRemovingScriptBlockers = false;
5960 #endif
5962 /* static */
5963 void nsContentUtils::RemoveScriptBlocker() {
5964 MOZ_ASSERT(NS_IsMainThread());
5965 MOZ_ASSERT(!sRemovingScriptBlockers);
5966 NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
5967 --sScriptBlockerCount;
5968 if (sScriptBlockerCount) {
5969 return;
5972 if (!sBlockedScriptRunners) {
5973 return;
5976 uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
5977 uint32_t lastBlocker = sBlockedScriptRunners->Length();
5978 uint32_t originalFirstBlocker = firstBlocker;
5979 uint32_t blockersCount = lastBlocker - firstBlocker;
5980 sRunnersCountAtFirstBlocker = 0;
5981 NS_ASSERTION(firstBlocker <= lastBlocker, "bad sRunnersCountAtFirstBlocker");
5983 while (firstBlocker < lastBlocker) {
5984 nsCOMPtr<nsIRunnable> runnable;
5985 runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
5986 ++firstBlocker;
5988 // Calling the runnable can reenter us
5990 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
5991 runnable->Run();
5993 // So can dropping the reference to the runnable
5994 runnable = nullptr;
5996 NS_ASSERTION(sRunnersCountAtFirstBlocker == 0, "Bad count");
5997 NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
5999 #ifdef DEBUG
6000 AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
6001 sRemovingScriptBlockers = true;
6002 #endif
6003 sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
6006 /* static */
6007 already_AddRefed<nsPIDOMWindowOuter>
6008 nsContentUtils::GetMostRecentNonPBWindow() {
6009 nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
6011 nsCOMPtr<mozIDOMWindowProxy> window;
6012 wm->GetMostRecentNonPBWindow(u"navigator:browser", getter_AddRefs(window));
6013 nsCOMPtr<nsPIDOMWindowOuter> pwindow;
6014 pwindow = do_QueryInterface(window);
6016 return pwindow.forget();
6019 /* static */
6020 void nsContentUtils::WarnScriptWasIgnored(Document* aDocument) {
6021 nsAutoString msg;
6022 bool privateBrowsing = false;
6023 bool chromeContext = false;
6025 if (aDocument) {
6026 nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
6027 if (uri) {
6028 msg.Append(NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()));
6029 msg.AppendLiteral(" : ");
6031 privateBrowsing =
6032 !!aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
6033 chromeContext = aDocument->NodePrincipal()->IsSystemPrincipal();
6036 msg.AppendLiteral(
6037 "Unable to run script because scripts are blocked internally.");
6038 LogSimpleConsoleError(msg, "DOM"_ns, privateBrowsing, chromeContext);
6041 /* static */
6042 void nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable) {
6043 nsCOMPtr<nsIRunnable> runnable = aRunnable;
6044 if (!runnable) {
6045 return;
6048 if (sScriptBlockerCount) {
6049 sBlockedScriptRunners->AppendElement(runnable.forget());
6050 return;
6053 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
6054 runnable->Run();
6057 /* static */
6058 void nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) {
6059 nsCOMPtr<nsIRunnable> runnable = aRunnable;
6060 AddScriptRunner(runnable.forget());
6063 /* static */ bool nsContentUtils::IsSafeToRunScript() {
6064 MOZ_ASSERT(NS_IsMainThread(),
6065 "This static variable only makes sense on the main thread!");
6066 return sScriptBlockerCount == 0;
6069 /* static */
6070 void nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable) {
6071 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6072 CycleCollectedJSContext::Get()->RunInStableState(std::move(aRunnable));
6075 /* static */
6076 void nsContentUtils::AddPendingIDBTransaction(
6077 already_AddRefed<nsIRunnable> aTransaction) {
6078 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6079 CycleCollectedJSContext::Get()->AddPendingIDBTransaction(
6080 std::move(aTransaction));
6083 /* static */
6084 bool nsContentUtils::IsInStableOrMetaStableState() {
6085 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6086 return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
6089 /* static */
6090 void nsContentUtils::HidePopupsInDocument(Document* aDocument) {
6091 RefPtr<nsXULPopupManager> pm = nsXULPopupManager::GetInstance();
6092 if (!pm || !aDocument) {
6093 return;
6095 nsCOMPtr<nsIDocShellTreeItem> docShellToHide = aDocument->GetDocShell();
6096 if (docShellToHide) {
6097 pm->HidePopupsInDocShell(docShellToHide);
6101 /* static */
6102 already_AddRefed<nsIDragSession> nsContentUtils::GetDragSession() {
6103 nsCOMPtr<nsIDragSession> dragSession;
6104 nsCOMPtr<nsIDragService> dragService =
6105 do_GetService("@mozilla.org/widget/dragservice;1");
6106 if (dragService) dragService->GetCurrentSession(getter_AddRefs(dragSession));
6107 return dragSession.forget();
6110 /* static */
6111 nsresult nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) {
6112 if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) {
6113 return NS_OK;
6116 // For dragstart events, the data transfer object is
6117 // created before the event fires, so it should already be set. For other
6118 // drag events, get the object from the drag session.
6119 NS_ASSERTION(aDragEvent->mMessage != eDragStart,
6120 "draggesture event created without a dataTransfer");
6122 nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
6123 NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
6125 RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
6126 if (!initialDataTransfer) {
6127 // A dataTransfer won't exist when a drag was started by some other
6128 // means, for instance calling the drag service directly, or a drag
6129 // from another application. In either case, a new dataTransfer should
6130 // be created that reflects the data.
6131 initialDataTransfer =
6132 new DataTransfer(aDragEvent->mTarget, aDragEvent->mMessage, true, -1);
6134 // now set it in the drag session so we don't need to create it again
6135 dragSession->SetDataTransfer(initialDataTransfer);
6138 bool isCrossDomainSubFrameDrop = false;
6139 if (aDragEvent->mMessage == eDrop) {
6140 isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
6143 // each event should use a clone of the original dataTransfer.
6144 initialDataTransfer->Clone(
6145 aDragEvent->mTarget, aDragEvent->mMessage, aDragEvent->mUserCancelled,
6146 isCrossDomainSubFrameDrop, getter_AddRefs(aDragEvent->mDataTransfer));
6147 if (NS_WARN_IF(!aDragEvent->mDataTransfer)) {
6148 return NS_ERROR_OUT_OF_MEMORY;
6151 // for the dragenter and dragover events, initialize the drop effect
6152 // from the drop action, which platform specific widget code sets before
6153 // the event is fired based on the keyboard state.
6154 if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
6155 uint32_t action;
6156 dragSession->GetDragAction(&action);
6157 uint32_t effectAllowed = aDragEvent->mDataTransfer->EffectAllowedInt();
6158 aDragEvent->mDataTransfer->SetDropEffectInt(
6159 FilterDropEffect(action, effectAllowed));
6160 } else if (aDragEvent->mMessage == eDrop ||
6161 aDragEvent->mMessage == eDragEnd) {
6162 // For the drop and dragend events, set the drop effect based on the
6163 // last value that the dropEffect had. This will have been set in
6164 // EventStateManager::PostHandleEvent for the last dragenter or
6165 // dragover event.
6166 aDragEvent->mDataTransfer->SetDropEffectInt(
6167 initialDataTransfer->DropEffectInt());
6170 return NS_OK;
6173 /* static */
6174 uint32_t nsContentUtils::FilterDropEffect(uint32_t aAction,
6175 uint32_t aEffectAllowed) {
6176 // It is possible for the drag action to include more than one action, but
6177 // the widget code which sets the action from the keyboard state should only
6178 // be including one. If multiple actions were set, we just consider them in
6179 // the following order:
6180 // copy, link, move
6181 if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
6182 aAction = nsIDragService::DRAGDROP_ACTION_COPY;
6183 else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
6184 aAction = nsIDragService::DRAGDROP_ACTION_LINK;
6185 else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
6186 aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
6188 // Filter the action based on the effectAllowed. If the effectAllowed
6189 // doesn't include the action, then that action cannot be done, so adjust
6190 // the action to something that is allowed. For a copy, adjust to move or
6191 // link. For a move, adjust to copy or link. For a link, adjust to move or
6192 // link. Otherwise, use none.
6193 if (aAction & aEffectAllowed ||
6194 aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
6195 return aAction;
6196 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
6197 return nsIDragService::DRAGDROP_ACTION_MOVE;
6198 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
6199 return nsIDragService::DRAGDROP_ACTION_COPY;
6200 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
6201 return nsIDragService::DRAGDROP_ACTION_LINK;
6202 return nsIDragService::DRAGDROP_ACTION_NONE;
6205 /* static */
6206 bool nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession,
6207 WidgetDragEvent* aDropEvent) {
6208 nsCOMPtr<nsIContent> target =
6209 nsIContent::FromEventTargetOrNull(aDropEvent->mOriginalTarget);
6210 if (!target) {
6211 return true;
6214 // Always allow dropping onto chrome shells.
6215 BrowsingContext* targetBC = target->OwnerDoc()->GetBrowsingContext();
6216 if (targetBC->IsChrome()) {
6217 return false;
6220 WindowContext* targetWC = target->OwnerDoc()->GetWindowContext();
6222 // If there is no source browsing context, then this is a drag from another
6223 // application, which should be allowed.
6224 RefPtr<WindowContext> sourceWC;
6225 aDragSession->GetSourceWindowContext(getter_AddRefs(sourceWC));
6226 if (sourceWC) {
6227 // Get each successive parent of the source document and compare it to
6228 // the drop document. If they match, then this is a drag from a child frame.
6229 for (sourceWC = sourceWC->GetParentWindowContext(); sourceWC;
6230 sourceWC = sourceWC->GetParentWindowContext()) {
6231 // If the source and the target match, then the drag started in a
6232 // descendant frame. If the source is discarded, err on the side of
6233 // caution and treat it as a subframe drag.
6234 if (sourceWC == targetWC || sourceWC->IsDiscarded()) {
6235 return true;
6240 return false;
6243 /* static */
6244 bool nsContentUtils::URIIsLocalFile(nsIURI* aURI) {
6245 bool isFile;
6246 nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
6248 // Important: we do NOT test the entire URI chain here!
6249 return util &&
6250 NS_SUCCEEDED(util->ProtocolHasFlags(
6251 aURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, &isFile)) &&
6252 isFile;
6255 /* static */
6256 JSContext* nsContentUtils::GetCurrentJSContext() {
6257 MOZ_ASSERT(IsInitialized());
6258 if (!IsJSAPIActive()) {
6259 return nullptr;
6261 return danger::GetJSContext();
6264 template <typename StringType, typename CharType>
6265 void _ASCIIToLowerInSitu(StringType& aStr) {
6266 CharType* iter = aStr.BeginWriting();
6267 CharType* end = aStr.EndWriting();
6268 MOZ_ASSERT(iter && end);
6270 while (iter != end) {
6271 CharType c = *iter;
6272 if (c >= 'A' && c <= 'Z') {
6273 *iter = c + ('a' - 'A');
6275 ++iter;
6279 /* static */
6280 void nsContentUtils::ASCIIToLower(nsAString& aStr) {
6281 return _ASCIIToLowerInSitu<nsAString, char16_t>(aStr);
6284 /* static */
6285 void nsContentUtils::ASCIIToLower(nsACString& aStr) {
6286 return _ASCIIToLowerInSitu<nsACString, char>(aStr);
6289 template <typename StringType, typename CharType>
6290 void _ASCIIToLowerCopy(const StringType& aSource, StringType& aDest) {
6291 uint32_t len = aSource.Length();
6292 aDest.SetLength(len);
6293 MOZ_ASSERT(aDest.Length() == len);
6295 CharType* dest = aDest.BeginWriting();
6296 MOZ_ASSERT(dest);
6298 const CharType* iter = aSource.BeginReading();
6299 const CharType* end = aSource.EndReading();
6300 while (iter != end) {
6301 CharType c = *iter;
6302 *dest = (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
6303 ++iter;
6304 ++dest;
6308 /* static */
6309 void nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest) {
6310 return _ASCIIToLowerCopy<nsAString, char16_t>(aSource, aDest);
6313 /* static */
6314 void nsContentUtils::ASCIIToLower(const nsACString& aSource,
6315 nsACString& aDest) {
6316 return _ASCIIToLowerCopy<nsACString, char>(aSource, aDest);
6319 template <typename StringType, typename CharType>
6320 void _ASCIIToUpperInSitu(StringType& aStr) {
6321 CharType* iter = aStr.BeginWriting();
6322 CharType* end = aStr.EndWriting();
6323 MOZ_ASSERT(iter && end);
6325 while (iter != end) {
6326 CharType c = *iter;
6327 if (c >= 'a' && c <= 'z') {
6328 *iter = c + ('A' - 'a');
6330 ++iter;
6334 /* static */
6335 void nsContentUtils::ASCIIToUpper(nsAString& aStr) {
6336 return _ASCIIToUpperInSitu<nsAString, char16_t>(aStr);
6339 /* static */
6340 void nsContentUtils::ASCIIToUpper(nsACString& aStr) {
6341 return _ASCIIToUpperInSitu<nsACString, char>(aStr);
6344 template <typename StringType, typename CharType>
6345 void _ASCIIToUpperCopy(const StringType& aSource, StringType& aDest) {
6346 uint32_t len = aSource.Length();
6347 aDest.SetLength(len);
6348 MOZ_ASSERT(aDest.Length() == len);
6350 CharType* dest = aDest.BeginWriting();
6351 MOZ_ASSERT(dest);
6353 const CharType* iter = aSource.BeginReading();
6354 const CharType* end = aSource.EndReading();
6355 while (iter != end) {
6356 CharType c = *iter;
6357 *dest = (c >= 'a' && c <= 'z') ? c + ('A' - 'a') : c;
6358 ++iter;
6359 ++dest;
6363 /* static */
6364 void nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest) {
6365 return _ASCIIToUpperCopy<nsAString, char16_t>(aSource, aDest);
6368 /* static */
6369 void nsContentUtils::ASCIIToUpper(const nsACString& aSource,
6370 nsACString& aDest) {
6371 return _ASCIIToUpperCopy<nsACString, char>(aSource, aDest);
6374 /* static */
6375 bool nsContentUtils::EqualsIgnoreASCIICase(nsAtom* aAtom1, nsAtom* aAtom2) {
6376 if (aAtom1 == aAtom2) {
6377 return true;
6380 // If both are ascii lowercase already, we know that the slow comparison
6381 // below is going to return false.
6382 if (aAtom1->IsAsciiLowercase() && aAtom2->IsAsciiLowercase()) {
6383 return false;
6386 return EqualsIgnoreASCIICase(nsDependentAtomString(aAtom1),
6387 nsDependentAtomString(aAtom2));
6390 /* static */
6391 bool nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
6392 const nsAString& aStr2) {
6393 uint32_t len = aStr1.Length();
6394 if (len != aStr2.Length()) {
6395 return false;
6398 const char16_t* str1 = aStr1.BeginReading();
6399 const char16_t* str2 = aStr2.BeginReading();
6400 const char16_t* end = str1 + len;
6402 while (str1 < end) {
6403 char16_t c1 = *str1++;
6404 char16_t c2 = *str2++;
6406 // First check if any bits other than the 0x0020 differs
6407 if ((c1 ^ c2) & 0xffdf) {
6408 return false;
6411 // We know they can only differ in the 0x0020 bit.
6412 // Likely the two chars are the same, so check that first
6413 if (c1 != c2) {
6414 // They do differ, but since it's only in the 0x0020 bit, check if it's
6415 // the same ascii char, but just differing in case
6416 char16_t c1Upper = c1 & 0xffdf;
6417 if (!('A' <= c1Upper && c1Upper <= 'Z')) {
6418 return false;
6423 return true;
6426 /* static */
6427 bool nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr) {
6428 const char16_t* iter = aStr.BeginReading();
6429 const char16_t* end = aStr.EndReading();
6430 while (iter != end) {
6431 char16_t c = *iter;
6432 if (c >= 'A' && c <= 'Z') {
6433 return true;
6435 ++iter;
6438 return false;
6441 /* static */
6442 nsIInterfaceRequestor* nsContentUtils::SameOriginChecker() {
6443 if (!sSameOriginChecker) {
6444 sSameOriginChecker = new SameOriginCheckerImpl();
6445 NS_ADDREF(sSameOriginChecker);
6447 return sSameOriginChecker;
6450 /* static */
6451 nsresult nsContentUtils::CheckSameOrigin(nsIChannel* aOldChannel,
6452 nsIChannel* aNewChannel) {
6453 if (!nsContentUtils::GetSecurityManager()) return NS_ERROR_NOT_AVAILABLE;
6455 nsCOMPtr<nsIPrincipal> oldPrincipal;
6456 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
6457 aOldChannel, getter_AddRefs(oldPrincipal));
6459 nsCOMPtr<nsIURI> newURI;
6460 aNewChannel->GetURI(getter_AddRefs(newURI));
6461 nsCOMPtr<nsIURI> newOriginalURI;
6462 aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
6464 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
6466 nsresult rv = oldPrincipal->CheckMayLoad(newURI, false);
6467 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
6468 rv = oldPrincipal->CheckMayLoad(newOriginalURI, false);
6471 return rv;
6474 NS_IMPL_ISUPPORTS(SameOriginCheckerImpl, nsIChannelEventSink,
6475 nsIInterfaceRequestor)
6477 NS_IMETHODIMP
6478 SameOriginCheckerImpl::AsyncOnChannelRedirect(
6479 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
6480 nsIAsyncVerifyRedirectCallback* cb) {
6481 MOZ_ASSERT(aNewChannel, "Redirecting to null channel?");
6483 nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
6484 if (NS_SUCCEEDED(rv)) {
6485 cb->OnRedirectVerifyCallback(NS_OK);
6488 return rv;
6491 NS_IMETHODIMP
6492 SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult) {
6493 return QueryInterface(aIID, aResult);
6496 /* static */
6497 nsresult nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsACString& aOrigin) {
6498 MOZ_ASSERT(aURI, "missing uri");
6500 // For Blob URI, the path is the URL of the owning page.
6501 if (aURI->SchemeIs(BLOBURI_SCHEME)) {
6502 nsAutoCString path;
6503 nsresult rv = aURI->GetPathQueryRef(path);
6504 NS_ENSURE_SUCCESS(rv, rv);
6506 nsCOMPtr<nsIURI> uri;
6507 rv = NS_NewURI(getter_AddRefs(uri), path);
6508 if (NS_FAILED(rv)) {
6509 aOrigin.AssignLiteral("null");
6510 return NS_OK;
6513 return GetASCIIOrigin(uri, aOrigin);
6516 aOrigin.Truncate();
6518 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
6519 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
6521 nsAutoCString host;
6522 nsresult rv = uri->GetAsciiHost(host);
6524 if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
6525 nsAutoCString userPass;
6526 uri->GetUserPass(userPass);
6528 nsAutoCString prePath;
6529 if (!userPass.IsEmpty()) {
6530 rv = NS_MutateURI(uri).SetUserPass(""_ns).Finalize(uri);
6531 NS_ENSURE_SUCCESS(rv, rv);
6534 rv = uri->GetPrePath(prePath);
6535 NS_ENSURE_SUCCESS(rv, rv);
6537 aOrigin = prePath;
6538 } else {
6539 aOrigin.AssignLiteral("null");
6542 return NS_OK;
6545 /* static */
6546 nsresult nsContentUtils::GetUTFOrigin(nsIPrincipal* aPrincipal,
6547 nsAString& aOrigin) {
6548 MOZ_ASSERT(aPrincipal, "missing principal");
6550 aOrigin.Truncate();
6551 nsAutoCString asciiOrigin;
6553 nsresult rv = aPrincipal->GetAsciiOrigin(asciiOrigin);
6554 if (NS_FAILED(rv)) {
6555 asciiOrigin.AssignLiteral("null");
6558 CopyUTF8toUTF16(asciiOrigin, aOrigin);
6559 return NS_OK;
6562 /* static */
6563 nsresult nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsAString& aOrigin) {
6564 MOZ_ASSERT(aURI, "missing uri");
6565 nsresult rv;
6567 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
6568 // Check if either URI has a special origin.
6569 nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin =
6570 do_QueryInterface(aURI);
6571 if (uriWithSpecialOrigin) {
6572 nsCOMPtr<nsIURI> origin;
6573 rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin));
6574 NS_ENSURE_SUCCESS(rv, rv);
6576 return GetUTFOrigin(origin, aOrigin);
6578 #endif
6580 nsAutoCString asciiOrigin;
6581 rv = GetASCIIOrigin(aURI, asciiOrigin);
6582 NS_ENSURE_SUCCESS(rv, rv);
6584 CopyUTF8toUTF16(asciiOrigin, aOrigin);
6585 return NS_OK;
6588 /* static */
6589 bool nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal,
6590 nsIChannel* aChannel,
6591 bool aAllowIfInheritsPrincipal) {
6592 nsCOMPtr<nsIURI> channelURI;
6593 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
6594 NS_ENSURE_SUCCESS(rv, false);
6596 return NS_SUCCEEDED(
6597 aPrincipal->CheckMayLoad(channelURI, aAllowIfInheritsPrincipal));
6600 /* static */
6601 bool nsContentUtils::CanAccessNativeAnon() {
6602 return LegacyIsCallerChromeOrNativeCode();
6605 /* static */
6606 nsresult nsContentUtils::DispatchXULCommand(nsIContent* aTarget, bool aTrusted,
6607 Event* aSourceEvent,
6608 PresShell* aPresShell, bool aCtrl,
6609 bool aAlt, bool aShift, bool aMeta,
6610 uint16_t aInputSource,
6611 int16_t aButton) {
6612 NS_ENSURE_STATE(aTarget);
6613 Document* doc = aTarget->OwnerDoc();
6614 nsPresContext* presContext = doc->GetPresContext();
6616 RefPtr<XULCommandEvent> xulCommand =
6617 new XULCommandEvent(doc, presContext, nullptr);
6618 xulCommand->InitCommandEvent(u"command"_ns, true, true,
6619 nsGlobalWindowInner::Cast(doc->GetInnerWindow()),
6620 0, aCtrl, aAlt, aShift, aMeta, aButton,
6621 aSourceEvent, aInputSource, IgnoreErrors());
6623 if (aPresShell) {
6624 nsEventStatus status = nsEventStatus_eIgnore;
6625 return aPresShell->HandleDOMEventWithTarget(aTarget, xulCommand, &status);
6628 ErrorResult rv;
6629 aTarget->DispatchEvent(*xulCommand, rv);
6630 return rv.StealNSResult();
6633 // static
6634 nsresult nsContentUtils::WrapNative(JSContext* cx, nsISupports* native,
6635 nsWrapperCache* cache, const nsIID* aIID,
6636 JS::MutableHandle<JS::Value> vp,
6637 bool aAllowWrapping) {
6638 MOZ_ASSERT(cx == GetCurrentJSContext());
6640 if (!native) {
6641 vp.setNull();
6643 return NS_OK;
6646 JSObject* wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
6647 if (wrapper) {
6648 return NS_OK;
6651 NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
6653 if (!NS_IsMainThread()) {
6654 MOZ_CRASH();
6657 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
6658 nsresult rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
6659 aAllowWrapping, vp);
6660 return rv;
6663 nsresult nsContentUtils::CreateArrayBuffer(JSContext* aCx,
6664 const nsACString& aData,
6665 JSObject** aResult) {
6666 if (!aCx) {
6667 return NS_ERROR_FAILURE;
6670 size_t dataLen = aData.Length();
6671 *aResult = JS::NewArrayBuffer(aCx, dataLen);
6672 if (!*aResult) {
6673 return NS_ERROR_FAILURE;
6676 if (dataLen > 0) {
6677 NS_ASSERTION(JS::IsArrayBufferObject(*aResult), "What happened?");
6678 JS::AutoCheckCannotGC nogc;
6679 bool isShared;
6680 memcpy(JS::GetArrayBufferData(*aResult, &isShared, nogc),
6681 aData.BeginReading(), dataLen);
6682 MOZ_ASSERT(!isShared);
6685 return NS_OK;
6688 void nsContentUtils::StripNullChars(const nsAString& aInStr,
6689 nsAString& aOutStr) {
6690 // In common cases where we don't have nulls in the
6691 // string we can simple simply bypass the checking code.
6692 int32_t firstNullPos = aInStr.FindChar('\0');
6693 if (firstNullPos == kNotFound) {
6694 aOutStr.Assign(aInStr);
6695 return;
6698 aOutStr.SetCapacity(aInStr.Length() - 1);
6699 nsAString::const_iterator start, end;
6700 aInStr.BeginReading(start);
6701 aInStr.EndReading(end);
6702 while (start != end) {
6703 if (*start != '\0') aOutStr.Append(*start);
6704 ++start;
6708 struct ClassMatchingInfo {
6709 AtomArray mClasses;
6710 nsCaseTreatment mCaseTreatment;
6713 // static
6714 bool nsContentUtils::MatchClassNames(Element* aElement, int32_t aNamespaceID,
6715 nsAtom* aAtom, void* aData) {
6716 // We can't match if there are no class names
6717 const nsAttrValue* classAttr = aElement->GetClasses();
6718 if (!classAttr) {
6719 return false;
6722 // need to match *all* of the classes
6723 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6724 uint32_t length = info->mClasses.Length();
6725 if (!length) {
6726 // If we actually had no classes, don't match.
6727 return false;
6729 uint32_t i;
6730 for (i = 0; i < length; ++i) {
6731 if (!classAttr->Contains(info->mClasses[i], info->mCaseTreatment)) {
6732 return false;
6736 return true;
6739 // static
6740 void nsContentUtils::DestroyClassNameArray(void* aData) {
6741 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6742 delete info;
6745 // static
6746 void* nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
6747 const nsString* aClasses) {
6748 nsAttrValue attrValue;
6749 attrValue.ParseAtomArray(*aClasses);
6750 // nsAttrValue::Equals is sensitive to order, so we'll send an array
6751 auto* info = new ClassMatchingInfo;
6752 if (attrValue.Type() == nsAttrValue::eAtomArray) {
6753 info->mClasses = std::move(attrValue.GetAtomArrayValue()->mArray);
6754 } else if (attrValue.Type() == nsAttrValue::eAtom) {
6755 info->mClasses.AppendElement(attrValue.GetAtomValue());
6758 info->mCaseTreatment =
6759 aRootNode->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks
6760 ? eIgnoreCase
6761 : eCaseMatters;
6762 return info;
6765 // static
6766 bool nsContentUtils::IsFocusedContent(const nsIContent* aContent) {
6767 nsFocusManager* fm = nsFocusManager::GetFocusManager();
6769 return fm && fm->GetFocusedElement() == aContent;
6772 bool nsContentUtils::HasScrollgrab(nsIContent* aContent) {
6773 // If we ever standardize this feature we'll want to hook this up properly
6774 // again. For now we're removing all the DOM-side code related to it but
6775 // leaving the layout and APZ handling for it in place.
6776 return false;
6779 void nsContentUtils::FlushLayoutForTree(nsPIDOMWindowOuter* aWindow) {
6780 if (!aWindow) {
6781 return;
6784 // Note that because FlushPendingNotifications flushes parents, this
6785 // is O(N^2) in docshell tree depth. However, the docshell tree is
6786 // usually pretty shallow.
6788 if (RefPtr<Document> doc = aWindow->GetDoc()) {
6789 doc->FlushPendingNotifications(FlushType::Layout);
6792 if (nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell()) {
6793 int32_t i = 0, i_end;
6794 docShell->GetInProcessChildCount(&i_end);
6795 for (; i < i_end; ++i) {
6796 nsCOMPtr<nsIDocShellTreeItem> item;
6797 if (docShell->GetInProcessChildAt(i, getter_AddRefs(item)) == NS_OK &&
6798 item) {
6799 if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
6800 FlushLayoutForTree(win);
6807 void nsContentUtils::RemoveNewlines(nsString& aString) { aString.StripCRLF(); }
6809 void nsContentUtils::PlatformToDOMLineBreaks(nsString& aString) {
6810 if (!PlatformToDOMLineBreaks(aString, fallible)) {
6811 aString.AllocFailed(aString.Length());
6815 bool nsContentUtils::PlatformToDOMLineBreaks(nsString& aString,
6816 const fallible_t& aFallible) {
6817 if (aString.FindChar(char16_t('\r')) != -1) {
6818 // Windows linebreaks: Map CRLF to LF:
6819 if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
6820 return false;
6823 // Mac linebreaks: Map any remaining CR to LF:
6824 if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
6825 return false;
6829 return true;
6832 void nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
6833 nsAString& aResultString) {
6834 MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
6836 uint32_t stringLen = NS_strlen(static_cast<char16_t*>(aBuf->Data()));
6838 // SANITY CHECK: In case the nsStringBuffer isn't correctly
6839 // null-terminated, let's clamp its length using the allocated size, to be
6840 // sure the resulting string doesn't sample past the end of the the buffer.
6841 // (Note that StorageSize() is in units of bytes, so we have to convert that
6842 // to units of PRUnichars, and subtract 1 for the null-terminator.)
6843 uint32_t allocStringLen = (aBuf->StorageSize() / sizeof(char16_t)) - 1;
6844 MOZ_ASSERT(stringLen <= allocStringLen,
6845 "string buffer lacks null terminator!");
6846 stringLen = std::min(stringLen, allocStringLen);
6848 aBuf->ToString(stringLen, aResultString);
6851 already_AddRefed<nsContentList> nsContentUtils::GetElementsByClassName(
6852 nsINode* aRootNode, const nsAString& aClasses) {
6853 MOZ_ASSERT(aRootNode, "Must have root node");
6855 return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(
6856 aRootNode, MatchClassNames, DestroyClassNameArray, AllocClassMatchingInfo,
6857 aClasses);
6860 PresShell* nsContentUtils::FindPresShellForDocument(const Document* aDocument) {
6861 const Document* doc = aDocument;
6862 Document* displayDoc = doc->GetDisplayDocument();
6863 if (displayDoc) {
6864 doc = displayDoc;
6867 PresShell* presShell = doc->GetPresShell();
6868 if (presShell) {
6869 return presShell;
6872 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
6873 while (docShellTreeItem) {
6874 // We may be in a display:none subdocument, or we may not have a presshell
6875 // created yet.
6876 // Walk the docshell tree to find the nearest container that has a
6877 // presshell, and return that.
6878 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
6879 if (PresShell* presShell = docShell->GetPresShell()) {
6880 return presShell;
6882 nsCOMPtr<nsIDocShellTreeItem> parent;
6883 docShellTreeItem->GetInProcessParent(getter_AddRefs(parent));
6884 docShellTreeItem = parent;
6887 return nullptr;
6890 /* static */
6891 nsPresContext* nsContentUtils::FindPresContextForDocument(
6892 const Document* aDocument) {
6893 if (PresShell* presShell = FindPresShellForDocument(aDocument)) {
6894 return presShell->GetPresContext();
6896 return nullptr;
6899 nsIWidget* nsContentUtils::WidgetForDocument(const Document* aDocument) {
6900 PresShell* presShell = FindPresShellForDocument(aDocument);
6901 if (!presShell) {
6902 return nullptr;
6904 nsViewManager* vm = presShell->GetViewManager();
6905 if (!vm) {
6906 return nullptr;
6908 nsView* rootView = vm->GetRootView();
6909 if (!rootView) {
6910 return nullptr;
6912 nsView* displayRoot = nsViewManager::GetDisplayRootFor(rootView);
6913 if (!displayRoot) {
6914 return nullptr;
6916 return displayRoot->GetNearestWidget(nullptr);
6919 nsIWidget* nsContentUtils::WidgetForContent(const nsIContent* aContent) {
6920 nsIFrame* frame = aContent->GetPrimaryFrame();
6921 if (frame) {
6922 frame = nsLayoutUtils::GetDisplayRootFrame(frame);
6924 nsView* view = frame->GetView();
6925 if (view) {
6926 return view->GetWidget();
6930 return nullptr;
6933 WindowRenderer* nsContentUtils::WindowRendererForContent(
6934 const nsIContent* aContent) {
6935 nsIWidget* widget = nsContentUtils::WidgetForContent(aContent);
6936 if (widget) {
6937 return widget->GetWindowRenderer();
6940 return nullptr;
6943 WindowRenderer* nsContentUtils::WindowRendererForDocument(
6944 const Document* aDoc) {
6945 nsIWidget* widget = nsContentUtils::WidgetForDocument(aDoc);
6946 if (widget) {
6947 return widget->GetWindowRenderer();
6950 return nullptr;
6953 bool nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal) {
6954 if (!aPrincipal) {
6955 return false;
6958 if (aPrincipal->IsSystemPrincipal()) {
6959 return true;
6962 return (StaticPrefs::dom_allow_XUL_XBL_for_file() &&
6963 aPrincipal->SchemeIs("file")) ||
6964 IsSitePermAllow(aPrincipal, "allowXULXBL"_ns);
6967 bool nsContentUtils::IsPDFJSEnabled() {
6968 nsCOMPtr<nsIStreamConverter> conv = do_CreateInstance(
6969 "@mozilla.org/streamconv;1?from=application/pdf&to=text/html");
6970 return conv;
6973 bool nsContentUtils::IsPDFJS(nsIPrincipal* aPrincipal) {
6974 if (!aPrincipal) {
6975 return false;
6977 nsAutoCString spec;
6978 nsresult rv = aPrincipal->GetAsciiSpec(spec);
6979 NS_ENSURE_SUCCESS(rv, false);
6980 return spec.EqualsLiteral("resource://pdf.js/web/viewer.html");
6983 bool nsContentUtils::IsSystemOrPDFJS(JSContext* aCx, JSObject*) {
6984 nsIPrincipal* principal = SubjectPrincipal(aCx);
6985 return principal && (principal->IsSystemPrincipal() || IsPDFJS(principal));
6988 already_AddRefed<nsIDocumentLoaderFactory>
6989 nsContentUtils::FindInternalContentViewer(const nsACString& aType,
6990 ContentViewerType* aLoaderType) {
6991 if (aLoaderType) {
6992 *aLoaderType = TYPE_UNSUPPORTED;
6995 // one helper factory, please
6996 nsCOMPtr<nsICategoryManager> catMan(
6997 do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
6998 if (!catMan) return nullptr;
7000 nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
7002 nsCString contractID;
7003 nsresult rv =
7004 catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, contractID);
7005 if (NS_SUCCEEDED(rv)) {
7006 docFactory = do_GetService(contractID.get());
7007 if (docFactory && aLoaderType) {
7008 if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
7009 *aLoaderType = TYPE_CONTENT;
7010 else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
7011 *aLoaderType = TYPE_FALLBACK;
7012 else
7013 *aLoaderType = TYPE_UNKNOWN;
7015 return docFactory.forget();
7018 if (DecoderTraits::IsSupportedInVideoDocument(aType)) {
7019 docFactory =
7020 do_GetService("@mozilla.org/content/document-loader-factory;1");
7021 if (docFactory && aLoaderType) {
7022 *aLoaderType = TYPE_CONTENT;
7024 return docFactory.forget();
7027 return nullptr;
7030 static void ReportPatternCompileFailure(nsAString& aPattern,
7031 const Document* aDocument,
7032 JS::MutableHandle<JS::Value> error,
7033 JSContext* cx) {
7034 JS::AutoSaveExceptionState savedExc(cx);
7035 JS::Rooted<JSObject*> exnObj(cx, &error.toObject());
7036 JS::Rooted<JS::Value> messageVal(cx);
7037 if (!JS_GetProperty(cx, exnObj, "message", &messageVal)) {
7038 return;
7040 JS::Rooted<JSString*> messageStr(cx, messageVal.toString());
7041 MOZ_ASSERT(messageStr);
7043 AutoTArray<nsString, 2> strings;
7044 strings.AppendElement(aPattern);
7045 if (!AssignJSString(cx, *strings.AppendElement(), messageStr)) {
7046 return;
7049 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns,
7050 aDocument, nsContentUtils::eDOM_PROPERTIES,
7051 "PatternAttributeCompileFailure", strings);
7052 savedExc.drop();
7055 // static
7056 Maybe<bool> nsContentUtils::IsPatternMatching(nsAString& aValue,
7057 nsAString& aPattern,
7058 const Document* aDocument,
7059 bool aHasMultiple) {
7060 NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
7062 // The fact that we're using a JS regexp under the hood should not be visible
7063 // to things like window onerror handlers, so we don't initialize our JSAPI
7064 // with the document's window (which may not exist anyway).
7065 AutoJSAPI jsapi;
7066 jsapi.Init();
7067 JSContext* cx = jsapi.cx();
7068 AutoDisableJSInterruptCallback disabler(cx);
7070 // We can use the junk scope here, because we're just using it for regexp
7071 // evaluation, not actual script execution, and we disable statics so that the
7072 // evaluation does not interact with the execution global.
7073 JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
7075 // Check if the pattern by itself is valid first, and not that it only becomes
7076 // valid once we add ^(?: and )$.
7077 JS::Rooted<JS::Value> error(cx);
7078 if (!JS::CheckRegExpSyntax(
7079 cx, static_cast<char16_t*>(aPattern.BeginWriting()),
7080 aPattern.Length(), JS::RegExpFlag::Unicode, &error)) {
7081 return Nothing();
7084 if (!error.isUndefined()) {
7085 ReportPatternCompileFailure(aPattern, aDocument, &error, cx);
7086 return Some(true);
7089 // The pattern has to match the entire value.
7090 aPattern.InsertLiteral(u"^(?:", 0);
7091 aPattern.AppendLiteral(")$");
7093 JS::Rooted<JSObject*> re(
7095 JS::NewUCRegExpObject(cx, static_cast<char16_t*>(aPattern.BeginWriting()),
7096 aPattern.Length(), JS::RegExpFlag::Unicode));
7097 if (!re) {
7098 return Nothing();
7101 JS::Rooted<JS::Value> rval(cx, JS::NullValue());
7102 if (!aHasMultiple) {
7103 size_t idx = 0;
7104 if (!JS::ExecuteRegExpNoStatics(
7105 cx, re, static_cast<char16_t*>(aValue.BeginWriting()),
7106 aValue.Length(), &idx, true, &rval)) {
7107 return Nothing();
7109 return Some(!rval.isNull());
7112 HTMLSplitOnSpacesTokenizer tokenizer(aValue, ',');
7113 while (tokenizer.hasMoreTokens()) {
7114 const nsAString& value = tokenizer.nextToken();
7115 size_t idx = 0;
7116 if (!JS::ExecuteRegExpNoStatics(
7117 cx, re, static_cast<const char16_t*>(value.BeginReading()),
7118 value.Length(), &idx, true, &rval)) {
7119 return Nothing();
7121 if (rval.isNull()) {
7122 return Some(false);
7125 return Some(true);
7128 // static
7129 nsresult nsContentUtils::URIInheritsSecurityContext(nsIURI* aURI,
7130 bool* aResult) {
7131 // Note: about:blank URIs do NOT inherit the security context from the
7132 // current document, which is what this function tests for...
7133 return NS_URIChainHasFlags(
7134 aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, aResult);
7137 // static
7138 bool nsContentUtils::ChannelShouldInheritPrincipal(
7139 nsIPrincipal* aLoadingPrincipal, nsIURI* aURI, bool aInheritForAboutBlank,
7140 bool aForceInherit) {
7141 MOZ_ASSERT(aLoadingPrincipal,
7142 "Can not check inheritance without a principal");
7144 // Only tell the channel to inherit if it can't provide its own security
7145 // context.
7147 // XXX: If this is ever changed, check all callers for what owners
7148 // they're passing in. In particular, see the code and
7149 // comments in nsDocShell::LoadURI where we fall back on
7150 // inheriting the owner if called from chrome. That would be
7151 // very wrong if this code changed anything but channels that
7152 // can't provide their own security context!
7154 // If aForceInherit is true, we will inherit, even for a channel that
7155 // can provide its own security context. This is used for srcdoc loads.
7156 bool inherit = aForceInherit;
7157 if (!inherit) {
7158 bool uriInherits;
7159 // We expect URIInheritsSecurityContext to return success for an
7160 // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
7161 // This condition needs to match the one in nsDocShell::InternalLoad where
7162 // we're checking for things that will use the owner.
7163 inherit =
7164 (NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &uriInherits)) &&
7165 (uriInherits || (aInheritForAboutBlank && NS_IsAboutBlank(aURI)))) ||
7167 // file: uri special-casing
7169 // If this is a file: load opened from another file: then it may need
7170 // to inherit the owner from the referrer so they can script each other.
7171 // If we don't set the owner explicitly then each file: gets an owner
7172 // based on its own codebase later.
7174 (URIIsLocalFile(aURI) &&
7175 NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, false)) &&
7176 // One more check here. CheckMayLoad will always return true for the
7177 // system principal, but we do NOT want to inherit in that case.
7178 !aLoadingPrincipal->IsSystemPrincipal());
7180 return inherit;
7183 /* static */
7184 bool nsContentUtils::IsCutCopyAllowed(Document* aDocument,
7185 nsIPrincipal& aSubjectPrincipal) {
7186 if (StaticPrefs::dom_allow_cut_copy() && aDocument &&
7187 aDocument->HasValidTransientUserGestureActivation()) {
7188 return true;
7191 return PrincipalHasPermission(aSubjectPrincipal, nsGkAtoms::clipboardWrite);
7194 /* static */
7195 bool nsContentUtils::HaveEqualPrincipals(Document* aDoc1, Document* aDoc2) {
7196 if (!aDoc1 || !aDoc2) {
7197 return false;
7199 bool principalsEqual = false;
7200 aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
7201 return principalsEqual;
7204 /* static */
7205 bool nsContentUtils::HasPluginWithUncontrolledEventDispatch(
7206 nsIContent* aContent) {
7207 return false;
7210 /* static */
7211 void nsContentUtils::FireMutationEventsForDirectParsing(
7212 Document* aDoc, nsIContent* aDest, int32_t aOldChildCount) {
7213 // Fire mutation events. Optimize for the case when there are no listeners
7214 int32_t newChildCount = aDest->GetChildCount();
7215 if (newChildCount && nsContentUtils::HasMutationListeners(
7216 aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
7217 AutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
7218 NS_ASSERTION(newChildCount - aOldChildCount >= 0,
7219 "What, some unexpected dom mutation has happened?");
7220 childNodes.SetCapacity(newChildCount - aOldChildCount);
7221 for (nsIContent* child = aDest->GetFirstChild(); child;
7222 child = child->GetNextSibling()) {
7223 childNodes.AppendElement(child);
7225 FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
7229 /* static */
7230 const Document* nsContentUtils::GetInProcessSubtreeRootDocument(
7231 const Document* aDoc) {
7232 if (!aDoc) {
7233 return nullptr;
7235 const Document* doc = aDoc;
7236 while (doc->GetInProcessParentDocument()) {
7237 doc = doc->GetInProcessParentDocument();
7239 return doc;
7242 // static
7243 int32_t nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
7244 int32_t aOffset) {
7245 // The structure of the anonymous frames within a text control frame is
7246 // an optional block frame, followed by an optional br frame.
7248 // If the offset frame has a child, then this frame is the block which
7249 // has the text frames (containing the content) as its children. This will
7250 // be the case if we click to the right of any of the text frames, or at the
7251 // bottom of the text area.
7252 nsIFrame* firstChild = aOffsetFrame->PrincipalChildList().FirstChild();
7253 if (firstChild) {
7254 // In this case, the passed-in offset is incorrect, and we want the length
7255 // of the entire content in the text control frame.
7256 return firstChild->GetContent()->Length();
7259 if (aOffsetFrame->GetPrevSibling() && !aOffsetFrame->GetNextSibling()) {
7260 // In this case, we're actually within the last frame, which is a br
7261 // frame. Our offset should therefore be the length of the first child of
7262 // our parent.
7263 int32_t aOutOffset = aOffsetFrame->GetParent()
7264 ->PrincipalChildList()
7265 .FirstChild()
7266 ->GetContent()
7267 ->Length();
7268 return aOutOffset;
7271 // Otherwise, we're within one of the text frames, in which case our offset
7272 // has already been correctly calculated.
7273 return aOffset;
7276 // static
7277 void nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
7278 Element* aRoot,
7279 uint32_t& aOutStartOffset,
7280 uint32_t& aOutEndOffset) {
7281 MOZ_ASSERT(aSelection && aRoot);
7283 // We don't care which end of this selection is anchor and which is focus. In
7284 // fact, we explicitly want to know which is the _start_ and which is the
7285 // _end_, not anchor vs focus.
7286 const nsRange* range = aSelection->GetAnchorFocusRange();
7287 if (!range) {
7288 // Nothing selected
7289 aOutStartOffset = aOutEndOffset = 0;
7290 return;
7293 // All the node pointers here are raw pointers for performance. We shouldn't
7294 // be doing anything in this function that invalidates the node tree.
7295 nsINode* startContainer = range->GetStartContainer();
7296 uint32_t startOffset = range->StartOffset();
7297 nsINode* endContainer = range->GetEndContainer();
7298 uint32_t endOffset = range->EndOffset();
7300 // We have at most two children, consisting of an optional text node followed
7301 // by an optional <br>.
7302 NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
7303 nsIContent* firstChild = aRoot->GetFirstChild();
7304 #ifdef DEBUG
7305 nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
7306 NS_ASSERTION(startContainer == aRoot || startContainer == firstChild ||
7307 startContainer == lastChild,
7308 "Unexpected startContainer");
7309 NS_ASSERTION(endContainer == aRoot || endContainer == firstChild ||
7310 endContainer == lastChild,
7311 "Unexpected endContainer");
7312 // firstChild is either text or a <br> (hence an element).
7313 MOZ_ASSERT_IF(firstChild, firstChild->IsText() || firstChild->IsElement());
7314 #endif
7315 if (!firstChild || firstChild->IsElement()) {
7316 // No text node, so everything is 0
7317 startOffset = endOffset = 0;
7318 } else {
7319 // First child is text. If the start/end is already in the text node,
7320 // or the start of the root node, no change needed. If it's in the root
7321 // node but not the start, or in the trailing <br>, we need to set the
7322 // offset to the end.
7323 if ((startContainer == aRoot && startOffset != 0) ||
7324 (startContainer != aRoot && startContainer != firstChild)) {
7325 startOffset = firstChild->Length();
7327 if ((endContainer == aRoot && endOffset != 0) ||
7328 (endContainer != aRoot && endContainer != firstChild)) {
7329 endOffset = firstChild->Length();
7333 MOZ_ASSERT(startOffset <= endOffset);
7334 aOutStartOffset = startOffset;
7335 aOutEndOffset = endOffset;
7338 // static
7339 HTMLEditor* nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext) {
7340 if (!aPresContext) {
7341 return nullptr;
7343 return GetHTMLEditor(aPresContext->GetDocShell());
7346 // static
7347 HTMLEditor* nsContentUtils::GetHTMLEditor(nsDocShell* aDocShell) {
7348 bool isEditable;
7349 if (!aDocShell || NS_FAILED(aDocShell->GetEditable(&isEditable)) ||
7350 !isEditable) {
7351 return nullptr;
7353 return aDocShell->GetHTMLEditor();
7356 // static
7357 EditorBase* nsContentUtils::GetActiveEditor(nsPresContext* aPresContext) {
7358 if (!aPresContext) {
7359 return nullptr;
7362 return GetActiveEditor(aPresContext->Document()->GetWindow());
7365 // static
7366 EditorBase* nsContentUtils::GetActiveEditor(nsPIDOMWindowOuter* aWindow) {
7367 if (!aWindow || !aWindow->GetExtantDoc()) {
7368 return nullptr;
7371 // If it's in designMode, nobody can have focus. Therefore, the HTMLEditor
7372 // handles all events. I.e., it's focused editor in this case.
7373 if (aWindow->GetExtantDoc()->IsInDesignMode()) {
7374 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7377 // If focused element is associated with TextEditor, it must be <input>
7378 // element or <textarea> element. Let's return it even if it's in a
7379 // contenteditable element.
7380 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
7381 if (Element* focusedElement = nsFocusManager::GetFocusedDescendant(
7382 aWindow, nsFocusManager::SearchRange::eOnlyCurrentWindow,
7383 getter_AddRefs(focusedWindow))) {
7384 if (TextEditor* textEditor = focusedElement->GetTextEditorInternal()) {
7385 return textEditor;
7389 // Otherwise, HTMLEditor may handle inputs even non-editable element has
7390 // focus or nobody has focus.
7391 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7394 // static
7395 TextEditor* nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(
7396 const nsIContent* aAnonymousContent) {
7397 if (!aAnonymousContent) {
7398 return nullptr;
7400 nsIContent* parent = aAnonymousContent->FindFirstNonChromeOnlyAccessContent();
7401 if (!parent || parent == aAnonymousContent) {
7402 return nullptr;
7404 if (HTMLInputElement* inputElement =
7405 HTMLInputElement::FromNodeOrNull(parent)) {
7406 return inputElement->GetTextEditorWithoutCreation();
7408 if (HTMLTextAreaElement* textareaElement =
7409 HTMLTextAreaElement::FromNodeOrNull(parent)) {
7410 return textareaElement->GetTextEditorWithoutCreation();
7412 return nullptr;
7415 // static
7416 bool nsContentUtils::IsNodeInEditableRegion(nsINode* aNode) {
7417 while (aNode) {
7418 if (aNode->IsEditable()) {
7419 return true;
7421 aNode = aNode->GetParent();
7423 return false;
7426 // static
7427 bool nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader,
7428 const nsACString& aValue) {
7429 if (IsForbiddenSystemRequestHeader(aHeader)) {
7430 return true;
7433 if ((nsContentUtils::IsOverrideMethodHeader(aHeader) &&
7434 nsContentUtils::ContainsForbiddenMethod(aValue))) {
7435 return true;
7438 if (StringBeginsWith(aHeader, "proxy-"_ns,
7439 nsCaseInsensitiveCStringComparator) ||
7440 StringBeginsWith(aHeader, "sec-"_ns,
7441 nsCaseInsensitiveCStringComparator)) {
7442 return true;
7445 return false;
7448 // static
7449 bool nsContentUtils::IsForbiddenSystemRequestHeader(const nsACString& aHeader) {
7450 static const char* kInvalidHeaders[] = {"accept-charset",
7451 "accept-encoding",
7452 "access-control-request-headers",
7453 "access-control-request-method",
7454 "connection",
7455 "content-length",
7456 "cookie",
7457 "cookie2",
7458 "date",
7459 "dnt",
7460 "expect",
7461 "host",
7462 "keep-alive",
7463 "origin",
7464 "referer",
7465 "set-cookie",
7466 "te",
7467 "trailer",
7468 "transfer-encoding",
7469 "upgrade",
7470 "via"};
7471 for (auto& kInvalidHeader : kInvalidHeaders) {
7472 if (aHeader.LowerCaseEqualsASCII(kInvalidHeader)) {
7473 return true;
7476 return false;
7479 // static
7480 bool nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader) {
7481 return (aHeader.LowerCaseEqualsASCII("set-cookie") ||
7482 aHeader.LowerCaseEqualsASCII("set-cookie2"));
7485 // static
7486 bool nsContentUtils::IsOverrideMethodHeader(const nsACString& headerName) {
7487 return headerName.EqualsIgnoreCase("x-http-method-override") ||
7488 headerName.EqualsIgnoreCase("x-http-method") ||
7489 headerName.EqualsIgnoreCase("x-method-override");
7492 // static
7493 bool nsContentUtils::ContainsForbiddenMethod(const nsACString& headerValue) {
7494 bool hasInsecureMethod = false;
7495 nsCCharSeparatedTokenizer tokenizer(headerValue, ',');
7497 while (tokenizer.hasMoreTokens()) {
7498 const nsDependentCSubstring& value = tokenizer.nextToken();
7500 if (value.EqualsIgnoreCase("connect") || value.EqualsIgnoreCase("trace") ||
7501 value.EqualsIgnoreCase("track")) {
7502 hasInsecureMethod = true;
7503 break;
7507 return hasInsecureMethod;
7510 // static
7511 bool nsContentUtils::IsCorsUnsafeRequestHeaderValue(
7512 const nsACString& aHeaderValue) {
7513 const char* cur = aHeaderValue.BeginReading();
7514 const char* end = aHeaderValue.EndReading();
7516 while (cur != end) {
7517 // Implementation of
7518 // https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte Is less
7519 // than a space but not a horizontal tab
7520 if ((*cur < ' ' && *cur != '\t') || *cur == '"' || *cur == '(' ||
7521 *cur == ')' || *cur == ':' || *cur == '<' || *cur == '>' ||
7522 *cur == '?' || *cur == '@' || *cur == '[' || *cur == '\\' ||
7523 *cur == ']' || *cur == '{' || *cur == '}' ||
7524 *cur == 0x7F) { // 0x75 is DEL
7525 return true;
7527 cur++;
7529 return false;
7532 // static
7533 bool nsContentUtils::IsAllowedNonCorsAccept(const nsACString& aHeaderValue) {
7534 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7535 return false;
7537 return true;
7540 // static
7541 bool nsContentUtils::IsAllowedNonCorsContentType(
7542 const nsACString& aHeaderValue) {
7543 nsAutoCString contentType;
7544 nsAutoCString unused;
7546 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7547 return false;
7550 nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
7551 if (NS_FAILED(rv)) {
7552 return false;
7555 return contentType.LowerCaseEqualsLiteral("text/plain") ||
7556 contentType.LowerCaseEqualsLiteral(
7557 "application/x-www-form-urlencoded") ||
7558 contentType.LowerCaseEqualsLiteral("multipart/form-data");
7561 // static
7562 bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
7563 const char* cur = aHeaderValue.BeginReading();
7564 const char* end = aHeaderValue.EndReading();
7566 while (cur != end) {
7567 if ((*cur >= '0' && *cur <= '9') || (*cur >= 'A' && *cur <= 'Z') ||
7568 (*cur >= 'a' && *cur <= 'z') || *cur == ' ' || *cur == '*' ||
7569 *cur == ',' || *cur == '-' || *cur == '.' || *cur == ';' ||
7570 *cur == '=') {
7571 cur++;
7572 continue;
7574 return false;
7576 return true;
7579 // static
7580 bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
7581 const nsACString& aValue) {
7582 // see https://fetch.spec.whatwg.org/#cors-safelisted-request-header
7583 if (aValue.Length() > 128) {
7584 return false;
7586 return (aName.LowerCaseEqualsLiteral("accept") &&
7587 nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
7588 (aName.LowerCaseEqualsLiteral("accept-language") &&
7589 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7590 (aName.LowerCaseEqualsLiteral("content-language") &&
7591 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7592 (aName.LowerCaseEqualsLiteral("content-type") &&
7593 nsContentUtils::IsAllowedNonCorsContentType(aValue));
7596 mozilla::LogModule* nsContentUtils::ResistFingerprintingLog() {
7597 return gResistFingerprintingLog;
7599 mozilla::LogModule* nsContentUtils::DOMDumpLog() { return sDOMDumpLog; }
7601 bool nsContentUtils::GetNodeTextContent(const nsINode* aNode, bool aDeep,
7602 nsAString& aResult,
7603 const fallible_t& aFallible) {
7604 aResult.Truncate();
7605 return AppendNodeTextContent(aNode, aDeep, aResult, aFallible);
7608 void nsContentUtils::GetNodeTextContent(const nsINode* aNode, bool aDeep,
7609 nsAString& aResult) {
7610 if (!GetNodeTextContent(aNode, aDeep, aResult, fallible)) {
7611 NS_ABORT_OOM(0); // Unfortunately we don't know the allocation size
7615 void nsContentUtils::DestroyMatchString(void* aData) {
7616 if (aData) {
7617 nsString* matchString = static_cast<nsString*>(aData);
7618 delete matchString;
7622 bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
7623 // Table ordered from most to least likely JS MIME types.
7624 static const char* jsTypes[] = {"text/javascript",
7625 "text/ecmascript",
7626 "application/javascript",
7627 "application/ecmascript",
7628 "application/x-javascript",
7629 "application/x-ecmascript",
7630 "text/javascript1.0",
7631 "text/javascript1.1",
7632 "text/javascript1.2",
7633 "text/javascript1.3",
7634 "text/javascript1.4",
7635 "text/javascript1.5",
7636 "text/jscript",
7637 "text/livescript",
7638 "text/x-ecmascript",
7639 "text/x-javascript",
7640 nullptr};
7642 for (uint32_t i = 0; jsTypes[i]; ++i) {
7643 if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
7644 return true;
7648 return false;
7651 bool nsContentUtils::PrefetchPreloadEnabled(nsIDocShell* aDocShell) {
7653 // SECURITY CHECK: disable prefetching and preloading from mailnews!
7655 // walk up the docshell tree to see if any containing
7656 // docshell are of type MAIL.
7659 if (!aDocShell) {
7660 return false;
7663 nsCOMPtr<nsIDocShell> docshell = aDocShell;
7664 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7666 do {
7667 auto appType = docshell->GetAppType();
7668 if (appType == nsIDocShell::APP_TYPE_MAIL) {
7669 return false; // do not prefetch, preload, preconnect from mailnews
7672 docshell->GetInProcessParent(getter_AddRefs(parentItem));
7673 if (parentItem) {
7674 docshell = do_QueryInterface(parentItem);
7675 if (!docshell) {
7676 NS_ERROR("cannot get a docshell from a treeItem!");
7677 return false;
7680 } while (parentItem);
7682 return true;
7685 uint64_t nsContentUtils::GetInnerWindowID(nsIRequest* aRequest) {
7686 // can't do anything if there's no nsIRequest!
7687 if (!aRequest) {
7688 return 0;
7691 nsCOMPtr<nsILoadGroup> loadGroup;
7692 nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
7694 if (NS_FAILED(rv) || !loadGroup) {
7695 return 0;
7698 return GetInnerWindowID(loadGroup);
7701 uint64_t nsContentUtils::GetInnerWindowID(nsILoadGroup* aLoadGroup) {
7702 if (!aLoadGroup) {
7703 return 0;
7706 nsCOMPtr<nsIInterfaceRequestor> callbacks;
7707 nsresult rv = aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
7708 if (NS_FAILED(rv) || !callbacks) {
7709 return 0;
7712 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
7713 if (!loadContext) {
7714 return 0;
7717 nsCOMPtr<mozIDOMWindowProxy> window;
7718 rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
7719 if (NS_FAILED(rv) || !window) {
7720 return 0;
7723 auto* pwindow = nsPIDOMWindowOuter::From(window);
7724 if (!pwindow) {
7725 return 0;
7728 nsPIDOMWindowInner* inner = pwindow->GetCurrentInnerWindow();
7729 return inner ? inner->WindowID() : 0;
7732 // static
7733 void nsContentUtils::MaybeFixIPv6Host(nsACString& aHost) {
7734 if (aHost.FindChar(':') != -1) { // Escape IPv6 address
7735 MOZ_ASSERT(!aHost.Length() ||
7736 (aHost[0] != '[' && aHost[aHost.Length() - 1] != ']'));
7737 aHost.Insert('[', 0);
7738 aHost.Append(']');
7742 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7743 nsACString& aHost) {
7744 aHost.Truncate();
7745 nsresult rv = aURI->GetHost(aHost);
7746 if (NS_FAILED(rv)) { // Some URIs do not have a host
7747 return rv;
7750 MaybeFixIPv6Host(aHost);
7752 return NS_OK;
7755 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7756 nsAString& aHost) {
7757 nsAutoCString hostname;
7758 nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname);
7759 if (NS_FAILED(rv)) {
7760 return rv;
7762 CopyUTF8toUTF16(hostname, aHost);
7763 return NS_OK;
7766 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIPrincipal* aPrincipal,
7767 nsACString& aHost) {
7768 nsresult rv = aPrincipal->GetAsciiHost(aHost);
7769 if (NS_FAILED(rv)) { // Some URIs do not have a host
7770 return rv;
7773 MaybeFixIPv6Host(aHost);
7774 return NS_OK;
7777 CallState nsContentUtils::CallOnAllRemoteChildren(
7778 MessageBroadcaster* aManager,
7779 const std::function<CallState(BrowserParent*)>& aCallback) {
7780 uint32_t browserChildCount = aManager->ChildCount();
7781 for (uint32_t j = 0; j < browserChildCount; ++j) {
7782 RefPtr<MessageListenerManager> childMM = aManager->GetChildAt(j);
7783 if (!childMM) {
7784 continue;
7787 RefPtr<MessageBroadcaster> nonLeafMM = MessageBroadcaster::From(childMM);
7788 if (nonLeafMM) {
7789 if (CallOnAllRemoteChildren(nonLeafMM, aCallback) == CallState::Stop) {
7790 return CallState::Stop;
7792 continue;
7795 mozilla::dom::ipc::MessageManagerCallback* cb = childMM->GetCallback();
7796 if (cb) {
7797 nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
7798 BrowserParent* remote = BrowserParent::GetFrom(fl);
7799 if (remote && aCallback) {
7800 if (aCallback(remote) == CallState::Stop) {
7801 return CallState::Stop;
7807 return CallState::Continue;
7810 void nsContentUtils::CallOnAllRemoteChildren(
7811 nsPIDOMWindowOuter* aWindow,
7812 const std::function<CallState(BrowserParent*)>& aCallback) {
7813 nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(aWindow);
7814 if (window->IsChromeWindow()) {
7815 RefPtr<MessageBroadcaster> windowMM = window->GetMessageManager();
7816 if (windowMM) {
7817 CallOnAllRemoteChildren(windowMM, aCallback);
7822 bool nsContentUtils::IPCTransferableDataItemHasKnownFlavor(
7823 const IPCTransferableDataItem& aItem) {
7824 // Unknown types are converted to kCustomTypesMime.
7825 if (aItem.flavor().EqualsASCII(kCustomTypesMime)) {
7826 return true;
7829 for (const char* format : DataTransfer::kKnownFormats) {
7830 if (aItem.flavor().EqualsASCII(format)) {
7831 return true;
7835 return false;
7838 nsresult nsContentUtils::IPCTransferableDataToTransferable(
7839 const IPCTransferableData& aTransferableData, bool aAddDataFlavor,
7840 nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
7841 nsresult rv;
7842 const nsTArray<IPCTransferableDataItem>& items = aTransferableData.items();
7843 for (const auto& item : items) {
7844 if (aFilterUnknownFlavors && !IPCTransferableDataItemHasKnownFlavor(item)) {
7845 NS_WARNING(
7846 "Ignoring unknown flavor in "
7847 "nsContentUtils::IPCTransferableDataToTransferable");
7848 continue;
7851 if (aAddDataFlavor) {
7852 aTransferable->AddDataFlavor(item.flavor().get());
7855 nsCOMPtr<nsISupports> transferData;
7856 switch (item.data().type()) {
7857 case IPCTransferableDataType::TIPCTransferableDataString: {
7858 const auto& data = item.data().get_IPCTransferableDataString();
7859 nsCOMPtr<nsISupportsString> dataWrapper =
7860 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
7861 NS_ENSURE_SUCCESS(rv, rv);
7862 rv = dataWrapper->SetData(nsDependentSubstring(
7863 reinterpret_cast<const char16_t*>(data.data().Data()),
7864 data.data().Size() / sizeof(char16_t)));
7865 NS_ENSURE_SUCCESS(rv, rv);
7866 transferData = dataWrapper;
7867 break;
7869 case IPCTransferableDataType::TIPCTransferableDataCString: {
7870 const auto& data = item.data().get_IPCTransferableDataCString();
7871 nsCOMPtr<nsISupportsCString> dataWrapper =
7872 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
7873 NS_ENSURE_SUCCESS(rv, rv);
7874 rv = dataWrapper->SetData(nsDependentCSubstring(
7875 reinterpret_cast<const char*>(data.data().Data()),
7876 data.data().Size()));
7877 NS_ENSURE_SUCCESS(rv, rv);
7878 transferData = dataWrapper;
7879 break;
7881 case IPCTransferableDataType::TIPCTransferableDataInputStream: {
7882 const auto& data = item.data().get_IPCTransferableDataInputStream();
7883 nsCOMPtr<nsIInputStream> stream;
7884 rv = NS_NewByteInputStream(getter_AddRefs(stream),
7885 AsChars(data.data().AsSpan()),
7886 NS_ASSIGNMENT_COPY);
7887 NS_ENSURE_SUCCESS(rv, rv);
7888 transferData = stream.forget();
7889 break;
7891 case IPCTransferableDataType::TIPCTransferableDataImageContainer: {
7892 const auto& data = item.data().get_IPCTransferableDataImageContainer();
7893 nsCOMPtr<imgIContainer> container;
7894 rv = DeserializeTransferableDataImageContainer(
7895 data, getter_AddRefs(container));
7896 NS_ENSURE_SUCCESS(rv, rv);
7897 transferData = container;
7898 break;
7900 case IPCTransferableDataType::TIPCTransferableDataBlob: {
7901 const auto& data = item.data().get_IPCTransferableDataBlob();
7902 transferData = IPCBlobUtils::Deserialize(data.blob());
7903 break;
7905 case IPCTransferableDataType::T__None:
7906 MOZ_ASSERT_UNREACHABLE();
7907 return NS_ERROR_FAILURE;
7910 rv = aTransferable->SetTransferData(item.flavor().get(), transferData);
7911 NS_ENSURE_SUCCESS(rv, rv);
7913 return NS_OK;
7916 nsresult nsContentUtils::IPCTransferableDataToTransferable(
7917 const IPCTransferableData& aTransferableData, const bool& aIsPrivateData,
7918 nsIPrincipal* aRequestingPrincipal,
7919 const nsContentPolicyType& aContentPolicyType, bool aAddDataFlavor,
7920 nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
7921 // Note that we need to set privacy status of transferable before adding any
7922 // data into it.
7923 aTransferable->SetIsPrivateData(aIsPrivateData);
7925 nsresult rv = IPCTransferableDataToTransferable(
7926 aTransferableData, aAddDataFlavor, aTransferable, aFilterUnknownFlavors);
7927 NS_ENSURE_SUCCESS(rv, rv);
7929 aTransferable->SetRequestingPrincipal(aRequestingPrincipal);
7930 aTransferable->SetContentPolicyType(aContentPolicyType);
7931 return NS_OK;
7934 nsresult nsContentUtils::IPCTransferableToTransferable(
7935 const IPCTransferable& aIPCTransferable, bool aAddDataFlavor,
7936 nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
7937 nsresult rv = IPCTransferableDataToTransferable(
7938 aIPCTransferable.data(), aIPCTransferable.isPrivateData(),
7939 aIPCTransferable.requestingPrincipal(),
7940 aIPCTransferable.contentPolicyType(), aAddDataFlavor, aTransferable,
7941 aFilterUnknownFlavors);
7942 NS_ENSURE_SUCCESS(rv, rv);
7944 if (aIPCTransferable.cookieJarSettings().isSome()) {
7945 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
7946 net::CookieJarSettings::Deserialize(
7947 aIPCTransferable.cookieJarSettings().ref(),
7948 getter_AddRefs(cookieJarSettings));
7949 aTransferable->SetCookieJarSettings(cookieJarSettings);
7951 aTransferable->SetReferrerInfo(aIPCTransferable.referrerInfo());
7952 return NS_OK;
7955 nsresult nsContentUtils::IPCTransferableDataItemToVariant(
7956 const IPCTransferableDataItem& aItem, nsIWritableVariant* aVariant) {
7957 MOZ_ASSERT(aVariant);
7959 switch (aItem.data().type()) {
7960 case IPCTransferableDataType::TIPCTransferableDataString: {
7961 const auto& data = aItem.data().get_IPCTransferableDataString();
7962 return aVariant->SetAsAString(nsDependentSubstring(
7963 reinterpret_cast<const char16_t*>(data.data().Data()),
7964 data.data().Size() / sizeof(char16_t)));
7966 case IPCTransferableDataType::TIPCTransferableDataCString: {
7967 const auto& data = aItem.data().get_IPCTransferableDataCString();
7968 return aVariant->SetAsACString(nsDependentCSubstring(
7969 reinterpret_cast<const char*>(data.data().Data()),
7970 data.data().Size()));
7972 case IPCTransferableDataType::TIPCTransferableDataInputStream: {
7973 const auto& data = aItem.data().get_IPCTransferableDataInputStream();
7974 nsCOMPtr<nsIInputStream> stream;
7975 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
7976 AsChars(data.data().AsSpan()),
7977 NS_ASSIGNMENT_COPY);
7978 NS_ENSURE_SUCCESS(rv, rv);
7979 return aVariant->SetAsISupports(stream);
7981 case IPCTransferableDataType::TIPCTransferableDataImageContainer: {
7982 const auto& data = aItem.data().get_IPCTransferableDataImageContainer();
7983 nsCOMPtr<imgIContainer> container;
7984 nsresult rv = DeserializeTransferableDataImageContainer(
7985 data, getter_AddRefs(container));
7986 NS_ENSURE_SUCCESS(rv, rv);
7987 return aVariant->SetAsISupports(container);
7989 case IPCTransferableDataType::TIPCTransferableDataBlob: {
7990 const auto& data = aItem.data().get_IPCTransferableDataBlob();
7991 RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(data.blob());
7992 return aVariant->SetAsISupports(blobImpl);
7994 case IPCTransferableDataType::T__None:
7995 break;
7998 MOZ_ASSERT_UNREACHABLE();
7999 return NS_ERROR_UNEXPECTED;
8002 void nsContentUtils::TransferablesToIPCTransferableDatas(
8003 nsIArray* aTransferables, nsTArray<IPCTransferableData>& aIPC,
8004 bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
8005 aIPC.Clear();
8006 if (aTransferables) {
8007 uint32_t transferableCount = 0;
8008 aTransferables->GetLength(&transferableCount);
8009 for (uint32_t i = 0; i < transferableCount; ++i) {
8010 IPCTransferableData* dt = aIPC.AppendElement();
8011 nsCOMPtr<nsITransferable> transferable =
8012 do_QueryElementAt(aTransferables, i);
8013 TransferableToIPCTransferableData(transferable, dt, aInSyncMessage,
8014 aParent);
8019 nsresult nsContentUtils::CalculateBufferSizeForImage(
8020 const uint32_t& aStride, const IntSize& aImageSize,
8021 const SurfaceFormat& aFormat, size_t* aMaxBufferSize,
8022 size_t* aUsedBufferSize) {
8023 CheckedInt32 requiredBytes =
8024 CheckedInt32(aStride) * CheckedInt32(aImageSize.height);
8026 CheckedInt32 usedBytes =
8027 requiredBytes - aStride +
8028 (CheckedInt32(aImageSize.width) * BytesPerPixel(aFormat));
8029 if (!usedBytes.isValid()) {
8030 return NS_ERROR_FAILURE;
8033 MOZ_ASSERT(requiredBytes.isValid(), "usedBytes valid but not required?");
8034 *aMaxBufferSize = requiredBytes.value();
8035 *aUsedBufferSize = usedBytes.value();
8036 return NS_OK;
8039 static already_AddRefed<DataSourceSurface> BigBufferToDataSurface(
8040 BigBuffer& aData, uint32_t aStride, const IntSize& aImageSize,
8041 SurfaceFormat aFormat) {
8042 if (!aData.Size() || !aImageSize.width || !aImageSize.height) {
8043 return nullptr;
8046 // Validate shared memory buffer size
8047 size_t imageBufLen = 0;
8048 size_t maxBufLen = 0;
8049 if (NS_FAILED(nsContentUtils::CalculateBufferSizeForImage(
8050 aStride, aImageSize, aFormat, &maxBufLen, &imageBufLen))) {
8051 return nullptr;
8053 if (imageBufLen > aData.Size()) {
8054 return nullptr;
8056 return CreateDataSourceSurfaceFromData(aImageSize, aFormat, aData.Data(),
8057 aStride);
8060 nsresult nsContentUtils::DeserializeTransferableDataImageContainer(
8061 const IPCTransferableDataImageContainer& aData,
8062 imgIContainer** aContainer) {
8063 const IntSize size(aData.width(), aData.height());
8064 size_t maxBufferSize = 0;
8065 size_t usedBufferSize = 0;
8066 nsresult rv = CalculateBufferSizeForImage(
8067 aData.stride(), size, aData.format(), &maxBufferSize, &usedBufferSize);
8068 NS_ENSURE_SUCCESS(rv, rv);
8069 if (usedBufferSize > aData.data().Size()) {
8070 return NS_ERROR_FAILURE;
8072 RefPtr<DataSourceSurface> surface =
8073 CreateDataSourceSurfaceFromData(size, aData.format(), aData.data().Data(),
8074 static_cast<int32_t>(aData.stride()));
8075 if (!surface) {
8076 return NS_ERROR_FAILURE;
8078 RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size);
8079 nsCOMPtr<imgIContainer> imageContainer =
8080 image::ImageOps::CreateFromDrawable(drawable);
8081 imageContainer.forget(aContainer);
8083 return NS_OK;
8086 bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
8087 return aFlavor.EqualsLiteral(kNativeImageMime) ||
8088 aFlavor.EqualsLiteral(kJPEGImageMime) ||
8089 aFlavor.EqualsLiteral(kJPGImageMime) ||
8090 aFlavor.EqualsLiteral(kPNGImageMime) ||
8091 aFlavor.EqualsLiteral(kGIFImageMime);
8094 // FIXME: This can probably be removed once bug 1783240 lands, as `nsString`
8095 // will be implicitly serialized in shmem when sent over IPDL directly.
8096 static IPCTransferableDataString AsIPCTransferableDataString(
8097 Span<const char16_t> aInput) {
8098 return IPCTransferableDataString{BigBuffer(AsBytes(aInput))};
8101 // FIXME: This can probably be removed once bug 1783240 lands, as `nsCString`
8102 // will be implicitly serialized in shmem when sent over IPDL directly.
8103 static IPCTransferableDataCString AsIPCTransferableDataCString(
8104 Span<const char> aInput) {
8105 return IPCTransferableDataCString{BigBuffer(AsBytes(aInput))};
8108 void nsContentUtils::TransferableToIPCTransferableData(
8109 nsITransferable* aTransferable, IPCTransferableData* aTransferableData,
8110 bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
8111 MOZ_ASSERT_IF(XRE_IsParentProcess(), aParent);
8113 if (aTransferable) {
8114 nsTArray<nsCString> flavorList;
8115 aTransferable->FlavorsTransferableCanExport(flavorList);
8117 for (uint32_t j = 0; j < flavorList.Length(); ++j) {
8118 nsCString& flavorStr = flavorList[j];
8119 if (!flavorStr.Length()) {
8120 continue;
8123 nsCOMPtr<nsISupports> data;
8124 nsresult rv =
8125 aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data));
8127 if (NS_FAILED(rv) || !data) {
8128 if (aInSyncMessage) {
8129 // Can't do anything.
8130 // FIXME: This shouldn't be the case anymore!
8131 continue;
8134 // This is a hack to support kFilePromiseMime.
8135 // On Windows there just needs to be an entry for it,
8136 // and for OSX we need to create
8137 // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
8138 if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
8139 IPCTransferableDataItem* item =
8140 aTransferableData->items().AppendElement();
8141 item->flavor() = flavorStr;
8142 item->data() =
8143 AsIPCTransferableDataString(NS_ConvertUTF8toUTF16(flavorStr));
8144 continue;
8147 // Empty element, transfer only the flavor
8148 IPCTransferableDataItem* item =
8149 aTransferableData->items().AppendElement();
8150 item->flavor() = flavorStr;
8151 item->data() = AsIPCTransferableDataString(EmptyString());
8152 continue;
8155 // We need to handle nsIInputStream before nsISupportsCString, otherwise
8156 // nsStringInputStream would be converted into a wrong type.
8157 if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
8158 IPCTransferableDataItem* item =
8159 aTransferableData->items().AppendElement();
8160 item->flavor() = flavorStr;
8161 nsCString imageData;
8162 DebugOnly<nsresult> rv =
8163 NS_ConsumeStream(stream, UINT32_MAX, imageData);
8164 MOZ_ASSERT(
8165 rv != NS_BASE_STREAM_WOULD_BLOCK,
8166 "cannot use async input streams in nsITransferable right now");
8167 // FIXME: This can probably be simplified once bug 1783240 lands, as
8168 // `nsCString` will be implicitly serialized in shmem when sent over
8169 // IPDL directly.
8170 item->data() =
8171 IPCTransferableDataInputStream(BigBuffer(AsBytes(Span(imageData))));
8172 continue;
8175 if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(data)) {
8176 nsAutoString dataAsString;
8177 MOZ_ALWAYS_SUCCEEDS(text->GetData(dataAsString));
8179 IPCTransferableDataItem* item =
8180 aTransferableData->items().AppendElement();
8181 item->flavor() = flavorStr;
8182 item->data() = AsIPCTransferableDataString(dataAsString);
8183 continue;
8186 if (nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data)) {
8187 nsAutoCString dataAsString;
8188 MOZ_ALWAYS_SUCCEEDS(ctext->GetData(dataAsString));
8190 IPCTransferableDataItem* item =
8191 aTransferableData->items().AppendElement();
8192 item->flavor() = flavorStr;
8193 item->data() = AsIPCTransferableDataCString(dataAsString);
8194 continue;
8197 if (nsCOMPtr<imgIContainer> image = do_QueryInterface(data)) {
8198 // Images to be placed on the clipboard are imgIContainers.
8199 RefPtr<mozilla::gfx::SourceSurface> surface = image->GetFrame(
8200 imgIContainer::FRAME_CURRENT,
8201 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
8202 if (!surface) {
8203 continue;
8205 RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
8206 surface->GetDataSurface();
8207 if (!dataSurface) {
8208 continue;
8210 size_t length;
8211 int32_t stride;
8212 Maybe<BigBuffer> surfaceData =
8213 GetSurfaceData(*dataSurface, &length, &stride);
8215 if (surfaceData.isNothing()) {
8216 continue;
8219 IPCTransferableDataItem* item =
8220 aTransferableData->items().AppendElement();
8221 item->flavor() = flavorStr;
8223 mozilla::gfx::IntSize size = dataSurface->GetSize();
8224 item->data() = IPCTransferableDataImageContainer(
8225 std::move(*surfaceData), size.width, size.height, stride,
8226 dataSurface->GetFormat());
8227 continue;
8230 // Otherwise, handle this as a file.
8231 nsCOMPtr<BlobImpl> blobImpl;
8232 if (nsCOMPtr<nsIFile> file = do_QueryInterface(data)) {
8233 if (aParent) {
8234 bool isDir = false;
8235 if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
8236 nsAutoString path;
8237 if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
8238 continue;
8241 RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
8242 fss->GrantAccessToContentProcess(aParent->ChildID(), path);
8246 blobImpl = new FileBlobImpl(file);
8248 IgnoredErrorResult rv;
8250 // Ensure that file data is cached no that the content process
8251 // has this data available to it when passed over:
8252 blobImpl->GetSize(rv);
8253 if (NS_WARN_IF(rv.Failed())) {
8254 continue;
8257 blobImpl->GetLastModified(rv);
8258 if (NS_WARN_IF(rv.Failed())) {
8259 continue;
8261 } else {
8262 if (aInSyncMessage) {
8263 // Can't do anything.
8264 // FIXME: This shouldn't be the case anymore!
8265 continue;
8268 blobImpl = do_QueryInterface(data);
8271 if (blobImpl) {
8272 // If we failed to create the blob actor, then this blob probably
8273 // can't get the file size for the underlying file, ignore it for
8274 // now. TODO pass this through anyway.
8275 IPCBlob ipcBlob;
8276 nsresult rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);
8277 if (NS_WARN_IF(NS_FAILED(rv))) {
8278 continue;
8281 IPCTransferableDataItem* item =
8282 aTransferableData->items().AppendElement();
8283 item->flavor() = flavorStr;
8284 item->data() = IPCTransferableDataBlob(ipcBlob);
8290 void nsContentUtils::TransferableToIPCTransferable(
8291 nsITransferable* aTransferable, IPCTransferable* aIPCTransferable,
8292 bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
8293 IPCTransferableData ipcTransferableData;
8294 TransferableToIPCTransferableData(aTransferable, &ipcTransferableData,
8295 aInSyncMessage, aParent);
8297 Maybe<net::CookieJarSettingsArgs> cookieJarSettingsArgs;
8298 if (nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
8299 aTransferable->GetCookieJarSettings()) {
8300 net::CookieJarSettingsArgs args;
8301 net::CookieJarSettings::Cast(cookieJarSettings)->Serialize(args);
8302 cookieJarSettingsArgs = Some(std::move(args));
8305 aIPCTransferable->data() = std::move(ipcTransferableData);
8306 aIPCTransferable->isPrivateData() = aTransferable->GetIsPrivateData();
8307 aIPCTransferable->requestingPrincipal() =
8308 aTransferable->GetRequestingPrincipal();
8309 aIPCTransferable->cookieJarSettings() = std::move(cookieJarSettingsArgs);
8310 aIPCTransferable->contentPolicyType() = aTransferable->GetContentPolicyType();
8311 aIPCTransferable->referrerInfo() = aTransferable->GetReferrerInfo();
8314 Maybe<BigBuffer> nsContentUtils::GetSurfaceData(DataSourceSurface& aSurface,
8315 size_t* aLength,
8316 int32_t* aStride) {
8317 mozilla::gfx::DataSourceSurface::MappedSurface map;
8318 if (!aSurface.Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map)) {
8319 return Nothing();
8322 size_t bufLen = 0;
8323 size_t maxBufLen = 0;
8324 nsresult rv = nsContentUtils::CalculateBufferSizeForImage(
8325 map.mStride, aSurface.GetSize(), aSurface.GetFormat(), &maxBufLen,
8326 &bufLen);
8327 if (NS_FAILED(rv)) {
8328 aSurface.Unmap();
8329 return Nothing();
8332 BigBuffer surfaceData(maxBufLen);
8333 memcpy(surfaceData.Data(), map.mData, bufLen);
8334 memset(surfaceData.Data() + bufLen, 0, maxBufLen - bufLen);
8336 *aLength = maxBufLen;
8337 *aStride = map.mStride;
8339 aSurface.Unmap();
8340 return Some(std::move(surfaceData));
8343 Maybe<IPCImage> nsContentUtils::SurfaceToIPCImage(DataSourceSurface& aSurface) {
8344 size_t len = 0;
8345 int32_t stride = 0;
8346 auto mem = GetSurfaceData(aSurface, &len, &stride);
8347 if (!mem) {
8348 return Nothing();
8350 return Some(IPCImage{std::move(*mem), uint32_t(stride), aSurface.GetFormat(),
8351 ImageIntSize::FromUnknownSize(aSurface.GetSize())});
8354 already_AddRefed<DataSourceSurface> nsContentUtils::IPCImageToSurface(
8355 IPCImage&& aImage) {
8356 return BigBufferToDataSurface(aImage.data(), aImage.stride(),
8357 aImage.size().ToUnknownSize(), aImage.format());
8360 Modifiers nsContentUtils::GetWidgetModifiers(int32_t aModifiers) {
8361 Modifiers result = 0;
8362 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SHIFT) {
8363 result |= mozilla::MODIFIER_SHIFT;
8365 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CONTROL) {
8366 result |= mozilla::MODIFIER_CONTROL;
8368 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALT) {
8369 result |= mozilla::MODIFIER_ALT;
8371 if (aModifiers & nsIDOMWindowUtils::MODIFIER_META) {
8372 result |= mozilla::MODIFIER_META;
8374 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALTGRAPH) {
8375 result |= mozilla::MODIFIER_ALTGRAPH;
8377 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CAPSLOCK) {
8378 result |= mozilla::MODIFIER_CAPSLOCK;
8380 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FN) {
8381 result |= mozilla::MODIFIER_FN;
8383 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FNLOCK) {
8384 result |= mozilla::MODIFIER_FNLOCK;
8386 if (aModifiers & nsIDOMWindowUtils::MODIFIER_NUMLOCK) {
8387 result |= mozilla::MODIFIER_NUMLOCK;
8389 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SCROLLLOCK) {
8390 result |= mozilla::MODIFIER_SCROLLLOCK;
8392 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOL) {
8393 result |= mozilla::MODIFIER_SYMBOL;
8395 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK) {
8396 result |= mozilla::MODIFIER_SYMBOLLOCK;
8398 if (aModifiers & nsIDOMWindowUtils::MODIFIER_OS) {
8399 result |= mozilla::MODIFIER_OS;
8401 return result;
8404 nsIWidget* nsContentUtils::GetWidget(PresShell* aPresShell, nsPoint* aOffset) {
8405 if (!aPresShell) {
8406 return nullptr;
8408 nsIFrame* frame = aPresShell->GetRootFrame();
8409 if (!frame) {
8410 return nullptr;
8412 return frame->GetView()->GetNearestWidget(aOffset);
8415 int16_t nsContentUtils::GetButtonsFlagForButton(int32_t aButton) {
8416 switch (aButton) {
8417 case -1:
8418 return MouseButtonsFlag::eNoButtons;
8419 case MouseButton::ePrimary:
8420 return MouseButtonsFlag::ePrimaryFlag;
8421 case MouseButton::eMiddle:
8422 return MouseButtonsFlag::eMiddleFlag;
8423 case MouseButton::eSecondary:
8424 return MouseButtonsFlag::eSecondaryFlag;
8425 case 3:
8426 return MouseButtonsFlag::e4thFlag;
8427 case 4:
8428 return MouseButtonsFlag::e5thFlag;
8429 case MouseButton::eEraser:
8430 return MouseButtonsFlag::eEraserFlag;
8431 default:
8432 NS_ERROR("Button not known.");
8433 return 0;
8437 LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(
8438 const CSSPoint& aPoint, const nsPoint& aOffset,
8439 nsPresContext* aPresContext) {
8440 nsPoint layoutRelative = CSSPoint::ToAppUnits(aPoint) + aOffset;
8441 nsPoint visualRelative =
8442 ViewportUtils::LayoutToVisual(layoutRelative, aPresContext->PresShell());
8443 return LayoutDeviceIntPoint::FromAppUnitsRounded(
8444 visualRelative, aPresContext->AppUnitsPerDevPixel());
8447 nsView* nsContentUtils::GetViewToDispatchEvent(nsPresContext* aPresContext,
8448 PresShell** aPresShell) {
8449 if (!aPresContext || !aPresShell) {
8450 return nullptr;
8452 RefPtr<PresShell> presShell = aPresContext->PresShell();
8453 if (NS_WARN_IF(!presShell)) {
8454 *aPresShell = nullptr;
8455 return nullptr;
8457 nsViewManager* viewManager = presShell->GetViewManager();
8458 if (!viewManager) {
8459 presShell.forget(aPresShell); // XXX Is this intentional?
8460 return nullptr;
8462 presShell.forget(aPresShell);
8463 return viewManager->GetRootView();
8466 nsresult nsContentUtils::SendMouseEvent(
8467 mozilla::PresShell* aPresShell, const nsAString& aType, float aX, float aY,
8468 int32_t aButton, int32_t aButtons, int32_t aClickCount, int32_t aModifiers,
8469 bool aIgnoreRootScrollFrame, float aPressure,
8470 unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
8471 PreventDefaultResult* aPreventDefault, bool aIsDOMEventSynthesized,
8472 bool aIsWidgetEventSynthesized) {
8473 nsPoint offset;
8474 nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
8475 if (!widget) return NS_ERROR_FAILURE;
8477 EventMessage msg;
8478 Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
8479 bool contextMenuKey = false;
8480 if (aType.EqualsLiteral("mousedown")) {
8481 msg = eMouseDown;
8482 } else if (aType.EqualsLiteral("mouseup")) {
8483 msg = eMouseUp;
8484 } else if (aType.EqualsLiteral("mousemove")) {
8485 msg = eMouseMove;
8486 } else if (aType.EqualsLiteral("mouseover")) {
8487 msg = eMouseEnterIntoWidget;
8488 } else if (aType.EqualsLiteral("mouseout")) {
8489 msg = eMouseExitFromWidget;
8490 exitFrom = Some(WidgetMouseEvent::ePlatformChild);
8491 } else if (aType.EqualsLiteral("mousecancel")) {
8492 msg = eMouseExitFromWidget;
8493 exitFrom = Some(XRE_IsParentProcess() ? WidgetMouseEvent::ePlatformTopLevel
8494 : WidgetMouseEvent::ePuppet);
8495 } else if (aType.EqualsLiteral("mouselongtap")) {
8496 msg = eMouseLongTap;
8497 } else if (aType.EqualsLiteral("contextmenu")) {
8498 msg = eContextMenu;
8499 contextMenuKey = (aButton == 0);
8500 } else if (aType.EqualsLiteral("MozMouseHittest")) {
8501 msg = eMouseHitTest;
8502 } else if (aType.EqualsLiteral("MozMouseExploreByTouch")) {
8503 msg = eMouseExploreByTouch;
8504 } else {
8505 return NS_ERROR_FAILURE;
8508 if (aInputSourceArg == MouseEvent_Binding::MOZ_SOURCE_UNKNOWN) {
8509 aInputSourceArg = MouseEvent_Binding::MOZ_SOURCE_MOUSE;
8512 WidgetMouseEvent event(true, msg, widget,
8513 aIsWidgetEventSynthesized
8514 ? WidgetMouseEvent::eSynthesized
8515 : WidgetMouseEvent::eReal,
8516 contextMenuKey ? WidgetMouseEvent::eContextMenuKey
8517 : WidgetMouseEvent::eNormal);
8518 event.pointerId = aIdentifier;
8519 event.mModifiers = GetWidgetModifiers(aModifiers);
8520 event.mButton = aButton;
8521 event.mButtons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED
8522 ? aButtons
8523 : msg == eMouseUp ? 0
8524 : GetButtonsFlagForButton(aButton);
8525 event.mPressure = aPressure;
8526 event.mInputSource = aInputSourceArg;
8527 event.mClickCount = aClickCount;
8528 event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
8529 event.mExitFrom = exitFrom;
8531 nsPresContext* presContext = aPresShell->GetPresContext();
8532 if (!presContext) return NS_ERROR_FAILURE;
8534 event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
8535 event.mIgnoreRootScrollFrame = aIgnoreRootScrollFrame;
8537 nsEventStatus status = nsEventStatus_eIgnore;
8538 if (aToWindow) {
8539 RefPtr<PresShell> presShell;
8540 nsView* view =
8541 GetViewToDispatchEvent(presContext, getter_AddRefs(presShell));
8542 if (!presShell || !view) {
8543 return NS_ERROR_FAILURE;
8545 return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
8547 if (StaticPrefs::test_events_async_enabled()) {
8548 status = widget->DispatchInputEvent(&event).mContentStatus;
8549 } else {
8550 nsresult rv = widget->DispatchEvent(&event, status);
8551 NS_ENSURE_SUCCESS(rv, rv);
8553 if (aPreventDefault) {
8554 if (status == nsEventStatus_eConsumeNoDefault) {
8555 if (event.mFlags.mDefaultPreventedByContent) {
8556 *aPreventDefault = PreventDefaultResult::ByContent;
8557 } else {
8558 *aPreventDefault = PreventDefaultResult::ByChrome;
8560 } else {
8561 *aPreventDefault = PreventDefaultResult::No;
8565 return NS_OK;
8568 /* static */
8569 void nsContentUtils::FirePageHideEventForFrameLoaderSwap(
8570 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8571 bool aOnlySystemGroup) {
8572 MOZ_DIAGNOSTIC_ASSERT(aItem);
8573 MOZ_DIAGNOSTIC_ASSERT(aChromeEventHandler);
8575 RefPtr<Document> doc = aItem->GetDocument();
8576 NS_ASSERTION(doc, "What happened here?");
8577 doc->OnPageHide(true, aChromeEventHandler, aOnlySystemGroup);
8579 int32_t childCount = 0;
8580 aItem->GetInProcessChildCount(&childCount);
8581 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8582 kids.AppendElements(childCount);
8583 for (int32_t i = 0; i < childCount; ++i) {
8584 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8587 for (uint32_t i = 0; i < kids.Length(); ++i) {
8588 if (kids[i]) {
8589 FirePageHideEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8590 aOnlySystemGroup);
8595 // The pageshow event is fired for a given document only if IsShowing() returns
8596 // the same thing as aFireIfShowing. This gives us a way to fire pageshow only
8597 // on documents that are still loading or only on documents that are already
8598 // loaded.
8599 /* static */
8600 void nsContentUtils::FirePageShowEventForFrameLoaderSwap(
8601 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8602 bool aFireIfShowing, bool aOnlySystemGroup) {
8603 int32_t childCount = 0;
8604 aItem->GetInProcessChildCount(&childCount);
8605 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8606 kids.AppendElements(childCount);
8607 for (int32_t i = 0; i < childCount; ++i) {
8608 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8611 for (uint32_t i = 0; i < kids.Length(); ++i) {
8612 if (kids[i]) {
8613 FirePageShowEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8614 aFireIfShowing, aOnlySystemGroup);
8618 RefPtr<Document> doc = aItem->GetDocument();
8619 NS_ASSERTION(doc, "What happened here?");
8620 if (doc->IsShowing() == aFireIfShowing) {
8621 doc->OnPageShow(true, aChromeEventHandler, aOnlySystemGroup);
8625 /* static */
8626 already_AddRefed<nsPIWindowRoot> nsContentUtils::GetWindowRoot(Document* aDoc) {
8627 if (aDoc) {
8628 if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
8629 return win->GetTopWindowRoot();
8632 return nullptr;
8635 /* static */
8636 bool nsContentUtils::LinkContextIsURI(const nsAString& aAnchor,
8637 nsIURI* aDocURI) {
8638 if (aAnchor.IsEmpty()) {
8639 // anchor parameter not present or empty -> same document reference
8640 return true;
8643 // the document URI might contain a fragment identifier ("#...')
8644 // we want to ignore that because it's invisible to the server
8645 // and just affects the local interpretation in the recipient
8646 nsCOMPtr<nsIURI> contextUri;
8647 nsresult rv = NS_GetURIWithoutRef(aDocURI, getter_AddRefs(contextUri));
8649 if (NS_FAILED(rv)) {
8650 // copying failed
8651 return false;
8654 // resolve anchor against context
8655 nsCOMPtr<nsIURI> resolvedUri;
8656 rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor, nullptr, contextUri);
8658 if (NS_FAILED(rv)) {
8659 // resolving failed
8660 return false;
8663 bool same;
8664 rv = contextUri->Equals(resolvedUri, &same);
8665 if (NS_FAILED(rv)) {
8666 // comparison failed
8667 return false;
8670 return same;
8673 /* static */
8674 bool nsContentUtils::IsPreloadType(nsContentPolicyType aType) {
8675 return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
8676 aType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD ||
8677 aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
8678 aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD ||
8679 aType == nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD ||
8680 aType == nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD);
8683 // static
8684 ReferrerPolicy nsContentUtils::GetReferrerPolicyFromChannel(
8685 nsIChannel* aChannel) {
8686 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
8687 if (!httpChannel) {
8688 return ReferrerPolicy::_empty;
8691 nsresult rv;
8692 nsAutoCString headerValue;
8693 rv = httpChannel->GetResponseHeader("referrer-policy"_ns, headerValue);
8694 if (NS_FAILED(rv) || headerValue.IsEmpty()) {
8695 return ReferrerPolicy::_empty;
8698 return ReferrerInfo::ReferrerPolicyFromHeaderString(
8699 NS_ConvertUTF8toUTF16(headerValue));
8702 // static
8703 bool nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel) {
8704 nsLoadFlags loadFlags = 0;
8705 aChannel->GetLoadFlags(&loadFlags);
8706 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
8707 return true;
8710 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
8711 nsContentPolicyType type = loadInfo->InternalContentPolicyType();
8712 return IsNonSubresourceInternalPolicyType(type);
8715 // static
8716 bool nsContentUtils::IsNonSubresourceInternalPolicyType(
8717 nsContentPolicyType aType) {
8718 return aType == nsIContentPolicy::TYPE_DOCUMENT ||
8719 aType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
8720 aType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
8721 aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
8722 aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
8725 // static public
8726 bool nsContentUtils::IsThirdPartyTrackingResourceWindow(
8727 nsPIDOMWindowInner* aWindow) {
8728 MOZ_ASSERT(aWindow);
8730 Document* document = aWindow->GetExtantDoc();
8731 if (!document) {
8732 return false;
8735 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8736 do_QueryInterface(document->GetChannel());
8737 if (!classifiedChannel) {
8738 return false;
8741 return classifiedChannel->IsThirdPartyTrackingResource();
8744 // static public
8745 bool nsContentUtils::IsFirstPartyTrackingResourceWindow(
8746 nsPIDOMWindowInner* aWindow) {
8747 MOZ_ASSERT(aWindow);
8749 Document* document = aWindow->GetExtantDoc();
8750 if (!document) {
8751 return false;
8754 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8755 do_QueryInterface(document->GetChannel());
8756 if (!classifiedChannel) {
8757 return false;
8760 uint32_t classificationFlags =
8761 classifiedChannel->GetFirstPartyClassificationFlags();
8763 return mozilla::net::UrlClassifierCommon::IsTrackingClassificationFlag(
8764 classificationFlags, NS_UsePrivateBrowsing(document->GetChannel()));
8767 namespace {
8769 // We put StringBuilder in the anonymous namespace to prevent anything outside
8770 // this file from accidentally being linked against it.
8771 class BulkAppender {
8772 using size_type = typename nsAString::size_type;
8774 public:
8775 explicit BulkAppender(BulkWriteHandle<char16_t>&& aHandle)
8776 : mHandle(std::move(aHandle)), mPosition(0) {}
8777 ~BulkAppender() = default;
8779 template <int N>
8780 void AppendLiteral(const char16_t (&aStr)[N]) {
8781 size_t len = N - 1;
8782 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8783 memcpy(mHandle.Elements() + mPosition, aStr, len * sizeof(char16_t));
8784 mPosition += len;
8787 void Append(Span<const char16_t> aStr) {
8788 size_t len = aStr.Length();
8789 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8790 // Both mHandle.Elements() and aStr.Elements() are guaranteed
8791 // to be non-null (by the string implementation and by Span,
8792 // respectively), so not checking the pointers for null before
8793 // memcpy does not lead to UB even if len was zero.
8794 memcpy(mHandle.Elements() + mPosition, aStr.Elements(),
8795 len * sizeof(char16_t));
8796 mPosition += len;
8799 void Append(Span<const char> aStr) {
8800 size_t len = aStr.Length();
8801 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8802 ConvertLatin1toUtf16(aStr, mHandle.AsSpan().From(mPosition));
8803 mPosition += len;
8806 void Finish() { mHandle.Finish(mPosition, false); }
8808 private:
8809 BulkWriteHandle<char16_t> mHandle;
8810 size_type mPosition;
8813 class StringBuilder {
8814 private:
8815 class Unit {
8816 public:
8817 Unit() : mAtom(nullptr) { MOZ_COUNT_CTOR(StringBuilder::Unit); }
8818 ~Unit() {
8819 if (mType == Type::String || mType == Type::StringWithEncode) {
8820 mString.~nsString();
8822 MOZ_COUNT_DTOR(StringBuilder::Unit);
8825 enum class Type : uint8_t {
8826 Unknown,
8827 Atom,
8828 String,
8829 StringWithEncode,
8830 Literal,
8831 TextFragment,
8832 TextFragmentWithEncode,
8835 union {
8836 nsAtom* mAtom;
8837 const char16_t* mLiteral;
8838 nsString mString;
8839 const nsTextFragment* mTextFragment;
8841 uint32_t mLength = 0;
8842 Type mType = Type::Unknown;
8845 static_assert(sizeof(void*) != 8 || sizeof(Unit) <= 3 * sizeof(void*),
8846 "Unit should remain small");
8848 public:
8849 // Try to keep the size of StringBuilder close to a jemalloc bucket size (the
8850 // 16kb one in this case).
8851 static constexpr uint32_t TARGET_SIZE = 16 * 1024;
8853 // The number of units we need to remove from the inline buffer so that the
8854 // rest of the builder members fit. A more precise approach would be to
8855 // calculate that extra size and use (TARGET_SIZE - OTHER_SIZE) / sizeof(Unit)
8856 // or so, but this is simpler.
8857 static constexpr uint32_t PADDING_UNITS = sizeof(void*) == 8 ? 1 : 2;
8859 static constexpr uint32_t STRING_BUFFER_UNITS =
8860 TARGET_SIZE / sizeof(Unit) - PADDING_UNITS;
8862 StringBuilder() : mLast(this), mLength(0) { MOZ_COUNT_CTOR(StringBuilder); }
8864 MOZ_COUNTED_DTOR(StringBuilder)
8866 void Append(nsAtom* aAtom) {
8867 Unit* u = AddUnit();
8868 u->mAtom = aAtom;
8869 u->mType = Unit::Type::Atom;
8870 uint32_t len = aAtom->GetLength();
8871 u->mLength = len;
8872 mLength += len;
8875 template <int N>
8876 void Append(const char16_t (&aLiteral)[N]) {
8877 Unit* u = AddUnit();
8878 u->mLiteral = aLiteral;
8879 u->mType = Unit::Type::Literal;
8880 uint32_t len = N - 1;
8881 u->mLength = len;
8882 mLength += len;
8885 void Append(nsString&& aString) {
8886 Unit* u = AddUnit();
8887 uint32_t len = aString.Length();
8888 new (&u->mString) nsString(std::move(aString));
8889 u->mType = Unit::Type::String;
8890 u->mLength = len;
8891 mLength += len;
8894 void AppendWithAttrEncode(nsString&& aString, uint32_t aLen) {
8895 Unit* u = AddUnit();
8896 new (&u->mString) nsString(std::move(aString));
8897 u->mType = Unit::Type::StringWithEncode;
8898 u->mLength = aLen;
8899 mLength += aLen;
8902 void Append(const nsTextFragment* aTextFragment) {
8903 Unit* u = AddUnit();
8904 u->mTextFragment = aTextFragment;
8905 u->mType = Unit::Type::TextFragment;
8906 uint32_t len = aTextFragment->GetLength();
8907 u->mLength = len;
8908 mLength += len;
8911 void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen) {
8912 Unit* u = AddUnit();
8913 u->mTextFragment = aTextFragment;
8914 u->mType = Unit::Type::TextFragmentWithEncode;
8915 u->mLength = aLen;
8916 mLength += aLen;
8919 bool ToString(nsAString& aOut) {
8920 if (!mLength.isValid()) {
8921 return false;
8923 auto appenderOrErr = aOut.BulkWrite(mLength.value(), 0, true);
8924 if (appenderOrErr.isErr()) {
8925 return false;
8928 BulkAppender appender{appenderOrErr.unwrap()};
8930 for (StringBuilder* current = this; current;
8931 current = current->mNext.get()) {
8932 uint32_t len = current->mUnits.Length();
8933 for (uint32_t i = 0; i < len; ++i) {
8934 Unit& u = current->mUnits[i];
8935 switch (u.mType) {
8936 case Unit::Type::Atom:
8937 appender.Append(*(u.mAtom));
8938 break;
8939 case Unit::Type::String:
8940 appender.Append(u.mString);
8941 break;
8942 case Unit::Type::StringWithEncode:
8943 EncodeAttrString(u.mString, appender);
8944 break;
8945 case Unit::Type::Literal:
8946 appender.Append(Span(u.mLiteral, u.mLength));
8947 break;
8948 case Unit::Type::TextFragment:
8949 if (u.mTextFragment->Is2b()) {
8950 appender.Append(
8951 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()));
8952 } else {
8953 appender.Append(
8954 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()));
8956 break;
8957 case Unit::Type::TextFragmentWithEncode:
8958 if (u.mTextFragment->Is2b()) {
8959 EncodeTextFragment(
8960 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()),
8961 appender);
8962 } else {
8963 EncodeTextFragment(
8964 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()),
8965 appender);
8967 break;
8968 default:
8969 MOZ_CRASH("Unknown unit type?");
8973 appender.Finish();
8974 return true;
8977 private:
8978 Unit* AddUnit() {
8979 if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
8980 new StringBuilder(this);
8982 return mLast->mUnits.AppendElement();
8985 explicit StringBuilder(StringBuilder* aFirst) : mLast(nullptr), mLength(0) {
8986 MOZ_COUNT_CTOR(StringBuilder);
8987 aFirst->mLast->mNext = WrapUnique(this);
8988 aFirst->mLast = this;
8991 void EncodeAttrString(Span<const char16_t> aStr, BulkAppender& aAppender) {
8992 size_t flushedUntil = 0;
8993 size_t currentPosition = 0;
8994 for (char16_t c : aStr) {
8995 switch (c) {
8996 case '"':
8997 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8998 aAppender.AppendLiteral(u"&quot;");
8999 flushedUntil = currentPosition + 1;
9000 break;
9001 case '&':
9002 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9003 aAppender.AppendLiteral(u"&amp;");
9004 flushedUntil = currentPosition + 1;
9005 break;
9006 case 0x00A0:
9007 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9008 aAppender.AppendLiteral(u"&nbsp;");
9009 flushedUntil = currentPosition + 1;
9010 break;
9011 default:
9012 break;
9014 currentPosition++;
9016 if (currentPosition > flushedUntil) {
9017 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9021 template <class T>
9022 void EncodeTextFragment(Span<const T> aStr, BulkAppender& aAppender) {
9023 size_t flushedUntil = 0;
9024 size_t currentPosition = 0;
9025 for (T c : aStr) {
9026 switch (c) {
9027 case '<':
9028 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9029 aAppender.AppendLiteral(u"&lt;");
9030 flushedUntil = currentPosition + 1;
9031 break;
9032 case '>':
9033 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9034 aAppender.AppendLiteral(u"&gt;");
9035 flushedUntil = currentPosition + 1;
9036 break;
9037 case '&':
9038 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9039 aAppender.AppendLiteral(u"&amp;");
9040 flushedUntil = currentPosition + 1;
9041 break;
9042 case T(0xA0):
9043 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9044 aAppender.AppendLiteral(u"&nbsp;");
9045 flushedUntil = currentPosition + 1;
9046 break;
9047 default:
9048 break;
9050 currentPosition++;
9052 if (currentPosition > flushedUntil) {
9053 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
9057 AutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
9058 UniquePtr<StringBuilder> mNext;
9059 StringBuilder* mLast;
9060 // mLength is used only in the first StringBuilder object in the linked list.
9061 CheckedInt<uint32_t> mLength;
9064 static_assert(sizeof(StringBuilder) <= StringBuilder::TARGET_SIZE,
9065 "StringBuilder should fit in the target bucket");
9067 } // namespace
9069 static void AppendEncodedCharacters(const nsTextFragment* aText,
9070 StringBuilder& aBuilder) {
9071 uint32_t extraSpaceNeeded = 0;
9072 uint32_t len = aText->GetLength();
9073 if (aText->Is2b()) {
9074 const char16_t* data = aText->Get2b();
9075 for (uint32_t i = 0; i < len; ++i) {
9076 const char16_t c = data[i];
9077 switch (c) {
9078 case '<':
9079 extraSpaceNeeded += ArrayLength("&lt;") - 2;
9080 break;
9081 case '>':
9082 extraSpaceNeeded += ArrayLength("&gt;") - 2;
9083 break;
9084 case '&':
9085 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9086 break;
9087 case 0x00A0:
9088 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9089 break;
9090 default:
9091 break;
9094 } else {
9095 const char* data = aText->Get1b();
9096 for (uint32_t i = 0; i < len; ++i) {
9097 const unsigned char c = data[i];
9098 switch (c) {
9099 case '<':
9100 extraSpaceNeeded += ArrayLength("&lt;") - 2;
9101 break;
9102 case '>':
9103 extraSpaceNeeded += ArrayLength("&gt;") - 2;
9104 break;
9105 case '&':
9106 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9107 break;
9108 case 0x00A0:
9109 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9110 break;
9111 default:
9112 break;
9117 if (extraSpaceNeeded) {
9118 aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
9119 } else {
9120 aBuilder.Append(aText);
9124 static uint32_t ExtraSpaceNeededForAttrEncoding(const nsAString& aValue) {
9125 const char16_t* c = aValue.BeginReading();
9126 const char16_t* end = aValue.EndReading();
9128 uint32_t extraSpaceNeeded = 0;
9129 while (c < end) {
9130 switch (*c) {
9131 case '"':
9132 extraSpaceNeeded += ArrayLength("&quot;") - 2;
9133 break;
9134 case '&':
9135 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9136 break;
9137 case 0x00A0:
9138 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9139 break;
9140 default:
9141 break;
9143 ++c;
9146 return extraSpaceNeeded;
9149 static void AppendEncodedAttributeValue(const nsAttrValue& aValue,
9150 StringBuilder& aBuilder) {
9151 if (nsAtom* atom = aValue.GetStoredAtom()) {
9152 nsDependentAtomString atomStr(atom);
9153 uint32_t space = ExtraSpaceNeededForAttrEncoding(atomStr);
9154 if (!space) {
9155 aBuilder.Append(atom);
9156 } else {
9157 aBuilder.AppendWithAttrEncode(nsString(atomStr),
9158 atomStr.Length() + space);
9160 return;
9162 // NOTE(emilio): In most cases this will just be a reference to the stored
9163 // nsStringBuffer.
9164 nsString str;
9165 aValue.ToString(str);
9166 uint32_t space = ExtraSpaceNeededForAttrEncoding(str);
9167 if (space) {
9168 aBuilder.AppendWithAttrEncode(std::move(str), str.Length() + space);
9169 } else {
9170 aBuilder.Append(std::move(str));
9174 static void StartElement(Element* aElement, StringBuilder& aBuilder) {
9175 nsAtom* localName = aElement->NodeInfo()->NameAtom();
9176 const int32_t tagNS = aElement->GetNameSpaceID();
9178 aBuilder.Append(u"<");
9179 if (tagNS == kNameSpaceID_XHTML || tagNS == kNameSpaceID_SVG ||
9180 tagNS == kNameSpaceID_MathML) {
9181 aBuilder.Append(localName);
9182 } else {
9183 aBuilder.Append(nsString(aElement->NodeName()));
9186 if (CustomElementData* ceData = aElement->GetCustomElementData()) {
9187 nsAtom* isAttr = ceData->GetIs(aElement);
9188 if (isAttr && !aElement->HasAttr(nsGkAtoms::is)) {
9189 aBuilder.Append(uR"( is=")");
9190 aBuilder.Append(isAttr);
9191 aBuilder.Append(uR"(")");
9195 uint32_t i = 0;
9196 while (BorrowedAttrInfo info = aElement->GetAttrInfoAt(i++)) {
9197 const nsAttrName* name = info.mName;
9199 int32_t attNs = name->NamespaceID();
9200 nsAtom* attName = name->LocalName();
9202 // Filter out any attribute starting with [-|_]moz
9203 // FIXME(emilio): Do we still need this?
9204 nsDependentAtomString attrNameStr(attName);
9205 if (StringBeginsWith(attrNameStr, u"_moz"_ns) ||
9206 StringBeginsWith(attrNameStr, u"-moz"_ns)) {
9207 continue;
9210 aBuilder.Append(u" ");
9212 if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
9213 (attNs == kNameSpaceID_XMLNS && attName == nsGkAtoms::xmlns)) {
9214 // Nothing else required
9215 } else if (attNs == kNameSpaceID_XML) {
9216 aBuilder.Append(u"xml:");
9217 } else if (attNs == kNameSpaceID_XMLNS) {
9218 aBuilder.Append(u"xmlns:");
9219 } else if (attNs == kNameSpaceID_XLink) {
9220 aBuilder.Append(u"xlink:");
9221 } else if (nsAtom* prefix = name->GetPrefix()) {
9222 aBuilder.Append(prefix);
9223 aBuilder.Append(u":");
9226 aBuilder.Append(attName);
9227 aBuilder.Append(uR"(=")");
9228 AppendEncodedAttributeValue(*info.mValue, aBuilder);
9229 aBuilder.Append(uR"(")");
9232 aBuilder.Append(u">");
9235 // Per HTML spec we should append one \n if the first child of
9236 // pre/textarea/listing is a textnode and starts with a \n.
9237 // But because browsers haven't traditionally had that behavior,
9238 // we're not changing our behavior either - yet.
9239 if (aContent->IsHTMLElement()) {
9240 if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
9241 localName == nsGkAtoms::listing) {
9242 nsIContent* fc = aContent->GetFirstChild();
9243 if (fc &&
9244 (fc->NodeType() == nsINode::TEXT_NODE ||
9245 fc->NodeType() == nsINode::CDATA_SECTION_NODE)) {
9246 const nsTextFragment* text = fc->GetText();
9247 if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
9248 aBuilder.Append("\n");
9255 static inline bool ShouldEscape(nsIContent* aParent) {
9256 if (!aParent || !aParent->IsHTMLElement()) {
9257 return true;
9260 static const nsAtom* nonEscapingElements[] = {
9261 nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
9262 nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
9263 nsGkAtoms::plaintext, nsGkAtoms::noscript};
9264 static mozilla::BitBloomFilter<12, nsAtom> sFilter;
9265 static bool sInitialized = false;
9266 if (!sInitialized) {
9267 sInitialized = true;
9268 for (auto& nonEscapingElement : nonEscapingElements) {
9269 sFilter.add(nonEscapingElement);
9273 nsAtom* tag = aParent->NodeInfo()->NameAtom();
9274 if (sFilter.mightContain(tag)) {
9275 for (auto& nonEscapingElement : nonEscapingElements) {
9276 if (tag == nonEscapingElement) {
9277 if (MOZ_UNLIKELY(tag == nsGkAtoms::noscript) &&
9278 MOZ_UNLIKELY(!aParent->OwnerDoc()->IsScriptEnabled())) {
9279 return true;
9281 return false;
9285 return true;
9288 static inline bool IsVoidTag(Element* aElement) {
9289 if (!aElement->IsHTMLElement()) {
9290 return false;
9292 return FragmentOrElement::IsHTMLVoid(aElement->NodeInfo()->NameAtom());
9295 bool nsContentUtils::SerializeNodeToMarkup(nsINode* aRoot,
9296 bool aDescendantsOnly,
9297 nsAString& aOut) {
9298 // If you pass in a DOCUMENT_NODE, you must pass aDescendentsOnly as true
9299 MOZ_ASSERT(aDescendantsOnly || aRoot->NodeType() != nsINode::DOCUMENT_NODE);
9301 nsINode* current =
9302 aDescendantsOnly ? aRoot->GetFirstChildOfTemplateOrNode() : aRoot;
9304 if (!current) {
9305 return true;
9308 StringBuilder builder;
9309 nsIContent* next;
9310 while (true) {
9311 bool isVoid = false;
9312 switch (current->NodeType()) {
9313 case nsINode::ELEMENT_NODE: {
9314 Element* elem = current->AsElement();
9315 StartElement(elem, builder);
9316 isVoid = IsVoidTag(elem);
9317 if (!isVoid && (next = current->GetFirstChildOfTemplateOrNode())) {
9318 current = next;
9319 continue;
9321 break;
9324 case nsINode::TEXT_NODE:
9325 case nsINode::CDATA_SECTION_NODE: {
9326 const nsTextFragment* text = &current->AsText()->TextFragment();
9327 nsIContent* parent = current->GetParent();
9328 if (ShouldEscape(parent)) {
9329 AppendEncodedCharacters(text, builder);
9330 } else {
9331 builder.Append(text);
9333 break;
9336 case nsINode::COMMENT_NODE: {
9337 builder.Append(u"<!--");
9338 builder.Append(static_cast<nsIContent*>(current)->GetText());
9339 builder.Append(u"-->");
9340 break;
9343 case nsINode::DOCUMENT_TYPE_NODE: {
9344 builder.Append(u"<!DOCTYPE ");
9345 builder.Append(nsString(current->NodeName()));
9346 builder.Append(u">");
9347 break;
9350 case nsINode::PROCESSING_INSTRUCTION_NODE: {
9351 builder.Append(u"<?");
9352 builder.Append(nsString(current->NodeName()));
9353 builder.Append(u" ");
9354 builder.Append(static_cast<nsIContent*>(current)->GetText());
9355 builder.Append(u">");
9356 break;
9360 while (true) {
9361 if (!isVoid && current->NodeType() == nsINode::ELEMENT_NODE) {
9362 builder.Append(u"</");
9363 nsIContent* elem = static_cast<nsIContent*>(current);
9364 if (elem->IsHTMLElement() || elem->IsSVGElement() ||
9365 elem->IsMathMLElement()) {
9366 builder.Append(elem->NodeInfo()->NameAtom());
9367 } else {
9368 builder.Append(nsString(current->NodeName()));
9370 builder.Append(u">");
9372 isVoid = false;
9374 if (current == aRoot) {
9375 return builder.ToString(aOut);
9378 if ((next = current->GetNextSibling())) {
9379 current = next;
9380 break;
9383 current = current->GetParentNode();
9385 // Handle template element. If the parent is a template's content,
9386 // then adjust the parent to be the template element.
9387 if (current != aRoot &&
9388 current->NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
9389 DocumentFragment* frag = static_cast<DocumentFragment*>(current);
9390 nsIContent* fragHost = frag->GetHost();
9391 if (fragHost && fragHost->IsTemplateElement()) {
9392 current = fragHost;
9396 if (aDescendantsOnly && current == aRoot) {
9397 return builder.ToString(aOut);
9403 bool nsContentUtils::IsSpecificAboutPage(JSObject* aGlobal, const char* aUri) {
9404 // aUri must start with about: or this isn't the right function to be using.
9405 MOZ_ASSERT(strncmp(aUri, "about:", 6) == 0);
9407 // Make sure the global is a window
9408 MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
9409 nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
9410 if (!win) {
9411 return false;
9414 nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
9415 NS_ENSURE_TRUE(principal, false);
9417 // First check the scheme to avoid getting long specs in the common case.
9418 if (!principal->SchemeIs("about")) {
9419 return false;
9422 nsAutoCString spec;
9423 principal->GetAsciiSpec(spec);
9425 return spec.EqualsASCII(aUri);
9428 /* static */
9429 void nsContentUtils::SetScrollbarsVisibility(nsIDocShell* aDocShell,
9430 bool aVisible) {
9431 if (!aDocShell) {
9432 return;
9434 auto pref = aVisible ? ScrollbarPreference::Auto : ScrollbarPreference::Never;
9435 nsDocShell::Cast(aDocShell)->SetScrollbarPreference(pref);
9438 /* static */
9439 nsIDocShell* nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget) {
9440 if (!aTarget) {
9441 return nullptr;
9444 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
9445 if (nsCOMPtr<nsINode> node = nsINode::FromEventTarget(aTarget)) {
9446 bool ignore;
9447 innerWindow =
9448 do_QueryInterface(node->OwnerDoc()->GetScriptHandlingObject(ignore));
9449 } else if ((innerWindow = nsPIDOMWindowInner::FromEventTarget(aTarget))) {
9450 // Nothing else to do
9451 } else {
9452 nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(aTarget);
9453 if (helper) {
9454 innerWindow = helper->GetOwner();
9458 if (innerWindow) {
9459 return innerWindow->GetDocShell();
9462 return nullptr;
9466 * Note: this function only relates to figuring out HTTPS state, which is an
9467 * input to the Secure Context algorithm. We are not actually implementing any
9468 * part of the Secure Context algorithm itself here.
9470 * This is a bit of a hack. Ideally we'd propagate HTTPS state through
9471 * nsIChannel as described in the Fetch and HTML specs, but making channels
9472 * know about whether they should inherit HTTPS state, propagating information
9473 * about who the channel's "client" is, exposing GetHttpsState API on channels
9474 * and modifying the various cache implementations to store and retrieve HTTPS
9475 * state involves a huge amount of code (see bug 1220687). We avoid that for
9476 * now using this function.
9478 * This function takes advantage of the observation that we can return true if
9479 * nsIContentSecurityManager::IsOriginPotentiallyTrustworthy returns true for
9480 * the document's origin (e.g. the origin has a scheme of 'https' or host
9481 * 'localhost' etc.). Since we generally propagate a creator document's origin
9482 * onto data:, blob:, etc. documents, this works for them too.
9484 * The scenario where this observation breaks down is sandboxing without the
9485 * 'allow-same-origin' flag, since in this case a document is given a unique
9486 * origin (IsOriginPotentiallyTrustworthy would return false). We handle that
9487 * by using the origin that the document would have had had it not been
9488 * sandboxed.
9490 * DEFICIENCIES: Note that this function uses nsIScriptSecurityManager's
9491 * getChannelResultPrincipalIfNotSandboxed, and that method's ignoring of
9492 * sandboxing is limited to the immediate sandbox. In the case that aDocument
9493 * should inherit its origin (e.g. data: URI) but its parent has ended up
9494 * with a unique origin due to sandboxing further up the parent chain we may
9495 * end up returning false when we would ideally return true (since we will
9496 * examine the parent's origin for 'https' and not finding it.) This means
9497 * that we may restrict the privileges of some pages unnecessarily in this
9498 * edge case.
9500 /* static */
9501 bool nsContentUtils::HttpsStateIsModern(Document* aDocument) {
9502 if (!aDocument) {
9503 return false;
9506 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
9508 if (principal->IsSystemPrincipal()) {
9509 return true;
9512 // If aDocument is sandboxed, try and get the principal that it would have
9513 // been given had it not been sandboxed:
9514 if (principal->GetIsNullPrincipal() &&
9515 (aDocument->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
9516 nsIChannel* channel = aDocument->GetChannel();
9517 if (channel) {
9518 nsCOMPtr<nsIScriptSecurityManager> ssm =
9519 nsContentUtils::GetSecurityManager();
9520 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9521 channel, getter_AddRefs(principal));
9522 if (NS_FAILED(rv)) {
9523 return false;
9525 if (principal->IsSystemPrincipal()) {
9526 // If a document with the system principal is sandboxing a subdocument
9527 // that would normally inherit the embedding element's principal (e.g.
9528 // a srcdoc document) then the embedding document does not trust the
9529 // content that is written to the embedded document. Unlike when the
9530 // embedding document is https, in this case we have no indication as
9531 // to whether the embedded document's contents are delivered securely
9532 // or not, and the sandboxing would possibly indicate that they were
9533 // not. To play it safe we return false here. (See bug 1162772
9534 // comment 73-80.)
9535 return false;
9540 if (principal->GetIsNullPrincipal()) {
9541 return false;
9544 MOZ_ASSERT(principal->GetIsContentPrincipal());
9546 return principal->GetIsOriginPotentiallyTrustworthy();
9549 /* static */
9550 bool nsContentUtils::ComputeIsSecureContext(nsIChannel* aChannel) {
9551 MOZ_ASSERT(aChannel);
9553 nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
9554 nsCOMPtr<nsIPrincipal> principal;
9555 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9556 aChannel, getter_AddRefs(principal));
9557 if (NS_FAILED(rv)) {
9558 return false;
9561 const RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
9563 if (principal->IsSystemPrincipal()) {
9564 // If the load would've been sandboxed, treat this load as an untrusted
9565 // load, as system code considers sandboxed resources insecure.
9566 return !loadInfo->GetLoadingSandboxed();
9569 if (principal->GetIsNullPrincipal()) {
9570 return false;
9573 if (const RefPtr<WindowContext> windowContext =
9574 WindowContext::GetById(loadInfo->GetInnerWindowID())) {
9575 if (!windowContext->GetIsSecureContext()) {
9576 return false;
9580 return principal->GetIsOriginPotentiallyTrustworthy();
9583 /* static */
9584 void nsContentUtils::TryToUpgradeElement(Element* aElement) {
9585 NodeInfo* nodeInfo = aElement->NodeInfo();
9586 RefPtr<nsAtom> typeAtom =
9587 aElement->GetCustomElementData()->GetCustomElementType();
9589 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9590 CustomElementDefinition* definition =
9591 nsContentUtils::LookupCustomElementDefinition(
9592 nodeInfo->GetDocument(), nodeInfo->NameAtom(),
9593 nodeInfo->NamespaceID(), typeAtom);
9594 if (definition) {
9595 nsContentUtils::EnqueueUpgradeReaction(aElement, definition);
9596 } else {
9597 // Add an unresolved custom element that is a candidate for upgrade when a
9598 // custom element is connected to the document.
9599 nsContentUtils::RegisterUnresolvedElement(aElement, typeAtom);
9603 MOZ_CAN_RUN_SCRIPT
9604 static void DoCustomElementCreate(Element** aElement, JSContext* aCx,
9605 Document* aDoc, NodeInfo* aNodeInfo,
9606 CustomElementConstructor* aConstructor,
9607 ErrorResult& aRv) {
9608 JS::Rooted<JS::Value> constructResult(aCx);
9609 aConstructor->Construct(&constructResult, aRv, "Custom Element Create",
9610 CallbackFunction::eRethrowExceptions);
9611 if (aRv.Failed()) {
9612 return;
9615 RefPtr<Element> element;
9616 // constructResult is an ObjectValue because construction with a callback
9617 // always forms the return value from a JSObject.
9618 UNWRAP_OBJECT(Element, &constructResult, element);
9619 if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9620 if (!element || !element->IsHTMLElement()) {
9621 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9622 "HTMLElement");
9623 return;
9625 } else {
9626 if (!element || !element->IsXULElement()) {
9627 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9628 "XULElement");
9629 return;
9633 nsAtom* localName = aNodeInfo->NameAtom();
9635 if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
9636 element->HasChildren() || element->GetAttrCount() ||
9637 element->NodeInfo()->NameAtom() != localName) {
9638 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
9639 return;
9642 element.forget(aElement);
9645 /* static */
9646 nsresult nsContentUtils::NewXULOrHTMLElement(
9647 Element** aResult, mozilla::dom::NodeInfo* aNodeInfo,
9648 FromParser aFromParser, nsAtom* aIsAtom,
9649 mozilla::dom::CustomElementDefinition* aDefinition) {
9650 RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
9651 MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
9652 nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
9653 "Can only create XUL or XHTML elements.");
9655 nsAtom* name = nodeInfo->NameAtom();
9656 int32_t tag = eHTMLTag_unknown;
9657 bool isCustomElementName = false;
9658 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9659 tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
9660 isCustomElementName =
9661 (tag == eHTMLTag_userdefined &&
9662 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML));
9663 } else { // kNameSpaceID_XUL
9664 if (aIsAtom) {
9665 // Make sure the customized built-in element to be constructed confirms
9666 // to our naming requirement, i.e. [is] must be a dashed name and
9667 // the tag name must not.
9668 // if so, set isCustomElementName to false to kick off all the logics
9669 // that pick up aIsAtom.
9670 if (nsContentUtils::IsNameWithDash(aIsAtom) &&
9671 !nsContentUtils::IsNameWithDash(name)) {
9672 isCustomElementName = false;
9673 } else {
9674 isCustomElementName =
9675 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9677 } else {
9678 isCustomElementName =
9679 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9683 nsAtom* tagAtom = nodeInfo->NameAtom();
9684 nsAtom* typeAtom = nullptr;
9685 bool isCustomElement = isCustomElementName || aIsAtom;
9686 if (isCustomElement) {
9687 typeAtom = isCustomElementName ? tagAtom : aIsAtom;
9690 MOZ_ASSERT_IF(aDefinition, isCustomElement);
9692 // https://dom.spec.whatwg.org/#concept-create-element
9693 // We only handle the "synchronous custom elements flag is set" now.
9694 // For the unset case (e.g. cloning a node), see bug 1319342 for that.
9695 // Step 4.
9696 RefPtr<CustomElementDefinition> definition = aDefinition;
9697 if (isCustomElement && !definition) {
9698 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9699 definition = nsContentUtils::LookupCustomElementDefinition(
9700 nodeInfo->GetDocument(), nodeInfo->NameAtom(), nodeInfo->NamespaceID(),
9701 typeAtom);
9704 // It might be a problem that parser synchronously calls constructor, so filed
9705 // bug 1378079 to figure out what we should do for parser case.
9706 if (definition) {
9708 * Synchronous custom elements flag is determined by 3 places in spec,
9709 * 1) create an element for a token, the flag is determined by
9710 * "will execute script" which is not originally created
9711 * for the HTML fragment parsing algorithm.
9712 * 2) createElement and createElementNS, the flag is the same as
9713 * NOT_FROM_PARSER.
9714 * 3) clone a node, our implementation will not go into this function.
9715 * For the unset case which is non-synchronous only applied for
9716 * inner/outerHTML.
9718 bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT;
9719 // Per discussion in https://github.com/w3c/webcomponents/issues/635,
9720 // use entry global in those places that are called from JS APIs and use the
9721 // node document's global object if it is called from parser.
9722 nsIGlobalObject* global;
9723 if (aFromParser == dom::NOT_FROM_PARSER) {
9724 global = GetEntryGlobal();
9726 // Documents created from the PrototypeDocumentSink always use
9727 // NOT_FROM_PARSER for non-XUL elements. We can get the global from the
9728 // document in that case.
9729 if (!global) {
9730 Document* doc = nodeInfo->GetDocument();
9731 if (doc && doc->LoadedFromPrototype()) {
9732 global = doc->GetScopeObject();
9735 } else {
9736 global = nodeInfo->GetDocument()->GetScopeObject();
9738 if (!global) {
9739 // In browser chrome code, one may have access to a document which doesn't
9740 // have scope object anymore.
9741 return NS_ERROR_FAILURE;
9744 AutoAllowLegacyScriptExecution exemption;
9745 AutoEntryScript aes(global, "create custom elements");
9746 JSContext* cx = aes.cx();
9747 ErrorResult rv;
9749 // Step 5.
9750 if (definition->IsCustomBuiltIn()) {
9751 // SetupCustomElement() should be called with an element that don't have
9752 // CustomElementData setup, if not we will hit the assertion in
9753 // SetCustomElementData().
9754 // Built-in element
9755 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9756 *aResult =
9757 CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9758 } else {
9759 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9761 (*aResult)->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom));
9762 if (synchronousCustomElements) {
9763 CustomElementRegistry::Upgrade(*aResult, definition, rv);
9764 if (rv.MaybeSetPendingException(cx)) {
9765 aes.ReportException();
9767 } else {
9768 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9771 return NS_OK;
9774 // Step 6.1.
9775 if (synchronousCustomElements) {
9776 definition->mPrefixStack.AppendElement(nodeInfo->GetPrefixAtom());
9777 RefPtr<Document> doc = nodeInfo->GetDocument();
9778 DoCustomElementCreate(aResult, cx, doc, nodeInfo,
9779 MOZ_KnownLive(definition->mConstructor), rv);
9780 if (rv.MaybeSetPendingException(cx)) {
9781 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9782 NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(),
9783 aFromParser));
9784 } else {
9785 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9787 (*aResult)->SetDefined(false);
9789 definition->mPrefixStack.RemoveLastElement();
9790 return NS_OK;
9793 // Step 6.2.
9794 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9795 NS_IF_ADDREF(*aResult =
9796 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9797 } else {
9798 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9800 (*aResult)->SetCustomElementData(
9801 MakeUnique<CustomElementData>(definition->mType));
9802 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9803 return NS_OK;
9806 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9807 // Per the Custom Element specification, unknown tags that are valid custom
9808 // element names should be HTMLElement instead of HTMLUnknownElement.
9809 if (isCustomElementName) {
9810 NS_IF_ADDREF(*aResult =
9811 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9812 } else {
9813 *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9815 } else {
9816 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9819 if (!*aResult) {
9820 return NS_ERROR_OUT_OF_MEMORY;
9823 if (isCustomElement) {
9824 (*aResult)->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom));
9825 nsContentUtils::RegisterCallbackUpgradeElement(*aResult, typeAtom);
9828 return NS_OK;
9831 CustomElementRegistry* nsContentUtils::GetCustomElementRegistry(
9832 Document* aDoc) {
9833 MOZ_ASSERT(aDoc);
9835 if (!aDoc->GetDocShell()) {
9836 return nullptr;
9839 nsPIDOMWindowInner* window = aDoc->GetInnerWindow();
9840 if (!window) {
9841 return nullptr;
9844 return window->CustomElements();
9847 /* static */
9848 CustomElementDefinition* nsContentUtils::LookupCustomElementDefinition(
9849 Document* aDoc, nsAtom* aNameAtom, uint32_t aNameSpaceID,
9850 nsAtom* aTypeAtom) {
9851 if (aNameSpaceID != kNameSpaceID_XUL && aNameSpaceID != kNameSpaceID_XHTML) {
9852 return nullptr;
9855 RefPtr<CustomElementRegistry> registry = GetCustomElementRegistry(aDoc);
9856 if (!registry) {
9857 return nullptr;
9860 return registry->LookupCustomElementDefinition(aNameAtom, aNameSpaceID,
9861 aTypeAtom);
9864 /* static */
9865 void nsContentUtils::RegisterCallbackUpgradeElement(Element* aElement,
9866 nsAtom* aTypeName) {
9867 MOZ_ASSERT(aElement);
9869 Document* doc = aElement->OwnerDoc();
9870 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9871 if (registry) {
9872 registry->RegisterCallbackUpgradeElement(aElement, aTypeName);
9876 /* static */
9877 void nsContentUtils::RegisterUnresolvedElement(Element* aElement,
9878 nsAtom* aTypeName) {
9879 MOZ_ASSERT(aElement);
9881 Document* doc = aElement->OwnerDoc();
9882 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9883 if (registry) {
9884 registry->RegisterUnresolvedElement(aElement, aTypeName);
9888 /* static */
9889 void nsContentUtils::UnregisterUnresolvedElement(Element* aElement) {
9890 MOZ_ASSERT(aElement);
9892 nsAtom* typeAtom = aElement->GetCustomElementData()->GetCustomElementType();
9893 Document* doc = aElement->OwnerDoc();
9894 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9895 if (registry) {
9896 registry->UnregisterUnresolvedElement(aElement, typeAtom);
9900 /* static */
9901 void nsContentUtils::EnqueueUpgradeReaction(
9902 Element* aElement, CustomElementDefinition* aDefinition) {
9903 MOZ_ASSERT(aElement);
9905 Document* doc = aElement->OwnerDoc();
9907 // No DocGroup means no custom element reactions stack.
9908 if (!doc->GetDocGroup()) {
9909 return;
9912 CustomElementReactionsStack* stack =
9913 doc->GetDocGroup()->CustomElementReactionsStack();
9914 stack->EnqueueUpgradeReaction(aElement, aDefinition);
9917 /* static */
9918 void nsContentUtils::EnqueueLifecycleCallback(
9919 ElementCallbackType aType, Element* aCustomElement,
9920 const LifecycleCallbackArgs& aArgs, CustomElementDefinition* aDefinition) {
9921 // No DocGroup means no custom element reactions stack.
9922 if (!aCustomElement->OwnerDoc()->GetDocGroup()) {
9923 return;
9926 CustomElementRegistry::EnqueueLifecycleCallback(aType, aCustomElement, aArgs,
9927 aDefinition);
9930 /* static */
9931 void nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
9932 Document* aDocument, nsTArray<nsIContent*>& aElements) {
9933 MOZ_ASSERT(aDocument);
9934 #ifdef DEBUG
9935 size_t oldLength = aElements.Length();
9936 #endif
9938 if (PresShell* presShell = aDocument->GetPresShell()) {
9939 if (nsIFrame* scrollFrame = presShell->GetRootScrollFrame()) {
9940 nsIAnonymousContentCreator* creator = do_QueryFrame(scrollFrame);
9941 MOZ_ASSERT(
9942 creator,
9943 "scroll frame should always implement nsIAnonymousContentCreator");
9944 creator->AppendAnonymousContentTo(aElements, 0);
9946 if (nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame()) {
9947 canvasFrame->AppendAnonymousContentTo(aElements, 0);
9951 #ifdef DEBUG
9952 for (size_t i = oldLength; i < aElements.Length(); i++) {
9953 MOZ_ASSERT(
9954 aElements[i]->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent),
9955 "Someone here has lied, or missed to flag the node");
9957 #endif
9960 static void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame,
9961 nsTArray<nsIContent*>& aKids,
9962 uint32_t aFlags) {
9963 if (nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame)) {
9964 ac->AppendAnonymousContentTo(aKids, aFlags);
9968 /* static */
9969 void nsContentUtils::AppendNativeAnonymousChildren(const nsIContent* aContent,
9970 nsTArray<nsIContent*>& aKids,
9971 uint32_t aFlags) {
9972 if (aContent->MayHaveAnonymousChildren()) {
9973 if (nsIFrame* primaryFrame = aContent->GetPrimaryFrame()) {
9974 // NAC created by the element's primary frame.
9975 AppendNativeAnonymousChildrenFromFrame(primaryFrame, aKids, aFlags);
9977 // NAC created by any other non-primary frames for the element.
9978 AutoTArray<nsIFrame::OwnedAnonBox, 8> ownedAnonBoxes;
9979 primaryFrame->AppendOwnedAnonBoxes(ownedAnonBoxes);
9980 for (nsIFrame::OwnedAnonBox& box : ownedAnonBoxes) {
9981 MOZ_ASSERT(box.mAnonBoxFrame->GetContent() == aContent);
9982 AppendNativeAnonymousChildrenFromFrame(box.mAnonBoxFrame, aKids,
9983 aFlags);
9987 // Get manually created NAC (editor resize handles, etc.).
9988 if (auto nac = static_cast<ManualNACArray*>(
9989 aContent->GetProperty(nsGkAtoms::manualNACProperty))) {
9990 aKids.AppendElements(*nac);
9994 // The root scroll frame is not the primary frame of the root element.
9995 // Detect and handle this case.
9996 if (!(aFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
9997 aContent == aContent->OwnerDoc()->GetRootElement()) {
9998 AppendDocumentLevelNativeAnonymousContentTo(aContent->OwnerDoc(), aKids);
10002 bool nsContentUtils::IsImageAvailable(nsIContent* aLoadingNode, nsIURI* aURI,
10003 nsIPrincipal* aDefaultTriggeringPrincipal,
10004 CORSMode aCORSMode) {
10005 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
10006 QueryTriggeringPrincipal(aLoadingNode, aDefaultTriggeringPrincipal,
10007 getter_AddRefs(triggeringPrincipal));
10008 MOZ_ASSERT(triggeringPrincipal);
10010 Document* doc = aLoadingNode->OwnerDoc();
10011 return IsImageAvailable(aURI, triggeringPrincipal, aCORSMode, doc);
10014 bool nsContentUtils::IsImageAvailable(nsIURI* aURI,
10015 nsIPrincipal* aTriggeringPrincipal,
10016 CORSMode aCORSMode, Document* aDoc) {
10017 imgLoader* imgLoader = GetImgLoaderForDocument(aDoc);
10018 return imgLoader->IsImageAvailable(aURI, aTriggeringPrincipal, aCORSMode,
10019 aDoc);
10022 /* static */
10023 bool nsContentUtils::QueryTriggeringPrincipal(
10024 nsIContent* aLoadingNode, nsIPrincipal* aDefaultPrincipal,
10025 nsIPrincipal** aTriggeringPrincipal) {
10026 MOZ_ASSERT(aLoadingNode);
10027 MOZ_ASSERT(aTriggeringPrincipal);
10029 bool result = false;
10030 nsCOMPtr<nsIPrincipal> loadingPrincipal = aDefaultPrincipal;
10031 if (!loadingPrincipal) {
10032 loadingPrincipal = aLoadingNode->NodePrincipal();
10035 // If aLoadingNode is content, bail out early.
10036 if (!aLoadingNode->NodePrincipal()->IsSystemPrincipal()) {
10037 loadingPrincipal.forget(aTriggeringPrincipal);
10038 return result;
10041 nsAutoString loadingStr;
10042 if (aLoadingNode->IsElement()) {
10043 aLoadingNode->AsElement()->GetAttr(
10044 kNameSpaceID_None, nsGkAtoms::triggeringprincipal, loadingStr);
10047 // Fall back if 'triggeringprincipal' isn't specified,
10048 if (loadingStr.IsEmpty()) {
10049 loadingPrincipal.forget(aTriggeringPrincipal);
10050 return result;
10053 nsCString binary;
10054 nsCOMPtr<nsIPrincipal> serializedPrin =
10055 BasePrincipal::FromJSON(NS_ConvertUTF16toUTF8(loadingStr));
10056 if (serializedPrin) {
10057 result = true;
10058 serializedPrin.forget(aTriggeringPrincipal);
10061 if (!result) {
10062 // Fallback if the deserialization is failed.
10063 loadingPrincipal.forget(aTriggeringPrincipal);
10066 return result;
10069 /* static */
10070 void nsContentUtils::GetContentPolicyTypeForUIImageLoading(
10071 nsIContent* aLoadingNode, nsIPrincipal** aTriggeringPrincipal,
10072 nsContentPolicyType& aContentPolicyType, uint64_t* aRequestContextID) {
10073 MOZ_ASSERT(aRequestContextID);
10075 bool result = QueryTriggeringPrincipal(aLoadingNode, aTriggeringPrincipal);
10076 if (result) {
10077 // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for
10078 // indicating it's a favicon loading.
10079 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
10081 nsAutoString requestContextID;
10082 if (aLoadingNode->IsElement()) {
10083 aLoadingNode->AsElement()->GetAttr(
10084 kNameSpaceID_None, nsGkAtoms::requestcontextid, requestContextID);
10086 nsresult rv;
10087 int64_t val = requestContextID.ToInteger64(&rv);
10088 *aRequestContextID = NS_SUCCEEDED(rv) ? val : 0;
10089 } else {
10090 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
10094 /* static */
10095 nsresult nsContentUtils::CreateJSValueFromSequenceOfObject(
10096 JSContext* aCx, const Sequence<JSObject*>& aTransfer,
10097 JS::MutableHandle<JS::Value> aValue) {
10098 if (aTransfer.IsEmpty()) {
10099 return NS_OK;
10102 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, aTransfer.Length()));
10103 if (!array) {
10104 return NS_ERROR_OUT_OF_MEMORY;
10107 for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
10108 JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
10109 if (!object) {
10110 continue;
10113 if (NS_WARN_IF(
10114 !JS_DefineElement(aCx, array, i, object, JSPROP_ENUMERATE))) {
10115 return NS_ERROR_OUT_OF_MEMORY;
10119 aValue.setObject(*array);
10120 return NS_OK;
10123 /* static */
10124 void nsContentUtils::StructuredClone(JSContext* aCx, nsIGlobalObject* aGlobal,
10125 JS::Handle<JS::Value> aValue,
10126 const StructuredSerializeOptions& aOptions,
10127 JS::MutableHandle<JS::Value> aRetval,
10128 ErrorResult& aError) {
10129 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
10130 aError = nsContentUtils::CreateJSValueFromSequenceOfObject(
10131 aCx, aOptions.mTransfer, &transferArray);
10132 if (NS_WARN_IF(aError.Failed())) {
10133 return;
10136 JS::CloneDataPolicy clonePolicy;
10137 // We are definitely staying in the same agent cluster.
10138 clonePolicy.allowIntraClusterClonableSharedObjects();
10139 if (aGlobal->IsSharedMemoryAllowed()) {
10140 clonePolicy.allowSharedMemoryObjects();
10143 StructuredCloneHolder holder(StructuredCloneHolder::CloningSupported,
10144 StructuredCloneHolder::TransferringSupported,
10145 JS::StructuredCloneScope::SameProcess);
10146 holder.Write(aCx, aValue, transferArray, clonePolicy, aError);
10147 if (NS_WARN_IF(aError.Failed())) {
10148 return;
10151 holder.Read(aGlobal, aCx, aRetval, clonePolicy, aError);
10152 if (NS_WARN_IF(aError.Failed())) {
10153 return;
10156 nsTArray<RefPtr<MessagePort>> ports = holder.TakeTransferredPorts();
10157 Unused << ports;
10160 /* static */
10161 bool nsContentUtils::ShouldBlockReservedKeys(WidgetKeyboardEvent* aKeyEvent) {
10162 nsCOMPtr<nsIPrincipal> principal;
10163 RefPtr<Element> targetElement =
10164 Element::FromEventTargetOrNull(aKeyEvent->mOriginalTarget);
10165 nsCOMPtr<nsIBrowser> targetBrowser;
10166 if (targetElement) {
10167 targetBrowser = targetElement->AsBrowser();
10169 bool isRemoteBrowser = false;
10170 if (targetBrowser) {
10171 targetBrowser->GetIsRemoteBrowser(&isRemoteBrowser);
10174 if (isRemoteBrowser) {
10175 targetBrowser->GetContentPrincipal(getter_AddRefs(principal));
10176 return principal ? nsContentUtils::IsSitePermDeny(principal, "shortcuts"_ns)
10177 : false;
10180 if (targetElement) {
10181 Document* doc = targetElement->GetUncomposedDoc();
10182 if (doc) {
10183 RefPtr<WindowContext> wc = doc->GetWindowContext();
10184 if (wc) {
10185 return wc->TopWindowContext()->GetShortcutsPermission() ==
10186 nsIPermissionManager::DENY_ACTION;
10191 return false;
10195 * Checks whether the given type is a supported document type for
10196 * loading within the nsObjectLoadingContent specified by aContent.
10198 * NOTE Helper method for nsContentUtils::HtmlObjectContentTypeForMIMEType.
10199 * NOTE Does not take content policy or capabilities into account
10201 static bool HtmlObjectContentSupportsDocument(const nsCString& aMimeType) {
10202 nsCOMPtr<nsIWebNavigationInfo> info(
10203 do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID));
10204 if (!info) {
10205 return false;
10208 uint32_t supported;
10209 nsresult rv = info->IsTypeSupported(aMimeType, &supported);
10211 if (NS_FAILED(rv)) {
10212 return false;
10215 if (supported != nsIWebNavigationInfo::UNSUPPORTED) {
10216 // Don't want to support plugins as documents
10217 return supported != nsIWebNavigationInfo::FALLBACK;
10220 // Try a stream converter
10221 // NOTE: We treat any type we can convert from as a supported type. If a
10222 // type is not actually supported, the URI loader will detect that and
10223 // return an error, and we'll fallback.
10224 nsCOMPtr<nsIStreamConverterService> convServ =
10225 do_GetService("@mozilla.org/streamConverters;1");
10226 bool canConvert = false;
10227 if (convServ) {
10228 rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
10230 return NS_SUCCEEDED(rv) && canConvert;
10233 /* static */
10234 already_AddRefed<nsIPluginTag> nsContentUtils::PluginTagForType(
10235 const nsCString& aMIMEType, bool aNoFakePlugin) {
10236 RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
10237 nsCOMPtr<nsIPluginTag> tag;
10238 NS_ENSURE_TRUE(pluginHost, nullptr);
10240 // ShouldPlay will handle the case where the plugin is disabled
10241 pluginHost->GetPluginTagForType(
10242 aMIMEType,
10243 aNoFakePlugin ? nsPluginHost::eExcludeFake : nsPluginHost::eExcludeNone,
10244 getter_AddRefs(tag));
10246 return tag.forget();
10249 /* static */
10250 uint32_t nsContentUtils::HtmlObjectContentTypeForMIMEType(
10251 const nsCString& aMIMEType, bool aNoFakePlugin) {
10252 if (aMIMEType.IsEmpty()) {
10253 return nsIObjectLoadingContent::TYPE_NULL;
10256 if (imgLoader::SupportImageWithMimeType(aMIMEType)) {
10257 return ResolveObjectType(nsIObjectLoadingContent::TYPE_IMAGE);
10260 // Faking support of the PDF content as a document for EMBED tags
10261 // when internal PDF viewer is enabled.
10262 if (aMIMEType.LowerCaseEqualsLiteral("application/pdf") && IsPDFJSEnabled()) {
10263 return nsIObjectLoadingContent::TYPE_DOCUMENT;
10266 if (HtmlObjectContentSupportsDocument(aMIMEType)) {
10267 return nsIObjectLoadingContent::TYPE_DOCUMENT;
10270 bool isSpecialPlugin = nsPluginHost::GetSpecialType(aMIMEType) !=
10271 nsPluginHost::eSpecialType_None;
10272 if (isSpecialPlugin) {
10273 return nsIObjectLoadingContent::TYPE_FALLBACK;
10276 return nsIObjectLoadingContent::TYPE_NULL;
10279 /* static */
10280 already_AddRefed<nsISerialEventTarget> nsContentUtils::GetEventTargetByLoadInfo(
10281 nsILoadInfo* aLoadInfo, TaskCategory aCategory) {
10282 if (NS_WARN_IF(!aLoadInfo)) {
10283 return nullptr;
10286 RefPtr<Document> doc;
10287 aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
10288 nsCOMPtr<nsISerialEventTarget> target;
10289 if (doc) {
10290 if (DocGroup* group = doc->GetDocGroup()) {
10291 target = group->EventTargetFor(aCategory);
10293 } else {
10294 target = GetMainThreadSerialEventTarget();
10297 return target.forget();
10300 /* static */
10301 bool nsContentUtils::IsLocalRefURL(const nsAString& aString) {
10302 return !aString.IsEmpty() && aString[0] == '#';
10305 // We use only 53 bits for the ID so that it can be converted to and from a JS
10306 // value without loss of precision. The upper bits of the ID hold the process
10307 // ID. The lower bits identify the object itself.
10308 static constexpr uint64_t kIdTotalBits = 53;
10309 static constexpr uint64_t kIdProcessBits = 22;
10310 static constexpr uint64_t kIdBits = kIdTotalBits - kIdProcessBits;
10312 /* static */
10313 uint64_t nsContentUtils::GenerateProcessSpecificId(uint64_t aId) {
10314 uint64_t processId = 0;
10315 if (XRE_IsContentProcess()) {
10316 ContentChild* cc = ContentChild::GetSingleton();
10317 processId = cc->GetID();
10320 MOZ_RELEASE_ASSERT(processId < (uint64_t(1) << kIdProcessBits));
10321 uint64_t processBits = processId & ((uint64_t(1) << kIdProcessBits) - 1);
10323 uint64_t id = aId;
10324 MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
10325 uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
10327 return (processBits << kIdBits) | bits;
10330 /* static */
10331 std::tuple<uint64_t, uint64_t> nsContentUtils::SplitProcessSpecificId(
10332 uint64_t aId) {
10333 return {aId >> kIdBits, aId & ((uint64_t(1) << kIdBits) - 1)};
10336 // Next process-local Tab ID.
10337 static uint64_t gNextTabId = 0;
10339 /* static */
10340 uint64_t nsContentUtils::GenerateTabId() {
10341 return GenerateProcessSpecificId(++gNextTabId);
10344 // Next process-local Browser ID.
10345 static uint64_t gNextBrowserId = 0;
10347 /* static */
10348 uint64_t nsContentUtils::GenerateBrowserId() {
10349 return GenerateProcessSpecificId(++gNextBrowserId);
10352 // Next process-local Browsing Context ID.
10353 static uint64_t gNextBrowsingContextId = 0;
10355 /* static */
10356 uint64_t nsContentUtils::GenerateBrowsingContextId() {
10357 return GenerateProcessSpecificId(++gNextBrowsingContextId);
10360 // Next process-local Window ID.
10361 static uint64_t gNextWindowId = 0;
10363 /* static */
10364 uint64_t nsContentUtils::GenerateWindowId() {
10365 return GenerateProcessSpecificId(++gNextWindowId);
10368 // Next process-local load.
10369 static Atomic<uint64_t> gNextLoadIdentifier(0);
10371 /* static */
10372 uint64_t nsContentUtils::GenerateLoadIdentifier() {
10373 return GenerateProcessSpecificId(++gNextLoadIdentifier);
10376 /* static */
10377 bool nsContentUtils::GetUserIsInteracting() {
10378 return UserInteractionObserver::sUserActive;
10381 /* static */
10382 bool nsContentUtils::GetSourceMapURL(nsIHttpChannel* aChannel,
10383 nsACString& aResult) {
10384 nsresult rv = aChannel->GetResponseHeader("SourceMap"_ns, aResult);
10385 if (NS_FAILED(rv)) {
10386 rv = aChannel->GetResponseHeader("X-SourceMap"_ns, aResult);
10388 return NS_SUCCEEDED(rv);
10391 /* static */
10392 bool nsContentUtils::IsMessageInputEvent(const IPC::Message& aMsg) {
10393 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10394 mozilla::dom::PBrowser::PBrowserStart) {
10395 switch (aMsg.type()) {
10396 case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
10397 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10398 case mozilla::dom::PBrowser::Msg_RealMouseEnterExitWidgetEvent__ID:
10399 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10400 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10401 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10402 case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
10403 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10404 case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
10405 return true;
10408 return false;
10411 /* static */
10412 bool nsContentUtils::IsMessageCriticalInputEvent(const IPC::Message& aMsg) {
10413 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10414 mozilla::dom::PBrowser::PBrowserStart) {
10415 switch (aMsg.type()) {
10416 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10417 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10418 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10419 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10420 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10421 return true;
10424 return false;
10427 static const char* kUserInteractionInactive = "user-interaction-inactive";
10428 static const char* kUserInteractionActive = "user-interaction-active";
10430 void nsContentUtils::UserInteractionObserver::Init() {
10431 // Listen for the observer messages from EventStateManager which are telling
10432 // us whether or not the user is interacting.
10433 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10434 obs->AddObserver(this, kUserInteractionInactive, false);
10435 obs->AddObserver(this, kUserInteractionActive, false);
10437 // We can't register ourselves as an annotator yet, as the
10438 // BackgroundHangMonitor hasn't started yet. It will have started by the
10439 // time we have the chance to spin the event loop.
10440 RefPtr<UserInteractionObserver> self = this;
10441 NS_DispatchToMainThread(NS_NewRunnableFunction(
10442 "nsContentUtils::UserInteractionObserver::Init",
10443 [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
10446 void nsContentUtils::UserInteractionObserver::Shutdown() {
10447 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10448 if (obs) {
10449 obs->RemoveObserver(this, kUserInteractionInactive);
10450 obs->RemoveObserver(this, kUserInteractionActive);
10453 BackgroundHangMonitor::UnregisterAnnotator(*this);
10457 * NB: This function is always called by the BackgroundHangMonitor thread.
10458 * Plan accordingly
10460 void nsContentUtils::UserInteractionObserver::AnnotateHang(
10461 BackgroundHangAnnotations& aAnnotations) {
10462 // NOTE: Only annotate the hang report if the user is known to be interacting.
10463 if (sUserActive) {
10464 aAnnotations.AddAnnotation(u"UserInteracting"_ns, true);
10468 NS_IMETHODIMP
10469 nsContentUtils::UserInteractionObserver::Observe(nsISupports* aSubject,
10470 const char* aTopic,
10471 const char16_t* aData) {
10472 if (!strcmp(aTopic, kUserInteractionInactive)) {
10473 if (sUserActive && XRE_IsParentProcess()) {
10474 glean::RecordPowerMetrics();
10476 sUserActive = false;
10477 } else if (!strcmp(aTopic, kUserInteractionActive)) {
10478 if (!sUserActive && XRE_IsParentProcess()) {
10479 glean::RecordPowerMetrics();
10481 nsCOMPtr<nsIUserIdleServiceInternal> idleService =
10482 do_GetService("@mozilla.org/widget/useridleservice;1");
10483 if (idleService) {
10484 idleService->ResetIdleTimeOut(0);
10488 sUserActive = true;
10489 } else {
10490 NS_WARNING("Unexpected observer notification");
10492 return NS_OK;
10495 Atomic<bool> nsContentUtils::UserInteractionObserver::sUserActive(false);
10496 NS_IMPL_ISUPPORTS(nsContentUtils::UserInteractionObserver, nsIObserver)
10498 /* static */
10499 bool nsContentUtils::IsSpecialName(const nsAString& aName) {
10500 return aName.LowerCaseEqualsLiteral("_blank") ||
10501 aName.LowerCaseEqualsLiteral("_top") ||
10502 aName.LowerCaseEqualsLiteral("_parent") ||
10503 aName.LowerCaseEqualsLiteral("_self");
10506 /* static */
10507 bool nsContentUtils::IsOverridingWindowName(const nsAString& aName) {
10508 return !aName.IsEmpty() && !IsSpecialName(aName);
10511 // Unfortunately, we can't unwrap an IDL object using only a concrete type.
10512 // We need to calculate type data based on the IDL typename. Which means
10513 // wrapping our templated function in a macro.
10514 #define EXTRACT_EXN_VALUES(T, ...) \
10515 ExtractExceptionValues<mozilla::dom::prototypes::id::T, \
10516 T##_Binding::NativeType, T>(__VA_ARGS__) \
10517 .isOk()
10519 template <prototypes::ID PrototypeID, class NativeType, typename T>
10520 static Result<Ok, nsresult> ExtractExceptionValues(
10521 JSContext* aCx, JS::Handle<JSObject*> aObj, nsAString& aSourceSpecOut,
10522 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10523 AssertStaticUnwrapOK<PrototypeID>();
10524 RefPtr<T> exn;
10525 MOZ_TRY((UnwrapObject<PrototypeID, NativeType>(aObj, exn, nullptr)));
10527 exn->GetFilename(aCx, aSourceSpecOut);
10528 if (!aSourceSpecOut.IsEmpty()) {
10529 *aLineOut = exn->LineNumber(aCx);
10530 *aColumnOut = exn->ColumnNumber();
10533 exn->GetName(aMessageOut);
10534 aMessageOut.AppendLiteral(": ");
10536 nsAutoString message;
10537 exn->GetMessageMoz(message);
10538 aMessageOut.Append(message);
10539 return Ok();
10542 /* static */
10543 void nsContentUtils::ExtractErrorValues(
10544 JSContext* aCx, JS::Handle<JS::Value> aValue, nsACString& aSourceSpecOut,
10545 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10546 nsAutoString sourceSpec;
10547 ExtractErrorValues(aCx, aValue, sourceSpec, aLineOut, aColumnOut,
10548 aMessageOut);
10549 CopyUTF16toUTF8(sourceSpec, aSourceSpecOut);
10552 /* static */
10553 void nsContentUtils::ExtractErrorValues(
10554 JSContext* aCx, JS::Handle<JS::Value> aValue, nsAString& aSourceSpecOut,
10555 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10556 MOZ_ASSERT(aLineOut);
10557 MOZ_ASSERT(aColumnOut);
10559 if (aValue.isObject()) {
10560 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
10562 // Try to process as an Error object. Use the file/line/column values
10563 // from the Error as they will be more specific to the root cause of
10564 // the problem.
10565 JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
10566 if (err) {
10567 // Use xpc to extract the error message only. We don't actually send
10568 // this report anywhere.
10569 RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
10570 report->Init(err,
10571 nullptr, // toString result
10572 false, // chrome
10573 0); // window ID
10575 if (!report->mFileName.IsEmpty()) {
10576 aSourceSpecOut = report->mFileName;
10577 *aLineOut = report->mLineNumber;
10578 *aColumnOut = report->mColumn;
10580 aMessageOut.Assign(report->mErrorMsg);
10583 // Next, try to unwrap the rejection value as a DOMException.
10584 else if (EXTRACT_EXN_VALUES(DOMException, aCx, obj, aSourceSpecOut,
10585 aLineOut, aColumnOut, aMessageOut)) {
10586 return;
10589 // Next, try to unwrap the rejection value as an XPC Exception.
10590 else if (EXTRACT_EXN_VALUES(Exception, aCx, obj, aSourceSpecOut, aLineOut,
10591 aColumnOut, aMessageOut)) {
10592 return;
10596 // If we could not unwrap a specific error type, then perform default safe
10597 // string conversions on primitives. Objects will result in "[Object]"
10598 // unfortunately.
10599 if (aMessageOut.IsEmpty()) {
10600 nsAutoJSString jsString;
10601 if (jsString.init(aCx, aValue)) {
10602 aMessageOut = jsString;
10603 } else {
10604 JS_ClearPendingException(aCx);
10609 #undef EXTRACT_EXN_VALUES
10611 /* static */
10612 bool nsContentUtils::ContentIsLink(nsIContent* aContent) {
10613 if (!aContent || !aContent->IsElement()) {
10614 return false;
10617 if (aContent->IsHTMLElement(nsGkAtoms::a)) {
10618 return true;
10621 return aContent->AsElement()->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
10622 nsGkAtoms::simple, eCaseMatters);
10625 /* static */
10626 already_AddRefed<ContentFrameMessageManager>
10627 nsContentUtils::TryGetBrowserChildGlobal(nsISupports* aFrom) {
10628 RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(aFrom);
10629 if (!frameLoaderOwner) {
10630 return nullptr;
10633 RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
10634 if (!frameLoader) {
10635 return nullptr;
10638 RefPtr<ContentFrameMessageManager> manager =
10639 frameLoader->GetBrowserChildMessageManager();
10640 return manager.forget();
10643 /* static */
10644 uint32_t nsContentUtils::InnerOrOuterWindowCreated() {
10645 MOZ_ASSERT(NS_IsMainThread());
10646 ++sInnerOrOuterWindowCount;
10647 return ++sInnerOrOuterWindowSerialCounter;
10650 /* static */
10651 void nsContentUtils::InnerOrOuterWindowDestroyed() {
10652 MOZ_ASSERT(NS_IsMainThread());
10653 MOZ_ASSERT(sInnerOrOuterWindowCount > 0);
10654 --sInnerOrOuterWindowCount;
10657 /* static */
10658 nsresult nsContentUtils::AnonymizeURI(nsIURI* aURI, nsCString& aAnonymizedURI) {
10659 MOZ_ASSERT(aURI);
10661 if (aURI->SchemeIs("data")) {
10662 aAnonymizedURI.Assign("data:..."_ns);
10663 return NS_OK;
10665 // Anonymize the URL.
10666 // Strip the URL of any possible username/password and make it ready to be
10667 // presented in the UI.
10668 nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(aURI);
10669 return exposableURI->GetSpec(aAnonymizedURI);
10672 static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
10673 nsAString* result = static_cast<nsAString*>(aData);
10674 result->Append(aBuf, aLen);
10675 return true;
10678 /* static */
10679 bool nsContentUtils::StringifyJSON(JSContext* aCx, JS::Handle<JS::Value> aValue,
10680 nsAString& aOutStr, JSONBehavior aBehavior) {
10681 MOZ_ASSERT(aCx);
10682 switch (aBehavior) {
10683 case UndefinedIsNullStringLiteral: {
10684 aOutStr.Truncate();
10685 JS::Rooted<JS::Value> value(aCx, aValue);
10686 nsAutoString serializedValue;
10687 NS_ENSURE_TRUE(JS_Stringify(aCx, &value, nullptr, JS::NullHandleValue,
10688 JSONCreator, &serializedValue),
10689 false);
10690 aOutStr = serializedValue;
10691 return true;
10693 case UndefinedIsVoidString: {
10694 aOutStr.SetIsVoid(true);
10695 return JS::ToJSON(aCx, aValue, nullptr, JS::NullHandleValue, JSONCreator,
10696 &aOutStr);
10698 default:
10699 MOZ_ASSERT_UNREACHABLE("Invalid value for aBehavior");
10700 return false;
10704 /* static */
10705 bool nsContentUtils::
10706 HighPriorityEventPendingForTopLevelDocumentBeforeContentfulPaint(
10707 Document* aDocument) {
10708 MOZ_ASSERT(XRE_IsContentProcess(),
10709 "This function only makes sense in content processes");
10711 if (aDocument && !aDocument->IsLoadedAsData()) {
10712 if (nsPresContext* presContext = FindPresContextForDocument(aDocument)) {
10713 MOZ_ASSERT(!presContext->IsChrome(),
10714 "Should never have a chrome PresContext in a content process");
10716 return !presContext->GetInProcessRootContentDocumentPresContext()
10717 ->HadFirstContentfulPaint() &&
10718 nsThreadManager::MainThreadHasPendingHighPriorityEvents();
10721 return false;
10724 static nsGlobalWindowInner* GetInnerWindowForGlobal(nsIGlobalObject* aGlobal) {
10725 NS_ENSURE_TRUE(aGlobal, nullptr);
10727 if (auto* window = aGlobal->AsInnerWindow()) {
10728 return nsGlobalWindowInner::Cast(window);
10731 // When Extensions run content scripts inside a sandbox, it uses
10732 // sandboxPrototype to make them appear as though they're running in the
10733 // scope of the page. So when a content script invokes postMessage, it expects
10734 // the |source| of the received message to be the window set as the
10735 // sandboxPrototype. This used to work incidentally for unrelated reasons, but
10736 // now we need to do some special handling to support it.
10737 JS::Rooted<JSObject*> scope(RootingCx(), aGlobal->GetGlobalJSObject());
10738 NS_ENSURE_TRUE(scope, nullptr);
10740 if (xpc::IsSandbox(scope)) {
10741 AutoJSAPI jsapi;
10742 MOZ_ALWAYS_TRUE(jsapi.Init(scope));
10743 JSContext* cx = jsapi.cx();
10744 // Our current Realm on aCx is the sandbox. Using that for unwrapping
10745 // makes sense: if the sandbox can unwrap the window, we can use it.
10746 return xpc::SandboxWindowOrNull(scope, cx);
10749 // The calling window must be holding a reference, so we can return a weak
10750 // pointer.
10751 return nsGlobalWindowInner::Cast(aGlobal->AsInnerWindow());
10754 /* static */
10755 nsGlobalWindowInner* nsContentUtils::IncumbentInnerWindow() {
10756 return GetInnerWindowForGlobal(GetIncumbentGlobal());
10759 /* static */
10760 nsGlobalWindowInner* nsContentUtils::EntryInnerWindow() {
10761 return GetInnerWindowForGlobal(GetEntryGlobal());
10764 /* static */
10765 bool nsContentUtils::IsURIInPrefList(nsIURI* aURI, const char* aPrefName) {
10766 MOZ_ASSERT(aPrefName);
10768 nsAutoCString list;
10769 Preferences::GetCString(aPrefName, list);
10770 ToLowerCase(list);
10771 return IsURIInList(aURI, list);
10774 /* static */
10775 bool nsContentUtils::IsURIInList(nsIURI* aURI, const nsCString& aList) {
10776 #ifdef DEBUG
10777 nsAutoCString listLowerCase(aList);
10778 ToLowerCase(listLowerCase);
10779 MOZ_ASSERT(listLowerCase.Equals(aList),
10780 "The aList argument should be lower-case");
10781 #endif
10783 if (!aURI) {
10784 return false;
10787 nsAutoCString scheme;
10788 aURI->GetScheme(scheme);
10789 if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
10790 return false;
10793 if (aList.IsEmpty()) {
10794 return false;
10797 // The list is comma separated domain list. Each item may start with "*.".
10798 // If starts with "*.", it matches any sub-domains.
10800 nsCCharSeparatedTokenizer tokenizer(aList, ',');
10801 while (tokenizer.hasMoreTokens()) {
10802 const nsCString token(tokenizer.nextToken());
10804 nsAutoCString host;
10805 aURI->GetHost(host);
10806 if (host.IsEmpty()) {
10807 return false;
10809 ToLowerCase(host);
10811 for (;;) {
10812 int32_t index = token.Find(host);
10813 if (index >= 0 &&
10814 static_cast<uint32_t>(index) + host.Length() <= token.Length()) {
10815 // If we found a full match, return true.
10816 size_t indexAfterHost = index + host.Length();
10817 if (index == 0 && indexAfterHost == token.Length()) {
10818 return true;
10820 // If next character is '/', we need to check the path too.
10821 // We assume the path in the list means "/foo" + "*".
10822 if (token[indexAfterHost] == '/') {
10823 nsDependentCSubstring pathInList(
10824 token, indexAfterHost,
10825 static_cast<nsDependentCSubstring::size_type>(-1));
10826 nsAutoCString filePath;
10827 aURI->GetFilePath(filePath);
10828 ToLowerCase(filePath);
10829 if (StringBeginsWith(filePath, pathInList) &&
10830 (filePath.Length() == pathInList.Length() ||
10831 pathInList.EqualsLiteral("/") ||
10832 filePath[pathInList.Length() - 1] == '/' ||
10833 filePath[pathInList.Length() - 1] == '?' ||
10834 filePath[pathInList.Length() - 1] == '#')) {
10835 return true;
10839 int32_t startIndexOfCurrentLevel = host[0] == '*' ? 1 : 0;
10840 int32_t startIndexOfNextLevel =
10841 host.Find(".", startIndexOfCurrentLevel + 1);
10842 if (startIndexOfNextLevel <= 0) {
10843 break;
10845 host = "*"_ns + nsDependentCSubstring(host, startIndexOfNextLevel);
10849 return false;
10852 /* static */
10853 ScreenIntMargin nsContentUtils::GetWindowSafeAreaInsets(
10854 nsIScreen* aScreen, const ScreenIntMargin& aSafeAreaInsets,
10855 const LayoutDeviceIntRect& aWindowRect) {
10856 // This calculates safe area insets of window from screen rectangle, window
10857 // rectangle and safe area insets of screen.
10859 // +----------------------------------------+ <-- screen
10860 // | +-------------------------------+ <------- window
10861 // | | window's safe area inset top) | |
10862 // +--+-------------------------------+--+ |
10863 // | | | |<------ safe area rectangle of
10864 // | | | | | screen
10865 // +--+-------------------------------+--+ |
10866 // | |window's safe area inset bottom| |
10867 // | +-------------------------------+ |
10868 // +----------------------------------------+
10870 ScreenIntMargin windowSafeAreaInsets;
10872 if (windowSafeAreaInsets == aSafeAreaInsets) {
10873 // no safe area insets.
10874 return windowSafeAreaInsets;
10877 int32_t screenLeft, screenTop, screenWidth, screenHeight;
10878 nsresult rv =
10879 aScreen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
10880 if (NS_WARN_IF(NS_FAILED(rv))) {
10881 return windowSafeAreaInsets;
10884 const ScreenIntRect screenRect(screenLeft, screenTop, screenWidth,
10885 screenHeight);
10887 ScreenIntRect safeAreaRect = screenRect;
10888 safeAreaRect.Deflate(aSafeAreaInsets);
10890 ScreenIntRect windowRect = ViewAs<ScreenPixel>(
10891 aWindowRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
10893 // FIXME(bug 1754323): This can trigger because the screen rect is not
10894 // orientation-aware.
10895 // MOZ_ASSERT(screenRect.Contains(windowRect),
10896 // "Screen doesn't contain window rect? Something seems off");
10898 // window's rect of safe area
10899 safeAreaRect = safeAreaRect.Intersect(windowRect);
10901 windowSafeAreaInsets.top = safeAreaRect.y - aWindowRect.y;
10902 windowSafeAreaInsets.left = safeAreaRect.x - aWindowRect.x;
10903 windowSafeAreaInsets.right =
10904 aWindowRect.x + aWindowRect.width - (safeAreaRect.x + safeAreaRect.width);
10905 windowSafeAreaInsets.bottom = aWindowRect.y + aWindowRect.height -
10906 (safeAreaRect.y + safeAreaRect.height);
10908 windowSafeAreaInsets.EnsureAtLeast(ScreenIntMargin());
10909 // This shouldn't be needed, but it wallpapers orientation issues, see bug
10910 // 1754323.
10911 windowSafeAreaInsets.EnsureAtMost(aSafeAreaInsets);
10913 return windowSafeAreaInsets;
10916 /* static */
10917 nsContentUtils::SubresourceCacheValidationInfo
10918 nsContentUtils::GetSubresourceCacheValidationInfo(nsIRequest* aRequest,
10919 nsIURI* aURI) {
10920 SubresourceCacheValidationInfo info;
10921 if (nsCOMPtr<nsICacheInfoChannel> cache = do_QueryInterface(aRequest)) {
10922 uint32_t value = 0;
10923 if (NS_SUCCEEDED(cache->GetCacheTokenExpirationTime(&value))) {
10924 info.mExpirationTime.emplace(value);
10928 // Determine whether the cache entry must be revalidated when we try to use
10929 // it. Currently, only HTTP specifies this information...
10930 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest)) {
10931 Unused << httpChannel->IsNoStoreResponse(&info.mMustRevalidate);
10933 if (!info.mMustRevalidate) {
10934 Unused << httpChannel->IsNoCacheResponse(&info.mMustRevalidate);
10938 // data: URIs are safe to cache across documents under any circumstance, so we
10939 // special-case them here even though the channel itself doesn't have any
10940 // caching policy. Same for chrome:// uris.
10942 // TODO(emilio): Figure out which other schemes that don't have caching
10943 // policies are safe to cache. Blobs should be...
10944 const bool knownCacheable = [&] {
10945 if (!aURI) {
10946 return false;
10948 if (aURI->SchemeIs("data") || aURI->SchemeIs("moz-page-thumb") ||
10949 aURI->SchemeIs("moz-extension")) {
10950 return true;
10952 if (aURI->SchemeIs("chrome") || aURI->SchemeIs("resource")) {
10953 return !StaticPrefs::nglayout_debug_disable_xul_cache();
10955 return false;
10956 }();
10958 if (knownCacheable) {
10959 MOZ_ASSERT(!info.mExpirationTime);
10960 MOZ_ASSERT(!info.mMustRevalidate);
10961 info.mExpirationTime = Some(0); // 0 means "doesn't expire".
10964 return info;
10967 nsCString nsContentUtils::TruncatedURLForDisplay(nsIURI* aURL, size_t aMaxLen) {
10968 nsCString spec;
10969 if (aURL) {
10970 aURL->GetSpec(spec);
10971 spec.Truncate(std::min(aMaxLen, spec.Length()));
10973 return spec;
10976 /* static */
10977 nsresult nsContentUtils::AnonymizeId(nsAString& aId,
10978 const nsACString& aOriginKey,
10979 OriginFormat aFormat) {
10980 MOZ_ASSERT(NS_IsMainThread());
10982 nsresult rv;
10983 nsCString rawKey;
10984 if (aFormat == OriginFormat::Base64) {
10985 rv = Base64Decode(aOriginKey, rawKey);
10986 NS_ENSURE_SUCCESS(rv, rv);
10987 } else {
10988 rawKey = aOriginKey;
10991 HMAC hmac;
10992 rv = hmac.Begin(
10993 SEC_OID_SHA256,
10994 Span(reinterpret_cast<const uint8_t*>(rawKey.get()), rawKey.Length()));
10995 NS_ENSURE_SUCCESS(rv, rv);
10997 NS_ConvertUTF16toUTF8 id(aId);
10998 rv = hmac.Update(reinterpret_cast<const uint8_t*>(id.get()), id.Length());
10999 NS_ENSURE_SUCCESS(rv, rv);
11001 nsTArray<uint8_t> macBytes;
11002 rv = hmac.End(macBytes);
11003 NS_ENSURE_SUCCESS(rv, rv);
11005 nsCString macBase64;
11006 rv = Base64Encode(
11007 nsDependentCSubstring(reinterpret_cast<const char*>(macBytes.Elements()),
11008 macBytes.Length()),
11009 macBase64);
11010 NS_ENSURE_SUCCESS(rv, rv);
11012 CopyUTF8toUTF16(macBase64, aId);
11013 return NS_OK;
11016 /* static */
11017 bool nsContentUtils::ShouldHideObjectOrEmbedImageDocument() {
11018 return StaticPrefs::
11019 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup() &&
11020 StaticPrefs::
11021 browser_opaqueResponseBlocking_syntheticBrowsingContext_filter_AtStartup_DoNotUseDirectly();
11024 /* static */
11025 uint32_t nsContentUtils::ResolveObjectType(uint32_t aType) {
11026 if (!StaticPrefs::
11027 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup()) {
11028 return aType;
11031 if (aType != nsIObjectLoadingContent::TYPE_IMAGE) {
11032 return aType;
11035 return nsIObjectLoadingContent::TYPE_DOCUMENT;
11038 void nsContentUtils::RequestGeckoTaskBurst() {
11039 nsCOMPtr<nsIAppShell> appShell = do_GetService(NS_APPSHELL_CID);
11040 if (appShell) {
11041 appShell->GeckoTaskBurst();
11045 nsIContent* nsContentUtils::GetClosestLinkInFlatTree(nsIContent* aContent) {
11046 for (nsIContent* content = aContent; content;
11047 content = content->GetFlattenedTreeParent()) {
11048 if (nsContentUtils::IsDraggableLink(content)) {
11049 return content;
11052 return nullptr;
11055 namespace mozilla {
11056 std::ostream& operator<<(std::ostream& aOut,
11057 const PreventDefaultResult aPreventDefaultResult) {
11058 switch (aPreventDefaultResult) {
11059 case PreventDefaultResult::No:
11060 aOut << "unhandled";
11061 break;
11062 case PreventDefaultResult::ByContent:
11063 aOut << "handled-by-content";
11064 break;
11065 case PreventDefaultResult::ByChrome:
11066 aOut << "handled-by-chrome";
11067 break;
11069 return aOut;
11071 } // namespace mozilla