Bug 1812499 [wpt PR 38184] - Simplify handling of name-to-subdir mapping in canvas...
[gecko.git] / dom / base / nsContentUtils.cpp
blob6d0a17f1c39eb43d4831cdda7e82f983040bc73c
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 "nsRFPService.h"
361 #include "nsRange.h"
362 #include "nsRefPtrHashtable.h"
363 #include "nsSandboxFlags.h"
364 #include "nsScriptSecurityManager.h"
365 #include "nsServiceManagerUtils.h"
366 #include "nsStreamUtils.h"
367 #include "nsString.h"
368 #include "nsStringBuffer.h"
369 #include "nsStringBundle.h"
370 #include "nsStringFlags.h"
371 #include "nsStringFwd.h"
372 #include "nsStringIterator.h"
373 #include "nsStringStream.h"
374 #include "nsTArray.h"
375 #include "nsTLiteralString.h"
376 #include "nsTPromiseFlatString.h"
377 #include "nsTStringRepr.h"
378 #include "nsTextFragment.h"
379 #include "nsTextNode.h"
380 #include "nsThreadManager.h"
381 #include "nsThreadUtils.h"
382 #include "nsTreeSanitizer.h"
383 #include "nsUGenCategory.h"
384 #include "nsURLHelper.h"
385 #include "nsUnicodeProperties.h"
386 #include "nsVariant.h"
387 #include "nsWidgetsCID.h"
388 #include "nsView.h"
389 #include "nsViewManager.h"
390 #include "nsXPCOM.h"
391 #include "nsXPCOMCID.h"
392 #include "nsXULAppAPI.h"
393 #include "nsXULElement.h"
394 #include "nsXULPopupManager.h"
395 #include "nscore.h"
396 #include "prinrval.h"
397 #include "xpcprivate.h"
398 #include "xpcpublic.h"
400 #if defined(XP_WIN)
401 // Undefine LoadImage to prevent naming conflict with Windows.
402 # undef LoadImage
403 #endif
405 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
406 const char** next, char16_t* result);
407 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end, int ns_aware,
408 const char** colon);
410 using namespace mozilla::dom;
411 using namespace mozilla::ipc;
412 using namespace mozilla::gfx;
413 using namespace mozilla::layers;
414 using namespace mozilla::widget;
415 using namespace mozilla;
417 const char kLoadAsData[] = "loadAsData";
419 nsIXPConnect* nsContentUtils::sXPConnect;
420 nsIScriptSecurityManager* nsContentUtils::sSecurityManager;
421 nsIPrincipal* nsContentUtils::sSystemPrincipal;
422 nsIPrincipal* nsContentUtils::sNullSubjectPrincipal;
423 nsIIOService* nsContentUtils::sIOService;
424 nsIConsoleService* nsContentUtils::sConsoleService;
425 nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>*
426 nsContentUtils::sAtomEventTable = nullptr;
427 nsTHashMap<nsStringHashKey, EventNameMapping>*
428 nsContentUtils::sStringEventTable = nullptr;
429 nsTArray<RefPtr<nsAtom>>* nsContentUtils::sUserDefinedEvents = nullptr;
430 nsIStringBundleService* nsContentUtils::sStringBundleService;
432 static StaticRefPtr<nsIStringBundle>
433 sStringBundles[nsContentUtils::PropertiesFile_COUNT];
435 nsIContentPolicy* nsContentUtils::sContentPolicyService;
436 bool nsContentUtils::sTriedToGetContentPolicy = false;
437 StaticRefPtr<nsIBidiKeyboard> nsContentUtils::sBidiKeyboard;
438 uint32_t nsContentUtils::sScriptBlockerCount = 0;
439 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
440 AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners =
441 nullptr;
442 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
443 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
445 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
447 nsString* nsContentUtils::sShiftText = nullptr;
448 nsString* nsContentUtils::sControlText = nullptr;
449 nsString* nsContentUtils::sMetaText = nullptr;
450 nsString* nsContentUtils::sOSText = nullptr;
451 nsString* nsContentUtils::sAltText = nullptr;
452 nsString* nsContentUtils::sModifierSeparator = nullptr;
454 bool nsContentUtils::sInitialized = false;
455 #ifndef RELEASE_OR_BETA
456 bool nsContentUtils::sBypassCSSOMOriginCheck = false;
457 #endif
459 nsCString* nsContentUtils::sJSScriptBytecodeMimeType = nullptr;
460 nsCString* nsContentUtils::sJSModuleBytecodeMimeType = nullptr;
462 nsContentUtils::UserInteractionObserver*
463 nsContentUtils::sUserInteractionObserver = nullptr;
465 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
466 nsParser* nsContentUtils::sXMLFragmentParser = nullptr;
467 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
468 bool nsContentUtils::sFragmentParsingActive = false;
470 mozilla::LazyLogModule nsContentUtils::gResistFingerprintingLog(
471 "nsResistFingerprinting");
472 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
474 int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
475 uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
477 template Maybe<int32_t> nsContentUtils::ComparePoints(
478 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary);
479 template Maybe<int32_t> nsContentUtils::ComparePoints(
480 const RangeBoundary& aFirstBoundary,
481 const RawRangeBoundary& aSecondBoundary);
482 template Maybe<int32_t> nsContentUtils::ComparePoints(
483 const RawRangeBoundary& aFirstBoundary,
484 const RangeBoundary& aSecondBoundary);
485 template Maybe<int32_t> nsContentUtils::ComparePoints(
486 const RawRangeBoundary& aFirstBoundary,
487 const RawRangeBoundary& aSecondBoundary);
489 template int32_t nsContentUtils::ComparePoints_Deprecated(
490 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
491 bool* aDisconnected);
492 template int32_t nsContentUtils::ComparePoints_Deprecated(
493 const RangeBoundary& aFirstBoundary,
494 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
495 template int32_t nsContentUtils::ComparePoints_Deprecated(
496 const RawRangeBoundary& aFirstBoundary,
497 const RangeBoundary& aSecondBoundary, bool* aDisconnected);
498 template int32_t nsContentUtils::ComparePoints_Deprecated(
499 const RawRangeBoundary& aFirstBoundary,
500 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
502 // Subset of
503 // http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
504 enum AutocompleteUnsupportedFieldName : uint8_t {
505 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
506 eAutocompleteUnsupportedFieldName_##name_,
507 #include "AutocompleteFieldList.h"
508 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
511 enum AutocompleteNoPersistFieldName : uint8_t {
512 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
513 eAutocompleteNoPersistFieldName_##name_,
514 #include "AutocompleteFieldList.h"
515 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
518 enum AutocompleteUnsupportFieldContactHint : uint8_t {
519 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
520 eAutocompleteUnsupportedFieldContactHint_##name_,
521 #include "AutocompleteFieldList.h"
522 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
525 enum AutocompleteFieldName : uint8_t {
526 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) eAutocompleteFieldName_##name_,
527 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
528 AUTOCOMPLETE_FIELD_NAME(name_, value_)
529 #include "AutocompleteFieldList.h"
530 #undef AUTOCOMPLETE_FIELD_NAME
531 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
534 enum AutocompleteFieldHint : uint8_t {
535 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) eAutocompleteFieldHint_##name_,
536 #include "AutocompleteFieldList.h"
537 #undef AUTOCOMPLETE_FIELD_HINT
540 enum AutocompleteFieldContactHint : uint8_t {
541 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
542 eAutocompleteFieldContactHint_##name_,
543 #include "AutocompleteFieldList.h"
544 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
547 enum AutocompleteCategory {
548 #define AUTOCOMPLETE_CATEGORY(name_, value_) eAutocompleteCategory_##name_,
549 #include "AutocompleteFieldList.h"
550 #undef AUTOCOMPLETE_CATEGORY
553 static const nsAttrValue::EnumTable kAutocompleteUnsupportedFieldNameTable[] = {
554 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
555 {value_, eAutocompleteUnsupportedFieldName_##name_},
556 #include "AutocompleteFieldList.h"
557 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
558 {nullptr, 0}};
560 static const nsAttrValue::EnumTable kAutocompleteNoPersistFieldNameTable[] = {
561 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
562 {value_, eAutocompleteNoPersistFieldName_##name_},
563 #include "AutocompleteFieldList.h"
564 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
565 {nullptr, 0}};
567 static const nsAttrValue::EnumTable
568 kAutocompleteUnsupportedContactFieldHintTable[] = {
569 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
570 {value_, eAutocompleteUnsupportedFieldContactHint_##name_},
571 #include "AutocompleteFieldList.h"
572 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
573 {nullptr, 0}};
575 static const nsAttrValue::EnumTable kAutocompleteFieldNameTable[] = {
576 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
577 {value_, eAutocompleteFieldName_##name_},
578 #include "AutocompleteFieldList.h"
579 #undef AUTOCOMPLETE_FIELD_NAME
580 {nullptr, 0}};
582 static const nsAttrValue::EnumTable kAutocompleteContactFieldNameTable[] = {
583 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
584 {value_, eAutocompleteFieldName_##name_},
585 #include "AutocompleteFieldList.h"
586 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
587 {nullptr, 0}};
589 static const nsAttrValue::EnumTable kAutocompleteFieldHintTable[] = {
590 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
591 {value_, eAutocompleteFieldHint_##name_},
592 #include "AutocompleteFieldList.h"
593 #undef AUTOCOMPLETE_FIELD_HINT
594 {nullptr, 0}};
596 static const nsAttrValue::EnumTable kAutocompleteContactFieldHintTable[] = {
597 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
598 {value_, eAutocompleteFieldContactHint_##name_},
599 #include "AutocompleteFieldList.h"
600 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
601 {nullptr, 0}};
603 namespace {
605 static PLDHashTable* sEventListenerManagersHash;
607 // A global hashtable to for keeping the arena alive for cross docGroup node
608 // adoption.
609 static nsRefPtrHashtable<nsPtrHashKey<const nsINode>, mozilla::dom::DOMArena>*
610 sDOMArenaHashtable;
612 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter {
613 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
615 ~DOMEventListenerManagersHashReporter() = default;
617 public:
618 NS_DECL_ISUPPORTS
620 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
621 nsISupports* aData, bool aAnonymize) override {
622 // We don't measure the |EventListenerManager| objects pointed to by the
623 // entries because those references are non-owning.
624 int64_t amount =
625 sEventListenerManagersHash
626 ? sEventListenerManagersHash->ShallowSizeOfIncludingThis(
627 MallocSizeOf)
628 : 0;
630 MOZ_COLLECT_REPORT(
631 "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
632 amount, "Memory used by the event listener manager's hash table.");
634 return NS_OK;
638 NS_IMPL_ISUPPORTS(DOMEventListenerManagersHashReporter, nsIMemoryReporter)
640 class EventListenerManagerMapEntry : public PLDHashEntryHdr {
641 public:
642 explicit EventListenerManagerMapEntry(const void* aKey) : mKey(aKey) {}
644 ~EventListenerManagerMapEntry() {
645 NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
648 protected: // declared protected to silence clang warnings
649 const void* mKey; // must be first, to look like PLDHashEntryStub
651 public:
652 RefPtr<EventListenerManager> mListenerManager;
655 static void EventListenerManagerHashInitEntry(PLDHashEntryHdr* entry,
656 const void* key) {
657 // Initialize the entry with placement new
658 new (entry) EventListenerManagerMapEntry(key);
661 static void EventListenerManagerHashClearEntry(PLDHashTable* table,
662 PLDHashEntryHdr* entry) {
663 EventListenerManagerMapEntry* lm =
664 static_cast<EventListenerManagerMapEntry*>(entry);
666 // Let the EventListenerManagerMapEntry clean itself up...
667 lm->~EventListenerManagerMapEntry();
670 class SameOriginCheckerImpl final : public nsIChannelEventSink,
671 public nsIInterfaceRequestor {
672 ~SameOriginCheckerImpl() = default;
674 NS_DECL_ISUPPORTS
675 NS_DECL_NSICHANNELEVENTSINK
676 NS_DECL_NSIINTERFACEREQUESTOR
679 } // namespace
681 void AutoSuppressEventHandling::SuppressDocument(Document* aDoc) {
682 // Note: Document::SuppressEventHandling will also automatically suppress
683 // event handling for any in-process sub-documents. However, since we need
684 // to deal with cases where remote BrowsingContexts may be interleaved
685 // with in-process ones, we still need to walk the entire tree ourselves.
686 // This may be slightly redundant in some cases, but since event handling
687 // suppressions maintain a count of current blockers, it does not cause
688 // any problems.
689 aDoc->SuppressEventHandling();
692 void AutoSuppressEventHandling::UnsuppressDocument(Document* aDoc) {
693 aDoc->UnsuppressEventHandlingAndFireEvents(true);
696 AutoSuppressEventHandling::~AutoSuppressEventHandling() {
697 UnsuppressDocuments();
700 void AutoSuppressEventHandlingAndSuspend::SuppressDocument(Document* aDoc) {
701 AutoSuppressEventHandling::SuppressDocument(aDoc);
702 if (nsCOMPtr<nsPIDOMWindowInner> win = aDoc->GetInnerWindow()) {
703 win->Suspend();
704 mWindows.AppendElement(win);
708 AutoSuppressEventHandlingAndSuspend::~AutoSuppressEventHandlingAndSuspend() {
709 for (const auto& win : mWindows) {
710 win->Resume();
715 * This class is used to determine whether or not the user is currently
716 * interacting with the browser. It listens to observer events to toggle the
717 * value of the sUserActive static.
719 * This class is an internal implementation detail.
720 * nsContentUtils::GetUserIsInteracting() should be used to access current
721 * user interaction status.
723 class nsContentUtils::UserInteractionObserver final
724 : public nsIObserver,
725 public BackgroundHangAnnotator {
726 public:
727 NS_DECL_ISUPPORTS
728 NS_DECL_NSIOBSERVER
730 void Init();
731 void Shutdown();
732 void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
734 static Atomic<bool> sUserActive;
736 private:
737 ~UserInteractionObserver() = default;
740 static constexpr nsLiteralCString kRfpPrefs[] = {
741 "privacy.resistFingerprinting"_ns,
742 "privacy.resistFingerprinting.testGranularityMask"_ns,
745 static void RecomputeResistFingerprintingAllDocs(const char*, void*) {
746 AutoTArray<RefPtr<BrowsingContextGroup>, 5> bcGroups;
747 BrowsingContextGroup::GetAllGroups(bcGroups);
748 for (auto& bcGroup : bcGroups) {
749 AutoTArray<DocGroup*, 5> docGroups;
750 bcGroup->GetDocGroups(docGroups);
751 for (auto* docGroup : docGroups) {
752 for (Document* doc : *docGroup) {
753 const bool old = doc->ShouldResistFingerprinting();
754 doc->RecomputeResistFingerprinting();
755 if (old != doc->ShouldResistFingerprinting()) {
756 if (auto* pc = doc->GetPresContext()) {
757 pc->MediaFeatureValuesChanged(
758 {MediaFeatureChangeReason::PreferenceChange},
759 MediaFeatureChangePropagation::JustThisDocument);
767 // static
768 nsresult nsContentUtils::Init() {
769 if (sInitialized) {
770 NS_WARNING("Init() called twice");
772 return NS_OK;
775 nsHTMLTags::AddRefTable();
777 sXPConnect = nsXPConnect::XPConnect();
778 // We hold a strong ref to sXPConnect to ensure that it does not go away until
779 // nsLayoutStatics::Shutdown is happening. Otherwise ~nsXPConnect can be
780 // triggered by xpcModuleDtor late in shutdown and cause crashes due to
781 // various stuff already being torn down by then. Note that this means that
782 // we are effectively making sure that if we leak nsLayoutStatics then we also
783 // leak nsXPConnect.
784 NS_ADDREF(sXPConnect);
786 sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
787 if (!sSecurityManager) return NS_ERROR_FAILURE;
788 NS_ADDREF(sSecurityManager);
790 sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
791 MOZ_ASSERT(sSystemPrincipal);
793 RefPtr<NullPrincipal> nullPrincipal =
794 NullPrincipal::CreateWithoutOriginAttributes();
795 if (!nullPrincipal) {
796 return NS_ERROR_FAILURE;
799 nullPrincipal.forget(&sNullSubjectPrincipal);
801 nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
802 if (NS_FAILED(rv)) {
803 // This makes life easier, but we can live without it.
805 sIOService = nullptr;
808 if (!InitializeEventTable()) return NS_ERROR_FAILURE;
810 if (!sEventListenerManagersHash) {
811 static const PLDHashTableOps hash_table_ops = {
812 PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
813 PLDHashTable::MoveEntryStub, EventListenerManagerHashClearEntry,
814 EventListenerManagerHashInitEntry};
816 sEventListenerManagersHash =
817 new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
819 RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
822 sBlockedScriptRunners = new AutoTArray<nsCOMPtr<nsIRunnable>, 8>;
824 #ifndef RELEASE_OR_BETA
825 sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
826 #endif
828 Element::InitCCCallbacks();
830 Unused << nsRFPService::GetOrCreate();
832 if (XRE_IsParentProcess()) {
833 AsyncPrecreateStringBundles();
836 RefPtr<UserInteractionObserver> uio = new UserInteractionObserver();
837 uio->Init();
838 uio.forget(&sUserInteractionObserver);
840 for (const auto& pref : kRfpPrefs) {
841 Preferences::RegisterCallback(RecomputeResistFingerprintingAllDocs, pref);
844 sInitialized = true;
846 return NS_OK;
849 bool nsContentUtils::InitJSBytecodeMimeType() {
850 MOZ_ASSERT(NS_IsMainThread());
851 MOZ_ASSERT(!sJSScriptBytecodeMimeType);
852 MOZ_ASSERT(!sJSModuleBytecodeMimeType);
854 JS::BuildIdCharVector jsBuildId;
855 if (!JS::GetScriptTranscodingBuildId(&jsBuildId)) {
856 return false;
859 nsDependentCSubstring jsBuildIdStr(jsBuildId.begin(), jsBuildId.length());
860 sJSScriptBytecodeMimeType =
861 new nsCString("javascript/moz-script-bytecode-"_ns + jsBuildIdStr);
862 sJSModuleBytecodeMimeType =
863 new nsCString("javascript/moz-module-bytecode-"_ns + jsBuildIdStr);
864 return true;
867 void nsContentUtils::GetShiftText(nsAString& text) {
868 if (!sShiftText) InitializeModifierStrings();
869 text.Assign(*sShiftText);
872 void nsContentUtils::GetControlText(nsAString& text) {
873 if (!sControlText) InitializeModifierStrings();
874 text.Assign(*sControlText);
877 void nsContentUtils::GetMetaText(nsAString& text) {
878 if (!sMetaText) InitializeModifierStrings();
879 text.Assign(*sMetaText);
882 void nsContentUtils::GetOSText(nsAString& text) {
883 if (!sOSText) {
884 InitializeModifierStrings();
886 text.Assign(*sOSText);
889 void nsContentUtils::GetAltText(nsAString& text) {
890 if (!sAltText) InitializeModifierStrings();
891 text.Assign(*sAltText);
894 void nsContentUtils::GetModifierSeparatorText(nsAString& text) {
895 if (!sModifierSeparator) InitializeModifierStrings();
896 text.Assign(*sModifierSeparator);
899 void nsContentUtils::InitializeModifierStrings() {
900 // load the display strings for the keyboard accelerators
901 nsCOMPtr<nsIStringBundleService> bundleService =
902 mozilla::components::StringBundle::Service();
903 nsCOMPtr<nsIStringBundle> bundle;
904 DebugOnly<nsresult> rv = NS_OK;
905 if (bundleService) {
906 rv = bundleService->CreateBundle(
907 "chrome://global-platform/locale/platformKeys.properties",
908 getter_AddRefs(bundle));
911 NS_ASSERTION(
912 NS_SUCCEEDED(rv) && bundle,
913 "chrome://global/locale/platformKeys.properties could not be loaded");
914 nsAutoString shiftModifier;
915 nsAutoString metaModifier;
916 nsAutoString osModifier;
917 nsAutoString altModifier;
918 nsAutoString controlModifier;
919 nsAutoString modifierSeparator;
920 if (bundle) {
921 // macs use symbols for each modifier key, so fetch each from the bundle,
922 // which also covers i18n
923 bundle->GetStringFromName("VK_SHIFT", shiftModifier);
924 bundle->GetStringFromName("VK_META", metaModifier);
925 bundle->GetStringFromName("VK_WIN", osModifier);
926 bundle->GetStringFromName("VK_ALT", altModifier);
927 bundle->GetStringFromName("VK_CONTROL", controlModifier);
928 bundle->GetStringFromName("MODIFIER_SEPARATOR", modifierSeparator);
930 // if any of these don't exist, we get an empty string
931 sShiftText = new nsString(shiftModifier);
932 sMetaText = new nsString(metaModifier);
933 sOSText = new nsString(osModifier);
934 sAltText = new nsString(altModifier);
935 sControlText = new nsString(controlModifier);
936 sModifierSeparator = new nsString(modifierSeparator);
939 mozilla::EventClassID nsContentUtils::GetEventClassIDFromMessage(
940 EventMessage aEventMessage) {
941 switch (aEventMessage) {
942 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
943 case message_: \
944 return struct_;
945 #include "mozilla/EventNameList.h"
946 #undef MESSAGE_TO_EVENT
947 default:
948 MOZ_ASSERT_UNREACHABLE("Invalid event message?");
949 return eBasicEventClass;
953 static nsAtom* GetEventTypeFromMessage(EventMessage aEventMessage) {
954 switch (aEventMessage) {
955 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
956 case message_: \
957 return nsGkAtoms::on##name_;
958 #include "mozilla/EventNameList.h"
959 #undef MESSAGE_TO_EVENT
960 default:
961 return nullptr;
965 // Because of SVG/SMIL we have several atoms mapped to the same
966 // id, but we can rely on MESSAGE_TO_EVENT to map id to only one atom.
967 static bool ShouldAddEventToStringEventTable(const EventNameMapping& aMapping) {
968 MOZ_ASSERT(aMapping.mAtom);
969 return GetEventTypeFromMessage(aMapping.mMessage) == aMapping.mAtom;
972 bool nsContentUtils::InitializeEventTable() {
973 NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
974 NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
976 static const EventNameMapping eventArray[] = {
977 #define EVENT(name_, _message, _type, _class) \
978 {nsGkAtoms::on##name_, _type, _message, _class, false},
979 #define WINDOW_ONLY_EVENT EVENT
980 #define DOCUMENT_ONLY_EVENT EVENT
981 #define NON_IDL_EVENT EVENT
982 #include "mozilla/EventNameList.h"
983 #undef WINDOW_ONLY_EVENT
984 #undef NON_IDL_EVENT
985 #undef EVENT
986 {nullptr}};
988 sAtomEventTable = new nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>(
989 ArrayLength(eventArray));
990 sStringEventTable = new nsTHashMap<nsStringHashKey, EventNameMapping>(
991 ArrayLength(eventArray));
992 sUserDefinedEvents = new nsTArray<RefPtr<nsAtom>>(64);
994 // Subtract one from the length because of the trailing null
995 for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) {
996 MOZ_ASSERT(!sAtomEventTable->Contains(eventArray[i].mAtom),
997 "Double-defining event name; fix your EventNameList.h");
998 sAtomEventTable->InsertOrUpdate(eventArray[i].mAtom, eventArray[i]);
999 if (ShouldAddEventToStringEventTable(eventArray[i])) {
1000 sStringEventTable->InsertOrUpdate(
1001 Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
1002 eventArray[i]);
1006 return true;
1009 void nsContentUtils::InitializeTouchEventTable() {
1010 static bool sEventTableInitialized = false;
1011 if (!sEventTableInitialized && sAtomEventTable && sStringEventTable) {
1012 sEventTableInitialized = true;
1013 static const EventNameMapping touchEventArray[] = {
1014 #define EVENT(name_, _message, _type, _class)
1015 #define TOUCH_EVENT(name_, _message, _type, _class) \
1016 {nsGkAtoms::on##name_, _type, _message, _class},
1017 #include "mozilla/EventNameList.h"
1018 #undef TOUCH_EVENT
1019 #undef EVENT
1020 {nullptr}};
1021 // Subtract one from the length because of the trailing null
1022 for (uint32_t i = 0; i < ArrayLength(touchEventArray) - 1; ++i) {
1023 sAtomEventTable->InsertOrUpdate(touchEventArray[i].mAtom,
1024 touchEventArray[i]);
1025 sStringEventTable->InsertOrUpdate(
1026 Substring(nsDependentAtomString(touchEventArray[i].mAtom), 2),
1027 touchEventArray[i]);
1032 static bool Is8bit(const nsAString& aString) {
1033 static const char16_t EIGHT_BIT = char16_t(~0x00FF);
1035 for (nsAString::const_char_iterator start = aString.BeginReading(),
1036 end = aString.EndReading();
1037 start != end; ++start) {
1038 if (*start & EIGHT_BIT) {
1039 return false;
1043 return true;
1046 nsresult nsContentUtils::Btoa(const nsAString& aBinaryData,
1047 nsAString& aAsciiBase64String) {
1048 if (!Is8bit(aBinaryData)) {
1049 aAsciiBase64String.Truncate();
1050 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1053 return Base64Encode(aBinaryData, aAsciiBase64String);
1056 nsresult nsContentUtils::Atob(const nsAString& aAsciiBase64String,
1057 nsAString& aBinaryData) {
1058 if (!Is8bit(aAsciiBase64String)) {
1059 aBinaryData.Truncate();
1060 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1063 const char16_t* start = aAsciiBase64String.BeginReading();
1064 const char16_t* cur = start;
1065 const char16_t* end = aAsciiBase64String.EndReading();
1066 bool hasWhitespace = false;
1068 while (cur < end) {
1069 if (nsContentUtils::IsHTMLWhitespace(*cur)) {
1070 hasWhitespace = true;
1071 break;
1073 cur++;
1076 nsresult rv;
1078 if (hasWhitespace) {
1079 nsString trimmedString;
1081 if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible)) {
1082 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1085 trimmedString.Append(start, cur - start);
1087 while (cur < end) {
1088 if (!nsContentUtils::IsHTMLWhitespace(*cur)) {
1089 trimmedString.Append(*cur);
1091 cur++;
1093 rv = Base64Decode(trimmedString, aBinaryData);
1094 } else {
1095 rv = Base64Decode(aAsciiBase64String, aBinaryData);
1098 if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
1099 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1101 return rv;
1104 bool nsContentUtils::IsAutocompleteEnabled(
1105 mozilla::dom::HTMLInputElement* aInput) {
1106 MOZ_ASSERT(aInput, "aInput should not be null!");
1108 nsAutoString autocomplete;
1109 aInput->GetAutocomplete(autocomplete);
1111 if (autocomplete.IsEmpty()) {
1112 auto* form = aInput->GetForm();
1113 if (!form) {
1114 return true;
1117 form->GetAutocomplete(autocomplete);
1120 return !autocomplete.EqualsLiteral("off");
1123 nsContentUtils::AutocompleteAttrState
1124 nsContentUtils::SerializeAutocompleteAttribute(
1125 const nsAttrValue* aAttr, nsAString& aResult,
1126 AutocompleteAttrState aCachedState) {
1127 if (!aAttr ||
1128 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1129 return aCachedState;
1132 if (aCachedState == nsContentUtils::eAutocompleteAttrState_Valid) {
1133 uint32_t atomCount = aAttr->GetAtomCount();
1134 for (uint32_t i = 0; i < atomCount; i++) {
1135 if (i != 0) {
1136 aResult.Append(' ');
1138 aResult.Append(nsDependentAtomString(aAttr->AtomAt(i)));
1140 nsContentUtils::ASCIIToLower(aResult);
1141 return aCachedState;
1144 aResult.Truncate();
1146 mozilla::dom::AutocompleteInfo info;
1147 AutocompleteAttrState state =
1148 InternalSerializeAutocompleteAttribute(aAttr, info);
1149 if (state == eAutocompleteAttrState_Valid) {
1150 // Concatenate the info fields.
1151 aResult = info.mSection;
1153 if (!info.mAddressType.IsEmpty()) {
1154 if (!aResult.IsEmpty()) {
1155 aResult += ' ';
1157 aResult += info.mAddressType;
1160 if (!info.mContactType.IsEmpty()) {
1161 if (!aResult.IsEmpty()) {
1162 aResult += ' ';
1164 aResult += info.mContactType;
1167 if (!info.mFieldName.IsEmpty()) {
1168 if (!aResult.IsEmpty()) {
1169 aResult += ' ';
1171 aResult += info.mFieldName;
1175 return state;
1178 nsContentUtils::AutocompleteAttrState
1179 nsContentUtils::SerializeAutocompleteAttribute(
1180 const nsAttrValue* aAttr, mozilla::dom::AutocompleteInfo& aInfo,
1181 AutocompleteAttrState aCachedState, bool aGrantAllValidValue) {
1182 if (!aAttr ||
1183 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1184 return aCachedState;
1187 return InternalSerializeAutocompleteAttribute(aAttr, aInfo,
1188 aGrantAllValidValue);
1192 * Helper to validate the @autocomplete tokens.
1194 * @return {AutocompleteAttrState} The state of the attribute (invalid/valid).
1196 nsContentUtils::AutocompleteAttrState
1197 nsContentUtils::InternalSerializeAutocompleteAttribute(
1198 const nsAttrValue* aAttrVal, mozilla::dom::AutocompleteInfo& aInfo,
1199 bool aGrantAllValidValue) {
1200 // No autocomplete attribute so we are done
1201 if (!aAttrVal) {
1202 return eAutocompleteAttrState_Invalid;
1205 uint32_t numTokens = aAttrVal->GetAtomCount();
1206 if (!numTokens) {
1207 return eAutocompleteAttrState_Invalid;
1210 uint32_t index = numTokens - 1;
1211 nsString tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1212 AutocompleteCategory category;
1213 nsAttrValue enumValue;
1215 bool unsupported = false;
1216 if (!aGrantAllValidValue) {
1217 unsupported = enumValue.ParseEnumValue(
1218 tokenString, kAutocompleteUnsupportedFieldNameTable, false);
1219 if (unsupported) {
1220 return eAutocompleteAttrState_Invalid;
1224 nsAutoString str;
1225 bool result =
1226 enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
1227 if (result) {
1228 // Off/Automatic/Normal categories.
1229 if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
1230 enumValue.Equals(u"on"_ns, eIgnoreCase)) {
1231 if (numTokens > 1) {
1232 return eAutocompleteAttrState_Invalid;
1234 enumValue.ToString(str);
1235 ASCIIToLower(str);
1236 aInfo.mFieldName.Assign(str);
1237 aInfo.mCanAutomaticallyPersist =
1238 !enumValue.Equals(u"off"_ns, eIgnoreCase);
1239 return eAutocompleteAttrState_Valid;
1242 // Only allow on/off if form autofill @autocomplete values aren't enabled
1243 // and it doesn't grant all valid values.
1244 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1245 !aGrantAllValidValue) {
1246 return eAutocompleteAttrState_Invalid;
1249 // Normal category
1250 if (numTokens > 3) {
1251 return eAutocompleteAttrState_Invalid;
1253 category = eAutocompleteCategory_NORMAL;
1254 } else { // Check if the last token is of the contact category instead.
1255 // Only allow on/off if form autofill @autocomplete values aren't enabled
1256 // and it doesn't grant all valid values.
1257 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1258 !aGrantAllValidValue) {
1259 return eAutocompleteAttrState_Invalid;
1262 result = enumValue.ParseEnumValue(
1263 tokenString, kAutocompleteContactFieldNameTable, false);
1264 if (!result || numTokens > 4) {
1265 return eAutocompleteAttrState_Invalid;
1268 category = eAutocompleteCategory_CONTACT;
1271 enumValue.ToString(str);
1272 ASCIIToLower(str);
1273 aInfo.mFieldName.Assign(str);
1275 aInfo.mCanAutomaticallyPersist = !enumValue.ParseEnumValue(
1276 tokenString, kAutocompleteNoPersistFieldNameTable, false);
1278 // We are done if this was the only token.
1279 if (numTokens == 1) {
1280 return eAutocompleteAttrState_Valid;
1283 --index;
1284 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1286 if (category == eAutocompleteCategory_CONTACT) {
1287 if (!aGrantAllValidValue) {
1288 unsupported = enumValue.ParseEnumValue(
1289 tokenString, kAutocompleteUnsupportedContactFieldHintTable, false);
1290 if (unsupported) {
1291 return eAutocompleteAttrState_Invalid;
1295 nsAttrValue contactFieldHint;
1296 result = contactFieldHint.ParseEnumValue(
1297 tokenString, kAutocompleteContactFieldHintTable, false);
1298 if (result) {
1299 nsAutoString contactFieldHintString;
1300 contactFieldHint.ToString(contactFieldHintString);
1301 ASCIIToLower(contactFieldHintString);
1302 aInfo.mContactType.Assign(contactFieldHintString);
1303 if (index == 0) {
1304 return eAutocompleteAttrState_Valid;
1306 --index;
1307 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1311 // Check for billing/shipping tokens
1312 nsAttrValue fieldHint;
1313 if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable,
1314 false)) {
1315 nsString fieldHintString;
1316 fieldHint.ToString(fieldHintString);
1317 ASCIIToLower(fieldHintString);
1318 aInfo.mAddressType.Assign(fieldHintString);
1319 if (index == 0) {
1320 return eAutocompleteAttrState_Valid;
1322 --index;
1323 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1326 // Check for section-* token
1327 const nsDependentSubstring& section = Substring(tokenString, 0, 8);
1328 if (section.LowerCaseEqualsASCII("section-")) {
1329 ASCIIToLower(tokenString);
1330 aInfo.mSection.Assign(tokenString);
1331 if (index == 0) {
1332 return eAutocompleteAttrState_Valid;
1336 // Clear the fields as the autocomplete attribute is invalid.
1337 aInfo.mSection.Truncate();
1338 aInfo.mAddressType.Truncate();
1339 aInfo.mContactType.Truncate();
1340 aInfo.mFieldName.Truncate();
1342 return eAutocompleteAttrState_Invalid;
1345 // Parse an integer according to HTML spec
1346 template <class CharT>
1347 int32_t nsContentUtils::ParseHTMLIntegerImpl(
1348 const CharT* aStart, const CharT* aEnd,
1349 ParseHTMLIntegerResultFlags* aResult) {
1350 int result = eParseHTMLInteger_NoFlags;
1352 const CharT* iter = aStart;
1354 while (iter != aEnd && nsContentUtils::IsHTMLWhitespace(*iter)) {
1355 result |= eParseHTMLInteger_NonStandard;
1356 ++iter;
1359 if (iter == aEnd) {
1360 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1361 *aResult = (ParseHTMLIntegerResultFlags)result;
1362 return 0;
1365 int sign = 1;
1366 if (*iter == CharT('-')) {
1367 sign = -1;
1368 result |= eParseHTMLInteger_Negative;
1369 ++iter;
1370 } else if (*iter == CharT('+')) {
1371 result |= eParseHTMLInteger_NonStandard;
1372 ++iter;
1375 bool foundValue = false;
1376 CheckedInt32 value = 0;
1378 // Check for leading zeros first.
1379 uint64_t leadingZeros = 0;
1380 while (iter != aEnd) {
1381 if (*iter != CharT('0')) {
1382 break;
1385 ++leadingZeros;
1386 foundValue = true;
1387 ++iter;
1390 while (iter != aEnd) {
1391 if (*iter >= CharT('0') && *iter <= CharT('9')) {
1392 value = (value * 10) + (*iter - CharT('0')) * sign;
1393 ++iter;
1394 if (!value.isValid()) {
1395 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
1396 break;
1398 foundValue = true;
1399 } else {
1400 break;
1404 if (!foundValue) {
1405 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1408 if (value.isValid() &&
1409 ((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
1410 (sign == -1 && value == 0))) {
1411 result |= eParseHTMLInteger_NonStandard;
1414 if (iter != aEnd) {
1415 result |= eParseHTMLInteger_DidNotConsumeAllInput;
1418 *aResult = (ParseHTMLIntegerResultFlags)result;
1419 return value.isValid() ? value.value() : 0;
1422 // Parse an integer according to HTML spec
1423 int32_t nsContentUtils::ParseHTMLInteger(const char16_t* aStart,
1424 const char16_t* aEnd,
1425 ParseHTMLIntegerResultFlags* aResult) {
1426 return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
1429 int32_t nsContentUtils::ParseHTMLInteger(const char* aStart, const char* aEnd,
1430 ParseHTMLIntegerResultFlags* aResult) {
1431 return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
1434 #define SKIP_WHITESPACE(iter, end_iter, end_res) \
1435 while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
1436 ++(iter); \
1438 if ((iter) == (end_iter)) { \
1439 return (end_res); \
1442 #define SKIP_ATTR_NAME(iter, end_iter) \
1443 while ((iter) != (end_iter) && !nsCRT::IsAsciiSpace(*(iter)) && \
1444 *(iter) != '=') { \
1445 ++(iter); \
1448 bool nsContentUtils::GetPseudoAttributeValue(const nsString& aSource,
1449 nsAtom* aName, nsAString& aValue) {
1450 aValue.Truncate();
1452 const char16_t* start = aSource.get();
1453 const char16_t* end = start + aSource.Length();
1454 const char16_t* iter;
1456 while (start != end) {
1457 SKIP_WHITESPACE(start, end, false)
1458 iter = start;
1459 SKIP_ATTR_NAME(iter, end)
1461 if (start == iter) {
1462 return false;
1465 // Remember the attr name.
1466 const nsDependentSubstring& attrName = Substring(start, iter);
1468 // Now check whether this is a valid name="value" pair.
1469 start = iter;
1470 SKIP_WHITESPACE(start, end, false)
1471 if (*start != '=') {
1472 // No '=', so this is not a name="value" pair. We don't know
1473 // what it is, and we have no way to handle it.
1474 return false;
1477 // Have to skip the value.
1478 ++start;
1479 SKIP_WHITESPACE(start, end, false)
1480 char16_t q = *start;
1481 if (q != kQuote && q != kApostrophe) {
1482 // Not a valid quoted value, so bail.
1483 return false;
1486 ++start; // Point to the first char of the value.
1487 iter = start;
1489 while (iter != end && *iter != q) {
1490 ++iter;
1493 if (iter == end) {
1494 // Oops, unterminated quoted string.
1495 return false;
1498 // At this point attrName holds the name of the "attribute" and
1499 // the value is between start and iter.
1501 if (aName->Equals(attrName)) {
1502 // We'll accumulate as many characters as possible (until we hit either
1503 // the end of the string or the beginning of an entity). Chunks will be
1504 // delimited by start and chunkEnd.
1505 const char16_t* chunkEnd = start;
1506 while (chunkEnd != iter) {
1507 if (*chunkEnd == kLessThan) {
1508 aValue.Truncate();
1510 return false;
1513 if (*chunkEnd == kAmpersand) {
1514 aValue.Append(start, chunkEnd - start);
1516 const char16_t* afterEntity = nullptr;
1517 char16_t result[2];
1518 uint32_t count = MOZ_XMLTranslateEntity(
1519 reinterpret_cast<const char*>(chunkEnd),
1520 reinterpret_cast<const char*>(iter),
1521 reinterpret_cast<const char**>(&afterEntity), result);
1522 if (count == 0) {
1523 aValue.Truncate();
1525 return false;
1528 aValue.Append(result, count);
1530 // Advance to after the entity and begin a new chunk.
1531 start = chunkEnd = afterEntity;
1532 } else {
1533 ++chunkEnd;
1537 // Append remainder.
1538 aValue.Append(start, iter - start);
1540 return true;
1543 // Resume scanning after the end of the attribute value (past the quote
1544 // char).
1545 start = iter + 1;
1548 return false;
1551 bool nsContentUtils::IsJavaScriptLanguage(const nsString& aName) {
1552 return aName.LowerCaseEqualsLiteral("javascript") ||
1553 aName.LowerCaseEqualsLiteral("livescript") ||
1554 aName.LowerCaseEqualsLiteral("mocha") ||
1555 aName.LowerCaseEqualsLiteral("javascript1.0") ||
1556 aName.LowerCaseEqualsLiteral("javascript1.1") ||
1557 aName.LowerCaseEqualsLiteral("javascript1.2") ||
1558 aName.LowerCaseEqualsLiteral("javascript1.3") ||
1559 aName.LowerCaseEqualsLiteral("javascript1.4") ||
1560 aName.LowerCaseEqualsLiteral("javascript1.5");
1563 void nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType,
1564 nsString& aParams) {
1565 aType.Truncate();
1566 aParams.Truncate();
1567 int32_t semiIndex = aValue.FindChar(char16_t(';'));
1568 if (-1 != semiIndex) {
1569 aType = Substring(aValue, 0, semiIndex);
1570 aParams =
1571 Substring(aValue, semiIndex + 1, aValue.Length() - (semiIndex + 1));
1572 aParams.StripWhitespace();
1573 } else {
1574 aType = aValue;
1576 aType.StripWhitespace();
1580 * A helper function that parses a sandbox attribute (of an <iframe> or a CSP
1581 * directive) and converts it to the set of flags used internally.
1583 * @param aSandboxAttr the sandbox attribute
1584 * @return the set of flags (SANDBOXED_NONE if aSandboxAttr is
1585 * null)
1587 uint32_t nsContentUtils::ParseSandboxAttributeToFlags(
1588 const nsAttrValue* aSandboxAttr) {
1589 if (!aSandboxAttr) {
1590 return SANDBOXED_NONE;
1593 uint32_t out = SANDBOX_ALL_FLAGS;
1595 #define SANDBOX_KEYWORD(string, atom, flags) \
1596 if (aSandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { \
1597 out &= ~(flags); \
1599 #include "IframeSandboxKeywordList.h"
1600 #undef SANDBOX_KEYWORD
1602 return out;
1606 * A helper function that checks if a string matches a valid sandbox flag.
1608 * @param aFlag the potential sandbox flag.
1609 * @return true if the flag is a sandbox flag.
1611 bool nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag) {
1612 #define SANDBOX_KEYWORD(string, atom, flags) \
1613 if (EqualsIgnoreASCIICase(nsDependentAtomString(nsGkAtoms::atom), aFlag)) { \
1614 return true; \
1616 #include "IframeSandboxKeywordList.h"
1617 #undef SANDBOX_KEYWORD
1618 return false;
1622 * A helper function that returns a string attribute corresponding to the
1623 * sandbox flags.
1625 * @param aFlags the sandbox flags
1626 * @param aString the attribute corresponding to the flags (null if aFlags
1627 * is zero)
1629 void nsContentUtils::SandboxFlagsToString(uint32_t aFlags, nsAString& aString) {
1630 if (!aFlags) {
1631 SetDOMStringToNull(aString);
1632 return;
1635 aString.Truncate();
1637 #define SANDBOX_KEYWORD(string, atom, flags) \
1638 if (!(aFlags & (flags))) { \
1639 if (!aString.IsEmpty()) { \
1640 aString.AppendLiteral(u" "); \
1642 aString.Append(nsDependentAtomString(nsGkAtoms::atom)); \
1644 #include "IframeSandboxKeywordList.h"
1645 #undef SANDBOX_KEYWORD
1648 nsIBidiKeyboard* nsContentUtils::GetBidiKeyboard() {
1649 if (!sBidiKeyboard) {
1650 sBidiKeyboard = nsIWidget::CreateBidiKeyboard();
1652 return sBidiKeyboard;
1656 * This is used to determine whether a character is in one of the classes
1657 * which CSS says should be part of the first-letter. Currently, that is
1658 * all punctuation classes (P*). Note that this is a change from CSS2
1659 * which excluded Pc and Pd.
1661 * https://www.w3.org/TR/css-pseudo-4/#first-letter-pseudo
1662 * "Punctuation (i.e, characters that belong to the Punctuation (P*) Unicode
1663 * general category [UAX44]) [...]"
1666 // static
1667 bool nsContentUtils::IsFirstLetterPunctuation(uint32_t aChar) {
1668 switch (mozilla::unicode::GetGeneralCategory(aChar)) {
1669 case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
1670 case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION: /* Pd */
1671 case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION: /* Pe */
1672 case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION: /* Pf */
1673 case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
1674 case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION: /* Po */
1675 case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION: /* Ps */
1676 return true;
1677 default:
1678 return false;
1682 // static
1683 bool nsContentUtils::IsAlphanumeric(uint32_t aChar) {
1684 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1686 return (cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber);
1689 // static
1690 bool nsContentUtils::IsAlphanumericOrSymbol(uint32_t aChar) {
1691 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1693 return cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber ||
1694 cat == nsUGenCategory::kSymbol;
1697 /* static */
1698 bool nsContentUtils::IsHTMLWhitespace(char16_t aChar) {
1699 return aChar == char16_t(0x0009) || aChar == char16_t(0x000A) ||
1700 aChar == char16_t(0x000C) || aChar == char16_t(0x000D) ||
1701 aChar == char16_t(0x0020);
1704 /* static */
1705 bool nsContentUtils::IsHTMLWhitespaceOrNBSP(char16_t aChar) {
1706 return IsHTMLWhitespace(aChar) || aChar == char16_t(0xA0);
1709 /* static */
1710 bool nsContentUtils::IsHTMLBlockLevelElement(nsIContent* aContent) {
1711 return aContent->IsAnyOfHTMLElements(
1712 nsGkAtoms::address, nsGkAtoms::article, nsGkAtoms::aside,
1713 nsGkAtoms::blockquote, nsGkAtoms::center, nsGkAtoms::dir, nsGkAtoms::div,
1714 nsGkAtoms::dl, // XXX why not dt and dd?
1715 nsGkAtoms::fieldset,
1716 nsGkAtoms::figure, // XXX shouldn't figcaption be on this list
1717 nsGkAtoms::footer, nsGkAtoms::form, nsGkAtoms::h1, nsGkAtoms::h2,
1718 nsGkAtoms::h3, nsGkAtoms::h4, nsGkAtoms::h5, nsGkAtoms::h6,
1719 nsGkAtoms::header, nsGkAtoms::hgroup, nsGkAtoms::hr, nsGkAtoms::li,
1720 nsGkAtoms::listing, nsGkAtoms::menu, nsGkAtoms::nav, nsGkAtoms::ol,
1721 nsGkAtoms::p, nsGkAtoms::pre, nsGkAtoms::section, nsGkAtoms::table,
1722 nsGkAtoms::ul, nsGkAtoms::xmp);
1725 /* static */
1726 bool nsContentUtils::ParseIntMarginValue(const nsAString& aString,
1727 nsIntMargin& result) {
1728 nsAutoString marginStr(aString);
1729 marginStr.CompressWhitespace(true, true);
1730 if (marginStr.IsEmpty()) {
1731 return false;
1734 int32_t start = 0, end = 0;
1735 for (int count = 0; count < 4; count++) {
1736 if ((uint32_t)end >= marginStr.Length()) return false;
1738 // top, right, bottom, left
1739 if (count < 3)
1740 end = Substring(marginStr, start).FindChar(',');
1741 else
1742 end = Substring(marginStr, start).Length();
1744 if (end <= 0) return false;
1746 nsresult ec;
1747 int32_t val = nsString(Substring(marginStr, start, end)).ToInteger(&ec);
1748 if (NS_FAILED(ec)) return false;
1750 switch (count) {
1751 case 0:
1752 result.top = val;
1753 break;
1754 case 1:
1755 result.right = val;
1756 break;
1757 case 2:
1758 result.bottom = val;
1759 break;
1760 case 3:
1761 result.left = val;
1762 break;
1764 start += end + 1;
1766 return true;
1769 // static
1770 int32_t nsContentUtils::ParseLegacyFontSize(const nsAString& aValue) {
1771 nsAString::const_iterator iter, end;
1772 aValue.BeginReading(iter);
1773 aValue.EndReading(end);
1775 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1776 ++iter;
1779 if (iter == end) {
1780 return 0;
1783 bool relative = false;
1784 bool negate = false;
1785 if (*iter == char16_t('-')) {
1786 relative = true;
1787 negate = true;
1788 ++iter;
1789 } else if (*iter == char16_t('+')) {
1790 relative = true;
1791 ++iter;
1794 if (iter == end || *iter < char16_t('0') || *iter > char16_t('9')) {
1795 return 0;
1798 // We don't have to worry about overflow, since we can bail out as soon as
1799 // we're bigger than 7.
1800 int32_t value = 0;
1801 while (iter != end && *iter >= char16_t('0') && *iter <= char16_t('9')) {
1802 value = 10 * value + (*iter - char16_t('0'));
1803 if (value >= 7) {
1804 break;
1806 ++iter;
1809 if (relative) {
1810 if (negate) {
1811 value = 3 - value;
1812 } else {
1813 value = 3 + value;
1817 return clamped(value, 1, 7);
1820 /* static */
1821 void nsContentUtils::GetOfflineAppManifest(Document* aDocument, nsIURI** aURI) {
1822 MOZ_ASSERT(NS_IsMainThread());
1823 MOZ_ASSERT(aDocument);
1824 *aURI = nullptr;
1826 if (aDocument->GetController().isSome()) {
1827 return;
1830 Element* docElement = aDocument->GetRootElement();
1831 if (!docElement) {
1832 return;
1835 nsAutoString manifestSpec;
1836 docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
1838 // Manifest URIs can't have fragment identifiers.
1839 if (manifestSpec.IsEmpty() || manifestSpec.Contains('#')) {
1840 return;
1843 nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec, aDocument,
1844 aDocument->GetDocBaseURI());
1847 /* static */
1848 bool nsContentUtils::OfflineAppAllowed(nsIURI* aURI) { return false; }
1850 /* static */
1851 bool nsContentUtils::OfflineAppAllowed(nsIPrincipal* aPrincipal) {
1852 return false;
1854 // Static
1855 bool nsContentUtils::IsErrorPage(nsIURI* aURI) {
1856 if (!aURI) {
1857 return false;
1860 if (!aURI->SchemeIs("about")) {
1861 return false;
1864 nsAutoCString name;
1865 nsresult rv = NS_GetAboutModuleName(aURI, name);
1866 NS_ENSURE_SUCCESS(rv, false);
1868 return name.EqualsLiteral("certerror") || name.EqualsLiteral("neterror") ||
1869 name.EqualsLiteral("blocked");
1872 // static
1873 void nsContentUtils::Shutdown() {
1874 sInitialized = false;
1876 nsHTMLTags::ReleaseTable();
1878 NS_IF_RELEASE(sContentPolicyService);
1879 sTriedToGetContentPolicy = false;
1880 for (StaticRefPtr<nsIStringBundle>& bundle : sStringBundles) {
1881 bundle = nullptr;
1884 NS_IF_RELEASE(sStringBundleService);
1885 NS_IF_RELEASE(sConsoleService);
1886 NS_IF_RELEASE(sXPConnect);
1887 NS_IF_RELEASE(sSecurityManager);
1888 NS_IF_RELEASE(sSystemPrincipal);
1889 NS_IF_RELEASE(sNullSubjectPrincipal);
1890 NS_IF_RELEASE(sIOService);
1892 sBidiKeyboard = nullptr;
1894 delete sAtomEventTable;
1895 sAtomEventTable = nullptr;
1896 delete sStringEventTable;
1897 sStringEventTable = nullptr;
1898 delete sUserDefinedEvents;
1899 sUserDefinedEvents = nullptr;
1901 if (sEventListenerManagersHash) {
1902 NS_ASSERTION(sEventListenerManagersHash->EntryCount() == 0,
1903 "Event listener manager hash not empty at shutdown!");
1905 // See comment above.
1907 // However, we have to handle this table differently. If it still
1908 // has entries, we want to leak it too, so that we can keep it alive
1909 // in case any elements are destroyed. Because if they are, we need
1910 // their event listener managers to be destroyed too, or otherwise
1911 // it could leave dangling references in DOMClassInfo's preserved
1912 // wrapper table.
1914 if (sEventListenerManagersHash->EntryCount() == 0) {
1915 delete sEventListenerManagersHash;
1916 sEventListenerManagersHash = nullptr;
1920 if (sDOMArenaHashtable) {
1921 MOZ_ASSERT(sDOMArenaHashtable->Count() == 0);
1922 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
1923 delete sDOMArenaHashtable;
1924 sDOMArenaHashtable = nullptr;
1927 NS_ASSERTION(!sBlockedScriptRunners || sBlockedScriptRunners->Length() == 0,
1928 "How'd this happen?");
1929 delete sBlockedScriptRunners;
1930 sBlockedScriptRunners = nullptr;
1932 delete sShiftText;
1933 sShiftText = nullptr;
1934 delete sControlText;
1935 sControlText = nullptr;
1936 delete sMetaText;
1937 sMetaText = nullptr;
1938 delete sOSText;
1939 sOSText = nullptr;
1940 delete sAltText;
1941 sAltText = nullptr;
1942 delete sModifierSeparator;
1943 sModifierSeparator = nullptr;
1945 delete sJSScriptBytecodeMimeType;
1946 sJSScriptBytecodeMimeType = nullptr;
1948 delete sJSModuleBytecodeMimeType;
1949 sJSModuleBytecodeMimeType = nullptr;
1951 NS_IF_RELEASE(sSameOriginChecker);
1953 if (sUserInteractionObserver) {
1954 sUserInteractionObserver->Shutdown();
1955 NS_RELEASE(sUserInteractionObserver);
1958 for (const auto& pref : kRfpPrefs) {
1959 Preferences::UnregisterCallback(RecomputeResistFingerprintingAllDocs, pref);
1962 TextControlState::Shutdown();
1963 nsMappedAttributes::Shutdown();
1967 * Checks whether two nodes come from the same origin. aTrustedNode is
1968 * considered 'safe' in that a user can operate on it.
1970 // static
1971 nsresult nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
1972 const nsINode* unTrustedNode) {
1973 MOZ_ASSERT(aTrustedNode);
1974 MOZ_ASSERT(unTrustedNode);
1977 * Get hold of each node's principal
1980 nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
1981 nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
1983 if (trustedPrincipal == unTrustedPrincipal) {
1984 return NS_OK;
1987 bool equal;
1988 // XXXbz should we actually have a Subsumes() check here instead? Or perhaps
1989 // a separate method for that, with callers using one or the other?
1990 if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
1991 !equal) {
1992 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
1995 return NS_OK;
1998 // static
1999 bool nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
2000 nsIPrincipal* aPrincipal) {
2001 bool subsumes;
2002 nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
2003 NS_ENSURE_SUCCESS(rv, false);
2005 if (subsumes) {
2006 return true;
2009 // The subject doesn't subsume aPrincipal. Allow access only if the subject
2010 // is chrome.
2011 return IsCallerChrome();
2014 // static
2015 bool nsContentUtils::CanCallerAccess(const nsINode* aNode) {
2016 nsIPrincipal* subject = SubjectPrincipal();
2017 if (subject->IsSystemPrincipal()) {
2018 return true;
2021 if (aNode->ChromeOnlyAccess()) {
2022 return false;
2025 return CanCallerAccess(subject, aNode->NodePrincipal());
2028 // static
2029 bool nsContentUtils::CanCallerAccess(nsPIDOMWindowInner* aWindow) {
2030 nsCOMPtr<nsIScriptObjectPrincipal> scriptObject = do_QueryInterface(aWindow);
2031 NS_ENSURE_TRUE(scriptObject, false);
2033 return CanCallerAccess(SubjectPrincipal(), scriptObject->GetPrincipal());
2036 // static
2037 bool nsContentUtils::PrincipalHasPermission(nsIPrincipal& aPrincipal,
2038 const nsAtom* aPerm) {
2039 // Chrome gets access by default.
2040 if (aPrincipal.IsSystemPrincipal()) {
2041 return true;
2044 // Otherwise, only allow if caller is an addon with the permission.
2045 return BasePrincipal::Cast(aPrincipal).AddonHasPermission(aPerm);
2048 // static
2049 bool nsContentUtils::CallerHasPermission(JSContext* aCx, const nsAtom* aPerm) {
2050 return PrincipalHasPermission(*SubjectPrincipal(aCx), aPerm);
2053 // static
2054 nsIPrincipal* nsContentUtils::GetAttrTriggeringPrincipal(
2055 nsIContent* aContent, const nsAString& aAttrValue,
2056 nsIPrincipal* aSubjectPrincipal) {
2057 nsIPrincipal* contentPrin = aContent ? aContent->NodePrincipal() : nullptr;
2059 // If the subject principal is the same as the content principal, or no
2060 // explicit subject principal was provided, we don't need to do any further
2061 // checks. Just return the content principal.
2062 if (contentPrin == aSubjectPrincipal || !aSubjectPrincipal) {
2063 return contentPrin;
2066 // Only use the subject principal if the URL string we are going to end up
2067 // fetching is under the control of that principal, which is never the case
2068 // for relative URLs.
2069 if (aAttrValue.IsEmpty() ||
2070 !IsAbsoluteURL(NS_ConvertUTF16toUTF8(aAttrValue))) {
2071 return contentPrin;
2074 // Only use the subject principal as the attr triggering principal if it
2075 // should override the CSP of the node's principal.
2076 if (BasePrincipal::Cast(aSubjectPrincipal)->OverridesCSP(contentPrin)) {
2077 return aSubjectPrincipal;
2080 return contentPrin;
2083 // static
2084 bool nsContentUtils::IsAbsoluteURL(const nsACString& aURL) {
2085 nsAutoCString scheme;
2086 if (NS_FAILED(net_ExtractURLScheme(aURL, scheme))) {
2087 // If we can't extract a scheme, it's not an absolute URL.
2088 return false;
2091 // If it parses as an absolute StandardURL, it's definitely an absolute URL,
2092 // so no need to check with the IO service.
2093 if (net_IsAbsoluteURL(aURL)) {
2094 return true;
2097 uint32_t flags;
2098 if (NS_SUCCEEDED(sIOService->GetProtocolFlags(scheme.get(), &flags))) {
2099 return flags & nsIProtocolHandler::URI_NORELATIVE;
2102 return false;
2105 // static
2106 bool nsContentUtils::InProlog(nsINode* aNode) {
2107 MOZ_ASSERT(aNode, "missing node to nsContentUtils::InProlog");
2109 nsINode* parent = aNode->GetParentNode();
2110 if (!parent || !parent->IsDocument()) {
2111 return false;
2114 const Document* doc = parent->AsDocument();
2115 const nsIContent* root = doc->GetRootElement();
2116 if (!root) {
2117 return true;
2119 const Maybe<uint32_t> indexOfNode = doc->ComputeIndexOf(aNode);
2120 const Maybe<uint32_t> indexOfRoot = doc->ComputeIndexOf(root);
2121 if (MOZ_LIKELY(indexOfNode.isSome() && indexOfRoot.isSome())) {
2122 return *indexOfNode < *indexOfRoot;
2124 // XXX Keep the odd traditional behavior for now.
2125 return indexOfNode.isNothing() && indexOfRoot.isSome();
2128 bool nsContentUtils::IsCallerChrome() {
2129 MOZ_ASSERT(NS_IsMainThread());
2130 return SubjectPrincipal() == sSystemPrincipal;
2133 #ifdef FUZZING
2134 bool nsContentUtils::IsFuzzingEnabled() {
2135 return StaticPrefs::fuzzing_enabled();
2137 #endif
2139 /* static */
2140 bool nsContentUtils::IsCallerChromeOrElementTransformGettersEnabled(
2141 JSContext* aCx, JSObject*) {
2142 return ThreadsafeIsSystemCaller(aCx) ||
2143 StaticPrefs::dom_element_transform_getters_enabled();
2146 // Older Should RFP Functions ----------------------------------
2148 /* static */
2149 bool nsContentUtils::ShouldResistFingerprinting() {
2150 return StaticPrefs::privacy_resistFingerprinting_DoNotUseDirectly();
2153 /* static */
2154 bool nsContentUtils::ShouldResistFingerprinting(
2155 nsIGlobalObject* aGlobalObject) {
2156 if (!aGlobalObject) {
2157 return ShouldResistFingerprinting();
2159 return aGlobalObject->ShouldResistFingerprinting();
2162 // Newer Should RFP Functions ----------------------------------
2164 inline void LogDomainAndPrefList(const char* exemptedDomainsPrefName,
2165 nsAutoCString& url, bool isExemptDomain) {
2166 nsAutoCString list;
2167 Preferences::GetCString(exemptedDomainsPrefName, list);
2168 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2169 ("Domain \"%s\" is %s the exempt list \"%s\"",
2170 PromiseFlatCString(url).get(), isExemptDomain ? "in" : "NOT in",
2171 PromiseFlatCString(list).get()));
2174 inline bool CookieJarSettingsSaysShouldResistFingerprinting(
2175 nsILoadInfo* aLoadInfo) {
2176 // If the loadinfo's CookieJarSettings says that we _should_ resist
2177 // fingerprinting we can always believe it. (This is the (*) rule from
2178 // CookieJarSettings.h)
2179 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
2180 nsresult rv =
2181 aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
2182 if (NS_WARN_IF(NS_FAILED(rv))) {
2183 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2184 ("Called CookieJarSettingsSaysShouldResistFingerprinting but the "
2185 "loadinfo's CookieJarSettings couldn't be retrieved"));
2186 return true;
2188 return cookieJarSettings->GetShouldResistFingerprinting();
2191 // These constants are used for privacy.resistFingerprinting.testGranularityMask
2192 const unsigned int sWebExtensionExemptMask = 0x01;
2193 const unsigned int sNonPBMExemptMask = 0x02;
2194 const unsigned int sSpecificDomainsExemptMask = 0x04;
2195 const char* kExemptedDomainsPrefName =
2196 "privacy.resistFingerprinting.exemptedDomains";
2198 /* static */
2199 bool nsContentUtils::ShouldResistFingerprinting(const char* aJustification) {
2200 // See comment in header file for information about usage
2201 return ShouldResistFingerprinting();
2204 /* static */
2205 bool nsContentUtils::ShouldResistFingerprinting(
2206 CallerType aCallerType, nsIGlobalObject* aGlobalObject) {
2207 if (aCallerType == CallerType::System) {
2208 return false;
2210 return ShouldResistFingerprinting(aGlobalObject);
2213 bool nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell) {
2214 if (!aDocShell) {
2215 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2216 ("Called nsContentUtils::ShouldResistFingerprinting(nsIDocShell*) "
2217 "with NULL docshell"));
2218 return ShouldResistFingerprinting();
2220 Document* doc = aDocShell->GetDocument();
2221 if (!doc) {
2222 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2223 ("Called nsContentUtils::ShouldResistFingerprinting(nsIDocShell*) "
2224 "with NULL doc"));
2225 return ShouldResistFingerprinting();
2227 return doc->ShouldResistFingerprinting();
2230 /* static */
2231 bool nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel) {
2232 if (!ShouldResistFingerprinting("Legacy quick-check")) {
2233 return false;
2236 if (!aChannel) {
2237 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2238 ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2239 "aChannel) with NULL channel"));
2240 return true;
2243 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
2244 if (!loadInfo) {
2245 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2246 ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2247 "aChannel) but the channel's loadinfo was NULL"));
2248 return true;
2251 // Document types have no loading principal. Subdocument types do have a
2252 // loading principal, but it is the loading principal of the parent document;
2253 // not the subdocument.
2254 auto contentType = loadInfo->GetExternalContentPolicyType();
2255 if (contentType == ExtContentPolicy::TYPE_DOCUMENT ||
2256 contentType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
2257 // This cookie jar check is relevant to both document and non-document
2258 // cases. but it will be performed inside the ShouldRFP(nsILoadInfo) as
2259 // well, so we put into this conditional to avoid doing it twice in that
2260 // case.
2261 if (CookieJarSettingsSaysShouldResistFingerprinting(loadInfo)) {
2262 return true;
2265 nsCOMPtr<nsIURI> channelURI;
2266 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
2267 MOZ_ASSERT(
2268 NS_SUCCEEDED(rv),
2269 "Failed to get URI in "
2270 "nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel)");
2271 // this check is to ensure that we do not crash in non-debug builds.
2272 if (NS_FAILED(rv)) {
2273 return true;
2276 #if 0
2277 if (loadInfo->GetExternalContentPolicyType() == ExtContentPolicy::TYPE_SUBDOCUMENT) {
2278 nsCOMPtr<nsIURI> channelURI;
2279 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
2280 nsAutoCString channelSpec;
2281 channelURI->GetSpec(channelSpec);
2283 if (!loadInfo->GetLoadingPrincipal()) {
2284 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2285 ("Sub Document Type. FinalChannelURI is %s, Loading Principal is NULL\n",
2286 channelSpec.get()));
2288 } else {
2289 nsAutoCString loadingPrincipalSpec;
2290 loadInfo->GetLoadingPrincipal()->GetOrigin(loadingPrincipalSpec);
2292 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2293 ("Sub Document Type. FinalChannelURI is %s, Loading Principal Origin is %s\n",
2294 channelSpec.get(), loadingPrincipalSpec.get()));
2298 #endif
2300 return ShouldResistFingerprinting_dangerous(
2301 channelURI, loadInfo->GetOriginAttributes(), "Internal Call");
2304 // Case 2: Subresource Load
2305 return ShouldResistFingerprinting(loadInfo);
2308 /* static */
2309 bool nsContentUtils::ShouldResistFingerprinting_dangerous(
2310 nsIURI* aURI, const mozilla::OriginAttributes& aOriginAttributes,
2311 const char* aJustification) {
2312 if (!ShouldResistFingerprinting("Legacy quick-check")) {
2313 return false;
2316 if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2317 sNonPBMExemptMask) {
2318 // if non-PBM exempt mask is true, exempt non-PBM channels.
2319 if (aOriginAttributes.mPrivateBrowsingId == 0) {
2320 return false;
2324 bool isExemptDomain = false;
2325 // Exclude internal schemes
2326 if (aURI->SchemeIs("about") || aURI->SchemeIs("chrome") ||
2327 aURI->SchemeIs("resource") || aURI->SchemeIs("view-source")) {
2328 return false;
2331 if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2332 sWebExtensionExemptMask) {
2333 if (aURI->SchemeIs("web-extension")) {
2334 return false;
2338 if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2339 sSpecificDomainsExemptMask) {
2340 nsAutoCString list;
2341 Preferences::GetCString(kExemptedDomainsPrefName, list);
2342 ToLowerCase(list);
2343 isExemptDomain = IsURIInList(aURI, list);
2345 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2346 mozilla::LogLevel::Debug)) {
2347 nsAutoCString url;
2348 aURI->GetHost(url);
2349 LogDomainAndPrefList(kExemptedDomainsPrefName, url, isExemptDomain);
2353 return !isExemptDomain;
2356 /* static */
2357 bool nsContentUtils::ShouldResistFingerprinting(nsILoadInfo* aLoadInfo) {
2358 MOZ_ASSERT(aLoadInfo->GetExternalContentPolicyType() !=
2359 ExtContentPolicy::TYPE_DOCUMENT &&
2360 aLoadInfo->GetExternalContentPolicyType() !=
2361 ExtContentPolicy::TYPE_SUBDOCUMENT);
2363 if (!ShouldResistFingerprinting("Legacy quick-check")) {
2364 return false;
2367 if (CookieJarSettingsSaysShouldResistFingerprinting(aLoadInfo)) {
2368 return true;
2371 // Because this function is only used for subresource loads, this
2372 // will check the parent's principal
2373 nsIPrincipal* principal = aLoadInfo->GetLoadingPrincipal();
2375 if (principal->IsSystemPrincipal()) {
2376 return false;
2379 MOZ_ASSERT(BasePrincipal::Cast(principal)->OriginAttributesRef() ==
2380 aLoadInfo->GetOriginAttributes());
2381 return ShouldResistFingerprinting_dangerous(principal, "Internal Call");
2384 /* static */
2385 bool nsContentUtils::ShouldResistFingerprinting_dangerous(
2386 nsIPrincipal* aPrincipal, const char* aJustification) {
2387 if (!ShouldResistFingerprinting("Legacy quick-check")) {
2388 return false;
2391 if (!aPrincipal) {
2392 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2393 ("Called nsContentUtils::ShouldResistFingerprinting(nsILoadInfo* "
2394 "aChannel) but the loadinfo's loadingprincipal was NULL"));
2395 return true;
2398 if (aPrincipal->IsSystemPrincipal()) {
2399 return false;
2402 auto originAttributes =
2403 BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
2404 if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2405 sNonPBMExemptMask) {
2406 // if non-PBM exempt mask is true, exempt non-PBM channels.
2407 if (originAttributes.mPrivateBrowsingId == 0) {
2408 return false;
2412 // Exclude internal schemes
2413 if (aPrincipal->SchemeIs("about") || aPrincipal->SchemeIs("chrome") ||
2414 aPrincipal->SchemeIs("resource") || aPrincipal->SchemeIs("view-source")) {
2415 return false;
2418 if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2419 sWebExtensionExemptMask) {
2420 if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
2421 return false;
2425 bool isExemptDomain = false;
2426 if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2427 sSpecificDomainsExemptMask) {
2428 aPrincipal->IsURIInPrefList(kExemptedDomainsPrefName, &isExemptDomain);
2430 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2431 mozilla::LogLevel::Debug)) {
2432 nsAutoCString origin;
2433 aPrincipal->GetAsciiOrigin(origin);
2434 LogDomainAndPrefList(kExemptedDomainsPrefName, origin, isExemptDomain);
2438 // If we've gotten here we have (probably) passed the CookieJarSettings
2439 // check that would tell us that if we _are_ a subdocument, then we are on
2440 // an exempted top-level domain and we should see if we ourselves are
2441 // exempted. But we may have gotten here because we directly called the
2442 // _dangerous function and we haven't done that check, but we _were_
2443 // instatiated from a state where we could have been partitioned.
2444 // So perform this last-ditch check for that scenario.
2445 // We arbitrarily use https as the scheme, but it doesn't matter.
2446 nsCOMPtr<nsIURI> uri;
2447 nsresult rv;
2448 if (isExemptDomain && StaticPrefs::privacy_firstparty_isolate() &&
2449 !originAttributes.mFirstPartyDomain.IsEmpty()) {
2450 rv = NS_NewURI(getter_AddRefs(uri),
2451 u"https://"_ns + originAttributes.mFirstPartyDomain);
2452 if (!NS_FAILED(rv)) {
2453 isExemptDomain =
2454 nsContentUtils::IsURIInPrefList(uri, kExemptedDomainsPrefName);
2456 } else if (isExemptDomain && !originAttributes.mPartitionKey.IsEmpty()) {
2457 rv = NS_NewURI(getter_AddRefs(uri),
2458 u"https://"_ns + originAttributes.mPartitionKey);
2459 if (!NS_FAILED(rv)) {
2460 isExemptDomain =
2461 nsContentUtils::IsURIInPrefList(uri, kExemptedDomainsPrefName);
2465 return !isExemptDomain;
2468 /* static */
2469 void nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
2470 int32_t aChromeWidth, int32_t aChromeHeight, int32_t aScreenWidth,
2471 int32_t aScreenHeight, int32_t aInputWidth, int32_t aInputHeight,
2472 bool aSetOuterWidth, bool aSetOuterHeight, int32_t* aOutputWidth,
2473 int32_t* aOutputHeight) {
2474 MOZ_ASSERT(aOutputWidth);
2475 MOZ_ASSERT(aOutputHeight);
2477 int32_t availContentWidth = 0;
2478 int32_t availContentHeight = 0;
2480 availContentWidth = std::min(StaticPrefs::privacy_window_maxInnerWidth(),
2481 aScreenWidth - aChromeWidth);
2482 #ifdef MOZ_WIDGET_GTK
2483 // In the GTK window, it will not report outside system decorations
2484 // when we get available window size, see Bug 581863. So, we leave a
2485 // 40 pixels space for them when calculating the available content
2486 // height. It is not necessary for the width since the content width
2487 // is usually pretty much the same as the chrome width.
2488 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2489 (-40 + aScreenHeight) - aChromeHeight);
2490 #else
2491 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2492 aScreenHeight - aChromeHeight);
2493 #endif
2495 // Ideally, we'd like to round window size to 1000x1000, but the
2496 // screen space could be too small to accommodate this size in some
2497 // cases. If it happens, we would round the window size to the nearest
2498 // 200x100.
2499 availContentWidth = availContentWidth - (availContentWidth % 200);
2500 availContentHeight = availContentHeight - (availContentHeight % 100);
2502 // If aIsOuter is true, we are setting the outer window. So we
2503 // have to consider the chrome UI.
2504 int32_t chromeOffsetWidth = aSetOuterWidth ? aChromeWidth : 0;
2505 int32_t chromeOffsetHeight = aSetOuterHeight ? aChromeHeight : 0;
2506 int32_t resultWidth = 0, resultHeight = 0;
2508 // if the original size is greater than the maximum available size, we set
2509 // it to the maximum size. And if the original value is less than the
2510 // minimum rounded size, we set it to the minimum 200x100.
2511 if (aInputWidth > (availContentWidth + chromeOffsetWidth)) {
2512 resultWidth = availContentWidth + chromeOffsetWidth;
2513 } else if (aInputWidth < (200 + chromeOffsetWidth)) {
2514 resultWidth = 200 + chromeOffsetWidth;
2515 } else {
2516 // Otherwise, we round the window to the nearest upper rounded 200x100.
2517 resultWidth = NSToIntCeil((aInputWidth - chromeOffsetWidth) / 200.0) * 200 +
2518 chromeOffsetWidth;
2521 if (aInputHeight > (availContentHeight + chromeOffsetHeight)) {
2522 resultHeight = availContentHeight + chromeOffsetHeight;
2523 } else if (aInputHeight < (100 + chromeOffsetHeight)) {
2524 resultHeight = 100 + chromeOffsetHeight;
2525 } else {
2526 resultHeight =
2527 NSToIntCeil((aInputHeight - chromeOffsetHeight) / 100.0) * 100 +
2528 chromeOffsetHeight;
2531 *aOutputWidth = resultWidth;
2532 *aOutputHeight = resultHeight;
2535 bool nsContentUtils::ThreadsafeIsCallerChrome() {
2536 return NS_IsMainThread() ? IsCallerChrome()
2537 : IsCurrentThreadRunningChromeWorker();
2540 bool nsContentUtils::IsCallerUAWidget() {
2541 JSContext* cx = GetCurrentJSContext();
2542 if (!cx) {
2543 return false;
2546 JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
2547 if (!realm) {
2548 return false;
2551 return xpc::IsUAWidgetScope(realm);
2554 bool nsContentUtils::IsSystemCaller(JSContext* aCx) {
2555 // Note that SubjectPrincipal() assumes we are in a compartment here.
2556 return SubjectPrincipal(aCx) == sSystemPrincipal;
2559 bool nsContentUtils::ThreadsafeIsSystemCaller(JSContext* aCx) {
2560 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
2561 MOZ_ASSERT(ccjscx->Context() == aCx);
2563 return ccjscx->IsSystemCaller();
2566 // static
2567 bool nsContentUtils::LookupBindingMember(
2568 JSContext* aCx, nsIContent* aContent, JS::Handle<jsid> aId,
2569 JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
2570 return true;
2573 nsINode* nsContentUtils::GetNearestInProcessCrossDocParentNode(
2574 nsINode* aChild) {
2575 if (aChild->IsDocument()) {
2576 for (BrowsingContext* bc = aChild->AsDocument()->GetBrowsingContext(); bc;
2577 bc = bc->GetParent()) {
2578 if (bc->GetEmbedderElement()) {
2579 return bc->GetEmbedderElement();
2582 return nullptr;
2585 nsINode* parent = aChild->GetParentNode();
2586 if (parent && parent->IsContent() && aChild->IsContent()) {
2587 parent = aChild->AsContent()->GetFlattenedTreeParent();
2590 return parent;
2593 bool nsContentUtils::ContentIsHostIncludingDescendantOf(
2594 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2595 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2596 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2598 do {
2599 if (aPossibleDescendant == aPossibleAncestor) return true;
2600 if (aPossibleDescendant->IsDocumentFragment()) {
2601 aPossibleDescendant =
2602 aPossibleDescendant->AsDocumentFragment()->GetHost();
2603 } else {
2604 aPossibleDescendant = aPossibleDescendant->GetParentNode();
2606 } while (aPossibleDescendant);
2608 return false;
2611 // static
2612 bool nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
2613 nsINode* aPossibleAncestor) {
2614 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2615 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2617 do {
2618 if (aPossibleDescendant == aPossibleAncestor) {
2619 return true;
2622 aPossibleDescendant =
2623 GetNearestInProcessCrossDocParentNode(aPossibleDescendant);
2624 } while (aPossibleDescendant);
2626 return false;
2629 // static
2630 bool nsContentUtils::ContentIsFlattenedTreeDescendantOf(
2631 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2632 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2633 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2635 do {
2636 if (aPossibleDescendant == aPossibleAncestor) {
2637 return true;
2639 aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
2640 } while (aPossibleDescendant);
2642 return false;
2645 // static
2646 bool nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
2647 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2648 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2649 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2651 do {
2652 if (aPossibleDescendant == aPossibleAncestor) {
2653 return true;
2655 aPossibleDescendant =
2656 aPossibleDescendant->GetFlattenedTreeParentNodeForStyle();
2657 } while (aPossibleDescendant);
2659 return false;
2662 // static
2663 nsINode* nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB) {
2664 while (true && aTargetA) {
2665 // If A's root is not a shadow root...
2666 nsINode* root = aTargetA->SubtreeRoot();
2667 if (!root->IsShadowRoot()) {
2668 // ...then return A.
2669 return aTargetA;
2672 // or A's root is a shadow-including inclusive ancestor of B...
2673 if (aTargetB->IsShadowIncludingInclusiveDescendantOf(root)) {
2674 // ...then return A.
2675 return aTargetA;
2678 aTargetA = ShadowRoot::FromNode(root)->GetHost();
2681 return nullptr;
2684 // static
2685 nsINode* nsContentUtils::GetAnElementForTiming(Element* aTarget,
2686 const Document* aDocument,
2687 nsIGlobalObject* aGlobal) {
2688 if (!aTarget->IsInComposedDoc()) {
2689 return nullptr;
2692 if (!aDocument) {
2693 nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(aGlobal);
2694 if (!inner) {
2695 return nullptr;
2697 aDocument = inner->GetExtantDoc();
2700 MOZ_ASSERT(aDocument);
2702 if (aTarget->GetUncomposedDocOrConnectedShadowRoot() != aDocument ||
2703 !aDocument->IsCurrentActiveDocument()) {
2704 return nullptr;
2707 return aTarget;
2710 // static
2711 nsresult nsContentUtils::GetInclusiveAncestors(nsINode* aNode,
2712 nsTArray<nsINode*>& aArray) {
2713 while (aNode) {
2714 aArray.AppendElement(aNode);
2715 aNode = aNode->GetParentNode();
2717 return NS_OK;
2720 // static
2721 nsresult nsContentUtils::GetInclusiveAncestorsAndOffsets(
2722 nsINode* aNode, uint32_t aOffset, nsTArray<nsIContent*>* aAncestorNodes,
2723 nsTArray<Maybe<uint32_t>>* aAncestorOffsets) {
2724 NS_ENSURE_ARG_POINTER(aNode);
2726 if (!aNode->IsContent()) {
2727 return NS_ERROR_FAILURE;
2729 nsIContent* content = aNode->AsContent();
2731 if (!aAncestorNodes->IsEmpty()) {
2732 NS_WARNING("aAncestorNodes is not empty");
2733 aAncestorNodes->Clear();
2736 if (!aAncestorOffsets->IsEmpty()) {
2737 NS_WARNING("aAncestorOffsets is not empty");
2738 aAncestorOffsets->Clear();
2741 // insert the node itself
2742 aAncestorNodes->AppendElement(content);
2743 aAncestorOffsets->AppendElement(Some(aOffset));
2745 // insert all the ancestors
2746 nsIContent* child = content;
2747 nsIContent* parent = child->GetParent();
2748 while (parent) {
2749 aAncestorNodes->AppendElement(parent);
2750 aAncestorOffsets->AppendElement(parent->ComputeIndexOf(child));
2751 child = parent;
2752 parent = parent->GetParent();
2755 return NS_OK;
2758 template <typename Node, typename GetParentFunc>
2759 static Node* GetCommonAncestorInternal(Node* aNode1, Node* aNode2,
2760 GetParentFunc aGetParentFunc) {
2761 MOZ_ASSERT(aNode1 != aNode2);
2763 // Build the chain of parents
2764 AutoTArray<Node*, 30> parents1, parents2;
2765 do {
2766 parents1.AppendElement(aNode1);
2767 aNode1 = aGetParentFunc(aNode1);
2768 } while (aNode1);
2769 do {
2770 parents2.AppendElement(aNode2);
2771 aNode2 = aGetParentFunc(aNode2);
2772 } while (aNode2);
2774 // Find where the parent chain differs
2775 uint32_t pos1 = parents1.Length();
2776 uint32_t pos2 = parents2.Length();
2777 Node* parent = nullptr;
2778 uint32_t len;
2779 for (len = std::min(pos1, pos2); len > 0; --len) {
2780 Node* child1 = parents1.ElementAt(--pos1);
2781 Node* child2 = parents2.ElementAt(--pos2);
2782 if (child1 != child2) {
2783 break;
2785 parent = child1;
2788 return parent;
2791 /* static */
2792 nsINode* nsContentUtils::GetCommonAncestorHelper(nsINode* aNode1,
2793 nsINode* aNode2) {
2794 return GetCommonAncestorInternal(
2795 aNode1, aNode2, [](nsINode* aNode) { return aNode->GetParentNode(); });
2798 /* static */
2799 nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorHelper(
2800 nsIContent* aContent1, nsIContent* aContent2) {
2801 return GetCommonAncestorInternal(
2802 aContent1, aContent2,
2803 [](nsIContent* aContent) { return aContent->GetFlattenedTreeParent(); });
2806 /* static */
2807 Element* nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
2808 Element* aElement1, Element* aElement2) {
2809 return GetCommonAncestorInternal(aElement1, aElement2, [](Element* aElement) {
2810 return aElement->GetFlattenedTreeParentElementForStyle();
2814 /* static */
2815 bool nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2,
2816 Maybe<uint32_t>* aNode1Index,
2817 Maybe<uint32_t>* aNode2Index) {
2818 // Note, CompareDocumentPosition takes the latter params in different order.
2819 return (aNode2->CompareDocumentPosition(*aNode1, aNode2Index, aNode1Index) &
2820 (Node_Binding::DOCUMENT_POSITION_PRECEDING |
2821 Node_Binding::DOCUMENT_POSITION_DISCONNECTED)) ==
2822 Node_Binding::DOCUMENT_POSITION_PRECEDING;
2825 /* static */
2826 Maybe<int32_t> nsContentUtils::ComparePoints(
2827 const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
2828 uint32_t aOffset2, ComparePointsCache* aParent1Cache) {
2829 bool disconnected{false};
2831 const int32_t order = ComparePoints_Deprecated(
2832 aParent1, aOffset1, aParent2, aOffset2, &disconnected, aParent1Cache);
2833 if (disconnected) {
2834 return Nothing();
2837 return Some(order);
2840 /* static */
2841 int32_t nsContentUtils::ComparePoints_Deprecated(
2842 const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
2843 uint32_t aOffset2, bool* aDisconnected, ComparePointsCache* aParent1Cache) {
2844 if (aParent1 == aParent2) {
2845 return aOffset1 < aOffset2 ? -1 : aOffset1 > aOffset2 ? 1 : 0;
2848 AutoTArray<const nsINode*, 32> parents1, parents2;
2849 const nsINode* node1 = aParent1;
2850 const nsINode* node2 = aParent2;
2851 do {
2852 parents1.AppendElement(node1);
2853 node1 = node1->GetParentOrShadowHostNode();
2854 } while (node1);
2855 do {
2856 parents2.AppendElement(node2);
2857 node2 = node2->GetParentOrShadowHostNode();
2858 } while (node2);
2860 uint32_t pos1 = parents1.Length() - 1;
2861 uint32_t pos2 = parents2.Length() - 1;
2863 bool disconnected = parents1.ElementAt(pos1) != parents2.ElementAt(pos2);
2864 if (aDisconnected) {
2865 *aDisconnected = disconnected;
2867 if (disconnected) {
2868 NS_ASSERTION(aDisconnected, "unexpected disconnected nodes");
2869 return 1;
2872 // Find where the parent chains differ
2873 const nsINode* parent = parents1.ElementAt(pos1);
2874 uint32_t len;
2875 for (len = std::min(pos1, pos2); len > 0; --len) {
2876 const nsINode* child1 = parents1.ElementAt(--pos1);
2877 const nsINode* child2 = parents2.ElementAt(--pos2);
2878 if (child1 != child2) {
2879 if (MOZ_UNLIKELY(child1->IsShadowRoot())) {
2880 // Shadow roots come before light DOM per
2881 // https://dom.spec.whatwg.org/#concept-shadow-including-tree-order
2882 MOZ_ASSERT(!child2->IsShadowRoot(), "Two shadow roots?");
2883 return -1;
2885 if (MOZ_UNLIKELY(child2->IsShadowRoot())) {
2886 return 1;
2888 const Maybe<uint32_t> child1Index =
2889 aParent1Cache ? aParent1Cache->ComputeIndexOf(parent, child1)
2890 : parent->ComputeIndexOf(child1);
2891 const Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2);
2892 if (MOZ_LIKELY(child1Index.isSome() && child2Index.isSome())) {
2893 return *child1Index < *child2Index ? -1 : 1;
2895 // XXX Keep the odd traditional behavior for now.
2896 return child1Index.isNothing() && child2Index.isSome() ? -1 : 1;
2898 parent = child1;
2901 // The parent chains never differed, so one of the nodes is an ancestor of
2902 // the other
2904 NS_ASSERTION(!pos1 || !pos2,
2905 "should have run out of parent chain for one of the nodes");
2907 if (!pos1) {
2908 const nsINode* child2 = parents2.ElementAt(--pos2);
2909 const Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2);
2910 if (MOZ_UNLIKELY(NS_WARN_IF(child2Index.isNothing()))) {
2911 return 1;
2913 return aOffset1 <= *child2Index ? -1 : 1;
2916 const nsINode* child1 = parents1.ElementAt(--pos1);
2917 const Maybe<uint32_t> child1Index =
2918 aParent1Cache ? aParent1Cache->ComputeIndexOf(parent, child1)
2919 : parent->ComputeIndexOf(child1);
2920 if (MOZ_UNLIKELY(NS_WARN_IF(child1Index.isNothing()))) {
2921 return -1;
2923 return *child1Index < aOffset2 ? -1 : 1;
2926 // static
2927 nsINode* nsContentUtils::GetCommonAncestorUnderInteractiveContent(
2928 nsINode* aNode1, nsINode* aNode2) {
2929 if (!aNode1 || !aNode2) {
2930 return nullptr;
2933 if (aNode1 == aNode2) {
2934 return aNode1;
2937 // Build the chain of parents
2938 AutoTArray<nsINode*, 30> parents1;
2939 do {
2940 parents1.AppendElement(aNode1);
2941 if (aNode1->IsElement() &&
2942 aNode1->AsElement()->IsInteractiveHTMLContent()) {
2943 break;
2945 aNode1 = aNode1->GetFlattenedTreeParentNode();
2946 } while (aNode1);
2948 AutoTArray<nsINode*, 30> parents2;
2949 do {
2950 parents2.AppendElement(aNode2);
2951 if (aNode2->IsElement() &&
2952 aNode2->AsElement()->IsInteractiveHTMLContent()) {
2953 break;
2955 aNode2 = aNode2->GetFlattenedTreeParentNode();
2956 } while (aNode2);
2958 // Find where the parent chain differs
2959 uint32_t pos1 = parents1.Length();
2960 uint32_t pos2 = parents2.Length();
2961 nsINode* parent = nullptr;
2962 for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
2963 nsINode* child1 = parents1.ElementAt(--pos1);
2964 nsINode* child2 = parents2.ElementAt(--pos2);
2965 if (child1 != child2) {
2966 break;
2968 parent = child1;
2971 return parent;
2974 /* static */
2975 BrowserParent* nsContentUtils::GetCommonBrowserParentAncestor(
2976 BrowserParent* aBrowserParent1, BrowserParent* aBrowserParent2) {
2977 return GetCommonAncestorInternal(
2978 aBrowserParent1, aBrowserParent2, [](BrowserParent* aBrowserParent) {
2979 return aBrowserParent->GetBrowserBridgeParent()
2980 ? aBrowserParent->GetBrowserBridgeParent()->Manager()
2981 : nullptr;
2985 /* static */
2986 template <typename FPT, typename FRT, typename SPT, typename SRT>
2987 Maybe<int32_t> nsContentUtils::ComparePoints(
2988 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
2989 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary) {
2990 if (!aFirstBoundary.IsSet() || !aSecondBoundary.IsSet()) {
2991 return Nothing{};
2994 bool disconnected{false};
2995 const int32_t order =
2996 ComparePoints_Deprecated(aFirstBoundary, aSecondBoundary, &disconnected);
2998 if (disconnected) {
2999 return Nothing{};
3002 return Some(order);
3005 /* static */
3006 template <typename FPT, typename FRT, typename SPT, typename SRT>
3007 int32_t nsContentUtils::ComparePoints_Deprecated(
3008 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
3009 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary, bool* aDisconnected) {
3010 if (NS_WARN_IF(!aFirstBoundary.IsSet()) ||
3011 NS_WARN_IF(!aSecondBoundary.IsSet())) {
3012 return -1;
3014 // XXX Re-implement this without calling `Offset()` as far as possible,
3015 // and the other overload should be an alias of this.
3016 return ComparePoints_Deprecated(
3017 aFirstBoundary.Container(),
3018 *aFirstBoundary.Offset(
3019 RangeBoundaryBase<FPT, FRT>::OffsetFilter::kValidOrInvalidOffsets),
3020 aSecondBoundary.Container(),
3021 *aSecondBoundary.Offset(
3022 RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOrInvalidOffsets),
3023 aDisconnected);
3026 inline bool IsCharInSet(const char* aSet, const char16_t aChar) {
3027 char16_t ch;
3028 while ((ch = *aSet)) {
3029 if (aChar == char16_t(ch)) {
3030 return true;
3032 ++aSet;
3034 return false;
3038 * This method strips leading/trailing chars, in given set, from string.
3041 // static
3042 const nsDependentSubstring nsContentUtils::TrimCharsInSet(
3043 const char* aSet, const nsAString& aValue) {
3044 nsAString::const_iterator valueCurrent, valueEnd;
3046 aValue.BeginReading(valueCurrent);
3047 aValue.EndReading(valueEnd);
3049 // Skip characters in the beginning
3050 while (valueCurrent != valueEnd) {
3051 if (!IsCharInSet(aSet, *valueCurrent)) {
3052 break;
3054 ++valueCurrent;
3057 if (valueCurrent != valueEnd) {
3058 for (;;) {
3059 --valueEnd;
3060 if (!IsCharInSet(aSet, *valueEnd)) {
3061 break;
3064 ++valueEnd; // Step beyond the last character we want in the value.
3067 // valueEnd should point to the char after the last to copy
3068 return Substring(valueCurrent, valueEnd);
3072 * This method strips leading and trailing whitespace from a string.
3075 // static
3076 template <bool IsWhitespace(char16_t)>
3077 const nsDependentSubstring nsContentUtils::TrimWhitespace(const nsAString& aStr,
3078 bool aTrimTrailing) {
3079 nsAString::const_iterator start, end;
3081 aStr.BeginReading(start);
3082 aStr.EndReading(end);
3084 // Skip whitespace characters in the beginning
3085 while (start != end && IsWhitespace(*start)) {
3086 ++start;
3089 if (aTrimTrailing) {
3090 // Skip whitespace characters in the end.
3091 while (end != start) {
3092 --end;
3094 if (!IsWhitespace(*end)) {
3095 // Step back to the last non-whitespace character.
3096 ++end;
3098 break;
3103 // Return a substring for the string w/o leading and/or trailing
3104 // whitespace
3106 return Substring(start, end);
3109 // Declaring the templates we are going to use avoid linking issues without
3110 // inlining the method. Considering there is not so much spaces checking
3111 // methods we can consider this to be better than inlining.
3112 template const nsDependentSubstring
3113 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, bool);
3114 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
3115 nsContentUtils::IsHTMLWhitespace>(const nsAString&, bool);
3116 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
3117 nsContentUtils::IsHTMLWhitespaceOrNBSP>(const nsAString&, bool);
3119 static inline void KeyAppendSep(nsACString& aKey) {
3120 if (!aKey.IsEmpty()) {
3121 aKey.Append('>');
3125 static inline void KeyAppendString(const nsAString& aString, nsACString& aKey) {
3126 KeyAppendSep(aKey);
3128 // Could escape separator here if collisions happen. > is not a legal char
3129 // for a name or type attribute, so we should be safe avoiding that extra
3130 // work.
3132 AppendUTF16toUTF8(aString, aKey);
3135 static inline void KeyAppendString(const nsACString& aString,
3136 nsACString& aKey) {
3137 KeyAppendSep(aKey);
3139 // Could escape separator here if collisions happen. > is not a legal char
3140 // for a name or type attribute, so we should be safe avoiding that extra
3141 // work.
3143 aKey.Append(aString);
3146 static inline void KeyAppendInt(int32_t aInt, nsACString& aKey) {
3147 KeyAppendSep(aKey);
3149 aKey.AppendInt(aInt);
3152 static inline bool IsAutocompleteOff(const nsIContent* aContent) {
3153 return aContent->IsElement() &&
3154 aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
3155 nsGkAtoms::autocomplete, u"off"_ns,
3156 eIgnoreCase);
3159 /*static*/
3160 void nsContentUtils::GenerateStateKey(nsIContent* aContent, Document* aDocument,
3161 nsACString& aKey) {
3162 MOZ_ASSERT(aContent);
3164 aKey.Truncate();
3166 uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
3168 // Don't capture state for anonymous content
3169 if (aContent->IsInNativeAnonymousSubtree()) {
3170 return;
3173 if (IsAutocompleteOff(aContent)) {
3174 return;
3177 RefPtr<Document> doc = aContent->GetUncomposedDoc();
3179 KeyAppendInt(partID, aKey); // first append a partID
3180 bool generatedUniqueKey = false;
3182 if (doc && doc->IsHTMLOrXHTML()) {
3183 nsHTMLDocument* htmlDoc = doc->AsHTMLDocument();
3185 // If we have a form control and can calculate form information, use that
3186 // as the key - it is more reliable than just recording position in the
3187 // DOM.
3188 // XXXbz Is it, really? We have bugs on this, I think...
3189 // Important to have a unique key, and tag/type/name may not be.
3191 // The format of the key depends on whether the control has a form,
3192 // and whether the element was parser inserted:
3194 // [Has Form, Parser Inserted]:
3195 // fp>type>FormNum>IndOfControlInForm>FormName>name
3197 // [No Form, Parser Inserted]:
3198 // dp>type>ControlNum>name
3200 // [Has Form, Not Parser Inserted]:
3201 // fn>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
3203 // [No Form, Not Parser Inserted]:
3204 // dn>type>IndOfControlInDoc>name
3206 // XXX We don't need to use index if name is there
3207 // XXXbz We don't? Why not? I don't follow.
3209 nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
3210 if (control) {
3211 // Get the control number if this was a parser inserted element from the
3212 // network.
3213 int32_t controlNumber =
3214 control->GetParserInsertedControlNumberForStateKey();
3215 bool parserInserted = controlNumber != -1;
3217 RefPtr<nsContentList> htmlForms;
3218 RefPtr<nsContentList> htmlFormControls;
3219 if (!parserInserted) {
3220 // Getting these lists is expensive, as we need to keep them up to date
3221 // as the document loads, so we avoid it if we don't need them.
3222 htmlDoc->GetFormsAndFormControls(getter_AddRefs(htmlForms),
3223 getter_AddRefs(htmlFormControls));
3226 // Append the control type
3227 KeyAppendInt(int32_t(control->ControlType()), aKey);
3229 // If in a form, add form name / index of form / index in form
3230 HTMLFormElement* formElement = control->GetForm();
3231 if (formElement) {
3232 if (IsAutocompleteOff(formElement)) {
3233 aKey.Truncate();
3234 return;
3237 // Append the form number, if this is a parser inserted control, or
3238 // the index of the form in the document otherwise.
3239 bool appendedForm = false;
3240 if (parserInserted) {
3241 MOZ_ASSERT(formElement->GetFormNumberForStateKey() != -1,
3242 "when generating a state key for a parser inserted form "
3243 "control we should have a parser inserted <form> element");
3244 KeyAppendString("fp"_ns, aKey);
3245 KeyAppendInt(formElement->GetFormNumberForStateKey(), aKey);
3246 appendedForm = true;
3247 } else {
3248 KeyAppendString("fn"_ns, aKey);
3249 int32_t index = htmlForms->IndexOf(formElement, false);
3250 if (index <= -1) {
3252 // XXX HACK this uses some state that was dumped into the document
3253 // specifically to fix bug 138892. What we are trying to do is
3254 // *guess* which form this control's state is found in, with the
3255 // highly likely guess that the highest form parsed so far is the
3256 // one. This code should not be on trunk, only branch.
3258 index = htmlDoc->GetNumFormsSynchronous() - 1;
3260 if (index > -1) {
3261 KeyAppendInt(index, aKey);
3262 appendedForm = true;
3266 if (appendedForm) {
3267 // Append the index of the control in the form
3268 int32_t index = formElement->IndexOfContent(aContent);
3270 if (index > -1) {
3271 KeyAppendInt(index, aKey);
3272 generatedUniqueKey = true;
3276 // Append the form name
3277 nsAutoString formName;
3278 formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
3279 KeyAppendString(formName, aKey);
3280 } else {
3281 // Not in a form. Append the control number, if this is a parser
3282 // inserted control, or the index of the control in the document
3283 // otherwise.
3284 if (parserInserted) {
3285 KeyAppendString("dp"_ns, aKey);
3286 KeyAppendInt(control->GetParserInsertedControlNumberForStateKey(),
3287 aKey);
3288 generatedUniqueKey = true;
3289 } else {
3290 KeyAppendString("dn"_ns, aKey);
3291 int32_t index = htmlFormControls->IndexOf(aContent, true);
3292 if (index > -1) {
3293 KeyAppendInt(index, aKey);
3294 generatedUniqueKey = true;
3298 // Append the control name
3299 nsAutoString name;
3300 aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name,
3301 name);
3302 KeyAppendString(name, aKey);
3307 if (!generatedUniqueKey) {
3308 // Either we didn't have a form control or we aren't in an HTML document so
3309 // we can't figure out form info. Append the tag name if it's an element
3310 // to avoid restoring state for one type of element on another type.
3311 if (aContent->IsElement()) {
3312 KeyAppendString(nsDependentAtomString(aContent->NodeInfo()->NameAtom()),
3313 aKey);
3314 } else {
3315 // Append a character that is not "d" or "f" to disambiguate from
3316 // the case when we were a form control in an HTML document.
3317 KeyAppendString("o"_ns, aKey);
3320 // Now start at aContent and append the indices of it and all its ancestors
3321 // in their containers. That should at least pin down its position in the
3322 // DOM...
3323 nsINode* parent = aContent->GetParentNode();
3324 nsINode* content = aContent;
3325 while (parent) {
3326 KeyAppendInt(parent->ComputeIndexOf_Deprecated(content), aKey);
3327 content = parent;
3328 parent = content->GetParentNode();
3333 // static
3334 nsIPrincipal* nsContentUtils::SubjectPrincipal(JSContext* aCx) {
3335 MOZ_ASSERT(NS_IsMainThread());
3337 // As opposed to SubjectPrincipal(), we do in fact assume that
3338 // we're in a realm here; anyone who calls this function in
3339 // situations where that's not the case is doing it wrong.
3340 JS::Realm* realm = js::GetContextRealm(aCx);
3341 MOZ_ASSERT(realm);
3343 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3344 return nsJSPrincipals::get(principals);
3347 // static
3348 nsIPrincipal* nsContentUtils::SubjectPrincipal() {
3349 MOZ_ASSERT(IsInitialized());
3350 MOZ_ASSERT(NS_IsMainThread());
3351 JSContext* cx = GetCurrentJSContext();
3352 if (!cx) {
3353 MOZ_CRASH(
3354 "Accessing the Subject Principal without an AutoJSAPI on the stack is "
3355 "forbidden");
3358 JS::Realm* realm = js::GetContextRealm(cx);
3360 // When an AutoJSAPI is instantiated, we are in a null realm until the
3361 // first JSAutoRealm, which is kind of a purgatory as far as permissions
3362 // go. It would be nice to just hard-abort if somebody does a security check
3363 // in this purgatory zone, but that would be too fragile, since it could be
3364 // triggered by random IsCallerChrome() checks 20-levels deep.
3366 // So we want to return _something_ here - and definitely not the System
3367 // Principal, since that would make an AutoJSAPI a very dangerous thing to
3368 // instantiate.
3370 // The natural thing to return is a null principal. Ideally, we'd return a
3371 // different null principal each time, to avoid any unexpected interactions
3372 // when the principal accidentally gets inherited somewhere. But
3373 // SubjectPrincipal doesn't return strong references, so there's no way to
3374 // sanely manage the lifetime of multiple null principals.
3376 // So we use a singleton null principal. To avoid it being accidentally
3377 // inherited and becoming a "real" subject or object principal, we do a
3378 // release-mode assert during realm creation against using this principal on
3379 // an actual global.
3380 if (!realm) {
3381 return sNullSubjectPrincipal;
3384 return SubjectPrincipal(cx);
3387 // static
3388 nsIPrincipal* nsContentUtils::ObjectPrincipal(JSObject* aObj) {
3389 #ifdef DEBUG
3390 JS::AssertObjectBelongsToCurrentThread(aObj);
3391 #endif
3393 MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(aObj));
3395 JS::Realm* realm = js::GetNonCCWObjectRealm(aObj);
3396 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3397 return nsJSPrincipals::get(principals);
3400 // static
3401 nsresult nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
3402 const nsAString& aSpec,
3403 Document* aDocument,
3404 nsIURI* aBaseURI) {
3405 if (aDocument) {
3406 return NS_NewURI(aResult, aSpec, aDocument->GetDocumentCharacterSet(),
3407 aBaseURI);
3409 return NS_NewURI(aResult, aSpec, nullptr, aBaseURI);
3412 // static
3413 bool nsContentUtils::IsNameWithDash(nsAtom* aName) {
3414 // A valid custom element name is a sequence of characters name which
3415 // must match the PotentialCustomElementName production:
3416 // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
3417 const char16_t* name = aName->GetUTF16String();
3418 uint32_t len = aName->GetLength();
3419 bool hasDash = false;
3421 if (!len || name[0] < 'a' || name[0] > 'z') {
3422 return false;
3425 uint32_t i = 1;
3426 while (i < len) {
3427 if (i + 1 < len && NS_IS_SURROGATE_PAIR(name[i], name[i + 1])) {
3428 // Merged two 16-bit surrogate pairs into code point.
3429 char32_t code = SURROGATE_TO_UCS4(name[i], name[i + 1]);
3431 if (code < 0x10000 || code > 0xEFFFF) {
3432 return false;
3435 i += 2;
3436 } else {
3437 if (name[i] == '-') {
3438 hasDash = true;
3441 if (name[i] != '-' && name[i] != '.' && name[i] != '_' &&
3442 name[i] != 0xB7 && (name[i] < '0' || name[i] > '9') &&
3443 (name[i] < 'a' || name[i] > 'z') &&
3444 (name[i] < 0xC0 || name[i] > 0xD6) &&
3445 (name[i] < 0xF8 || name[i] > 0x37D) &&
3446 (name[i] < 0x37F || name[i] > 0x1FFF) &&
3447 (name[i] < 0x200C || name[i] > 0x200D) &&
3448 (name[i] < 0x203F || name[i] > 0x2040) &&
3449 (name[i] < 0x2070 || name[i] > 0x218F) &&
3450 (name[i] < 0x2C00 || name[i] > 0x2FEF) &&
3451 (name[i] < 0x3001 || name[i] > 0xD7FF) &&
3452 (name[i] < 0xF900 || name[i] > 0xFDCF) &&
3453 (name[i] < 0xFDF0 || name[i] > 0xFFFD)) {
3454 return false;
3457 i++;
3461 return hasDash;
3464 // static
3465 bool nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID) {
3466 // Allow non-dashed names in XUL for XBL to Custom Element migrations.
3467 if (aNameSpaceID == kNameSpaceID_XUL) {
3468 return true;
3471 bool hasDash = IsNameWithDash(aName);
3472 if (!hasDash) {
3473 return false;
3476 // The custom element name must not be one of the following values:
3477 // annotation-xml
3478 // color-profile
3479 // font-face
3480 // font-face-src
3481 // font-face-uri
3482 // font-face-format
3483 // font-face-name
3484 // missing-glyph
3485 return aName != nsGkAtoms::annotation_xml_ &&
3486 aName != nsGkAtoms::colorProfile && aName != nsGkAtoms::font_face &&
3487 aName != nsGkAtoms::font_face_src &&
3488 aName != nsGkAtoms::font_face_uri &&
3489 aName != nsGkAtoms::font_face_format &&
3490 aName != nsGkAtoms::font_face_name && aName != nsGkAtoms::missingGlyph;
3493 // static
3494 nsresult nsContentUtils::CheckQName(const nsAString& aQualifiedName,
3495 bool aNamespaceAware,
3496 const char16_t** aColon) {
3497 const char* colon = nullptr;
3498 const char16_t* begin = aQualifiedName.BeginReading();
3499 const char16_t* end = aQualifiedName.EndReading();
3501 int result = MOZ_XMLCheckQName(reinterpret_cast<const char*>(begin),
3502 reinterpret_cast<const char*>(end),
3503 aNamespaceAware, &colon);
3505 if (!result) {
3506 if (aColon) {
3507 *aColon = reinterpret_cast<const char16_t*>(colon);
3510 return NS_OK;
3513 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
3516 // static
3517 nsresult nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
3518 const nsString& aQName, int32_t* aNamespace,
3519 nsAtom** aLocalName) {
3520 const char16_t* colon;
3521 nsresult rv = nsContentUtils::CheckQName(aQName, true, &colon);
3522 NS_ENSURE_SUCCESS(rv, rv);
3524 if (colon) {
3525 const char16_t* end;
3526 aQName.EndReading(end);
3527 nsAutoString nameSpace;
3528 rv = aNamespaceResolver->LookupNamespaceURIInternal(
3529 Substring(aQName.get(), colon), nameSpace);
3530 NS_ENSURE_SUCCESS(rv, rv);
3532 *aNamespace = nsNameSpaceManager::GetInstance()->GetNameSpaceID(
3533 nameSpace, nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
3534 if (*aNamespace == kNameSpaceID_Unknown) return NS_ERROR_FAILURE;
3536 *aLocalName = NS_AtomizeMainThread(Substring(colon + 1, end)).take();
3537 } else {
3538 *aNamespace = kNameSpaceID_None;
3539 *aLocalName = NS_AtomizeMainThread(aQName).take();
3541 NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
3542 return NS_OK;
3545 // static
3546 nsresult nsContentUtils::GetNodeInfoFromQName(
3547 const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
3548 nsNodeInfoManager* aNodeInfoManager, uint16_t aNodeType,
3549 mozilla::dom::NodeInfo** aNodeInfo) {
3550 const nsString& qName = PromiseFlatString(aQualifiedName);
3551 const char16_t* colon;
3552 nsresult rv = nsContentUtils::CheckQName(qName, true, &colon);
3553 NS_ENSURE_SUCCESS(rv, rv);
3555 int32_t nsID;
3556 nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, nsID);
3557 if (colon) {
3558 const char16_t* end;
3559 qName.EndReading(end);
3561 RefPtr<nsAtom> prefix = NS_AtomizeMainThread(Substring(qName.get(), colon));
3563 rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix, nsID,
3564 aNodeType, aNodeInfo);
3565 } else {
3566 rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID, aNodeType,
3567 aNodeInfo);
3569 NS_ENSURE_SUCCESS(rv, rv);
3571 return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
3572 (*aNodeInfo)->GetPrefixAtom(),
3573 (*aNodeInfo)->NamespaceID())
3574 ? NS_OK
3575 : NS_ERROR_DOM_NAMESPACE_ERR;
3578 // static
3579 void nsContentUtils::SplitExpatName(const char16_t* aExpatName,
3580 nsAtom** aPrefix, nsAtom** aLocalName,
3581 int32_t* aNameSpaceID) {
3583 * Expat can send the following:
3584 * localName
3585 * namespaceURI<separator>localName
3586 * namespaceURI<separator>localName<separator>prefix
3588 * and we use 0xFFFF for the <separator>.
3592 const char16_t* uriEnd = nullptr;
3593 const char16_t* nameEnd = nullptr;
3594 const char16_t* pos;
3595 for (pos = aExpatName; *pos; ++pos) {
3596 if (*pos == 0xFFFF) {
3597 if (uriEnd) {
3598 nameEnd = pos;
3599 } else {
3600 uriEnd = pos;
3605 const char16_t* nameStart;
3606 if (uriEnd) {
3607 nsNameSpaceManager::GetInstance()->RegisterNameSpace(
3608 nsDependentSubstring(aExpatName, uriEnd), *aNameSpaceID);
3610 nameStart = (uriEnd + 1);
3611 if (nameEnd) {
3612 const char16_t* prefixStart = nameEnd + 1;
3613 *aPrefix = NS_AtomizeMainThread(Substring(prefixStart, pos)).take();
3614 } else {
3615 nameEnd = pos;
3616 *aPrefix = nullptr;
3618 } else {
3619 *aNameSpaceID = kNameSpaceID_None;
3620 nameStart = aExpatName;
3621 nameEnd = pos;
3622 *aPrefix = nullptr;
3624 *aLocalName = NS_AtomizeMainThread(Substring(nameStart, nameEnd)).take();
3627 // static
3628 PresShell* nsContentUtils::GetPresShellForContent(const nsIContent* aContent) {
3629 Document* doc = aContent->GetComposedDoc();
3630 if (!doc) {
3631 return nullptr;
3633 return doc->GetPresShell();
3636 // static
3637 nsPresContext* nsContentUtils::GetContextForContent(
3638 const nsIContent* aContent) {
3639 PresShell* presShell = GetPresShellForContent(aContent);
3640 if (!presShell) {
3641 return nullptr;
3643 return presShell->GetPresContext();
3646 // static
3647 bool nsContentUtils::CanLoadImage(nsIURI* aURI, nsINode* aNode,
3648 Document* aLoadingDocument,
3649 nsIPrincipal* aLoadingPrincipal) {
3650 MOZ_ASSERT(aURI, "Must have a URI");
3651 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3652 MOZ_ASSERT(aLoadingPrincipal, "Must have a loading principal");
3654 nsresult rv;
3656 auto appType = nsIDocShell::APP_TYPE_UNKNOWN;
3659 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
3660 aLoadingDocument->GetDocShell();
3661 if (docShellTreeItem) {
3662 nsCOMPtr<nsIDocShellTreeItem> root;
3663 docShellTreeItem->GetInProcessRootTreeItem(getter_AddRefs(root));
3665 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
3667 if (docShell) {
3668 appType = docShell->GetAppType();
3673 if (appType != nsIDocShell::APP_TYPE_EDITOR) {
3674 // Editor apps get special treatment here, editors can load images
3675 // from anywhere. This allows editor to insert images from file://
3676 // into documents that are being edited.
3677 rv = sSecurityManager->CheckLoadURIWithPrincipal(
3678 aLoadingPrincipal, aURI, nsIScriptSecurityManager::ALLOW_CHROME,
3679 aLoadingDocument->InnerWindowID());
3680 if (NS_FAILED(rv)) {
3681 return false;
3685 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new mozilla::net::LoadInfo(
3686 aLoadingPrincipal,
3687 aLoadingPrincipal, // triggering principal
3688 aNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
3689 nsIContentPolicy::TYPE_INTERNAL_IMAGE);
3691 int16_t decision = nsIContentPolicy::ACCEPT;
3693 rv = NS_CheckContentLoadPolicy(aURI, secCheckLoadInfo,
3694 ""_ns, // mime guess
3695 &decision, GetContentPolicy());
3697 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(decision);
3700 // static
3701 bool nsContentUtils::IsInPrivateBrowsing(Document* aDoc) {
3702 if (!aDoc) {
3703 return false;
3706 nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
3707 if (loadGroup) {
3708 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3709 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3710 if (callbacks) {
3711 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3712 if (loadContext) {
3713 return loadContext->UsePrivateBrowsing();
3718 nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
3719 return channel && NS_UsePrivateBrowsing(channel);
3722 // static
3723 bool nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup) {
3724 if (!aLoadGroup) {
3725 return false;
3727 bool isPrivate = false;
3728 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3729 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3730 if (callbacks) {
3731 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3732 isPrivate = loadContext && loadContext->UsePrivateBrowsing();
3734 return isPrivate;
3737 // FIXME(emilio): This is (effectively) almost but not quite the same as
3738 // Document::ShouldLoadImages(), which one is right?
3739 bool nsContentUtils::DocumentInactiveForImageLoads(Document* aDocument) {
3740 if (!aDocument) {
3741 return false;
3743 if (IsChromeDoc(aDocument) || aDocument->IsResourceDoc() ||
3744 aDocument->IsStaticDocument()) {
3745 return false;
3747 nsCOMPtr<nsPIDOMWindowInner> win =
3748 do_QueryInterface(aDocument->GetScopeObject());
3749 return !win || !win->GetDocShell();
3752 imgLoader* nsContentUtils::GetImgLoaderForDocument(Document* aDoc) {
3753 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
3755 if (!aDoc) {
3756 return imgLoader::NormalLoader();
3758 bool isPrivate = IsInPrivateBrowsing(aDoc);
3759 return isPrivate ? imgLoader::PrivateBrowsingLoader()
3760 : imgLoader::NormalLoader();
3763 // static
3764 imgLoader* nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
3765 Document* aContext) {
3766 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
3768 if (!aChannel) {
3769 return imgLoader::NormalLoader();
3771 nsCOMPtr<nsILoadContext> context;
3772 NS_QueryNotificationCallbacks(aChannel, context);
3773 return context && context->UsePrivateBrowsing()
3774 ? imgLoader::PrivateBrowsingLoader()
3775 : imgLoader::NormalLoader();
3778 // static
3779 bool nsContentUtils::IsImageInCache(nsIURI* aURI, Document* aDocument) {
3780 imgILoader* loader = GetImgLoaderForDocument(aDocument);
3781 nsCOMPtr<imgICache> cache = do_QueryInterface(loader);
3783 // If something unexpected happened we return false, otherwise if props
3784 // is set, the image is cached and we return true
3785 nsCOMPtr<nsIProperties> props;
3786 nsresult rv =
3787 cache->FindEntryProperties(aURI, aDocument, getter_AddRefs(props));
3788 return (NS_SUCCEEDED(rv) && props);
3791 // static
3792 int32_t nsContentUtils::CORSModeToLoadImageFlags(mozilla::CORSMode aMode) {
3793 switch (aMode) {
3794 case CORS_ANONYMOUS:
3795 return imgILoader::LOAD_CORS_ANONYMOUS;
3796 case CORS_USE_CREDENTIALS:
3797 return imgILoader::LOAD_CORS_USE_CREDENTIALS;
3798 default:
3799 return 0;
3803 // static
3804 nsresult nsContentUtils::LoadImage(
3805 nsIURI* aURI, nsINode* aContext, Document* aLoadingDocument,
3806 nsIPrincipal* aLoadingPrincipal, uint64_t aRequestContextID,
3807 nsIReferrerInfo* aReferrerInfo, imgINotificationObserver* aObserver,
3808 int32_t aLoadFlags, const nsAString& initiatorType,
3809 imgRequestProxy** aRequest, nsContentPolicyType aContentPolicyType,
3810 bool aUseUrgentStartForChannel, bool aLinkPreload,
3811 uint64_t aEarlyHintPreloaderId) {
3812 MOZ_ASSERT(aURI, "Must have a URI");
3813 MOZ_ASSERT(aContext, "Must have a context");
3814 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3815 MOZ_ASSERT(aLoadingPrincipal, "Must have a principal");
3816 MOZ_ASSERT(aRequest, "Null out param");
3818 imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
3819 if (!imgLoader) {
3820 // nothing we can do here
3821 return NS_ERROR_FAILURE;
3824 nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
3826 nsIURI* documentURI = aLoadingDocument->GetDocumentURI();
3828 NS_ASSERTION(loadGroup || aLoadingDocument->IsSVGGlyphsDocument(),
3829 "Could not get loadgroup; onload may fire too early");
3831 // XXXbz using "documentURI" for the initialDocumentURI is not quite
3832 // right, but the best we can do here...
3833 return imgLoader->LoadImage(aURI, /* uri to load */
3834 documentURI, /* initialDocumentURI */
3835 aReferrerInfo, /* referrerInfo */
3836 aLoadingPrincipal, /* loading principal */
3837 aRequestContextID, /* request context ID */
3838 loadGroup, /* loadgroup */
3839 aObserver, /* imgINotificationObserver */
3840 aContext, /* loading context */
3841 aLoadingDocument, /* uniquification key */
3842 aLoadFlags, /* load flags */
3843 nullptr, /* cache key */
3844 aContentPolicyType, /* content policy type */
3845 initiatorType, /* the load initiator */
3846 aUseUrgentStartForChannel, /* urgent-start flag */
3847 aLinkPreload, /* <link preload> initiator */
3848 aEarlyHintPreloaderId, aRequest);
3851 // static
3852 already_AddRefed<imgIContainer> nsContentUtils::GetImageFromContent(
3853 nsIImageLoadingContent* aContent, imgIRequest** aRequest) {
3854 if (aRequest) {
3855 *aRequest = nullptr;
3858 NS_ENSURE_TRUE(aContent, nullptr);
3860 nsCOMPtr<imgIRequest> imgRequest;
3861 aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3862 getter_AddRefs(imgRequest));
3863 if (!imgRequest) {
3864 return nullptr;
3867 nsCOMPtr<imgIContainer> imgContainer;
3868 imgRequest->GetImage(getter_AddRefs(imgContainer));
3870 if (!imgContainer) {
3871 return nullptr;
3874 if (aRequest) {
3875 // If the consumer wants the request, verify it has actually loaded
3876 // successfully.
3877 uint32_t imgStatus;
3878 imgRequest->GetImageStatus(&imgStatus);
3879 if (imgStatus & imgIRequest::STATUS_FRAME_COMPLETE &&
3880 !(imgStatus & imgIRequest::STATUS_ERROR)) {
3881 imgRequest.swap(*aRequest);
3885 return imgContainer.forget();
3888 static bool IsLinkWithURI(const nsIContent& aContent) {
3889 const auto* element = Element::FromNode(aContent);
3890 if (!element || !element->IsLink()) {
3891 return false;
3893 nsCOMPtr<nsIURI> absURI = element->GetHrefURI();
3894 return !!absURI;
3897 static bool HasImageRequest(nsIContent& aContent) {
3898 nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(&aContent));
3899 if (!imageContent) {
3900 return false;
3903 nsCOMPtr<imgIRequest> imgRequest;
3904 imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3905 getter_AddRefs(imgRequest));
3907 // XXXbz It may be draggable even if the request resulted in an error. Why?
3908 // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
3909 return !!imgRequest;
3912 static Maybe<bool> DraggableOverride(const nsIContent& aContent) {
3913 if (auto* el = nsGenericHTMLElement::FromNode(aContent)) {
3914 if (el->Draggable()) {
3915 return Some(true);
3918 if (el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
3919 nsGkAtoms::_false, eIgnoreCase)) {
3920 return Some(false);
3923 if (aContent.IsSVGElement()) {
3924 return Some(false);
3926 return Nothing();
3929 // static
3930 bool nsContentUtils::ContentIsDraggable(nsIContent* aContent) {
3931 MOZ_ASSERT(aContent);
3933 if (auto draggable = DraggableOverride(*aContent)) {
3934 return *draggable;
3937 // special handling for content area image and link dragging
3938 return HasImageRequest(*aContent) || IsLinkWithURI(*aContent);
3941 // static
3942 bool nsContentUtils::IsDraggableImage(nsIContent* aContent) {
3943 MOZ_ASSERT(aContent);
3944 return HasImageRequest(*aContent) &&
3945 DraggableOverride(*aContent).valueOr(true);
3948 // static
3949 bool nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
3950 MOZ_ASSERT(aContent);
3951 return IsLinkWithURI(*aContent) && DraggableOverride(*aContent).valueOr(true);
3954 // static
3955 nsresult nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo,
3956 nsAtom* aName,
3957 mozilla::dom::NodeInfo** aResult) {
3958 nsNodeInfoManager* niMgr = aNodeInfo->NodeInfoManager();
3960 *aResult = niMgr
3961 ->GetNodeInfo(aName, nullptr, aNodeInfo->NamespaceID(),
3962 aNodeInfo->NodeType(), aNodeInfo->GetExtraName())
3963 .take();
3964 return NS_OK;
3967 static bool TestSitePerm(nsIPrincipal* aPrincipal, const nsACString& aType,
3968 uint32_t aPerm, bool aExactHostMatch) {
3969 if (!aPrincipal) {
3970 // We always deny (i.e. don't allow) the permission if we don't have a
3971 // principal.
3972 return aPerm != nsIPermissionManager::ALLOW_ACTION;
3975 nsCOMPtr<nsIPermissionManager> permMgr =
3976 components::PermissionManager::Service();
3977 NS_ENSURE_TRUE(permMgr, false);
3979 uint32_t perm;
3980 nsresult rv;
3981 if (aExactHostMatch) {
3982 rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
3983 } else {
3984 rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
3986 NS_ENSURE_SUCCESS(rv, false);
3988 return perm == aPerm;
3991 bool nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal,
3992 const nsACString& aType) {
3993 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
3994 false);
3997 bool nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal,
3998 const nsACString& aType) {
3999 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
4000 false);
4003 bool nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal,
4004 const nsACString& aType) {
4005 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
4006 true);
4009 bool nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal,
4010 const nsACString& aType) {
4011 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
4012 true);
4015 bool nsContentUtils::HasSitePerm(nsIPrincipal* aPrincipal,
4016 const nsACString& aType) {
4017 if (!aPrincipal) {
4018 return false;
4021 nsCOMPtr<nsIPermissionManager> permMgr =
4022 components::PermissionManager::Service();
4023 NS_ENSURE_TRUE(permMgr, false);
4025 uint32_t perm;
4026 nsresult rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
4027 NS_ENSURE_SUCCESS(rv, false);
4029 return perm != nsIPermissionManager::UNKNOWN_ACTION;
4032 static const char* gEventNames[] = {"event"};
4033 static const char* gSVGEventNames[] = {"evt"};
4034 // for b/w compat, the first name to onerror is still 'event', even though it
4035 // is actually the error message
4036 static const char* gOnErrorNames[] = {"event", "source", "lineno", "colno",
4037 "error"};
4039 // static
4040 void nsContentUtils::GetEventArgNames(int32_t aNameSpaceID, nsAtom* aEventName,
4041 bool aIsForWindow, uint32_t* aArgCount,
4042 const char*** aArgArray) {
4043 #define SET_EVENT_ARG_NAMES(names) \
4044 *aArgCount = sizeof(names) / sizeof(names[0]); \
4045 *aArgArray = names;
4047 // JSEventHandler is what does the arg magic for onerror, and it does
4048 // not seem to take the namespace into account. So we let onerror in all
4049 // namespaces get the 3 arg names.
4050 if (aEventName == nsGkAtoms::onerror && aIsForWindow) {
4051 SET_EVENT_ARG_NAMES(gOnErrorNames);
4052 } else if (aNameSpaceID == kNameSpaceID_SVG) {
4053 SET_EVENT_ARG_NAMES(gSVGEventNames);
4054 } else {
4055 SET_EVENT_ARG_NAMES(gEventNames);
4059 // Note: The list of content bundles in nsStringBundle.cpp should be updated
4060 // whenever entries are added or removed from this list.
4061 static const char* gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT] = {
4062 // Must line up with the enum values in |PropertiesFile| enum.
4063 "chrome://global/locale/css.properties",
4064 "chrome://global/locale/xul.properties",
4065 "chrome://global/locale/layout_errors.properties",
4066 "chrome://global/locale/layout/HtmlForm.properties",
4067 "chrome://global/locale/printing.properties",
4068 "chrome://global/locale/dom/dom.properties",
4069 "chrome://global/locale/layout/htmlparser.properties",
4070 "chrome://global/locale/svg/svg.properties",
4071 "chrome://branding/locale/brand.properties",
4072 "chrome://global/locale/commonDialogs.properties",
4073 "chrome://global/locale/mathml/mathml.properties",
4074 "chrome://global/locale/security/security.properties",
4075 "chrome://necko/locale/necko.properties",
4076 "resource://gre/res/locale/layout/HtmlForm.properties",
4077 "resource://gre/res/locale/dom/dom.properties"};
4079 /* static */
4080 nsresult nsContentUtils::EnsureStringBundle(PropertiesFile aFile) {
4081 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(),
4082 "Should not create bundles off main thread.");
4083 if (!sStringBundles[aFile]) {
4084 if (!sStringBundleService) {
4085 nsresult rv =
4086 CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
4087 NS_ENSURE_SUCCESS(rv, rv);
4089 RefPtr<nsIStringBundle> bundle;
4090 MOZ_TRY(sStringBundleService->CreateBundle(gPropertiesFiles[aFile],
4091 getter_AddRefs(bundle)));
4092 sStringBundles[aFile] = bundle.forget();
4094 return NS_OK;
4097 /* static */
4098 void nsContentUtils::AsyncPrecreateStringBundles() {
4099 // We only ever want to pre-create bundles in the parent process.
4101 // All nsContentUtils bundles are shared between the parent and child
4102 // precesses, and the shared memory regions that back them *must* be created
4103 // in the parent, and then sent to all children.
4105 // If we attempt to create a bundle in the child before its memory region is
4106 // available, we need to create a temporary non-shared bundle, and later
4107 // replace that with the shared memory copy. So attempting to pre-load in the
4108 // child is wasteful and unnecessary.
4109 MOZ_ASSERT(XRE_IsParentProcess());
4111 for (uint32_t bundleIndex = 0; bundleIndex < PropertiesFile_COUNT;
4112 ++bundleIndex) {
4113 nsresult rv = NS_DispatchToCurrentThreadQueue(
4114 NS_NewRunnableFunction("AsyncPrecreateStringBundles",
4115 [bundleIndex]() {
4116 PropertiesFile file =
4117 static_cast<PropertiesFile>(bundleIndex);
4118 EnsureStringBundle(file);
4119 nsIStringBundle* bundle = sStringBundles[file];
4120 bundle->AsyncPreload();
4122 EventQueuePriority::Idle);
4123 Unused << NS_WARN_IF(NS_FAILED(rv));
4127 /* static */
4128 bool nsContentUtils::SpoofLocaleEnglish() {
4129 // 0 - will prompt
4130 // 1 - don't spoof
4131 // 2 - spoof
4132 return StaticPrefs::privacy_spoof_english() == 2;
4135 static nsContentUtils::PropertiesFile GetMaybeSpoofedPropertiesFile(
4136 nsContentUtils::PropertiesFile aFile, const char* aKey,
4137 Document* aDocument) {
4138 // When we spoof English, use en-US properties in strings that are accessible
4139 // by content.
4140 bool spoofLocale = nsContentUtils::SpoofLocaleEnglish() &&
4141 (!aDocument || !aDocument->AllowsL10n());
4142 if (spoofLocale) {
4143 switch (aFile) {
4144 case nsContentUtils::eFORMS_PROPERTIES:
4145 return nsContentUtils::eFORMS_PROPERTIES_en_US;
4146 case nsContentUtils::eDOM_PROPERTIES:
4147 return nsContentUtils::eDOM_PROPERTIES_en_US;
4148 default:
4149 break;
4152 return aFile;
4155 /* static */
4156 nsresult nsContentUtils::GetMaybeLocalizedString(PropertiesFile aFile,
4157 const char* aKey,
4158 Document* aDocument,
4159 nsAString& aResult) {
4160 return GetLocalizedString(
4161 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aResult);
4164 /* static */
4165 nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
4166 const char* aKey,
4167 nsAString& aResult) {
4168 return FormatLocalizedString(aFile, aKey, {}, aResult);
4171 /* static */
4172 nsresult nsContentUtils::FormatMaybeLocalizedString(
4173 PropertiesFile aFile, const char* aKey, Document* aDocument,
4174 const nsTArray<nsString>& aParams, nsAString& aResult) {
4175 return FormatLocalizedString(
4176 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aParams,
4177 aResult);
4180 class FormatLocalizedStringRunnable final : public WorkerMainThreadRunnable {
4181 public:
4182 FormatLocalizedStringRunnable(WorkerPrivate* aWorkerPrivate,
4183 nsContentUtils::PropertiesFile aFile,
4184 const char* aKey,
4185 const nsTArray<nsString>& aParams,
4186 nsAString& aLocalizedString)
4187 : WorkerMainThreadRunnable(aWorkerPrivate,
4188 "FormatLocalizedStringRunnable"_ns),
4189 mFile(aFile),
4190 mKey(aKey),
4191 mParams(aParams),
4192 mLocalizedString(aLocalizedString) {
4193 MOZ_ASSERT(aWorkerPrivate);
4194 aWorkerPrivate->AssertIsOnWorkerThread();
4197 bool MainThreadRun() override {
4198 AssertIsOnMainThread();
4200 mResult = nsContentUtils::FormatLocalizedString(mFile, mKey, mParams,
4201 mLocalizedString);
4202 Unused << NS_WARN_IF(NS_FAILED(mResult));
4203 return true;
4206 nsresult GetResult() const { return mResult; }
4208 private:
4209 const nsContentUtils::PropertiesFile mFile;
4210 const char* mKey;
4211 const nsTArray<nsString>& mParams;
4212 nsresult mResult = NS_ERROR_FAILURE;
4213 nsAString& mLocalizedString;
4216 /* static */
4217 nsresult nsContentUtils::FormatLocalizedString(
4218 PropertiesFile aFile, const char* aKey, const nsTArray<nsString>& aParams,
4219 nsAString& aResult) {
4220 if (!NS_IsMainThread()) {
4221 // nsIStringBundle is thread-safe but its creation is not, and in particular
4222 // we don't create and store nsIStringBundle objects in a thread-safe way.
4224 // TODO(emilio): Maybe if we already have the right bundle created we could
4225 // just call into it, but we should make sure that Shutdown() doesn't get
4226 // called on the main thread when that happens which is a bit tricky to
4227 // prove?
4228 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
4229 if (NS_WARN_IF(!workerPrivate)) {
4230 return NS_ERROR_UNEXPECTED;
4233 auto runnable = MakeRefPtr<FormatLocalizedStringRunnable>(
4234 workerPrivate, aFile, aKey, aParams, aResult);
4236 runnable->Dispatch(Canceling, IgnoreErrors());
4237 return runnable->GetResult();
4240 MOZ_TRY(EnsureStringBundle(aFile));
4241 nsIStringBundle* bundle = sStringBundles[aFile];
4242 if (aParams.IsEmpty()) {
4243 return bundle->GetStringFromName(aKey, aResult);
4245 return bundle->FormatStringFromName(aKey, aParams, aResult);
4248 /* static */
4249 void nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
4250 const nsACString& aCategory,
4251 bool aFromPrivateWindow,
4252 bool aFromChromeContext,
4253 uint32_t aErrorFlags) {
4254 nsCOMPtr<nsIScriptError> scriptError =
4255 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
4256 if (scriptError) {
4257 nsCOMPtr<nsIConsoleService> console =
4258 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
4259 if (console && NS_SUCCEEDED(scriptError->Init(
4260 aErrorText, u""_ns, u""_ns, 0, 0, aErrorFlags, aCategory,
4261 aFromPrivateWindow, aFromChromeContext))) {
4262 console->LogMessage(scriptError);
4267 /* static */
4268 nsresult nsContentUtils::ReportToConsole(
4269 uint32_t aErrorFlags, const nsACString& aCategory,
4270 const Document* aDocument, PropertiesFile aFile, const char* aMessageName,
4271 const nsTArray<nsString>& aParams, nsIURI* aURI,
4272 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber) {
4273 nsresult rv;
4274 nsAutoString errorText;
4275 if (!aParams.IsEmpty()) {
4276 rv = FormatLocalizedString(aFile, aMessageName, aParams, errorText);
4277 } else {
4278 rv = GetLocalizedString(aFile, aMessageName, errorText);
4280 NS_ENSURE_SUCCESS(rv, rv);
4282 return ReportToConsoleNonLocalized(errorText, aErrorFlags, aCategory,
4283 aDocument, aURI, aSourceLine, aLineNumber,
4284 aColumnNumber);
4287 /* static */
4288 void nsContentUtils::ReportEmptyGetElementByIdArg(const Document* aDoc) {
4289 ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, aDoc,
4290 nsContentUtils::eDOM_PROPERTIES, "EmptyGetElementByIdParam");
4293 /* static */
4294 nsresult nsContentUtils::ReportToConsoleNonLocalized(
4295 const nsAString& aErrorText, uint32_t aErrorFlags,
4296 const nsACString& aCategory, const Document* aDocument, nsIURI* aURI,
4297 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4298 MissingErrorLocationMode aLocationMode) {
4299 uint64_t innerWindowID = 0;
4300 if (aDocument) {
4301 if (!aURI) {
4302 aURI = aDocument->GetDocumentURI();
4304 innerWindowID = aDocument->InnerWindowID();
4307 return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
4308 innerWindowID, aURI, aSourceLine,
4309 aLineNumber, aColumnNumber, aLocationMode);
4312 /* static */
4313 nsresult nsContentUtils::ReportToConsoleByWindowID(
4314 const nsAString& aErrorText, uint32_t aErrorFlags,
4315 const nsACString& aCategory, uint64_t aInnerWindowID, nsIURI* aURI,
4316 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4317 MissingErrorLocationMode aLocationMode) {
4318 nsresult rv;
4319 if (!sConsoleService) { // only need to bother null-checking here
4320 rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4321 NS_ENSURE_SUCCESS(rv, rv);
4324 nsAutoString spec;
4325 if (!aLineNumber && aLocationMode == eUSE_CALLING_LOCATION) {
4326 JSContext* cx = GetCurrentJSContext();
4327 if (cx) {
4328 nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber, &aColumnNumber);
4332 nsCOMPtr<nsIScriptError> errorObject =
4333 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
4334 NS_ENSURE_SUCCESS(rv, rv);
4336 if (!spec.IsEmpty()) {
4337 rv = errorObject->InitWithWindowID(aErrorText,
4338 spec, // file name
4339 aSourceLine, aLineNumber, aColumnNumber,
4340 aErrorFlags, aCategory, aInnerWindowID);
4341 } else {
4342 rv = errorObject->InitWithSourceURI(aErrorText, aURI, aSourceLine,
4343 aLineNumber, aColumnNumber, aErrorFlags,
4344 aCategory, aInnerWindowID);
4346 NS_ENSURE_SUCCESS(rv, rv);
4348 return sConsoleService->LogMessage(errorObject);
4351 void nsContentUtils::LogMessageToConsole(const char* aMsg) {
4352 if (!sConsoleService) { // only need to bother null-checking here
4353 CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4354 if (!sConsoleService) {
4355 return;
4358 sConsoleService->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
4361 bool nsContentUtils::IsChromeDoc(const Document* aDocument) {
4362 return aDocument && aDocument->NodePrincipal() == sSystemPrincipal;
4365 bool nsContentUtils::IsChildOfSameType(Document* aDoc) {
4366 if (BrowsingContext* bc = aDoc->GetBrowsingContext()) {
4367 return bc->GetParent();
4369 return false;
4372 bool nsContentUtils::IsPlainTextType(const nsACString& aContentType) {
4373 // NOTE: if you add a type here, add it to the CONTENTDLF_CATEGORIES
4374 // define in nsContentDLF.h as well.
4375 return aContentType.EqualsLiteral(TEXT_PLAIN) ||
4376 aContentType.EqualsLiteral(TEXT_CSS) ||
4377 aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4378 aContentType.EqualsLiteral(TEXT_VTT) ||
4379 aContentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
4380 aContentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
4381 aContentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
4382 aContentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
4383 aContentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
4384 aContentType.EqualsLiteral(APPLICATION_JSON) ||
4385 aContentType.EqualsLiteral(TEXT_JSON);
4388 bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) {
4389 // NOTE: This must be a subset of the list in IsPlainTextType().
4390 return aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4391 aContentType.EqualsLiteral(APPLICATION_JSON) ||
4392 aContentType.EqualsLiteral(TEXT_JSON) ||
4393 aContentType.EqualsLiteral(TEXT_VTT);
4396 bool nsContentUtils::IsInChromeDocshell(const Document* aDocument) {
4397 return aDocument && aDocument->IsInChromeDocShell();
4400 // static
4401 nsIContentPolicy* nsContentUtils::GetContentPolicy() {
4402 if (!sTriedToGetContentPolicy) {
4403 CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
4404 // It's OK to not have a content policy service
4405 sTriedToGetContentPolicy = true;
4408 return sContentPolicyService;
4411 // static
4412 bool nsContentUtils::IsEventAttributeName(nsAtom* aName, int32_t aType) {
4413 const char16_t* name = aName->GetUTF16String();
4414 if (name[0] != 'o' || name[1] != 'n') {
4415 return false;
4418 EventNameMapping mapping;
4419 return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
4422 // static
4423 EventMessage nsContentUtils::GetEventMessage(nsAtom* aName) {
4424 MOZ_ASSERT(NS_IsMainThread(), "sAtomEventTable is not threadsafe");
4425 if (aName) {
4426 EventNameMapping mapping;
4427 if (sAtomEventTable->Get(aName, &mapping)) {
4428 return mapping.mMessage;
4432 return eUnidentifiedEvent;
4435 // static
4436 mozilla::EventClassID nsContentUtils::GetEventClassID(const nsAString& aName) {
4437 EventNameMapping mapping;
4438 if (sStringEventTable->Get(aName, &mapping)) return mapping.mEventClassID;
4440 return eBasicEventClass;
4443 nsAtom* nsContentUtils::GetEventMessageAndAtom(
4444 const nsAString& aName, mozilla::EventClassID aEventClassID,
4445 EventMessage* aEventMessage) {
4446 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4447 EventNameMapping mapping;
4448 if (sStringEventTable->Get(aName, &mapping)) {
4449 *aEventMessage = mapping.mEventClassID == aEventClassID
4450 ? mapping.mMessage
4451 : eUnidentifiedEvent;
4452 return mapping.mAtom;
4455 // If we have cached lots of user defined event names, clear some of them.
4456 if (sUserDefinedEvents->Length() > 127) {
4457 while (sUserDefinedEvents->Length() > 64) {
4458 nsAtom* first = sUserDefinedEvents->ElementAt(0);
4459 sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
4460 sUserDefinedEvents->RemoveElementAt(0);
4464 *aEventMessage = eUnidentifiedEvent;
4465 RefPtr<nsAtom> atom = NS_AtomizeMainThread(u"on"_ns + aName);
4466 sUserDefinedEvents->AppendElement(atom);
4467 mapping.mAtom = atom;
4468 mapping.mMessage = eUnidentifiedEvent;
4469 mapping.mType = EventNameType_None;
4470 mapping.mEventClassID = eBasicEventClass;
4471 // This is a slow hashtable call, but at least we cache the result for the
4472 // following calls. Because GetEventMessageAndAtomForListener utilizes
4473 // sStringEventTable, it needs to know in which cases sStringEventTable
4474 // doesn't contain the information it needs so that it can use
4475 // sAtomEventTable instead.
4476 mapping.mMaybeSpecialSVGorSMILEvent =
4477 GetEventMessage(atom) != eUnidentifiedEvent;
4478 sStringEventTable->InsertOrUpdate(aName, mapping);
4479 return mapping.mAtom;
4482 // static
4483 EventMessage nsContentUtils::GetEventMessageAndAtomForListener(
4484 const nsAString& aName, nsAtom** aOnName) {
4485 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4487 // Because of SVG/SMIL sStringEventTable contains a subset of the event names
4488 // comparing to the sAtomEventTable. However, usually sStringEventTable
4489 // contains the information we need, so in order to reduce hashtable
4490 // lookups, start from it.
4491 EventNameMapping mapping;
4492 EventMessage msg = eUnidentifiedEvent;
4493 RefPtr<nsAtom> atom;
4494 if (sStringEventTable->Get(aName, &mapping)) {
4495 if (mapping.mMaybeSpecialSVGorSMILEvent) {
4496 // Try the atom version so that we should get the right message for
4497 // SVG/SMIL.
4498 atom = NS_AtomizeMainThread(u"on"_ns + aName);
4499 msg = GetEventMessage(atom);
4500 } else {
4501 atom = mapping.mAtom;
4502 msg = mapping.mMessage;
4504 atom.forget(aOnName);
4505 return msg;
4508 // GetEventMessageAndAtom will cache the event type for the future usage...
4509 GetEventMessageAndAtom(aName, eBasicEventClass, &msg);
4511 // ...and then call this method recursively to get the message and atom from
4512 // now updated sStringEventTable.
4513 return GetEventMessageAndAtomForListener(aName, aOnName);
4516 static nsresult GetEventAndTarget(Document* aDoc, nsISupports* aTarget,
4517 const nsAString& aEventName,
4518 CanBubble aCanBubble, Cancelable aCancelable,
4519 Composed aComposed, Trusted aTrusted,
4520 Event** aEvent, EventTarget** aTargetOut) {
4521 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4522 NS_ENSURE_TRUE(aDoc && target, NS_ERROR_INVALID_ARG);
4524 ErrorResult err;
4525 RefPtr<Event> event =
4526 aDoc->CreateEvent(u"Events"_ns, CallerType::System, err);
4527 if (NS_WARN_IF(err.Failed())) {
4528 return err.StealNSResult();
4531 event->InitEvent(aEventName, aCanBubble, aCancelable, aComposed);
4532 event->SetTrusted(aTrusted == Trusted::eYes);
4534 event->SetTarget(target);
4536 event.forget(aEvent);
4537 target.forget(aTargetOut);
4538 return NS_OK;
4541 // static
4542 nsresult nsContentUtils::DispatchTrustedEvent(
4543 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4544 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4545 bool* aDefaultAction) {
4546 MOZ_ASSERT(!aEventName.EqualsLiteral("input") &&
4547 !aEventName.EqualsLiteral("beforeinput"),
4548 "Use DispatchInputEvent() instead");
4549 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4550 aComposed, Trusted::eYes, aDefaultAction);
4553 // static
4554 nsresult nsContentUtils::DispatchUntrustedEvent(
4555 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4556 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4557 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4558 Composed::eDefault, Trusted::eNo, aDefaultAction);
4561 // static
4562 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4563 const nsAString& aEventName,
4564 CanBubble aCanBubble,
4565 Cancelable aCancelable,
4566 Composed aComposed, Trusted aTrusted,
4567 bool* aDefaultAction,
4568 ChromeOnlyDispatch aOnlyChromeDispatch) {
4569 RefPtr<Event> event;
4570 nsCOMPtr<EventTarget> target;
4571 nsresult rv = GetEventAndTarget(
4572 aDoc, aTarget, aEventName, aCanBubble, aCancelable, aComposed, aTrusted,
4573 getter_AddRefs(event), getter_AddRefs(target));
4574 NS_ENSURE_SUCCESS(rv, rv);
4575 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch =
4576 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4578 ErrorResult err;
4579 bool doDefault = target->DispatchEvent(*event, CallerType::System, err);
4580 if (aDefaultAction) {
4581 *aDefaultAction = doDefault;
4583 return err.StealNSResult();
4586 // static
4587 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4588 WidgetEvent& aEvent,
4589 EventMessage aEventMessage,
4590 CanBubble aCanBubble,
4591 Cancelable aCancelable, Trusted aTrusted,
4592 bool* aDefaultAction,
4593 ChromeOnlyDispatch aOnlyChromeDispatch) {
4594 MOZ_ASSERT_IF(aOnlyChromeDispatch == ChromeOnlyDispatch::eYes,
4595 aTrusted == Trusted::eYes);
4597 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4599 aEvent.mSpecifiedEventType = GetEventTypeFromMessage(aEventMessage);
4600 aEvent.SetDefaultComposed();
4601 aEvent.SetDefaultComposedInNativeAnonymousContent();
4603 aEvent.mFlags.mBubbles = aCanBubble == CanBubble::eYes;
4604 aEvent.mFlags.mCancelable = aCancelable == Cancelable::eYes;
4605 aEvent.mFlags.mOnlyChromeDispatch =
4606 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4608 aEvent.mTarget = target;
4610 nsEventStatus status = nsEventStatus_eIgnore;
4611 nsresult rv = EventDispatcher::DispatchDOMEvent(target, &aEvent, nullptr,
4612 nullptr, &status);
4613 if (aDefaultAction) {
4614 *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
4616 return rv;
4619 // static
4620 nsresult nsContentUtils::DispatchInputEvent(Element* aEventTarget) {
4621 return DispatchInputEvent(aEventTarget, mozilla::eEditorInput,
4622 mozilla::EditorInputType::eUnknown, nullptr,
4623 InputEventOptions());
4626 // static
4627 nsresult nsContentUtils::DispatchInputEvent(
4628 Element* aEventTargetElement, EventMessage aEventMessage,
4629 EditorInputType aEditorInputType, EditorBase* aEditorBase,
4630 InputEventOptions&& aOptions, nsEventStatus* aEventStatus /* = nullptr */) {
4631 MOZ_ASSERT(aEventMessage == eEditorInput ||
4632 aEventMessage == eEditorBeforeInput);
4634 if (NS_WARN_IF(!aEventTargetElement)) {
4635 return NS_ERROR_INVALID_ARG;
4638 // If this is called from editor, the instance should be set to aEditorBase.
4639 // Otherwise, we need to look for an editor for aEventTargetElement.
4640 // However, we don't need to do it for HTMLEditor since nobody shouldn't
4641 // dispatch "beforeinput" nor "input" event for HTMLEditor except HTMLEditor
4642 // itself.
4643 bool useInputEvent = false;
4644 if (aEditorBase) {
4645 useInputEvent = true;
4646 } else if (HTMLTextAreaElement* textAreaElement =
4647 HTMLTextAreaElement::FromNode(aEventTargetElement)) {
4648 aEditorBase = textAreaElement->GetTextEditorWithoutCreation();
4649 useInputEvent = true;
4650 } else if (HTMLInputElement* inputElement =
4651 HTMLInputElement::FromNode(aEventTargetElement)) {
4652 if (inputElement->IsInputEventTarget()) {
4653 aEditorBase = inputElement->GetTextEditorWithoutCreation();
4654 useInputEvent = true;
4657 #ifdef DEBUG
4658 else {
4659 MOZ_ASSERT(!aEventTargetElement->IsTextControlElement(),
4660 "The event target may have editor, but we've not known it yet.");
4662 #endif // #ifdef DEBUG
4664 if (!useInputEvent) {
4665 MOZ_ASSERT(aEventMessage == eEditorInput);
4666 MOZ_ASSERT(aEditorInputType == EditorInputType::eUnknown);
4667 MOZ_ASSERT(!aOptions.mNeverCancelable);
4668 // Dispatch "input" event with Event instance.
4669 WidgetEvent widgetEvent(true, eUnidentifiedEvent);
4670 widgetEvent.mSpecifiedEventType = nsGkAtoms::oninput;
4671 widgetEvent.mFlags.mCancelable = false;
4672 widgetEvent.mFlags.mComposed = true;
4673 (new AsyncEventDispatcher(aEventTargetElement, widgetEvent))
4674 ->RunDOMEventWhenSafe();
4675 return NS_OK;
4678 MOZ_ASSERT_IF(aEventMessage != eEditorBeforeInput,
4679 !aOptions.mNeverCancelable);
4680 MOZ_ASSERT_IF(
4681 aEventMessage == eEditorBeforeInput && aOptions.mNeverCancelable,
4682 aEditorInputType == EditorInputType::eInsertReplacementText);
4684 nsCOMPtr<nsIWidget> widget;
4685 if (aEditorBase) {
4686 widget = aEditorBase->GetWidget();
4687 if (NS_WARN_IF(!widget)) {
4688 return NS_ERROR_FAILURE;
4690 } else {
4691 Document* document = aEventTargetElement->OwnerDoc();
4692 if (NS_WARN_IF(!document)) {
4693 return NS_ERROR_FAILURE;
4695 // If we're running xpcshell tests, we fail to get presShell here.
4696 // Even in such case, we need to dispatch "input" event without widget.
4697 PresShell* presShell = document->GetPresShell();
4698 if (presShell) {
4699 nsPresContext* presContext = presShell->GetPresContext();
4700 if (NS_WARN_IF(!presContext)) {
4701 return NS_ERROR_FAILURE;
4703 widget = presContext->GetRootWidget();
4704 if (NS_WARN_IF(!widget)) {
4705 return NS_ERROR_FAILURE;
4710 // Dispatch "input" event with InputEvent instance.
4711 InternalEditorInputEvent inputEvent(true, aEventMessage, widget);
4713 inputEvent.mFlags.mCancelable =
4714 !aOptions.mNeverCancelable && aEventMessage == eEditorBeforeInput &&
4715 IsCancelableBeforeInputEvent(aEditorInputType);
4716 MOZ_ASSERT(!inputEvent.mFlags.mCancelable || aEventStatus);
4718 // If there is an editor, set isComposing to true when it has composition.
4719 // Note that EditorBase::IsIMEComposing() may return false even when we
4720 // need to set it to true.
4721 // Otherwise, i.e., editor hasn't been created for the element yet,
4722 // we should set isComposing to false since the element can never has
4723 // composition without editor.
4724 inputEvent.mIsComposing = aEditorBase && aEditorBase->GetComposition();
4726 if (!aEditorBase || aEditorBase->IsTextEditor()) {
4727 if (IsDataAvailableOnTextEditor(aEditorInputType)) {
4728 inputEvent.mData = std::move(aOptions.mData);
4729 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4730 "inputEvent.mData shouldn't be void");
4732 #ifdef DEBUG
4733 else {
4734 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4736 #endif // #ifdef DEBUG
4737 MOZ_ASSERT(
4738 aOptions.mTargetRanges.IsEmpty(),
4739 "Target ranges for <input> and <textarea> should always be empty");
4740 } else {
4741 MOZ_ASSERT(aEditorBase->IsHTMLEditor());
4742 if (IsDataAvailableOnHTMLEditor(aEditorInputType)) {
4743 inputEvent.mData = std::move(aOptions.mData);
4744 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4745 "inputEvent.mData shouldn't be void");
4746 } else {
4747 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4748 if (IsDataTransferAvailableOnHTMLEditor(aEditorInputType)) {
4749 inputEvent.mDataTransfer = std::move(aOptions.mDataTransfer);
4750 MOZ_ASSERT(inputEvent.mDataTransfer,
4751 "inputEvent.mDataTransfer shouldn't be nullptr");
4752 MOZ_ASSERT(inputEvent.mDataTransfer->IsReadOnly(),
4753 "inputEvent.mDataTransfer should be read only");
4755 #ifdef DEBUG
4756 else {
4757 MOZ_ASSERT(!inputEvent.mDataTransfer,
4758 "inputEvent.mDataTransfer should be nullptr");
4760 #endif // #ifdef DEBUG
4762 if (aEventMessage == eEditorBeforeInput &&
4763 MayHaveTargetRangesOnHTMLEditor(aEditorInputType)) {
4764 inputEvent.mTargetRanges = std::move(aOptions.mTargetRanges);
4766 #ifdef DEBUG
4767 else {
4768 MOZ_ASSERT(aOptions.mTargetRanges.IsEmpty(),
4769 "Target ranges shouldn't be set for the dispatching event");
4771 #endif // #ifdef DEBUG
4774 inputEvent.mInputType = aEditorInputType;
4776 if (!IsSafeToRunScript()) {
4777 // If we cannot dispatch an event right now, we cannot make it cancelable.
4778 NS_ASSERTION(
4779 !inputEvent.mFlags.mCancelable,
4780 "Cancelable beforeinput event dispatcher should run when it's safe");
4781 inputEvent.mFlags.mCancelable = false;
4782 (new AsyncEventDispatcher(aEventTargetElement, inputEvent))
4783 ->RunDOMEventWhenSafe();
4784 return NS_OK;
4787 // If we're running xpcshell tests, we fail to get presShell here.
4788 // Even in such case, we need to dispatch "input" event without widget.
4789 RefPtr<nsPresContext> presContext =
4790 aEventTargetElement->OwnerDoc()->GetPresContext();
4791 nsresult rv = EventDispatcher::Dispatch(aEventTargetElement, presContext,
4792 &inputEvent, nullptr, aEventStatus);
4793 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
4794 "Dispatching `beforeinput` or `input` event failed");
4795 return rv;
4798 nsresult nsContentUtils::DispatchChromeEvent(
4799 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4800 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4801 RefPtr<Event> event;
4802 nsCOMPtr<EventTarget> target;
4803 nsresult rv = GetEventAndTarget(
4804 aDoc, aTarget, aEventName, aCanBubble, aCancelable, Composed::eDefault,
4805 Trusted::eYes, getter_AddRefs(event), getter_AddRefs(target));
4806 NS_ENSURE_SUCCESS(rv, rv);
4808 NS_ASSERTION(aDoc, "GetEventAndTarget lied?");
4809 if (!aDoc->GetWindow()) return NS_ERROR_INVALID_ARG;
4811 EventTarget* piTarget = aDoc->GetWindow()->GetParentTarget();
4812 if (!piTarget) return NS_ERROR_INVALID_ARG;
4814 ErrorResult err;
4815 bool defaultActionEnabled =
4816 piTarget->DispatchEvent(*event, CallerType::System, err);
4817 if (aDefaultAction) {
4818 *aDefaultAction = defaultActionEnabled;
4820 return err.StealNSResult();
4823 void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise,
4824 CallerType aCallerType) {
4825 RefPtr<Element> target = &aFrameElement;
4826 bool defaultAction = true;
4827 if (aCanRaise) {
4828 DispatchEventOnlyToChrome(target->OwnerDoc(), target,
4829 u"framefocusrequested"_ns, CanBubble::eYes,
4830 Cancelable::eYes, &defaultAction);
4832 if (!defaultAction) {
4833 return;
4836 RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
4837 if (!fm) {
4838 return;
4841 uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
4842 if (aCanRaise) {
4843 flags |= nsIFocusManager::FLAG_RAISE;
4846 if (aCallerType == CallerType::NonSystem) {
4847 flags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
4850 fm->SetFocus(target, flags);
4853 nsresult nsContentUtils::DispatchEventOnlyToChrome(
4854 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4855 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4856 bool* aDefaultAction) {
4857 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4858 aComposed, Trusted::eYes, aDefaultAction,
4859 ChromeOnlyDispatch::eYes);
4862 /* static */
4863 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4864 const nsAtom* aId) {
4865 for (nsIContent* cur = aContent; cur; cur = cur->GetNextNode(aContent)) {
4866 if (aId == cur->GetID()) {
4867 return cur->AsElement();
4871 return nullptr;
4874 /* static */
4875 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4876 const nsAString& aId) {
4877 MOZ_ASSERT(!aId.IsEmpty(), "Will match random elements");
4879 // ID attrs are generally stored as atoms, so just atomize this up front
4880 RefPtr<nsAtom> id(NS_Atomize(aId));
4881 if (!id) {
4882 // OOM, so just bail
4883 return nullptr;
4886 return MatchElementId(aContent, id);
4889 /* static */
4890 void nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver) {
4891 nsCOMPtr<nsIObserverService> observerService =
4892 mozilla::services::GetObserverService();
4893 if (observerService) {
4894 observerService->AddObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
4895 false);
4899 /* static */
4900 void nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver) {
4901 nsCOMPtr<nsIObserverService> observerService =
4902 mozilla::services::GetObserverService();
4903 if (observerService) {
4904 observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
4908 /* static */
4909 bool nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent,
4910 int32_t aNameSpaceID, nsAtom* aName) {
4911 static AttrArray::AttrValuesArray strings[] = {nsGkAtoms::_empty, nullptr};
4912 return aContent->IsElement() &&
4913 aContent->AsElement()->FindAttrValueIn(aNameSpaceID, aName, strings,
4914 eCaseMatters) ==
4915 AttrArray::ATTR_VALUE_NO_MATCH;
4918 /* static */
4919 bool nsContentUtils::HasMutationListeners(nsINode* aNode, uint32_t aType,
4920 nsINode* aTargetForSubtreeModified) {
4921 Document* doc = aNode->OwnerDoc();
4923 // global object will be null for documents that don't have windows.
4924 nsPIDOMWindowInner* window = doc->GetInnerWindow();
4925 // This relies on EventListenerManager::AddEventListener, which sets
4926 // all mutation bits when there is a listener for DOMSubtreeModified event.
4927 if (window && !window->HasMutationListeners(aType)) {
4928 return false;
4931 if (aNode->ChromeOnlyAccess() || aNode->IsInShadowTree()) {
4932 return false;
4935 doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
4937 // If we have a window, we can check it for mutation listeners now.
4938 if (aNode->IsInUncomposedDoc()) {
4939 nsCOMPtr<EventTarget> piTarget(do_QueryInterface(window));
4940 if (piTarget) {
4941 EventListenerManager* manager = piTarget->GetExistingListenerManager();
4942 if (manager && manager->HasMutationListeners()) {
4943 return true;
4948 // If we have a window, we know a mutation listener is registered, but it
4949 // might not be in our chain. If we don't have a window, we might have a
4950 // mutation listener. Check quickly to see.
4951 while (aNode) {
4952 EventListenerManager* manager = aNode->GetExistingListenerManager();
4953 if (manager && manager->HasMutationListeners()) {
4954 return true;
4957 aNode = aNode->GetParentNode();
4960 return false;
4963 /* static */
4964 bool nsContentUtils::HasMutationListeners(Document* aDocument, uint32_t aType) {
4965 nsPIDOMWindowInner* window =
4966 aDocument ? aDocument->GetInnerWindow() : nullptr;
4968 // This relies on EventListenerManager::AddEventListener, which sets
4969 // all mutation bits when there is a listener for DOMSubtreeModified event.
4970 return !window || window->HasMutationListeners(aType);
4973 void nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent) {
4974 MOZ_ASSERT(aChild, "Missing child");
4975 MOZ_ASSERT(aChild->GetParentNode() == aParent, "Wrong parent");
4976 MOZ_ASSERT(aChild->OwnerDoc() == aParent->OwnerDoc(), "Wrong owner-doc");
4978 // Having an explicit check here since it's an easy mistake to fall into,
4979 // and there might be existing code with problems. We'd rather be safe
4980 // than fire DOMNodeRemoved in all corner cases. We also rely on it for
4981 // nsAutoScriptBlockerSuppressNodeRemoved.
4982 if (!IsSafeToRunScript()) {
4983 // This checks that IsSafeToRunScript is true since we don't want to fire
4984 // events when that is false. We can't rely on EventDispatcher to assert
4985 // this in this situation since most of the time there are no mutation
4986 // event listeners, in which case we won't even attempt to dispatch events.
4987 // However this also allows for two exceptions. First off, we don't assert
4988 // if the mutation happens to native anonymous content since we never fire
4989 // mutation events on such content anyway.
4990 // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
4991 // that is a know case when we'd normally fire a mutation event, but can't
4992 // make that safe and so we suppress it at this time. Ideally this should
4993 // go away eventually.
4994 if (!aChild->IsInNativeAnonymousSubtree() &&
4995 !sDOMNodeRemovedSuppressCount) {
4996 NS_ERROR("Want to fire DOMNodeRemoved event, but it's not safe");
4997 WarnScriptWasIgnored(aChild->OwnerDoc());
4999 return;
5003 Document* doc = aParent->OwnerDoc();
5004 if (MOZ_UNLIKELY(doc->DevToolsWatchingDOMMutations()) &&
5005 aChild->IsInComposedDoc() && !aChild->ChromeOnlyAccess()) {
5006 DispatchChromeEvent(doc, aChild, u"devtoolschildremoved"_ns,
5007 CanBubble::eNo, Cancelable::eNo);
5011 if (HasMutationListeners(aChild, NS_EVENT_BITS_MUTATION_NODEREMOVED,
5012 aParent)) {
5013 InternalMutationEvent mutation(true, eLegacyNodeRemoved);
5014 mutation.mRelatedNode = aParent;
5016 mozAutoSubtreeModified subtree(aParent->OwnerDoc(), aParent);
5017 EventDispatcher::Dispatch(aChild, nullptr, &mutation);
5021 void nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments() {
5022 if (!sEventListenerManagersHash) {
5023 return;
5026 for (auto i = sEventListenerManagersHash->Iter(); !i.Done(); i.Next()) {
5027 auto entry = static_cast<EventListenerManagerMapEntry*>(i.Get());
5028 nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
5029 if (n && n->IsInComposedDoc() &&
5030 nsCCUncollectableMarker::InGeneration(
5031 n->OwnerDoc()->GetMarkedCCGeneration())) {
5032 entry->mListenerManager->MarkForCC();
5037 /* static */
5038 void nsContentUtils::TraverseListenerManager(
5039 nsINode* aNode, nsCycleCollectionTraversalCallback& cb) {
5040 if (!sEventListenerManagersHash) {
5041 // We're already shut down, just return.
5042 return;
5045 auto entry = static_cast<EventListenerManagerMapEntry*>(
5046 sEventListenerManagersHash->Search(aNode));
5047 if (entry) {
5048 CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
5049 "[via hash] mListenerManager");
5053 EventListenerManager* nsContentUtils::GetListenerManagerForNode(
5054 nsINode* aNode) {
5055 if (!sEventListenerManagersHash) {
5056 // We're already shut down, don't bother creating an event listener
5057 // manager.
5059 return nullptr;
5062 auto entry = static_cast<EventListenerManagerMapEntry*>(
5063 sEventListenerManagersHash->Add(aNode, fallible));
5065 if (!entry) {
5066 return nullptr;
5069 if (!entry->mListenerManager) {
5070 entry->mListenerManager = new EventListenerManager(aNode);
5072 aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
5075 return entry->mListenerManager;
5078 EventListenerManager* nsContentUtils::GetExistingListenerManagerForNode(
5079 const nsINode* aNode) {
5080 if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
5081 return nullptr;
5084 if (!sEventListenerManagersHash) {
5085 // We're already shut down, don't bother creating an event listener
5086 // manager.
5088 return nullptr;
5091 auto entry = static_cast<EventListenerManagerMapEntry*>(
5092 sEventListenerManagersHash->Search(aNode));
5093 if (entry) {
5094 return entry->mListenerManager;
5097 return nullptr;
5100 void nsContentUtils::AddEntryToDOMArenaTable(nsINode* aNode,
5101 DOMArena* aDOMArena) {
5102 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
5103 MOZ_ASSERT_IF(sDOMArenaHashtable, !sDOMArenaHashtable->Contains(aNode));
5104 MOZ_ASSERT(!aNode->HasFlag(NODE_KEEPS_DOMARENA));
5105 if (!sDOMArenaHashtable) {
5106 sDOMArenaHashtable =
5107 new nsRefPtrHashtable<nsPtrHashKey<const nsINode>, dom::DOMArena>();
5109 aNode->SetFlags(NODE_KEEPS_DOMARENA);
5110 sDOMArenaHashtable->InsertOrUpdate(aNode, RefPtr<DOMArena>(aDOMArena));
5113 already_AddRefed<DOMArena> nsContentUtils::TakeEntryFromDOMArenaTable(
5114 const nsINode* aNode) {
5115 MOZ_ASSERT(sDOMArenaHashtable->Contains(aNode));
5116 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
5117 RefPtr<DOMArena> arena;
5118 sDOMArenaHashtable->Remove(aNode, getter_AddRefs(arena));
5119 return arena.forget();
5122 /* static */
5123 void nsContentUtils::RemoveListenerManager(nsINode* aNode) {
5124 if (sEventListenerManagersHash) {
5125 auto entry = static_cast<EventListenerManagerMapEntry*>(
5126 sEventListenerManagersHash->Search(aNode));
5127 if (entry) {
5128 RefPtr<EventListenerManager> listenerManager;
5129 listenerManager.swap(entry->mListenerManager);
5130 // Remove the entry and *then* do operations that could cause further
5131 // modification of sEventListenerManagersHash. See bug 334177.
5132 sEventListenerManagersHash->RawRemove(entry);
5133 if (listenerManager) {
5134 listenerManager->Disconnect();
5140 /* static */
5141 bool nsContentUtils::IsValidNodeName(nsAtom* aLocalName, nsAtom* aPrefix,
5142 int32_t aNamespaceID) {
5143 if (aNamespaceID == kNameSpaceID_Unknown) {
5144 return false;
5147 if (!aPrefix) {
5148 // If the prefix is null, then either the QName must be xmlns or the
5149 // namespace must not be XMLNS.
5150 return (aLocalName == nsGkAtoms::xmlns) ==
5151 (aNamespaceID == kNameSpaceID_XMLNS);
5154 // If the prefix is non-null then the namespace must not be null.
5155 if (aNamespaceID == kNameSpaceID_None) {
5156 return false;
5159 // If the namespace is the XMLNS namespace then the prefix must be xmlns,
5160 // but the localname must not be xmlns.
5161 if (aNamespaceID == kNameSpaceID_XMLNS) {
5162 return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
5165 // If the namespace is not the XMLNS namespace then the prefix must not be
5166 // xmlns.
5167 // If the namespace is the XML namespace then the prefix can be anything.
5168 // If the namespace is not the XML namespace then the prefix must not be xml.
5169 return aPrefix != nsGkAtoms::xmlns &&
5170 (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
5173 already_AddRefed<DocumentFragment> nsContentUtils::CreateContextualFragment(
5174 nsINode* aContextNode, const nsAString& aFragment,
5175 bool aPreventScriptExecution, ErrorResult& aRv) {
5176 if (!aContextNode) {
5177 aRv.Throw(NS_ERROR_INVALID_ARG);
5178 return nullptr;
5181 // If we don't have a document here, we can't get the right security context
5182 // for compiling event handlers... so just bail out.
5183 RefPtr<Document> document = aContextNode->OwnerDoc();
5184 bool isHTML = document->IsHTMLDocument();
5186 if (isHTML) {
5187 RefPtr<DocumentFragment> frag = new (document->NodeInfoManager())
5188 DocumentFragment(document->NodeInfoManager());
5190 Element* element = aContextNode->GetAsElementOrParentElement();
5191 if (element && !element->IsHTMLElement(nsGkAtoms::html)) {
5192 aRv = ParseFragmentHTML(
5193 aFragment, frag, element->NodeInfo()->NameAtom(),
5194 element->GetNameSpaceID(),
5195 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
5196 aPreventScriptExecution);
5197 } else {
5198 aRv = ParseFragmentHTML(
5199 aFragment, frag, nsGkAtoms::body, kNameSpaceID_XHTML,
5200 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
5201 aPreventScriptExecution);
5204 return frag.forget();
5207 AutoTArray<nsString, 32> tagStack;
5208 nsAutoString uriStr, nameStr;
5209 for (Element* element : aContextNode->InclusiveAncestorsOfType<Element>()) {
5210 nsString& tagName = *tagStack.AppendElement();
5211 // It mostly doesn't actually matter what tag name we use here: XML doesn't
5212 // have parsing that depends on the open tag stack, apart from namespace
5213 // declarations. So this whole tagStack bit is just there to get the right
5214 // namespace declarations to the XML parser. That said, the parser _is_
5215 // going to create elements with the tag names we provide here, so we need
5216 // to make sure they are not names that can trigger custom element
5217 // constructors. Just make up a name that is never going to be a valid
5218 // custom element name.
5220 // The principled way to do this would probably be to add a new FromParser
5221 // value and make sure we use it when creating the context elements, then
5222 // make sure we teach all FromParser consumers (and in particular the custom
5223 // element code) about it as needed. But right now the XML parser never
5224 // actually uses FromParser values other than NOT_FROM_PARSER, and changing
5225 // that is pretty complicated.
5226 tagName.AssignLiteral("notacustomelement");
5228 // see if we need to add xmlns declarations
5229 uint32_t count = element->GetAttrCount();
5230 bool setDefaultNamespace = false;
5231 if (count > 0) {
5232 uint32_t index;
5234 for (index = 0; index < count; index++) {
5235 const BorrowedAttrInfo info = element->GetAttrInfoAt(index);
5236 const nsAttrName* name = info.mName;
5237 if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
5238 info.mValue->ToString(uriStr);
5240 // really want something like nsXMLContentSerializer::SerializeAttr
5241 tagName.AppendLiteral(" xmlns"); // space important
5242 if (name->GetPrefix()) {
5243 tagName.Append(char16_t(':'));
5244 name->LocalName()->ToString(nameStr);
5245 tagName.Append(nameStr);
5246 } else {
5247 setDefaultNamespace = true;
5249 tagName.AppendLiteral(R"(=")");
5250 tagName.Append(uriStr);
5251 tagName.Append('"');
5256 if (!setDefaultNamespace) {
5257 mozilla::dom::NodeInfo* info = element->NodeInfo();
5258 if (!info->GetPrefixAtom() && info->NamespaceID() != kNameSpaceID_None) {
5259 // We have no namespace prefix, but have a namespace ID. Push
5260 // default namespace attr in, so that our kids will be in our
5261 // namespace.
5262 info->GetNamespaceURI(uriStr);
5263 tagName.AppendLiteral(R"( xmlns=")");
5264 tagName.Append(uriStr);
5265 tagName.Append('"');
5270 RefPtr<DocumentFragment> frag;
5271 aRv = ParseFragmentXML(aFragment, document, tagStack, aPreventScriptExecution,
5272 -1, getter_AddRefs(frag));
5273 return frag.forget();
5276 /* static */
5277 void nsContentUtils::DropFragmentParsers() {
5278 NS_IF_RELEASE(sHTMLFragmentParser);
5279 NS_IF_RELEASE(sXMLFragmentParser);
5280 NS_IF_RELEASE(sXMLFragmentSink);
5283 /* static */
5284 void nsContentUtils::XPCOMShutdown() { nsContentUtils::DropFragmentParsers(); }
5286 /* Helper function to compuate Sanitization Flags for ParseFramentHTML/XML */
5287 uint32_t computeSanitizationFlags(nsIPrincipal* aPrincipal, int32_t aFlags) {
5288 uint32_t sanitizationFlags = 0;
5289 if (aPrincipal->IsSystemPrincipal()) {
5290 if (aFlags < 0) {
5291 // if this is a chrome-privileged document and no explicit flags
5292 // were passed, then use this sanitization flags.
5293 sanitizationFlags = nsIParserUtils::SanitizerAllowStyle |
5294 nsIParserUtils::SanitizerAllowComments |
5295 nsIParserUtils::SanitizerDropForms |
5296 nsIParserUtils::SanitizerLogRemovals;
5297 } else {
5298 // if the caller explicitly passes flags, then we use those
5299 // flags but additionally drop forms.
5300 sanitizationFlags = aFlags | nsIParserUtils::SanitizerDropForms;
5302 } else if (aFlags >= 0) {
5303 // aFlags by default is -1 and is only ever non equal to -1 if the
5304 // caller of ParseFragmentHTML/ParseFragmentXML is
5305 // ParserUtils::ParseFragment(). Only in that case we should use
5306 // the sanitization flags passed within aFlags.
5307 sanitizationFlags = aFlags;
5309 return sanitizationFlags;
5312 /* static */
5313 bool AllowsUnsanitizedContentForAboutNewTab(nsIPrincipal* aPrincipal) {
5314 if (StaticPrefs::dom_about_newtab_sanitization_enabled() ||
5315 !aPrincipal->SchemeIs("about")) {
5316 return false;
5318 uint32_t aboutModuleFlags = 0;
5319 aPrincipal->GetAboutModuleFlags(&aboutModuleFlags);
5320 return aboutModuleFlags & nsIAboutModule::ALLOW_UNSANITIZED_CONTENT;
5323 /* static */
5324 nsresult nsContentUtils::ParseFragmentHTML(
5325 const nsAString& aSourceBuffer, nsIContent* aTargetNode,
5326 nsAtom* aContextLocalName, int32_t aContextNamespace, bool aQuirks,
5327 bool aPreventScriptExecution, int32_t aFlags) {
5328 AutoTimelineMarker m(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");
5330 if (nsContentUtils::sFragmentParsingActive) {
5331 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5332 return NS_ERROR_DOM_INVALID_STATE_ERR;
5334 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5335 nsContentUtils::sFragmentParsingActive = true;
5336 if (!sHTMLFragmentParser) {
5337 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5338 // Now sHTMLFragmentParser owns the object
5341 nsCOMPtr<nsIPrincipal> nodePrincipal = aTargetNode->NodePrincipal();
5343 #ifdef DEBUG
5344 // aFlags should always be -1 unless the caller of ParseFragmentHTML
5345 // is ParserUtils::ParseFragment() which is the only caller that intends
5346 // sanitization. For all other callers we need to ensure to call
5347 // AuditParsingOfHTMLXMLFragments.
5348 if (aFlags < 0) {
5349 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5350 aSourceBuffer);
5352 #endif
5354 nsIContent* target = aTargetNode;
5356 RefPtr<Document> doc = aTargetNode->OwnerDoc();
5357 RefPtr<DocumentFragment> fragment;
5358 // We sanitize if the fragment occurs in a system privileged
5359 // context, an about: page, or if there are explicit sanitization flags.
5360 // Please note that about:blank and about:srcdoc inherit the security
5361 // context from the embedding context and hence are not loaded using
5362 // an about: scheme principal.
5363 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5364 nodePrincipal->SchemeIs("about") || aFlags >= 0;
5365 if (shouldSanitize &&
5366 !AllowsUnsanitizedContentForAboutNewTab(nodePrincipal)) {
5367 if (!doc->IsLoadedAsData()) {
5368 doc = nsContentUtils::CreateInertHTMLDocument(doc);
5369 if (!doc) {
5370 return NS_ERROR_FAILURE;
5373 fragment =
5374 new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager());
5375 target = fragment;
5378 nsresult rv = sHTMLFragmentParser->ParseFragment(
5379 aSourceBuffer, target, aContextLocalName, aContextNamespace, aQuirks,
5380 aPreventScriptExecution);
5381 NS_ENSURE_SUCCESS(rv, rv);
5383 if (fragment) {
5384 uint32_t sanitizationFlags =
5385 computeSanitizationFlags(nodePrincipal, aFlags);
5386 // Don't fire mutation events for nodes removed by the sanitizer.
5387 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5388 nsTreeSanitizer sanitizer(sanitizationFlags);
5389 sanitizer.Sanitize(fragment);
5391 ErrorResult error;
5392 aTargetNode->AppendChild(*fragment, error);
5393 rv = error.StealNSResult();
5396 return rv;
5399 /* static */
5400 nsresult nsContentUtils::ParseDocumentHTML(
5401 const nsAString& aSourceBuffer, Document* aTargetDocument,
5402 bool aScriptingEnabledForNoscriptParsing) {
5403 AutoTimelineMarker m(aTargetDocument->GetDocShell(), "Parse HTML");
5405 if (nsContentUtils::sFragmentParsingActive) {
5406 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5407 return NS_ERROR_DOM_INVALID_STATE_ERR;
5409 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5410 nsContentUtils::sFragmentParsingActive = true;
5411 if (!sHTMLFragmentParser) {
5412 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5413 // Now sHTMLFragmentParser owns the object
5415 nsresult rv = sHTMLFragmentParser->ParseDocument(
5416 aSourceBuffer, aTargetDocument, aScriptingEnabledForNoscriptParsing);
5417 return rv;
5420 /* static */
5421 nsresult nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
5422 Document* aDocument,
5423 nsTArray<nsString>& aTagStack,
5424 bool aPreventScriptExecution,
5425 int32_t aFlags,
5426 DocumentFragment** aReturn) {
5427 AutoTimelineMarker m(aDocument->GetDocShell(), "Parse XML");
5429 if (nsContentUtils::sFragmentParsingActive) {
5430 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5431 return NS_ERROR_DOM_INVALID_STATE_ERR;
5433 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5434 nsContentUtils::sFragmentParsingActive = true;
5435 if (!sXMLFragmentParser) {
5436 RefPtr<nsParser> parser = new nsParser();
5437 parser.forget(&sXMLFragmentParser);
5438 // sXMLFragmentParser now owns the parser
5440 if (!sXMLFragmentSink) {
5441 NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
5442 // sXMLFragmentSink now owns the sink
5444 nsCOMPtr<nsIContentSink> contentsink = do_QueryInterface(sXMLFragmentSink);
5445 MOZ_ASSERT(contentsink, "Sink doesn't QI to nsIContentSink!");
5446 sXMLFragmentParser->SetContentSink(contentsink);
5448 RefPtr<Document> doc;
5449 nsCOMPtr<nsIPrincipal> nodePrincipal = aDocument->NodePrincipal();
5451 #ifdef DEBUG
5452 // aFlags should always be -1 unless the caller of ParseFragmentXML
5453 // is ParserUtils::ParseFragment() which is the only caller that intends
5454 // sanitization. For all other callers we need to ensure to call
5455 // AuditParsingOfHTMLXMLFragments.
5456 if (aFlags < 0) {
5457 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5458 aSourceBuffer);
5460 #endif
5462 // We sanitize if the fragment occurs in a system privileged
5463 // context, an about: page, or if there are explicit sanitization flags.
5464 // Please note that about:blank and about:srcdoc inherit the security
5465 // context from the embedding context and hence are not loaded using
5466 // an about: scheme principal.
5467 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5468 nodePrincipal->SchemeIs("about") || aFlags >= 0;
5469 if (shouldSanitize && !aDocument->IsLoadedAsData()) {
5470 doc = nsContentUtils::CreateInertXMLDocument(aDocument);
5471 } else {
5472 doc = aDocument;
5475 sXMLFragmentSink->SetTargetDocument(doc);
5476 sXMLFragmentSink->SetPreventScriptExecution(aPreventScriptExecution);
5478 nsresult rv = sXMLFragmentParser->ParseFragment(aSourceBuffer, aTagStack);
5479 if (NS_FAILED(rv)) {
5480 // Drop the fragment parser and sink that might be in an inconsistent state
5481 NS_IF_RELEASE(sXMLFragmentParser);
5482 NS_IF_RELEASE(sXMLFragmentSink);
5483 return rv;
5486 rv = sXMLFragmentSink->FinishFragmentParsing(aReturn);
5488 sXMLFragmentParser->Reset();
5489 NS_ENSURE_SUCCESS(rv, rv);
5491 if (shouldSanitize) {
5492 uint32_t sanitizationFlags =
5493 computeSanitizationFlags(nodePrincipal, aFlags);
5494 // Don't fire mutation events for nodes removed by the sanitizer.
5495 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5496 nsTreeSanitizer sanitizer(sanitizationFlags);
5497 sanitizer.Sanitize(*aReturn);
5500 return rv;
5503 /* static */
5504 nsresult nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
5505 nsAString& aResultBuffer,
5506 uint32_t aFlags,
5507 uint32_t aWrapCol) {
5508 RefPtr<Document> document = nsContentUtils::CreateInertHTMLDocument(nullptr);
5509 if (!document) {
5510 return NS_ERROR_FAILURE;
5513 nsresult rv = nsContentUtils::ParseDocumentHTML(
5514 aSourceBuffer, document,
5515 !(aFlags & nsIDocumentEncoder::OutputNoScriptContent));
5516 NS_ENSURE_SUCCESS(rv, rv);
5518 nsCOMPtr<nsIDocumentEncoder> encoder = do_createDocumentEncoder("text/plain");
5520 rv = encoder->Init(document, u"text/plain"_ns, aFlags);
5521 NS_ENSURE_SUCCESS(rv, rv);
5523 encoder->SetWrapColumn(aWrapCol);
5525 return encoder->EncodeToString(aResultBuffer);
5528 static already_AddRefed<Document> CreateInertDocument(const Document* aTemplate,
5529 DocumentFlavor aFlavor) {
5530 if (aTemplate) {
5531 bool hasHad = true;
5532 nsIScriptGlobalObject* sgo = aTemplate->GetScriptHandlingObject(hasHad);
5533 NS_ENSURE_TRUE(sgo || !hasHad, nullptr);
5535 nsCOMPtr<Document> doc;
5536 nsresult rv = NS_NewDOMDocument(
5537 getter_AddRefs(doc), u""_ns, u""_ns, nullptr,
5538 aTemplate->GetDocumentURI(), aTemplate->GetDocBaseURI(),
5539 aTemplate->NodePrincipal(), true, sgo, aFlavor);
5540 if (NS_FAILED(rv)) {
5541 return nullptr;
5543 return doc.forget();
5545 nsCOMPtr<nsIURI> uri;
5546 NS_NewURI(getter_AddRefs(uri), "about:blank"_ns);
5547 if (!uri) {
5548 return nullptr;
5551 RefPtr<NullPrincipal> nullPrincipal =
5552 NullPrincipal::CreateWithoutOriginAttributes();
5553 if (!nullPrincipal) {
5554 return nullptr;
5557 nsCOMPtr<Document> doc;
5558 nsresult rv =
5559 NS_NewDOMDocument(getter_AddRefs(doc), u""_ns, u""_ns, nullptr, uri, uri,
5560 nullPrincipal, true, nullptr, aFlavor);
5561 if (NS_FAILED(rv)) {
5562 return nullptr;
5564 return doc.forget();
5567 /* static */
5568 already_AddRefed<Document> nsContentUtils::CreateInertXMLDocument(
5569 const Document* aTemplate) {
5570 return CreateInertDocument(aTemplate, DocumentFlavorXML);
5573 /* static */
5574 already_AddRefed<Document> nsContentUtils::CreateInertHTMLDocument(
5575 const Document* aTemplate) {
5576 return CreateInertDocument(aTemplate, DocumentFlavorHTML);
5579 /* static */
5580 nsresult nsContentUtils::SetNodeTextContent(nsIContent* aContent,
5581 const nsAString& aValue,
5582 bool aTryReuse) {
5583 // Fire DOMNodeRemoved mutation events before we do anything else.
5584 nsCOMPtr<nsIContent> owningContent;
5586 // Batch possible DOMSubtreeModified events.
5587 mozAutoSubtreeModified subtree(nullptr, nullptr);
5589 // Scope firing mutation events so that we don't carry any state that
5590 // might be stale
5592 // We're relying on mozAutoSubtreeModified to keep a strong reference if
5593 // needed.
5594 Document* doc = aContent->OwnerDoc();
5596 // Optimize the common case of there being no observers
5597 if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
5598 subtree.UpdateTarget(doc, nullptr);
5599 owningContent = aContent;
5600 nsCOMPtr<nsINode> child;
5601 bool skipFirst = aTryReuse;
5602 for (child = aContent->GetFirstChild();
5603 child && child->GetParentNode() == aContent;
5604 child = child->GetNextSibling()) {
5605 if (skipFirst && child->IsText()) {
5606 skipFirst = false;
5607 continue;
5609 nsContentUtils::MaybeFireNodeRemoved(child, aContent);
5614 // Might as well stick a batch around this since we're performing several
5615 // mutations.
5616 mozAutoDocUpdate updateBatch(aContent->GetComposedDoc(), true);
5617 nsAutoMutationBatch mb;
5619 if (aTryReuse && !aValue.IsEmpty()) {
5620 // Let's remove nodes until we find a eTEXT.
5621 while (aContent->HasChildren()) {
5622 nsIContent* child = aContent->GetFirstChild();
5623 if (child->IsText()) {
5624 break;
5626 aContent->RemoveChildNode(child, true);
5629 // If we have a node, it must be a eTEXT and we reuse it.
5630 if (aContent->HasChildren()) {
5631 nsIContent* child = aContent->GetFirstChild();
5632 nsresult rv = child->AsText()->SetText(aValue, true);
5633 NS_ENSURE_SUCCESS(rv, rv);
5635 // All the following nodes, if they exist, must be deleted.
5636 while (nsIContent* nextChild = child->GetNextSibling()) {
5637 aContent->RemoveChildNode(nextChild, true);
5641 if (aContent->HasChildren()) {
5642 return NS_OK;
5644 } else {
5645 mb.Init(aContent, true, false);
5646 while (aContent->HasChildren()) {
5647 aContent->RemoveChildNode(aContent->GetFirstChild(), true);
5650 mb.RemovalDone();
5652 if (aValue.IsEmpty()) {
5653 return NS_OK;
5656 RefPtr<nsTextNode> textContent = new (aContent->NodeInfo()->NodeInfoManager())
5657 nsTextNode(aContent->NodeInfo()->NodeInfoManager());
5659 textContent->SetText(aValue, true);
5661 ErrorResult rv;
5662 aContent->AppendChildTo(textContent, true, rv);
5663 mb.NodesAdded();
5664 return rv.StealNSResult();
5667 static bool AppendNodeTextContentsRecurse(const nsINode* aNode,
5668 nsAString& aResult,
5669 const fallible_t& aFallible) {
5670 for (nsIContent* child = aNode->GetFirstChild(); child;
5671 child = child->GetNextSibling()) {
5672 if (child->IsElement()) {
5673 bool ok = AppendNodeTextContentsRecurse(child, aResult, aFallible);
5674 if (!ok) {
5675 return false;
5677 } else if (Text* text = child->GetAsText()) {
5678 bool ok = text->AppendTextTo(aResult, aFallible);
5679 if (!ok) {
5680 return false;
5685 return true;
5688 /* static */
5689 bool nsContentUtils::AppendNodeTextContent(const nsINode* aNode, bool aDeep,
5690 nsAString& aResult,
5691 const fallible_t& aFallible) {
5692 if (const Text* text = aNode->GetAsText()) {
5693 return text->AppendTextTo(aResult, aFallible);
5695 if (aDeep) {
5696 return AppendNodeTextContentsRecurse(aNode, aResult, aFallible);
5699 for (nsIContent* child = aNode->GetFirstChild(); child;
5700 child = child->GetNextSibling()) {
5701 if (Text* text = child->GetAsText()) {
5702 bool ok = text->AppendTextTo(aResult, fallible);
5703 if (!ok) {
5704 return false;
5708 return true;
5711 bool nsContentUtils::HasNonEmptyTextContent(
5712 nsINode* aNode, TextContentDiscoverMode aDiscoverMode) {
5713 for (nsIContent* child = aNode->GetFirstChild(); child;
5714 child = child->GetNextSibling()) {
5715 if (child->IsText() && child->TextLength() > 0) {
5716 return true;
5719 if (aDiscoverMode == eRecurseIntoChildren &&
5720 HasNonEmptyTextContent(child, aDiscoverMode)) {
5721 return true;
5725 return false;
5728 /* static */
5729 bool nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
5730 const nsIContent* aContent) {
5731 MOZ_ASSERT(aNode, "Must have a node to work with");
5732 MOZ_ASSERT(aContent, "Must have a content to work with");
5734 if (aNode->IsInNativeAnonymousSubtree() !=
5735 aContent->IsInNativeAnonymousSubtree()) {
5736 return false;
5739 if (aNode->IsInNativeAnonymousSubtree()) {
5740 return aContent->GetClosestNativeAnonymousSubtreeRoot() ==
5741 aNode->GetClosestNativeAnonymousSubtreeRoot();
5744 // FIXME: This doesn't deal with disconnected nodes whatsoever, but it didn't
5745 // use to either. Maybe that's fine.
5746 return aNode->GetContainingShadow() == aContent->GetContainingShadow();
5749 /* static */
5750 bool nsContentUtils::IsInInteractiveHTMLContent(const Element* aElement,
5751 const Element* aStop) {
5752 const Element* element = aElement;
5753 while (element && element != aStop) {
5754 if (element->IsInteractiveHTMLContent()) {
5755 return true;
5757 element = element->GetFlattenedTreeParentElement();
5759 return false;
5762 /* static */
5763 void nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling) {
5764 IMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
5767 /* static */
5768 bool nsContentUtils::SchemeIs(nsIURI* aURI, const char* aScheme) {
5769 nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
5770 NS_ENSURE_TRUE(baseURI, false);
5771 return baseURI->SchemeIs(aScheme);
5774 bool nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal) {
5775 return aPrincipal && aPrincipal->GetIsExpandedPrincipal();
5778 bool nsContentUtils::IsSystemOrExpandedPrincipal(nsIPrincipal* aPrincipal) {
5779 return (aPrincipal && aPrincipal->IsSystemPrincipal()) ||
5780 IsExpandedPrincipal(aPrincipal);
5783 nsIPrincipal* nsContentUtils::GetSystemPrincipal() {
5784 MOZ_ASSERT(IsInitialized());
5785 return sSystemPrincipal;
5788 bool nsContentUtils::CombineResourcePrincipals(
5789 nsCOMPtr<nsIPrincipal>* aResourcePrincipal, nsIPrincipal* aExtraPrincipal) {
5790 if (!aExtraPrincipal) {
5791 return false;
5793 if (!*aResourcePrincipal) {
5794 *aResourcePrincipal = aExtraPrincipal;
5795 return true;
5797 if (*aResourcePrincipal == aExtraPrincipal) {
5798 return false;
5800 bool subsumes;
5801 if (NS_SUCCEEDED(
5802 (*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
5803 subsumes) {
5804 return false;
5806 *aResourcePrincipal = sSystemPrincipal;
5807 return true;
5810 /* static */
5811 void nsContentUtils::TriggerLink(nsIContent* aContent, nsIURI* aLinkURI,
5812 const nsString& aTargetSpec, bool aClick,
5813 bool aIsTrusted) {
5814 MOZ_ASSERT(aLinkURI, "No link URI");
5816 if (aContent->IsEditable() || !aContent->OwnerDoc()->LinkHandlingEnabled()) {
5817 return;
5820 nsCOMPtr<nsIDocShell> docShell = aContent->OwnerDoc()->GetDocShell();
5821 if (!docShell) {
5822 return;
5825 if (!aClick) {
5826 nsDocShell::Cast(docShell)->OnOverLink(aContent, aLinkURI, aTargetSpec);
5827 return;
5830 // Check that this page is allowed to load this URI.
5831 nsresult proceed = NS_OK;
5833 if (sSecurityManager) {
5834 uint32_t flag = static_cast<uint32_t>(nsIScriptSecurityManager::STANDARD);
5835 proceed = sSecurityManager->CheckLoadURIWithPrincipal(
5836 aContent->NodePrincipal(), aLinkURI, flag,
5837 aContent->OwnerDoc()->InnerWindowID());
5840 // Only pass off the click event if the script security manager says it's ok.
5841 // We need to rest aTargetSpec for forced downloads.
5842 if (NS_SUCCEEDED(proceed)) {
5843 // A link/area element with a download attribute is allowed to set
5844 // a pseudo Content-Disposition header.
5845 // For security reasons we only allow websites to declare same-origin
5846 // resources as downloadable. If this check fails we will just do the normal
5847 // thing (i.e. navigate to the resource).
5848 nsAutoString fileName;
5849 if ((!aContent->IsHTMLElement(nsGkAtoms::a) &&
5850 !aContent->IsHTMLElement(nsGkAtoms::area) &&
5851 !aContent->IsSVGElement(nsGkAtoms::a)) ||
5852 !aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::download,
5853 fileName) ||
5854 NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, true))) {
5855 fileName.SetIsVoid(true); // No actionable download attribute was found.
5858 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aContent->NodePrincipal();
5859 nsCOMPtr<nsIContentSecurityPolicy> csp = aContent->GetCsp();
5861 // Sanitize fileNames containing null characters by replacing them with
5862 // underscores.
5863 if (!fileName.IsVoid()) {
5864 fileName.ReplaceChar(char16_t(0), '_');
5866 nsDocShell::Cast(docShell)->OnLinkClick(
5867 aContent, aLinkURI, fileName.IsVoid() ? aTargetSpec : u""_ns, fileName,
5868 nullptr, nullptr, UserActivation::IsHandlingUserInput(), aIsTrusted,
5869 triggeringPrincipal, csp);
5873 /* static */
5874 void nsContentUtils::GetLinkLocation(Element* aElement,
5875 nsString& aLocationString) {
5876 nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
5877 if (hrefURI) {
5878 nsAutoCString specUTF8;
5879 nsresult rv = hrefURI->GetSpec(specUTF8);
5880 if (NS_SUCCEEDED(rv)) CopyUTF8toUTF16(specUTF8, aLocationString);
5884 /* static */
5885 nsIWidget* nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget) {
5886 if (!aWidget) return nullptr;
5888 return aWidget->GetTopLevelWidget();
5891 /* static */
5892 const nsDependentString nsContentUtils::GetLocalizedEllipsis() {
5893 static char16_t sBuf[4] = {0, 0, 0, 0};
5894 if (!sBuf[0]) {
5895 if (!SpoofLocaleEnglish()) {
5896 nsAutoString tmp;
5897 Preferences::GetLocalizedString("intl.ellipsis", tmp);
5898 uint32_t len =
5899 std::min(uint32_t(tmp.Length()), uint32_t(ArrayLength(sBuf) - 1));
5900 CopyUnicodeTo(tmp, 0, sBuf, len);
5902 if (!sBuf[0]) sBuf[0] = char16_t(0x2026);
5904 return nsDependentString(sBuf);
5907 /* static */
5908 void nsContentUtils::AddScriptBlocker() {
5909 MOZ_ASSERT(NS_IsMainThread());
5910 if (!sScriptBlockerCount) {
5911 MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
5912 "Should not already have a count");
5913 sRunnersCountAtFirstBlocker =
5914 sBlockedScriptRunners ? sBlockedScriptRunners->Length() : 0;
5916 ++sScriptBlockerCount;
5919 #ifdef DEBUG
5920 static bool sRemovingScriptBlockers = false;
5921 #endif
5923 /* static */
5924 void nsContentUtils::RemoveScriptBlocker() {
5925 MOZ_ASSERT(NS_IsMainThread());
5926 MOZ_ASSERT(!sRemovingScriptBlockers);
5927 NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
5928 --sScriptBlockerCount;
5929 if (sScriptBlockerCount) {
5930 return;
5933 if (!sBlockedScriptRunners) {
5934 return;
5937 uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
5938 uint32_t lastBlocker = sBlockedScriptRunners->Length();
5939 uint32_t originalFirstBlocker = firstBlocker;
5940 uint32_t blockersCount = lastBlocker - firstBlocker;
5941 sRunnersCountAtFirstBlocker = 0;
5942 NS_ASSERTION(firstBlocker <= lastBlocker, "bad sRunnersCountAtFirstBlocker");
5944 while (firstBlocker < lastBlocker) {
5945 nsCOMPtr<nsIRunnable> runnable;
5946 runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
5947 ++firstBlocker;
5949 // Calling the runnable can reenter us
5951 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
5952 runnable->Run();
5954 // So can dropping the reference to the runnable
5955 runnable = nullptr;
5957 NS_ASSERTION(sRunnersCountAtFirstBlocker == 0, "Bad count");
5958 NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
5960 #ifdef DEBUG
5961 AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
5962 sRemovingScriptBlockers = true;
5963 #endif
5964 sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
5967 /* static */
5968 already_AddRefed<nsPIDOMWindowOuter>
5969 nsContentUtils::GetMostRecentNonPBWindow() {
5970 nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
5972 nsCOMPtr<mozIDOMWindowProxy> window;
5973 wm->GetMostRecentNonPBWindow(u"navigator:browser", getter_AddRefs(window));
5974 nsCOMPtr<nsPIDOMWindowOuter> pwindow;
5975 pwindow = do_QueryInterface(window);
5977 return pwindow.forget();
5980 /* static */
5981 void nsContentUtils::WarnScriptWasIgnored(Document* aDocument) {
5982 nsAutoString msg;
5983 bool privateBrowsing = false;
5984 bool chromeContext = false;
5986 if (aDocument) {
5987 nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
5988 if (uri) {
5989 msg.Append(NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()));
5990 msg.AppendLiteral(" : ");
5992 privateBrowsing =
5993 !!aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
5994 chromeContext = aDocument->NodePrincipal()->IsSystemPrincipal();
5997 msg.AppendLiteral(
5998 "Unable to run script because scripts are blocked internally.");
5999 LogSimpleConsoleError(msg, "DOM"_ns, privateBrowsing, chromeContext);
6002 /* static */
6003 void nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable) {
6004 nsCOMPtr<nsIRunnable> runnable = aRunnable;
6005 if (!runnable) {
6006 return;
6009 if (sScriptBlockerCount) {
6010 sBlockedScriptRunners->AppendElement(runnable.forget());
6011 return;
6014 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
6015 runnable->Run();
6018 /* static */
6019 void nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) {
6020 nsCOMPtr<nsIRunnable> runnable = aRunnable;
6021 AddScriptRunner(runnable.forget());
6024 /* static */ bool nsContentUtils::IsSafeToRunScript() {
6025 MOZ_ASSERT(NS_IsMainThread(),
6026 "This static variable only makes sense on the main thread!");
6027 return sScriptBlockerCount == 0;
6030 /* static */
6031 void nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable) {
6032 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6033 CycleCollectedJSContext::Get()->RunInStableState(std::move(aRunnable));
6036 /* static */
6037 void nsContentUtils::AddPendingIDBTransaction(
6038 already_AddRefed<nsIRunnable> aTransaction) {
6039 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6040 CycleCollectedJSContext::Get()->AddPendingIDBTransaction(
6041 std::move(aTransaction));
6044 /* static */
6045 bool nsContentUtils::IsInStableOrMetaStableState() {
6046 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
6047 return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
6050 /* static */
6051 void nsContentUtils::HidePopupsInDocument(Document* aDocument) {
6052 RefPtr<nsXULPopupManager> pm = nsXULPopupManager::GetInstance();
6053 if (!pm || !aDocument) {
6054 return;
6056 nsCOMPtr<nsIDocShellTreeItem> docShellToHide = aDocument->GetDocShell();
6057 if (docShellToHide) {
6058 pm->HidePopupsInDocShell(docShellToHide);
6062 /* static */
6063 already_AddRefed<nsIDragSession> nsContentUtils::GetDragSession() {
6064 nsCOMPtr<nsIDragSession> dragSession;
6065 nsCOMPtr<nsIDragService> dragService =
6066 do_GetService("@mozilla.org/widget/dragservice;1");
6067 if (dragService) dragService->GetCurrentSession(getter_AddRefs(dragSession));
6068 return dragSession.forget();
6071 /* static */
6072 nsresult nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) {
6073 if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) {
6074 return NS_OK;
6077 // For dragstart events, the data transfer object is
6078 // created before the event fires, so it should already be set. For other
6079 // drag events, get the object from the drag session.
6080 NS_ASSERTION(aDragEvent->mMessage != eDragStart,
6081 "draggesture event created without a dataTransfer");
6083 nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
6084 NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
6086 RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
6087 if (!initialDataTransfer) {
6088 // A dataTransfer won't exist when a drag was started by some other
6089 // means, for instance calling the drag service directly, or a drag
6090 // from another application. In either case, a new dataTransfer should
6091 // be created that reflects the data.
6092 initialDataTransfer =
6093 new DataTransfer(aDragEvent->mTarget, aDragEvent->mMessage, true, -1);
6095 // now set it in the drag session so we don't need to create it again
6096 dragSession->SetDataTransfer(initialDataTransfer);
6099 bool isCrossDomainSubFrameDrop = false;
6100 if (aDragEvent->mMessage == eDrop) {
6101 isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
6104 // each event should use a clone of the original dataTransfer.
6105 initialDataTransfer->Clone(
6106 aDragEvent->mTarget, aDragEvent->mMessage, aDragEvent->mUserCancelled,
6107 isCrossDomainSubFrameDrop, getter_AddRefs(aDragEvent->mDataTransfer));
6108 if (NS_WARN_IF(!aDragEvent->mDataTransfer)) {
6109 return NS_ERROR_OUT_OF_MEMORY;
6112 // for the dragenter and dragover events, initialize the drop effect
6113 // from the drop action, which platform specific widget code sets before
6114 // the event is fired based on the keyboard state.
6115 if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
6116 uint32_t action;
6117 dragSession->GetDragAction(&action);
6118 uint32_t effectAllowed = aDragEvent->mDataTransfer->EffectAllowedInt();
6119 aDragEvent->mDataTransfer->SetDropEffectInt(
6120 FilterDropEffect(action, effectAllowed));
6121 } else if (aDragEvent->mMessage == eDrop ||
6122 aDragEvent->mMessage == eDragEnd) {
6123 // For the drop and dragend events, set the drop effect based on the
6124 // last value that the dropEffect had. This will have been set in
6125 // EventStateManager::PostHandleEvent for the last dragenter or
6126 // dragover event.
6127 aDragEvent->mDataTransfer->SetDropEffectInt(
6128 initialDataTransfer->DropEffectInt());
6131 return NS_OK;
6134 /* static */
6135 uint32_t nsContentUtils::FilterDropEffect(uint32_t aAction,
6136 uint32_t aEffectAllowed) {
6137 // It is possible for the drag action to include more than one action, but
6138 // the widget code which sets the action from the keyboard state should only
6139 // be including one. If multiple actions were set, we just consider them in
6140 // the following order:
6141 // copy, link, move
6142 if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
6143 aAction = nsIDragService::DRAGDROP_ACTION_COPY;
6144 else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
6145 aAction = nsIDragService::DRAGDROP_ACTION_LINK;
6146 else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
6147 aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
6149 // Filter the action based on the effectAllowed. If the effectAllowed
6150 // doesn't include the action, then that action cannot be done, so adjust
6151 // the action to something that is allowed. For a copy, adjust to move or
6152 // link. For a move, adjust to copy or link. For a link, adjust to move or
6153 // link. Otherwise, use none.
6154 if (aAction & aEffectAllowed ||
6155 aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
6156 return aAction;
6157 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
6158 return nsIDragService::DRAGDROP_ACTION_MOVE;
6159 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
6160 return nsIDragService::DRAGDROP_ACTION_COPY;
6161 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
6162 return nsIDragService::DRAGDROP_ACTION_LINK;
6163 return nsIDragService::DRAGDROP_ACTION_NONE;
6166 /* static */
6167 bool nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession,
6168 WidgetDragEvent* aDropEvent) {
6169 nsCOMPtr<nsIContent> target =
6170 nsIContent::FromEventTargetOrNull(aDropEvent->mOriginalTarget);
6171 if (!target) {
6172 return true;
6175 // Always allow dropping onto chrome shells.
6176 BrowsingContext* targetBC = target->OwnerDoc()->GetBrowsingContext();
6177 if (targetBC->IsChrome()) {
6178 return false;
6181 WindowContext* targetWC = target->OwnerDoc()->GetWindowContext();
6183 // If there is no source browsing context, then this is a drag from another
6184 // application, which should be allowed.
6185 RefPtr<WindowContext> sourceWC;
6186 aDragSession->GetSourceWindowContext(getter_AddRefs(sourceWC));
6187 if (sourceWC) {
6188 // Get each successive parent of the source document and compare it to
6189 // the drop document. If they match, then this is a drag from a child frame.
6190 for (sourceWC = sourceWC->GetParentWindowContext(); sourceWC;
6191 sourceWC = sourceWC->GetParentWindowContext()) {
6192 // If the source and the target match, then the drag started in a
6193 // descendant frame. If the source is discarded, err on the side of
6194 // caution and treat it as a subframe drag.
6195 if (sourceWC == targetWC || sourceWC->IsDiscarded()) {
6196 return true;
6201 return false;
6204 /* static */
6205 bool nsContentUtils::URIIsLocalFile(nsIURI* aURI) {
6206 bool isFile;
6207 nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
6209 // Important: we do NOT test the entire URI chain here!
6210 return util &&
6211 NS_SUCCEEDED(util->ProtocolHasFlags(
6212 aURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, &isFile)) &&
6213 isFile;
6216 /* static */
6217 JSContext* nsContentUtils::GetCurrentJSContext() {
6218 MOZ_ASSERT(IsInitialized());
6219 if (!IsJSAPIActive()) {
6220 return nullptr;
6222 return danger::GetJSContext();
6225 template <typename StringType, typename CharType>
6226 void _ASCIIToLowerInSitu(StringType& aStr) {
6227 CharType* iter = aStr.BeginWriting();
6228 CharType* end = aStr.EndWriting();
6229 MOZ_ASSERT(iter && end);
6231 while (iter != end) {
6232 CharType c = *iter;
6233 if (c >= 'A' && c <= 'Z') {
6234 *iter = c + ('a' - 'A');
6236 ++iter;
6240 /* static */
6241 void nsContentUtils::ASCIIToLower(nsAString& aStr) {
6242 return _ASCIIToLowerInSitu<nsAString, char16_t>(aStr);
6245 /* static */
6246 void nsContentUtils::ASCIIToLower(nsACString& aStr) {
6247 return _ASCIIToLowerInSitu<nsACString, char>(aStr);
6250 template <typename StringType, typename CharType>
6251 void _ASCIIToLowerCopy(const StringType& aSource, StringType& aDest) {
6252 uint32_t len = aSource.Length();
6253 aDest.SetLength(len);
6254 MOZ_ASSERT(aDest.Length() == len);
6256 CharType* dest = aDest.BeginWriting();
6257 MOZ_ASSERT(dest);
6259 const CharType* iter = aSource.BeginReading();
6260 const CharType* end = aSource.EndReading();
6261 while (iter != end) {
6262 CharType c = *iter;
6263 *dest = (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
6264 ++iter;
6265 ++dest;
6269 /* static */
6270 void nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest) {
6271 return _ASCIIToLowerCopy<nsAString, char16_t>(aSource, aDest);
6274 /* static */
6275 void nsContentUtils::ASCIIToLower(const nsACString& aSource,
6276 nsACString& aDest) {
6277 return _ASCIIToLowerCopy<nsACString, char>(aSource, aDest);
6280 template <typename StringType, typename CharType>
6281 void _ASCIIToUpperInSitu(StringType& aStr) {
6282 CharType* iter = aStr.BeginWriting();
6283 CharType* end = aStr.EndWriting();
6284 MOZ_ASSERT(iter && end);
6286 while (iter != end) {
6287 CharType c = *iter;
6288 if (c >= 'a' && c <= 'z') {
6289 *iter = c + ('A' - 'a');
6291 ++iter;
6295 /* static */
6296 void nsContentUtils::ASCIIToUpper(nsAString& aStr) {
6297 return _ASCIIToUpperInSitu<nsAString, char16_t>(aStr);
6300 /* static */
6301 void nsContentUtils::ASCIIToUpper(nsACString& aStr) {
6302 return _ASCIIToUpperInSitu<nsACString, char>(aStr);
6305 template <typename StringType, typename CharType>
6306 void _ASCIIToUpperCopy(const StringType& aSource, StringType& aDest) {
6307 uint32_t len = aSource.Length();
6308 aDest.SetLength(len);
6309 MOZ_ASSERT(aDest.Length() == len);
6311 CharType* dest = aDest.BeginWriting();
6312 MOZ_ASSERT(dest);
6314 const CharType* iter = aSource.BeginReading();
6315 const CharType* end = aSource.EndReading();
6316 while (iter != end) {
6317 CharType c = *iter;
6318 *dest = (c >= 'a' && c <= 'z') ? c + ('A' - 'a') : c;
6319 ++iter;
6320 ++dest;
6324 /* static */
6325 void nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest) {
6326 return _ASCIIToUpperCopy<nsAString, char16_t>(aSource, aDest);
6329 /* static */
6330 void nsContentUtils::ASCIIToUpper(const nsACString& aSource,
6331 nsACString& aDest) {
6332 return _ASCIIToUpperCopy<nsACString, char>(aSource, aDest);
6335 /* static */
6336 bool nsContentUtils::EqualsIgnoreASCIICase(nsAtom* aAtom1, nsAtom* aAtom2) {
6337 if (aAtom1 == aAtom2) {
6338 return true;
6341 // If both are ascii lowercase already, we know that the slow comparison
6342 // below is going to return false.
6343 if (aAtom1->IsAsciiLowercase() && aAtom2->IsAsciiLowercase()) {
6344 return false;
6347 return EqualsIgnoreASCIICase(nsDependentAtomString(aAtom1),
6348 nsDependentAtomString(aAtom2));
6351 /* static */
6352 bool nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
6353 const nsAString& aStr2) {
6354 uint32_t len = aStr1.Length();
6355 if (len != aStr2.Length()) {
6356 return false;
6359 const char16_t* str1 = aStr1.BeginReading();
6360 const char16_t* str2 = aStr2.BeginReading();
6361 const char16_t* end = str1 + len;
6363 while (str1 < end) {
6364 char16_t c1 = *str1++;
6365 char16_t c2 = *str2++;
6367 // First check if any bits other than the 0x0020 differs
6368 if ((c1 ^ c2) & 0xffdf) {
6369 return false;
6372 // We know they can only differ in the 0x0020 bit.
6373 // Likely the two chars are the same, so check that first
6374 if (c1 != c2) {
6375 // They do differ, but since it's only in the 0x0020 bit, check if it's
6376 // the same ascii char, but just differing in case
6377 char16_t c1Upper = c1 & 0xffdf;
6378 if (!('A' <= c1Upper && c1Upper <= 'Z')) {
6379 return false;
6384 return true;
6387 /* static */
6388 bool nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr) {
6389 const char16_t* iter = aStr.BeginReading();
6390 const char16_t* end = aStr.EndReading();
6391 while (iter != end) {
6392 char16_t c = *iter;
6393 if (c >= 'A' && c <= 'Z') {
6394 return true;
6396 ++iter;
6399 return false;
6402 /* static */
6403 nsIInterfaceRequestor* nsContentUtils::SameOriginChecker() {
6404 if (!sSameOriginChecker) {
6405 sSameOriginChecker = new SameOriginCheckerImpl();
6406 NS_ADDREF(sSameOriginChecker);
6408 return sSameOriginChecker;
6411 /* static */
6412 nsresult nsContentUtils::CheckSameOrigin(nsIChannel* aOldChannel,
6413 nsIChannel* aNewChannel) {
6414 if (!nsContentUtils::GetSecurityManager()) return NS_ERROR_NOT_AVAILABLE;
6416 nsCOMPtr<nsIPrincipal> oldPrincipal;
6417 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
6418 aOldChannel, getter_AddRefs(oldPrincipal));
6420 nsCOMPtr<nsIURI> newURI;
6421 aNewChannel->GetURI(getter_AddRefs(newURI));
6422 nsCOMPtr<nsIURI> newOriginalURI;
6423 aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
6425 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
6427 nsresult rv = oldPrincipal->CheckMayLoad(newURI, false);
6428 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
6429 rv = oldPrincipal->CheckMayLoad(newOriginalURI, false);
6432 return rv;
6435 NS_IMPL_ISUPPORTS(SameOriginCheckerImpl, nsIChannelEventSink,
6436 nsIInterfaceRequestor)
6438 NS_IMETHODIMP
6439 SameOriginCheckerImpl::AsyncOnChannelRedirect(
6440 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
6441 nsIAsyncVerifyRedirectCallback* cb) {
6442 MOZ_ASSERT(aNewChannel, "Redirecting to null channel?");
6444 nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
6445 if (NS_SUCCEEDED(rv)) {
6446 cb->OnRedirectVerifyCallback(NS_OK);
6449 return rv;
6452 NS_IMETHODIMP
6453 SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult) {
6454 return QueryInterface(aIID, aResult);
6457 /* static */
6458 nsresult nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsACString& aOrigin) {
6459 MOZ_ASSERT(aURI, "missing uri");
6461 // For Blob URI, the path is the URL of the owning page.
6462 if (aURI->SchemeIs(BLOBURI_SCHEME)) {
6463 nsAutoCString path;
6464 nsresult rv = aURI->GetPathQueryRef(path);
6465 NS_ENSURE_SUCCESS(rv, rv);
6467 nsCOMPtr<nsIURI> uri;
6468 rv = NS_NewURI(getter_AddRefs(uri), path);
6469 if (NS_FAILED(rv)) {
6470 aOrigin.AssignLiteral("null");
6471 return NS_OK;
6474 return GetASCIIOrigin(uri, aOrigin);
6477 aOrigin.Truncate();
6479 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
6480 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
6482 nsAutoCString host;
6483 nsresult rv = uri->GetAsciiHost(host);
6485 if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
6486 nsAutoCString userPass;
6487 uri->GetUserPass(userPass);
6489 nsAutoCString prePath;
6490 if (!userPass.IsEmpty()) {
6491 rv = NS_MutateURI(uri).SetUserPass(""_ns).Finalize(uri);
6492 NS_ENSURE_SUCCESS(rv, rv);
6495 rv = uri->GetPrePath(prePath);
6496 NS_ENSURE_SUCCESS(rv, rv);
6498 aOrigin = prePath;
6499 } else {
6500 aOrigin.AssignLiteral("null");
6503 return NS_OK;
6506 /* static */
6507 nsresult nsContentUtils::GetUTFOrigin(nsIPrincipal* aPrincipal,
6508 nsAString& aOrigin) {
6509 MOZ_ASSERT(aPrincipal, "missing principal");
6511 aOrigin.Truncate();
6512 nsAutoCString asciiOrigin;
6514 nsresult rv = aPrincipal->GetAsciiOrigin(asciiOrigin);
6515 if (NS_FAILED(rv)) {
6516 asciiOrigin.AssignLiteral("null");
6519 CopyUTF8toUTF16(asciiOrigin, aOrigin);
6520 return NS_OK;
6523 /* static */
6524 nsresult nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsAString& aOrigin) {
6525 MOZ_ASSERT(aURI, "missing uri");
6526 nsresult rv;
6528 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
6529 // Check if either URI has a special origin.
6530 nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin =
6531 do_QueryInterface(aURI);
6532 if (uriWithSpecialOrigin) {
6533 nsCOMPtr<nsIURI> origin;
6534 rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin));
6535 NS_ENSURE_SUCCESS(rv, rv);
6537 return GetUTFOrigin(origin, aOrigin);
6539 #endif
6541 nsAutoCString asciiOrigin;
6542 rv = GetASCIIOrigin(aURI, asciiOrigin);
6543 NS_ENSURE_SUCCESS(rv, rv);
6545 CopyUTF8toUTF16(asciiOrigin, aOrigin);
6546 return NS_OK;
6549 /* static */
6550 bool nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal,
6551 nsIChannel* aChannel,
6552 bool aAllowIfInheritsPrincipal) {
6553 nsCOMPtr<nsIURI> channelURI;
6554 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
6555 NS_ENSURE_SUCCESS(rv, false);
6557 return NS_SUCCEEDED(
6558 aPrincipal->CheckMayLoad(channelURI, aAllowIfInheritsPrincipal));
6561 /* static */
6562 bool nsContentUtils::CanAccessNativeAnon() {
6563 return LegacyIsCallerChromeOrNativeCode();
6566 /* static */
6567 nsresult nsContentUtils::DispatchXULCommand(nsIContent* aTarget, bool aTrusted,
6568 Event* aSourceEvent,
6569 PresShell* aPresShell, bool aCtrl,
6570 bool aAlt, bool aShift, bool aMeta,
6571 uint16_t aInputSource,
6572 int16_t aButton) {
6573 NS_ENSURE_STATE(aTarget);
6574 Document* doc = aTarget->OwnerDoc();
6575 nsPresContext* presContext = doc->GetPresContext();
6577 RefPtr<XULCommandEvent> xulCommand =
6578 new XULCommandEvent(doc, presContext, nullptr);
6579 xulCommand->InitCommandEvent(u"command"_ns, true, true,
6580 nsGlobalWindowInner::Cast(doc->GetInnerWindow()),
6581 0, aCtrl, aAlt, aShift, aMeta, aButton,
6582 aSourceEvent, aInputSource, IgnoreErrors());
6584 if (aPresShell) {
6585 nsEventStatus status = nsEventStatus_eIgnore;
6586 return aPresShell->HandleDOMEventWithTarget(aTarget, xulCommand, &status);
6589 ErrorResult rv;
6590 aTarget->DispatchEvent(*xulCommand, rv);
6591 return rv.StealNSResult();
6594 // static
6595 nsresult nsContentUtils::WrapNative(JSContext* cx, nsISupports* native,
6596 nsWrapperCache* cache, const nsIID* aIID,
6597 JS::MutableHandle<JS::Value> vp,
6598 bool aAllowWrapping) {
6599 MOZ_ASSERT(cx == GetCurrentJSContext());
6601 if (!native) {
6602 vp.setNull();
6604 return NS_OK;
6607 JSObject* wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
6608 if (wrapper) {
6609 return NS_OK;
6612 NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
6614 if (!NS_IsMainThread()) {
6615 MOZ_CRASH();
6618 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
6619 nsresult rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
6620 aAllowWrapping, vp);
6621 return rv;
6624 nsresult nsContentUtils::CreateArrayBuffer(JSContext* aCx,
6625 const nsACString& aData,
6626 JSObject** aResult) {
6627 if (!aCx) {
6628 return NS_ERROR_FAILURE;
6631 size_t dataLen = aData.Length();
6632 *aResult = JS::NewArrayBuffer(aCx, dataLen);
6633 if (!*aResult) {
6634 return NS_ERROR_FAILURE;
6637 if (dataLen > 0) {
6638 NS_ASSERTION(JS::IsArrayBufferObject(*aResult), "What happened?");
6639 JS::AutoCheckCannotGC nogc;
6640 bool isShared;
6641 memcpy(JS::GetArrayBufferData(*aResult, &isShared, nogc),
6642 aData.BeginReading(), dataLen);
6643 MOZ_ASSERT(!isShared);
6646 return NS_OK;
6649 void nsContentUtils::StripNullChars(const nsAString& aInStr,
6650 nsAString& aOutStr) {
6651 // In common cases where we don't have nulls in the
6652 // string we can simple simply bypass the checking code.
6653 int32_t firstNullPos = aInStr.FindChar('\0');
6654 if (firstNullPos == kNotFound) {
6655 aOutStr.Assign(aInStr);
6656 return;
6659 aOutStr.SetCapacity(aInStr.Length() - 1);
6660 nsAString::const_iterator start, end;
6661 aInStr.BeginReading(start);
6662 aInStr.EndReading(end);
6663 while (start != end) {
6664 if (*start != '\0') aOutStr.Append(*start);
6665 ++start;
6669 struct ClassMatchingInfo {
6670 AtomArray mClasses;
6671 nsCaseTreatment mCaseTreatment;
6674 // static
6675 bool nsContentUtils::MatchClassNames(Element* aElement, int32_t aNamespaceID,
6676 nsAtom* aAtom, void* aData) {
6677 // We can't match if there are no class names
6678 const nsAttrValue* classAttr = aElement->GetClasses();
6679 if (!classAttr) {
6680 return false;
6683 // need to match *all* of the classes
6684 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6685 uint32_t length = info->mClasses.Length();
6686 if (!length) {
6687 // If we actually had no classes, don't match.
6688 return false;
6690 uint32_t i;
6691 for (i = 0; i < length; ++i) {
6692 if (!classAttr->Contains(info->mClasses[i], info->mCaseTreatment)) {
6693 return false;
6697 return true;
6700 // static
6701 void nsContentUtils::DestroyClassNameArray(void* aData) {
6702 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6703 delete info;
6706 // static
6707 void* nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
6708 const nsString* aClasses) {
6709 nsAttrValue attrValue;
6710 attrValue.ParseAtomArray(*aClasses);
6711 // nsAttrValue::Equals is sensitive to order, so we'll send an array
6712 auto* info = new ClassMatchingInfo;
6713 if (attrValue.Type() == nsAttrValue::eAtomArray) {
6714 info->mClasses = std::move(attrValue.GetAtomArrayValue()->mArray);
6715 } else if (attrValue.Type() == nsAttrValue::eAtom) {
6716 info->mClasses.AppendElement(attrValue.GetAtomValue());
6719 info->mCaseTreatment =
6720 aRootNode->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks
6721 ? eIgnoreCase
6722 : eCaseMatters;
6723 return info;
6726 // static
6727 bool nsContentUtils::IsFocusedContent(const nsIContent* aContent) {
6728 nsFocusManager* fm = nsFocusManager::GetFocusManager();
6730 return fm && fm->GetFocusedElement() == aContent;
6733 bool nsContentUtils::HasScrollgrab(nsIContent* aContent) {
6734 // If we ever standardize this feature we'll want to hook this up properly
6735 // again. For now we're removing all the DOM-side code related to it but
6736 // leaving the layout and APZ handling for it in place.
6737 return false;
6740 void nsContentUtils::FlushLayoutForTree(nsPIDOMWindowOuter* aWindow) {
6741 if (!aWindow) {
6742 return;
6745 // Note that because FlushPendingNotifications flushes parents, this
6746 // is O(N^2) in docshell tree depth. However, the docshell tree is
6747 // usually pretty shallow.
6749 if (RefPtr<Document> doc = aWindow->GetDoc()) {
6750 doc->FlushPendingNotifications(FlushType::Layout);
6753 if (nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell()) {
6754 int32_t i = 0, i_end;
6755 docShell->GetInProcessChildCount(&i_end);
6756 for (; i < i_end; ++i) {
6757 nsCOMPtr<nsIDocShellTreeItem> item;
6758 if (docShell->GetInProcessChildAt(i, getter_AddRefs(item)) == NS_OK &&
6759 item) {
6760 if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
6761 FlushLayoutForTree(win);
6768 void nsContentUtils::RemoveNewlines(nsString& aString) { aString.StripCRLF(); }
6770 void nsContentUtils::PlatformToDOMLineBreaks(nsString& aString) {
6771 if (!PlatformToDOMLineBreaks(aString, fallible)) {
6772 aString.AllocFailed(aString.Length());
6776 bool nsContentUtils::PlatformToDOMLineBreaks(nsString& aString,
6777 const fallible_t& aFallible) {
6778 if (aString.FindChar(char16_t('\r')) != -1) {
6779 // Windows linebreaks: Map CRLF to LF:
6780 if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
6781 return false;
6784 // Mac linebreaks: Map any remaining CR to LF:
6785 if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
6786 return false;
6790 return true;
6793 void nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
6794 nsAString& aResultString) {
6795 MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
6797 uint32_t stringLen = NS_strlen(static_cast<char16_t*>(aBuf->Data()));
6799 // SANITY CHECK: In case the nsStringBuffer isn't correctly
6800 // null-terminated, let's clamp its length using the allocated size, to be
6801 // sure the resulting string doesn't sample past the end of the the buffer.
6802 // (Note that StorageSize() is in units of bytes, so we have to convert that
6803 // to units of PRUnichars, and subtract 1 for the null-terminator.)
6804 uint32_t allocStringLen = (aBuf->StorageSize() / sizeof(char16_t)) - 1;
6805 MOZ_ASSERT(stringLen <= allocStringLen,
6806 "string buffer lacks null terminator!");
6807 stringLen = std::min(stringLen, allocStringLen);
6809 aBuf->ToString(stringLen, aResultString);
6812 already_AddRefed<nsContentList> nsContentUtils::GetElementsByClassName(
6813 nsINode* aRootNode, const nsAString& aClasses) {
6814 MOZ_ASSERT(aRootNode, "Must have root node");
6816 return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(
6817 aRootNode, MatchClassNames, DestroyClassNameArray, AllocClassMatchingInfo,
6818 aClasses);
6821 PresShell* nsContentUtils::FindPresShellForDocument(const Document* aDocument) {
6822 const Document* doc = aDocument;
6823 Document* displayDoc = doc->GetDisplayDocument();
6824 if (displayDoc) {
6825 doc = displayDoc;
6828 PresShell* presShell = doc->GetPresShell();
6829 if (presShell) {
6830 return presShell;
6833 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
6834 while (docShellTreeItem) {
6835 // We may be in a display:none subdocument, or we may not have a presshell
6836 // created yet.
6837 // Walk the docshell tree to find the nearest container that has a
6838 // presshell, and return that.
6839 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
6840 if (PresShell* presShell = docShell->GetPresShell()) {
6841 return presShell;
6843 nsCOMPtr<nsIDocShellTreeItem> parent;
6844 docShellTreeItem->GetInProcessParent(getter_AddRefs(parent));
6845 docShellTreeItem = parent;
6848 return nullptr;
6851 /* static */
6852 nsPresContext* nsContentUtils::FindPresContextForDocument(
6853 const Document* aDocument) {
6854 if (PresShell* presShell = FindPresShellForDocument(aDocument)) {
6855 return presShell->GetPresContext();
6857 return nullptr;
6860 nsIWidget* nsContentUtils::WidgetForDocument(const Document* aDocument) {
6861 PresShell* presShell = FindPresShellForDocument(aDocument);
6862 if (!presShell) {
6863 return nullptr;
6865 nsViewManager* vm = presShell->GetViewManager();
6866 if (!vm) {
6867 return nullptr;
6869 nsView* rootView = vm->GetRootView();
6870 if (!rootView) {
6871 return nullptr;
6873 nsView* displayRoot = nsViewManager::GetDisplayRootFor(rootView);
6874 if (!displayRoot) {
6875 return nullptr;
6877 return displayRoot->GetNearestWidget(nullptr);
6880 nsIWidget* nsContentUtils::WidgetForContent(const nsIContent* aContent) {
6881 nsIFrame* frame = aContent->GetPrimaryFrame();
6882 if (frame) {
6883 frame = nsLayoutUtils::GetDisplayRootFrame(frame);
6885 nsView* view = frame->GetView();
6886 if (view) {
6887 return view->GetWidget();
6891 return nullptr;
6894 WindowRenderer* nsContentUtils::WindowRendererForContent(
6895 const nsIContent* aContent) {
6896 nsIWidget* widget = nsContentUtils::WidgetForContent(aContent);
6897 if (widget) {
6898 return widget->GetWindowRenderer();
6901 return nullptr;
6904 WindowRenderer* nsContentUtils::WindowRendererForDocument(
6905 const Document* aDoc) {
6906 nsIWidget* widget = nsContentUtils::WidgetForDocument(aDoc);
6907 if (widget) {
6908 return widget->GetWindowRenderer();
6911 return nullptr;
6914 bool nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal) {
6915 if (!aPrincipal) {
6916 return false;
6919 if (aPrincipal->IsSystemPrincipal()) {
6920 return true;
6923 return (StaticPrefs::dom_allow_XUL_XBL_for_file() &&
6924 aPrincipal->SchemeIs("file")) ||
6925 IsSitePermAllow(aPrincipal, "allowXULXBL"_ns);
6928 bool nsContentUtils::IsPDFJSEnabled() {
6929 nsCOMPtr<nsIStreamConverter> conv = do_CreateInstance(
6930 "@mozilla.org/streamconv;1?from=application/pdf&to=text/html");
6931 return conv;
6934 bool nsContentUtils::IsPDFJS(nsIPrincipal* aPrincipal) {
6935 if (!aPrincipal) {
6936 return false;
6938 nsAutoCString spec;
6939 nsresult rv = aPrincipal->GetAsciiSpec(spec);
6940 NS_ENSURE_SUCCESS(rv, false);
6941 return spec.EqualsLiteral("resource://pdf.js/web/viewer.html");
6944 bool nsContentUtils::IsSystemOrPDFJS(JSContext* aCx, JSObject*) {
6945 nsIPrincipal* principal = SubjectPrincipal(aCx);
6946 return principal && (principal->IsSystemPrincipal() || IsPDFJS(principal));
6949 already_AddRefed<nsIDocumentLoaderFactory>
6950 nsContentUtils::FindInternalContentViewer(const nsACString& aType,
6951 ContentViewerType* aLoaderType) {
6952 if (aLoaderType) {
6953 *aLoaderType = TYPE_UNSUPPORTED;
6956 // one helper factory, please
6957 nsCOMPtr<nsICategoryManager> catMan(
6958 do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
6959 if (!catMan) return nullptr;
6961 nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
6963 nsCString contractID;
6964 nsresult rv =
6965 catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, contractID);
6966 if (NS_SUCCEEDED(rv)) {
6967 docFactory = do_GetService(contractID.get());
6968 if (docFactory && aLoaderType) {
6969 if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
6970 *aLoaderType = TYPE_CONTENT;
6971 else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
6972 *aLoaderType = TYPE_FALLBACK;
6973 else
6974 *aLoaderType = TYPE_UNKNOWN;
6976 return docFactory.forget();
6979 if (DecoderTraits::IsSupportedInVideoDocument(aType)) {
6980 docFactory =
6981 do_GetService("@mozilla.org/content/document-loader-factory;1");
6982 if (docFactory && aLoaderType) {
6983 *aLoaderType = TYPE_CONTENT;
6985 return docFactory.forget();
6988 return nullptr;
6991 static void ReportPatternCompileFailure(nsAString& aPattern,
6992 const Document* aDocument,
6993 JS::MutableHandle<JS::Value> error,
6994 JSContext* cx) {
6995 JS::AutoSaveExceptionState savedExc(cx);
6996 JS::Rooted<JSObject*> exnObj(cx, &error.toObject());
6997 JS::Rooted<JS::Value> messageVal(cx);
6998 if (!JS_GetProperty(cx, exnObj, "message", &messageVal)) {
6999 return;
7001 JS::Rooted<JSString*> messageStr(cx, messageVal.toString());
7002 MOZ_ASSERT(messageStr);
7004 AutoTArray<nsString, 2> strings;
7005 strings.AppendElement(aPattern);
7006 if (!AssignJSString(cx, *strings.AppendElement(), messageStr)) {
7007 return;
7010 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns,
7011 aDocument, nsContentUtils::eDOM_PROPERTIES,
7012 "PatternAttributeCompileFailure", strings);
7013 savedExc.drop();
7016 // static
7017 Maybe<bool> nsContentUtils::IsPatternMatching(nsAString& aValue,
7018 nsAString& aPattern,
7019 const Document* aDocument,
7020 bool aHasMultiple) {
7021 NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
7023 // The fact that we're using a JS regexp under the hood should not be visible
7024 // to things like window onerror handlers, so we don't initialize our JSAPI
7025 // with the document's window (which may not exist anyway).
7026 AutoJSAPI jsapi;
7027 jsapi.Init();
7028 JSContext* cx = jsapi.cx();
7029 AutoDisableJSInterruptCallback disabler(cx);
7031 // We can use the junk scope here, because we're just using it for regexp
7032 // evaluation, not actual script execution, and we disable statics so that the
7033 // evaluation does not interact with the execution global.
7034 JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
7036 // Check if the pattern by itself is valid first, and not that it only becomes
7037 // valid once we add ^(?: and )$.
7038 JS::Rooted<JS::Value> error(cx);
7039 if (!JS::CheckRegExpSyntax(
7040 cx, static_cast<char16_t*>(aPattern.BeginWriting()),
7041 aPattern.Length(), JS::RegExpFlag::Unicode, &error)) {
7042 return Nothing();
7045 if (!error.isUndefined()) {
7046 ReportPatternCompileFailure(aPattern, aDocument, &error, cx);
7047 return Some(true);
7050 // The pattern has to match the entire value.
7051 aPattern.InsertLiteral(u"^(?:", 0);
7052 aPattern.AppendLiteral(")$");
7054 JS::Rooted<JSObject*> re(
7056 JS::NewUCRegExpObject(cx, static_cast<char16_t*>(aPattern.BeginWriting()),
7057 aPattern.Length(), JS::RegExpFlag::Unicode));
7058 if (!re) {
7059 return Nothing();
7062 JS::Rooted<JS::Value> rval(cx, JS::NullValue());
7063 if (!aHasMultiple) {
7064 size_t idx = 0;
7065 if (!JS::ExecuteRegExpNoStatics(
7066 cx, re, static_cast<char16_t*>(aValue.BeginWriting()),
7067 aValue.Length(), &idx, true, &rval)) {
7068 return Nothing();
7070 return Some(!rval.isNull());
7073 HTMLSplitOnSpacesTokenizer tokenizer(aValue, ',');
7074 while (tokenizer.hasMoreTokens()) {
7075 const nsAString& value = tokenizer.nextToken();
7076 size_t idx = 0;
7077 if (!JS::ExecuteRegExpNoStatics(
7078 cx, re, static_cast<const char16_t*>(value.BeginReading()),
7079 value.Length(), &idx, true, &rval)) {
7080 return Nothing();
7082 if (rval.isNull()) {
7083 return Some(false);
7086 return Some(true);
7089 // static
7090 nsresult nsContentUtils::URIInheritsSecurityContext(nsIURI* aURI,
7091 bool* aResult) {
7092 // Note: about:blank URIs do NOT inherit the security context from the
7093 // current document, which is what this function tests for...
7094 return NS_URIChainHasFlags(
7095 aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, aResult);
7098 // static
7099 bool nsContentUtils::ChannelShouldInheritPrincipal(
7100 nsIPrincipal* aLoadingPrincipal, nsIURI* aURI, bool aInheritForAboutBlank,
7101 bool aForceInherit) {
7102 MOZ_ASSERT(aLoadingPrincipal,
7103 "Can not check inheritance without a principal");
7105 // Only tell the channel to inherit if it can't provide its own security
7106 // context.
7108 // XXX: If this is ever changed, check all callers for what owners
7109 // they're passing in. In particular, see the code and
7110 // comments in nsDocShell::LoadURI where we fall back on
7111 // inheriting the owner if called from chrome. That would be
7112 // very wrong if this code changed anything but channels that
7113 // can't provide their own security context!
7115 // If aForceInherit is true, we will inherit, even for a channel that
7116 // can provide its own security context. This is used for srcdoc loads.
7117 bool inherit = aForceInherit;
7118 if (!inherit) {
7119 bool uriInherits;
7120 // We expect URIInheritsSecurityContext to return success for an
7121 // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
7122 // This condition needs to match the one in nsDocShell::InternalLoad where
7123 // we're checking for things that will use the owner.
7124 inherit =
7125 (NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &uriInherits)) &&
7126 (uriInherits || (aInheritForAboutBlank && NS_IsAboutBlank(aURI)))) ||
7128 // file: uri special-casing
7130 // If this is a file: load opened from another file: then it may need
7131 // to inherit the owner from the referrer so they can script each other.
7132 // If we don't set the owner explicitly then each file: gets an owner
7133 // based on its own codebase later.
7135 (URIIsLocalFile(aURI) &&
7136 NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, false)) &&
7137 // One more check here. CheckMayLoad will always return true for the
7138 // system principal, but we do NOT want to inherit in that case.
7139 !aLoadingPrincipal->IsSystemPrincipal());
7141 return inherit;
7144 /* static */
7145 bool nsContentUtils::IsCutCopyAllowed(Document* aDocument,
7146 nsIPrincipal& aSubjectPrincipal) {
7147 if (StaticPrefs::dom_allow_cut_copy() && aDocument &&
7148 aDocument->HasValidTransientUserGestureActivation()) {
7149 return true;
7152 return PrincipalHasPermission(aSubjectPrincipal, nsGkAtoms::clipboardWrite);
7155 /* static */
7156 bool nsContentUtils::HaveEqualPrincipals(Document* aDoc1, Document* aDoc2) {
7157 if (!aDoc1 || !aDoc2) {
7158 return false;
7160 bool principalsEqual = false;
7161 aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
7162 return principalsEqual;
7165 /* static */
7166 bool nsContentUtils::HasPluginWithUncontrolledEventDispatch(
7167 nsIContent* aContent) {
7168 return false;
7171 /* static */
7172 void nsContentUtils::FireMutationEventsForDirectParsing(
7173 Document* aDoc, nsIContent* aDest, int32_t aOldChildCount) {
7174 // Fire mutation events. Optimize for the case when there are no listeners
7175 int32_t newChildCount = aDest->GetChildCount();
7176 if (newChildCount && nsContentUtils::HasMutationListeners(
7177 aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
7178 AutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
7179 NS_ASSERTION(newChildCount - aOldChildCount >= 0,
7180 "What, some unexpected dom mutation has happened?");
7181 childNodes.SetCapacity(newChildCount - aOldChildCount);
7182 for (nsIContent* child = aDest->GetFirstChild(); child;
7183 child = child->GetNextSibling()) {
7184 childNodes.AppendElement(child);
7186 FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
7190 /* static */
7191 const Document* nsContentUtils::GetInProcessSubtreeRootDocument(
7192 const Document* aDoc) {
7193 if (!aDoc) {
7194 return nullptr;
7196 const Document* doc = aDoc;
7197 while (doc->GetInProcessParentDocument()) {
7198 doc = doc->GetInProcessParentDocument();
7200 return doc;
7203 // static
7204 int32_t nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
7205 int32_t aOffset) {
7206 // The structure of the anonymous frames within a text control frame is
7207 // an optional block frame, followed by an optional br frame.
7209 // If the offset frame has a child, then this frame is the block which
7210 // has the text frames (containing the content) as its children. This will
7211 // be the case if we click to the right of any of the text frames, or at the
7212 // bottom of the text area.
7213 nsIFrame* firstChild = aOffsetFrame->PrincipalChildList().FirstChild();
7214 if (firstChild) {
7215 // In this case, the passed-in offset is incorrect, and we want the length
7216 // of the entire content in the text control frame.
7217 return firstChild->GetContent()->Length();
7220 if (aOffsetFrame->GetPrevSibling() && !aOffsetFrame->GetNextSibling()) {
7221 // In this case, we're actually within the last frame, which is a br
7222 // frame. Our offset should therefore be the length of the first child of
7223 // our parent.
7224 int32_t aOutOffset = aOffsetFrame->GetParent()
7225 ->PrincipalChildList()
7226 .FirstChild()
7227 ->GetContent()
7228 ->Length();
7229 return aOutOffset;
7232 // Otherwise, we're within one of the text frames, in which case our offset
7233 // has already been correctly calculated.
7234 return aOffset;
7237 // static
7238 void nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
7239 Element* aRoot,
7240 uint32_t& aOutStartOffset,
7241 uint32_t& aOutEndOffset) {
7242 MOZ_ASSERT(aSelection && aRoot);
7244 // We don't care which end of this selection is anchor and which is focus. In
7245 // fact, we explicitly want to know which is the _start_ and which is the
7246 // _end_, not anchor vs focus.
7247 const nsRange* range = aSelection->GetAnchorFocusRange();
7248 if (!range) {
7249 // Nothing selected
7250 aOutStartOffset = aOutEndOffset = 0;
7251 return;
7254 // All the node pointers here are raw pointers for performance. We shouldn't
7255 // be doing anything in this function that invalidates the node tree.
7256 nsINode* startContainer = range->GetStartContainer();
7257 uint32_t startOffset = range->StartOffset();
7258 nsINode* endContainer = range->GetEndContainer();
7259 uint32_t endOffset = range->EndOffset();
7261 // We have at most two children, consisting of an optional text node followed
7262 // by an optional <br>.
7263 NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
7264 nsIContent* firstChild = aRoot->GetFirstChild();
7265 #ifdef DEBUG
7266 nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
7267 NS_ASSERTION(startContainer == aRoot || startContainer == firstChild ||
7268 startContainer == lastChild,
7269 "Unexpected startContainer");
7270 NS_ASSERTION(endContainer == aRoot || endContainer == firstChild ||
7271 endContainer == lastChild,
7272 "Unexpected endContainer");
7273 // firstChild is either text or a <br> (hence an element).
7274 MOZ_ASSERT_IF(firstChild, firstChild->IsText() || firstChild->IsElement());
7275 #endif
7276 // Testing IsElement() is faster than testing IsNodeOfType(), since it's
7277 // non-virtual.
7278 if (!firstChild || firstChild->IsElement()) {
7279 // No text node, so everything is 0
7280 startOffset = endOffset = 0;
7281 } else {
7282 // First child is text. If the start/end is already in the text node,
7283 // or the start of the root node, no change needed. If it's in the root
7284 // node but not the start, or in the trailing <br>, we need to set the
7285 // offset to the end.
7286 if ((startContainer == aRoot && startOffset != 0) ||
7287 (startContainer != aRoot && startContainer != firstChild)) {
7288 startOffset = firstChild->Length();
7290 if ((endContainer == aRoot && endOffset != 0) ||
7291 (endContainer != aRoot && endContainer != firstChild)) {
7292 endOffset = firstChild->Length();
7296 MOZ_ASSERT(startOffset <= endOffset);
7297 aOutStartOffset = startOffset;
7298 aOutEndOffset = endOffset;
7301 // static
7302 HTMLEditor* nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext) {
7303 if (!aPresContext) {
7304 return nullptr;
7306 return GetHTMLEditor(aPresContext->GetDocShell());
7309 // static
7310 HTMLEditor* nsContentUtils::GetHTMLEditor(nsDocShell* aDocShell) {
7311 bool isEditable;
7312 if (!aDocShell || NS_FAILED(aDocShell->GetEditable(&isEditable)) ||
7313 !isEditable) {
7314 return nullptr;
7316 return aDocShell->GetHTMLEditor();
7319 // static
7320 EditorBase* nsContentUtils::GetActiveEditor(nsPresContext* aPresContext) {
7321 if (!aPresContext) {
7322 return nullptr;
7325 return GetActiveEditor(aPresContext->Document()->GetWindow());
7328 // static
7329 EditorBase* nsContentUtils::GetActiveEditor(nsPIDOMWindowOuter* aWindow) {
7330 if (!aWindow || !aWindow->GetExtantDoc()) {
7331 return nullptr;
7334 // If it's in designMode, nobody can have focus. Therefore, the HTMLEditor
7335 // handles all events. I.e., it's focused editor in this case.
7336 if (aWindow->GetExtantDoc()->IsInDesignMode()) {
7337 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7340 // If focused element is associated with TextEditor, it must be <input>
7341 // element or <textarea> element. Let's return it even if it's in a
7342 // contenteditable element.
7343 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
7344 if (Element* focusedElement = nsFocusManager::GetFocusedDescendant(
7345 aWindow, nsFocusManager::SearchRange::eOnlyCurrentWindow,
7346 getter_AddRefs(focusedWindow))) {
7347 if (TextEditor* textEditor = focusedElement->GetTextEditorInternal()) {
7348 return textEditor;
7352 // Otherwise, HTMLEditor may handle inputs even non-editable element has
7353 // focus or nobody has focus.
7354 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7357 // static
7358 TextEditor* nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(
7359 const nsIContent* aAnonymousContent) {
7360 if (!aAnonymousContent) {
7361 return nullptr;
7363 nsIContent* parent = aAnonymousContent->FindFirstNonChromeOnlyAccessContent();
7364 if (!parent || parent == aAnonymousContent) {
7365 return nullptr;
7367 if (HTMLInputElement* inputElement =
7368 HTMLInputElement::FromNodeOrNull(parent)) {
7369 return inputElement->GetTextEditorWithoutCreation();
7371 if (HTMLTextAreaElement* textareaElement =
7372 HTMLTextAreaElement::FromNodeOrNull(parent)) {
7373 return textareaElement->GetTextEditorWithoutCreation();
7375 return nullptr;
7378 // static
7379 bool nsContentUtils::IsNodeInEditableRegion(nsINode* aNode) {
7380 while (aNode) {
7381 if (aNode->IsEditable()) {
7382 return true;
7384 aNode = aNode->GetParent();
7386 return false;
7389 // static
7390 bool nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader,
7391 const nsACString& aValue) {
7392 if (IsForbiddenSystemRequestHeader(aHeader)) {
7393 return true;
7396 if ((nsContentUtils::IsOverrideMethodHeader(aHeader) &&
7397 nsContentUtils::ContainsForbiddenMethod(aValue))) {
7398 return true;
7401 if (StringBeginsWith(aHeader, "proxy-"_ns,
7402 nsCaseInsensitiveCStringComparator) ||
7403 StringBeginsWith(aHeader, "sec-"_ns,
7404 nsCaseInsensitiveCStringComparator)) {
7405 return true;
7408 return false;
7411 // static
7412 bool nsContentUtils::IsForbiddenSystemRequestHeader(const nsACString& aHeader) {
7413 static const char* kInvalidHeaders[] = {"accept-charset",
7414 "accept-encoding",
7415 "access-control-request-headers",
7416 "access-control-request-method",
7417 "connection",
7418 "content-length",
7419 "cookie",
7420 "cookie2",
7421 "date",
7422 "dnt",
7423 "expect",
7424 "host",
7425 "keep-alive",
7426 "origin",
7427 "referer",
7428 "set-cookie",
7429 "te",
7430 "trailer",
7431 "transfer-encoding",
7432 "upgrade",
7433 "via"};
7434 for (auto& kInvalidHeader : kInvalidHeaders) {
7435 if (aHeader.LowerCaseEqualsASCII(kInvalidHeader)) {
7436 return true;
7439 return false;
7442 // static
7443 bool nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader) {
7444 return (aHeader.LowerCaseEqualsASCII("set-cookie") ||
7445 aHeader.LowerCaseEqualsASCII("set-cookie2"));
7448 // static
7449 bool nsContentUtils::IsOverrideMethodHeader(const nsACString& headerName) {
7450 return headerName.EqualsIgnoreCase("x-http-method-override") ||
7451 headerName.EqualsIgnoreCase("x-http-method") ||
7452 headerName.EqualsIgnoreCase("x-method-override");
7455 // static
7456 bool nsContentUtils::ContainsForbiddenMethod(const nsACString& headerValue) {
7457 bool hasInsecureMethod = false;
7458 nsCCharSeparatedTokenizer tokenizer(headerValue, ',');
7460 while (tokenizer.hasMoreTokens()) {
7461 const nsDependentCSubstring& value = tokenizer.nextToken();
7463 if (value.EqualsIgnoreCase("connect") || value.EqualsIgnoreCase("trace") ||
7464 value.EqualsIgnoreCase("track")) {
7465 hasInsecureMethod = true;
7466 break;
7470 return hasInsecureMethod;
7473 // static
7474 bool nsContentUtils::IsCorsUnsafeRequestHeaderValue(
7475 const nsACString& aHeaderValue) {
7476 const char* cur = aHeaderValue.BeginReading();
7477 const char* end = aHeaderValue.EndReading();
7479 while (cur != end) {
7480 // Implementation of
7481 // https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte Is less
7482 // than a space but not a horizontal tab
7483 if ((*cur < ' ' && *cur != '\t') || *cur == '"' || *cur == '(' ||
7484 *cur == ')' || *cur == ':' || *cur == '<' || *cur == '>' ||
7485 *cur == '?' || *cur == '@' || *cur == '[' || *cur == '\\' ||
7486 *cur == ']' || *cur == '{' || *cur == '}' ||
7487 *cur == 0x7F) { // 0x75 is DEL
7488 return true;
7490 cur++;
7492 return false;
7495 // static
7496 bool nsContentUtils::IsAllowedNonCorsAccept(const nsACString& aHeaderValue) {
7497 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7498 return false;
7500 return true;
7503 // static
7504 bool nsContentUtils::IsAllowedNonCorsContentType(
7505 const nsACString& aHeaderValue) {
7506 nsAutoCString contentType;
7507 nsAutoCString unused;
7509 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7510 return false;
7513 nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
7514 if (NS_FAILED(rv)) {
7515 return false;
7518 return contentType.LowerCaseEqualsLiteral("text/plain") ||
7519 contentType.LowerCaseEqualsLiteral(
7520 "application/x-www-form-urlencoded") ||
7521 contentType.LowerCaseEqualsLiteral("multipart/form-data");
7524 // static
7525 bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
7526 const char* cur = aHeaderValue.BeginReading();
7527 const char* end = aHeaderValue.EndReading();
7529 while (cur != end) {
7530 if ((*cur >= '0' && *cur <= '9') || (*cur >= 'A' && *cur <= 'Z') ||
7531 (*cur >= 'a' && *cur <= 'z') || *cur == ' ' || *cur == '*' ||
7532 *cur == ',' || *cur == '-' || *cur == '.' || *cur == ';' ||
7533 *cur == '=') {
7534 cur++;
7535 continue;
7537 return false;
7539 return true;
7542 // static
7543 bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
7544 const nsACString& aValue) {
7545 // see https://fetch.spec.whatwg.org/#cors-safelisted-request-header
7546 if (aValue.Length() > 128) {
7547 return false;
7549 return (aName.LowerCaseEqualsLiteral("accept") &&
7550 nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
7551 (aName.LowerCaseEqualsLiteral("accept-language") &&
7552 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7553 (aName.LowerCaseEqualsLiteral("content-language") &&
7554 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7555 (aName.LowerCaseEqualsLiteral("content-type") &&
7556 nsContentUtils::IsAllowedNonCorsContentType(aValue));
7559 mozilla::LogModule* nsContentUtils::ResistFingerprintingLog() {
7560 return gResistFingerprintingLog;
7562 mozilla::LogModule* nsContentUtils::DOMDumpLog() { return sDOMDumpLog; }
7564 bool nsContentUtils::GetNodeTextContent(const nsINode* aNode, bool aDeep,
7565 nsAString& aResult,
7566 const fallible_t& aFallible) {
7567 aResult.Truncate();
7568 return AppendNodeTextContent(aNode, aDeep, aResult, aFallible);
7571 void nsContentUtils::GetNodeTextContent(const nsINode* aNode, bool aDeep,
7572 nsAString& aResult) {
7573 if (!GetNodeTextContent(aNode, aDeep, aResult, fallible)) {
7574 NS_ABORT_OOM(0); // Unfortunately we don't know the allocation size
7578 void nsContentUtils::DestroyMatchString(void* aData) {
7579 if (aData) {
7580 nsString* matchString = static_cast<nsString*>(aData);
7581 delete matchString;
7585 bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
7586 // Table ordered from most to least likely JS MIME types.
7587 static const char* jsTypes[] = {"text/javascript",
7588 "text/ecmascript",
7589 "application/javascript",
7590 "application/ecmascript",
7591 "application/x-javascript",
7592 "application/x-ecmascript",
7593 "text/javascript1.0",
7594 "text/javascript1.1",
7595 "text/javascript1.2",
7596 "text/javascript1.3",
7597 "text/javascript1.4",
7598 "text/javascript1.5",
7599 "text/jscript",
7600 "text/livescript",
7601 "text/x-ecmascript",
7602 "text/x-javascript",
7603 nullptr};
7605 for (uint32_t i = 0; jsTypes[i]; ++i) {
7606 if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
7607 return true;
7611 return false;
7614 bool nsContentUtils::PrefetchPreloadEnabled(nsIDocShell* aDocShell) {
7616 // SECURITY CHECK: disable prefetching and preloading from mailnews!
7618 // walk up the docshell tree to see if any containing
7619 // docshell are of type MAIL.
7622 if (!aDocShell) {
7623 return false;
7626 nsCOMPtr<nsIDocShell> docshell = aDocShell;
7627 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7629 do {
7630 auto appType = docshell->GetAppType();
7631 if (appType == nsIDocShell::APP_TYPE_MAIL) {
7632 return false; // do not prefetch, preload, preconnect from mailnews
7635 docshell->GetInProcessParent(getter_AddRefs(parentItem));
7636 if (parentItem) {
7637 docshell = do_QueryInterface(parentItem);
7638 if (!docshell) {
7639 NS_ERROR("cannot get a docshell from a treeItem!");
7640 return false;
7643 } while (parentItem);
7645 return true;
7648 uint64_t nsContentUtils::GetInnerWindowID(nsIRequest* aRequest) {
7649 // can't do anything if there's no nsIRequest!
7650 if (!aRequest) {
7651 return 0;
7654 nsCOMPtr<nsILoadGroup> loadGroup;
7655 nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
7657 if (NS_FAILED(rv) || !loadGroup) {
7658 return 0;
7661 return GetInnerWindowID(loadGroup);
7664 uint64_t nsContentUtils::GetInnerWindowID(nsILoadGroup* aLoadGroup) {
7665 if (!aLoadGroup) {
7666 return 0;
7669 nsCOMPtr<nsIInterfaceRequestor> callbacks;
7670 nsresult rv = aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
7671 if (NS_FAILED(rv) || !callbacks) {
7672 return 0;
7675 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
7676 if (!loadContext) {
7677 return 0;
7680 nsCOMPtr<mozIDOMWindowProxy> window;
7681 rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
7682 if (NS_FAILED(rv) || !window) {
7683 return 0;
7686 auto* pwindow = nsPIDOMWindowOuter::From(window);
7687 if (!pwindow) {
7688 return 0;
7691 nsPIDOMWindowInner* inner = pwindow->GetCurrentInnerWindow();
7692 return inner ? inner->WindowID() : 0;
7695 // static
7696 void nsContentUtils::MaybeFixIPv6Host(nsACString& aHost) {
7697 if (aHost.FindChar(':') != -1) { // Escape IPv6 address
7698 MOZ_ASSERT(!aHost.Length() ||
7699 (aHost[0] != '[' && aHost[aHost.Length() - 1] != ']'));
7700 aHost.Insert('[', 0);
7701 aHost.Append(']');
7705 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7706 nsACString& aHost) {
7707 aHost.Truncate();
7708 nsresult rv = aURI->GetHost(aHost);
7709 if (NS_FAILED(rv)) { // Some URIs do not have a host
7710 return rv;
7713 MaybeFixIPv6Host(aHost);
7715 return NS_OK;
7718 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7719 nsAString& aHost) {
7720 nsAutoCString hostname;
7721 nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname);
7722 if (NS_FAILED(rv)) {
7723 return rv;
7725 CopyUTF8toUTF16(hostname, aHost);
7726 return NS_OK;
7729 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIPrincipal* aPrincipal,
7730 nsACString& aHost) {
7731 nsresult rv = aPrincipal->GetAsciiHost(aHost);
7732 if (NS_FAILED(rv)) { // Some URIs do not have a host
7733 return rv;
7736 MaybeFixIPv6Host(aHost);
7737 return NS_OK;
7740 CallState nsContentUtils::CallOnAllRemoteChildren(
7741 MessageBroadcaster* aManager,
7742 const std::function<CallState(BrowserParent*)>& aCallback) {
7743 uint32_t browserChildCount = aManager->ChildCount();
7744 for (uint32_t j = 0; j < browserChildCount; ++j) {
7745 RefPtr<MessageListenerManager> childMM = aManager->GetChildAt(j);
7746 if (!childMM) {
7747 continue;
7750 RefPtr<MessageBroadcaster> nonLeafMM = MessageBroadcaster::From(childMM);
7751 if (nonLeafMM) {
7752 if (CallOnAllRemoteChildren(nonLeafMM, aCallback) == CallState::Stop) {
7753 return CallState::Stop;
7755 continue;
7758 mozilla::dom::ipc::MessageManagerCallback* cb = childMM->GetCallback();
7759 if (cb) {
7760 nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
7761 BrowserParent* remote = BrowserParent::GetFrom(fl);
7762 if (remote && aCallback) {
7763 if (aCallback(remote) == CallState::Stop) {
7764 return CallState::Stop;
7770 return CallState::Continue;
7773 void nsContentUtils::CallOnAllRemoteChildren(
7774 nsPIDOMWindowOuter* aWindow,
7775 const std::function<CallState(BrowserParent*)>& aCallback) {
7776 nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(aWindow);
7777 if (window->IsChromeWindow()) {
7778 RefPtr<MessageBroadcaster> windowMM = window->GetMessageManager();
7779 if (windowMM) {
7780 CallOnAllRemoteChildren(windowMM, aCallback);
7785 bool nsContentUtils::IPCDataTransferItemHasKnownFlavor(
7786 const IPCDataTransferItem& aItem) {
7787 // Unknown types are converted to kCustomTypesMime.
7788 // FIXME(bug 1776879) text/plain is converted to text/unicode still.
7789 if (aItem.flavor().EqualsASCII(kCustomTypesMime) ||
7790 aItem.flavor().EqualsASCII(kUnicodeMime)) {
7791 return true;
7794 for (const char* format : DataTransfer::kKnownFormats) {
7795 if (aItem.flavor().EqualsASCII(format)) {
7796 return true;
7800 return false;
7803 nsresult nsContentUtils::IPCTransferableToTransferable(
7804 const IPCDataTransfer& aDataTransfer, bool aAddDataFlavor,
7805 nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
7806 nsresult rv;
7807 const nsTArray<IPCDataTransferItem>& items = aDataTransfer.items();
7808 for (const auto& item : items) {
7809 if (aFilterUnknownFlavors && !IPCDataTransferItemHasKnownFlavor(item)) {
7810 NS_WARNING(
7811 "Ignoring unknown flavor in "
7812 "nsContentUtils::IPCTransferableToTransferable");
7813 continue;
7816 if (aAddDataFlavor) {
7817 aTransferable->AddDataFlavor(item.flavor().get());
7820 nsCOMPtr<nsISupports> transferData;
7821 switch (item.data().type()) {
7822 case IPCDataTransferData::TIPCDataTransferString: {
7823 const auto& data = item.data().get_IPCDataTransferString();
7824 nsCOMPtr<nsISupportsString> dataWrapper =
7825 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
7826 NS_ENSURE_SUCCESS(rv, rv);
7827 rv = dataWrapper->SetData(nsDependentSubstring(
7828 reinterpret_cast<const char16_t*>(data.data().Data()),
7829 data.data().Size() / sizeof(char16_t)));
7830 NS_ENSURE_SUCCESS(rv, rv);
7831 transferData = dataWrapper;
7832 break;
7834 case IPCDataTransferData::TIPCDataTransferCString: {
7835 const auto& data = item.data().get_IPCDataTransferCString();
7836 nsCOMPtr<nsISupportsCString> dataWrapper =
7837 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
7838 NS_ENSURE_SUCCESS(rv, rv);
7839 rv = dataWrapper->SetData(nsDependentCSubstring(
7840 reinterpret_cast<const char*>(data.data().Data()),
7841 data.data().Size()));
7842 NS_ENSURE_SUCCESS(rv, rv);
7843 transferData = dataWrapper;
7844 break;
7846 case IPCDataTransferData::TIPCDataTransferInputStream: {
7847 const auto& data = item.data().get_IPCDataTransferInputStream();
7848 nsCOMPtr<nsIInputStream> stream;
7849 rv = NS_NewByteInputStream(getter_AddRefs(stream),
7850 AsChars(data.data().AsSpan()),
7851 NS_ASSIGNMENT_COPY);
7852 NS_ENSURE_SUCCESS(rv, rv);
7853 transferData = stream.forget();
7854 break;
7856 case IPCDataTransferData::TIPCDataTransferImageContainer: {
7857 const auto& data = item.data().get_IPCDataTransferImageContainer();
7858 nsCOMPtr<imgIContainer> container;
7859 rv = DeserializeDataTransferImageContainer(data,
7860 getter_AddRefs(container));
7861 NS_ENSURE_SUCCESS(rv, rv);
7862 transferData = container;
7863 break;
7865 case IPCDataTransferData::TIPCDataTransferBlob: {
7866 const auto& data = item.data().get_IPCDataTransferBlob();
7867 transferData = IPCBlobUtils::Deserialize(data.blob());
7868 break;
7870 case IPCDataTransferData::T__None:
7871 MOZ_ASSERT_UNREACHABLE();
7872 return NS_ERROR_FAILURE;
7875 rv = aTransferable->SetTransferData(item.flavor().get(), transferData);
7876 NS_ENSURE_SUCCESS(rv, rv);
7878 return NS_OK;
7881 nsresult nsContentUtils::IPCTransferableToTransferable(
7882 const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
7883 nsIPrincipal* aRequestingPrincipal,
7884 const nsContentPolicyType& aContentPolicyType, bool aAddDataFlavor,
7885 nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
7886 aTransferable->SetIsPrivateData(aIsPrivateData);
7888 nsresult rv = IPCTransferableToTransferable(
7889 aDataTransfer, aAddDataFlavor, aTransferable, aFilterUnknownFlavors);
7890 NS_ENSURE_SUCCESS(rv, rv);
7892 aTransferable->SetRequestingPrincipal(aRequestingPrincipal);
7893 aTransferable->SetContentPolicyType(aContentPolicyType);
7894 return NS_OK;
7897 nsresult nsContentUtils::IPCTransferableItemToVariant(
7898 const IPCDataTransferItem& aDataTransferItem,
7899 nsIWritableVariant* aVariant) {
7900 MOZ_ASSERT(aVariant);
7902 switch (aDataTransferItem.data().type()) {
7903 case IPCDataTransferData::TIPCDataTransferString: {
7904 const auto& data = aDataTransferItem.data().get_IPCDataTransferString();
7905 return aVariant->SetAsAString(nsDependentSubstring(
7906 reinterpret_cast<const char16_t*>(data.data().Data()),
7907 data.data().Size() / sizeof(char16_t)));
7909 case IPCDataTransferData::TIPCDataTransferCString: {
7910 const auto& data = aDataTransferItem.data().get_IPCDataTransferCString();
7911 return aVariant->SetAsACString(nsDependentCSubstring(
7912 reinterpret_cast<const char*>(data.data().Data()),
7913 data.data().Size()));
7915 case IPCDataTransferData::TIPCDataTransferInputStream: {
7916 const auto& data =
7917 aDataTransferItem.data().get_IPCDataTransferInputStream();
7918 nsCOMPtr<nsIInputStream> stream;
7919 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
7920 AsChars(data.data().AsSpan()),
7921 NS_ASSIGNMENT_COPY);
7922 NS_ENSURE_SUCCESS(rv, rv);
7923 return aVariant->SetAsISupports(stream);
7925 case IPCDataTransferData::TIPCDataTransferImageContainer: {
7926 const auto& data =
7927 aDataTransferItem.data().get_IPCDataTransferImageContainer();
7928 nsCOMPtr<imgIContainer> container;
7929 nsresult rv = DeserializeDataTransferImageContainer(
7930 data, getter_AddRefs(container));
7931 NS_ENSURE_SUCCESS(rv, rv);
7932 return aVariant->SetAsISupports(container);
7934 case IPCDataTransferData::TIPCDataTransferBlob: {
7935 const auto& data = aDataTransferItem.data().get_IPCDataTransferBlob();
7936 RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(data.blob());
7937 return aVariant->SetAsISupports(blobImpl);
7939 case IPCDataTransferData::T__None:
7940 break;
7943 MOZ_ASSERT_UNREACHABLE();
7944 return NS_ERROR_UNEXPECTED;
7947 void nsContentUtils::TransferablesToIPCTransferables(
7948 nsIArray* aTransferables, nsTArray<IPCDataTransfer>& aIPC,
7949 bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
7950 mozilla::dom::ContentParent* aParent) {
7951 aIPC.Clear();
7952 if (aTransferables) {
7953 uint32_t transferableCount = 0;
7954 aTransferables->GetLength(&transferableCount);
7955 for (uint32_t i = 0; i < transferableCount; ++i) {
7956 IPCDataTransfer* dt = aIPC.AppendElement();
7957 nsCOMPtr<nsITransferable> transferable =
7958 do_QueryElementAt(aTransferables, i);
7959 TransferableToIPCTransferable(transferable, dt, aInSyncMessage, aChild,
7960 aParent);
7965 nsresult nsContentUtils::CalculateBufferSizeForImage(
7966 const uint32_t& aStride, const IntSize& aImageSize,
7967 const SurfaceFormat& aFormat, size_t* aMaxBufferSize,
7968 size_t* aUsedBufferSize) {
7969 CheckedInt32 requiredBytes =
7970 CheckedInt32(aStride) * CheckedInt32(aImageSize.height);
7972 CheckedInt32 usedBytes =
7973 requiredBytes - aStride +
7974 (CheckedInt32(aImageSize.width) * BytesPerPixel(aFormat));
7975 if (!usedBytes.isValid()) {
7976 return NS_ERROR_FAILURE;
7979 MOZ_ASSERT(requiredBytes.isValid(), "usedBytes valid but not required?");
7980 *aMaxBufferSize = requiredBytes.value();
7981 *aUsedBufferSize = usedBytes.value();
7982 return NS_OK;
7985 static already_AddRefed<DataSourceSurface> BigBufferToDataSurface(
7986 BigBuffer& aData, uint32_t aStride, const IntSize& aImageSize,
7987 SurfaceFormat aFormat) {
7988 if (!aData.Size() || !aImageSize.width || !aImageSize.height) {
7989 return nullptr;
7992 // Validate shared memory buffer size
7993 size_t imageBufLen = 0;
7994 size_t maxBufLen = 0;
7995 if (NS_FAILED(nsContentUtils::CalculateBufferSizeForImage(
7996 aStride, aImageSize, aFormat, &maxBufLen, &imageBufLen))) {
7997 return nullptr;
7999 if (imageBufLen > aData.Size()) {
8000 return nullptr;
8002 return CreateDataSourceSurfaceFromData(aImageSize, aFormat, aData.Data(),
8003 aStride);
8006 nsresult nsContentUtils::DeserializeDataTransferImageContainer(
8007 const IPCDataTransferImageContainer& aData, imgIContainer** aContainer) {
8008 const IntSize size(aData.width(), aData.height());
8009 size_t maxBufferSize = 0;
8010 size_t usedBufferSize = 0;
8011 nsresult rv = CalculateBufferSizeForImage(
8012 aData.stride(), size, aData.format(), &maxBufferSize, &usedBufferSize);
8013 NS_ENSURE_SUCCESS(rv, rv);
8014 if (usedBufferSize > aData.data().Size()) {
8015 return NS_ERROR_FAILURE;
8017 RefPtr<DataSourceSurface> surface =
8018 CreateDataSourceSurfaceFromData(size, aData.format(), aData.data().Data(),
8019 static_cast<int32_t>(aData.stride()));
8020 if (!surface) {
8021 return NS_ERROR_FAILURE;
8023 RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size);
8024 nsCOMPtr<imgIContainer> imageContainer =
8025 image::ImageOps::CreateFromDrawable(drawable);
8026 imageContainer.forget(aContainer);
8028 return NS_OK;
8031 bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
8032 return aFlavor.EqualsLiteral(kNativeImageMime) ||
8033 aFlavor.EqualsLiteral(kJPEGImageMime) ||
8034 aFlavor.EqualsLiteral(kJPGImageMime) ||
8035 aFlavor.EqualsLiteral(kPNGImageMime) ||
8036 aFlavor.EqualsLiteral(kGIFImageMime);
8039 // FIXME: This can probably be removed once bug 1783240 lands, as `nsString`
8040 // will be implicitly serialized in shmem when sent over IPDL directly.
8041 static IPCDataTransferString AsIPCDataTransferString(
8042 Span<const char16_t> aInput) {
8043 return IPCDataTransferString{BigBuffer(AsBytes(aInput))};
8046 // FIXME: This can probably be removed once bug 1783240 lands, as `nsCString`
8047 // will be implicitly serialized in shmem when sent over IPDL directly.
8048 static IPCDataTransferCString AsIPCDataTransferCString(
8049 Span<const char> aInput) {
8050 return IPCDataTransferCString{BigBuffer(AsBytes(aInput))};
8053 void nsContentUtils::TransferableToIPCTransferable(
8054 nsITransferable* aTransferable, IPCDataTransfer* aIPCDataTransfer,
8055 bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
8056 mozilla::dom::ContentParent* aParent) {
8057 MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
8059 if (aTransferable) {
8060 nsTArray<nsCString> flavorList;
8061 aTransferable->FlavorsTransferableCanExport(flavorList);
8063 for (uint32_t j = 0; j < flavorList.Length(); ++j) {
8064 nsCString& flavorStr = flavorList[j];
8065 if (!flavorStr.Length()) {
8066 continue;
8069 nsCOMPtr<nsISupports> data;
8070 nsresult rv =
8071 aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data));
8073 if (NS_FAILED(rv) || !data) {
8074 if (aInSyncMessage) {
8075 // Can't do anything.
8076 // FIXME: This shouldn't be the case anymore!
8077 continue;
8080 // This is a hack to support kFilePromiseMime.
8081 // On Windows there just needs to be an entry for it,
8082 // and for OSX we need to create
8083 // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
8084 if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
8085 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
8086 item->flavor() = flavorStr;
8087 item->data() =
8088 AsIPCDataTransferString(NS_ConvertUTF8toUTF16(flavorStr));
8089 continue;
8092 // Empty element, transfer only the flavor
8093 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
8094 item->flavor() = flavorStr;
8095 item->data() = AsIPCDataTransferString(EmptyString());
8096 continue;
8099 // We need to handle nsIInputStream before nsISupportsCString, otherwise
8100 // nsStringInputStream would be converted into a wrong type.
8101 if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
8102 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
8103 item->flavor() = flavorStr;
8104 nsCString imageData;
8105 DebugOnly<nsresult> rv =
8106 NS_ConsumeStream(stream, UINT32_MAX, imageData);
8107 MOZ_ASSERT(
8108 rv != NS_BASE_STREAM_WOULD_BLOCK,
8109 "cannot use async input streams in nsITransferable right now");
8110 // FIXME: This can probably be simplified once bug 1783240 lands, as
8111 // `nsCString` will be implicitly serialized in shmem when sent over
8112 // IPDL directly.
8113 item->data() =
8114 IPCDataTransferInputStream(BigBuffer(AsBytes(Span(imageData))));
8115 continue;
8118 if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(data)) {
8119 nsAutoString dataAsString;
8120 MOZ_ALWAYS_SUCCEEDS(text->GetData(dataAsString));
8122 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
8123 item->flavor() = flavorStr;
8124 item->data() = AsIPCDataTransferString(dataAsString);
8125 continue;
8128 if (nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data)) {
8129 nsAutoCString dataAsString;
8130 MOZ_ALWAYS_SUCCEEDS(ctext->GetData(dataAsString));
8132 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
8133 item->flavor() = flavorStr;
8134 item->data() = AsIPCDataTransferCString(dataAsString);
8135 continue;
8138 if (nsCOMPtr<imgIContainer> image = do_QueryInterface(data)) {
8139 // Images to be placed on the clipboard are imgIContainers.
8140 RefPtr<mozilla::gfx::SourceSurface> surface = image->GetFrame(
8141 imgIContainer::FRAME_CURRENT,
8142 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
8143 if (!surface) {
8144 continue;
8146 RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
8147 surface->GetDataSurface();
8148 if (!dataSurface) {
8149 continue;
8151 size_t length;
8152 int32_t stride;
8153 Maybe<BigBuffer> surfaceData =
8154 GetSurfaceData(*dataSurface, &length, &stride);
8156 if (surfaceData.isNothing()) {
8157 continue;
8160 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
8161 item->flavor() = flavorStr;
8163 mozilla::gfx::IntSize size = dataSurface->GetSize();
8164 item->data() = IPCDataTransferImageContainer(
8165 std::move(*surfaceData), size.width, size.height, stride,
8166 dataSurface->GetFormat());
8167 continue;
8170 // Otherwise, handle this as a file.
8171 nsCOMPtr<BlobImpl> blobImpl;
8172 if (nsCOMPtr<nsIFile> file = do_QueryInterface(data)) {
8173 if (aParent) {
8174 bool isDir = false;
8175 if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
8176 nsAutoString path;
8177 if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
8178 continue;
8181 RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
8182 fss->GrantAccessToContentProcess(aParent->ChildID(), path);
8186 blobImpl = new FileBlobImpl(file);
8188 IgnoredErrorResult rv;
8190 // Ensure that file data is cached no that the content process
8191 // has this data available to it when passed over:
8192 blobImpl->GetSize(rv);
8193 if (NS_WARN_IF(rv.Failed())) {
8194 continue;
8197 blobImpl->GetLastModified(rv);
8198 if (NS_WARN_IF(rv.Failed())) {
8199 continue;
8201 } else {
8202 if (aInSyncMessage) {
8203 // Can't do anything.
8204 // FIXME: This shouldn't be the case anymore!
8205 continue;
8208 blobImpl = do_QueryInterface(data);
8211 if (blobImpl) {
8212 // If we failed to create the blob actor, then this blob probably
8213 // can't get the file size for the underlying file, ignore it for
8214 // now. TODO pass this through anyway.
8215 IPCBlob ipcBlob;
8216 nsresult rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);
8217 if (NS_WARN_IF(NS_FAILED(rv))) {
8218 continue;
8221 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
8222 item->flavor() = flavorStr;
8223 item->data() = IPCDataTransferBlob(ipcBlob);
8229 Maybe<BigBuffer> nsContentUtils::GetSurfaceData(DataSourceSurface& aSurface,
8230 size_t* aLength,
8231 int32_t* aStride) {
8232 mozilla::gfx::DataSourceSurface::MappedSurface map;
8233 if (!aSurface.Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map)) {
8234 return Nothing();
8237 size_t bufLen = 0;
8238 size_t maxBufLen = 0;
8239 nsresult rv = nsContentUtils::CalculateBufferSizeForImage(
8240 map.mStride, aSurface.GetSize(), aSurface.GetFormat(), &maxBufLen,
8241 &bufLen);
8242 if (NS_FAILED(rv)) {
8243 aSurface.Unmap();
8244 return Nothing();
8247 BigBuffer surfaceData(maxBufLen);
8248 memcpy(surfaceData.Data(), map.mData, bufLen);
8249 memset(surfaceData.Data() + bufLen, 0, maxBufLen - bufLen);
8251 *aLength = maxBufLen;
8252 *aStride = map.mStride;
8254 aSurface.Unmap();
8255 return Some(std::move(surfaceData));
8258 Maybe<IPCImage> nsContentUtils::SurfaceToIPCImage(DataSourceSurface& aSurface) {
8259 size_t len = 0;
8260 int32_t stride = 0;
8261 auto mem = GetSurfaceData(aSurface, &len, &stride);
8262 if (!mem) {
8263 return Nothing();
8265 return Some(IPCImage{std::move(*mem), uint32_t(stride), aSurface.GetFormat(),
8266 ImageIntSize::FromUnknownSize(aSurface.GetSize())});
8269 already_AddRefed<DataSourceSurface> nsContentUtils::IPCImageToSurface(
8270 IPCImage&& aImage) {
8271 return BigBufferToDataSurface(aImage.data(), aImage.stride(),
8272 aImage.size().ToUnknownSize(), aImage.format());
8275 Modifiers nsContentUtils::GetWidgetModifiers(int32_t aModifiers) {
8276 Modifiers result = 0;
8277 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SHIFT) {
8278 result |= mozilla::MODIFIER_SHIFT;
8280 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CONTROL) {
8281 result |= mozilla::MODIFIER_CONTROL;
8283 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALT) {
8284 result |= mozilla::MODIFIER_ALT;
8286 if (aModifiers & nsIDOMWindowUtils::MODIFIER_META) {
8287 result |= mozilla::MODIFIER_META;
8289 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALTGRAPH) {
8290 result |= mozilla::MODIFIER_ALTGRAPH;
8292 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CAPSLOCK) {
8293 result |= mozilla::MODIFIER_CAPSLOCK;
8295 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FN) {
8296 result |= mozilla::MODIFIER_FN;
8298 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FNLOCK) {
8299 result |= mozilla::MODIFIER_FNLOCK;
8301 if (aModifiers & nsIDOMWindowUtils::MODIFIER_NUMLOCK) {
8302 result |= mozilla::MODIFIER_NUMLOCK;
8304 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SCROLLLOCK) {
8305 result |= mozilla::MODIFIER_SCROLLLOCK;
8307 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOL) {
8308 result |= mozilla::MODIFIER_SYMBOL;
8310 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK) {
8311 result |= mozilla::MODIFIER_SYMBOLLOCK;
8313 if (aModifiers & nsIDOMWindowUtils::MODIFIER_OS) {
8314 result |= mozilla::MODIFIER_OS;
8316 return result;
8319 nsIWidget* nsContentUtils::GetWidget(PresShell* aPresShell, nsPoint* aOffset) {
8320 if (!aPresShell) {
8321 return nullptr;
8323 nsIFrame* frame = aPresShell->GetRootFrame();
8324 if (!frame) {
8325 return nullptr;
8327 return frame->GetView()->GetNearestWidget(aOffset);
8330 int16_t nsContentUtils::GetButtonsFlagForButton(int32_t aButton) {
8331 switch (aButton) {
8332 case -1:
8333 return MouseButtonsFlag::eNoButtons;
8334 case MouseButton::ePrimary:
8335 return MouseButtonsFlag::ePrimaryFlag;
8336 case MouseButton::eMiddle:
8337 return MouseButtonsFlag::eMiddleFlag;
8338 case MouseButton::eSecondary:
8339 return MouseButtonsFlag::eSecondaryFlag;
8340 case 3:
8341 return MouseButtonsFlag::e4thFlag;
8342 case 4:
8343 return MouseButtonsFlag::e5thFlag;
8344 case MouseButton::eEraser:
8345 return MouseButtonsFlag::eEraserFlag;
8346 default:
8347 NS_ERROR("Button not known.");
8348 return 0;
8352 LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(
8353 const CSSPoint& aPoint, const nsPoint& aOffset,
8354 nsPresContext* aPresContext) {
8355 nsPoint layoutRelative = CSSPoint::ToAppUnits(aPoint) + aOffset;
8356 nsPoint visualRelative =
8357 ViewportUtils::LayoutToVisual(layoutRelative, aPresContext->PresShell());
8358 return LayoutDeviceIntPoint::FromAppUnitsRounded(
8359 visualRelative, aPresContext->AppUnitsPerDevPixel());
8362 nsView* nsContentUtils::GetViewToDispatchEvent(nsPresContext* aPresContext,
8363 PresShell** aPresShell) {
8364 if (!aPresContext || !aPresShell) {
8365 return nullptr;
8367 RefPtr<PresShell> presShell = aPresContext->PresShell();
8368 if (NS_WARN_IF(!presShell)) {
8369 *aPresShell = nullptr;
8370 return nullptr;
8372 nsViewManager* viewManager = presShell->GetViewManager();
8373 if (!viewManager) {
8374 presShell.forget(aPresShell); // XXX Is this intentional?
8375 return nullptr;
8377 presShell.forget(aPresShell);
8378 return viewManager->GetRootView();
8381 nsresult nsContentUtils::SendMouseEvent(
8382 mozilla::PresShell* aPresShell, const nsAString& aType, float aX, float aY,
8383 int32_t aButton, int32_t aButtons, int32_t aClickCount, int32_t aModifiers,
8384 bool aIgnoreRootScrollFrame, float aPressure,
8385 unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
8386 PreventDefaultResult* aPreventDefault, bool aIsDOMEventSynthesized,
8387 bool aIsWidgetEventSynthesized) {
8388 nsPoint offset;
8389 nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
8390 if (!widget) return NS_ERROR_FAILURE;
8392 EventMessage msg;
8393 Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
8394 bool contextMenuKey = false;
8395 if (aType.EqualsLiteral("mousedown")) {
8396 msg = eMouseDown;
8397 } else if (aType.EqualsLiteral("mouseup")) {
8398 msg = eMouseUp;
8399 } else if (aType.EqualsLiteral("mousemove")) {
8400 msg = eMouseMove;
8401 } else if (aType.EqualsLiteral("mouseover")) {
8402 msg = eMouseEnterIntoWidget;
8403 } else if (aType.EqualsLiteral("mouseout")) {
8404 msg = eMouseExitFromWidget;
8405 exitFrom = Some(WidgetMouseEvent::ePlatformChild);
8406 } else if (aType.EqualsLiteral("mousecancel")) {
8407 msg = eMouseExitFromWidget;
8408 exitFrom = Some(XRE_IsParentProcess() ? WidgetMouseEvent::ePlatformTopLevel
8409 : WidgetMouseEvent::ePuppet);
8410 } else if (aType.EqualsLiteral("mouselongtap")) {
8411 msg = eMouseLongTap;
8412 } else if (aType.EqualsLiteral("contextmenu")) {
8413 msg = eContextMenu;
8414 contextMenuKey = (aButton == 0);
8415 } else if (aType.EqualsLiteral("MozMouseHittest")) {
8416 msg = eMouseHitTest;
8417 } else if (aType.EqualsLiteral("MozMouseExploreByTouch")) {
8418 msg = eMouseExploreByTouch;
8419 } else {
8420 return NS_ERROR_FAILURE;
8423 if (aInputSourceArg == MouseEvent_Binding::MOZ_SOURCE_UNKNOWN) {
8424 aInputSourceArg = MouseEvent_Binding::MOZ_SOURCE_MOUSE;
8427 WidgetMouseEvent event(true, msg, widget,
8428 aIsWidgetEventSynthesized
8429 ? WidgetMouseEvent::eSynthesized
8430 : WidgetMouseEvent::eReal,
8431 contextMenuKey ? WidgetMouseEvent::eContextMenuKey
8432 : WidgetMouseEvent::eNormal);
8433 event.pointerId = aIdentifier;
8434 event.mModifiers = GetWidgetModifiers(aModifiers);
8435 event.mButton = aButton;
8436 event.mButtons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED
8437 ? aButtons
8438 : msg == eMouseUp ? 0
8439 : GetButtonsFlagForButton(aButton);
8440 event.mPressure = aPressure;
8441 event.mInputSource = aInputSourceArg;
8442 event.mClickCount = aClickCount;
8443 event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
8444 event.mExitFrom = exitFrom;
8446 nsPresContext* presContext = aPresShell->GetPresContext();
8447 if (!presContext) return NS_ERROR_FAILURE;
8449 event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
8450 event.mIgnoreRootScrollFrame = aIgnoreRootScrollFrame;
8452 nsEventStatus status = nsEventStatus_eIgnore;
8453 if (aToWindow) {
8454 RefPtr<PresShell> presShell;
8455 nsView* view =
8456 GetViewToDispatchEvent(presContext, getter_AddRefs(presShell));
8457 if (!presShell || !view) {
8458 return NS_ERROR_FAILURE;
8460 return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
8462 if (StaticPrefs::test_events_async_enabled()) {
8463 status = widget->DispatchInputEvent(&event).mContentStatus;
8464 } else {
8465 nsresult rv = widget->DispatchEvent(&event, status);
8466 NS_ENSURE_SUCCESS(rv, rv);
8468 if (aPreventDefault) {
8469 if (status == nsEventStatus_eConsumeNoDefault) {
8470 if (event.mFlags.mDefaultPreventedByContent) {
8471 *aPreventDefault = PreventDefaultResult::ByContent;
8472 } else {
8473 *aPreventDefault = PreventDefaultResult::ByChrome;
8475 } else {
8476 *aPreventDefault = PreventDefaultResult::No;
8480 return NS_OK;
8483 /* static */
8484 void nsContentUtils::FirePageHideEventForFrameLoaderSwap(
8485 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8486 bool aOnlySystemGroup) {
8487 MOZ_DIAGNOSTIC_ASSERT(aItem);
8488 MOZ_DIAGNOSTIC_ASSERT(aChromeEventHandler);
8490 RefPtr<Document> doc = aItem->GetDocument();
8491 NS_ASSERTION(doc, "What happened here?");
8492 doc->OnPageHide(true, aChromeEventHandler, aOnlySystemGroup);
8494 int32_t childCount = 0;
8495 aItem->GetInProcessChildCount(&childCount);
8496 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8497 kids.AppendElements(childCount);
8498 for (int32_t i = 0; i < childCount; ++i) {
8499 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8502 for (uint32_t i = 0; i < kids.Length(); ++i) {
8503 if (kids[i]) {
8504 FirePageHideEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8505 aOnlySystemGroup);
8510 // The pageshow event is fired for a given document only if IsShowing() returns
8511 // the same thing as aFireIfShowing. This gives us a way to fire pageshow only
8512 // on documents that are still loading or only on documents that are already
8513 // loaded.
8514 /* static */
8515 void nsContentUtils::FirePageShowEventForFrameLoaderSwap(
8516 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8517 bool aFireIfShowing, bool aOnlySystemGroup) {
8518 int32_t childCount = 0;
8519 aItem->GetInProcessChildCount(&childCount);
8520 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8521 kids.AppendElements(childCount);
8522 for (int32_t i = 0; i < childCount; ++i) {
8523 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8526 for (uint32_t i = 0; i < kids.Length(); ++i) {
8527 if (kids[i]) {
8528 FirePageShowEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8529 aFireIfShowing, aOnlySystemGroup);
8533 RefPtr<Document> doc = aItem->GetDocument();
8534 NS_ASSERTION(doc, "What happened here?");
8535 if (doc->IsShowing() == aFireIfShowing) {
8536 doc->OnPageShow(true, aChromeEventHandler, aOnlySystemGroup);
8540 /* static */
8541 already_AddRefed<nsPIWindowRoot> nsContentUtils::GetWindowRoot(Document* aDoc) {
8542 if (aDoc) {
8543 if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
8544 return win->GetTopWindowRoot();
8547 return nullptr;
8550 /* static */
8551 bool nsContentUtils::LinkContextIsURI(const nsAString& aAnchor,
8552 nsIURI* aDocURI) {
8553 if (aAnchor.IsEmpty()) {
8554 // anchor parameter not present or empty -> same document reference
8555 return true;
8558 // the document URI might contain a fragment identifier ("#...')
8559 // we want to ignore that because it's invisible to the server
8560 // and just affects the local interpretation in the recipient
8561 nsCOMPtr<nsIURI> contextUri;
8562 nsresult rv = NS_GetURIWithoutRef(aDocURI, getter_AddRefs(contextUri));
8564 if (NS_FAILED(rv)) {
8565 // copying failed
8566 return false;
8569 // resolve anchor against context
8570 nsCOMPtr<nsIURI> resolvedUri;
8571 rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor, nullptr, contextUri);
8573 if (NS_FAILED(rv)) {
8574 // resolving failed
8575 return false;
8578 bool same;
8579 rv = contextUri->Equals(resolvedUri, &same);
8580 if (NS_FAILED(rv)) {
8581 // comparison failed
8582 return false;
8585 return same;
8588 /* static */
8589 bool nsContentUtils::IsPreloadType(nsContentPolicyType aType) {
8590 return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
8591 aType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD ||
8592 aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
8593 aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD ||
8594 aType == nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD ||
8595 aType == nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD);
8598 /* static */
8599 bool nsContentUtils::IsUpgradableDisplayType(ExtContentPolicyType aType) {
8600 MOZ_ASSERT(NS_IsMainThread());
8601 return (aType == ExtContentPolicy::TYPE_IMAGE ||
8602 aType == ExtContentPolicy::TYPE_MEDIA);
8605 // static
8606 ReferrerPolicy nsContentUtils::GetReferrerPolicyFromChannel(
8607 nsIChannel* aChannel) {
8608 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
8609 if (!httpChannel) {
8610 return ReferrerPolicy::_empty;
8613 nsresult rv;
8614 nsAutoCString headerValue;
8615 rv = httpChannel->GetResponseHeader("referrer-policy"_ns, headerValue);
8616 if (NS_FAILED(rv) || headerValue.IsEmpty()) {
8617 return ReferrerPolicy::_empty;
8620 return ReferrerInfo::ReferrerPolicyFromHeaderString(
8621 NS_ConvertUTF8toUTF16(headerValue));
8624 // static
8625 bool nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel) {
8626 nsLoadFlags loadFlags = 0;
8627 aChannel->GetLoadFlags(&loadFlags);
8628 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
8629 return true;
8632 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
8633 nsContentPolicyType type = loadInfo->InternalContentPolicyType();
8634 return IsNonSubresourceInternalPolicyType(type);
8637 // static
8638 bool nsContentUtils::IsNonSubresourceInternalPolicyType(
8639 nsContentPolicyType aType) {
8640 return aType == nsIContentPolicy::TYPE_DOCUMENT ||
8641 aType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
8642 aType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
8643 aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
8644 aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
8647 // static public
8648 bool nsContentUtils::IsThirdPartyTrackingResourceWindow(
8649 nsPIDOMWindowInner* aWindow) {
8650 MOZ_ASSERT(aWindow);
8652 Document* document = aWindow->GetExtantDoc();
8653 if (!document) {
8654 return false;
8657 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8658 do_QueryInterface(document->GetChannel());
8659 if (!classifiedChannel) {
8660 return false;
8663 return classifiedChannel->IsThirdPartyTrackingResource();
8666 // static public
8667 bool nsContentUtils::IsFirstPartyTrackingResourceWindow(
8668 nsPIDOMWindowInner* aWindow) {
8669 MOZ_ASSERT(aWindow);
8671 Document* document = aWindow->GetExtantDoc();
8672 if (!document) {
8673 return false;
8676 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8677 do_QueryInterface(document->GetChannel());
8678 if (!classifiedChannel) {
8679 return false;
8682 uint32_t classificationFlags =
8683 classifiedChannel->GetFirstPartyClassificationFlags();
8685 return mozilla::net::UrlClassifierCommon::IsTrackingClassificationFlag(
8686 classificationFlags, NS_UsePrivateBrowsing(document->GetChannel()));
8689 namespace {
8691 // We put StringBuilder in the anonymous namespace to prevent anything outside
8692 // this file from accidentally being linked against it.
8693 class BulkAppender {
8694 using size_type = typename nsAString::size_type;
8696 public:
8697 explicit BulkAppender(BulkWriteHandle<char16_t>&& aHandle)
8698 : mHandle(std::move(aHandle)), mPosition(0) {}
8699 ~BulkAppender() = default;
8701 template <int N>
8702 void AppendLiteral(const char16_t (&aStr)[N]) {
8703 size_t len = N - 1;
8704 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8705 memcpy(mHandle.Elements() + mPosition, aStr, len * sizeof(char16_t));
8706 mPosition += len;
8709 void Append(Span<const char16_t> aStr) {
8710 size_t len = aStr.Length();
8711 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8712 // Both mHandle.Elements() and aStr.Elements() are guaranteed
8713 // to be non-null (by the string implementation and by Span,
8714 // respectively), so not checking the pointers for null before
8715 // memcpy does not lead to UB even if len was zero.
8716 memcpy(mHandle.Elements() + mPosition, aStr.Elements(),
8717 len * sizeof(char16_t));
8718 mPosition += len;
8721 void Append(Span<const char> aStr) {
8722 size_t len = aStr.Length();
8723 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8724 ConvertLatin1toUtf16(aStr, mHandle.AsSpan().From(mPosition));
8725 mPosition += len;
8728 void Finish() { mHandle.Finish(mPosition, false); }
8730 private:
8731 mozilla::BulkWriteHandle<char16_t> mHandle;
8732 size_type mPosition;
8735 class StringBuilder {
8736 private:
8737 // Try to keep the size of StringBuilder close to a jemalloc bucket size.
8738 static const uint32_t STRING_BUFFER_UNITS = 1020;
8739 class Unit {
8740 public:
8741 Unit() : mAtom(nullptr), mType(eUnknown), mLength(0) {
8742 MOZ_COUNT_CTOR(StringBuilder::Unit);
8744 ~Unit() {
8745 if (mType == eString || mType == eStringWithEncode) {
8746 delete mString;
8748 MOZ_COUNT_DTOR(StringBuilder::Unit);
8751 enum Type {
8752 eUnknown,
8753 eAtom,
8754 eString,
8755 eStringWithEncode,
8756 eLiteral,
8757 eTextFragment,
8758 eTextFragmentWithEncode,
8761 union {
8762 nsAtom* mAtom;
8763 const char16_t* mLiteral;
8764 nsAutoString* mString;
8765 const nsTextFragment* mTextFragment;
8767 Type mType;
8768 uint32_t mLength;
8771 public:
8772 StringBuilder() : mLast(this), mLength(0) { MOZ_COUNT_CTOR(StringBuilder); }
8774 MOZ_COUNTED_DTOR(StringBuilder)
8776 void Append(nsAtom* aAtom) {
8777 Unit* u = AddUnit();
8778 u->mAtom = aAtom;
8779 u->mType = Unit::eAtom;
8780 uint32_t len = aAtom->GetLength();
8781 u->mLength = len;
8782 mLength += len;
8785 template <int N>
8786 void Append(const char16_t (&aLiteral)[N]) {
8787 Unit* u = AddUnit();
8788 u->mLiteral = aLiteral;
8789 u->mType = Unit::eLiteral;
8790 uint32_t len = N - 1;
8791 u->mLength = len;
8792 mLength += len;
8795 void Append(const nsAString& aString) {
8796 Unit* u = AddUnit();
8797 u->mString = new nsAutoString(aString);
8798 u->mType = Unit::eString;
8799 uint32_t len = aString.Length();
8800 u->mLength = len;
8801 mLength += len;
8804 void Append(nsAutoString* aString) {
8805 Unit* u = AddUnit();
8806 u->mString = aString;
8807 u->mType = Unit::eString;
8808 uint32_t len = aString->Length();
8809 u->mLength = len;
8810 mLength += len;
8813 void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen) {
8814 Unit* u = AddUnit();
8815 u->mString = aString;
8816 u->mType = Unit::eStringWithEncode;
8817 u->mLength = aLen;
8818 mLength += aLen;
8821 void Append(const nsTextFragment* aTextFragment) {
8822 Unit* u = AddUnit();
8823 u->mTextFragment = aTextFragment;
8824 u->mType = Unit::eTextFragment;
8825 uint32_t len = aTextFragment->GetLength();
8826 u->mLength = len;
8827 mLength += len;
8830 void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen) {
8831 Unit* u = AddUnit();
8832 u->mTextFragment = aTextFragment;
8833 u->mType = Unit::eTextFragmentWithEncode;
8834 u->mLength = aLen;
8835 mLength += aLen;
8838 bool ToString(nsAString& aOut) {
8839 if (!mLength.isValid()) {
8840 return false;
8842 auto appenderOrErr = aOut.BulkWrite(mLength.value(), 0, true);
8843 if (appenderOrErr.isErr()) {
8844 return false;
8847 BulkAppender appender{appenderOrErr.unwrap()};
8849 for (StringBuilder* current = this; current;
8850 current = current->mNext.get()) {
8851 uint32_t len = current->mUnits.Length();
8852 for (uint32_t i = 0; i < len; ++i) {
8853 Unit& u = current->mUnits[i];
8854 switch (u.mType) {
8855 case Unit::eAtom:
8856 appender.Append(*(u.mAtom));
8857 break;
8858 case Unit::eString:
8859 appender.Append(*(u.mString));
8860 break;
8861 case Unit::eStringWithEncode:
8862 EncodeAttrString(*(u.mString), appender);
8863 break;
8864 case Unit::eLiteral:
8865 appender.Append(Span(u.mLiteral, u.mLength));
8866 break;
8867 case Unit::eTextFragment:
8868 if (u.mTextFragment->Is2b()) {
8869 appender.Append(
8870 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()));
8871 } else {
8872 appender.Append(
8873 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()));
8875 break;
8876 case Unit::eTextFragmentWithEncode:
8877 if (u.mTextFragment->Is2b()) {
8878 EncodeTextFragment(
8879 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()),
8880 appender);
8881 } else {
8882 EncodeTextFragment(
8883 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()),
8884 appender);
8886 break;
8887 default:
8888 MOZ_CRASH("Unknown unit type?");
8892 appender.Finish();
8893 return true;
8896 private:
8897 Unit* AddUnit() {
8898 if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
8899 new StringBuilder(this);
8901 return mLast->mUnits.AppendElement();
8904 explicit StringBuilder(StringBuilder* aFirst) : mLast(nullptr), mLength(0) {
8905 MOZ_COUNT_CTOR(StringBuilder);
8906 aFirst->mLast->mNext = WrapUnique(this);
8907 aFirst->mLast = this;
8910 void EncodeAttrString(Span<const char16_t> aStr, BulkAppender& aAppender) {
8911 size_t flushedUntil = 0;
8912 size_t currentPosition = 0;
8913 for (char16_t c : aStr) {
8914 switch (c) {
8915 case '"':
8916 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8917 aAppender.AppendLiteral(u"&quot;");
8918 flushedUntil = currentPosition + 1;
8919 break;
8920 case '&':
8921 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8922 aAppender.AppendLiteral(u"&amp;");
8923 flushedUntil = currentPosition + 1;
8924 break;
8925 case 0x00A0:
8926 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8927 aAppender.AppendLiteral(u"&nbsp;");
8928 flushedUntil = currentPosition + 1;
8929 break;
8930 default:
8931 break;
8933 currentPosition++;
8935 if (currentPosition > flushedUntil) {
8936 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8940 template <class T>
8941 void EncodeTextFragment(Span<const T> aStr, BulkAppender& aAppender) {
8942 size_t flushedUntil = 0;
8943 size_t currentPosition = 0;
8944 for (T c : aStr) {
8945 switch (c) {
8946 case '<':
8947 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8948 aAppender.AppendLiteral(u"&lt;");
8949 flushedUntil = currentPosition + 1;
8950 break;
8951 case '>':
8952 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8953 aAppender.AppendLiteral(u"&gt;");
8954 flushedUntil = currentPosition + 1;
8955 break;
8956 case '&':
8957 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8958 aAppender.AppendLiteral(u"&amp;");
8959 flushedUntil = currentPosition + 1;
8960 break;
8961 case T(0xA0):
8962 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8963 aAppender.AppendLiteral(u"&nbsp;");
8964 flushedUntil = currentPosition + 1;
8965 break;
8966 default:
8967 break;
8969 currentPosition++;
8971 if (currentPosition > flushedUntil) {
8972 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8976 AutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
8977 mozilla::UniquePtr<StringBuilder> mNext;
8978 StringBuilder* mLast;
8979 // mLength is used only in the first StringBuilder object in the linked list.
8980 CheckedInt<uint32_t> mLength;
8983 } // namespace
8985 static void AppendEncodedCharacters(const nsTextFragment* aText,
8986 StringBuilder& aBuilder) {
8987 uint32_t extraSpaceNeeded = 0;
8988 uint32_t len = aText->GetLength();
8989 if (aText->Is2b()) {
8990 const char16_t* data = aText->Get2b();
8991 for (uint32_t i = 0; i < len; ++i) {
8992 const char16_t c = data[i];
8993 switch (c) {
8994 case '<':
8995 extraSpaceNeeded += ArrayLength("&lt;") - 2;
8996 break;
8997 case '>':
8998 extraSpaceNeeded += ArrayLength("&gt;") - 2;
8999 break;
9000 case '&':
9001 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9002 break;
9003 case 0x00A0:
9004 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9005 break;
9006 default:
9007 break;
9010 } else {
9011 const char* data = aText->Get1b();
9012 for (uint32_t i = 0; i < len; ++i) {
9013 const unsigned char c = data[i];
9014 switch (c) {
9015 case '<':
9016 extraSpaceNeeded += ArrayLength("&lt;") - 2;
9017 break;
9018 case '>':
9019 extraSpaceNeeded += ArrayLength("&gt;") - 2;
9020 break;
9021 case '&':
9022 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9023 break;
9024 case 0x00A0:
9025 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9026 break;
9027 default:
9028 break;
9033 if (extraSpaceNeeded) {
9034 aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
9035 } else {
9036 aBuilder.Append(aText);
9040 static void AppendEncodedAttributeValue(nsAutoString* aValue,
9041 StringBuilder& aBuilder) {
9042 const char16_t* c = aValue->BeginReading();
9043 const char16_t* end = aValue->EndReading();
9045 uint32_t extraSpaceNeeded = 0;
9046 while (c < end) {
9047 switch (*c) {
9048 case '"':
9049 extraSpaceNeeded += ArrayLength("&quot;") - 2;
9050 break;
9051 case '&':
9052 extraSpaceNeeded += ArrayLength("&amp;") - 2;
9053 break;
9054 case 0x00A0:
9055 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
9056 break;
9057 default:
9058 break;
9060 ++c;
9063 if (extraSpaceNeeded) {
9064 aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
9065 } else {
9066 aBuilder.Append(aValue);
9070 static void StartElement(Element* aContent, StringBuilder& aBuilder) {
9071 nsAtom* localName = aContent->NodeInfo()->NameAtom();
9072 int32_t tagNS = aContent->GetNameSpaceID();
9074 aBuilder.Append(u"<");
9075 if (aContent->IsHTMLElement() || aContent->IsSVGElement() ||
9076 aContent->IsMathMLElement()) {
9077 aBuilder.Append(localName);
9078 } else {
9079 aBuilder.Append(aContent->NodeName());
9082 CustomElementData* ceData = aContent->GetCustomElementData();
9083 if (ceData) {
9084 nsAtom* isAttr = ceData->GetIs(aContent);
9085 if (isAttr && !aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
9086 aBuilder.Append(uR"( is=")");
9087 aBuilder.Append(nsDependentAtomString(isAttr));
9088 aBuilder.Append(uR"(")");
9092 int32_t count = aContent->GetAttrCount();
9093 for (int32_t i = 0; i < count; i++) {
9094 const nsAttrName* name = aContent->GetAttrNameAt(i);
9095 int32_t attNs = name->NamespaceID();
9096 nsAtom* attName = name->LocalName();
9098 // Filter out any attribute starting with [-|_]moz
9099 nsDependentAtomString attrNameStr(attName);
9100 if (StringBeginsWith(attrNameStr, u"_moz"_ns) ||
9101 StringBeginsWith(attrNameStr, u"-moz"_ns)) {
9102 continue;
9105 auto* attValue = new nsAutoString();
9106 aContent->GetAttr(attNs, attName, *attValue);
9108 // Filter out special case of <br type="_moz*"> used by the editor.
9109 // Bug 16988. Yuck.
9110 if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
9111 attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
9112 StringBeginsWith(*attValue, u"_moz"_ns)) {
9113 delete attValue;
9114 continue;
9117 aBuilder.Append(u" ");
9119 if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
9120 (attNs == kNameSpaceID_XMLNS && attName == nsGkAtoms::xmlns)) {
9121 // Nothing else required
9122 } else if (attNs == kNameSpaceID_XML) {
9123 aBuilder.Append(u"xml:");
9124 } else if (attNs == kNameSpaceID_XMLNS) {
9125 aBuilder.Append(u"xmlns:");
9126 } else if (attNs == kNameSpaceID_XLink) {
9127 aBuilder.Append(u"xlink:");
9128 } else {
9129 nsAtom* prefix = name->GetPrefix();
9130 if (prefix) {
9131 aBuilder.Append(prefix);
9132 aBuilder.Append(u":");
9136 aBuilder.Append(attName);
9137 aBuilder.Append(uR"(=")");
9138 AppendEncodedAttributeValue(attValue, aBuilder);
9139 aBuilder.Append(uR"(")");
9142 aBuilder.Append(u">");
9145 // Per HTML spec we should append one \n if the first child of
9146 // pre/textarea/listing is a textnode and starts with a \n.
9147 // But because browsers haven't traditionally had that behavior,
9148 // we're not changing our behavior either - yet.
9149 if (aContent->IsHTMLElement()) {
9150 if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
9151 localName == nsGkAtoms::listing) {
9152 nsIContent* fc = aContent->GetFirstChild();
9153 if (fc &&
9154 (fc->NodeType() == nsINode::TEXT_NODE ||
9155 fc->NodeType() == nsINode::CDATA_SECTION_NODE)) {
9156 const nsTextFragment* text = fc->GetText();
9157 if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
9158 aBuilder.Append("\n");
9165 static inline bool ShouldEscape(nsIContent* aParent) {
9166 if (!aParent || !aParent->IsHTMLElement()) {
9167 return true;
9170 static const nsAtom* nonEscapingElements[] = {
9171 nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
9172 nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
9173 nsGkAtoms::plaintext, nsGkAtoms::noscript};
9174 static mozilla::BitBloomFilter<12, nsAtom> sFilter;
9175 static bool sInitialized = false;
9176 if (!sInitialized) {
9177 sInitialized = true;
9178 for (auto& nonEscapingElement : nonEscapingElements) {
9179 sFilter.add(nonEscapingElement);
9183 nsAtom* tag = aParent->NodeInfo()->NameAtom();
9184 if (sFilter.mightContain(tag)) {
9185 for (auto& nonEscapingElement : nonEscapingElements) {
9186 if (tag == nonEscapingElement) {
9187 if (MOZ_UNLIKELY(tag == nsGkAtoms::noscript) &&
9188 MOZ_UNLIKELY(!aParent->OwnerDoc()->IsScriptEnabled())) {
9189 return true;
9191 return false;
9195 return true;
9198 static inline bool IsVoidTag(Element* aElement) {
9199 if (!aElement->IsHTMLElement()) {
9200 return false;
9202 return FragmentOrElement::IsHTMLVoid(aElement->NodeInfo()->NameAtom());
9205 bool nsContentUtils::SerializeNodeToMarkup(nsINode* aRoot,
9206 bool aDescendentsOnly,
9207 nsAString& aOut) {
9208 // If you pass in a DOCUMENT_NODE, you must pass aDescendentsOnly as true
9209 MOZ_ASSERT(aDescendentsOnly || aRoot->NodeType() != nsINode::DOCUMENT_NODE);
9211 nsINode* current =
9212 aDescendentsOnly ? aRoot->GetFirstChildOfTemplateOrNode() : aRoot;
9214 if (!current) {
9215 return true;
9218 StringBuilder builder;
9219 nsIContent* next;
9220 while (true) {
9221 bool isVoid = false;
9222 switch (current->NodeType()) {
9223 case nsINode::ELEMENT_NODE: {
9224 Element* elem = current->AsElement();
9225 StartElement(elem, builder);
9226 isVoid = IsVoidTag(elem);
9227 if (!isVoid && (next = current->GetFirstChildOfTemplateOrNode())) {
9228 current = next;
9229 continue;
9231 break;
9234 case nsINode::TEXT_NODE:
9235 case nsINode::CDATA_SECTION_NODE: {
9236 const nsTextFragment* text = &current->AsText()->TextFragment();
9237 nsIContent* parent = current->GetParent();
9238 if (ShouldEscape(parent)) {
9239 AppendEncodedCharacters(text, builder);
9240 } else {
9241 builder.Append(text);
9243 break;
9246 case nsINode::COMMENT_NODE: {
9247 builder.Append(u"<!--");
9248 builder.Append(static_cast<nsIContent*>(current)->GetText());
9249 builder.Append(u"-->");
9250 break;
9253 case nsINode::DOCUMENT_TYPE_NODE: {
9254 builder.Append(u"<!DOCTYPE ");
9255 builder.Append(current->NodeName());
9256 builder.Append(u">");
9257 break;
9260 case nsINode::PROCESSING_INSTRUCTION_NODE: {
9261 builder.Append(u"<?");
9262 builder.Append(current->NodeName());
9263 builder.Append(u" ");
9264 builder.Append(static_cast<nsIContent*>(current)->GetText());
9265 builder.Append(u">");
9266 break;
9270 while (true) {
9271 if (!isVoid && current->NodeType() == nsINode::ELEMENT_NODE) {
9272 builder.Append(u"</");
9273 nsIContent* elem = static_cast<nsIContent*>(current);
9274 if (elem->IsHTMLElement() || elem->IsSVGElement() ||
9275 elem->IsMathMLElement()) {
9276 builder.Append(elem->NodeInfo()->NameAtom());
9277 } else {
9278 builder.Append(current->NodeName());
9280 builder.Append(u">");
9282 isVoid = false;
9284 if (current == aRoot) {
9285 return builder.ToString(aOut);
9288 if ((next = current->GetNextSibling())) {
9289 current = next;
9290 break;
9293 current = current->GetParentNode();
9295 // Handle template element. If the parent is a template's content,
9296 // then adjust the parent to be the template element.
9297 if (current != aRoot &&
9298 current->NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
9299 DocumentFragment* frag = static_cast<DocumentFragment*>(current);
9300 nsIContent* fragHost = frag->GetHost();
9301 if (fragHost && fragHost->IsTemplateElement()) {
9302 current = fragHost;
9306 if (aDescendentsOnly && current == aRoot) {
9307 return builder.ToString(aOut);
9313 bool nsContentUtils::IsSpecificAboutPage(JSObject* aGlobal, const char* aUri) {
9314 // aUri must start with about: or this isn't the right function to be using.
9315 MOZ_ASSERT(strncmp(aUri, "about:", 6) == 0);
9317 // Make sure the global is a window
9318 MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
9319 nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
9320 if (!win) {
9321 return false;
9324 nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
9325 NS_ENSURE_TRUE(principal, false);
9327 // First check the scheme to avoid getting long specs in the common case.
9328 if (!principal->SchemeIs("about")) {
9329 return false;
9332 nsAutoCString spec;
9333 principal->GetAsciiSpec(spec);
9335 return spec.EqualsASCII(aUri);
9338 /* static */
9339 void nsContentUtils::SetScrollbarsVisibility(nsIDocShell* aDocShell,
9340 bool aVisible) {
9341 if (!aDocShell) {
9342 return;
9344 auto pref = aVisible ? ScrollbarPreference::Auto : ScrollbarPreference::Never;
9345 nsDocShell::Cast(aDocShell)->SetScrollbarPreference(pref);
9348 /* static */
9349 nsIDocShell* nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget) {
9350 if (!aTarget) {
9351 return nullptr;
9354 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
9355 if (nsCOMPtr<nsINode> node = nsINode::FromEventTarget(aTarget)) {
9356 bool ignore;
9357 innerWindow =
9358 do_QueryInterface(node->OwnerDoc()->GetScriptHandlingObject(ignore));
9359 } else if ((innerWindow = nsPIDOMWindowInner::FromEventTarget(aTarget))) {
9360 // Nothing else to do
9361 } else {
9362 nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(aTarget);
9363 if (helper) {
9364 innerWindow = helper->GetOwner();
9368 if (innerWindow) {
9369 return innerWindow->GetDocShell();
9372 return nullptr;
9376 * Note: this function only relates to figuring out HTTPS state, which is an
9377 * input to the Secure Context algorithm. We are not actually implementing any
9378 * part of the Secure Context algorithm itself here.
9380 * This is a bit of a hack. Ideally we'd propagate HTTPS state through
9381 * nsIChannel as described in the Fetch and HTML specs, but making channels
9382 * know about whether they should inherit HTTPS state, propagating information
9383 * about who the channel's "client" is, exposing GetHttpsState API on channels
9384 * and modifying the various cache implementations to store and retrieve HTTPS
9385 * state involves a huge amount of code (see bug 1220687). We avoid that for
9386 * now using this function.
9388 * This function takes advantage of the observation that we can return true if
9389 * nsIContentSecurityManager::IsOriginPotentiallyTrustworthy returns true for
9390 * the document's origin (e.g. the origin has a scheme of 'https' or host
9391 * 'localhost' etc.). Since we generally propagate a creator document's origin
9392 * onto data:, blob:, etc. documents, this works for them too.
9394 * The scenario where this observation breaks down is sandboxing without the
9395 * 'allow-same-origin' flag, since in this case a document is given a unique
9396 * origin (IsOriginPotentiallyTrustworthy would return false). We handle that
9397 * by using the origin that the document would have had had it not been
9398 * sandboxed.
9400 * DEFICIENCIES: Note that this function uses nsIScriptSecurityManager's
9401 * getChannelResultPrincipalIfNotSandboxed, and that method's ignoring of
9402 * sandboxing is limited to the immediate sandbox. In the case that aDocument
9403 * should inherit its origin (e.g. data: URI) but its parent has ended up
9404 * with a unique origin due to sandboxing further up the parent chain we may
9405 * end up returning false when we would ideally return true (since we will
9406 * examine the parent's origin for 'https' and not finding it.) This means
9407 * that we may restrict the privileges of some pages unnecessarily in this
9408 * edge case.
9410 /* static */
9411 bool nsContentUtils::HttpsStateIsModern(Document* aDocument) {
9412 if (!aDocument) {
9413 return false;
9416 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
9418 if (principal->IsSystemPrincipal()) {
9419 return true;
9422 // If aDocument is sandboxed, try and get the principal that it would have
9423 // been given had it not been sandboxed:
9424 if (principal->GetIsNullPrincipal() &&
9425 (aDocument->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
9426 nsIChannel* channel = aDocument->GetChannel();
9427 if (channel) {
9428 nsCOMPtr<nsIScriptSecurityManager> ssm =
9429 nsContentUtils::GetSecurityManager();
9430 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9431 channel, getter_AddRefs(principal));
9432 if (NS_FAILED(rv)) {
9433 return false;
9435 if (principal->IsSystemPrincipal()) {
9436 // If a document with the system principal is sandboxing a subdocument
9437 // that would normally inherit the embedding element's principal (e.g.
9438 // a srcdoc document) then the embedding document does not trust the
9439 // content that is written to the embedded document. Unlike when the
9440 // embedding document is https, in this case we have no indication as
9441 // to whether the embedded document's contents are delivered securely
9442 // or not, and the sandboxing would possibly indicate that they were
9443 // not. To play it safe we return false here. (See bug 1162772
9444 // comment 73-80.)
9445 return false;
9450 if (principal->GetIsNullPrincipal()) {
9451 return false;
9454 MOZ_ASSERT(principal->GetIsContentPrincipal());
9456 return principal->GetIsOriginPotentiallyTrustworthy();
9459 /* static */
9460 bool nsContentUtils::ComputeIsSecureContext(nsIChannel* aChannel) {
9461 MOZ_ASSERT(aChannel);
9463 nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
9464 nsCOMPtr<nsIPrincipal> principal;
9465 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9466 aChannel, getter_AddRefs(principal));
9467 if (NS_FAILED(rv)) {
9468 return false;
9471 const RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
9473 if (principal->IsSystemPrincipal()) {
9474 // If the load would've been sandboxed, treat this load as an untrusted
9475 // load, as system code considers sandboxed resources insecure.
9476 return !loadInfo->GetLoadingSandboxed();
9479 if (principal->GetIsNullPrincipal()) {
9480 return false;
9483 if (const RefPtr<WindowContext> windowContext =
9484 WindowContext::GetById(loadInfo->GetInnerWindowID())) {
9485 if (!windowContext->GetIsSecureContext()) {
9486 return false;
9490 return principal->GetIsOriginPotentiallyTrustworthy();
9493 /* static */
9494 void nsContentUtils::TryToUpgradeElement(Element* aElement) {
9495 NodeInfo* nodeInfo = aElement->NodeInfo();
9496 RefPtr<nsAtom> typeAtom =
9497 aElement->GetCustomElementData()->GetCustomElementType();
9499 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9500 CustomElementDefinition* definition =
9501 nsContentUtils::LookupCustomElementDefinition(
9502 nodeInfo->GetDocument(), nodeInfo->NameAtom(),
9503 nodeInfo->NamespaceID(), typeAtom);
9504 if (definition) {
9505 nsContentUtils::EnqueueUpgradeReaction(aElement, definition);
9506 } else {
9507 // Add an unresolved custom element that is a candidate for upgrade when a
9508 // custom element is connected to the document.
9509 nsContentUtils::RegisterUnresolvedElement(aElement, typeAtom);
9513 MOZ_CAN_RUN_SCRIPT
9514 static void DoCustomElementCreate(Element** aElement, JSContext* aCx,
9515 Document* aDoc, NodeInfo* aNodeInfo,
9516 CustomElementConstructor* aConstructor,
9517 ErrorResult& aRv) {
9518 JS::Rooted<JS::Value> constructResult(aCx);
9519 aConstructor->Construct(&constructResult, aRv, "Custom Element Create",
9520 CallbackFunction::eRethrowExceptions);
9521 if (aRv.Failed()) {
9522 return;
9525 RefPtr<Element> element;
9526 // constructResult is an ObjectValue because construction with a callback
9527 // always forms the return value from a JSObject.
9528 UNWRAP_OBJECT(Element, &constructResult, element);
9529 if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9530 if (!element || !element->IsHTMLElement()) {
9531 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9532 "HTMLElement");
9533 return;
9535 } else {
9536 if (!element || !element->IsXULElement()) {
9537 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9538 "XULElement");
9539 return;
9543 nsAtom* localName = aNodeInfo->NameAtom();
9545 if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
9546 element->HasChildren() || element->GetAttrCount() ||
9547 element->NodeInfo()->NameAtom() != localName) {
9548 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
9549 return;
9552 element.forget(aElement);
9555 /* static */
9556 nsresult nsContentUtils::NewXULOrHTMLElement(
9557 Element** aResult, mozilla::dom::NodeInfo* aNodeInfo,
9558 FromParser aFromParser, nsAtom* aIsAtom,
9559 mozilla::dom::CustomElementDefinition* aDefinition) {
9560 RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
9561 MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
9562 nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
9563 "Can only create XUL or XHTML elements.");
9565 nsAtom* name = nodeInfo->NameAtom();
9566 int32_t tag = eHTMLTag_unknown;
9567 bool isCustomElementName = false;
9568 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9569 tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
9570 isCustomElementName =
9571 (tag == eHTMLTag_userdefined &&
9572 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML));
9573 } else { // kNameSpaceID_XUL
9574 if (aIsAtom) {
9575 // Make sure the customized built-in element to be constructed confirms
9576 // to our naming requirement, i.e. [is] must be a dashed name and
9577 // the tag name must not.
9578 // if so, set isCustomElementName to false to kick off all the logics
9579 // that pick up aIsAtom.
9580 if (nsContentUtils::IsNameWithDash(aIsAtom) &&
9581 !nsContentUtils::IsNameWithDash(name)) {
9582 isCustomElementName = false;
9583 } else {
9584 isCustomElementName =
9585 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9587 } else {
9588 isCustomElementName =
9589 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9593 nsAtom* tagAtom = nodeInfo->NameAtom();
9594 nsAtom* typeAtom = nullptr;
9595 bool isCustomElement = isCustomElementName || aIsAtom;
9596 if (isCustomElement) {
9597 typeAtom = isCustomElementName ? tagAtom : aIsAtom;
9600 MOZ_ASSERT_IF(aDefinition, isCustomElement);
9602 // https://dom.spec.whatwg.org/#concept-create-element
9603 // We only handle the "synchronous custom elements flag is set" now.
9604 // For the unset case (e.g. cloning a node), see bug 1319342 for that.
9605 // Step 4.
9606 RefPtr<CustomElementDefinition> definition = aDefinition;
9607 if (isCustomElement && !definition) {
9608 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9609 definition = nsContentUtils::LookupCustomElementDefinition(
9610 nodeInfo->GetDocument(), nodeInfo->NameAtom(), nodeInfo->NamespaceID(),
9611 typeAtom);
9614 // It might be a problem that parser synchronously calls constructor, so filed
9615 // bug 1378079 to figure out what we should do for parser case.
9616 if (definition) {
9618 * Synchronous custom elements flag is determined by 3 places in spec,
9619 * 1) create an element for a token, the flag is determined by
9620 * "will execute script" which is not originally created
9621 * for the HTML fragment parsing algorithm.
9622 * 2) createElement and createElementNS, the flag is the same as
9623 * NOT_FROM_PARSER.
9624 * 3) clone a node, our implementation will not go into this function.
9625 * For the unset case which is non-synchronous only applied for
9626 * inner/outerHTML.
9628 bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT;
9629 // Per discussion in https://github.com/w3c/webcomponents/issues/635,
9630 // use entry global in those places that are called from JS APIs and use the
9631 // node document's global object if it is called from parser.
9632 nsIGlobalObject* global;
9633 if (aFromParser == dom::NOT_FROM_PARSER) {
9634 global = GetEntryGlobal();
9636 // Documents created from the PrototypeDocumentSink always use
9637 // NOT_FROM_PARSER for non-XUL elements. We can get the global from the
9638 // document in that case.
9639 if (!global) {
9640 Document* doc = nodeInfo->GetDocument();
9641 if (doc && doc->LoadedFromPrototype()) {
9642 global = doc->GetScopeObject();
9645 } else {
9646 global = nodeInfo->GetDocument()->GetScopeObject();
9648 if (!global) {
9649 // In browser chrome code, one may have access to a document which doesn't
9650 // have scope object anymore.
9651 return NS_ERROR_FAILURE;
9654 AutoAllowLegacyScriptExecution exemption;
9655 AutoEntryScript aes(global, "create custom elements");
9656 JSContext* cx = aes.cx();
9657 ErrorResult rv;
9659 // Step 5.
9660 if (definition->IsCustomBuiltIn()) {
9661 // SetupCustomElement() should be called with an element that don't have
9662 // CustomElementData setup, if not we will hit the assertion in
9663 // SetCustomElementData().
9664 // Built-in element
9665 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9666 *aResult =
9667 CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9668 } else {
9669 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9671 (*aResult)->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom));
9672 if (synchronousCustomElements) {
9673 CustomElementRegistry::Upgrade(*aResult, definition, rv);
9674 if (rv.MaybeSetPendingException(cx)) {
9675 aes.ReportException();
9677 } else {
9678 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9681 return NS_OK;
9684 // Step 6.1.
9685 if (synchronousCustomElements) {
9686 definition->mPrefixStack.AppendElement(nodeInfo->GetPrefixAtom());
9687 RefPtr<Document> doc = nodeInfo->GetDocument();
9688 DoCustomElementCreate(aResult, cx, doc, nodeInfo,
9689 MOZ_KnownLive(definition->mConstructor), rv);
9690 if (rv.MaybeSetPendingException(cx)) {
9691 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9692 NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(),
9693 aFromParser));
9694 } else {
9695 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9697 (*aResult)->SetDefined(false);
9699 definition->mPrefixStack.RemoveLastElement();
9700 return NS_OK;
9703 // Step 6.2.
9704 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9705 NS_IF_ADDREF(*aResult =
9706 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9707 } else {
9708 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9710 (*aResult)->SetCustomElementData(
9711 MakeUnique<CustomElementData>(definition->mType));
9712 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9713 return NS_OK;
9716 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9717 // Per the Custom Element specification, unknown tags that are valid custom
9718 // element names should be HTMLElement instead of HTMLUnknownElement.
9719 if (isCustomElementName) {
9720 NS_IF_ADDREF(*aResult =
9721 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9722 } else {
9723 *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9725 } else {
9726 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9729 if (!*aResult) {
9730 return NS_ERROR_OUT_OF_MEMORY;
9733 if (isCustomElement) {
9734 (*aResult)->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom));
9735 nsContentUtils::RegisterCallbackUpgradeElement(*aResult, typeAtom);
9738 return NS_OK;
9741 CustomElementRegistry* nsContentUtils::GetCustomElementRegistry(
9742 Document* aDoc) {
9743 MOZ_ASSERT(aDoc);
9745 if (!aDoc->GetDocShell()) {
9746 return nullptr;
9749 nsPIDOMWindowInner* window = aDoc->GetInnerWindow();
9750 if (!window) {
9751 return nullptr;
9754 return window->CustomElements();
9757 /* static */
9758 CustomElementDefinition* nsContentUtils::LookupCustomElementDefinition(
9759 Document* aDoc, nsAtom* aNameAtom, uint32_t aNameSpaceID,
9760 nsAtom* aTypeAtom) {
9761 if (aNameSpaceID != kNameSpaceID_XUL && aNameSpaceID != kNameSpaceID_XHTML) {
9762 return nullptr;
9765 RefPtr<CustomElementRegistry> registry = GetCustomElementRegistry(aDoc);
9766 if (!registry) {
9767 return nullptr;
9770 return registry->LookupCustomElementDefinition(aNameAtom, aNameSpaceID,
9771 aTypeAtom);
9774 /* static */
9775 void nsContentUtils::RegisterCallbackUpgradeElement(Element* aElement,
9776 nsAtom* aTypeName) {
9777 MOZ_ASSERT(aElement);
9779 Document* doc = aElement->OwnerDoc();
9780 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9781 if (registry) {
9782 registry->RegisterCallbackUpgradeElement(aElement, aTypeName);
9786 /* static */
9787 void nsContentUtils::RegisterUnresolvedElement(Element* aElement,
9788 nsAtom* aTypeName) {
9789 MOZ_ASSERT(aElement);
9791 Document* doc = aElement->OwnerDoc();
9792 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9793 if (registry) {
9794 registry->RegisterUnresolvedElement(aElement, aTypeName);
9798 /* static */
9799 void nsContentUtils::UnregisterUnresolvedElement(Element* aElement) {
9800 MOZ_ASSERT(aElement);
9802 nsAtom* typeAtom = aElement->GetCustomElementData()->GetCustomElementType();
9803 Document* doc = aElement->OwnerDoc();
9804 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9805 if (registry) {
9806 registry->UnregisterUnresolvedElement(aElement, typeAtom);
9810 /* static */
9811 void nsContentUtils::EnqueueUpgradeReaction(
9812 Element* aElement, CustomElementDefinition* aDefinition) {
9813 MOZ_ASSERT(aElement);
9815 Document* doc = aElement->OwnerDoc();
9817 // No DocGroup means no custom element reactions stack.
9818 if (!doc->GetDocGroup()) {
9819 return;
9822 CustomElementReactionsStack* stack =
9823 doc->GetDocGroup()->CustomElementReactionsStack();
9824 stack->EnqueueUpgradeReaction(aElement, aDefinition);
9827 /* static */
9828 void nsContentUtils::EnqueueLifecycleCallback(
9829 ElementCallbackType aType, Element* aCustomElement,
9830 const LifecycleCallbackArgs& aArgs, CustomElementDefinition* aDefinition) {
9831 // No DocGroup means no custom element reactions stack.
9832 if (!aCustomElement->OwnerDoc()->GetDocGroup()) {
9833 return;
9836 CustomElementRegistry::EnqueueLifecycleCallback(aType, aCustomElement, aArgs,
9837 aDefinition);
9840 /* static */
9841 void nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
9842 Document* aDocument, nsTArray<nsIContent*>& aElements) {
9843 MOZ_ASSERT(aDocument);
9844 #ifdef DEBUG
9845 size_t oldLength = aElements.Length();
9846 #endif
9848 if (PresShell* presShell = aDocument->GetPresShell()) {
9849 if (nsIFrame* scrollFrame = presShell->GetRootScrollFrame()) {
9850 nsIAnonymousContentCreator* creator = do_QueryFrame(scrollFrame);
9851 MOZ_ASSERT(
9852 creator,
9853 "scroll frame should always implement nsIAnonymousContentCreator");
9854 creator->AppendAnonymousContentTo(aElements, 0);
9856 if (nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame()) {
9857 canvasFrame->AppendAnonymousContentTo(aElements, 0);
9861 #ifdef DEBUG
9862 for (size_t i = oldLength; i < aElements.Length(); i++) {
9863 MOZ_ASSERT(
9864 aElements[i]->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent),
9865 "Someone here has lied, or missed to flag the node");
9867 #endif
9870 static void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame,
9871 nsTArray<nsIContent*>& aKids,
9872 uint32_t aFlags) {
9873 if (nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame)) {
9874 ac->AppendAnonymousContentTo(aKids, aFlags);
9878 /* static */
9879 void nsContentUtils::AppendNativeAnonymousChildren(const nsIContent* aContent,
9880 nsTArray<nsIContent*>& aKids,
9881 uint32_t aFlags) {
9882 if (aContent->MayHaveAnonymousChildren()) {
9883 if (nsIFrame* primaryFrame = aContent->GetPrimaryFrame()) {
9884 // NAC created by the element's primary frame.
9885 AppendNativeAnonymousChildrenFromFrame(primaryFrame, aKids, aFlags);
9887 // NAC created by any other non-primary frames for the element.
9888 AutoTArray<nsIFrame::OwnedAnonBox, 8> ownedAnonBoxes;
9889 primaryFrame->AppendOwnedAnonBoxes(ownedAnonBoxes);
9890 for (nsIFrame::OwnedAnonBox& box : ownedAnonBoxes) {
9891 MOZ_ASSERT(box.mAnonBoxFrame->GetContent() == aContent);
9892 AppendNativeAnonymousChildrenFromFrame(box.mAnonBoxFrame, aKids,
9893 aFlags);
9897 // Get manually created NAC (editor resize handles, etc.).
9898 if (auto nac = static_cast<ManualNACArray*>(
9899 aContent->GetProperty(nsGkAtoms::manualNACProperty))) {
9900 aKids.AppendElements(*nac);
9904 // The root scroll frame is not the primary frame of the root element.
9905 // Detect and handle this case.
9906 if (!(aFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
9907 aContent == aContent->OwnerDoc()->GetRootElement()) {
9908 AppendDocumentLevelNativeAnonymousContentTo(aContent->OwnerDoc(), aKids);
9912 bool nsContentUtils::IsImageAvailable(nsIContent* aLoadingNode, nsIURI* aURI,
9913 nsIPrincipal* aDefaultTriggeringPrincipal,
9914 CORSMode aCORSMode) {
9915 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
9916 QueryTriggeringPrincipal(aLoadingNode, aDefaultTriggeringPrincipal,
9917 getter_AddRefs(triggeringPrincipal));
9918 MOZ_ASSERT(triggeringPrincipal);
9920 Document* doc = aLoadingNode->OwnerDoc();
9921 imgLoader* imgLoader = GetImgLoaderForDocument(doc);
9922 return imgLoader->IsImageAvailable(aURI, triggeringPrincipal, aCORSMode, doc);
9925 /* static */
9926 bool nsContentUtils::QueryTriggeringPrincipal(
9927 nsIContent* aLoadingNode, nsIPrincipal* aDefaultPrincipal,
9928 nsIPrincipal** aTriggeringPrincipal) {
9929 MOZ_ASSERT(aLoadingNode);
9930 MOZ_ASSERT(aTriggeringPrincipal);
9932 bool result = false;
9933 nsCOMPtr<nsIPrincipal> loadingPrincipal = aDefaultPrincipal;
9934 if (!loadingPrincipal) {
9935 loadingPrincipal = aLoadingNode->NodePrincipal();
9938 // If aLoadingNode is content, bail out early.
9939 if (!aLoadingNode->NodePrincipal()->IsSystemPrincipal()) {
9940 loadingPrincipal.forget(aTriggeringPrincipal);
9941 return result;
9944 nsAutoString loadingStr;
9945 if (aLoadingNode->IsElement()) {
9946 aLoadingNode->AsElement()->GetAttr(
9947 kNameSpaceID_None, nsGkAtoms::triggeringprincipal, loadingStr);
9950 // Fall back if 'triggeringprincipal' isn't specified,
9951 if (loadingStr.IsEmpty()) {
9952 loadingPrincipal.forget(aTriggeringPrincipal);
9953 return result;
9956 nsCString binary;
9957 nsresult rv = Base64Decode(NS_ConvertUTF16toUTF8(loadingStr), binary);
9958 if (NS_SUCCEEDED(rv)) {
9959 nsCOMPtr<nsIPrincipal> serializedPrin = BasePrincipal::FromJSON(binary);
9960 if (serializedPrin) {
9961 result = true;
9962 serializedPrin.forget(aTriggeringPrincipal);
9964 } else {
9965 MOZ_ASSERT(false, "Unable to deserialize base64 principal");
9968 if (!result) {
9969 // Fallback if the deserialization is failed.
9970 loadingPrincipal.forget(aTriggeringPrincipal);
9973 return result;
9976 /* static */
9977 void nsContentUtils::GetContentPolicyTypeForUIImageLoading(
9978 nsIContent* aLoadingNode, nsIPrincipal** aTriggeringPrincipal,
9979 nsContentPolicyType& aContentPolicyType, uint64_t* aRequestContextID) {
9980 MOZ_ASSERT(aRequestContextID);
9982 bool result = QueryTriggeringPrincipal(aLoadingNode, aTriggeringPrincipal);
9983 if (result) {
9984 // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for
9985 // indicating it's a favicon loading.
9986 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
9988 nsAutoString requestContextID;
9989 if (aLoadingNode->IsElement()) {
9990 aLoadingNode->AsElement()->GetAttr(
9991 kNameSpaceID_None, nsGkAtoms::requestcontextid, requestContextID);
9993 nsresult rv;
9994 int64_t val = requestContextID.ToInteger64(&rv);
9995 *aRequestContextID = NS_SUCCEEDED(rv) ? val : 0;
9996 } else {
9997 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
10001 /* static */
10002 nsresult nsContentUtils::CreateJSValueFromSequenceOfObject(
10003 JSContext* aCx, const Sequence<JSObject*>& aTransfer,
10004 JS::MutableHandle<JS::Value> aValue) {
10005 if (aTransfer.IsEmpty()) {
10006 return NS_OK;
10009 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, aTransfer.Length()));
10010 if (!array) {
10011 return NS_ERROR_OUT_OF_MEMORY;
10014 for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
10015 JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
10016 if (!object) {
10017 continue;
10020 if (NS_WARN_IF(
10021 !JS_DefineElement(aCx, array, i, object, JSPROP_ENUMERATE))) {
10022 return NS_ERROR_OUT_OF_MEMORY;
10026 aValue.setObject(*array);
10027 return NS_OK;
10030 /* static */
10031 void nsContentUtils::StructuredClone(JSContext* aCx, nsIGlobalObject* aGlobal,
10032 JS::Handle<JS::Value> aValue,
10033 const StructuredSerializeOptions& aOptions,
10034 JS::MutableHandle<JS::Value> aRetval,
10035 ErrorResult& aError) {
10036 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
10037 aError = nsContentUtils::CreateJSValueFromSequenceOfObject(
10038 aCx, aOptions.mTransfer, &transferArray);
10039 if (NS_WARN_IF(aError.Failed())) {
10040 return;
10043 JS::CloneDataPolicy clonePolicy;
10044 // We are definitely staying in the same agent cluster.
10045 clonePolicy.allowIntraClusterClonableSharedObjects();
10046 if (aGlobal->IsSharedMemoryAllowed()) {
10047 clonePolicy.allowSharedMemoryObjects();
10050 StructuredCloneHolder holder(StructuredCloneHolder::CloningSupported,
10051 StructuredCloneHolder::TransferringSupported,
10052 JS::StructuredCloneScope::SameProcess);
10053 holder.Write(aCx, aValue, transferArray, clonePolicy, aError);
10054 if (NS_WARN_IF(aError.Failed())) {
10055 return;
10058 holder.Read(aGlobal, aCx, aRetval, clonePolicy, aError);
10059 if (NS_WARN_IF(aError.Failed())) {
10060 return;
10063 nsTArray<RefPtr<MessagePort>> ports = holder.TakeTransferredPorts();
10064 Unused << ports;
10067 /* static */
10068 bool nsContentUtils::ShouldBlockReservedKeys(WidgetKeyboardEvent* aKeyEvent) {
10069 nsCOMPtr<nsIPrincipal> principal;
10070 RefPtr<Element> targetElement =
10071 Element::FromEventTargetOrNull(aKeyEvent->mOriginalTarget);
10072 nsCOMPtr<nsIBrowser> targetBrowser;
10073 if (targetElement) {
10074 targetBrowser = targetElement->AsBrowser();
10076 bool isRemoteBrowser = false;
10077 if (targetBrowser) {
10078 targetBrowser->GetIsRemoteBrowser(&isRemoteBrowser);
10081 if (isRemoteBrowser) {
10082 targetBrowser->GetContentPrincipal(getter_AddRefs(principal));
10083 return principal ? nsContentUtils::IsSitePermDeny(principal, "shortcuts"_ns)
10084 : false;
10087 if (targetElement) {
10088 Document* doc = targetElement->GetUncomposedDoc();
10089 if (doc) {
10090 RefPtr<WindowContext> wc = doc->GetWindowContext();
10091 if (wc) {
10092 return wc->TopWindowContext()->GetShortcutsPermission() ==
10093 nsIPermissionManager::DENY_ACTION;
10098 return false;
10102 * Checks whether the given type is a supported document type for
10103 * loading within the nsObjectLoadingContent specified by aContent.
10105 * NOTE Helper method for nsContentUtils::HtmlObjectContentTypeForMIMEType.
10106 * NOTE Does not take content policy or capabilities into account
10108 static bool HtmlObjectContentSupportsDocument(const nsCString& aMimeType) {
10109 nsCOMPtr<nsIWebNavigationInfo> info(
10110 do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID));
10111 if (!info) {
10112 return false;
10115 uint32_t supported;
10116 nsresult rv = info->IsTypeSupported(aMimeType, &supported);
10118 if (NS_FAILED(rv)) {
10119 return false;
10122 if (supported != nsIWebNavigationInfo::UNSUPPORTED) {
10123 // Don't want to support plugins as documents
10124 return supported != nsIWebNavigationInfo::FALLBACK;
10127 // Try a stream converter
10128 // NOTE: We treat any type we can convert from as a supported type. If a
10129 // type is not actually supported, the URI loader will detect that and
10130 // return an error, and we'll fallback.
10131 nsCOMPtr<nsIStreamConverterService> convServ =
10132 do_GetService("@mozilla.org/streamConverters;1");
10133 bool canConvert = false;
10134 if (convServ) {
10135 rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
10137 return NS_SUCCEEDED(rv) && canConvert;
10140 /* static */
10141 already_AddRefed<nsIPluginTag> nsContentUtils::PluginTagForType(
10142 const nsCString& aMIMEType, bool aNoFakePlugin) {
10143 RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
10144 nsCOMPtr<nsIPluginTag> tag;
10145 NS_ENSURE_TRUE(pluginHost, nullptr);
10147 // ShouldPlay will handle the case where the plugin is disabled
10148 pluginHost->GetPluginTagForType(
10149 aMIMEType,
10150 aNoFakePlugin ? nsPluginHost::eExcludeFake : nsPluginHost::eExcludeNone,
10151 getter_AddRefs(tag));
10153 return tag.forget();
10156 /* static */
10157 uint32_t nsContentUtils::HtmlObjectContentTypeForMIMEType(
10158 const nsCString& aMIMEType, bool aNoFakePlugin) {
10159 if (aMIMEType.IsEmpty()) {
10160 return nsIObjectLoadingContent::TYPE_NULL;
10163 if (imgLoader::SupportImageWithMimeType(aMIMEType)) {
10164 return ResolveObjectType(nsIObjectLoadingContent::TYPE_IMAGE);
10167 // Faking support of the PDF content as a document for EMBED tags
10168 // when internal PDF viewer is enabled.
10169 if (aMIMEType.LowerCaseEqualsLiteral("application/pdf") && IsPDFJSEnabled()) {
10170 return nsIObjectLoadingContent::TYPE_DOCUMENT;
10173 if (HtmlObjectContentSupportsDocument(aMIMEType)) {
10174 return nsIObjectLoadingContent::TYPE_DOCUMENT;
10177 bool isSpecialPlugin = nsPluginHost::GetSpecialType(aMIMEType) !=
10178 nsPluginHost::eSpecialType_None;
10179 if (isSpecialPlugin) {
10180 return nsIObjectLoadingContent::TYPE_FALLBACK;
10183 return nsIObjectLoadingContent::TYPE_NULL;
10186 /* static */
10187 already_AddRefed<nsISerialEventTarget> nsContentUtils::GetEventTargetByLoadInfo(
10188 nsILoadInfo* aLoadInfo, TaskCategory aCategory) {
10189 if (NS_WARN_IF(!aLoadInfo)) {
10190 return nullptr;
10193 RefPtr<Document> doc;
10194 aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
10195 nsCOMPtr<nsISerialEventTarget> target;
10196 if (doc) {
10197 if (DocGroup* group = doc->GetDocGroup()) {
10198 target = group->EventTargetFor(aCategory);
10200 } else {
10201 target = GetMainThreadSerialEventTarget();
10204 return target.forget();
10207 /* static */
10208 bool nsContentUtils::IsLocalRefURL(const nsAString& aString) {
10209 return !aString.IsEmpty() && aString[0] == '#';
10212 // We use only 53 bits for the ID so that it can be converted to and from a JS
10213 // value without loss of precision. The upper bits of the ID hold the process
10214 // ID. The lower bits identify the object itself.
10215 static constexpr uint64_t kIdTotalBits = 53;
10216 static constexpr uint64_t kIdProcessBits = 22;
10217 static constexpr uint64_t kIdBits = kIdTotalBits - kIdProcessBits;
10219 /* static */
10220 uint64_t nsContentUtils::GenerateProcessSpecificId(uint64_t aId) {
10221 uint64_t processId = 0;
10222 if (XRE_IsContentProcess()) {
10223 ContentChild* cc = ContentChild::GetSingleton();
10224 processId = cc->GetID();
10227 MOZ_RELEASE_ASSERT(processId < (uint64_t(1) << kIdProcessBits));
10228 uint64_t processBits = processId & ((uint64_t(1) << kIdProcessBits) - 1);
10230 uint64_t id = aId;
10231 MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
10232 uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
10234 return (processBits << kIdBits) | bits;
10237 /* static */
10238 std::tuple<uint64_t, uint64_t> nsContentUtils::SplitProcessSpecificId(
10239 uint64_t aId) {
10240 return {aId >> kIdBits, aId & ((uint64_t(1) << kIdBits) - 1)};
10243 // Next process-local Tab ID.
10244 static uint64_t gNextTabId = 0;
10246 /* static */
10247 uint64_t nsContentUtils::GenerateTabId() {
10248 return GenerateProcessSpecificId(++gNextTabId);
10251 // Next process-local Browser ID.
10252 static uint64_t gNextBrowserId = 0;
10254 /* static */
10255 uint64_t nsContentUtils::GenerateBrowserId() {
10256 return GenerateProcessSpecificId(++gNextBrowserId);
10259 // Next process-local Browsing Context ID.
10260 static uint64_t gNextBrowsingContextId = 0;
10262 /* static */
10263 uint64_t nsContentUtils::GenerateBrowsingContextId() {
10264 return GenerateProcessSpecificId(++gNextBrowsingContextId);
10267 // Next process-local Window ID.
10268 static uint64_t gNextWindowId = 0;
10270 /* static */
10271 uint64_t nsContentUtils::GenerateWindowId() {
10272 return GenerateProcessSpecificId(++gNextWindowId);
10275 // Next process-local load.
10276 static Atomic<uint64_t> gNextLoadIdentifier(0);
10278 /* static */
10279 uint64_t nsContentUtils::GenerateLoadIdentifier() {
10280 return GenerateProcessSpecificId(++gNextLoadIdentifier);
10283 /* static */
10284 bool nsContentUtils::GetUserIsInteracting() {
10285 return UserInteractionObserver::sUserActive;
10288 /* static */
10289 bool nsContentUtils::GetSourceMapURL(nsIHttpChannel* aChannel,
10290 nsACString& aResult) {
10291 nsresult rv = aChannel->GetResponseHeader("SourceMap"_ns, aResult);
10292 if (NS_FAILED(rv)) {
10293 rv = aChannel->GetResponseHeader("X-SourceMap"_ns, aResult);
10295 return NS_SUCCEEDED(rv);
10298 /* static */
10299 bool nsContentUtils::IsMessageInputEvent(const IPC::Message& aMsg) {
10300 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10301 mozilla::dom::PBrowser::PBrowserStart) {
10302 switch (aMsg.type()) {
10303 case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
10304 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10305 case mozilla::dom::PBrowser::Msg_RealMouseEnterExitWidgetEvent__ID:
10306 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10307 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10308 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10309 case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
10310 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10311 case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
10312 return true;
10315 return false;
10318 /* static */
10319 bool nsContentUtils::IsMessageCriticalInputEvent(const IPC::Message& aMsg) {
10320 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10321 mozilla::dom::PBrowser::PBrowserStart) {
10322 switch (aMsg.type()) {
10323 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10324 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10325 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10326 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10327 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10328 return true;
10331 return false;
10334 static const char* kUserInteractionInactive = "user-interaction-inactive";
10335 static const char* kUserInteractionActive = "user-interaction-active";
10337 void nsContentUtils::UserInteractionObserver::Init() {
10338 // Listen for the observer messages from EventStateManager which are telling
10339 // us whether or not the user is interacting.
10340 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10341 obs->AddObserver(this, kUserInteractionInactive, false);
10342 obs->AddObserver(this, kUserInteractionActive, false);
10344 // We can't register ourselves as an annotator yet, as the
10345 // BackgroundHangMonitor hasn't started yet. It will have started by the
10346 // time we have the chance to spin the event loop.
10347 RefPtr<UserInteractionObserver> self = this;
10348 NS_DispatchToMainThread(NS_NewRunnableFunction(
10349 "nsContentUtils::UserInteractionObserver::Init",
10350 [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
10353 void nsContentUtils::UserInteractionObserver::Shutdown() {
10354 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10355 if (obs) {
10356 obs->RemoveObserver(this, kUserInteractionInactive);
10357 obs->RemoveObserver(this, kUserInteractionActive);
10360 BackgroundHangMonitor::UnregisterAnnotator(*this);
10364 * NB: This function is always called by the BackgroundHangMonitor thread.
10365 * Plan accordingly
10367 void nsContentUtils::UserInteractionObserver::AnnotateHang(
10368 BackgroundHangAnnotations& aAnnotations) {
10369 // NOTE: Only annotate the hang report if the user is known to be interacting.
10370 if (sUserActive) {
10371 aAnnotations.AddAnnotation(u"UserInteracting"_ns, true);
10375 NS_IMETHODIMP
10376 nsContentUtils::UserInteractionObserver::Observe(nsISupports* aSubject,
10377 const char* aTopic,
10378 const char16_t* aData) {
10379 if (!strcmp(aTopic, kUserInteractionInactive)) {
10380 if (sUserActive && XRE_IsParentProcess()) {
10381 glean::RecordPowerMetrics();
10383 sUserActive = false;
10384 } else if (!strcmp(aTopic, kUserInteractionActive)) {
10385 if (!sUserActive && XRE_IsParentProcess()) {
10386 glean::RecordPowerMetrics();
10388 nsCOMPtr<nsIUserIdleServiceInternal> idleService =
10389 do_GetService("@mozilla.org/widget/useridleservice;1");
10390 if (idleService) {
10391 idleService->ResetIdleTimeOut(0);
10395 sUserActive = true;
10396 } else {
10397 NS_WARNING("Unexpected observer notification");
10399 return NS_OK;
10402 Atomic<bool> nsContentUtils::UserInteractionObserver::sUserActive(false);
10403 NS_IMPL_ISUPPORTS(nsContentUtils::UserInteractionObserver, nsIObserver)
10405 /* static */
10406 bool nsContentUtils::IsSpecialName(const nsAString& aName) {
10407 return aName.LowerCaseEqualsLiteral("_blank") ||
10408 aName.LowerCaseEqualsLiteral("_top") ||
10409 aName.LowerCaseEqualsLiteral("_parent") ||
10410 aName.LowerCaseEqualsLiteral("_self");
10413 /* static */
10414 bool nsContentUtils::IsOverridingWindowName(const nsAString& aName) {
10415 return !aName.IsEmpty() && !IsSpecialName(aName);
10418 // Unfortunately, we can't unwrap an IDL object using only a concrete type.
10419 // We need to calculate type data based on the IDL typename. Which means
10420 // wrapping our templated function in a macro.
10421 #define EXTRACT_EXN_VALUES(T, ...) \
10422 ExtractExceptionValues<mozilla::dom::prototypes::id::T, \
10423 T##_Binding::NativeType, T>(__VA_ARGS__) \
10424 .isOk()
10426 template <prototypes::ID PrototypeID, class NativeType, typename T>
10427 static Result<Ok, nsresult> ExtractExceptionValues(
10428 JSContext* aCx, JS::Handle<JSObject*> aObj, nsAString& aSourceSpecOut,
10429 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10430 AssertStaticUnwrapOK<PrototypeID>();
10431 RefPtr<T> exn;
10432 MOZ_TRY((UnwrapObject<PrototypeID, NativeType>(aObj, exn, nullptr)));
10434 exn->GetFilename(aCx, aSourceSpecOut);
10435 if (!aSourceSpecOut.IsEmpty()) {
10436 *aLineOut = exn->LineNumber(aCx);
10437 *aColumnOut = exn->ColumnNumber();
10440 exn->GetName(aMessageOut);
10441 aMessageOut.AppendLiteral(": ");
10443 nsAutoString message;
10444 exn->GetMessageMoz(message);
10445 aMessageOut.Append(message);
10446 return Ok();
10449 /* static */
10450 void nsContentUtils::ExtractErrorValues(
10451 JSContext* aCx, JS::Handle<JS::Value> aValue, nsACString& aSourceSpecOut,
10452 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10453 nsAutoString sourceSpec;
10454 ExtractErrorValues(aCx, aValue, sourceSpec, aLineOut, aColumnOut,
10455 aMessageOut);
10456 CopyUTF16toUTF8(sourceSpec, aSourceSpecOut);
10459 /* static */
10460 void nsContentUtils::ExtractErrorValues(
10461 JSContext* aCx, JS::Handle<JS::Value> aValue, nsAString& aSourceSpecOut,
10462 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10463 MOZ_ASSERT(aLineOut);
10464 MOZ_ASSERT(aColumnOut);
10466 if (aValue.isObject()) {
10467 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
10469 // Try to process as an Error object. Use the file/line/column values
10470 // from the Error as they will be more specific to the root cause of
10471 // the problem.
10472 JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
10473 if (err) {
10474 // Use xpc to extract the error message only. We don't actually send
10475 // this report anywhere.
10476 RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
10477 report->Init(err,
10478 nullptr, // toString result
10479 false, // chrome
10480 0); // window ID
10482 if (!report->mFileName.IsEmpty()) {
10483 aSourceSpecOut = report->mFileName;
10484 *aLineOut = report->mLineNumber;
10485 *aColumnOut = report->mColumn;
10487 aMessageOut.Assign(report->mErrorMsg);
10490 // Next, try to unwrap the rejection value as a DOMException.
10491 else if (EXTRACT_EXN_VALUES(DOMException, aCx, obj, aSourceSpecOut,
10492 aLineOut, aColumnOut, aMessageOut)) {
10493 return;
10496 // Next, try to unwrap the rejection value as an XPC Exception.
10497 else if (EXTRACT_EXN_VALUES(Exception, aCx, obj, aSourceSpecOut, aLineOut,
10498 aColumnOut, aMessageOut)) {
10499 return;
10503 // If we could not unwrap a specific error type, then perform default safe
10504 // string conversions on primitives. Objects will result in "[Object]"
10505 // unfortunately.
10506 if (aMessageOut.IsEmpty()) {
10507 nsAutoJSString jsString;
10508 if (jsString.init(aCx, aValue)) {
10509 aMessageOut = jsString;
10510 } else {
10511 JS_ClearPendingException(aCx);
10516 #undef EXTRACT_EXN_VALUES
10518 /* static */
10519 bool nsContentUtils::ContentIsLink(nsIContent* aContent) {
10520 if (!aContent || !aContent->IsElement()) {
10521 return false;
10524 if (aContent->IsHTMLElement(nsGkAtoms::a)) {
10525 return true;
10528 return aContent->AsElement()->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
10529 nsGkAtoms::simple, eCaseMatters);
10532 /* static */
10533 already_AddRefed<ContentFrameMessageManager>
10534 nsContentUtils::TryGetBrowserChildGlobal(nsISupports* aFrom) {
10535 RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(aFrom);
10536 if (!frameLoaderOwner) {
10537 return nullptr;
10540 RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
10541 if (!frameLoader) {
10542 return nullptr;
10545 RefPtr<ContentFrameMessageManager> manager =
10546 frameLoader->GetBrowserChildMessageManager();
10547 return manager.forget();
10550 /* static */
10551 uint32_t nsContentUtils::InnerOrOuterWindowCreated() {
10552 MOZ_ASSERT(NS_IsMainThread());
10553 ++sInnerOrOuterWindowCount;
10554 return ++sInnerOrOuterWindowSerialCounter;
10557 /* static */
10558 void nsContentUtils::InnerOrOuterWindowDestroyed() {
10559 MOZ_ASSERT(NS_IsMainThread());
10560 MOZ_ASSERT(sInnerOrOuterWindowCount > 0);
10561 --sInnerOrOuterWindowCount;
10564 static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
10565 nsAString* result = static_cast<nsAString*>(aData);
10566 result->Append(static_cast<const char16_t*>(aBuf),
10567 static_cast<uint32_t>(aLen));
10568 return true;
10571 /* static */
10572 nsresult nsContentUtils::AnonymizeURI(nsIURI* aURI, nsCString& aAnonymizedURI) {
10573 MOZ_ASSERT(aURI);
10575 if (aURI->SchemeIs("data")) {
10576 aAnonymizedURI.Assign("data:..."_ns);
10577 return NS_OK;
10579 // Anonymize the URL.
10580 // Strip the URL of any possible username/password and make it ready to be
10581 // presented in the UI.
10582 nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(aURI);
10583 return exposableURI->GetSpec(aAnonymizedURI);
10586 /* static */
10587 bool nsContentUtils::StringifyJSON(JSContext* aCx,
10588 JS::MutableHandle<JS::Value> aValue,
10589 nsAString& aOutStr) {
10590 MOZ_ASSERT(aCx);
10591 aOutStr.Truncate();
10592 JS::Rooted<JS::Value> value(aCx, aValue.get());
10593 nsAutoString serializedValue;
10594 NS_ENSURE_TRUE(JS_Stringify(aCx, &value, nullptr, JS::NullHandleValue,
10595 JSONCreator, &serializedValue),
10596 false);
10597 aOutStr = serializedValue;
10598 return true;
10601 /* static */
10602 bool nsContentUtils::
10603 HighPriorityEventPendingForTopLevelDocumentBeforeContentfulPaint(
10604 Document* aDocument) {
10605 MOZ_ASSERT(XRE_IsContentProcess(),
10606 "This function only makes sense in content processes");
10608 if (aDocument && !aDocument->IsLoadedAsData()) {
10609 if (nsPresContext* presContext = FindPresContextForDocument(aDocument)) {
10610 MOZ_ASSERT(!presContext->IsChrome(),
10611 "Should never have a chrome PresContext in a content process");
10613 return !presContext->GetInProcessRootContentDocumentPresContext()
10614 ->HadContentfulPaint() &&
10615 nsThreadManager::MainThreadHasPendingHighPriorityEvents();
10618 return false;
10621 static nsGlobalWindowInner* GetInnerWindowForGlobal(nsIGlobalObject* aGlobal) {
10622 NS_ENSURE_TRUE(aGlobal, nullptr);
10624 if (auto* window = aGlobal->AsInnerWindow()) {
10625 return nsGlobalWindowInner::Cast(window);
10628 // When Extensions run content scripts inside a sandbox, it uses
10629 // sandboxPrototype to make them appear as though they're running in the
10630 // scope of the page. So when a content script invokes postMessage, it expects
10631 // the |source| of the received message to be the window set as the
10632 // sandboxPrototype. This used to work incidentally for unrelated reasons, but
10633 // now we need to do some special handling to support it.
10634 JS::Rooted<JSObject*> scope(RootingCx(), aGlobal->GetGlobalJSObject());
10635 NS_ENSURE_TRUE(scope, nullptr);
10637 if (xpc::IsSandbox(scope)) {
10638 AutoJSAPI jsapi;
10639 MOZ_ALWAYS_TRUE(jsapi.Init(scope));
10640 JSContext* cx = jsapi.cx();
10641 // Our current Realm on aCx is the sandbox. Using that for unwrapping
10642 // makes sense: if the sandbox can unwrap the window, we can use it.
10643 return xpc::SandboxWindowOrNull(scope, cx);
10646 // The calling window must be holding a reference, so we can return a weak
10647 // pointer.
10648 return nsGlobalWindowInner::Cast(aGlobal->AsInnerWindow());
10651 /* static */
10652 nsGlobalWindowInner* nsContentUtils::IncumbentInnerWindow() {
10653 return GetInnerWindowForGlobal(GetIncumbentGlobal());
10656 /* static */
10657 nsGlobalWindowInner* nsContentUtils::EntryInnerWindow() {
10658 return GetInnerWindowForGlobal(GetEntryGlobal());
10661 /* static */
10662 bool nsContentUtils::IsURIInPrefList(nsIURI* aURI, const char* aPrefName) {
10663 MOZ_ASSERT(aPrefName);
10665 nsAutoCString list;
10666 Preferences::GetCString(aPrefName, list);
10667 ToLowerCase(list);
10668 return IsURIInList(aURI, list);
10671 /* static */
10672 bool nsContentUtils::IsURIInList(nsIURI* aURI, const nsCString& aList) {
10673 #ifdef DEBUG
10674 nsAutoCString listLowerCase(aList);
10675 ToLowerCase(listLowerCase);
10676 MOZ_ASSERT(listLowerCase.Equals(aList),
10677 "The aList argument should be lower-case");
10678 #endif
10680 if (!aURI) {
10681 return false;
10684 nsAutoCString scheme;
10685 aURI->GetScheme(scheme);
10686 if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
10687 return false;
10690 if (aList.IsEmpty()) {
10691 return false;
10694 // The list is comma separated domain list. Each item may start with "*.".
10695 // If starts with "*.", it matches any sub-domains.
10697 nsCCharSeparatedTokenizer tokenizer(aList, ',');
10698 while (tokenizer.hasMoreTokens()) {
10699 const nsCString token(tokenizer.nextToken());
10701 nsAutoCString host;
10702 aURI->GetHost(host);
10703 if (host.IsEmpty()) {
10704 return false;
10706 ToLowerCase(host);
10708 for (;;) {
10709 int32_t index = token.Find(host);
10710 if (index >= 0 &&
10711 static_cast<uint32_t>(index) + host.Length() <= token.Length()) {
10712 // If we found a full match, return true.
10713 size_t indexAfterHost = index + host.Length();
10714 if (index == 0 && indexAfterHost == token.Length()) {
10715 return true;
10717 // If next character is '/', we need to check the path too.
10718 // We assume the path in the list means "/foo" + "*".
10719 if (token[indexAfterHost] == '/') {
10720 nsDependentCSubstring pathInList(
10721 token, indexAfterHost,
10722 static_cast<nsDependentCSubstring::size_type>(-1));
10723 nsAutoCString filePath;
10724 aURI->GetFilePath(filePath);
10725 ToLowerCase(filePath);
10726 if (StringBeginsWith(filePath, pathInList) &&
10727 (filePath.Length() == pathInList.Length() ||
10728 pathInList.EqualsLiteral("/") ||
10729 filePath[pathInList.Length() - 1] == '/' ||
10730 filePath[pathInList.Length() - 1] == '?' ||
10731 filePath[pathInList.Length() - 1] == '#')) {
10732 return true;
10736 int32_t startIndexOfCurrentLevel = host[0] == '*' ? 1 : 0;
10737 int32_t startIndexOfNextLevel =
10738 host.Find(".", startIndexOfCurrentLevel + 1);
10739 if (startIndexOfNextLevel <= 0) {
10740 break;
10742 host = "*"_ns + nsDependentCSubstring(host, startIndexOfNextLevel);
10746 return false;
10749 /* static */
10750 ScreenIntMargin nsContentUtils::GetWindowSafeAreaInsets(
10751 nsIScreen* aScreen, const ScreenIntMargin& aSafeAreaInsets,
10752 const LayoutDeviceIntRect& aWindowRect) {
10753 // This calculates safe area insets of window from screen rectangle, window
10754 // rectangle and safe area insets of screen.
10756 // +----------------------------------------+ <-- screen
10757 // | +-------------------------------+ <------- window
10758 // | | window's safe area inset top) | |
10759 // +--+-------------------------------+--+ |
10760 // | | | |<------ safe area rectangle of
10761 // | | | | | screen
10762 // +--+-------------------------------+--+ |
10763 // | |window's safe area inset bottom| |
10764 // | +-------------------------------+ |
10765 // +----------------------------------------+
10767 ScreenIntMargin windowSafeAreaInsets;
10769 if (windowSafeAreaInsets == aSafeAreaInsets) {
10770 // no safe area insets.
10771 return windowSafeAreaInsets;
10774 int32_t screenLeft, screenTop, screenWidth, screenHeight;
10775 nsresult rv =
10776 aScreen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
10777 if (NS_WARN_IF(NS_FAILED(rv))) {
10778 return windowSafeAreaInsets;
10781 const ScreenIntRect screenRect(screenLeft, screenTop, screenWidth,
10782 screenHeight);
10784 ScreenIntRect safeAreaRect = screenRect;
10785 safeAreaRect.Deflate(aSafeAreaInsets);
10787 ScreenIntRect windowRect = ViewAs<ScreenPixel>(
10788 aWindowRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
10790 // FIXME(bug 1754323): This can trigger because the screen rect is not
10791 // orientation-aware.
10792 // MOZ_ASSERT(screenRect.Contains(windowRect),
10793 // "Screen doesn't contain window rect? Something seems off");
10795 // window's rect of safe area
10796 safeAreaRect = safeAreaRect.Intersect(windowRect);
10798 windowSafeAreaInsets.top = safeAreaRect.y - aWindowRect.y;
10799 windowSafeAreaInsets.left = safeAreaRect.x - aWindowRect.x;
10800 windowSafeAreaInsets.right =
10801 aWindowRect.x + aWindowRect.width - (safeAreaRect.x + safeAreaRect.width);
10802 windowSafeAreaInsets.bottom = aWindowRect.y + aWindowRect.height -
10803 (safeAreaRect.y + safeAreaRect.height);
10805 windowSafeAreaInsets.EnsureAtLeast(ScreenIntMargin());
10806 // This shouldn't be needed, but it wallpapers orientation issues, see bug
10807 // 1754323.
10808 windowSafeAreaInsets.EnsureAtMost(aSafeAreaInsets);
10810 return windowSafeAreaInsets;
10813 /* static */
10814 nsContentUtils::SubresourceCacheValidationInfo
10815 nsContentUtils::GetSubresourceCacheValidationInfo(nsIRequest* aRequest,
10816 nsIURI* aURI) {
10817 SubresourceCacheValidationInfo info;
10818 if (nsCOMPtr<nsICacheInfoChannel> cache = do_QueryInterface(aRequest)) {
10819 uint32_t value = 0;
10820 if (NS_SUCCEEDED(cache->GetCacheTokenExpirationTime(&value))) {
10821 info.mExpirationTime.emplace(value);
10825 // Determine whether the cache entry must be revalidated when we try to use
10826 // it. Currently, only HTTP specifies this information...
10827 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest)) {
10828 Unused << httpChannel->IsNoStoreResponse(&info.mMustRevalidate);
10830 if (!info.mMustRevalidate) {
10831 Unused << httpChannel->IsNoCacheResponse(&info.mMustRevalidate);
10835 // data: URIs are safe to cache across documents under any circumstance, so we
10836 // special-case them here even though the channel itself doesn't have any
10837 // caching policy. Same for chrome:// uris.
10839 // TODO(emilio): Figure out which other schemes that don't have caching
10840 // policies are safe to cache. Blobs should be...
10841 const bool knownCacheable = [&] {
10842 if (!aURI) {
10843 return false;
10845 if (aURI->SchemeIs("data") || aURI->SchemeIs("moz-page-thumb") ||
10846 aURI->SchemeIs("moz-extension")) {
10847 return true;
10849 if (aURI->SchemeIs("chrome") || aURI->SchemeIs("resource")) {
10850 return !StaticPrefs::nglayout_debug_disable_xul_cache();
10852 return false;
10853 }();
10855 if (knownCacheable) {
10856 MOZ_ASSERT(!info.mExpirationTime);
10857 MOZ_ASSERT(!info.mMustRevalidate);
10858 info.mExpirationTime = Some(0); // 0 means "doesn't expire".
10861 return info;
10864 nsCString nsContentUtils::TruncatedURLForDisplay(nsIURI* aURL, size_t aMaxLen) {
10865 nsCString spec;
10866 if (aURL) {
10867 aURL->GetSpec(spec);
10868 spec.Truncate(std::min(aMaxLen, spec.Length()));
10870 return spec;
10873 /* static */
10874 nsresult nsContentUtils::AnonymizeId(nsAString& aId,
10875 const nsACString& aOriginKey,
10876 OriginFormat aFormat) {
10877 MOZ_ASSERT(NS_IsMainThread());
10879 nsresult rv;
10880 nsCString rawKey;
10881 if (aFormat == OriginFormat::Base64) {
10882 rv = Base64Decode(aOriginKey, rawKey);
10883 NS_ENSURE_SUCCESS(rv, rv);
10884 } else {
10885 rawKey = aOriginKey;
10888 HMAC hmac;
10889 rv = hmac.Begin(
10890 SEC_OID_SHA256,
10891 Span(reinterpret_cast<const uint8_t*>(rawKey.get()), rawKey.Length()));
10892 NS_ENSURE_SUCCESS(rv, rv);
10894 NS_ConvertUTF16toUTF8 id(aId);
10895 rv = hmac.Update(reinterpret_cast<const uint8_t*>(id.get()), id.Length());
10896 NS_ENSURE_SUCCESS(rv, rv);
10898 nsTArray<uint8_t> macBytes;
10899 rv = hmac.End(macBytes);
10900 NS_ENSURE_SUCCESS(rv, rv);
10902 nsCString macBase64;
10903 rv = Base64Encode(
10904 nsDependentCSubstring(reinterpret_cast<const char*>(macBytes.Elements()),
10905 macBytes.Length()),
10906 macBase64);
10907 NS_ENSURE_SUCCESS(rv, rv);
10909 CopyUTF8toUTF16(macBase64, aId);
10910 return NS_OK;
10913 /* static */
10914 bool nsContentUtils::ShouldHideObjectOrEmbedImageDocument() {
10915 return StaticPrefs::
10916 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup() &&
10917 StaticPrefs::
10918 browser_opaqueResponseBlocking_syntheticBrowsingContext_filter_AtStartup_DoNotUseDirectly();
10921 /* static */
10922 uint32_t nsContentUtils::ResolveObjectType(uint32_t aType) {
10923 if (!StaticPrefs::
10924 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup()) {
10925 return aType;
10928 if (aType != nsIObjectLoadingContent::TYPE_IMAGE) {
10929 return aType;
10932 return nsIObjectLoadingContent::TYPE_DOCUMENT;
10935 void nsContentUtils::RequestGeckoTaskBurst() {
10936 nsCOMPtr<nsIAppShell> appShell = do_GetService(NS_APPSHELL_CID);
10937 if (appShell) {
10938 appShell->GeckoTaskBurst();
10942 namespace mozilla {
10943 std::ostream& operator<<(std::ostream& aOut,
10944 const PreventDefaultResult aPreventDefaultResult) {
10945 switch (aPreventDefaultResult) {
10946 case PreventDefaultResult::No:
10947 aOut << "unhandled";
10948 break;
10949 case PreventDefaultResult::ByContent:
10950 aOut << "handled-by-content";
10951 break;
10952 case PreventDefaultResult::ByChrome:
10953 aOut << "handled-by-chrome";
10954 break;
10956 return aOut;
10958 } // namespace mozilla