Bug 1700051: part 35) Reduce accessibility of `mSoftText.mDOMMapping` to `private...
[gecko.git] / dom / base / nsContentUtils.cpp
blob2290e61c7adc718c98c9e5763b7b193498b8c8bb
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 "ImageOps.h"
23 #include "InProcessBrowserChildMessageManager.h"
24 #include "Layers.h"
25 #include "MainThreadUtils.h"
26 #include "PLDHashTable.h"
27 #include "ReferrerInfo.h"
28 #include "ThirdPartyUtil.h"
29 #include "Units.h"
30 #include "chrome/common/ipc_message.h"
31 #include "gfxDrawable.h"
32 #include "harfbuzz/hb.h"
33 #include "imgICache.h"
34 #include "imgIContainer.h"
35 #include "imgILoader.h"
36 #include "imgIRequest.h"
37 #include "imgLoader.h"
38 #include "js/Array.h"
39 #include "js/ArrayBuffer.h"
40 #include "js/BuildId.h"
41 #include "js/GCAPI.h"
42 #include "js/Id.h"
43 #include "js/JSON.h"
44 #include "js/PropertyDescriptor.h"
45 #include "js/Realm.h"
46 #include "js/RegExp.h"
47 #include "js/RegExpFlags.h"
48 #include "js/RootingAPI.h"
49 #include "js/TypeDecls.h"
50 #include "js/Value.h"
51 #include "js/Wrapper.h"
52 #include "jsapi.h"
53 #include "jsfriendapi.h"
54 #include "mozAutoDocUpdate.h"
55 #include "mozIDOMWindow.h"
56 #include "mozilla/AlreadyAddRefed.h"
57 #include "mozilla/ArrayIterator.h"
58 #include "mozilla/ArrayUtils.h"
59 #include "mozilla/AsyncEventDispatcher.h"
60 #include "mozilla/AtomArray.h"
61 #include "mozilla/Atomics.h"
62 #include "mozilla/Attributes.h"
63 #include "mozilla/AutoRestore.h"
64 #include "mozilla/AutoTimelineMarker.h"
65 #include "mozilla/BackgroundHangMonitor.h"
66 #include "mozilla/Base64.h"
67 #include "mozilla/BasePrincipal.h"
68 #include "mozilla/BasicEvents.h"
69 #include "mozilla/BloomFilter.h"
70 #include "mozilla/CORSMode.h"
71 #include "mozilla/CallState.h"
72 #include "mozilla/CheckedInt.h"
73 #include "mozilla/Components.h"
74 #include "mozilla/CycleCollectedJSContext.h"
75 #include "mozilla/DOMEventTargetHelper.h"
76 #include "mozilla/DebugOnly.h"
77 #include "mozilla/ErrorResult.h"
78 #include "mozilla/EventDispatcher.h"
79 #include "mozilla/EventListenerManager.h"
80 #include "mozilla/EventQueue.h"
81 #include "mozilla/EventStateManager.h"
82 #include "mozilla/FlushType.h"
83 #include "mozilla/HTMLEditor.h"
84 #include "mozilla/HangAnnotations.h"
85 #include "mozilla/IMEStateManager.h"
86 #include "mozilla/InputEventOptions.h"
87 #include "mozilla/InternalMutationEvent.h"
88 #include "mozilla/Latin1.h"
89 #include "mozilla/Likely.h"
90 #include "mozilla/LoadInfo.h"
91 #include "mozilla/Logging.h"
92 #include "mozilla/MacroForEach.h"
93 #include "mozilla/ManualNAC.h"
94 #include "mozilla/Maybe.h"
95 #include "mozilla/MouseEvents.h"
96 #include "mozilla/NotNull.h"
97 #include "mozilla/NullPrincipal.h"
98 #include "mozilla/OriginAttributes.h"
99 #include "mozilla/Preferences.h"
100 #include "mozilla/PresShell.h"
101 #include "mozilla/RangeBoundary.h"
102 #include "mozilla/RefPtr.h"
103 #include "mozilla/Result.h"
104 #include "mozilla/ResultExtensions.h"
105 #include "mozilla/ScrollbarPreferences.h"
106 #include "mozilla/Span.h"
107 #include "mozilla/StaticAnalysisFunctions.h"
108 #include "mozilla/StaticPrefs_dom.h"
109 #ifdef FUZZING
110 # include "mozilla/StaticPrefs_fuzzing.h"
111 #endif
112 #include "mozilla/StaticPrefs_privacy.h"
113 #include "mozilla/StaticPrefs_test.h"
114 #include "mozilla/StaticPrefs_ui.h"
115 #include "mozilla/StaticPtr.h"
116 #include "mozilla/TaskCategory.h"
117 #include "mozilla/TextControlState.h"
118 #include "mozilla/TextEditor.h"
119 #include "mozilla/TextEvents.h"
120 #include "mozilla/UniquePtr.h"
121 #include "mozilla/Unused.h"
122 #include "mozilla/Variant.h"
123 #include "mozilla/ViewportUtils.h"
124 #include "mozilla/dom/AncestorIterator.h"
125 #include "mozilla/dom/AutoEntryScript.h"
126 #include "mozilla/dom/AutocompleteInfoBinding.h"
127 #include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
128 #include "mozilla/dom/BindingDeclarations.h"
129 #include "mozilla/dom/BindingUtils.h"
130 #include "mozilla/dom/BlobImpl.h"
131 #include "mozilla/dom/BlobURLProtocolHandler.h"
132 #include "mozilla/dom/BorrowedAttrInfo.h"
133 #include "mozilla/dom/BrowserBridgeParent.h"
134 #include "mozilla/dom/BrowserParent.h"
135 #include "mozilla/dom/BrowsingContext.h"
136 #include "mozilla/dom/BrowsingContextGroup.h"
137 #include "mozilla/dom/CallbackFunction.h"
138 #include "mozilla/dom/CallbackObject.h"
139 #include "mozilla/dom/ChromeMessageBroadcaster.h"
140 #include "mozilla/dom/ContentChild.h"
141 #include "mozilla/dom/ContentFrameMessageManager.h"
142 #include "mozilla/dom/ContentParent.h"
143 #include "mozilla/dom/CustomElementRegistry.h"
144 #include "mozilla/dom/CustomElementRegistryBinding.h"
145 #include "mozilla/dom/DOMArena.h"
146 #include "mozilla/dom/DOMException.h"
147 #include "mozilla/dom/DOMExceptionBinding.h"
148 #include "mozilla/dom/DOMSecurityMonitor.h"
149 #include "mozilla/dom/DOMTypes.h"
150 #include "mozilla/dom/DataTransfer.h"
151 #include "mozilla/dom/DocGroup.h"
152 #include "mozilla/dom/Document.h"
153 #include "mozilla/dom/DocumentFragment.h"
154 #include "mozilla/dom/DocumentInlines.h"
155 #include "mozilla/dom/Element.h"
156 #include "mozilla/dom/ElementBinding.h"
157 #include "mozilla/dom/ElementInlines.h"
158 #include "mozilla/dom/Event.h"
159 #include "mozilla/dom/EventTarget.h"
160 #include "mozilla/dom/FileBlobImpl.h"
161 #include "mozilla/dom/FileSystemSecurity.h"
162 #include "mozilla/dom/FilteredNodeIterator.h"
163 #include "mozilla/dom/FontTableURIProtocolHandler.h"
164 #include "mozilla/dom/FragmentOrElement.h"
165 #include "mozilla/dom/FromParser.h"
166 #include "mozilla/dom/HTMLFormElement.h"
167 #include "mozilla/dom/HTMLInputElement.h"
168 #include "mozilla/dom/HTMLTextAreaElement.h"
169 #include "mozilla/dom/IPCBlob.h"
170 #include "mozilla/dom/IPCBlobUtils.h"
171 #include "mozilla/dom/MessageBroadcaster.h"
172 #include "mozilla/dom/MessageListenerManager.h"
173 #include "mozilla/dom/MouseEventBinding.h"
174 #include "mozilla/dom/NameSpaceConstants.h"
175 #include "mozilla/dom/NodeBinding.h"
176 #include "mozilla/dom/NodeInfo.h"
177 #include "mozilla/dom/PBrowser.h"
178 #include "mozilla/dom/PContentChild.h"
179 #include "mozilla/dom/PrototypeList.h"
180 #include "mozilla/dom/ReferrerPolicyBinding.h"
181 #include "mozilla/dom/ScriptSettings.h"
182 #include "mozilla/dom/Selection.h"
183 #include "mozilla/dom/ShadowRoot.h"
184 #include "mozilla/dom/Text.h"
185 #include "mozilla/dom/UserActivation.h"
186 #include "mozilla/dom/WindowContext.h"
187 #include "mozilla/dom/WorkerCommon.h"
188 #include "mozilla/dom/WorkerPrivate.h"
189 #include "mozilla/dom/XULCommandEvent.h"
190 #include "mozilla/fallible.h"
191 #include "mozilla/gfx/2D.h"
192 #include "mozilla/gfx/BaseMargin.h"
193 #include "mozilla/gfx/BasePoint.h"
194 #include "mozilla/gfx/BaseSize.h"
195 #include "mozilla/gfx/DataSurfaceHelpers.h"
196 #include "mozilla/gfx/Point.h"
197 #include "mozilla/gfx/Rect.h"
198 #include "mozilla/gfx/Types.h"
199 #include "mozilla/intl/LineBreaker.h"
200 #include "mozilla/intl/WordBreaker.h"
201 #include "mozilla/ipc/ProtocolUtils.h"
202 #include "mozilla/ipc/SharedMemory.h"
203 #include "mozilla/ipc/Shmem.h"
204 #include "mozilla/net/UrlClassifierCommon.h"
205 #include "mozilla/widget/IMEData.h"
206 #include "nsAboutProtocolUtils.h"
207 #include "nsAlgorithm.h"
208 #include "nsArrayUtils.h"
209 #include "nsAtom.h"
210 #include "nsAttrName.h"
211 #include "nsAttrValue.h"
212 #include "nsAttrValueInlines.h"
213 #include "nsBaseHashtable.h"
214 #include "nsCCUncollectableMarker.h"
215 #include "nsCOMPtr.h"
216 #include "nsCRT.h"
217 #include "nsCRTGlue.h"
218 #include "nsCanvasFrame.h"
219 #include "nsCaseTreatment.h"
220 #include "nsCharSeparatedTokenizer.h"
221 #include "nsCharTraits.h"
222 #include "nsCompatibility.h"
223 #include "nsComponentManagerUtils.h"
224 #include "nsContainerFrame.h"
225 #include "nsContentCreatorFunctions.h"
226 #include "nsContentDLF.h"
227 #include "nsContentList.h"
228 #include "nsContentListDeclarations.h"
229 #include "nsContentPolicyUtils.h"
230 #include "nsCoord.h"
231 #include "nsCycleCollectionNoteChild.h"
232 #include "nsDOMMutationObserver.h"
233 #include "nsDOMString.h"
234 #include "nsTHashMap.h"
235 #include "nsDebug.h"
236 #include "nsDocShell.h"
237 #include "nsDocShellCID.h"
238 #include "nsError.h"
239 #include "nsFocusManager.h"
240 #include "nsFrameList.h"
241 #include "nsFrameLoader.h"
242 #include "nsFrameLoaderOwner.h"
243 #include "nsGenericHTMLElement.h"
244 #include "nsGkAtoms.h"
245 #include "nsGlobalWindowInner.h"
246 #include "nsGlobalWindowOuter.h"
247 #include "nsHTMLDocument.h"
248 #include "nsHTMLTags.h"
249 #include "nsHashKeys.h"
250 #include "nsHtml5StringParser.h"
251 #include "nsIAboutModule.h"
252 #include "nsIAnonymousContentCreator.h"
253 #include "nsIArray.h"
254 #include "nsIAsyncVerifyRedirectCallback.h"
255 #include "nsIBidiKeyboard.h"
256 #include "nsIBrowser.h"
257 #include "nsICacheInfoChannel.h"
258 #include "nsICategoryManager.h"
259 #include "nsIChannel.h"
260 #include "nsIChannelEventSink.h"
261 #include "nsIClassifiedChannel.h"
262 #include "nsIConsoleService.h"
263 #include "nsIContent.h"
264 #include "nsIContentInlines.h"
265 #include "nsIContentPolicy.h"
266 #include "nsIContentSecurityPolicy.h"
267 #include "nsIContentSink.h"
268 #include "nsIContentViewer.h"
269 #include "nsID.h"
270 #include "nsIDOMWindowUtils.h"
271 #include "nsIDocShell.h"
272 #include "nsIDocShellTreeItem.h"
273 #include "nsIDocumentEncoder.h"
274 #include "nsIDocumentLoaderFactory.h"
275 #include "nsIDragService.h"
276 #include "nsIDragSession.h"
277 #include "nsIFile.h"
278 #include "nsIFocusManager.h"
279 #include "nsIFormControl.h"
280 #include "nsIFragmentContentSink.h"
281 #include "nsIFrame.h"
282 #include "nsIGlobalObject.h"
283 #include "nsIHttpChannel.h"
284 #include "nsIHttpChannelInternal.h"
285 #include "nsIIOService.h"
286 #include "nsIImageLoadingContent.h"
287 #include "nsIInputStream.h"
288 #include "nsIInterfaceRequestor.h"
289 #include "nsIInterfaceRequestorUtils.h"
290 #include "nsILoadContext.h"
291 #include "nsILoadGroup.h"
292 #include "nsILoadInfo.h"
293 #include "nsIMIMEService.h"
294 #include "nsIMemoryReporter.h"
295 #include "nsINetUtil.h"
296 #include "nsINode.h"
297 #include "nsIObjectLoadingContent.h"
298 #include "nsIObserver.h"
299 #include "nsIObserverService.h"
300 #include "nsIOfflineCacheUpdate.h"
301 #include "nsIParser.h"
302 #include "nsIParserUtils.h"
303 #include "nsIPermissionManager.h"
304 #include "nsIPluginTag.h"
305 #include "nsIPrincipal.h"
306 #include "nsIProperties.h"
307 #include "nsIProtocolHandler.h"
308 #include "nsIRequest.h"
309 #include "nsIRunnable.h"
310 #include "nsIScreen.h"
311 #include "nsIScriptError.h"
312 #include "nsIScriptGlobalObject.h"
313 #include "nsIScriptObjectPrincipal.h"
314 #include "nsIScriptSecurityManager.h"
315 #include "nsISerialEventTarget.h"
316 #include "nsIStreamConverter.h"
317 #include "nsIStreamConverterService.h"
318 #include "nsIStringBundle.h"
319 #include "nsISupports.h"
320 #include "nsISupportsPrimitives.h"
321 #include "nsISupportsUtils.h"
322 #include "nsITransferable.h"
323 #include "nsIURI.h"
324 #include "nsIURIMutator.h"
325 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
326 # include "nsIURIWithSpecialOrigin.h"
327 #endif
328 #include "nsIUUIDGenerator.h"
329 #include "nsIUserIdleService.h"
330 #include "nsIWeakReferenceUtils.h"
331 #include "nsIWebNavigation.h"
332 #include "nsIWebNavigationInfo.h"
333 #include "nsIWidget.h"
334 #include "nsIWindowMediator.h"
335 #include "nsIXPConnect.h"
336 #include "nsJSPrincipals.h"
337 #include "nsJSUtils.h"
338 #include "nsLayoutUtils.h"
339 #include "nsLiteralString.h"
340 #include "nsMappedAttributes.h"
341 #include "nsMargin.h"
342 #include "nsMimeTypes.h"
343 #include "nsNameSpaceManager.h"
344 #include "nsNetCID.h"
345 #include "nsNetUtil.h"
346 #include "nsNodeInfoManager.h"
347 #include "nsPIDOMWindow.h"
348 #include "nsPIDOMWindowInlines.h"
349 #include "nsParserCIID.h"
350 #include "nsParserConstants.h"
351 #include "nsPluginHost.h"
352 #include "nsPoint.h"
353 #include "nsPointerHashKeys.h"
354 #include "nsPresContext.h"
355 #include "nsQueryFrame.h"
356 #include "nsQueryObject.h"
357 #include "nsRFPService.h"
358 #include "nsRange.h"
359 #include "nsRefPtrHashtable.h"
360 #include "nsSandboxFlags.h"
361 #include "nsScriptSecurityManager.h"
362 #include "nsServiceManagerUtils.h"
363 #include "nsStreamUtils.h"
364 #include "nsString.h"
365 #include "nsStringBuffer.h"
366 #include "nsStringFlags.h"
367 #include "nsStringFwd.h"
368 #include "nsStringIterator.h"
369 #include "nsTArray.h"
370 #include "nsTLiteralString.h"
371 #include "nsTPromiseFlatString.h"
372 #include "nsTStringRepr.h"
373 #include "nsTextFragment.h"
374 #include "nsTextNode.h"
375 #include "nsThreadManager.h"
376 #include "nsThreadUtils.h"
377 #include "nsTreeSanitizer.h"
378 #include "nsUGenCategory.h"
379 #include "nsURLHelper.h"
380 #include "nsUnicodeProperties.h"
381 #include "nsView.h"
382 #include "nsViewManager.h"
383 #include "nsXPCOM.h"
384 #include "nsXPCOMCID.h"
385 #include "nsXULAppAPI.h"
386 #include "nsXULElement.h"
387 #include "nsXULPopupManager.h"
388 #include "nscore.h"
389 #include "prinrval.h"
390 #include "xpcprivate.h"
391 #include "xpcpublic.h"
393 #if defined(XP_WIN)
394 // Undefine LoadImage to prevent naming conflict with Windows.
395 # undef LoadImage
396 #endif
398 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
399 const char** next, char16_t* result);
400 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end, int ns_aware,
401 const char** colon);
403 using namespace mozilla::dom;
404 using namespace mozilla::ipc;
405 using namespace mozilla::gfx;
406 using namespace mozilla::layers;
407 using namespace mozilla::widget;
408 using namespace mozilla;
410 const char kLoadAsData[] = "loadAsData";
412 nsIXPConnect* nsContentUtils::sXPConnect;
413 nsIScriptSecurityManager* nsContentUtils::sSecurityManager;
414 nsIPrincipal* nsContentUtils::sSystemPrincipal;
415 nsIPrincipal* nsContentUtils::sNullSubjectPrincipal;
416 nsNameSpaceManager* nsContentUtils::sNameSpaceManager;
417 nsIIOService* nsContentUtils::sIOService;
418 nsIUUIDGenerator* nsContentUtils::sUUIDGenerator;
419 nsIConsoleService* nsContentUtils::sConsoleService;
420 nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>*
421 nsContentUtils::sAtomEventTable = nullptr;
422 nsTHashMap<nsStringHashKey, EventNameMapping>*
423 nsContentUtils::sStringEventTable = nullptr;
424 nsTArray<RefPtr<nsAtom>>* nsContentUtils::sUserDefinedEvents = nullptr;
425 nsIStringBundleService* nsContentUtils::sStringBundleService;
426 nsIStringBundle* nsContentUtils::sStringBundles[PropertiesFile_COUNT];
427 nsIContentPolicy* nsContentUtils::sContentPolicyService;
428 bool nsContentUtils::sTriedToGetContentPolicy = false;
429 RefPtr<mozilla::intl::LineBreaker> nsContentUtils::sLineBreaker;
430 RefPtr<mozilla::intl::WordBreaker> nsContentUtils::sWordBreaker;
431 StaticRefPtr<nsIBidiKeyboard> nsContentUtils::sBidiKeyboard;
432 uint32_t nsContentUtils::sScriptBlockerCount = 0;
433 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
434 AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners =
435 nullptr;
436 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
437 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
439 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
441 nsString* nsContentUtils::sShiftText = nullptr;
442 nsString* nsContentUtils::sControlText = nullptr;
443 nsString* nsContentUtils::sMetaText = nullptr;
444 nsString* nsContentUtils::sOSText = nullptr;
445 nsString* nsContentUtils::sAltText = nullptr;
446 nsString* nsContentUtils::sModifierSeparator = nullptr;
448 bool nsContentUtils::sInitialized = false;
449 #ifndef RELEASE_OR_BETA
450 bool nsContentUtils::sBypassCSSOMOriginCheck = false;
451 #endif
453 nsCString* nsContentUtils::sJSBytecodeMimeType = nullptr;
455 nsContentUtils::UserInteractionObserver*
456 nsContentUtils::sUserInteractionObserver = nullptr;
458 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
459 nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
460 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
461 bool nsContentUtils::sFragmentParsingActive = false;
463 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
465 int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
466 uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
468 template Maybe<int32_t> nsContentUtils::ComparePoints(
469 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary);
470 template Maybe<int32_t> nsContentUtils::ComparePoints(
471 const RangeBoundary& aFirstBoundary,
472 const RawRangeBoundary& aSecondBoundary);
473 template Maybe<int32_t> nsContentUtils::ComparePoints(
474 const RawRangeBoundary& aFirstBoundary,
475 const RangeBoundary& aSecondBoundary);
476 template Maybe<int32_t> nsContentUtils::ComparePoints(
477 const RawRangeBoundary& aFirstBoundary,
478 const RawRangeBoundary& aSecondBoundary);
480 template int32_t nsContentUtils::ComparePoints_Deprecated(
481 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
482 bool* aDisconnected);
483 template int32_t nsContentUtils::ComparePoints_Deprecated(
484 const RangeBoundary& aFirstBoundary,
485 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
486 template int32_t nsContentUtils::ComparePoints_Deprecated(
487 const RawRangeBoundary& aFirstBoundary,
488 const RangeBoundary& aSecondBoundary, bool* aDisconnected);
489 template int32_t nsContentUtils::ComparePoints_Deprecated(
490 const RawRangeBoundary& aFirstBoundary,
491 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
493 // Subset of
494 // http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
495 enum AutocompleteUnsupportedFieldName : uint8_t {
496 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
497 eAutocompleteUnsupportedFieldName_##name_,
498 #include "AutocompleteFieldList.h"
499 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
502 enum AutocompleteNoPersistFieldName : uint8_t {
503 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
504 eAutocompleteNoPersistFieldName_##name_,
505 #include "AutocompleteFieldList.h"
506 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
509 enum AutocompleteUnsupportFieldContactHint : uint8_t {
510 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
511 eAutocompleteUnsupportedFieldContactHint_##name_,
512 #include "AutocompleteFieldList.h"
513 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
516 enum AutocompleteFieldName : uint8_t {
517 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) eAutocompleteFieldName_##name_,
518 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
519 AUTOCOMPLETE_FIELD_NAME(name_, value_)
520 #include "AutocompleteFieldList.h"
521 #undef AUTOCOMPLETE_FIELD_NAME
522 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
525 enum AutocompleteFieldHint : uint8_t {
526 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) eAutocompleteFieldHint_##name_,
527 #include "AutocompleteFieldList.h"
528 #undef AUTOCOMPLETE_FIELD_HINT
531 enum AutocompleteFieldContactHint : uint8_t {
532 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
533 eAutocompleteFieldContactHint_##name_,
534 #include "AutocompleteFieldList.h"
535 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
538 enum AutocompleteCategory {
539 #define AUTOCOMPLETE_CATEGORY(name_, value_) eAutocompleteCategory_##name_,
540 #include "AutocompleteFieldList.h"
541 #undef AUTOCOMPLETE_CATEGORY
544 static const nsAttrValue::EnumTable kAutocompleteUnsupportedFieldNameTable[] = {
545 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
546 {value_, eAutocompleteUnsupportedFieldName_##name_},
547 #include "AutocompleteFieldList.h"
548 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
549 {nullptr, 0}};
551 static const nsAttrValue::EnumTable kAutocompleteNoPersistFieldNameTable[] = {
552 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
553 {value_, eAutocompleteNoPersistFieldName_##name_},
554 #include "AutocompleteFieldList.h"
555 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
556 {nullptr, 0}};
558 static const nsAttrValue::EnumTable
559 kAutocompleteUnsupportedContactFieldHintTable[] = {
560 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
561 {value_, eAutocompleteUnsupportedFieldContactHint_##name_},
562 #include "AutocompleteFieldList.h"
563 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
564 {nullptr, 0}};
566 static const nsAttrValue::EnumTable kAutocompleteFieldNameTable[] = {
567 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
568 {value_, eAutocompleteFieldName_##name_},
569 #include "AutocompleteFieldList.h"
570 #undef AUTOCOMPLETE_FIELD_NAME
571 {nullptr, 0}};
573 static const nsAttrValue::EnumTable kAutocompleteContactFieldNameTable[] = {
574 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
575 {value_, eAutocompleteFieldName_##name_},
576 #include "AutocompleteFieldList.h"
577 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
578 {nullptr, 0}};
580 static const nsAttrValue::EnumTable kAutocompleteFieldHintTable[] = {
581 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
582 {value_, eAutocompleteFieldHint_##name_},
583 #include "AutocompleteFieldList.h"
584 #undef AUTOCOMPLETE_FIELD_HINT
585 {nullptr, 0}};
587 static const nsAttrValue::EnumTable kAutocompleteContactFieldHintTable[] = {
588 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
589 {value_, eAutocompleteFieldContactHint_##name_},
590 #include "AutocompleteFieldList.h"
591 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
592 {nullptr, 0}};
594 namespace {
596 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
598 static PLDHashTable* sEventListenerManagersHash;
600 // A global hashtable to for keeping the arena alive for cross docGroup node
601 // adoption.
602 static nsRefPtrHashtable<nsPtrHashKey<const nsINode>, mozilla::dom::DOMArena>*
603 sDOMArenaHashtable;
605 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter {
606 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
608 ~DOMEventListenerManagersHashReporter() = default;
610 public:
611 NS_DECL_ISUPPORTS
613 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
614 nsISupports* aData, bool aAnonymize) override {
615 // We don't measure the |EventListenerManager| objects pointed to by the
616 // entries because those references are non-owning.
617 int64_t amount =
618 sEventListenerManagersHash
619 ? sEventListenerManagersHash->ShallowSizeOfIncludingThis(
620 MallocSizeOf)
621 : 0;
623 MOZ_COLLECT_REPORT(
624 "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
625 amount, "Memory used by the event listener manager's hash table.");
627 return NS_OK;
631 NS_IMPL_ISUPPORTS(DOMEventListenerManagersHashReporter, nsIMemoryReporter)
633 class EventListenerManagerMapEntry : public PLDHashEntryHdr {
634 public:
635 explicit EventListenerManagerMapEntry(const void* aKey) : mKey(aKey) {}
637 ~EventListenerManagerMapEntry() {
638 NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
641 protected: // declared protected to silence clang warnings
642 const void* mKey; // must be first, to look like PLDHashEntryStub
644 public:
645 RefPtr<EventListenerManager> mListenerManager;
648 static void EventListenerManagerHashInitEntry(PLDHashEntryHdr* entry,
649 const void* key) {
650 // Initialize the entry with placement new
651 new (entry) EventListenerManagerMapEntry(key);
654 static void EventListenerManagerHashClearEntry(PLDHashTable* table,
655 PLDHashEntryHdr* entry) {
656 EventListenerManagerMapEntry* lm =
657 static_cast<EventListenerManagerMapEntry*>(entry);
659 // Let the EventListenerManagerMapEntry clean itself up...
660 lm->~EventListenerManagerMapEntry();
663 class SameOriginCheckerImpl final : public nsIChannelEventSink,
664 public nsIInterfaceRequestor {
665 ~SameOriginCheckerImpl() = default;
667 NS_DECL_ISUPPORTS
668 NS_DECL_NSICHANNELEVENTSINK
669 NS_DECL_NSIINTERFACEREQUESTOR
672 } // namespace
674 void AutoSuppressEventHandlingAndSuspend::SuppressDocument(Document* aDoc) {
675 // Note: Document::SuppressEventHandling will also automatically suppress
676 // event handling for any in-process sub-documents. However, since we need
677 // to deal with cases where remote BrowsingContexts may be interleaved
678 // with in-process ones, we still need to walk the entire tree ourselves.
679 // This may be slightly redundant in some cases, but since event handling
680 // suppressions maintain a count of current blockers, it does not cause
681 // any problems.
682 aDoc->SuppressEventHandling();
683 if (nsCOMPtr<nsPIDOMWindowInner> win = aDoc->GetInnerWindow()) {
684 win->Suspend();
685 mWindows.AppendElement(win);
689 void AutoSuppressEventHandlingAndSuspend::UnsuppressDocument(Document* aDoc) {
690 aDoc->UnsuppressEventHandlingAndFireEvents(true);
693 AutoSuppressEventHandlingAndSuspend::~AutoSuppressEventHandlingAndSuspend() {
694 for (const auto& win : mWindows) {
695 win->Resume();
697 UnsuppressDocuments();
701 * This class is used to determine whether or not the user is currently
702 * interacting with the browser. It listens to observer events to toggle the
703 * value of the sUserActive static.
705 * This class is an internal implementation detail.
706 * nsContentUtils::GetUserIsInteracting() should be used to access current
707 * user interaction status.
709 class nsContentUtils::UserInteractionObserver final
710 : public nsIObserver,
711 public BackgroundHangAnnotator {
712 public:
713 NS_DECL_ISUPPORTS
714 NS_DECL_NSIOBSERVER
716 void Init();
717 void Shutdown();
718 void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
720 static Atomic<bool> sUserActive;
722 private:
723 ~UserInteractionObserver() = default;
726 // static
727 nsresult nsContentUtils::Init() {
728 if (sInitialized) {
729 NS_WARNING("Init() called twice");
731 return NS_OK;
734 nsHTMLTags::AddRefTable();
736 sNameSpaceManager = nsNameSpaceManager::GetInstance();
737 NS_ENSURE_TRUE(sNameSpaceManager, NS_ERROR_OUT_OF_MEMORY);
739 sXPConnect = nsXPConnect::XPConnect();
740 // We hold a strong ref to sXPConnect to ensure that it does not go away until
741 // nsLayoutStatics::Shutdown is happening. Otherwise ~nsXPConnect can be
742 // triggered by xpcModuleDtor late in shutdown and cause crashes due to
743 // various stuff already being torn down by then. Note that this means that
744 // we are effectively making sure that if we leak nsLayoutStatics then we also
745 // leak nsXPConnect.
746 NS_ADDREF(sXPConnect);
748 sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
749 if (!sSecurityManager) return NS_ERROR_FAILURE;
750 NS_ADDREF(sSecurityManager);
752 sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
753 MOZ_ASSERT(sSystemPrincipal);
755 RefPtr<NullPrincipal> nullPrincipal =
756 NullPrincipal::CreateWithoutOriginAttributes();
757 if (!nullPrincipal) {
758 return NS_ERROR_FAILURE;
761 nullPrincipal.forget(&sNullSubjectPrincipal);
763 nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
764 if (NS_FAILED(rv)) {
765 // This makes life easier, but we can live without it.
767 sIOService = nullptr;
770 sLineBreaker = mozilla::intl::LineBreaker::Create();
772 sWordBreaker = mozilla::intl::WordBreaker::Create();
774 if (!InitializeEventTable()) return NS_ERROR_FAILURE;
776 if (!sEventListenerManagersHash) {
777 static const PLDHashTableOps hash_table_ops = {
778 PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
779 PLDHashTable::MoveEntryStub, EventListenerManagerHashClearEntry,
780 EventListenerManagerHashInitEntry};
782 sEventListenerManagersHash =
783 new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
785 RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
788 sBlockedScriptRunners = new AutoTArray<nsCOMPtr<nsIRunnable>, 8>;
790 #ifndef RELEASE_OR_BETA
791 sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
792 #endif
794 Element::InitCCCallbacks();
796 Unused << nsRFPService::GetOrCreate();
798 nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
799 do_GetService("@mozilla.org/uuid-generator;1", &rv);
800 if (NS_WARN_IF(NS_FAILED(rv))) {
801 return rv;
803 uuidGenerator.forget(&sUUIDGenerator);
805 if (XRE_IsParentProcess()) {
806 AsyncPrecreateStringBundles();
809 RefPtr<UserInteractionObserver> uio = new UserInteractionObserver();
810 uio->Init();
811 uio.forget(&sUserInteractionObserver);
813 sInitialized = true;
815 return NS_OK;
818 bool nsContentUtils::InitJSBytecodeMimeType() {
819 MOZ_ASSERT(NS_IsMainThread());
820 MOZ_ASSERT(!sJSBytecodeMimeType);
822 JS::BuildIdCharVector jsBuildId;
823 if (!JS::GetScriptTranscodingBuildId(&jsBuildId)) {
824 return false;
827 nsDependentCSubstring jsBuildIdStr(jsBuildId.begin(), jsBuildId.length());
828 sJSBytecodeMimeType =
829 new nsCString("javascript/moz-bytecode-"_ns + jsBuildIdStr);
830 return true;
833 void nsContentUtils::GetShiftText(nsAString& text) {
834 if (!sShiftText) InitializeModifierStrings();
835 text.Assign(*sShiftText);
838 void nsContentUtils::GetControlText(nsAString& text) {
839 if (!sControlText) InitializeModifierStrings();
840 text.Assign(*sControlText);
843 void nsContentUtils::GetMetaText(nsAString& text) {
844 if (!sMetaText) InitializeModifierStrings();
845 text.Assign(*sMetaText);
848 void nsContentUtils::GetOSText(nsAString& text) {
849 if (!sOSText) {
850 InitializeModifierStrings();
852 text.Assign(*sOSText);
855 void nsContentUtils::GetAltText(nsAString& text) {
856 if (!sAltText) InitializeModifierStrings();
857 text.Assign(*sAltText);
860 void nsContentUtils::GetModifierSeparatorText(nsAString& text) {
861 if (!sModifierSeparator) InitializeModifierStrings();
862 text.Assign(*sModifierSeparator);
865 void nsContentUtils::InitializeModifierStrings() {
866 // load the display strings for the keyboard accelerators
867 nsCOMPtr<nsIStringBundleService> bundleService =
868 mozilla::components::StringBundle::Service();
869 nsCOMPtr<nsIStringBundle> bundle;
870 DebugOnly<nsresult> rv = NS_OK;
871 if (bundleService) {
872 rv = bundleService->CreateBundle(
873 "chrome://global-platform/locale/platformKeys.properties",
874 getter_AddRefs(bundle));
877 NS_ASSERTION(
878 NS_SUCCEEDED(rv) && bundle,
879 "chrome://global/locale/platformKeys.properties could not be loaded");
880 nsAutoString shiftModifier;
881 nsAutoString metaModifier;
882 nsAutoString osModifier;
883 nsAutoString altModifier;
884 nsAutoString controlModifier;
885 nsAutoString modifierSeparator;
886 if (bundle) {
887 // macs use symbols for each modifier key, so fetch each from the bundle,
888 // which also covers i18n
889 bundle->GetStringFromName("VK_SHIFT", shiftModifier);
890 bundle->GetStringFromName("VK_META", metaModifier);
891 bundle->GetStringFromName("VK_WIN", osModifier);
892 bundle->GetStringFromName("VK_ALT", altModifier);
893 bundle->GetStringFromName("VK_CONTROL", controlModifier);
894 bundle->GetStringFromName("MODIFIER_SEPARATOR", modifierSeparator);
896 // if any of these don't exist, we get an empty string
897 sShiftText = new nsString(shiftModifier);
898 sMetaText = new nsString(metaModifier);
899 sOSText = new nsString(osModifier);
900 sAltText = new nsString(altModifier);
901 sControlText = new nsString(controlModifier);
902 sModifierSeparator = new nsString(modifierSeparator);
905 mozilla::EventClassID nsContentUtils::GetEventClassIDFromMessage(
906 EventMessage aEventMessage) {
907 switch (aEventMessage) {
908 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
909 case message_: \
910 return struct_;
911 #include "mozilla/EventNameList.h"
912 #undef MESSAGE_TO_EVENT
913 default:
914 MOZ_ASSERT_UNREACHABLE("Invalid event message?");
915 return eBasicEventClass;
919 static nsAtom* GetEventTypeFromMessage(EventMessage aEventMessage) {
920 switch (aEventMessage) {
921 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
922 case message_: \
923 return nsGkAtoms::on##name_;
924 #include "mozilla/EventNameList.h"
925 #undef MESSAGE_TO_EVENT
926 default:
927 return nullptr;
931 // Because of SVG/SMIL we have several atoms mapped to the same
932 // id, but we can rely on MESSAGE_TO_EVENT to map id to only one atom.
933 static bool ShouldAddEventToStringEventTable(const EventNameMapping& aMapping) {
934 MOZ_ASSERT(aMapping.mAtom);
935 return GetEventTypeFromMessage(aMapping.mMessage) == aMapping.mAtom;
938 bool nsContentUtils::InitializeEventTable() {
939 NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
940 NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
942 static const EventNameMapping eventArray[] = {
943 #define EVENT(name_, _message, _type, _class) \
944 {nsGkAtoms::on##name_, _type, _message, _class, false},
945 #define WINDOW_ONLY_EVENT EVENT
946 #define DOCUMENT_ONLY_EVENT EVENT
947 #define NON_IDL_EVENT EVENT
948 #include "mozilla/EventNameList.h"
949 #undef WINDOW_ONLY_EVENT
950 #undef NON_IDL_EVENT
951 #undef EVENT
952 {nullptr}};
954 sAtomEventTable = new nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>(
955 ArrayLength(eventArray));
956 sStringEventTable = new nsTHashMap<nsStringHashKey, EventNameMapping>(
957 ArrayLength(eventArray));
958 sUserDefinedEvents = new nsTArray<RefPtr<nsAtom>>(64);
960 // Subtract one from the length because of the trailing null
961 for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) {
962 MOZ_ASSERT(!sAtomEventTable->Contains(eventArray[i].mAtom),
963 "Double-defining event name; fix your EventNameList.h");
964 sAtomEventTable->InsertOrUpdate(eventArray[i].mAtom, eventArray[i]);
965 if (ShouldAddEventToStringEventTable(eventArray[i])) {
966 sStringEventTable->InsertOrUpdate(
967 Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
968 eventArray[i]);
972 return true;
975 void nsContentUtils::InitializeTouchEventTable() {
976 static bool sEventTableInitialized = false;
977 if (!sEventTableInitialized && sAtomEventTable && sStringEventTable) {
978 sEventTableInitialized = true;
979 static const EventNameMapping touchEventArray[] = {
980 #define EVENT(name_, _message, _type, _class)
981 #define TOUCH_EVENT(name_, _message, _type, _class) \
982 {nsGkAtoms::on##name_, _type, _message, _class},
983 #include "mozilla/EventNameList.h"
984 #undef TOUCH_EVENT
985 #undef EVENT
986 {nullptr}};
987 // Subtract one from the length because of the trailing null
988 for (uint32_t i = 0; i < ArrayLength(touchEventArray) - 1; ++i) {
989 sAtomEventTable->InsertOrUpdate(touchEventArray[i].mAtom,
990 touchEventArray[i]);
991 sStringEventTable->InsertOrUpdate(
992 Substring(nsDependentAtomString(touchEventArray[i].mAtom), 2),
993 touchEventArray[i]);
998 static bool Is8bit(const nsAString& aString) {
999 static const char16_t EIGHT_BIT = char16_t(~0x00FF);
1001 for (nsAString::const_char_iterator start = aString.BeginReading(),
1002 end = aString.EndReading();
1003 start != end; ++start) {
1004 if (*start & EIGHT_BIT) {
1005 return false;
1009 return true;
1012 nsresult nsContentUtils::Btoa(const nsAString& aBinaryData,
1013 nsAString& aAsciiBase64String) {
1014 if (!Is8bit(aBinaryData)) {
1015 aAsciiBase64String.Truncate();
1016 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1019 return Base64Encode(aBinaryData, aAsciiBase64String);
1022 nsresult nsContentUtils::Atob(const nsAString& aAsciiBase64String,
1023 nsAString& aBinaryData) {
1024 if (!Is8bit(aAsciiBase64String)) {
1025 aBinaryData.Truncate();
1026 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1029 const char16_t* start = aAsciiBase64String.BeginReading();
1030 const char16_t* cur = start;
1031 const char16_t* end = aAsciiBase64String.EndReading();
1032 bool hasWhitespace = false;
1034 while (cur < end) {
1035 if (nsContentUtils::IsHTMLWhitespace(*cur)) {
1036 hasWhitespace = true;
1037 break;
1039 cur++;
1042 nsresult rv;
1044 if (hasWhitespace) {
1045 nsString trimmedString;
1047 if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible)) {
1048 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1051 trimmedString.Append(start, cur - start);
1053 while (cur < end) {
1054 if (!nsContentUtils::IsHTMLWhitespace(*cur)) {
1055 trimmedString.Append(*cur);
1057 cur++;
1059 rv = Base64Decode(trimmedString, aBinaryData);
1060 } else {
1061 rv = Base64Decode(aAsciiBase64String, aBinaryData);
1064 if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
1065 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1067 return rv;
1070 bool nsContentUtils::IsAutocompleteEnabled(
1071 mozilla::dom::HTMLInputElement* aInput) {
1072 MOZ_ASSERT(aInput, "aInput should not be null!");
1074 nsAutoString autocomplete;
1075 aInput->GetAutocomplete(autocomplete);
1077 if (autocomplete.IsEmpty()) {
1078 auto* form = aInput->GetForm();
1079 if (!form) {
1080 return true;
1083 form->GetAutocomplete(autocomplete);
1086 return !autocomplete.EqualsLiteral("off");
1089 nsContentUtils::AutocompleteAttrState
1090 nsContentUtils::SerializeAutocompleteAttribute(
1091 const nsAttrValue* aAttr, nsAString& aResult,
1092 AutocompleteAttrState aCachedState) {
1093 if (!aAttr ||
1094 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1095 return aCachedState;
1098 if (aCachedState == nsContentUtils::eAutocompleteAttrState_Valid) {
1099 uint32_t atomCount = aAttr->GetAtomCount();
1100 for (uint32_t i = 0; i < atomCount; i++) {
1101 if (i != 0) {
1102 aResult.Append(' ');
1104 aResult.Append(nsDependentAtomString(aAttr->AtomAt(i)));
1106 nsContentUtils::ASCIIToLower(aResult);
1107 return aCachedState;
1110 aResult.Truncate();
1112 mozilla::dom::AutocompleteInfo info;
1113 AutocompleteAttrState state =
1114 InternalSerializeAutocompleteAttribute(aAttr, info);
1115 if (state == eAutocompleteAttrState_Valid) {
1116 // Concatenate the info fields.
1117 aResult = info.mSection;
1119 if (!info.mAddressType.IsEmpty()) {
1120 if (!aResult.IsEmpty()) {
1121 aResult += ' ';
1123 aResult += info.mAddressType;
1126 if (!info.mContactType.IsEmpty()) {
1127 if (!aResult.IsEmpty()) {
1128 aResult += ' ';
1130 aResult += info.mContactType;
1133 if (!info.mFieldName.IsEmpty()) {
1134 if (!aResult.IsEmpty()) {
1135 aResult += ' ';
1137 aResult += info.mFieldName;
1141 return state;
1144 nsContentUtils::AutocompleteAttrState
1145 nsContentUtils::SerializeAutocompleteAttribute(
1146 const nsAttrValue* aAttr, mozilla::dom::AutocompleteInfo& aInfo,
1147 AutocompleteAttrState aCachedState, bool aGrantAllValidValue) {
1148 if (!aAttr ||
1149 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1150 return aCachedState;
1153 return InternalSerializeAutocompleteAttribute(aAttr, aInfo,
1154 aGrantAllValidValue);
1158 * Helper to validate the @autocomplete tokens.
1160 * @return {AutocompleteAttrState} The state of the attribute (invalid/valid).
1162 nsContentUtils::AutocompleteAttrState
1163 nsContentUtils::InternalSerializeAutocompleteAttribute(
1164 const nsAttrValue* aAttrVal, mozilla::dom::AutocompleteInfo& aInfo,
1165 bool aGrantAllValidValue) {
1166 // No autocomplete attribute so we are done
1167 if (!aAttrVal) {
1168 return eAutocompleteAttrState_Invalid;
1171 uint32_t numTokens = aAttrVal->GetAtomCount();
1172 if (!numTokens) {
1173 return eAutocompleteAttrState_Invalid;
1176 uint32_t index = numTokens - 1;
1177 nsString tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1178 AutocompleteCategory category;
1179 nsAttrValue enumValue;
1181 bool unsupported = false;
1182 if (!aGrantAllValidValue) {
1183 unsupported = enumValue.ParseEnumValue(
1184 tokenString, kAutocompleteUnsupportedFieldNameTable, false);
1185 if (unsupported) {
1186 return eAutocompleteAttrState_Invalid;
1190 nsAutoString str;
1191 bool result =
1192 enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
1193 if (result) {
1194 // Off/Automatic/Normal categories.
1195 if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
1196 enumValue.Equals(u"on"_ns, eIgnoreCase)) {
1197 if (numTokens > 1) {
1198 return eAutocompleteAttrState_Invalid;
1200 enumValue.ToString(str);
1201 ASCIIToLower(str);
1202 aInfo.mFieldName.Assign(str);
1203 aInfo.mCanAutomaticallyPersist =
1204 !enumValue.Equals(u"off"_ns, eIgnoreCase);
1205 return eAutocompleteAttrState_Valid;
1208 // Only allow on/off if form autofill @autocomplete values aren't enabled
1209 // and it doesn't grant all valid values.
1210 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1211 !aGrantAllValidValue) {
1212 return eAutocompleteAttrState_Invalid;
1215 // Normal category
1216 if (numTokens > 3) {
1217 return eAutocompleteAttrState_Invalid;
1219 category = eAutocompleteCategory_NORMAL;
1220 } else { // Check if the last token is of the contact category instead.
1221 // Only allow on/off if form autofill @autocomplete values aren't enabled
1222 // and it doesn't grant all valid values.
1223 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1224 !aGrantAllValidValue) {
1225 return eAutocompleteAttrState_Invalid;
1228 result = enumValue.ParseEnumValue(
1229 tokenString, kAutocompleteContactFieldNameTable, false);
1230 if (!result || numTokens > 4) {
1231 return eAutocompleteAttrState_Invalid;
1234 category = eAutocompleteCategory_CONTACT;
1237 enumValue.ToString(str);
1238 ASCIIToLower(str);
1239 aInfo.mFieldName.Assign(str);
1241 aInfo.mCanAutomaticallyPersist = !enumValue.ParseEnumValue(
1242 tokenString, kAutocompleteNoPersistFieldNameTable, false);
1244 // We are done if this was the only token.
1245 if (numTokens == 1) {
1246 return eAutocompleteAttrState_Valid;
1249 --index;
1250 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1252 if (category == eAutocompleteCategory_CONTACT) {
1253 if (!aGrantAllValidValue) {
1254 unsupported = enumValue.ParseEnumValue(
1255 tokenString, kAutocompleteUnsupportedContactFieldHintTable, false);
1256 if (unsupported) {
1257 return eAutocompleteAttrState_Invalid;
1261 nsAttrValue contactFieldHint;
1262 result = contactFieldHint.ParseEnumValue(
1263 tokenString, kAutocompleteContactFieldHintTable, false);
1264 if (result) {
1265 nsAutoString contactFieldHintString;
1266 contactFieldHint.ToString(contactFieldHintString);
1267 ASCIIToLower(contactFieldHintString);
1268 aInfo.mContactType.Assign(contactFieldHintString);
1269 if (index == 0) {
1270 return eAutocompleteAttrState_Valid;
1272 --index;
1273 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1277 // Check for billing/shipping tokens
1278 nsAttrValue fieldHint;
1279 if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable,
1280 false)) {
1281 nsString fieldHintString;
1282 fieldHint.ToString(fieldHintString);
1283 ASCIIToLower(fieldHintString);
1284 aInfo.mAddressType.Assign(fieldHintString);
1285 if (index == 0) {
1286 return eAutocompleteAttrState_Valid;
1288 --index;
1289 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1292 // Check for section-* token
1293 const nsDependentSubstring& section = Substring(tokenString, 0, 8);
1294 if (section.LowerCaseEqualsASCII("section-")) {
1295 ASCIIToLower(tokenString);
1296 aInfo.mSection.Assign(tokenString);
1297 if (index == 0) {
1298 return eAutocompleteAttrState_Valid;
1302 // Clear the fields as the autocomplete attribute is invalid.
1303 aInfo.mSection.Truncate();
1304 aInfo.mAddressType.Truncate();
1305 aInfo.mContactType.Truncate();
1306 aInfo.mFieldName.Truncate();
1308 return eAutocompleteAttrState_Invalid;
1311 // Parse an integer according to HTML spec
1312 template <class StringT>
1313 int32_t nsContentUtils::ParseHTMLIntegerImpl(
1314 const StringT& aValue, ParseHTMLIntegerResultFlags* aResult) {
1315 using CharT = typename StringT::char_type;
1317 int result = eParseHTMLInteger_NoFlags;
1319 typename StringT::const_iterator iter, end;
1320 aValue.BeginReading(iter);
1321 aValue.EndReading(end);
1323 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1324 result |= eParseHTMLInteger_NonStandard;
1325 ++iter;
1328 if (iter == end) {
1329 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1330 *aResult = (ParseHTMLIntegerResultFlags)result;
1331 return 0;
1334 int sign = 1;
1335 if (*iter == CharT('-')) {
1336 sign = -1;
1337 result |= eParseHTMLInteger_Negative;
1338 ++iter;
1339 } else if (*iter == CharT('+')) {
1340 result |= eParseHTMLInteger_NonStandard;
1341 ++iter;
1344 bool foundValue = false;
1345 CheckedInt32 value = 0;
1347 // Check for leading zeros first.
1348 uint64_t leadingZeros = 0;
1349 while (iter != end) {
1350 if (*iter != CharT('0')) {
1351 break;
1354 ++leadingZeros;
1355 foundValue = true;
1356 ++iter;
1359 while (iter != end) {
1360 if (*iter >= CharT('0') && *iter <= CharT('9')) {
1361 value = (value * 10) + (*iter - CharT('0')) * sign;
1362 ++iter;
1363 if (!value.isValid()) {
1364 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
1365 break;
1367 foundValue = true;
1368 } else {
1369 break;
1373 if (!foundValue) {
1374 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1377 if (value.isValid() &&
1378 ((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
1379 (sign == -1 && value == 0))) {
1380 result |= eParseHTMLInteger_NonStandard;
1383 if (iter != end) {
1384 result |= eParseHTMLInteger_DidNotConsumeAllInput;
1387 *aResult = (ParseHTMLIntegerResultFlags)result;
1388 return value.isValid() ? value.value() : 0;
1391 // Parse an integer according to HTML spec
1392 int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
1393 ParseHTMLIntegerResultFlags* aResult) {
1394 return ParseHTMLIntegerImpl(aValue, aResult);
1397 int32_t nsContentUtils::ParseHTMLInteger(const nsACString& aValue,
1398 ParseHTMLIntegerResultFlags* aResult) {
1399 return ParseHTMLIntegerImpl(aValue, aResult);
1402 #define SKIP_WHITESPACE(iter, end_iter, end_res) \
1403 while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
1404 ++(iter); \
1406 if ((iter) == (end_iter)) { \
1407 return (end_res); \
1410 #define SKIP_ATTR_NAME(iter, end_iter) \
1411 while ((iter) != (end_iter) && !nsCRT::IsAsciiSpace(*(iter)) && \
1412 *(iter) != '=') { \
1413 ++(iter); \
1416 bool nsContentUtils::GetPseudoAttributeValue(const nsString& aSource,
1417 nsAtom* aName, nsAString& aValue) {
1418 aValue.Truncate();
1420 const char16_t* start = aSource.get();
1421 const char16_t* end = start + aSource.Length();
1422 const char16_t* iter;
1424 while (start != end) {
1425 SKIP_WHITESPACE(start, end, false)
1426 iter = start;
1427 SKIP_ATTR_NAME(iter, end)
1429 if (start == iter) {
1430 return false;
1433 // Remember the attr name.
1434 const nsDependentSubstring& attrName = Substring(start, iter);
1436 // Now check whether this is a valid name="value" pair.
1437 start = iter;
1438 SKIP_WHITESPACE(start, end, false)
1439 if (*start != '=') {
1440 // No '=', so this is not a name="value" pair. We don't know
1441 // what it is, and we have no way to handle it.
1442 return false;
1445 // Have to skip the value.
1446 ++start;
1447 SKIP_WHITESPACE(start, end, false)
1448 char16_t q = *start;
1449 if (q != kQuote && q != kApostrophe) {
1450 // Not a valid quoted value, so bail.
1451 return false;
1454 ++start; // Point to the first char of the value.
1455 iter = start;
1457 while (iter != end && *iter != q) {
1458 ++iter;
1461 if (iter == end) {
1462 // Oops, unterminated quoted string.
1463 return false;
1466 // At this point attrName holds the name of the "attribute" and
1467 // the value is between start and iter.
1469 if (aName->Equals(attrName)) {
1470 // We'll accumulate as many characters as possible (until we hit either
1471 // the end of the string or the beginning of an entity). Chunks will be
1472 // delimited by start and chunkEnd.
1473 const char16_t* chunkEnd = start;
1474 while (chunkEnd != iter) {
1475 if (*chunkEnd == kLessThan) {
1476 aValue.Truncate();
1478 return false;
1481 if (*chunkEnd == kAmpersand) {
1482 aValue.Append(start, chunkEnd - start);
1484 const char16_t* afterEntity = nullptr;
1485 char16_t result[2];
1486 uint32_t count = MOZ_XMLTranslateEntity(
1487 reinterpret_cast<const char*>(chunkEnd),
1488 reinterpret_cast<const char*>(iter),
1489 reinterpret_cast<const char**>(&afterEntity), result);
1490 if (count == 0) {
1491 aValue.Truncate();
1493 return false;
1496 aValue.Append(result, count);
1498 // Advance to after the entity and begin a new chunk.
1499 start = chunkEnd = afterEntity;
1500 } else {
1501 ++chunkEnd;
1505 // Append remainder.
1506 aValue.Append(start, iter - start);
1508 return true;
1511 // Resume scanning after the end of the attribute value (past the quote
1512 // char).
1513 start = iter + 1;
1516 return false;
1519 bool nsContentUtils::IsJavaScriptLanguage(const nsString& aName) {
1520 return aName.LowerCaseEqualsLiteral("javascript") ||
1521 aName.LowerCaseEqualsLiteral("livescript") ||
1522 aName.LowerCaseEqualsLiteral("mocha") ||
1523 aName.LowerCaseEqualsLiteral("javascript1.0") ||
1524 aName.LowerCaseEqualsLiteral("javascript1.1") ||
1525 aName.LowerCaseEqualsLiteral("javascript1.2") ||
1526 aName.LowerCaseEqualsLiteral("javascript1.3") ||
1527 aName.LowerCaseEqualsLiteral("javascript1.4") ||
1528 aName.LowerCaseEqualsLiteral("javascript1.5");
1531 void nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType,
1532 nsString& aParams) {
1533 aType.Truncate();
1534 aParams.Truncate();
1535 int32_t semiIndex = aValue.FindChar(char16_t(';'));
1536 if (-1 != semiIndex) {
1537 aType = Substring(aValue, 0, semiIndex);
1538 aParams =
1539 Substring(aValue, semiIndex + 1, aValue.Length() - (semiIndex + 1));
1540 aParams.StripWhitespace();
1541 } else {
1542 aType = aValue;
1544 aType.StripWhitespace();
1547 nsresult nsContentUtils::IsUserIdle(uint32_t aRequestedIdleTimeInMS,
1548 bool* aUserIsIdle) {
1549 nsresult rv;
1550 nsCOMPtr<nsIUserIdleService> idleService =
1551 do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
1552 NS_ENSURE_SUCCESS(rv, rv);
1554 uint32_t idleTimeInMS;
1555 rv = idleService->GetIdleTime(&idleTimeInMS);
1556 NS_ENSURE_SUCCESS(rv, rv);
1558 *aUserIsIdle = idleTimeInMS >= aRequestedIdleTimeInMS;
1559 return NS_OK;
1563 * A helper function that parses a sandbox attribute (of an <iframe> or a CSP
1564 * directive) and converts it to the set of flags used internally.
1566 * @param aSandboxAttr the sandbox attribute
1567 * @return the set of flags (SANDBOXED_NONE if aSandboxAttr is
1568 * null)
1570 uint32_t nsContentUtils::ParseSandboxAttributeToFlags(
1571 const nsAttrValue* aSandboxAttr) {
1572 if (!aSandboxAttr) {
1573 return SANDBOXED_NONE;
1576 uint32_t out = SANDBOX_ALL_FLAGS;
1578 #define SANDBOX_KEYWORD(string, atom, flags) \
1579 if (aSandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { \
1580 out &= ~(flags); \
1582 #include "IframeSandboxKeywordList.h"
1583 #undef SANDBOX_KEYWORD
1585 return out;
1589 * A helper function that checks if a string matches a valid sandbox flag.
1591 * @param aFlag the potential sandbox flag.
1592 * @return true if the flag is a sandbox flag.
1594 bool nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag) {
1595 #define SANDBOX_KEYWORD(string, atom, flags) \
1596 if (EqualsIgnoreASCIICase(nsDependentAtomString(nsGkAtoms::atom), aFlag)) { \
1597 return true; \
1599 #include "IframeSandboxKeywordList.h"
1600 #undef SANDBOX_KEYWORD
1601 return false;
1605 * A helper function that returns a string attribute corresponding to the
1606 * sandbox flags.
1608 * @param aFlags the sandbox flags
1609 * @param aString the attribute corresponding to the flags (null if aFlags
1610 * is zero)
1612 void nsContentUtils::SandboxFlagsToString(uint32_t aFlags, nsAString& aString) {
1613 if (!aFlags) {
1614 SetDOMStringToNull(aString);
1615 return;
1618 aString.Truncate();
1620 #define SANDBOX_KEYWORD(string, atom, flags) \
1621 if (!(aFlags & (flags))) { \
1622 if (!aString.IsEmpty()) { \
1623 aString.AppendLiteral(u" "); \
1625 aString.Append(nsDependentAtomString(nsGkAtoms::atom)); \
1627 #include "IframeSandboxKeywordList.h"
1628 #undef SANDBOX_KEYWORD
1631 nsIBidiKeyboard* nsContentUtils::GetBidiKeyboard() {
1632 if (!sBidiKeyboard) {
1633 sBidiKeyboard = nsIWidget::CreateBidiKeyboard();
1635 return sBidiKeyboard;
1639 * This is used to determine whether a character is in one of the classes
1640 * which CSS says should be part of the first-letter. Currently, that is
1641 * all punctuation classes (P*). Note that this is a change from CSS2
1642 * which excluded Pc and Pd.
1644 * https://www.w3.org/TR/css-pseudo-4/#first-letter-pseudo
1645 * "Punctuation (i.e, characters that belong to the Punctuation (P*) Unicode
1646 * general category [UAX44]) [...]"
1649 // static
1650 bool nsContentUtils::IsFirstLetterPunctuation(uint32_t aChar) {
1651 switch (mozilla::unicode::GetGeneralCategory(aChar)) {
1652 case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
1653 case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION: /* Pd */
1654 case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION: /* Pe */
1655 case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION: /* Pf */
1656 case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
1657 case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION: /* Po */
1658 case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION: /* Ps */
1659 return true;
1660 default:
1661 return false;
1665 // static
1666 bool nsContentUtils::IsAlphanumeric(uint32_t aChar) {
1667 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1669 return (cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber);
1672 // static
1673 bool nsContentUtils::IsAlphanumericOrSymbol(uint32_t aChar) {
1674 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1676 return cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber ||
1677 cat == nsUGenCategory::kSymbol;
1680 // static
1681 bool nsContentUtils::IsAlphanumericOrSymbolAt(const nsTextFragment* aFrag,
1682 uint32_t aOffset) {
1683 char16_t h = aFrag->CharAt(aOffset);
1684 if (!IS_SURROGATE(h)) {
1685 return IsAlphanumericOrSymbol(h);
1687 if (NS_IS_HIGH_SURROGATE(h) && aOffset + 1 < aFrag->GetLength()) {
1688 char16_t l = aFrag->CharAt(aOffset + 1);
1689 if (NS_IS_LOW_SURROGATE(l)) {
1690 return IsAlphanumericOrSymbol(SURROGATE_TO_UCS4(h, l));
1693 return false;
1696 /* static */
1697 bool nsContentUtils::IsHTMLWhitespace(char16_t aChar) {
1698 return aChar == char16_t(0x0009) || aChar == char16_t(0x000A) ||
1699 aChar == char16_t(0x000C) || aChar == char16_t(0x000D) ||
1700 aChar == char16_t(0x0020);
1703 /* static */
1704 bool nsContentUtils::IsHTMLWhitespaceOrNBSP(char16_t aChar) {
1705 return IsHTMLWhitespace(aChar) || aChar == char16_t(0xA0);
1708 /* static */
1709 bool nsContentUtils::IsHTMLBlockLevelElement(nsIContent* aContent) {
1710 return aContent->IsAnyOfHTMLElements(
1711 nsGkAtoms::address, nsGkAtoms::article, nsGkAtoms::aside,
1712 nsGkAtoms::blockquote, nsGkAtoms::center, nsGkAtoms::dir, nsGkAtoms::div,
1713 nsGkAtoms::dl, // XXX why not dt and dd?
1714 nsGkAtoms::fieldset,
1715 nsGkAtoms::figure, // XXX shouldn't figcaption be on this list
1716 nsGkAtoms::footer, nsGkAtoms::form, nsGkAtoms::h1, nsGkAtoms::h2,
1717 nsGkAtoms::h3, nsGkAtoms::h4, nsGkAtoms::h5, nsGkAtoms::h6,
1718 nsGkAtoms::header, nsGkAtoms::hgroup, nsGkAtoms::hr, nsGkAtoms::li,
1719 nsGkAtoms::listing, nsGkAtoms::menu, nsGkAtoms::nav, nsGkAtoms::ol,
1720 nsGkAtoms::p, nsGkAtoms::pre, nsGkAtoms::section, nsGkAtoms::table,
1721 nsGkAtoms::ul, nsGkAtoms::xmp);
1724 /* static */
1725 bool nsContentUtils::ParseIntMarginValue(const nsAString& aString,
1726 nsIntMargin& result) {
1727 nsAutoString marginStr(aString);
1728 marginStr.CompressWhitespace(true, true);
1729 if (marginStr.IsEmpty()) {
1730 return false;
1733 int32_t start = 0, end = 0;
1734 for (int count = 0; count < 4; count++) {
1735 if ((uint32_t)end >= marginStr.Length()) return false;
1737 // top, right, bottom, left
1738 if (count < 3)
1739 end = Substring(marginStr, start).FindChar(',');
1740 else
1741 end = Substring(marginStr, start).Length();
1743 if (end <= 0) return false;
1745 nsresult ec;
1746 int32_t val = nsString(Substring(marginStr, start, end)).ToInteger(&ec);
1747 if (NS_FAILED(ec)) return false;
1749 switch (count) {
1750 case 0:
1751 result.top = val;
1752 break;
1753 case 1:
1754 result.right = val;
1755 break;
1756 case 2:
1757 result.bottom = val;
1758 break;
1759 case 3:
1760 result.left = val;
1761 break;
1763 start += end + 1;
1765 return true;
1768 // static
1769 int32_t nsContentUtils::ParseLegacyFontSize(const nsAString& aValue) {
1770 nsAString::const_iterator iter, end;
1771 aValue.BeginReading(iter);
1772 aValue.EndReading(end);
1774 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1775 ++iter;
1778 if (iter == end) {
1779 return 0;
1782 bool relative = false;
1783 bool negate = false;
1784 if (*iter == char16_t('-')) {
1785 relative = true;
1786 negate = true;
1787 ++iter;
1788 } else if (*iter == char16_t('+')) {
1789 relative = true;
1790 ++iter;
1793 if (iter == end || *iter < char16_t('0') || *iter > char16_t('9')) {
1794 return 0;
1797 // We don't have to worry about overflow, since we can bail out as soon as
1798 // we're bigger than 7.
1799 int32_t value = 0;
1800 while (iter != end && *iter >= char16_t('0') && *iter <= char16_t('9')) {
1801 value = 10 * value + (*iter - char16_t('0'));
1802 if (value >= 7) {
1803 break;
1805 ++iter;
1808 if (relative) {
1809 if (negate) {
1810 value = 3 - value;
1811 } else {
1812 value = 3 + value;
1816 return clamped(value, 1, 7);
1819 /* static */
1820 void nsContentUtils::GetOfflineAppManifest(Document* aDocument, nsIURI** aURI) {
1821 MOZ_ASSERT(NS_IsMainThread());
1822 MOZ_ASSERT(aDocument);
1823 *aURI = nullptr;
1825 if (aDocument->GetController().isSome()) {
1826 return;
1829 Element* docElement = aDocument->GetRootElement();
1830 if (!docElement) {
1831 return;
1834 nsAutoString manifestSpec;
1835 docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
1837 // Manifest URIs can't have fragment identifiers.
1838 if (manifestSpec.IsEmpty() || manifestSpec.Contains('#')) {
1839 return;
1842 nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec, aDocument,
1843 aDocument->GetDocBaseURI());
1846 /* static */
1847 bool nsContentUtils::OfflineAppAllowed(nsIURI* aURI) {
1848 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1849 components::OfflineCacheUpdate::Service();
1850 if (!updateService) {
1851 return false;
1854 bool allowed;
1855 nsresult rv = updateService->OfflineAppAllowedForURI(aURI, &allowed);
1856 return NS_SUCCEEDED(rv) && allowed;
1859 /* static */
1860 bool nsContentUtils::OfflineAppAllowed(nsIPrincipal* aPrincipal) {
1861 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1862 components::OfflineCacheUpdate::Service();
1863 if (!updateService) {
1864 return false;
1867 bool allowed;
1868 nsresult rv = updateService->OfflineAppAllowed(aPrincipal, &allowed);
1869 return NS_SUCCEEDED(rv) && allowed;
1871 // Static
1872 bool nsContentUtils::IsErrorPage(nsIURI* aURI) {
1873 if (!aURI) {
1874 return false;
1877 if (!aURI->SchemeIs("about")) {
1878 return false;
1881 nsAutoCString name;
1882 nsresult rv = NS_GetAboutModuleName(aURI, name);
1883 NS_ENSURE_SUCCESS(rv, false);
1885 return name.EqualsLiteral("certerror") || name.EqualsLiteral("neterror") ||
1886 name.EqualsLiteral("blocked");
1889 // static
1890 void nsContentUtils::Shutdown() {
1891 sInitialized = false;
1893 nsHTMLTags::ReleaseTable();
1895 NS_IF_RELEASE(sContentPolicyService);
1896 sTriedToGetContentPolicy = false;
1897 uint32_t i;
1898 for (i = 0; i < PropertiesFile_COUNT; ++i) NS_IF_RELEASE(sStringBundles[i]);
1900 NS_IF_RELEASE(sStringBundleService);
1901 NS_IF_RELEASE(sConsoleService);
1902 NS_IF_RELEASE(sXPConnect);
1903 NS_IF_RELEASE(sSecurityManager);
1904 NS_IF_RELEASE(sSystemPrincipal);
1905 NS_IF_RELEASE(sNullSubjectPrincipal);
1906 NS_IF_RELEASE(sIOService);
1907 NS_IF_RELEASE(sUUIDGenerator);
1908 sLineBreaker = nullptr;
1909 sWordBreaker = nullptr;
1910 sBidiKeyboard = nullptr;
1912 delete sAtomEventTable;
1913 sAtomEventTable = nullptr;
1914 delete sStringEventTable;
1915 sStringEventTable = nullptr;
1916 delete sUserDefinedEvents;
1917 sUserDefinedEvents = nullptr;
1919 if (sEventListenerManagersHash) {
1920 NS_ASSERTION(sEventListenerManagersHash->EntryCount() == 0,
1921 "Event listener manager hash not empty at shutdown!");
1923 // See comment above.
1925 // However, we have to handle this table differently. If it still
1926 // has entries, we want to leak it too, so that we can keep it alive
1927 // in case any elements are destroyed. Because if they are, we need
1928 // their event listener managers to be destroyed too, or otherwise
1929 // it could leave dangling references in DOMClassInfo's preserved
1930 // wrapper table.
1932 if (sEventListenerManagersHash->EntryCount() == 0) {
1933 delete sEventListenerManagersHash;
1934 sEventListenerManagersHash = nullptr;
1938 if (sDOMArenaHashtable) {
1939 MOZ_ASSERT(sDOMArenaHashtable->Count() == 0);
1940 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
1941 delete sDOMArenaHashtable;
1942 sDOMArenaHashtable = nullptr;
1945 NS_ASSERTION(!sBlockedScriptRunners || sBlockedScriptRunners->Length() == 0,
1946 "How'd this happen?");
1947 delete sBlockedScriptRunners;
1948 sBlockedScriptRunners = nullptr;
1950 delete sShiftText;
1951 sShiftText = nullptr;
1952 delete sControlText;
1953 sControlText = nullptr;
1954 delete sMetaText;
1955 sMetaText = nullptr;
1956 delete sOSText;
1957 sOSText = nullptr;
1958 delete sAltText;
1959 sAltText = nullptr;
1960 delete sModifierSeparator;
1961 sModifierSeparator = nullptr;
1963 delete sJSBytecodeMimeType;
1964 sJSBytecodeMimeType = nullptr;
1966 NS_IF_RELEASE(sSameOriginChecker);
1968 if (sUserInteractionObserver) {
1969 sUserInteractionObserver->Shutdown();
1970 NS_RELEASE(sUserInteractionObserver);
1973 TextControlState::Shutdown();
1974 nsMappedAttributes::Shutdown();
1978 * Checks whether two nodes come from the same origin. aTrustedNode is
1979 * considered 'safe' in that a user can operate on it.
1981 // static
1982 nsresult nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
1983 const nsINode* unTrustedNode) {
1984 MOZ_ASSERT(aTrustedNode);
1985 MOZ_ASSERT(unTrustedNode);
1988 * Get hold of each node's principal
1991 nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
1992 nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
1994 if (trustedPrincipal == unTrustedPrincipal) {
1995 return NS_OK;
1998 bool equal;
1999 // XXXbz should we actually have a Subsumes() check here instead? Or perhaps
2000 // a separate method for that, with callers using one or the other?
2001 if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
2002 !equal) {
2003 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
2006 return NS_OK;
2009 // static
2010 bool nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
2011 nsIPrincipal* aPrincipal) {
2012 bool subsumes;
2013 nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
2014 NS_ENSURE_SUCCESS(rv, false);
2016 if (subsumes) {
2017 return true;
2020 // The subject doesn't subsume aPrincipal. Allow access only if the subject
2021 // is chrome.
2022 return IsCallerChrome();
2025 // static
2026 bool nsContentUtils::CanCallerAccess(const nsINode* aNode) {
2027 nsIPrincipal* subject = SubjectPrincipal();
2028 if (subject->IsSystemPrincipal()) {
2029 return true;
2032 if (aNode->ChromeOnlyAccess()) {
2033 return false;
2036 return CanCallerAccess(subject, aNode->NodePrincipal());
2039 // static
2040 bool nsContentUtils::CanCallerAccess(nsPIDOMWindowInner* aWindow) {
2041 nsCOMPtr<nsIScriptObjectPrincipal> scriptObject = do_QueryInterface(aWindow);
2042 NS_ENSURE_TRUE(scriptObject, false);
2044 return CanCallerAccess(SubjectPrincipal(), scriptObject->GetPrincipal());
2047 // static
2048 bool nsContentUtils::PrincipalHasPermission(nsIPrincipal& aPrincipal,
2049 const nsAtom* aPerm) {
2050 // Chrome gets access by default.
2051 if (aPrincipal.IsSystemPrincipal()) {
2052 return true;
2055 // Otherwise, only allow if caller is an addon with the permission.
2056 return BasePrincipal::Cast(aPrincipal).AddonHasPermission(aPerm);
2059 // static
2060 bool nsContentUtils::CallerHasPermission(JSContext* aCx, const nsAtom* aPerm) {
2061 return PrincipalHasPermission(*SubjectPrincipal(aCx), aPerm);
2064 // static
2065 nsIPrincipal* nsContentUtils::GetAttrTriggeringPrincipal(
2066 nsIContent* aContent, const nsAString& aAttrValue,
2067 nsIPrincipal* aSubjectPrincipal) {
2068 nsIPrincipal* contentPrin = aContent ? aContent->NodePrincipal() : nullptr;
2070 // If the subject principal is the same as the content principal, or no
2071 // explicit subject principal was provided, we don't need to do any further
2072 // checks. Just return the content principal.
2073 if (contentPrin == aSubjectPrincipal || !aSubjectPrincipal) {
2074 return contentPrin;
2077 // Only use the subject principal if the URL string we are going to end up
2078 // fetching is under the control of that principal, which is never the case
2079 // for relative URLs.
2080 if (aAttrValue.IsEmpty() ||
2081 !IsAbsoluteURL(NS_ConvertUTF16toUTF8(aAttrValue))) {
2082 return contentPrin;
2085 // Only use the subject principal as the attr triggering principal if it
2086 // should override the CSP of the node's principal.
2087 if (BasePrincipal::Cast(aSubjectPrincipal)->OverridesCSP(contentPrin)) {
2088 return aSubjectPrincipal;
2091 return contentPrin;
2094 // static
2095 bool nsContentUtils::IsAbsoluteURL(const nsACString& aURL) {
2096 nsAutoCString scheme;
2097 if (NS_FAILED(net_ExtractURLScheme(aURL, scheme))) {
2098 // If we can't extract a scheme, it's not an absolute URL.
2099 return false;
2102 // If it parses as an absolute StandardURL, it's definitely an absolute URL,
2103 // so no need to check with the IO service.
2104 if (net_IsAbsoluteURL(aURL)) {
2105 return true;
2108 uint32_t flags;
2109 if (NS_SUCCEEDED(sIOService->GetProtocolFlags(scheme.get(), &flags))) {
2110 return flags & nsIProtocolHandler::URI_NORELATIVE;
2113 return false;
2116 // static
2117 bool nsContentUtils::InProlog(nsINode* aNode) {
2118 MOZ_ASSERT(aNode, "missing node to nsContentUtils::InProlog");
2120 nsINode* parent = aNode->GetParentNode();
2121 if (!parent || !parent->IsDocument()) {
2122 return false;
2125 Document* doc = parent->AsDocument();
2126 nsIContent* root = doc->GetRootElement();
2128 return !root || doc->ComputeIndexOf(aNode) < doc->ComputeIndexOf(root);
2131 bool nsContentUtils::IsCallerChrome() {
2132 MOZ_ASSERT(NS_IsMainThread());
2133 return SubjectPrincipal() == sSystemPrincipal;
2136 #ifdef FUZZING
2137 bool nsContentUtils::IsFuzzingEnabled() {
2138 return StaticPrefs::fuzzing_enabled();
2140 #endif
2142 /* static */
2143 bool nsContentUtils::IsCallerChromeOrElementTransformGettersEnabled(
2144 JSContext* aCx, JSObject*) {
2145 return ThreadsafeIsSystemCaller(aCx) ||
2146 StaticPrefs::dom_element_transform_getters_enabled();
2149 /* static */
2150 bool nsContentUtils::ShouldResistFingerprinting() {
2151 return StaticPrefs::privacy_resistFingerprinting();
2154 bool nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell) {
2155 if (!aDocShell) {
2156 return ShouldResistFingerprinting();
2158 return ShouldResistFingerprinting(aDocShell->GetDocument());
2161 /* static */
2162 bool nsContentUtils::ShouldResistFingerprinting(const Document* aDoc) {
2163 if (!aDoc) {
2164 return ShouldResistFingerprinting();
2166 bool isChrome = nsContentUtils::IsChromeDoc(aDoc);
2167 return !isChrome && ShouldResistFingerprinting();
2170 /* static */
2171 bool nsContentUtils::ShouldResistFingerprinting(nsIPrincipal* aPrincipal) {
2172 if (!aPrincipal) {
2173 return ShouldResistFingerprinting();
2175 bool isChrome = aPrincipal->IsSystemPrincipal();
2176 return !isChrome && ShouldResistFingerprinting();
2179 /* static */
2180 bool nsContentUtils::ShouldResistFingerprinting(WorkerPrivate* aWorkerPrivate) {
2181 if (!aWorkerPrivate) {
2182 // We may be on a non-worker thread!
2183 return ShouldResistFingerprinting();
2185 bool isChrome = aWorkerPrivate->UsesSystemPrincipal();
2186 return !isChrome && ShouldResistFingerprinting();
2189 /* static */
2190 bool nsContentUtils::UseStandinsForNativeColors() {
2191 return ShouldResistFingerprinting() ||
2192 StaticPrefs::ui_use_standins_for_native_colors();
2195 /* static */
2196 void nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
2197 int32_t aChromeWidth, int32_t aChromeHeight, int32_t aScreenWidth,
2198 int32_t aScreenHeight, int32_t aInputWidth, int32_t aInputHeight,
2199 bool aSetOuterWidth, bool aSetOuterHeight, int32_t* aOutputWidth,
2200 int32_t* aOutputHeight) {
2201 MOZ_ASSERT(aOutputWidth);
2202 MOZ_ASSERT(aOutputHeight);
2204 int32_t availContentWidth = 0;
2205 int32_t availContentHeight = 0;
2207 availContentWidth = std::min(StaticPrefs::privacy_window_maxInnerWidth(),
2208 aScreenWidth - aChromeWidth);
2209 #ifdef MOZ_WIDGET_GTK
2210 // In the GTK window, it will not report outside system decorations
2211 // when we get available window size, see Bug 581863. So, we leave a
2212 // 40 pixels space for them when calculating the available content
2213 // height. It is not necessary for the width since the content width
2214 // is usually pretty much the same as the chrome width.
2215 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2216 (-40 + aScreenHeight) - aChromeHeight);
2217 #else
2218 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2219 aScreenHeight - aChromeHeight);
2220 #endif
2222 // Ideally, we'd like to round window size to 1000x1000, but the
2223 // screen space could be too small to accommodate this size in some
2224 // cases. If it happens, we would round the window size to the nearest
2225 // 200x100.
2226 availContentWidth = availContentWidth - (availContentWidth % 200);
2227 availContentHeight = availContentHeight - (availContentHeight % 100);
2229 // If aIsOuter is true, we are setting the outer window. So we
2230 // have to consider the chrome UI.
2231 int32_t chromeOffsetWidth = aSetOuterWidth ? aChromeWidth : 0;
2232 int32_t chromeOffsetHeight = aSetOuterHeight ? aChromeHeight : 0;
2233 int32_t resultWidth = 0, resultHeight = 0;
2235 // if the original size is greater than the maximum available size, we set
2236 // it to the maximum size. And if the original value is less than the
2237 // minimum rounded size, we set it to the minimum 200x100.
2238 if (aInputWidth > (availContentWidth + chromeOffsetWidth)) {
2239 resultWidth = availContentWidth + chromeOffsetWidth;
2240 } else if (aInputWidth < (200 + chromeOffsetWidth)) {
2241 resultWidth = 200 + chromeOffsetWidth;
2242 } else {
2243 // Otherwise, we round the window to the nearest upper rounded 200x100.
2244 resultWidth = NSToIntCeil((aInputWidth - chromeOffsetWidth) / 200.0) * 200 +
2245 chromeOffsetWidth;
2248 if (aInputHeight > (availContentHeight + chromeOffsetHeight)) {
2249 resultHeight = availContentHeight + chromeOffsetHeight;
2250 } else if (aInputHeight < (100 + chromeOffsetHeight)) {
2251 resultHeight = 100 + chromeOffsetHeight;
2252 } else {
2253 resultHeight =
2254 NSToIntCeil((aInputHeight - chromeOffsetHeight) / 100.0) * 100 +
2255 chromeOffsetHeight;
2258 *aOutputWidth = resultWidth;
2259 *aOutputHeight = resultHeight;
2262 bool nsContentUtils::ThreadsafeIsCallerChrome() {
2263 return NS_IsMainThread() ? IsCallerChrome()
2264 : IsCurrentThreadRunningChromeWorker();
2267 bool nsContentUtils::IsCallerUAWidget() {
2268 JSContext* cx = GetCurrentJSContext();
2269 if (!cx) {
2270 return false;
2273 JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
2274 if (!realm) {
2275 return false;
2278 return xpc::IsUAWidgetScope(realm);
2281 bool nsContentUtils::IsSystemCaller(JSContext* aCx) {
2282 // Note that SubjectPrincipal() assumes we are in a compartment here.
2283 return SubjectPrincipal(aCx) == sSystemPrincipal;
2286 bool nsContentUtils::ThreadsafeIsSystemCaller(JSContext* aCx) {
2287 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
2288 MOZ_ASSERT(ccjscx->Context() == aCx);
2290 return ccjscx->IsSystemCaller();
2293 // static
2294 bool nsContentUtils::LookupBindingMember(
2295 JSContext* aCx, nsIContent* aContent, JS::Handle<jsid> aId,
2296 JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
2297 return true;
2300 nsINode* nsContentUtils::GetNearestInProcessCrossDocParentNode(
2301 nsINode* aChild) {
2302 if (aChild->IsDocument()) {
2303 for (BrowsingContext* bc = aChild->AsDocument()->GetBrowsingContext(); bc;
2304 bc = bc->GetParent()) {
2305 if (bc->GetEmbedderElement()) {
2306 return bc->GetEmbedderElement();
2309 return nullptr;
2312 nsINode* parent = aChild->GetParentNode();
2313 if (parent && parent->IsContent() && aChild->IsContent()) {
2314 parent = aChild->AsContent()->GetFlattenedTreeParent();
2317 return parent;
2320 bool nsContentUtils::ContentIsHostIncludingDescendantOf(
2321 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2322 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2323 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2325 do {
2326 if (aPossibleDescendant == aPossibleAncestor) return true;
2327 if (aPossibleDescendant->IsDocumentFragment()) {
2328 aPossibleDescendant =
2329 aPossibleDescendant->AsDocumentFragment()->GetHost();
2330 } else {
2331 aPossibleDescendant = aPossibleDescendant->GetParentNode();
2333 } while (aPossibleDescendant);
2335 return false;
2338 // static
2339 bool nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
2340 nsINode* aPossibleAncestor) {
2341 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2342 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2344 do {
2345 if (aPossibleDescendant == aPossibleAncestor) {
2346 return true;
2349 aPossibleDescendant =
2350 GetNearestInProcessCrossDocParentNode(aPossibleDescendant);
2351 } while (aPossibleDescendant);
2353 return false;
2356 // static
2357 bool nsContentUtils::ContentIsFlattenedTreeDescendantOf(
2358 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2359 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2360 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2362 do {
2363 if (aPossibleDescendant == aPossibleAncestor) {
2364 return true;
2366 aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
2367 } while (aPossibleDescendant);
2369 return false;
2372 // static
2373 bool nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
2374 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2375 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2376 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2378 do {
2379 if (aPossibleDescendant == aPossibleAncestor) {
2380 return true;
2382 aPossibleDescendant =
2383 aPossibleDescendant->GetFlattenedTreeParentNodeForStyle();
2384 } while (aPossibleDescendant);
2386 return false;
2389 // static
2390 nsINode* nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB) {
2391 while (true && aTargetA) {
2392 // If A's root is not a shadow root...
2393 nsINode* root = aTargetA->SubtreeRoot();
2394 if (!root->IsShadowRoot()) {
2395 // ...then return A.
2396 return aTargetA;
2399 // or A's root is a shadow-including inclusive ancestor of B...
2400 if (aTargetB->IsShadowIncludingInclusiveDescendantOf(root)) {
2401 // ...then return A.
2402 return aTargetA;
2405 aTargetA = ShadowRoot::FromNode(root)->GetHost();
2408 return nullptr;
2411 // static
2412 nsresult nsContentUtils::GetInclusiveAncestors(nsINode* aNode,
2413 nsTArray<nsINode*>& aArray) {
2414 while (aNode) {
2415 aArray.AppendElement(aNode);
2416 aNode = aNode->GetParentNode();
2418 return NS_OK;
2421 // static
2422 nsresult nsContentUtils::GetInclusiveAncestorsAndOffsets(
2423 nsINode* aNode, int32_t aOffset, nsTArray<nsIContent*>* aAncestorNodes,
2424 nsTArray<int32_t>* aAncestorOffsets) {
2425 NS_ENSURE_ARG_POINTER(aNode);
2427 if (!aNode->IsContent()) {
2428 return NS_ERROR_FAILURE;
2430 nsIContent* content = aNode->AsContent();
2432 if (!aAncestorNodes->IsEmpty()) {
2433 NS_WARNING("aAncestorNodes is not empty");
2434 aAncestorNodes->Clear();
2437 if (!aAncestorOffsets->IsEmpty()) {
2438 NS_WARNING("aAncestorOffsets is not empty");
2439 aAncestorOffsets->Clear();
2442 // insert the node itself
2443 aAncestorNodes->AppendElement(content);
2444 aAncestorOffsets->AppendElement(aOffset);
2446 // insert all the ancestors
2447 nsIContent* child = content;
2448 nsIContent* parent = child->GetParent();
2449 while (parent) {
2450 aAncestorNodes->AppendElement(parent);
2451 aAncestorOffsets->AppendElement(parent->ComputeIndexOf(child));
2452 child = parent;
2453 parent = parent->GetParent();
2456 return NS_OK;
2459 template <typename Node, typename GetParentFunc>
2460 static Node* GetCommonAncestorInternal(Node* aNode1, Node* aNode2,
2461 GetParentFunc aGetParentFunc) {
2462 MOZ_ASSERT(aNode1 != aNode2);
2464 // Build the chain of parents
2465 AutoTArray<Node*, 30> parents1, parents2;
2466 do {
2467 parents1.AppendElement(aNode1);
2468 aNode1 = aGetParentFunc(aNode1);
2469 } while (aNode1);
2470 do {
2471 parents2.AppendElement(aNode2);
2472 aNode2 = aGetParentFunc(aNode2);
2473 } while (aNode2);
2475 // Find where the parent chain differs
2476 uint32_t pos1 = parents1.Length();
2477 uint32_t pos2 = parents2.Length();
2478 Node* parent = nullptr;
2479 uint32_t len;
2480 for (len = std::min(pos1, pos2); len > 0; --len) {
2481 Node* child1 = parents1.ElementAt(--pos1);
2482 Node* child2 = parents2.ElementAt(--pos2);
2483 if (child1 != child2) {
2484 break;
2486 parent = child1;
2489 return parent;
2492 /* static */
2493 nsINode* nsContentUtils::GetCommonAncestorHelper(nsINode* aNode1,
2494 nsINode* aNode2) {
2495 return GetCommonAncestorInternal(
2496 aNode1, aNode2, [](nsINode* aNode) { return aNode->GetParentNode(); });
2499 /* static */
2500 nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorHelper(
2501 nsIContent* aContent1, nsIContent* aContent2) {
2502 return GetCommonAncestorInternal(
2503 aContent1, aContent2,
2504 [](nsIContent* aContent) { return aContent->GetFlattenedTreeParent(); });
2507 /* static */
2508 Element* nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
2509 Element* aElement1, Element* aElement2) {
2510 return GetCommonAncestorInternal(aElement1, aElement2, [](Element* aElement) {
2511 return aElement->GetFlattenedTreeParentElementForStyle();
2515 /* static */
2516 bool nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2,
2517 int32_t* aNode1Index,
2518 int32_t* aNode2Index) {
2519 // Note, CompareDocumentPosition takes the latter params in different order.
2520 return (aNode2->CompareDocumentPosition(*aNode1, aNode2Index, aNode1Index) &
2521 (Node_Binding::DOCUMENT_POSITION_PRECEDING |
2522 Node_Binding::DOCUMENT_POSITION_DISCONNECTED)) ==
2523 Node_Binding::DOCUMENT_POSITION_PRECEDING;
2526 /* static */
2527 Maybe<int32_t> nsContentUtils::ComparePoints(
2528 const nsINode* aParent1, int32_t aOffset1, const nsINode* aParent2,
2529 int32_t aOffset2, ComparePointsCache* aParent1Cache) {
2530 bool disconnected{false};
2532 const int32_t order = ComparePoints_Deprecated(
2533 aParent1, aOffset1, aParent2, aOffset2, &disconnected, aParent1Cache);
2534 if (disconnected) {
2535 return Nothing();
2538 return Some(order);
2541 /* static */
2542 int32_t nsContentUtils::ComparePoints_Deprecated(
2543 const nsINode* aParent1, int32_t aOffset1, const nsINode* aParent2,
2544 int32_t aOffset2, bool* aDisconnected, ComparePointsCache* aParent1Cache) {
2545 if (aParent1 == aParent2) {
2546 // XXX This is odd. aOffset1 and/or aOffset2 may be -1, e.g., it's result
2547 // of nsINode::ComputeIndexOf(), but this compares such invalid
2548 // offset with valid offset.
2549 return aOffset1 < aOffset2 ? -1 : aOffset1 > aOffset2 ? 1 : 0;
2552 AutoTArray<const nsINode*, 32> parents1, parents2;
2553 const nsINode* node1 = aParent1;
2554 const nsINode* node2 = aParent2;
2555 do {
2556 parents1.AppendElement(node1);
2557 node1 = node1->GetParentNode();
2558 } while (node1);
2559 do {
2560 parents2.AppendElement(node2);
2561 node2 = node2->GetParentNode();
2562 } while (node2);
2564 uint32_t pos1 = parents1.Length() - 1;
2565 uint32_t pos2 = parents2.Length() - 1;
2567 bool disconnected = parents1.ElementAt(pos1) != parents2.ElementAt(pos2);
2568 if (aDisconnected) {
2569 *aDisconnected = disconnected;
2571 if (disconnected) {
2572 NS_ASSERTION(aDisconnected, "unexpected disconnected nodes");
2573 return 1;
2576 // Find where the parent chains differ
2577 const nsINode* parent = parents1.ElementAt(pos1);
2578 uint32_t len;
2579 for (len = std::min(pos1, pos2); len > 0; --len) {
2580 const nsINode* child1 = parents1.ElementAt(--pos1);
2581 const nsINode* child2 = parents2.ElementAt(--pos2);
2582 if (child1 != child2) {
2583 int32_t child1index = aParent1Cache
2584 ? aParent1Cache->ComputeIndexOf(parent, child1)
2585 : parent->ComputeIndexOf(child1);
2586 return child1index < parent->ComputeIndexOf(child2) ? -1 : 1;
2588 parent = child1;
2591 // The parent chains never differed, so one of the nodes is an ancestor of
2592 // the other
2594 NS_ASSERTION(!pos1 || !pos2,
2595 "should have run out of parent chain for one of the nodes");
2597 if (!pos1) {
2598 const nsINode* child2 = parents2.ElementAt(--pos2);
2599 // XXX aOffset1 may be -1 as mentioned above. So, why does this return
2600 // it's *before* of the valid DOM point?
2601 return aOffset1 <= parent->ComputeIndexOf(child2) ? -1 : 1;
2604 const nsINode* child1 = parents1.ElementAt(--pos1);
2605 // XXX aOffset2 may be -1 as mentioned above. So, why does this return it's
2606 // *after* of the valid DOM point?
2607 int32_t child1index = aParent1Cache
2608 ? aParent1Cache->ComputeIndexOf(parent, child1)
2609 : parent->ComputeIndexOf(child1);
2610 return child1index < aOffset2 ? -1 : 1;
2613 // static
2614 nsINode* nsContentUtils::GetCommonAncestorUnderInteractiveContent(
2615 nsINode* aNode1, nsINode* aNode2) {
2616 if (!aNode1 || !aNode2) {
2617 return nullptr;
2620 if (aNode1 == aNode2) {
2621 return aNode1;
2624 // Build the chain of parents
2625 AutoTArray<nsINode*, 30> parents1;
2626 do {
2627 parents1.AppendElement(aNode1);
2628 if (aNode1->IsElement() &&
2629 aNode1->AsElement()->IsInteractiveHTMLContent()) {
2630 break;
2632 aNode1 = aNode1->GetFlattenedTreeParentNode();
2633 } while (aNode1);
2635 AutoTArray<nsINode*, 30> parents2;
2636 do {
2637 parents2.AppendElement(aNode2);
2638 if (aNode2->IsElement() &&
2639 aNode2->AsElement()->IsInteractiveHTMLContent()) {
2640 break;
2642 aNode2 = aNode2->GetFlattenedTreeParentNode();
2643 } while (aNode2);
2645 // Find where the parent chain differs
2646 uint32_t pos1 = parents1.Length();
2647 uint32_t pos2 = parents2.Length();
2648 nsINode* parent = nullptr;
2649 for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
2650 nsINode* child1 = parents1.ElementAt(--pos1);
2651 nsINode* child2 = parents2.ElementAt(--pos2);
2652 if (child1 != child2) {
2653 break;
2655 parent = child1;
2658 return parent;
2661 /* static */
2662 BrowserParent* nsContentUtils::GetCommonBrowserParentAncestor(
2663 BrowserParent* aBrowserParent1, BrowserParent* aBrowserParent2) {
2664 return GetCommonAncestorInternal(
2665 aBrowserParent1, aBrowserParent2, [](BrowserParent* aBrowserParent) {
2666 return aBrowserParent->GetBrowserBridgeParent()
2667 ? aBrowserParent->GetBrowserBridgeParent()->Manager()
2668 : nullptr;
2672 /* static */
2673 template <typename FPT, typename FRT, typename SPT, typename SRT>
2674 Maybe<int32_t> nsContentUtils::ComparePoints(
2675 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
2676 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary) {
2677 if (!aFirstBoundary.IsSet() || !aSecondBoundary.IsSet()) {
2678 return Nothing{};
2681 bool disconnected{false};
2682 const int32_t order =
2683 ComparePoints_Deprecated(aFirstBoundary, aSecondBoundary, &disconnected);
2685 if (disconnected) {
2686 return Nothing{};
2689 return Some(order);
2692 /* static */
2693 template <typename FPT, typename FRT, typename SPT, typename SRT>
2694 int32_t nsContentUtils::ComparePoints_Deprecated(
2695 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
2696 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary, bool* aDisconnected) {
2697 if (NS_WARN_IF(!aFirstBoundary.IsSet()) ||
2698 NS_WARN_IF(!aSecondBoundary.IsSet())) {
2699 return -1;
2701 // XXX Re-implement this without calling `Offset()` as far as possible,
2702 // and the other overload should be an alias of this.
2703 return ComparePoints_Deprecated(
2704 aFirstBoundary.Container(),
2705 *aFirstBoundary.Offset(
2706 RangeBoundaryBase<FPT, FRT>::OffsetFilter::kValidOrInvalidOffsets),
2707 aSecondBoundary.Container(),
2708 *aSecondBoundary.Offset(
2709 RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOrInvalidOffsets),
2710 aDisconnected);
2713 inline bool IsCharInSet(const char* aSet, const char16_t aChar) {
2714 char16_t ch;
2715 while ((ch = *aSet)) {
2716 if (aChar == char16_t(ch)) {
2717 return true;
2719 ++aSet;
2721 return false;
2725 * This method strips leading/trailing chars, in given set, from string.
2728 // static
2729 const nsDependentSubstring nsContentUtils::TrimCharsInSet(
2730 const char* aSet, const nsAString& aValue) {
2731 nsAString::const_iterator valueCurrent, valueEnd;
2733 aValue.BeginReading(valueCurrent);
2734 aValue.EndReading(valueEnd);
2736 // Skip characters in the beginning
2737 while (valueCurrent != valueEnd) {
2738 if (!IsCharInSet(aSet, *valueCurrent)) {
2739 break;
2741 ++valueCurrent;
2744 if (valueCurrent != valueEnd) {
2745 for (;;) {
2746 --valueEnd;
2747 if (!IsCharInSet(aSet, *valueEnd)) {
2748 break;
2751 ++valueEnd; // Step beyond the last character we want in the value.
2754 // valueEnd should point to the char after the last to copy
2755 return Substring(valueCurrent, valueEnd);
2759 * This method strips leading and trailing whitespace from a string.
2762 // static
2763 template <bool IsWhitespace(char16_t)>
2764 const nsDependentSubstring nsContentUtils::TrimWhitespace(const nsAString& aStr,
2765 bool aTrimTrailing) {
2766 nsAString::const_iterator start, end;
2768 aStr.BeginReading(start);
2769 aStr.EndReading(end);
2771 // Skip whitespace characters in the beginning
2772 while (start != end && IsWhitespace(*start)) {
2773 ++start;
2776 if (aTrimTrailing) {
2777 // Skip whitespace characters in the end.
2778 while (end != start) {
2779 --end;
2781 if (!IsWhitespace(*end)) {
2782 // Step back to the last non-whitespace character.
2783 ++end;
2785 break;
2790 // Return a substring for the string w/o leading and/or trailing
2791 // whitespace
2793 return Substring(start, end);
2796 // Declaring the templates we are going to use avoid linking issues without
2797 // inlining the method. Considering there is not so much spaces checking
2798 // methods we can consider this to be better than inlining.
2799 template const nsDependentSubstring
2800 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, bool);
2801 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
2802 nsContentUtils::IsHTMLWhitespace>(const nsAString&, bool);
2803 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
2804 nsContentUtils::IsHTMLWhitespaceOrNBSP>(const nsAString&, bool);
2806 static inline void KeyAppendSep(nsACString& aKey) {
2807 if (!aKey.IsEmpty()) {
2808 aKey.Append('>');
2812 static inline void KeyAppendString(const nsAString& aString, nsACString& aKey) {
2813 KeyAppendSep(aKey);
2815 // Could escape separator here if collisions happen. > is not a legal char
2816 // for a name or type attribute, so we should be safe avoiding that extra
2817 // work.
2819 AppendUTF16toUTF8(aString, aKey);
2822 static inline void KeyAppendString(const nsACString& aString,
2823 nsACString& aKey) {
2824 KeyAppendSep(aKey);
2826 // Could escape separator here if collisions happen. > is not a legal char
2827 // for a name or type attribute, so we should be safe avoiding that extra
2828 // work.
2830 aKey.Append(aString);
2833 static inline void KeyAppendInt(int32_t aInt, nsACString& aKey) {
2834 KeyAppendSep(aKey);
2836 aKey.AppendInt(aInt);
2839 static inline bool IsAutocompleteOff(const nsIContent* aContent) {
2840 return aContent->IsElement() &&
2841 aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
2842 nsGkAtoms::autocomplete, u"off"_ns,
2843 eIgnoreCase);
2846 /*static*/
2847 void nsContentUtils::GenerateStateKey(nsIContent* aContent, Document* aDocument,
2848 nsACString& aKey) {
2849 MOZ_ASSERT(aContent);
2851 aKey.Truncate();
2853 uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
2855 // Don't capture state for anonymous content
2856 if (aContent->IsInNativeAnonymousSubtree()) {
2857 return;
2860 if (IsAutocompleteOff(aContent)) {
2861 return;
2864 RefPtr<Document> doc = aContent->GetUncomposedDoc();
2866 KeyAppendInt(partID, aKey); // first append a partID
2867 bool generatedUniqueKey = false;
2869 if (doc && doc->IsHTMLOrXHTML()) {
2870 nsHTMLDocument* htmlDoc = doc->AsHTMLDocument();
2872 // If we have a form control and can calculate form information, use that
2873 // as the key - it is more reliable than just recording position in the
2874 // DOM.
2875 // XXXbz Is it, really? We have bugs on this, I think...
2876 // Important to have a unique key, and tag/type/name may not be.
2878 // The format of the key depends on whether the control has a form,
2879 // and whether the element was parser inserted:
2881 // [Has Form, Parser Inserted]:
2882 // fp>type>FormNum>IndOfControlInForm>FormName>name
2884 // [No Form, Parser Inserted]:
2885 // dp>type>ControlNum>name
2887 // [Has Form, Not Parser Inserted]:
2888 // fn>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
2890 // [No Form, Not Parser Inserted]:
2891 // dn>type>IndOfControlInDoc>name
2893 // XXX We don't need to use index if name is there
2894 // XXXbz We don't? Why not? I don't follow.
2896 nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
2897 if (control) {
2898 // Get the control number if this was a parser inserted element from the
2899 // network.
2900 int32_t controlNumber =
2901 control->GetParserInsertedControlNumberForStateKey();
2902 bool parserInserted = controlNumber != -1;
2904 RefPtr<nsContentList> htmlForms;
2905 RefPtr<nsContentList> htmlFormControls;
2906 if (!parserInserted) {
2907 // Getting these lists is expensive, as we need to keep them up to date
2908 // as the document loads, so we avoid it if we don't need them.
2909 htmlDoc->GetFormsAndFormControls(getter_AddRefs(htmlForms),
2910 getter_AddRefs(htmlFormControls));
2913 // Append the control type
2914 KeyAppendInt(control->ControlType(), aKey);
2916 // If in a form, add form name / index of form / index in form
2917 HTMLFormElement* formElement = control->GetFormElement();
2918 if (formElement) {
2919 if (IsAutocompleteOff(formElement)) {
2920 aKey.Truncate();
2921 return;
2924 // Append the form number, if this is a parser inserted control, or
2925 // the index of the form in the document otherwise.
2926 bool appendedForm = false;
2927 if (parserInserted) {
2928 MOZ_ASSERT(formElement->GetFormNumberForStateKey() != -1,
2929 "when generating a state key for a parser inserted form "
2930 "control we should have a parser inserted <form> element");
2931 KeyAppendString("fp"_ns, aKey);
2932 KeyAppendInt(formElement->GetFormNumberForStateKey(), aKey);
2933 appendedForm = true;
2934 } else {
2935 KeyAppendString("fn"_ns, aKey);
2936 int32_t index = htmlForms->IndexOf(formElement, false);
2937 if (index <= -1) {
2939 // XXX HACK this uses some state that was dumped into the document
2940 // specifically to fix bug 138892. What we are trying to do is
2941 // *guess* which form this control's state is found in, with the
2942 // highly likely guess that the highest form parsed so far is the
2943 // one. This code should not be on trunk, only branch.
2945 index = htmlDoc->GetNumFormsSynchronous() - 1;
2947 if (index > -1) {
2948 KeyAppendInt(index, aKey);
2949 appendedForm = true;
2953 if (appendedForm) {
2954 // Append the index of the control in the form
2955 int32_t index = formElement->IndexOfControl(control);
2957 if (index > -1) {
2958 KeyAppendInt(index, aKey);
2959 generatedUniqueKey = true;
2963 // Append the form name
2964 nsAutoString formName;
2965 formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
2966 KeyAppendString(formName, aKey);
2967 } else {
2968 // Not in a form. Append the control number, if this is a parser
2969 // inserted control, or the index of the control in the document
2970 // otherwise.
2971 if (parserInserted) {
2972 KeyAppendString("dp"_ns, aKey);
2973 KeyAppendInt(control->GetParserInsertedControlNumberForStateKey(),
2974 aKey);
2975 generatedUniqueKey = true;
2976 } else {
2977 KeyAppendString("dn"_ns, aKey);
2978 int32_t index = htmlFormControls->IndexOf(aContent, true);
2979 if (index > -1) {
2980 KeyAppendInt(index, aKey);
2981 generatedUniqueKey = true;
2985 // Append the control name
2986 nsAutoString name;
2987 aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name,
2988 name);
2989 KeyAppendString(name, aKey);
2994 if (!generatedUniqueKey) {
2995 // Either we didn't have a form control or we aren't in an HTML document so
2996 // we can't figure out form info. Append the tag name if it's an element
2997 // to avoid restoring state for one type of element on another type.
2998 if (aContent->IsElement()) {
2999 KeyAppendString(nsDependentAtomString(aContent->NodeInfo()->NameAtom()),
3000 aKey);
3001 } else {
3002 // Append a character that is not "d" or "f" to disambiguate from
3003 // the case when we were a form control in an HTML document.
3004 KeyAppendString("o"_ns, aKey);
3007 // Now start at aContent and append the indices of it and all its ancestors
3008 // in their containers. That should at least pin down its position in the
3009 // DOM...
3010 nsINode* parent = aContent->GetParentNode();
3011 nsINode* content = aContent;
3012 while (parent) {
3013 KeyAppendInt(parent->ComputeIndexOf(content), aKey);
3014 content = parent;
3015 parent = content->GetParentNode();
3020 // static
3021 nsIPrincipal* nsContentUtils::SubjectPrincipal(JSContext* aCx) {
3022 MOZ_ASSERT(NS_IsMainThread());
3024 // As opposed to SubjectPrincipal(), we do in fact assume that
3025 // we're in a realm here; anyone who calls this function in
3026 // situations where that's not the case is doing it wrong.
3027 JS::Realm* realm = js::GetContextRealm(aCx);
3028 MOZ_ASSERT(realm);
3030 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3031 return nsJSPrincipals::get(principals);
3034 // static
3035 nsIPrincipal* nsContentUtils::SubjectPrincipal() {
3036 MOZ_ASSERT(IsInitialized());
3037 MOZ_ASSERT(NS_IsMainThread());
3038 JSContext* cx = GetCurrentJSContext();
3039 if (!cx) {
3040 MOZ_CRASH(
3041 "Accessing the Subject Principal without an AutoJSAPI on the stack is "
3042 "forbidden");
3045 JS::Realm* realm = js::GetContextRealm(cx);
3047 // When an AutoJSAPI is instantiated, we are in a null realm until the
3048 // first JSAutoRealm, which is kind of a purgatory as far as permissions
3049 // go. It would be nice to just hard-abort if somebody does a security check
3050 // in this purgatory zone, but that would be too fragile, since it could be
3051 // triggered by random IsCallerChrome() checks 20-levels deep.
3053 // So we want to return _something_ here - and definitely not the System
3054 // Principal, since that would make an AutoJSAPI a very dangerous thing to
3055 // instantiate.
3057 // The natural thing to return is a null principal. Ideally, we'd return a
3058 // different null principal each time, to avoid any unexpected interactions
3059 // when the principal accidentally gets inherited somewhere. But
3060 // SubjectPrincipal doesn't return strong references, so there's no way to
3061 // sanely manage the lifetime of multiple null principals.
3063 // So we use a singleton null principal. To avoid it being accidentally
3064 // inherited and becoming a "real" subject or object principal, we do a
3065 // release-mode assert during realm creation against using this principal on
3066 // an actual global.
3067 if (!realm) {
3068 return sNullSubjectPrincipal;
3071 return SubjectPrincipal(cx);
3074 // static
3075 nsIPrincipal* nsContentUtils::ObjectPrincipal(JSObject* aObj) {
3076 MOZ_ASSERT(NS_IsMainThread());
3078 #ifdef DEBUG
3079 JS::AssertObjectBelongsToCurrentThread(aObj);
3080 #endif
3082 MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(aObj));
3084 JS::Realm* realm = js::GetNonCCWObjectRealm(aObj);
3085 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3086 return nsJSPrincipals::get(principals);
3089 // static
3090 nsresult nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
3091 const nsAString& aSpec,
3092 Document* aDocument,
3093 nsIURI* aBaseURI) {
3094 if (aDocument) {
3095 return NS_NewURI(aResult, aSpec, aDocument->GetDocumentCharacterSet(),
3096 aBaseURI);
3098 return NS_NewURI(aResult, aSpec, nullptr, aBaseURI);
3101 // static
3102 bool nsContentUtils::IsNameWithDash(nsAtom* aName) {
3103 // A valid custom element name is a sequence of characters name which
3104 // must match the PotentialCustomElementName production:
3105 // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
3106 const char16_t* name = aName->GetUTF16String();
3107 uint32_t len = aName->GetLength();
3108 bool hasDash = false;
3110 if (!len || name[0] < 'a' || name[0] > 'z') {
3111 return false;
3114 uint32_t i = 1;
3115 while (i < len) {
3116 if (i + 1 < len && NS_IS_SURROGATE_PAIR(name[i], name[i + 1])) {
3117 // Merged two 16-bit surrogate pairs into code point.
3118 char32_t code = SURROGATE_TO_UCS4(name[i], name[i + 1]);
3120 if (code < 0x10000 || code > 0xEFFFF) {
3121 return false;
3124 i += 2;
3125 } else {
3126 if (name[i] == '-') {
3127 hasDash = true;
3130 if (name[i] != '-' && name[i] != '.' && name[i] != '_' &&
3131 name[i] != 0xB7 && (name[i] < '0' || name[i] > '9') &&
3132 (name[i] < 'a' || name[i] > 'z') &&
3133 (name[i] < 0xC0 || name[i] > 0xD6) &&
3134 (name[i] < 0xF8 || name[i] > 0x37D) &&
3135 (name[i] < 0x37F || name[i] > 0x1FFF) &&
3136 (name[i] < 0x200C || name[i] > 0x200D) &&
3137 (name[i] < 0x203F || name[i] > 0x2040) &&
3138 (name[i] < 0x2070 || name[i] > 0x218F) &&
3139 (name[i] < 0x2C00 || name[i] > 0x2FEF) &&
3140 (name[i] < 0x3001 || name[i] > 0xD7FF) &&
3141 (name[i] < 0xF900 || name[i] > 0xFDCF) &&
3142 (name[i] < 0xFDF0 || name[i] > 0xFFFD)) {
3143 return false;
3146 i++;
3150 return hasDash;
3153 // static
3154 bool nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID) {
3155 // Allow non-dashed names in XUL for XBL to Custom Element migrations.
3156 if (aNameSpaceID == kNameSpaceID_XUL) {
3157 return true;
3160 bool hasDash = IsNameWithDash(aName);
3161 if (!hasDash) {
3162 return false;
3165 // The custom element name must not be one of the following values:
3166 // annotation-xml
3167 // color-profile
3168 // font-face
3169 // font-face-src
3170 // font-face-uri
3171 // font-face-format
3172 // font-face-name
3173 // missing-glyph
3174 return aName != nsGkAtoms::annotation_xml_ &&
3175 aName != nsGkAtoms::colorProfile && aName != nsGkAtoms::font_face &&
3176 aName != nsGkAtoms::font_face_src &&
3177 aName != nsGkAtoms::font_face_uri &&
3178 aName != nsGkAtoms::font_face_format &&
3179 aName != nsGkAtoms::font_face_name && aName != nsGkAtoms::missingGlyph;
3182 // static
3183 nsresult nsContentUtils::CheckQName(const nsAString& aQualifiedName,
3184 bool aNamespaceAware,
3185 const char16_t** aColon) {
3186 const char* colon = nullptr;
3187 const char16_t* begin = aQualifiedName.BeginReading();
3188 const char16_t* end = aQualifiedName.EndReading();
3190 int result = MOZ_XMLCheckQName(reinterpret_cast<const char*>(begin),
3191 reinterpret_cast<const char*>(end),
3192 aNamespaceAware, &colon);
3194 if (!result) {
3195 if (aColon) {
3196 *aColon = reinterpret_cast<const char16_t*>(colon);
3199 return NS_OK;
3202 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
3205 // static
3206 nsresult nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
3207 const nsString& aQName, int32_t* aNamespace,
3208 nsAtom** aLocalName) {
3209 const char16_t* colon;
3210 nsresult rv = nsContentUtils::CheckQName(aQName, true, &colon);
3211 NS_ENSURE_SUCCESS(rv, rv);
3213 if (colon) {
3214 const char16_t* end;
3215 aQName.EndReading(end);
3216 nsAutoString nameSpace;
3217 rv = aNamespaceResolver->LookupNamespaceURIInternal(
3218 Substring(aQName.get(), colon), nameSpace);
3219 NS_ENSURE_SUCCESS(rv, rv);
3221 *aNamespace = NameSpaceManager()->GetNameSpaceID(
3222 nameSpace, nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
3223 if (*aNamespace == kNameSpaceID_Unknown) return NS_ERROR_FAILURE;
3225 *aLocalName = NS_AtomizeMainThread(Substring(colon + 1, end)).take();
3226 } else {
3227 *aNamespace = kNameSpaceID_None;
3228 *aLocalName = NS_AtomizeMainThread(aQName).take();
3230 NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
3231 return NS_OK;
3234 // static
3235 nsresult nsContentUtils::GetNodeInfoFromQName(
3236 const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
3237 nsNodeInfoManager* aNodeInfoManager, uint16_t aNodeType,
3238 mozilla::dom::NodeInfo** aNodeInfo) {
3239 const nsString& qName = PromiseFlatString(aQualifiedName);
3240 const char16_t* colon;
3241 nsresult rv = nsContentUtils::CheckQName(qName, true, &colon);
3242 NS_ENSURE_SUCCESS(rv, rv);
3244 int32_t nsID;
3245 sNameSpaceManager->RegisterNameSpace(aNamespaceURI, nsID);
3246 if (colon) {
3247 const char16_t* end;
3248 qName.EndReading(end);
3250 RefPtr<nsAtom> prefix = NS_AtomizeMainThread(Substring(qName.get(), colon));
3252 rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix, nsID,
3253 aNodeType, aNodeInfo);
3254 } else {
3255 rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID, aNodeType,
3256 aNodeInfo);
3258 NS_ENSURE_SUCCESS(rv, rv);
3260 return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
3261 (*aNodeInfo)->GetPrefixAtom(),
3262 (*aNodeInfo)->NamespaceID())
3263 ? NS_OK
3264 : NS_ERROR_DOM_NAMESPACE_ERR;
3267 // static
3268 void nsContentUtils::SplitExpatName(const char16_t* aExpatName,
3269 nsAtom** aPrefix, nsAtom** aLocalName,
3270 int32_t* aNameSpaceID) {
3272 * Expat can send the following:
3273 * localName
3274 * namespaceURI<separator>localName
3275 * namespaceURI<separator>localName<separator>prefix
3277 * and we use 0xFFFF for the <separator>.
3281 const char16_t* uriEnd = nullptr;
3282 const char16_t* nameEnd = nullptr;
3283 const char16_t* pos;
3284 for (pos = aExpatName; *pos; ++pos) {
3285 if (*pos == 0xFFFF) {
3286 if (uriEnd) {
3287 nameEnd = pos;
3288 } else {
3289 uriEnd = pos;
3294 const char16_t* nameStart;
3295 if (uriEnd) {
3296 if (sNameSpaceManager) {
3297 sNameSpaceManager->RegisterNameSpace(
3298 nsDependentSubstring(aExpatName, uriEnd), *aNameSpaceID);
3299 } else {
3300 *aNameSpaceID = kNameSpaceID_Unknown;
3303 nameStart = (uriEnd + 1);
3304 if (nameEnd) {
3305 const char16_t* prefixStart = nameEnd + 1;
3306 *aPrefix = NS_AtomizeMainThread(Substring(prefixStart, pos)).take();
3307 } else {
3308 nameEnd = pos;
3309 *aPrefix = nullptr;
3311 } else {
3312 *aNameSpaceID = kNameSpaceID_None;
3313 nameStart = aExpatName;
3314 nameEnd = pos;
3315 *aPrefix = nullptr;
3317 *aLocalName = NS_AtomizeMainThread(Substring(nameStart, nameEnd)).take();
3320 // static
3321 PresShell* nsContentUtils::GetPresShellForContent(const nsIContent* aContent) {
3322 Document* doc = aContent->GetComposedDoc();
3323 if (!doc) {
3324 return nullptr;
3326 return doc->GetPresShell();
3329 // static
3330 nsPresContext* nsContentUtils::GetContextForContent(
3331 const nsIContent* aContent) {
3332 PresShell* presShell = GetPresShellForContent(aContent);
3333 if (!presShell) {
3334 return nullptr;
3336 return presShell->GetPresContext();
3339 // static
3340 bool nsContentUtils::CanLoadImage(nsIURI* aURI, nsINode* aNode,
3341 Document* aLoadingDocument,
3342 nsIPrincipal* aLoadingPrincipal) {
3343 MOZ_ASSERT(aURI, "Must have a URI");
3344 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3345 MOZ_ASSERT(aLoadingPrincipal, "Must have a loading principal");
3347 nsresult rv;
3349 auto appType = nsIDocShell::APP_TYPE_UNKNOWN;
3352 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
3353 aLoadingDocument->GetDocShell();
3354 if (docShellTreeItem) {
3355 nsCOMPtr<nsIDocShellTreeItem> root;
3356 docShellTreeItem->GetInProcessRootTreeItem(getter_AddRefs(root));
3358 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
3360 if (docShell) {
3361 appType = docShell->GetAppType();
3366 if (appType != nsIDocShell::APP_TYPE_EDITOR) {
3367 // Editor apps get special treatment here, editors can load images
3368 // from anywhere. This allows editor to insert images from file://
3369 // into documents that are being edited.
3370 rv = sSecurityManager->CheckLoadURIWithPrincipal(
3371 aLoadingPrincipal, aURI, nsIScriptSecurityManager::ALLOW_CHROME,
3372 aLoadingDocument->InnerWindowID());
3373 if (NS_FAILED(rv)) {
3374 return false;
3378 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new mozilla::net::LoadInfo(
3379 aLoadingPrincipal,
3380 aLoadingPrincipal, // triggering principal
3381 aNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
3382 nsIContentPolicy::TYPE_INTERNAL_IMAGE);
3384 int16_t decision = nsIContentPolicy::ACCEPT;
3386 rv = NS_CheckContentLoadPolicy(aURI, secCheckLoadInfo,
3387 ""_ns, // mime guess
3388 &decision, GetContentPolicy());
3390 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(decision);
3393 // static
3394 bool nsContentUtils::IsInPrivateBrowsing(Document* aDoc) {
3395 if (!aDoc) {
3396 return false;
3399 nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
3400 if (loadGroup) {
3401 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3402 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3403 if (callbacks) {
3404 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3405 if (loadContext) {
3406 return loadContext->UsePrivateBrowsing();
3411 nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
3412 return channel && NS_UsePrivateBrowsing(channel);
3415 // static
3416 bool nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup) {
3417 if (!aLoadGroup) {
3418 return false;
3420 bool isPrivate = false;
3421 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3422 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3423 if (callbacks) {
3424 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3425 isPrivate = loadContext && loadContext->UsePrivateBrowsing();
3427 return isPrivate;
3430 // FIXME(emilio): This is (effectively) almost but not quite the same as
3431 // Document::ShouldLoadImages(), which one is right?
3432 bool nsContentUtils::DocumentInactiveForImageLoads(Document* aDocument) {
3433 if (!aDocument) {
3434 return false;
3436 if (IsChromeDoc(aDocument) || aDocument->IsResourceDoc() ||
3437 aDocument->IsStaticDocument()) {
3438 return false;
3440 nsCOMPtr<nsPIDOMWindowInner> win =
3441 do_QueryInterface(aDocument->GetScopeObject());
3442 return !win || !win->GetDocShell();
3445 imgLoader* nsContentUtils::GetImgLoaderForDocument(Document* aDoc) {
3446 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
3448 if (!aDoc) {
3449 return imgLoader::NormalLoader();
3451 bool isPrivate = IsInPrivateBrowsing(aDoc);
3452 return isPrivate ? imgLoader::PrivateBrowsingLoader()
3453 : imgLoader::NormalLoader();
3456 // static
3457 imgLoader* nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
3458 Document* aContext) {
3459 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
3461 if (!aChannel) {
3462 return imgLoader::NormalLoader();
3464 nsCOMPtr<nsILoadContext> context;
3465 NS_QueryNotificationCallbacks(aChannel, context);
3466 return context && context->UsePrivateBrowsing()
3467 ? imgLoader::PrivateBrowsingLoader()
3468 : imgLoader::NormalLoader();
3471 // static
3472 bool nsContentUtils::IsImageInCache(nsIURI* aURI, Document* aDocument) {
3473 imgILoader* loader = GetImgLoaderForDocument(aDocument);
3474 nsCOMPtr<imgICache> cache = do_QueryInterface(loader);
3476 // If something unexpected happened we return false, otherwise if props
3477 // is set, the image is cached and we return true
3478 nsCOMPtr<nsIProperties> props;
3479 nsresult rv =
3480 cache->FindEntryProperties(aURI, aDocument, getter_AddRefs(props));
3481 return (NS_SUCCEEDED(rv) && props);
3484 // static
3485 int32_t nsContentUtils::CORSModeToLoadImageFlags(mozilla::CORSMode aMode) {
3486 switch (aMode) {
3487 case CORS_ANONYMOUS:
3488 return imgILoader::LOAD_CORS_ANONYMOUS;
3489 case CORS_USE_CREDENTIALS:
3490 return imgILoader::LOAD_CORS_USE_CREDENTIALS;
3491 default:
3492 return 0;
3496 // static
3497 nsresult nsContentUtils::LoadImage(
3498 nsIURI* aURI, nsINode* aContext, Document* aLoadingDocument,
3499 nsIPrincipal* aLoadingPrincipal, uint64_t aRequestContextID,
3500 nsIReferrerInfo* aReferrerInfo, imgINotificationObserver* aObserver,
3501 int32_t aLoadFlags, const nsAString& initiatorType,
3502 imgRequestProxy** aRequest, nsContentPolicyType aContentPolicyType,
3503 bool aUseUrgentStartForChannel, bool aLinkPreload) {
3504 MOZ_ASSERT(aURI, "Must have a URI");
3505 MOZ_ASSERT(aContext, "Must have a context");
3506 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3507 MOZ_ASSERT(aLoadingPrincipal, "Must have a principal");
3508 MOZ_ASSERT(aRequest, "Null out param");
3510 imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
3511 if (!imgLoader) {
3512 // nothing we can do here
3513 return NS_ERROR_FAILURE;
3516 nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
3518 nsIURI* documentURI = aLoadingDocument->GetDocumentURI();
3520 NS_ASSERTION(loadGroup || IsFontTableURI(documentURI),
3521 "Could not get loadgroup; onload may fire too early");
3523 // XXXbz using "documentURI" for the initialDocumentURI is not quite
3524 // right, but the best we can do here...
3525 return imgLoader->LoadImage(aURI, /* uri to load */
3526 documentURI, /* initialDocumentURI */
3527 aReferrerInfo, /* referrerInfo */
3528 aLoadingPrincipal, /* loading principal */
3529 aRequestContextID, /* request context ID */
3530 loadGroup, /* loadgroup */
3531 aObserver, /* imgINotificationObserver */
3532 aContext, /* loading context */
3533 aLoadingDocument, /* uniquification key */
3534 aLoadFlags, /* load flags */
3535 nullptr, /* cache key */
3536 aContentPolicyType, /* content policy type */
3537 initiatorType, /* the load initiator */
3538 aUseUrgentStartForChannel, /* urgent-start flag */
3539 aLinkPreload, /* <link preload> initiator */
3540 aRequest);
3543 // static
3544 already_AddRefed<imgIContainer> nsContentUtils::GetImageFromContent(
3545 nsIImageLoadingContent* aContent, imgIRequest** aRequest) {
3546 if (aRequest) {
3547 *aRequest = nullptr;
3550 NS_ENSURE_TRUE(aContent, nullptr);
3552 nsCOMPtr<imgIRequest> imgRequest;
3553 aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3554 getter_AddRefs(imgRequest));
3555 if (!imgRequest) {
3556 return nullptr;
3559 nsCOMPtr<imgIContainer> imgContainer;
3560 imgRequest->GetImage(getter_AddRefs(imgContainer));
3562 if (!imgContainer) {
3563 return nullptr;
3566 if (aRequest) {
3567 // If the consumer wants the request, verify it has actually loaded
3568 // successfully.
3569 uint32_t imgStatus;
3570 imgRequest->GetImageStatus(&imgStatus);
3571 if (imgStatus & imgIRequest::STATUS_FRAME_COMPLETE &&
3572 !(imgStatus & imgIRequest::STATUS_ERROR)) {
3573 imgRequest.swap(*aRequest);
3577 return imgContainer.forget();
3580 // static
3581 bool nsContentUtils::ContentIsDraggable(nsIContent* aContent) {
3582 MOZ_ASSERT(aContent);
3584 if (auto htmlElement = nsGenericHTMLElement::FromNode(aContent)) {
3585 if (htmlElement->Draggable()) {
3586 return true;
3589 if (htmlElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
3590 nsGkAtoms::_false, eIgnoreCase)) {
3591 return false;
3594 if (aContent->IsSVGElement()) {
3595 return false;
3598 // special handling for content area image and link dragging
3599 return IsDraggableImage(aContent) || IsDraggableLink(aContent);
3602 // static
3603 bool nsContentUtils::IsDraggableImage(nsIContent* aContent) {
3604 MOZ_ASSERT(aContent, "Must have content node to test");
3606 nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(aContent));
3607 if (!imageContent) {
3608 return false;
3611 nsCOMPtr<imgIRequest> imgRequest;
3612 imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3613 getter_AddRefs(imgRequest));
3615 // XXXbz It may be draggable even if the request resulted in an error. Why?
3616 // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
3617 return imgRequest != nullptr;
3620 // static
3621 bool nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
3622 nsCOMPtr<nsIURI> absURI;
3623 return aContent->IsLink(getter_AddRefs(absURI));
3626 // static
3627 nsresult nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo,
3628 nsAtom* aName,
3629 mozilla::dom::NodeInfo** aResult) {
3630 nsNodeInfoManager* niMgr = aNodeInfo->NodeInfoManager();
3632 *aResult = niMgr
3633 ->GetNodeInfo(aName, nullptr, aNodeInfo->NamespaceID(),
3634 aNodeInfo->NodeType(), aNodeInfo->GetExtraName())
3635 .take();
3636 return NS_OK;
3639 static bool TestSitePerm(nsIPrincipal* aPrincipal, const nsACString& aType,
3640 uint32_t aPerm, bool aExactHostMatch) {
3641 if (!aPrincipal) {
3642 // We always deny (i.e. don't allow) the permission if we don't have a
3643 // principal.
3644 return aPerm != nsIPermissionManager::ALLOW_ACTION;
3647 nsCOMPtr<nsIPermissionManager> permMgr =
3648 components::PermissionManager::Service();
3649 NS_ENSURE_TRUE(permMgr, false);
3651 uint32_t perm;
3652 nsresult rv;
3653 if (aExactHostMatch) {
3654 rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
3655 } else {
3656 rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
3658 NS_ENSURE_SUCCESS(rv, false);
3660 return perm == aPerm;
3663 bool nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal,
3664 const nsACString& aType) {
3665 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
3666 false);
3669 bool nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal,
3670 const nsACString& aType) {
3671 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
3672 false);
3675 bool nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal,
3676 const nsACString& aType) {
3677 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
3678 true);
3681 bool nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal,
3682 const nsACString& aType) {
3683 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
3684 true);
3687 static const char* gEventNames[] = {"event"};
3688 static const char* gSVGEventNames[] = {"evt"};
3689 // for b/w compat, the first name to onerror is still 'event', even though it
3690 // is actually the error message
3691 static const char* gOnErrorNames[] = {"event", "source", "lineno", "colno",
3692 "error"};
3694 // static
3695 void nsContentUtils::GetEventArgNames(int32_t aNameSpaceID, nsAtom* aEventName,
3696 bool aIsForWindow, uint32_t* aArgCount,
3697 const char*** aArgArray) {
3698 #define SET_EVENT_ARG_NAMES(names) \
3699 *aArgCount = sizeof(names) / sizeof(names[0]); \
3700 *aArgArray = names;
3702 // JSEventHandler is what does the arg magic for onerror, and it does
3703 // not seem to take the namespace into account. So we let onerror in all
3704 // namespaces get the 3 arg names.
3705 if (aEventName == nsGkAtoms::onerror && aIsForWindow) {
3706 SET_EVENT_ARG_NAMES(gOnErrorNames);
3707 } else if (aNameSpaceID == kNameSpaceID_SVG) {
3708 SET_EVENT_ARG_NAMES(gSVGEventNames);
3709 } else {
3710 SET_EVENT_ARG_NAMES(gEventNames);
3714 // Note: The list of content bundles in nsStringBundle.cpp should be updated
3715 // whenever entries are added or removed from this list.
3716 static const char* gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT] = {
3717 // Must line up with the enum values in |PropertiesFile| enum.
3718 "chrome://global/locale/css.properties",
3719 "chrome://global/locale/xul.properties",
3720 "chrome://global/locale/layout_errors.properties",
3721 "chrome://global/locale/layout/HtmlForm.properties",
3722 "chrome://global/locale/printing.properties",
3723 "chrome://global/locale/dom/dom.properties",
3724 "chrome://global/locale/layout/htmlparser.properties",
3725 "chrome://global/locale/svg/svg.properties",
3726 "chrome://branding/locale/brand.properties",
3727 "chrome://global/locale/commonDialogs.properties",
3728 "chrome://global/locale/mathml/mathml.properties",
3729 "chrome://global/locale/security/security.properties",
3730 "chrome://necko/locale/necko.properties",
3731 "resource://gre/res/locale/layout/HtmlForm.properties",
3732 "resource://gre/res/locale/dom/dom.properties"};
3734 /* static */
3735 nsresult nsContentUtils::EnsureStringBundle(PropertiesFile aFile) {
3736 if (!sStringBundles[aFile]) {
3737 if (!sStringBundleService) {
3738 nsresult rv =
3739 CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
3740 NS_ENSURE_SUCCESS(rv, rv);
3742 nsIStringBundle* bundle;
3743 nsresult rv =
3744 sStringBundleService->CreateBundle(gPropertiesFiles[aFile], &bundle);
3745 NS_ENSURE_SUCCESS(rv, rv);
3746 sStringBundles[aFile] = bundle; // transfer ownership
3748 return NS_OK;
3751 /* static */
3752 void nsContentUtils::AsyncPrecreateStringBundles() {
3753 // We only ever want to pre-create bundles in the parent process.
3755 // All nsContentUtils bundles are shared between the parent and child
3756 // precesses, and the shared memory regions that back them *must* be created
3757 // in the parent, and then sent to all children.
3759 // If we attempt to create a bundle in the child before its memory region is
3760 // available, we need to create a temporary non-shared bundle, and later
3761 // replace that with the shared memory copy. So attempting to pre-load in the
3762 // child is wasteful and unnecessary.
3763 MOZ_ASSERT(XRE_IsParentProcess());
3765 for (uint32_t bundleIndex = 0; bundleIndex < PropertiesFile_COUNT;
3766 ++bundleIndex) {
3767 nsresult rv = NS_DispatchToCurrentThreadQueue(
3768 NS_NewRunnableFunction("AsyncPrecreateStringBundles",
3769 [bundleIndex]() {
3770 PropertiesFile file =
3771 static_cast<PropertiesFile>(bundleIndex);
3772 EnsureStringBundle(file);
3773 nsIStringBundle* bundle = sStringBundles[file];
3774 bundle->AsyncPreload();
3776 EventQueuePriority::Idle);
3777 Unused << NS_WARN_IF(NS_FAILED(rv));
3781 /* static */
3782 bool nsContentUtils::SpoofLocaleEnglish() {
3783 // 0 - will prompt
3784 // 1 - don't spoof
3785 // 2 - spoof
3786 return StaticPrefs::privacy_spoof_english() == 2;
3789 static nsContentUtils::PropertiesFile GetMaybeSpoofedPropertiesFile(
3790 nsContentUtils::PropertiesFile aFile, const char* aKey,
3791 Document* aDocument) {
3792 // When we spoof English, use en-US properties in strings that are accessible
3793 // by content.
3794 bool spoofLocale = nsContentUtils::SpoofLocaleEnglish() &&
3795 (!aDocument || !aDocument->AllowsL10n());
3796 if (spoofLocale) {
3797 switch (aFile) {
3798 case nsContentUtils::eFORMS_PROPERTIES:
3799 return nsContentUtils::eFORMS_PROPERTIES_en_US;
3800 case nsContentUtils::eDOM_PROPERTIES:
3801 return nsContentUtils::eDOM_PROPERTIES_en_US;
3802 default:
3803 break;
3806 return aFile;
3809 /* static */
3810 nsresult nsContentUtils::GetMaybeLocalizedString(PropertiesFile aFile,
3811 const char* aKey,
3812 Document* aDocument,
3813 nsAString& aResult) {
3814 return GetLocalizedString(
3815 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aResult);
3818 /* static */
3819 nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
3820 const char* aKey,
3821 nsAString& aResult) {
3822 nsresult rv = EnsureStringBundle(aFile);
3823 NS_ENSURE_SUCCESS(rv, rv);
3824 nsIStringBundle* bundle = sStringBundles[aFile];
3825 return bundle->GetStringFromName(aKey, aResult);
3828 /* static */
3829 nsresult nsContentUtils::FormatMaybeLocalizedString(
3830 PropertiesFile aFile, const char* aKey, Document* aDocument,
3831 const nsTArray<nsString>& aParams, nsAString& aResult) {
3832 return FormatLocalizedString(
3833 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aParams,
3834 aResult);
3837 /* static */
3838 nsresult nsContentUtils::FormatLocalizedString(
3839 PropertiesFile aFile, const char* aKey, const nsTArray<nsString>& aParams,
3840 nsAString& aResult) {
3841 nsresult rv = EnsureStringBundle(aFile);
3842 NS_ENSURE_SUCCESS(rv, rv);
3843 nsIStringBundle* bundle = sStringBundles[aFile];
3845 if (aParams.IsEmpty()) {
3846 return bundle->GetStringFromName(aKey, aResult);
3849 return bundle->FormatStringFromName(aKey, aParams, aResult);
3852 /* static */
3853 void nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
3854 const char* aCategory,
3855 bool aFromPrivateWindow,
3856 bool aFromChromeContext,
3857 uint32_t aErrorFlags) {
3858 nsCOMPtr<nsIScriptError> scriptError =
3859 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
3860 if (scriptError) {
3861 nsCOMPtr<nsIConsoleService> console =
3862 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
3863 if (console && NS_SUCCEEDED(scriptError->Init(
3864 aErrorText, u""_ns, u""_ns, 0, 0, aErrorFlags, aCategory,
3865 aFromPrivateWindow, aFromChromeContext))) {
3866 console->LogMessage(scriptError);
3871 /* static */
3872 nsresult nsContentUtils::ReportToConsole(
3873 uint32_t aErrorFlags, const nsACString& aCategory,
3874 const Document* aDocument, PropertiesFile aFile, const char* aMessageName,
3875 const nsTArray<nsString>& aParams, nsIURI* aURI,
3876 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber) {
3877 nsresult rv;
3878 nsAutoString errorText;
3879 if (!aParams.IsEmpty()) {
3880 rv = FormatLocalizedString(aFile, aMessageName, aParams, errorText);
3881 } else {
3882 rv = GetLocalizedString(aFile, aMessageName, errorText);
3884 NS_ENSURE_SUCCESS(rv, rv);
3886 return ReportToConsoleNonLocalized(errorText, aErrorFlags, aCategory,
3887 aDocument, aURI, aSourceLine, aLineNumber,
3888 aColumnNumber);
3891 /* static */
3892 void nsContentUtils::ReportEmptyGetElementByIdArg(const Document* aDoc) {
3893 ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, aDoc,
3894 nsContentUtils::eDOM_PROPERTIES, "EmptyGetElementByIdParam");
3897 /* static */
3898 nsresult nsContentUtils::ReportToConsoleNonLocalized(
3899 const nsAString& aErrorText, uint32_t aErrorFlags,
3900 const nsACString& aCategory, const Document* aDocument, nsIURI* aURI,
3901 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
3902 MissingErrorLocationMode aLocationMode) {
3903 uint64_t innerWindowID = 0;
3904 if (aDocument) {
3905 if (!aURI) {
3906 aURI = aDocument->GetDocumentURI();
3908 innerWindowID = aDocument->InnerWindowID();
3911 return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
3912 innerWindowID, aURI, aSourceLine,
3913 aLineNumber, aColumnNumber, aLocationMode);
3916 /* static */
3917 nsresult nsContentUtils::ReportToConsoleByWindowID(
3918 const nsAString& aErrorText, uint32_t aErrorFlags,
3919 const nsACString& aCategory, uint64_t aInnerWindowID, nsIURI* aURI,
3920 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
3921 MissingErrorLocationMode aLocationMode) {
3922 nsresult rv;
3923 if (!sConsoleService) { // only need to bother null-checking here
3924 rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
3925 NS_ENSURE_SUCCESS(rv, rv);
3928 nsAutoString spec;
3929 if (!aLineNumber && aLocationMode == eUSE_CALLING_LOCATION) {
3930 JSContext* cx = GetCurrentJSContext();
3931 if (cx) {
3932 nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber, &aColumnNumber);
3936 nsCOMPtr<nsIScriptError> errorObject =
3937 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
3938 NS_ENSURE_SUCCESS(rv, rv);
3940 if (!spec.IsEmpty()) {
3941 rv = errorObject->InitWithWindowID(aErrorText,
3942 spec, // file name
3943 aSourceLine, aLineNumber, aColumnNumber,
3944 aErrorFlags, aCategory, aInnerWindowID);
3945 } else {
3946 rv = errorObject->InitWithSourceURI(aErrorText, aURI, aSourceLine,
3947 aLineNumber, aColumnNumber, aErrorFlags,
3948 aCategory, aInnerWindowID);
3950 NS_ENSURE_SUCCESS(rv, rv);
3952 return sConsoleService->LogMessage(errorObject);
3955 void nsContentUtils::LogMessageToConsole(const char* aMsg) {
3956 if (!sConsoleService) { // only need to bother null-checking here
3957 CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
3958 if (!sConsoleService) {
3959 return;
3962 sConsoleService->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
3965 bool nsContentUtils::IsChromeDoc(const Document* aDocument) {
3966 return aDocument && aDocument->NodePrincipal() == sSystemPrincipal;
3969 bool nsContentUtils::IsChildOfSameType(Document* aDoc) {
3970 if (BrowsingContext* bc = aDoc->GetBrowsingContext()) {
3971 return bc->GetParent();
3973 return false;
3976 bool nsContentUtils::IsPlainTextType(const nsACString& aContentType) {
3977 // NOTE: if you add a type here, add it to the CONTENTDLF_CATEGORIES
3978 // define in nsContentDLF.h as well.
3979 return aContentType.EqualsLiteral(TEXT_PLAIN) ||
3980 aContentType.EqualsLiteral(TEXT_CSS) ||
3981 aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
3982 aContentType.EqualsLiteral(TEXT_VTT) ||
3983 aContentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
3984 aContentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
3985 aContentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
3986 aContentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
3987 aContentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
3988 aContentType.EqualsLiteral(APPLICATION_JSON) ||
3989 aContentType.EqualsLiteral(TEXT_JSON);
3992 bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) {
3993 // NOTE: This must be a subset of the list in IsPlainTextType().
3994 return aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
3995 aContentType.EqualsLiteral(APPLICATION_JSON) ||
3996 aContentType.EqualsLiteral(TEXT_JSON) ||
3997 aContentType.EqualsLiteral(TEXT_VTT);
4000 bool nsContentUtils::IsInChromeDocshell(const Document* aDocument) {
4001 return aDocument && aDocument->IsInChromeDocShell();
4004 // static
4005 nsIContentPolicy* nsContentUtils::GetContentPolicy() {
4006 if (!sTriedToGetContentPolicy) {
4007 CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
4008 // It's OK to not have a content policy service
4009 sTriedToGetContentPolicy = true;
4012 return sContentPolicyService;
4015 // static
4016 bool nsContentUtils::IsEventAttributeName(nsAtom* aName, int32_t aType) {
4017 const char16_t* name = aName->GetUTF16String();
4018 if (name[0] != 'o' || name[1] != 'n') {
4019 return false;
4022 EventNameMapping mapping;
4023 return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
4026 // static
4027 EventMessage nsContentUtils::GetEventMessage(nsAtom* aName) {
4028 MOZ_ASSERT(NS_IsMainThread(), "sAtomEventTable is not threadsafe");
4029 if (aName) {
4030 EventNameMapping mapping;
4031 if (sAtomEventTable->Get(aName, &mapping)) {
4032 return mapping.mMessage;
4036 return eUnidentifiedEvent;
4039 // static
4040 mozilla::EventClassID nsContentUtils::GetEventClassID(const nsAString& aName) {
4041 EventNameMapping mapping;
4042 if (sStringEventTable->Get(aName, &mapping)) return mapping.mEventClassID;
4044 return eBasicEventClass;
4047 nsAtom* nsContentUtils::GetEventMessageAndAtom(
4048 const nsAString& aName, mozilla::EventClassID aEventClassID,
4049 EventMessage* aEventMessage) {
4050 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4051 EventNameMapping mapping;
4052 if (sStringEventTable->Get(aName, &mapping)) {
4053 *aEventMessage = mapping.mEventClassID == aEventClassID
4054 ? mapping.mMessage
4055 : eUnidentifiedEvent;
4056 return mapping.mAtom;
4059 // If we have cached lots of user defined event names, clear some of them.
4060 if (sUserDefinedEvents->Length() > 127) {
4061 while (sUserDefinedEvents->Length() > 64) {
4062 nsAtom* first = sUserDefinedEvents->ElementAt(0);
4063 sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
4064 sUserDefinedEvents->RemoveElementAt(0);
4068 *aEventMessage = eUnidentifiedEvent;
4069 RefPtr<nsAtom> atom = NS_AtomizeMainThread(u"on"_ns + aName);
4070 sUserDefinedEvents->AppendElement(atom);
4071 mapping.mAtom = atom;
4072 mapping.mMessage = eUnidentifiedEvent;
4073 mapping.mType = EventNameType_None;
4074 mapping.mEventClassID = eBasicEventClass;
4075 // This is a slow hashtable call, but at least we cache the result for the
4076 // following calls. Because GetEventMessageAndAtomForListener utilizes
4077 // sStringEventTable, it needs to know in which cases sStringEventTable
4078 // doesn't contain the information it needs so that it can use
4079 // sAtomEventTable instead.
4080 mapping.mMaybeSpecialSVGorSMILEvent =
4081 GetEventMessage(atom) != eUnidentifiedEvent;
4082 sStringEventTable->InsertOrUpdate(aName, mapping);
4083 return mapping.mAtom;
4086 // static
4087 EventMessage nsContentUtils::GetEventMessageAndAtomForListener(
4088 const nsAString& aName, nsAtom** aOnName) {
4089 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4091 // Because of SVG/SMIL sStringEventTable contains a subset of the event names
4092 // comparing to the sAtomEventTable. However, usually sStringEventTable
4093 // contains the information we need, so in order to reduce hashtable
4094 // lookups, start from it.
4095 EventNameMapping mapping;
4096 EventMessage msg = eUnidentifiedEvent;
4097 RefPtr<nsAtom> atom;
4098 if (sStringEventTable->Get(aName, &mapping)) {
4099 if (mapping.mMaybeSpecialSVGorSMILEvent) {
4100 // Try the atom version so that we should get the right message for
4101 // SVG/SMIL.
4102 atom = NS_AtomizeMainThread(u"on"_ns + aName);
4103 msg = GetEventMessage(atom);
4104 } else {
4105 atom = mapping.mAtom;
4106 msg = mapping.mMessage;
4108 atom.forget(aOnName);
4109 return msg;
4112 // GetEventMessageAndAtom will cache the event type for the future usage...
4113 GetEventMessageAndAtom(aName, eBasicEventClass, &msg);
4115 // ...and then call this method recursively to get the message and atom from
4116 // now updated sStringEventTable.
4117 return GetEventMessageAndAtomForListener(aName, aOnName);
4120 static nsresult GetEventAndTarget(Document* aDoc, nsISupports* aTarget,
4121 const nsAString& aEventName,
4122 CanBubble aCanBubble, Cancelable aCancelable,
4123 Composed aComposed, Trusted aTrusted,
4124 Event** aEvent, EventTarget** aTargetOut) {
4125 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4126 NS_ENSURE_TRUE(aDoc && target, NS_ERROR_INVALID_ARG);
4128 ErrorResult err;
4129 RefPtr<Event> event =
4130 aDoc->CreateEvent(u"Events"_ns, CallerType::System, err);
4131 if (NS_WARN_IF(err.Failed())) {
4132 return err.StealNSResult();
4135 event->InitEvent(aEventName, aCanBubble, aCancelable, aComposed);
4136 event->SetTrusted(aTrusted == Trusted::eYes);
4138 event->SetTarget(target);
4140 event.forget(aEvent);
4141 target.forget(aTargetOut);
4142 return NS_OK;
4145 // static
4146 nsresult nsContentUtils::DispatchTrustedEvent(
4147 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4148 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4149 bool* aDefaultAction) {
4150 MOZ_ASSERT(!aEventName.EqualsLiteral("input") &&
4151 !aEventName.EqualsLiteral("beforeinput"),
4152 "Use DispatchInputEvent() instead");
4153 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4154 aComposed, Trusted::eYes, aDefaultAction);
4157 // static
4158 nsresult nsContentUtils::DispatchUntrustedEvent(
4159 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4160 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4161 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4162 Composed::eDefault, Trusted::eNo, aDefaultAction);
4165 // static
4166 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4167 const nsAString& aEventName,
4168 CanBubble aCanBubble,
4169 Cancelable aCancelable,
4170 Composed aComposed, Trusted aTrusted,
4171 bool* aDefaultAction,
4172 ChromeOnlyDispatch aOnlyChromeDispatch) {
4173 RefPtr<Event> event;
4174 nsCOMPtr<EventTarget> target;
4175 nsresult rv = GetEventAndTarget(
4176 aDoc, aTarget, aEventName, aCanBubble, aCancelable, aComposed, aTrusted,
4177 getter_AddRefs(event), getter_AddRefs(target));
4178 NS_ENSURE_SUCCESS(rv, rv);
4179 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch =
4180 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4182 ErrorResult err;
4183 bool doDefault = target->DispatchEvent(*event, CallerType::System, err);
4184 if (aDefaultAction) {
4185 *aDefaultAction = doDefault;
4187 return err.StealNSResult();
4190 // static
4191 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4192 WidgetEvent& aEvent,
4193 EventMessage aEventMessage,
4194 CanBubble aCanBubble,
4195 Cancelable aCancelable, Trusted aTrusted,
4196 bool* aDefaultAction,
4197 ChromeOnlyDispatch aOnlyChromeDispatch) {
4198 MOZ_ASSERT_IF(aOnlyChromeDispatch == ChromeOnlyDispatch::eYes,
4199 aTrusted == Trusted::eYes);
4201 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4203 aEvent.mTime = PR_Now();
4205 aEvent.mSpecifiedEventType = GetEventTypeFromMessage(aEventMessage);
4206 aEvent.SetDefaultComposed();
4207 aEvent.SetDefaultComposedInNativeAnonymousContent();
4209 aEvent.mFlags.mBubbles = aCanBubble == CanBubble::eYes;
4210 aEvent.mFlags.mCancelable = aCancelable == Cancelable::eYes;
4211 aEvent.mFlags.mOnlyChromeDispatch =
4212 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4214 aEvent.mTarget = target;
4216 nsEventStatus status = nsEventStatus_eIgnore;
4217 nsresult rv = EventDispatcher::DispatchDOMEvent(target, &aEvent, nullptr,
4218 nullptr, &status);
4219 if (aDefaultAction) {
4220 *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
4222 return rv;
4225 // static
4226 nsresult nsContentUtils::DispatchInputEvent(Element* aEventTarget) {
4227 return DispatchInputEvent(aEventTarget, mozilla::eEditorInput,
4228 mozilla::EditorInputType::eUnknown, nullptr,
4229 InputEventOptions());
4232 // static
4233 nsresult nsContentUtils::DispatchInputEvent(
4234 Element* aEventTargetElement, EventMessage aEventMessage,
4235 EditorInputType aEditorInputType, TextEditor* aTextEditor,
4236 InputEventOptions&& aOptions, nsEventStatus* aEventStatus /* = nullptr */) {
4237 MOZ_ASSERT(aEventMessage == eEditorInput ||
4238 aEventMessage == eEditorBeforeInput);
4240 if (NS_WARN_IF(!aEventTargetElement)) {
4241 return NS_ERROR_INVALID_ARG;
4244 // If this is called from editor, the instance should be set to aTextEditor.
4245 // Otherwise, we need to look for an editor for aEventTargetElement.
4246 // However, we don't need to do it for HTMLEditor since nobody shouldn't
4247 // dispatch "beforeinput" nor "input" event for HTMLEditor except HTMLEditor
4248 // itself.
4249 bool useInputEvent = false;
4250 if (aTextEditor) {
4251 useInputEvent = true;
4252 } else if (HTMLTextAreaElement* textAreaElement =
4253 HTMLTextAreaElement::FromNode(aEventTargetElement)) {
4254 aTextEditor = textAreaElement->GetTextEditorWithoutCreation();
4255 useInputEvent = true;
4256 } else if (HTMLInputElement* inputElement =
4257 HTMLInputElement::FromNode(aEventTargetElement)) {
4258 if (inputElement->IsInputEventTarget()) {
4259 aTextEditor = inputElement->GetTextEditorWithoutCreation();
4260 useInputEvent = true;
4263 #ifdef DEBUG
4264 else {
4265 MOZ_ASSERT(!aEventTargetElement->IsTextControlElement(),
4266 "The event target may have editor, but we've not known it yet.");
4268 #endif // #ifdef DEBUG
4270 if (!useInputEvent) {
4271 MOZ_ASSERT(aEventMessage == eEditorInput);
4272 MOZ_ASSERT(aEditorInputType == EditorInputType::eUnknown);
4273 MOZ_ASSERT(!aOptions.mNeverCancelable);
4274 // Dispatch "input" event with Event instance.
4275 WidgetEvent widgetEvent(true, eUnidentifiedEvent);
4276 widgetEvent.mSpecifiedEventType = nsGkAtoms::oninput;
4277 widgetEvent.mFlags.mCancelable = false;
4278 widgetEvent.mFlags.mComposed = true;
4279 // Using same time as nsContentUtils::DispatchEvent() for backward
4280 // compatibility.
4281 widgetEvent.mTime = PR_Now();
4282 (new AsyncEventDispatcher(aEventTargetElement, widgetEvent))
4283 ->RunDOMEventWhenSafe();
4284 return NS_OK;
4287 MOZ_ASSERT_IF(aEventMessage != eEditorBeforeInput,
4288 !aOptions.mNeverCancelable);
4289 MOZ_ASSERT_IF(
4290 aEventMessage == eEditorBeforeInput && aOptions.mNeverCancelable,
4291 aEditorInputType == EditorInputType::eInsertReplacementText);
4293 nsCOMPtr<nsIWidget> widget;
4294 if (aTextEditor) {
4295 widget = aTextEditor->GetWidget();
4296 if (NS_WARN_IF(!widget)) {
4297 return NS_ERROR_FAILURE;
4299 } else {
4300 Document* document = aEventTargetElement->OwnerDoc();
4301 if (NS_WARN_IF(!document)) {
4302 return NS_ERROR_FAILURE;
4304 // If we're running xpcshell tests, we fail to get presShell here.
4305 // Even in such case, we need to dispatch "input" event without widget.
4306 PresShell* presShell = document->GetPresShell();
4307 if (presShell) {
4308 nsPresContext* presContext = presShell->GetPresContext();
4309 if (NS_WARN_IF(!presContext)) {
4310 return NS_ERROR_FAILURE;
4312 widget = presContext->GetRootWidget();
4313 if (NS_WARN_IF(!widget)) {
4314 return NS_ERROR_FAILURE;
4319 // Dispatch "input" event with InputEvent instance.
4320 InternalEditorInputEvent inputEvent(true, aEventMessage, widget);
4322 inputEvent.mFlags.mCancelable =
4323 !aOptions.mNeverCancelable && aEventMessage == eEditorBeforeInput &&
4324 IsCancelableBeforeInputEvent(aEditorInputType);
4325 MOZ_ASSERT(!inputEvent.mFlags.mCancelable || aEventStatus);
4327 // Using same time as old event dispatcher in EditorBase for backward
4328 // compatibility.
4329 inputEvent.mTime = static_cast<uint64_t>(PR_Now() / 1000);
4331 // If there is an editor, set isComposing to true when it has composition.
4332 // Note that EditorBase::IsIMEComposing() may return false even when we
4333 // need to set it to true.
4334 // Otherwise, i.e., editor hasn't been created for the element yet,
4335 // we should set isComposing to false since the element can never has
4336 // composition without editor.
4337 inputEvent.mIsComposing = aTextEditor && aTextEditor->GetComposition();
4339 if (!aTextEditor || !aTextEditor->AsHTMLEditor()) {
4340 if (IsDataAvailableOnTextEditor(aEditorInputType)) {
4341 inputEvent.mData = std::move(aOptions.mData);
4342 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4343 "inputEvent.mData shouldn't be void");
4345 #ifdef DEBUG
4346 else {
4347 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4349 #endif // #ifdef DEBUG
4350 MOZ_ASSERT(
4351 aOptions.mTargetRanges.IsEmpty(),
4352 "Target ranges for <input> and <textarea> should always be empty");
4353 } else {
4354 MOZ_ASSERT(aTextEditor->AsHTMLEditor());
4355 if (IsDataAvailableOnHTMLEditor(aEditorInputType)) {
4356 inputEvent.mData = std::move(aOptions.mData);
4357 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4358 "inputEvent.mData shouldn't be void");
4359 } else {
4360 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4361 if (IsDataTransferAvailableOnHTMLEditor(aEditorInputType)) {
4362 inputEvent.mDataTransfer = std::move(aOptions.mDataTransfer);
4363 MOZ_ASSERT(inputEvent.mDataTransfer,
4364 "inputEvent.mDataTransfer shouldn't be nullptr");
4365 MOZ_ASSERT(inputEvent.mDataTransfer->IsReadOnly(),
4366 "inputEvent.mDataTransfer should be read only");
4368 #ifdef DEBUG
4369 else {
4370 MOZ_ASSERT(!inputEvent.mDataTransfer,
4371 "inputEvent.mDataTransfer should be nullptr");
4373 #endif // #ifdef DEBUG
4375 if (aEventMessage == eEditorBeforeInput &&
4376 MayHaveTargetRangesOnHTMLEditor(aEditorInputType)) {
4377 inputEvent.mTargetRanges = std::move(aOptions.mTargetRanges);
4379 #ifdef DEBUG
4380 else {
4381 MOZ_ASSERT(aOptions.mTargetRanges.IsEmpty(),
4382 "Target ranges shouldn't be set for the dispatching event");
4384 #endif // #ifdef DEBUG
4387 inputEvent.mInputType = aEditorInputType;
4389 if (!IsSafeToRunScript()) {
4390 // If we cannot dispatch an event right now, we cannot make it cancelable.
4391 NS_ASSERTION(
4392 !inputEvent.mFlags.mCancelable,
4393 "Cancelable beforeinput event dispatcher should run when it's safe");
4394 inputEvent.mFlags.mCancelable = false;
4395 (new AsyncEventDispatcher(aEventTargetElement, inputEvent))
4396 ->RunDOMEventWhenSafe();
4397 return NS_OK;
4400 // If we're running xpcshell tests, we fail to get presShell here.
4401 // Even in such case, we need to dispatch "input" event without widget.
4402 RefPtr<nsPresContext> presContext =
4403 aEventTargetElement->OwnerDoc()->GetPresContext();
4404 nsresult rv = EventDispatcher::Dispatch(aEventTargetElement, presContext,
4405 &inputEvent, nullptr, aEventStatus);
4406 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
4407 "Dispatching `beforeinput` or `input` event failed");
4408 return rv;
4411 nsresult nsContentUtils::DispatchChromeEvent(
4412 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4413 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4414 RefPtr<Event> event;
4415 nsCOMPtr<EventTarget> target;
4416 nsresult rv = GetEventAndTarget(
4417 aDoc, aTarget, aEventName, aCanBubble, aCancelable, Composed::eDefault,
4418 Trusted::eYes, getter_AddRefs(event), getter_AddRefs(target));
4419 NS_ENSURE_SUCCESS(rv, rv);
4421 NS_ASSERTION(aDoc, "GetEventAndTarget lied?");
4422 if (!aDoc->GetWindow()) return NS_ERROR_INVALID_ARG;
4424 EventTarget* piTarget = aDoc->GetWindow()->GetParentTarget();
4425 if (!piTarget) return NS_ERROR_INVALID_ARG;
4427 ErrorResult err;
4428 bool defaultActionEnabled =
4429 piTarget->DispatchEvent(*event, CallerType::System, err);
4430 if (aDefaultAction) {
4431 *aDefaultAction = defaultActionEnabled;
4433 return err.StealNSResult();
4436 void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise,
4437 CallerType aCallerType) {
4438 RefPtr<Element> target = &aFrameElement;
4439 bool defaultAction = true;
4440 if (aCanRaise) {
4441 DispatchEventOnlyToChrome(target->OwnerDoc(), target,
4442 u"framefocusrequested"_ns, CanBubble::eYes,
4443 Cancelable::eYes, &defaultAction);
4445 if (!defaultAction) {
4446 return;
4449 RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
4450 if (!fm) {
4451 return;
4454 uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
4455 if (aCanRaise) {
4456 flags |= nsIFocusManager::FLAG_RAISE;
4459 if (aCallerType == CallerType::NonSystem) {
4460 flags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
4463 fm->SetFocus(target, flags);
4466 nsresult nsContentUtils::DispatchEventOnlyToChrome(
4467 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4468 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4469 bool* aDefaultAction) {
4470 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4471 aComposed, Trusted::eYes, aDefaultAction,
4472 ChromeOnlyDispatch::eYes);
4475 /* static */
4476 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4477 const nsAtom* aId) {
4478 for (nsIContent* cur = aContent; cur; cur = cur->GetNextNode(aContent)) {
4479 if (aId == cur->GetID()) {
4480 return cur->AsElement();
4484 return nullptr;
4487 /* static */
4488 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4489 const nsAString& aId) {
4490 MOZ_ASSERT(!aId.IsEmpty(), "Will match random elements");
4492 // ID attrs are generally stored as atoms, so just atomize this up front
4493 RefPtr<nsAtom> id(NS_Atomize(aId));
4494 if (!id) {
4495 // OOM, so just bail
4496 return nullptr;
4499 return MatchElementId(aContent, id);
4502 /* static */
4503 void nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver) {
4504 nsCOMPtr<nsIObserverService> observerService =
4505 mozilla::services::GetObserverService();
4506 if (observerService) {
4507 observerService->AddObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
4508 false);
4512 /* static */
4513 void nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver) {
4514 nsCOMPtr<nsIObserverService> observerService =
4515 mozilla::services::GetObserverService();
4516 if (observerService) {
4517 observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
4521 /* static */
4522 bool nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent,
4523 int32_t aNameSpaceID, nsAtom* aName) {
4524 static Element::AttrValuesArray strings[] = {nsGkAtoms::_empty, nullptr};
4525 return aContent->IsElement() &&
4526 aContent->AsElement()->FindAttrValueIn(aNameSpaceID, aName, strings,
4527 eCaseMatters) ==
4528 Element::ATTR_VALUE_NO_MATCH;
4531 /* static */
4532 bool nsContentUtils::HasMutationListeners(nsINode* aNode, uint32_t aType,
4533 nsINode* aTargetForSubtreeModified) {
4534 Document* doc = aNode->OwnerDoc();
4536 // global object will be null for documents that don't have windows.
4537 nsPIDOMWindowInner* window = doc->GetInnerWindow();
4538 // This relies on EventListenerManager::AddEventListener, which sets
4539 // all mutation bits when there is a listener for DOMSubtreeModified event.
4540 if (window && !window->HasMutationListeners(aType)) {
4541 return false;
4544 if (aNode->ChromeOnlyAccess() || aNode->IsInShadowTree()) {
4545 return false;
4548 doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
4550 // If we have a window, we can check it for mutation listeners now.
4551 if (aNode->IsInUncomposedDoc()) {
4552 nsCOMPtr<EventTarget> piTarget(do_QueryInterface(window));
4553 if (piTarget) {
4554 EventListenerManager* manager = piTarget->GetExistingListenerManager();
4555 if (manager && manager->HasMutationListeners()) {
4556 return true;
4561 // If we have a window, we know a mutation listener is registered, but it
4562 // might not be in our chain. If we don't have a window, we might have a
4563 // mutation listener. Check quickly to see.
4564 while (aNode) {
4565 EventListenerManager* manager = aNode->GetExistingListenerManager();
4566 if (manager && manager->HasMutationListeners()) {
4567 return true;
4570 aNode = aNode->GetParentNode();
4573 return false;
4576 /* static */
4577 bool nsContentUtils::HasMutationListeners(Document* aDocument, uint32_t aType) {
4578 nsPIDOMWindowInner* window =
4579 aDocument ? aDocument->GetInnerWindow() : nullptr;
4581 // This relies on EventListenerManager::AddEventListener, which sets
4582 // all mutation bits when there is a listener for DOMSubtreeModified event.
4583 return !window || window->HasMutationListeners(aType);
4586 void nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent) {
4587 MOZ_ASSERT(aChild, "Missing child");
4588 MOZ_ASSERT(aChild->GetParentNode() == aParent, "Wrong parent");
4589 MOZ_ASSERT(aChild->OwnerDoc() == aParent->OwnerDoc(), "Wrong owner-doc");
4591 // Having an explicit check here since it's an easy mistake to fall into,
4592 // and there might be existing code with problems. We'd rather be safe
4593 // than fire DOMNodeRemoved in all corner cases. We also rely on it for
4594 // nsAutoScriptBlockerSuppressNodeRemoved.
4595 if (!IsSafeToRunScript()) {
4596 // This checks that IsSafeToRunScript is true since we don't want to fire
4597 // events when that is false. We can't rely on EventDispatcher to assert
4598 // this in this situation since most of the time there are no mutation
4599 // event listeners, in which case we won't even attempt to dispatch events.
4600 // However this also allows for two exceptions. First off, we don't assert
4601 // if the mutation happens to native anonymous content since we never fire
4602 // mutation events on such content anyway.
4603 // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
4604 // that is a know case when we'd normally fire a mutation event, but can't
4605 // make that safe and so we suppress it at this time. Ideally this should
4606 // go away eventually.
4607 if (!aChild->IsInNativeAnonymousSubtree() &&
4608 !sDOMNodeRemovedSuppressCount) {
4609 NS_ERROR("Want to fire DOMNodeRemoved event, but it's not safe");
4610 WarnScriptWasIgnored(aChild->OwnerDoc());
4612 return;
4616 Document* doc = aParent->OwnerDoc();
4617 if (MOZ_UNLIKELY(doc->DevToolsWatchingDOMMutations()) &&
4618 aChild->IsInComposedDoc() && !aChild->ChromeOnlyAccess()) {
4619 DispatchChromeEvent(doc, aChild, u"devtoolschildremoved"_ns,
4620 CanBubble::eNo, Cancelable::eNo);
4624 if (HasMutationListeners(aChild, NS_EVENT_BITS_MUTATION_NODEREMOVED,
4625 aParent)) {
4626 InternalMutationEvent mutation(true, eLegacyNodeRemoved);
4627 mutation.mRelatedNode = aParent;
4629 mozAutoSubtreeModified subtree(aParent->OwnerDoc(), aParent);
4630 EventDispatcher::Dispatch(aChild, nullptr, &mutation);
4634 void nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments() {
4635 if (!sEventListenerManagersHash) {
4636 return;
4639 for (auto i = sEventListenerManagersHash->Iter(); !i.Done(); i.Next()) {
4640 auto entry = static_cast<EventListenerManagerMapEntry*>(i.Get());
4641 nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
4642 if (n && n->IsInComposedDoc() &&
4643 nsCCUncollectableMarker::InGeneration(
4644 n->OwnerDoc()->GetMarkedCCGeneration())) {
4645 entry->mListenerManager->MarkForCC();
4650 /* static */
4651 void nsContentUtils::TraverseListenerManager(
4652 nsINode* aNode, nsCycleCollectionTraversalCallback& cb) {
4653 if (!sEventListenerManagersHash) {
4654 // We're already shut down, just return.
4655 return;
4658 auto entry = static_cast<EventListenerManagerMapEntry*>(
4659 sEventListenerManagersHash->Search(aNode));
4660 if (entry) {
4661 CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
4662 "[via hash] mListenerManager");
4666 EventListenerManager* nsContentUtils::GetListenerManagerForNode(
4667 nsINode* aNode) {
4668 if (!sEventListenerManagersHash) {
4669 // We're already shut down, don't bother creating an event listener
4670 // manager.
4672 return nullptr;
4675 auto entry = static_cast<EventListenerManagerMapEntry*>(
4676 sEventListenerManagersHash->Add(aNode, fallible));
4678 if (!entry) {
4679 return nullptr;
4682 if (!entry->mListenerManager) {
4683 entry->mListenerManager = new EventListenerManager(aNode);
4685 aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
4688 return entry->mListenerManager;
4691 EventListenerManager* nsContentUtils::GetExistingListenerManagerForNode(
4692 const nsINode* aNode) {
4693 if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
4694 return nullptr;
4697 if (!sEventListenerManagersHash) {
4698 // We're already shut down, don't bother creating an event listener
4699 // manager.
4701 return nullptr;
4704 auto entry = static_cast<EventListenerManagerMapEntry*>(
4705 sEventListenerManagersHash->Search(aNode));
4706 if (entry) {
4707 return entry->mListenerManager;
4710 return nullptr;
4713 void nsContentUtils::AddEntryToDOMArenaTable(nsINode* aNode,
4714 DOMArena* aDOMArena) {
4715 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
4716 MOZ_ASSERT_IF(sDOMArenaHashtable, !sDOMArenaHashtable->Contains(aNode));
4717 MOZ_ASSERT(!aNode->HasFlag(NODE_KEEPS_DOMARENA));
4718 if (!sDOMArenaHashtable) {
4719 sDOMArenaHashtable =
4720 new nsRefPtrHashtable<nsPtrHashKey<const nsINode>, dom::DOMArena>();
4722 aNode->SetFlags(NODE_KEEPS_DOMARENA);
4723 sDOMArenaHashtable->InsertOrUpdate(aNode, RefPtr<DOMArena>(aDOMArena));
4726 already_AddRefed<DOMArena> nsContentUtils::TakeEntryFromDOMArenaTable(
4727 const nsINode* aNode) {
4728 MOZ_ASSERT(sDOMArenaHashtable->Contains(aNode));
4729 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
4730 RefPtr<DOMArena> arena;
4731 sDOMArenaHashtable->Remove(aNode, getter_AddRefs(arena));
4732 return arena.forget();
4735 /* static */
4736 void nsContentUtils::RemoveListenerManager(nsINode* aNode) {
4737 if (sEventListenerManagersHash) {
4738 auto entry = static_cast<EventListenerManagerMapEntry*>(
4739 sEventListenerManagersHash->Search(aNode));
4740 if (entry) {
4741 RefPtr<EventListenerManager> listenerManager;
4742 listenerManager.swap(entry->mListenerManager);
4743 // Remove the entry and *then* do operations that could cause further
4744 // modification of sEventListenerManagersHash. See bug 334177.
4745 sEventListenerManagersHash->RawRemove(entry);
4746 if (listenerManager) {
4747 listenerManager->Disconnect();
4753 /* static */
4754 bool nsContentUtils::IsValidNodeName(nsAtom* aLocalName, nsAtom* aPrefix,
4755 int32_t aNamespaceID) {
4756 if (aNamespaceID == kNameSpaceID_Unknown) {
4757 return false;
4760 if (!aPrefix) {
4761 // If the prefix is null, then either the QName must be xmlns or the
4762 // namespace must not be XMLNS.
4763 return (aLocalName == nsGkAtoms::xmlns) ==
4764 (aNamespaceID == kNameSpaceID_XMLNS);
4767 // If the prefix is non-null then the namespace must not be null.
4768 if (aNamespaceID == kNameSpaceID_None) {
4769 return false;
4772 // If the namespace is the XMLNS namespace then the prefix must be xmlns,
4773 // but the localname must not be xmlns.
4774 if (aNamespaceID == kNameSpaceID_XMLNS) {
4775 return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
4778 // If the namespace is not the XMLNS namespace then the prefix must not be
4779 // xmlns.
4780 // If the namespace is the XML namespace then the prefix can be anything.
4781 // If the namespace is not the XML namespace then the prefix must not be xml.
4782 return aPrefix != nsGkAtoms::xmlns &&
4783 (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
4786 already_AddRefed<DocumentFragment> nsContentUtils::CreateContextualFragment(
4787 nsINode* aContextNode, const nsAString& aFragment,
4788 bool aPreventScriptExecution, ErrorResult& aRv) {
4789 if (!aContextNode) {
4790 aRv.Throw(NS_ERROR_INVALID_ARG);
4791 return nullptr;
4794 // If we don't have a document here, we can't get the right security context
4795 // for compiling event handlers... so just bail out.
4796 RefPtr<Document> document = aContextNode->OwnerDoc();
4797 bool isHTML = document->IsHTMLDocument();
4799 if (isHTML) {
4800 RefPtr<DocumentFragment> frag = new (document->NodeInfoManager())
4801 DocumentFragment(document->NodeInfoManager());
4803 Element* element = aContextNode->GetAsElementOrParentElement();
4804 if (element && !element->IsHTMLElement(nsGkAtoms::html)) {
4805 aRv = ParseFragmentHTML(
4806 aFragment, frag, element->NodeInfo()->NameAtom(),
4807 element->GetNameSpaceID(),
4808 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
4809 aPreventScriptExecution);
4810 } else {
4811 aRv = ParseFragmentHTML(
4812 aFragment, frag, nsGkAtoms::body, kNameSpaceID_XHTML,
4813 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
4814 aPreventScriptExecution);
4817 return frag.forget();
4820 AutoTArray<nsString, 32> tagStack;
4821 nsAutoString uriStr, nameStr;
4822 for (Element* element : aContextNode->InclusiveAncestorsOfType<Element>()) {
4823 nsString& tagName = *tagStack.AppendElement();
4824 // It mostly doesn't actually matter what tag name we use here: XML doesn't
4825 // have parsing that depends on the open tag stack, apart from namespace
4826 // declarations. So this whole tagStack bit is just there to get the right
4827 // namespace declarations to the XML parser. That said, the parser _is_
4828 // going to create elements with the tag names we provide here, so we need
4829 // to make sure they are not names that can trigger custom element
4830 // constructors. Just make up a name that is never going to be a valid
4831 // custom element name.
4833 // The principled way to do this would probably be to add a new FromParser
4834 // value and make sure we use it when creating the context elements, then
4835 // make sure we teach all FromParser consumers (and in particular the custom
4836 // element code) about it as needed. But right now the XML parser never
4837 // actually uses FromParser values other than NOT_FROM_PARSER, and changing
4838 // that is pretty complicated.
4839 tagName.AssignLiteral("notacustomelement");
4841 // see if we need to add xmlns declarations
4842 uint32_t count = element->GetAttrCount();
4843 bool setDefaultNamespace = false;
4844 if (count > 0) {
4845 uint32_t index;
4847 for (index = 0; index < count; index++) {
4848 const BorrowedAttrInfo info = element->GetAttrInfoAt(index);
4849 const nsAttrName* name = info.mName;
4850 if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
4851 info.mValue->ToString(uriStr);
4853 // really want something like nsXMLContentSerializer::SerializeAttr
4854 tagName.AppendLiteral(" xmlns"); // space important
4855 if (name->GetPrefix()) {
4856 tagName.Append(char16_t(':'));
4857 name->LocalName()->ToString(nameStr);
4858 tagName.Append(nameStr);
4859 } else {
4860 setDefaultNamespace = true;
4862 tagName.AppendLiteral(R"(=")");
4863 tagName.Append(uriStr);
4864 tagName.Append('"');
4869 if (!setDefaultNamespace) {
4870 mozilla::dom::NodeInfo* info = element->NodeInfo();
4871 if (!info->GetPrefixAtom() && info->NamespaceID() != kNameSpaceID_None) {
4872 // We have no namespace prefix, but have a namespace ID. Push
4873 // default namespace attr in, so that our kids will be in our
4874 // namespace.
4875 info->GetNamespaceURI(uriStr);
4876 tagName.AppendLiteral(R"( xmlns=")");
4877 tagName.Append(uriStr);
4878 tagName.Append('"');
4883 RefPtr<DocumentFragment> frag;
4884 aRv = ParseFragmentXML(aFragment, document, tagStack, aPreventScriptExecution,
4885 -1, getter_AddRefs(frag));
4886 return frag.forget();
4889 /* static */
4890 void nsContentUtils::DropFragmentParsers() {
4891 NS_IF_RELEASE(sHTMLFragmentParser);
4892 NS_IF_RELEASE(sXMLFragmentParser);
4893 NS_IF_RELEASE(sXMLFragmentSink);
4896 /* static */
4897 void nsContentUtils::XPCOMShutdown() { nsContentUtils::DropFragmentParsers(); }
4899 /* Helper function to compuate Sanitization Flags for ParseFramentHTML/XML */
4900 uint32_t computeSanitizationFlags(nsIPrincipal* aPrincipal, int32_t aFlags) {
4901 uint32_t sanitizationFlags = 0;
4902 if (aPrincipal->IsSystemPrincipal()) {
4903 if (aFlags < 0) {
4904 // if this is a chrome-privileged document and no explicit flags
4905 // were passed, then use this sanitization flags.
4906 sanitizationFlags = nsIParserUtils::SanitizerAllowStyle |
4907 nsIParserUtils::SanitizerAllowComments |
4908 nsIParserUtils::SanitizerDropForms |
4909 nsIParserUtils::SanitizerLogRemovals;
4910 } else {
4911 // if the caller explicitly passes flags, then we use those
4912 // flags but additionally drop forms.
4913 sanitizationFlags = aFlags | nsIParserUtils::SanitizerDropForms;
4915 } else if (aFlags >= 0) {
4916 // aFlags by default is -1 and is only ever non equal to -1 if the
4917 // caller of ParseFragmentHTML/ParseFragmentXML is
4918 // ParserUtils::ParseFragment(). Only in that case we should use
4919 // the sanitization flags passed within aFlags.
4920 sanitizationFlags = aFlags;
4922 return sanitizationFlags;
4925 /* static */
4926 bool AllowsUnsanitizedContentForAboutNewTab(nsIPrincipal* aPrincipal) {
4927 if (StaticPrefs::dom_about_newtab_sanitization_enabled() ||
4928 !aPrincipal->SchemeIs("about")) {
4929 return false;
4931 uint32_t aboutModuleFlags = 0;
4932 aPrincipal->GetAboutModuleFlags(&aboutModuleFlags);
4933 return aboutModuleFlags & nsIAboutModule::ALLOW_UNSANITIZED_CONTENT;
4936 /* static */
4937 nsresult nsContentUtils::ParseFragmentHTML(
4938 const nsAString& aSourceBuffer, nsIContent* aTargetNode,
4939 nsAtom* aContextLocalName, int32_t aContextNamespace, bool aQuirks,
4940 bool aPreventScriptExecution, int32_t aFlags) {
4941 AutoTimelineMarker m(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");
4943 if (nsContentUtils::sFragmentParsingActive) {
4944 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
4945 return NS_ERROR_DOM_INVALID_STATE_ERR;
4947 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
4948 nsContentUtils::sFragmentParsingActive = true;
4949 if (!sHTMLFragmentParser) {
4950 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
4951 // Now sHTMLFragmentParser owns the object
4954 nsCOMPtr<nsIPrincipal> nodePrincipal = aTargetNode->NodePrincipal();
4956 #ifdef DEBUG
4957 // aFlags should always be -1 unless the caller of ParseFragmentHTML
4958 // is ParserUtils::ParseFragment() which is the only caller that intends
4959 // sanitization. For all other callers we need to ensure to call
4960 // AuditParsingOfHTMLXMLFragments.
4961 if (aFlags < 0) {
4962 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
4963 aSourceBuffer);
4965 #endif
4967 nsIContent* target = aTargetNode;
4969 RefPtr<Document> doc = aTargetNode->OwnerDoc();
4970 RefPtr<DocumentFragment> fragment;
4971 // We sanitize if the fragment occurs in a system privileged
4972 // context, an about: page, or if there are explicit sanitization flags.
4973 // Please note that about:blank and about:srcdoc inherit the security
4974 // context from the embedding context and hence are not loaded using
4975 // an about: scheme principal.
4976 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
4977 nodePrincipal->SchemeIs("about") || aFlags >= 0;
4978 if (shouldSanitize &&
4979 !AllowsUnsanitizedContentForAboutNewTab(nodePrincipal)) {
4980 if (!doc->IsLoadedAsData()) {
4981 doc = nsContentUtils::CreateInertHTMLDocument(doc);
4982 if (!doc) {
4983 return NS_ERROR_FAILURE;
4986 fragment =
4987 new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager());
4988 target = fragment;
4991 nsresult rv = sHTMLFragmentParser->ParseFragment(
4992 aSourceBuffer, target, aContextLocalName, aContextNamespace, aQuirks,
4993 aPreventScriptExecution);
4994 NS_ENSURE_SUCCESS(rv, rv);
4996 if (fragment) {
4997 uint32_t sanitizationFlags =
4998 computeSanitizationFlags(nodePrincipal, aFlags);
4999 // Don't fire mutation events for nodes removed by the sanitizer.
5000 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5001 nsTreeSanitizer sanitizer(sanitizationFlags);
5002 sanitizer.Sanitize(fragment);
5004 ErrorResult error;
5005 aTargetNode->AppendChild(*fragment, error);
5006 rv = error.StealNSResult();
5009 return rv;
5012 /* static */
5013 nsresult nsContentUtils::ParseDocumentHTML(
5014 const nsAString& aSourceBuffer, Document* aTargetDocument,
5015 bool aScriptingEnabledForNoscriptParsing) {
5016 AutoTimelineMarker m(aTargetDocument->GetDocShell(), "Parse HTML");
5018 if (nsContentUtils::sFragmentParsingActive) {
5019 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5020 return NS_ERROR_DOM_INVALID_STATE_ERR;
5022 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5023 nsContentUtils::sFragmentParsingActive = true;
5024 if (!sHTMLFragmentParser) {
5025 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5026 // Now sHTMLFragmentParser owns the object
5028 nsresult rv = sHTMLFragmentParser->ParseDocument(
5029 aSourceBuffer, aTargetDocument, aScriptingEnabledForNoscriptParsing);
5030 return rv;
5033 /* static */
5034 nsresult nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
5035 Document* aDocument,
5036 nsTArray<nsString>& aTagStack,
5037 bool aPreventScriptExecution,
5038 int32_t aFlags,
5039 DocumentFragment** aReturn) {
5040 AutoTimelineMarker m(aDocument->GetDocShell(), "Parse XML");
5042 if (nsContentUtils::sFragmentParsingActive) {
5043 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5044 return NS_ERROR_DOM_INVALID_STATE_ERR;
5046 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5047 nsContentUtils::sFragmentParsingActive = true;
5048 if (!sXMLFragmentParser) {
5049 nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID);
5050 parser.forget(&sXMLFragmentParser);
5051 // sXMLFragmentParser now owns the parser
5053 if (!sXMLFragmentSink) {
5054 NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
5055 // sXMLFragmentSink now owns the sink
5057 nsCOMPtr<nsIContentSink> contentsink = do_QueryInterface(sXMLFragmentSink);
5058 MOZ_ASSERT(contentsink, "Sink doesn't QI to nsIContentSink!");
5059 sXMLFragmentParser->SetContentSink(contentsink);
5061 RefPtr<Document> doc;
5062 nsCOMPtr<nsIPrincipal> nodePrincipal = aDocument->NodePrincipal();
5064 #ifdef DEBUG
5065 // aFlags should always be -1 unless the caller of ParseFragmentXML
5066 // is ParserUtils::ParseFragment() which is the only caller that intends
5067 // sanitization. For all other callers we need to ensure to call
5068 // AuditParsingOfHTMLXMLFragments.
5069 if (aFlags < 0) {
5070 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5071 aSourceBuffer);
5073 #endif
5075 // We sanitize if the fragment occurs in a system privileged
5076 // context, an about: page, or if there are explicit sanitization flags.
5077 // Please note that about:blank and about:srcdoc inherit the security
5078 // context from the embedding context and hence are not loaded using
5079 // an about: scheme principal.
5080 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5081 nodePrincipal->SchemeIs("about") || aFlags >= 0;
5082 if (shouldSanitize && !aDocument->IsLoadedAsData()) {
5083 doc = nsContentUtils::CreateInertXMLDocument(aDocument);
5084 } else {
5085 doc = aDocument;
5088 sXMLFragmentSink->SetTargetDocument(doc);
5089 sXMLFragmentSink->SetPreventScriptExecution(aPreventScriptExecution);
5091 nsresult rv = sXMLFragmentParser->ParseFragment(aSourceBuffer, aTagStack);
5092 if (NS_FAILED(rv)) {
5093 // Drop the fragment parser and sink that might be in an inconsistent state
5094 NS_IF_RELEASE(sXMLFragmentParser);
5095 NS_IF_RELEASE(sXMLFragmentSink);
5096 return rv;
5099 rv = sXMLFragmentSink->FinishFragmentParsing(aReturn);
5101 sXMLFragmentParser->Reset();
5102 NS_ENSURE_SUCCESS(rv, rv);
5104 if (shouldSanitize) {
5105 uint32_t sanitizationFlags =
5106 computeSanitizationFlags(nodePrincipal, aFlags);
5107 // Don't fire mutation events for nodes removed by the sanitizer.
5108 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5109 nsTreeSanitizer sanitizer(sanitizationFlags);
5110 sanitizer.Sanitize(*aReturn);
5113 return rv;
5116 /* static */
5117 nsresult nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
5118 nsAString& aResultBuffer,
5119 uint32_t aFlags,
5120 uint32_t aWrapCol) {
5121 RefPtr<Document> document = nsContentUtils::CreateInertHTMLDocument(nullptr);
5122 if (!document) {
5123 return NS_ERROR_FAILURE;
5126 nsresult rv = nsContentUtils::ParseDocumentHTML(
5127 aSourceBuffer, document,
5128 !(aFlags & nsIDocumentEncoder::OutputNoScriptContent));
5129 NS_ENSURE_SUCCESS(rv, rv);
5131 nsCOMPtr<nsIDocumentEncoder> encoder = do_createDocumentEncoder("text/plain");
5133 rv = encoder->Init(document, u"text/plain"_ns, aFlags);
5134 NS_ENSURE_SUCCESS(rv, rv);
5136 encoder->SetWrapColumn(aWrapCol);
5138 return encoder->EncodeToString(aResultBuffer);
5141 static already_AddRefed<Document> CreateInertDocument(const Document* aTemplate,
5142 DocumentFlavor aFlavor) {
5143 if (aTemplate) {
5144 bool hasHad = true;
5145 nsIScriptGlobalObject* sgo = aTemplate->GetScriptHandlingObject(hasHad);
5146 NS_ENSURE_TRUE(sgo || !hasHad, nullptr);
5148 nsCOMPtr<Document> doc;
5149 nsresult rv = NS_NewDOMDocument(
5150 getter_AddRefs(doc), u""_ns, u""_ns, nullptr,
5151 aTemplate->GetDocumentURI(), aTemplate->GetDocBaseURI(),
5152 aTemplate->NodePrincipal(), true, sgo, aFlavor);
5153 if (NS_FAILED(rv)) {
5154 return nullptr;
5156 return doc.forget();
5158 nsCOMPtr<nsIURI> uri;
5159 NS_NewURI(getter_AddRefs(uri), "about:blank"_ns);
5160 if (!uri) {
5161 return nullptr;
5164 RefPtr<NullPrincipal> nullPrincipal =
5165 NullPrincipal::CreateWithoutOriginAttributes();
5166 if (!nullPrincipal) {
5167 return nullptr;
5170 nsCOMPtr<Document> doc;
5171 nsresult rv =
5172 NS_NewDOMDocument(getter_AddRefs(doc), u""_ns, u""_ns, nullptr, uri, uri,
5173 nullPrincipal, true, nullptr, aFlavor);
5174 if (NS_FAILED(rv)) {
5175 return nullptr;
5177 return doc.forget();
5180 /* static */
5181 already_AddRefed<Document> nsContentUtils::CreateInertXMLDocument(
5182 const Document* aTemplate) {
5183 return CreateInertDocument(aTemplate, DocumentFlavorXML);
5186 /* static */
5187 already_AddRefed<Document> nsContentUtils::CreateInertHTMLDocument(
5188 const Document* aTemplate) {
5189 return CreateInertDocument(aTemplate, DocumentFlavorHTML);
5192 /* static */
5193 nsresult nsContentUtils::SetNodeTextContent(nsIContent* aContent,
5194 const nsAString& aValue,
5195 bool aTryReuse) {
5196 // Fire DOMNodeRemoved mutation events before we do anything else.
5197 nsCOMPtr<nsIContent> owningContent;
5199 // Batch possible DOMSubtreeModified events.
5200 mozAutoSubtreeModified subtree(nullptr, nullptr);
5202 // Scope firing mutation events so that we don't carry any state that
5203 // might be stale
5205 // We're relying on mozAutoSubtreeModified to keep a strong reference if
5206 // needed.
5207 Document* doc = aContent->OwnerDoc();
5209 // Optimize the common case of there being no observers
5210 if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
5211 subtree.UpdateTarget(doc, nullptr);
5212 owningContent = aContent;
5213 nsCOMPtr<nsINode> child;
5214 bool skipFirst = aTryReuse;
5215 for (child = aContent->GetFirstChild();
5216 child && child->GetParentNode() == aContent;
5217 child = child->GetNextSibling()) {
5218 if (skipFirst && child->IsText()) {
5219 skipFirst = false;
5220 continue;
5222 nsContentUtils::MaybeFireNodeRemoved(child, aContent);
5227 // Might as well stick a batch around this since we're performing several
5228 // mutations.
5229 mozAutoDocUpdate updateBatch(aContent->GetComposedDoc(), true);
5230 nsAutoMutationBatch mb;
5232 if (aTryReuse && !aValue.IsEmpty()) {
5233 // Let's remove nodes until we find a eTEXT.
5234 while (aContent->HasChildren()) {
5235 nsIContent* child = aContent->GetFirstChild();
5236 if (child->IsText()) {
5237 break;
5239 aContent->RemoveChildNode(child, true);
5242 // If we have a node, it must be a eTEXT and we reuse it.
5243 if (aContent->HasChildren()) {
5244 nsIContent* child = aContent->GetFirstChild();
5245 nsresult rv = child->AsText()->SetText(aValue, true);
5246 NS_ENSURE_SUCCESS(rv, rv);
5248 // All the following nodes, if they exist, must be deleted.
5249 while (nsIContent* nextChild = child->GetNextSibling()) {
5250 aContent->RemoveChildNode(nextChild, true);
5254 if (aContent->HasChildren()) {
5255 return NS_OK;
5257 } else {
5258 mb.Init(aContent, true, false);
5259 while (aContent->HasChildren()) {
5260 aContent->RemoveChildNode(aContent->GetFirstChild(), true);
5263 mb.RemovalDone();
5265 if (aValue.IsEmpty()) {
5266 return NS_OK;
5269 RefPtr<nsTextNode> textContent = new (aContent->NodeInfo()->NodeInfoManager())
5270 nsTextNode(aContent->NodeInfo()->NodeInfoManager());
5272 textContent->SetText(aValue, true);
5274 nsresult rv = aContent->AppendChildTo(textContent, true);
5275 mb.NodesAdded();
5276 return rv;
5279 static bool AppendNodeTextContentsRecurse(nsINode* aNode, nsAString& aResult,
5280 const fallible_t& aFallible) {
5281 for (nsIContent* child = aNode->GetFirstChild(); child;
5282 child = child->GetNextSibling()) {
5283 if (child->IsElement()) {
5284 bool ok = AppendNodeTextContentsRecurse(child, aResult, aFallible);
5285 if (!ok) {
5286 return false;
5288 } else if (Text* text = child->GetAsText()) {
5289 bool ok = text->AppendTextTo(aResult, aFallible);
5290 if (!ok) {
5291 return false;
5296 return true;
5299 /* static */
5300 bool nsContentUtils::AppendNodeTextContent(nsINode* aNode, bool aDeep,
5301 nsAString& aResult,
5302 const fallible_t& aFallible) {
5303 if (Text* text = aNode->GetAsText()) {
5304 return text->AppendTextTo(aResult, aFallible);
5306 if (aDeep) {
5307 return AppendNodeTextContentsRecurse(aNode, aResult, aFallible);
5310 for (nsIContent* child = aNode->GetFirstChild(); child;
5311 child = child->GetNextSibling()) {
5312 if (Text* text = child->GetAsText()) {
5313 bool ok = text->AppendTextTo(aResult, fallible);
5314 if (!ok) {
5315 return false;
5319 return true;
5322 bool nsContentUtils::HasNonEmptyTextContent(
5323 nsINode* aNode, TextContentDiscoverMode aDiscoverMode) {
5324 for (nsIContent* child = aNode->GetFirstChild(); child;
5325 child = child->GetNextSibling()) {
5326 if (child->IsText() && child->TextLength() > 0) {
5327 return true;
5330 if (aDiscoverMode == eRecurseIntoChildren &&
5331 HasNonEmptyTextContent(child, aDiscoverMode)) {
5332 return true;
5336 return false;
5339 /* static */
5340 bool nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
5341 const nsIContent* aContent) {
5342 MOZ_ASSERT(aNode, "Must have a node to work with");
5343 MOZ_ASSERT(aContent, "Must have a content to work with");
5345 if (aNode->IsInNativeAnonymousSubtree() !=
5346 aContent->IsInNativeAnonymousSubtree()) {
5347 return false;
5350 if (aNode->IsInNativeAnonymousSubtree()) {
5351 return aContent->GetClosestNativeAnonymousSubtreeRoot() ==
5352 aNode->GetClosestNativeAnonymousSubtreeRoot();
5355 // FIXME: This doesn't deal with disconnected nodes whatsoever, but it didn't
5356 // use to either. Maybe that's fine.
5357 return aNode->GetContainingShadow() == aContent->GetContainingShadow();
5360 /* static */
5361 bool nsContentUtils::IsInInteractiveHTMLContent(const Element* aElement,
5362 const Element* aStop) {
5363 const Element* element = aElement;
5364 while (element && element != aStop) {
5365 if (element->IsInteractiveHTMLContent()) {
5366 return true;
5368 element = element->GetFlattenedTreeParentElement();
5370 return false;
5373 /* static */
5374 void nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling) {
5375 IMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
5378 /* static */
5379 bool nsContentUtils::SchemeIs(nsIURI* aURI, const char* aScheme) {
5380 nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
5381 NS_ENSURE_TRUE(baseURI, false);
5382 return baseURI->SchemeIs(aScheme);
5385 bool nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal) {
5386 nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
5387 return !!ep;
5390 bool nsContentUtils::IsSystemOrExpandedPrincipal(nsIPrincipal* aPrincipal) {
5391 return (aPrincipal && aPrincipal->IsSystemPrincipal()) ||
5392 IsExpandedPrincipal(aPrincipal);
5395 nsIPrincipal* nsContentUtils::GetSystemPrincipal() {
5396 MOZ_ASSERT(IsInitialized());
5397 return sSystemPrincipal;
5400 bool nsContentUtils::CombineResourcePrincipals(
5401 nsCOMPtr<nsIPrincipal>* aResourcePrincipal, nsIPrincipal* aExtraPrincipal) {
5402 if (!aExtraPrincipal) {
5403 return false;
5405 if (!*aResourcePrincipal) {
5406 *aResourcePrincipal = aExtraPrincipal;
5407 return true;
5409 if (*aResourcePrincipal == aExtraPrincipal) {
5410 return false;
5412 bool subsumes;
5413 if (NS_SUCCEEDED(
5414 (*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
5415 subsumes) {
5416 return false;
5418 *aResourcePrincipal = sSystemPrincipal;
5419 return true;
5422 /* static */
5423 void nsContentUtils::TriggerLink(nsIContent* aContent, nsIURI* aLinkURI,
5424 const nsString& aTargetSpec, bool aClick,
5425 bool aIsTrusted) {
5426 MOZ_ASSERT(aLinkURI, "No link URI");
5428 if (aContent->IsEditable() || !aContent->OwnerDoc()->LinkHandlingEnabled()) {
5429 return;
5432 nsCOMPtr<nsIDocShell> docShell = aContent->OwnerDoc()->GetDocShell();
5433 if (!docShell) {
5434 return;
5437 if (!aClick) {
5438 nsDocShell::Cast(docShell)->OnOverLink(aContent, aLinkURI, aTargetSpec);
5439 return;
5442 // Check that this page is allowed to load this URI.
5443 nsresult proceed = NS_OK;
5445 if (sSecurityManager) {
5446 uint32_t flag = static_cast<uint32_t>(nsIScriptSecurityManager::STANDARD);
5447 proceed = sSecurityManager->CheckLoadURIWithPrincipal(
5448 aContent->NodePrincipal(), aLinkURI, flag,
5449 aContent->OwnerDoc()->InnerWindowID());
5452 // Only pass off the click event if the script security manager says it's ok.
5453 // We need to rest aTargetSpec for forced downloads.
5454 if (NS_SUCCEEDED(proceed)) {
5455 // A link/area element with a download attribute is allowed to set
5456 // a pseudo Content-Disposition header.
5457 // For security reasons we only allow websites to declare same-origin
5458 // resources as downloadable. If this check fails we will just do the normal
5459 // thing (i.e. navigate to the resource).
5460 nsAutoString fileName;
5461 if ((!aContent->IsHTMLElement(nsGkAtoms::a) &&
5462 !aContent->IsHTMLElement(nsGkAtoms::area) &&
5463 !aContent->IsSVGElement(nsGkAtoms::a)) ||
5464 !aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::download,
5465 fileName) ||
5466 NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, true))) {
5467 fileName.SetIsVoid(true); // No actionable download attribute was found.
5470 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aContent->NodePrincipal();
5471 nsCOMPtr<nsIContentSecurityPolicy> csp = aContent->GetCsp();
5473 // Sanitize fileNames containing null characters by replacing them with
5474 // underscores.
5475 if (!fileName.IsVoid()) {
5476 fileName.ReplaceChar(char16_t(0), '_');
5478 nsDocShell::Cast(docShell)->OnLinkClick(
5479 aContent, aLinkURI, fileName.IsVoid() ? aTargetSpec : u""_ns, fileName,
5480 nullptr, nullptr, UserActivation::IsHandlingUserInput(), aIsTrusted,
5481 triggeringPrincipal, csp);
5485 /* static */
5486 void nsContentUtils::GetLinkLocation(Element* aElement,
5487 nsString& aLocationString) {
5488 nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
5489 if (hrefURI) {
5490 nsAutoCString specUTF8;
5491 nsresult rv = hrefURI->GetSpec(specUTF8);
5492 if (NS_SUCCEEDED(rv)) CopyUTF8toUTF16(specUTF8, aLocationString);
5496 /* static */
5497 nsIWidget* nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget) {
5498 if (!aWidget) return nullptr;
5500 return aWidget->GetTopLevelWidget();
5503 /* static */
5504 const nsDependentString nsContentUtils::GetLocalizedEllipsis() {
5505 static char16_t sBuf[4] = {0, 0, 0, 0};
5506 if (!sBuf[0]) {
5507 if (!SpoofLocaleEnglish()) {
5508 nsAutoString tmp;
5509 Preferences::GetLocalizedString("intl.ellipsis", tmp);
5510 uint32_t len =
5511 std::min(uint32_t(tmp.Length()), uint32_t(ArrayLength(sBuf) - 1));
5512 CopyUnicodeTo(tmp, 0, sBuf, len);
5514 if (!sBuf[0]) sBuf[0] = char16_t(0x2026);
5516 return nsDependentString(sBuf);
5519 /* static */
5520 void nsContentUtils::AddScriptBlocker() {
5521 MOZ_ASSERT(NS_IsMainThread());
5522 if (!sScriptBlockerCount) {
5523 MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
5524 "Should not already have a count");
5525 sRunnersCountAtFirstBlocker =
5526 sBlockedScriptRunners ? sBlockedScriptRunners->Length() : 0;
5528 ++sScriptBlockerCount;
5531 #ifdef DEBUG
5532 static bool sRemovingScriptBlockers = false;
5533 #endif
5535 /* static */
5536 void nsContentUtils::RemoveScriptBlocker() {
5537 MOZ_ASSERT(NS_IsMainThread());
5538 MOZ_ASSERT(!sRemovingScriptBlockers);
5539 NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
5540 --sScriptBlockerCount;
5541 if (sScriptBlockerCount) {
5542 return;
5545 if (!sBlockedScriptRunners) {
5546 return;
5549 uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
5550 uint32_t lastBlocker = sBlockedScriptRunners->Length();
5551 uint32_t originalFirstBlocker = firstBlocker;
5552 uint32_t blockersCount = lastBlocker - firstBlocker;
5553 sRunnersCountAtFirstBlocker = 0;
5554 NS_ASSERTION(firstBlocker <= lastBlocker, "bad sRunnersCountAtFirstBlocker");
5556 while (firstBlocker < lastBlocker) {
5557 nsCOMPtr<nsIRunnable> runnable;
5558 runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
5559 ++firstBlocker;
5561 // Calling the runnable can reenter us
5563 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
5564 runnable->Run();
5566 // So can dropping the reference to the runnable
5567 runnable = nullptr;
5569 NS_ASSERTION(sRunnersCountAtFirstBlocker == 0, "Bad count");
5570 NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
5572 #ifdef DEBUG
5573 AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
5574 sRemovingScriptBlockers = true;
5575 #endif
5576 sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
5579 /* static */
5580 already_AddRefed<nsPIDOMWindowOuter>
5581 nsContentUtils::GetMostRecentNonPBWindow() {
5582 nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
5584 nsCOMPtr<mozIDOMWindowProxy> window;
5585 wm->GetMostRecentNonPBWindow(u"navigator:browser", getter_AddRefs(window));
5586 nsCOMPtr<nsPIDOMWindowOuter> pwindow;
5587 pwindow = do_QueryInterface(window);
5589 return pwindow.forget();
5592 /* static */
5593 void nsContentUtils::WarnScriptWasIgnored(Document* aDocument) {
5594 nsAutoString msg;
5595 bool privateBrowsing = false;
5596 bool chromeContext = false;
5598 if (aDocument) {
5599 nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
5600 if (uri) {
5601 msg.Append(NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()));
5602 msg.AppendLiteral(" : ");
5604 privateBrowsing =
5605 !!aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
5606 chromeContext = aDocument->NodePrincipal()->IsSystemPrincipal();
5609 msg.AppendLiteral(
5610 "Unable to run script because scripts are blocked internally.");
5611 LogSimpleConsoleError(msg, "DOM", privateBrowsing, chromeContext);
5614 /* static */
5615 void nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable) {
5616 nsCOMPtr<nsIRunnable> runnable = aRunnable;
5617 if (!runnable) {
5618 return;
5621 if (sScriptBlockerCount) {
5622 sBlockedScriptRunners->AppendElement(runnable.forget());
5623 return;
5626 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
5627 runnable->Run();
5630 /* static */
5631 void nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) {
5632 nsCOMPtr<nsIRunnable> runnable = aRunnable;
5633 AddScriptRunner(runnable.forget());
5636 /* static */ bool nsContentUtils::IsSafeToRunScript() {
5637 MOZ_ASSERT(NS_IsMainThread(),
5638 "This static variable only makes sense on the main thread!");
5639 return sScriptBlockerCount == 0;
5642 /* static */
5643 void nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable) {
5644 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5645 CycleCollectedJSContext::Get()->RunInStableState(std::move(aRunnable));
5648 /* static */
5649 void nsContentUtils::AddPendingIDBTransaction(
5650 already_AddRefed<nsIRunnable> aTransaction) {
5651 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5652 CycleCollectedJSContext::Get()->AddPendingIDBTransaction(
5653 std::move(aTransaction));
5656 /* static */
5657 bool nsContentUtils::IsInStableOrMetaStableState() {
5658 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5659 return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
5662 /* static */
5663 void nsContentUtils::HidePopupsInDocument(Document* aDocument) {
5664 #ifdef MOZ_XUL
5665 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
5666 if (pm && aDocument) {
5667 nsCOMPtr<nsIDocShellTreeItem> docShellToHide = aDocument->GetDocShell();
5668 if (docShellToHide) pm->HidePopupsInDocShell(docShellToHide);
5670 #endif
5673 /* static */
5674 already_AddRefed<nsIDragSession> nsContentUtils::GetDragSession() {
5675 nsCOMPtr<nsIDragSession> dragSession;
5676 nsCOMPtr<nsIDragService> dragService =
5677 do_GetService("@mozilla.org/widget/dragservice;1");
5678 if (dragService) dragService->GetCurrentSession(getter_AddRefs(dragSession));
5679 return dragSession.forget();
5682 /* static */
5683 nsresult nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) {
5684 if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) {
5685 return NS_OK;
5688 // For dragstart events, the data transfer object is
5689 // created before the event fires, so it should already be set. For other
5690 // drag events, get the object from the drag session.
5691 NS_ASSERTION(aDragEvent->mMessage != eDragStart,
5692 "draggesture event created without a dataTransfer");
5694 nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
5695 NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
5697 RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
5698 if (!initialDataTransfer) {
5699 // A dataTransfer won't exist when a drag was started by some other
5700 // means, for instance calling the drag service directly, or a drag
5701 // from another application. In either case, a new dataTransfer should
5702 // be created that reflects the data.
5703 initialDataTransfer =
5704 new DataTransfer(aDragEvent->mTarget, aDragEvent->mMessage, true, -1);
5706 // now set it in the drag session so we don't need to create it again
5707 dragSession->SetDataTransfer(initialDataTransfer);
5710 bool isCrossDomainSubFrameDrop = false;
5711 if (aDragEvent->mMessage == eDrop) {
5712 isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
5715 // each event should use a clone of the original dataTransfer.
5716 initialDataTransfer->Clone(
5717 aDragEvent->mTarget, aDragEvent->mMessage, aDragEvent->mUserCancelled,
5718 isCrossDomainSubFrameDrop, getter_AddRefs(aDragEvent->mDataTransfer));
5719 if (NS_WARN_IF(!aDragEvent->mDataTransfer)) {
5720 return NS_ERROR_OUT_OF_MEMORY;
5723 // for the dragenter and dragover events, initialize the drop effect
5724 // from the drop action, which platform specific widget code sets before
5725 // the event is fired based on the keyboard state.
5726 if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
5727 uint32_t action;
5728 dragSession->GetDragAction(&action);
5729 uint32_t effectAllowed = aDragEvent->mDataTransfer->EffectAllowedInt();
5730 aDragEvent->mDataTransfer->SetDropEffectInt(
5731 FilterDropEffect(action, effectAllowed));
5732 } else if (aDragEvent->mMessage == eDrop ||
5733 aDragEvent->mMessage == eDragEnd) {
5734 // For the drop and dragend events, set the drop effect based on the
5735 // last value that the dropEffect had. This will have been set in
5736 // EventStateManager::PostHandleEvent for the last dragenter or
5737 // dragover event.
5738 aDragEvent->mDataTransfer->SetDropEffectInt(
5739 initialDataTransfer->DropEffectInt());
5742 return NS_OK;
5745 /* static */
5746 uint32_t nsContentUtils::FilterDropEffect(uint32_t aAction,
5747 uint32_t aEffectAllowed) {
5748 // It is possible for the drag action to include more than one action, but
5749 // the widget code which sets the action from the keyboard state should only
5750 // be including one. If multiple actions were set, we just consider them in
5751 // the following order:
5752 // copy, link, move
5753 if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
5754 aAction = nsIDragService::DRAGDROP_ACTION_COPY;
5755 else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
5756 aAction = nsIDragService::DRAGDROP_ACTION_LINK;
5757 else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
5758 aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
5760 // Filter the action based on the effectAllowed. If the effectAllowed
5761 // doesn't include the action, then that action cannot be done, so adjust
5762 // the action to something that is allowed. For a copy, adjust to move or
5763 // link. For a move, adjust to copy or link. For a link, adjust to move or
5764 // link. Otherwise, use none.
5765 if (aAction & aEffectAllowed ||
5766 aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
5767 return aAction;
5768 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
5769 return nsIDragService::DRAGDROP_ACTION_MOVE;
5770 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
5771 return nsIDragService::DRAGDROP_ACTION_COPY;
5772 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
5773 return nsIDragService::DRAGDROP_ACTION_LINK;
5774 return nsIDragService::DRAGDROP_ACTION_NONE;
5777 /* static */
5778 bool nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession,
5779 WidgetDragEvent* aDropEvent) {
5780 nsCOMPtr<nsIContent> target = do_QueryInterface(aDropEvent->mOriginalTarget);
5781 if (!target) {
5782 return true;
5785 // Always allow dropping onto chrome shells.
5786 BrowsingContext* targetBC = target->OwnerDoc()->GetBrowsingContext();
5787 if (targetBC->IsChrome()) {
5788 return false;
5791 // If there is no source node, then this is a drag from another
5792 // application, which should be allowed.
5793 RefPtr<Document> doc(aDragSession->GetSourceDocument());
5794 if (doc && doc->GetBrowsingContext()) {
5795 // Get each successive parent of the source document and compare it to
5796 // the drop document. If they match, then this is a drag from a child frame.
5797 for (BrowsingContext* bc = doc->GetBrowsingContext()->GetParent(); bc;
5798 bc = bc->GetParent()) {
5799 if (bc == targetBC) {
5800 // The drag is from a descendant frame.
5801 return true;
5806 return false;
5809 /* static */
5810 bool nsContentUtils::URIIsLocalFile(nsIURI* aURI) {
5811 bool isFile;
5812 nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
5814 // Important: we do NOT test the entire URI chain here!
5815 return util &&
5816 NS_SUCCEEDED(util->ProtocolHasFlags(
5817 aURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, &isFile)) &&
5818 isFile;
5821 /* static */
5822 JSContext* nsContentUtils::GetCurrentJSContext() {
5823 MOZ_ASSERT(IsInitialized());
5824 if (!IsJSAPIActive()) {
5825 return nullptr;
5827 return danger::GetJSContext();
5830 template <typename StringType, typename CharType>
5831 void _ASCIIToLowerInSitu(StringType& aStr) {
5832 CharType* iter = aStr.BeginWriting();
5833 CharType* end = aStr.EndWriting();
5834 MOZ_ASSERT(iter && end);
5836 while (iter != end) {
5837 CharType c = *iter;
5838 if (c >= 'A' && c <= 'Z') {
5839 *iter = c + ('a' - 'A');
5841 ++iter;
5845 /* static */
5846 void nsContentUtils::ASCIIToLower(nsAString& aStr) {
5847 return _ASCIIToLowerInSitu<nsAString, char16_t>(aStr);
5850 /* static */
5851 void nsContentUtils::ASCIIToLower(nsACString& aStr) {
5852 return _ASCIIToLowerInSitu<nsACString, char>(aStr);
5855 template <typename StringType, typename CharType>
5856 void _ASCIIToLowerCopy(const StringType& aSource, StringType& aDest) {
5857 uint32_t len = aSource.Length();
5858 aDest.SetLength(len);
5859 MOZ_ASSERT(aDest.Length() == len);
5861 CharType* dest = aDest.BeginWriting();
5862 MOZ_ASSERT(dest);
5864 const CharType* iter = aSource.BeginReading();
5865 const CharType* end = aSource.EndReading();
5866 while (iter != end) {
5867 CharType c = *iter;
5868 *dest = (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
5869 ++iter;
5870 ++dest;
5874 /* static */
5875 void nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest) {
5876 return _ASCIIToLowerCopy<nsAString, char16_t>(aSource, aDest);
5879 /* static */
5880 void nsContentUtils::ASCIIToLower(const nsACString& aSource,
5881 nsACString& aDest) {
5882 return _ASCIIToLowerCopy<nsACString, char>(aSource, aDest);
5885 template <typename StringType, typename CharType>
5886 void _ASCIIToUpperInSitu(StringType& aStr) {
5887 CharType* iter = aStr.BeginWriting();
5888 CharType* end = aStr.EndWriting();
5889 MOZ_ASSERT(iter && end);
5891 while (iter != end) {
5892 CharType c = *iter;
5893 if (c >= 'a' && c <= 'z') {
5894 *iter = c + ('A' - 'a');
5896 ++iter;
5900 /* static */
5901 void nsContentUtils::ASCIIToUpper(nsAString& aStr) {
5902 return _ASCIIToUpperInSitu<nsAString, char16_t>(aStr);
5905 /* static */
5906 void nsContentUtils::ASCIIToUpper(nsACString& aStr) {
5907 return _ASCIIToUpperInSitu<nsACString, char>(aStr);
5910 template <typename StringType, typename CharType>
5911 void _ASCIIToUpperCopy(const StringType& aSource, StringType& aDest) {
5912 uint32_t len = aSource.Length();
5913 aDest.SetLength(len);
5914 MOZ_ASSERT(aDest.Length() == len);
5916 CharType* dest = aDest.BeginWriting();
5917 MOZ_ASSERT(dest);
5919 const CharType* iter = aSource.BeginReading();
5920 const CharType* end = aSource.EndReading();
5921 while (iter != end) {
5922 CharType c = *iter;
5923 *dest = (c >= 'a' && c <= 'z') ? c + ('A' - 'a') : c;
5924 ++iter;
5925 ++dest;
5929 /* static */
5930 void nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest) {
5931 return _ASCIIToUpperCopy<nsAString, char16_t>(aSource, aDest);
5934 /* static */
5935 void nsContentUtils::ASCIIToUpper(const nsACString& aSource,
5936 nsACString& aDest) {
5937 return _ASCIIToUpperCopy<nsACString, char>(aSource, aDest);
5940 /* static */
5941 bool nsContentUtils::EqualsIgnoreASCIICase(nsAtom* aAtom1, nsAtom* aAtom2) {
5942 if (aAtom1 == aAtom2) {
5943 return true;
5946 // If both are ascii lowercase already, we know that the slow comparison
5947 // below is going to return false.
5948 if (aAtom1->IsAsciiLowercase() && aAtom2->IsAsciiLowercase()) {
5949 return false;
5952 return EqualsIgnoreASCIICase(nsDependentAtomString(aAtom1),
5953 nsDependentAtomString(aAtom2));
5956 /* static */
5957 bool nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
5958 const nsAString& aStr2) {
5959 uint32_t len = aStr1.Length();
5960 if (len != aStr2.Length()) {
5961 return false;
5964 const char16_t* str1 = aStr1.BeginReading();
5965 const char16_t* str2 = aStr2.BeginReading();
5966 const char16_t* end = str1 + len;
5968 while (str1 < end) {
5969 char16_t c1 = *str1++;
5970 char16_t c2 = *str2++;
5972 // First check if any bits other than the 0x0020 differs
5973 if ((c1 ^ c2) & 0xffdf) {
5974 return false;
5977 // We know they can only differ in the 0x0020 bit.
5978 // Likely the two chars are the same, so check that first
5979 if (c1 != c2) {
5980 // They do differ, but since it's only in the 0x0020 bit, check if it's
5981 // the same ascii char, but just differing in case
5982 char16_t c1Upper = c1 & 0xffdf;
5983 if (!('A' <= c1Upper && c1Upper <= 'Z')) {
5984 return false;
5989 return true;
5992 /* static */
5993 bool nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr) {
5994 const char16_t* iter = aStr.BeginReading();
5995 const char16_t* end = aStr.EndReading();
5996 while (iter != end) {
5997 char16_t c = *iter;
5998 if (c >= 'A' && c <= 'Z') {
5999 return true;
6001 ++iter;
6004 return false;
6007 /* static */
6008 nsIInterfaceRequestor* nsContentUtils::SameOriginChecker() {
6009 if (!sSameOriginChecker) {
6010 sSameOriginChecker = new SameOriginCheckerImpl();
6011 NS_ADDREF(sSameOriginChecker);
6013 return sSameOriginChecker;
6016 /* static */
6017 nsresult nsContentUtils::CheckSameOrigin(nsIChannel* aOldChannel,
6018 nsIChannel* aNewChannel) {
6019 if (!nsContentUtils::GetSecurityManager()) return NS_ERROR_NOT_AVAILABLE;
6021 nsCOMPtr<nsIPrincipal> oldPrincipal;
6022 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
6023 aOldChannel, getter_AddRefs(oldPrincipal));
6025 nsCOMPtr<nsIURI> newURI;
6026 aNewChannel->GetURI(getter_AddRefs(newURI));
6027 nsCOMPtr<nsIURI> newOriginalURI;
6028 aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
6030 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
6032 nsresult rv = oldPrincipal->CheckMayLoad(newURI, false);
6033 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
6034 rv = oldPrincipal->CheckMayLoad(newOriginalURI, false);
6037 return rv;
6040 NS_IMPL_ISUPPORTS(SameOriginCheckerImpl, nsIChannelEventSink,
6041 nsIInterfaceRequestor)
6043 NS_IMETHODIMP
6044 SameOriginCheckerImpl::AsyncOnChannelRedirect(
6045 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
6046 nsIAsyncVerifyRedirectCallback* cb) {
6047 MOZ_ASSERT(aNewChannel, "Redirecting to null channel?");
6049 nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
6050 if (NS_SUCCEEDED(rv)) {
6051 cb->OnRedirectVerifyCallback(NS_OK);
6054 return rv;
6057 NS_IMETHODIMP
6058 SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult) {
6059 return QueryInterface(aIID, aResult);
6062 /* static */
6063 nsresult nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsACString& aOrigin) {
6064 MOZ_ASSERT(aURI, "missing uri");
6066 // For Blob URI, the path is the URL of the owning page.
6067 if (aURI->SchemeIs(BLOBURI_SCHEME)) {
6068 nsAutoCString path;
6069 nsresult rv = aURI->GetPathQueryRef(path);
6070 NS_ENSURE_SUCCESS(rv, rv);
6072 nsCOMPtr<nsIURI> uri;
6073 rv = NS_NewURI(getter_AddRefs(uri), path);
6074 if (NS_FAILED(rv)) {
6075 aOrigin.AssignLiteral("null");
6076 return NS_OK;
6079 return GetASCIIOrigin(uri, aOrigin);
6082 aOrigin.Truncate();
6084 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
6085 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
6087 nsAutoCString host;
6088 nsresult rv = uri->GetAsciiHost(host);
6090 if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
6091 nsAutoCString userPass;
6092 uri->GetUserPass(userPass);
6094 nsAutoCString prePath;
6095 if (!userPass.IsEmpty()) {
6096 rv = NS_MutateURI(uri).SetUserPass(""_ns).Finalize(uri);
6097 NS_ENSURE_SUCCESS(rv, rv);
6100 rv = uri->GetPrePath(prePath);
6101 NS_ENSURE_SUCCESS(rv, rv);
6103 aOrigin = prePath;
6104 } else {
6105 aOrigin.AssignLiteral("null");
6108 return NS_OK;
6111 /* static */
6112 nsresult nsContentUtils::GetUTFOrigin(nsIPrincipal* aPrincipal,
6113 nsAString& aOrigin) {
6114 MOZ_ASSERT(aPrincipal, "missing principal");
6116 aOrigin.Truncate();
6117 nsAutoCString asciiOrigin;
6119 nsresult rv = aPrincipal->GetAsciiOrigin(asciiOrigin);
6120 if (NS_FAILED(rv)) {
6121 asciiOrigin.AssignLiteral("null");
6124 CopyUTF8toUTF16(asciiOrigin, aOrigin);
6125 return NS_OK;
6128 /* static */
6129 nsresult nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsAString& aOrigin) {
6130 MOZ_ASSERT(aURI, "missing uri");
6131 nsresult rv;
6133 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
6134 // Check if either URI has a special origin.
6135 nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin =
6136 do_QueryInterface(aURI);
6137 if (uriWithSpecialOrigin) {
6138 nsCOMPtr<nsIURI> origin;
6139 rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin));
6140 NS_ENSURE_SUCCESS(rv, rv);
6142 return GetUTFOrigin(origin, aOrigin);
6144 #endif
6146 nsAutoCString asciiOrigin;
6147 rv = GetASCIIOrigin(aURI, asciiOrigin);
6148 NS_ENSURE_SUCCESS(rv, rv);
6150 CopyUTF8toUTF16(asciiOrigin, aOrigin);
6151 return NS_OK;
6154 /* static */
6155 bool nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal,
6156 nsIChannel* aChannel,
6157 bool aAllowIfInheritsPrincipal) {
6158 nsCOMPtr<nsIURI> channelURI;
6159 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
6160 NS_ENSURE_SUCCESS(rv, false);
6162 return NS_SUCCEEDED(
6163 aPrincipal->CheckMayLoad(channelURI, aAllowIfInheritsPrincipal));
6166 /* static */
6167 bool nsContentUtils::CanAccessNativeAnon() {
6168 return LegacyIsCallerChromeOrNativeCode();
6171 /* static */
6172 nsresult nsContentUtils::DispatchXULCommand(nsIContent* aTarget, bool aTrusted,
6173 Event* aSourceEvent,
6174 PresShell* aPresShell, bool aCtrl,
6175 bool aAlt, bool aShift, bool aMeta,
6176 uint16_t aInputSource) {
6177 NS_ENSURE_STATE(aTarget);
6178 Document* doc = aTarget->OwnerDoc();
6179 nsPresContext* presContext = doc->GetPresContext();
6181 RefPtr<XULCommandEvent> xulCommand =
6182 new XULCommandEvent(doc, presContext, nullptr);
6183 xulCommand->InitCommandEvent(u"command"_ns, true, true,
6184 nsGlobalWindowInner::Cast(doc->GetInnerWindow()),
6185 0, aCtrl, aAlt, aShift, aMeta, aSourceEvent,
6186 aInputSource, IgnoreErrors());
6188 if (aPresShell) {
6189 nsEventStatus status = nsEventStatus_eIgnore;
6190 return aPresShell->HandleDOMEventWithTarget(aTarget, xulCommand, &status);
6193 ErrorResult rv;
6194 aTarget->DispatchEvent(*xulCommand, rv);
6195 return rv.StealNSResult();
6198 // static
6199 nsresult nsContentUtils::WrapNative(JSContext* cx, nsISupports* native,
6200 nsWrapperCache* cache, const nsIID* aIID,
6201 JS::MutableHandle<JS::Value> vp,
6202 bool aAllowWrapping) {
6203 MOZ_ASSERT(cx == GetCurrentJSContext());
6205 if (!native) {
6206 vp.setNull();
6208 return NS_OK;
6211 JSObject* wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
6212 if (wrapper) {
6213 return NS_OK;
6216 NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
6218 if (!NS_IsMainThread()) {
6219 MOZ_CRASH();
6222 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
6223 nsresult rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
6224 aAllowWrapping, vp);
6225 return rv;
6228 nsresult nsContentUtils::CreateArrayBuffer(JSContext* aCx,
6229 const nsACString& aData,
6230 JSObject** aResult) {
6231 if (!aCx) {
6232 return NS_ERROR_FAILURE;
6235 size_t dataLen = aData.Length();
6236 *aResult = JS::NewArrayBuffer(aCx, dataLen);
6237 if (!*aResult) {
6238 return NS_ERROR_FAILURE;
6241 if (dataLen > 0) {
6242 NS_ASSERTION(JS::IsArrayBufferObject(*aResult), "What happened?");
6243 JS::AutoCheckCannotGC nogc;
6244 bool isShared;
6245 memcpy(JS::GetArrayBufferData(*aResult, &isShared, nogc),
6246 aData.BeginReading(), dataLen);
6247 MOZ_ASSERT(!isShared);
6250 return NS_OK;
6253 void nsContentUtils::StripNullChars(const nsAString& aInStr,
6254 nsAString& aOutStr) {
6255 // In common cases where we don't have nulls in the
6256 // string we can simple simply bypass the checking code.
6257 int32_t firstNullPos = aInStr.FindChar('\0');
6258 if (firstNullPos == kNotFound) {
6259 aOutStr.Assign(aInStr);
6260 return;
6263 aOutStr.SetCapacity(aInStr.Length() - 1);
6264 nsAString::const_iterator start, end;
6265 aInStr.BeginReading(start);
6266 aInStr.EndReading(end);
6267 while (start != end) {
6268 if (*start != '\0') aOutStr.Append(*start);
6269 ++start;
6273 struct ClassMatchingInfo {
6274 AtomArray mClasses;
6275 nsCaseTreatment mCaseTreatment;
6278 // static
6279 bool nsContentUtils::MatchClassNames(Element* aElement, int32_t aNamespaceID,
6280 nsAtom* aAtom, void* aData) {
6281 // We can't match if there are no class names
6282 const nsAttrValue* classAttr = aElement->GetClasses();
6283 if (!classAttr) {
6284 return false;
6287 // need to match *all* of the classes
6288 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6289 uint32_t length = info->mClasses.Length();
6290 if (!length) {
6291 // If we actually had no classes, don't match.
6292 return false;
6294 uint32_t i;
6295 for (i = 0; i < length; ++i) {
6296 if (!classAttr->Contains(info->mClasses[i], info->mCaseTreatment)) {
6297 return false;
6301 return true;
6304 // static
6305 void nsContentUtils::DestroyClassNameArray(void* aData) {
6306 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6307 delete info;
6310 // static
6311 void* nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
6312 const nsString* aClasses) {
6313 nsAttrValue attrValue;
6314 attrValue.ParseAtomArray(*aClasses);
6315 // nsAttrValue::Equals is sensitive to order, so we'll send an array
6316 auto* info = new ClassMatchingInfo;
6317 if (attrValue.Type() == nsAttrValue::eAtomArray) {
6318 info->mClasses = std::move(*(attrValue.GetAtomArrayValue()));
6319 } else if (attrValue.Type() == nsAttrValue::eAtom) {
6320 info->mClasses.AppendElement(attrValue.GetAtomValue());
6323 info->mCaseTreatment =
6324 aRootNode->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks
6325 ? eIgnoreCase
6326 : eCaseMatters;
6327 return info;
6330 // static
6331 bool nsContentUtils::IsFocusedContent(const nsIContent* aContent) {
6332 nsFocusManager* fm = nsFocusManager::GetFocusManager();
6334 return fm && fm->GetFocusedElement() == aContent;
6337 bool nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent) {
6338 Document* doc = aContent->GetComposedDoc();
6339 if (!doc) {
6340 return false;
6343 // If the subdocument lives in another process, the frame is
6344 // tabbable.
6345 if (EventStateManager::IsRemoteTarget(aContent)) {
6346 return true;
6349 // XXXbz should this use OwnerDoc() for GetSubDocumentFor?
6350 // sXBL/XBL2 issue!
6351 Document* subDoc = doc->GetSubDocumentFor(aContent);
6352 if (!subDoc) {
6353 return false;
6356 nsCOMPtr<nsIDocShell> docShell = subDoc->GetDocShell();
6357 if (!docShell) {
6358 return false;
6361 nsCOMPtr<nsIContentViewer> contentViewer;
6362 docShell->GetContentViewer(getter_AddRefs(contentViewer));
6363 if (!contentViewer) {
6364 return false;
6367 // If there are 2 viewers for the current docshell, that
6368 // means the current document may be a zombie document.
6369 // While load and pageshow events are dispatched, zombie viewer is the old,
6370 // to be hidden document.
6371 if (contentViewer->GetPreviousViewer()) {
6372 bool inOnLoad = false;
6373 docShell->GetIsExecutingOnLoadHandler(&inOnLoad);
6374 return inOnLoad;
6377 return true;
6380 bool nsContentUtils::HasScrollgrab(nsIContent* aContent) {
6381 // If we ever standardize this feature we'll want to hook this up properly
6382 // again. For now we're removing all the DOM-side code related to it but
6383 // leaving the layout and APZ handling for it in place.
6384 return false;
6387 void nsContentUtils::FlushLayoutForTree(nsPIDOMWindowOuter* aWindow) {
6388 if (!aWindow) {
6389 return;
6392 // Note that because FlushPendingNotifications flushes parents, this
6393 // is O(N^2) in docshell tree depth. However, the docshell tree is
6394 // usually pretty shallow.
6396 if (RefPtr<Document> doc = aWindow->GetDoc()) {
6397 doc->FlushPendingNotifications(FlushType::Layout);
6400 if (nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell()) {
6401 int32_t i = 0, i_end;
6402 docShell->GetInProcessChildCount(&i_end);
6403 for (; i < i_end; ++i) {
6404 nsCOMPtr<nsIDocShellTreeItem> item;
6405 if (docShell->GetInProcessChildAt(i, getter_AddRefs(item)) == NS_OK &&
6406 item) {
6407 if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
6408 FlushLayoutForTree(win);
6415 void nsContentUtils::RemoveNewlines(nsString& aString) { aString.StripCRLF(); }
6417 void nsContentUtils::PlatformToDOMLineBreaks(nsString& aString) {
6418 if (!PlatformToDOMLineBreaks(aString, fallible)) {
6419 aString.AllocFailed(aString.Length());
6423 bool nsContentUtils::PlatformToDOMLineBreaks(nsString& aString,
6424 const fallible_t& aFallible) {
6425 if (aString.FindChar(char16_t('\r')) != -1) {
6426 // Windows linebreaks: Map CRLF to LF:
6427 if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
6428 return false;
6431 // Mac linebreaks: Map any remaining CR to LF:
6432 if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
6433 return false;
6437 return true;
6440 void nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
6441 nsAString& aResultString) {
6442 MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
6444 uint32_t stringLen = NS_strlen(static_cast<char16_t*>(aBuf->Data()));
6446 // SANITY CHECK: In case the nsStringBuffer isn't correctly
6447 // null-terminated, let's clamp its length using the allocated size, to be
6448 // sure the resulting string doesn't sample past the end of the the buffer.
6449 // (Note that StorageSize() is in units of bytes, so we have to convert that
6450 // to units of PRUnichars, and subtract 1 for the null-terminator.)
6451 uint32_t allocStringLen = (aBuf->StorageSize() / sizeof(char16_t)) - 1;
6452 MOZ_ASSERT(stringLen <= allocStringLen,
6453 "string buffer lacks null terminator!");
6454 stringLen = std::min(stringLen, allocStringLen);
6456 aBuf->ToString(stringLen, aResultString);
6459 already_AddRefed<nsContentList> nsContentUtils::GetElementsByClassName(
6460 nsINode* aRootNode, const nsAString& aClasses) {
6461 MOZ_ASSERT(aRootNode, "Must have root node");
6463 return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(
6464 aRootNode, MatchClassNames, DestroyClassNameArray, AllocClassMatchingInfo,
6465 aClasses);
6468 PresShell* nsContentUtils::FindPresShellForDocument(const Document* aDocument) {
6469 const Document* doc = aDocument;
6470 Document* displayDoc = doc->GetDisplayDocument();
6471 if (displayDoc) {
6472 doc = displayDoc;
6475 PresShell* presShell = doc->GetPresShell();
6476 if (presShell) {
6477 return presShell;
6480 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
6481 while (docShellTreeItem) {
6482 // We may be in a display:none subdocument, or we may not have a presshell
6483 // created yet.
6484 // Walk the docshell tree to find the nearest container that has a
6485 // presshell, and return that.
6486 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
6487 if (PresShell* presShell = docShell->GetPresShell()) {
6488 return presShell;
6490 nsCOMPtr<nsIDocShellTreeItem> parent;
6491 docShellTreeItem->GetInProcessParent(getter_AddRefs(parent));
6492 docShellTreeItem = parent;
6495 return nullptr;
6498 /* static */
6499 nsPresContext* nsContentUtils::FindPresContextForDocument(
6500 const Document* aDocument) {
6501 if (PresShell* presShell = FindPresShellForDocument(aDocument)) {
6502 return presShell->GetPresContext();
6504 return nullptr;
6507 nsIWidget* nsContentUtils::WidgetForDocument(const Document* aDocument) {
6508 PresShell* presShell = FindPresShellForDocument(aDocument);
6509 if (!presShell) {
6510 return nullptr;
6512 nsViewManager* vm = presShell->GetViewManager();
6513 if (!vm) {
6514 return nullptr;
6516 nsView* rootView = vm->GetRootView();
6517 if (!rootView) {
6518 return nullptr;
6520 nsView* displayRoot = nsViewManager::GetDisplayRootFor(rootView);
6521 if (!displayRoot) {
6522 return nullptr;
6524 return displayRoot->GetNearestWidget(nullptr);
6527 nsIWidget* nsContentUtils::WidgetForContent(const nsIContent* aContent) {
6528 nsIFrame* frame = aContent->GetPrimaryFrame();
6529 if (frame) {
6530 frame = nsLayoutUtils::GetDisplayRootFrame(frame);
6532 nsView* view = frame->GetView();
6533 if (view) {
6534 return view->GetWidget();
6538 return nullptr;
6541 already_AddRefed<LayerManager> nsContentUtils::LayerManagerForContent(
6542 const nsIContent* aContent) {
6543 nsIWidget* widget = nsContentUtils::WidgetForContent(aContent);
6544 if (widget) {
6545 RefPtr<LayerManager> manager = widget->GetLayerManager();
6546 return manager.forget();
6549 return nullptr;
6552 static already_AddRefed<LayerManager> LayerManagerForDocumentInternal(
6553 const Document* aDoc, bool aRequirePersistent) {
6554 nsIWidget* widget = nsContentUtils::WidgetForDocument(aDoc);
6555 if (widget) {
6556 RefPtr<LayerManager> manager = widget->GetLayerManager(
6557 aRequirePersistent ? nsIWidget::LAYER_MANAGER_PERSISTENT
6558 : nsIWidget::LAYER_MANAGER_CURRENT);
6559 return manager.forget();
6562 return nullptr;
6565 already_AddRefed<LayerManager> nsContentUtils::LayerManagerForDocument(
6566 const Document* aDoc) {
6567 return LayerManagerForDocumentInternal(aDoc, false);
6570 already_AddRefed<LayerManager>
6571 nsContentUtils::PersistentLayerManagerForDocument(Document* aDoc) {
6572 return LayerManagerForDocumentInternal(aDoc, true);
6575 bool nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal) {
6576 if (!aPrincipal) {
6577 return false;
6580 if (aPrincipal->IsSystemPrincipal()) {
6581 return true;
6584 return (StaticPrefs::dom_allow_XUL_XBL_for_file() &&
6585 aPrincipal->SchemeIs("file")) ||
6586 IsSitePermAllow(aPrincipal, "allowXULXBL"_ns);
6589 bool nsContentUtils::IsPDFJSEnabled() {
6590 nsCOMPtr<nsIStreamConverter> conv = do_CreateInstance(
6591 "@mozilla.org/streamconv;1?from=application/pdf&to=text/html");
6592 return conv;
6595 bool nsContentUtils::IsPDFJS(nsIPrincipal* aPrincipal) {
6596 if (!aPrincipal) {
6597 return false;
6599 nsAutoCString spec;
6600 nsresult rv = aPrincipal->GetAsciiSpec(spec);
6601 NS_ENSURE_SUCCESS(rv, false);
6602 return spec.EqualsLiteral("resource://pdf.js/web/viewer.html");
6605 bool nsContentUtils::IsPDFJS(JSContext* aCx, JSObject*) {
6606 return IsPDFJS(SubjectPrincipal(aCx));
6609 already_AddRefed<nsIDocumentLoaderFactory>
6610 nsContentUtils::FindInternalContentViewer(const nsACString& aType,
6611 ContentViewerType* aLoaderType) {
6612 if (aLoaderType) {
6613 *aLoaderType = TYPE_UNSUPPORTED;
6616 // one helper factory, please
6617 nsCOMPtr<nsICategoryManager> catMan(
6618 do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
6619 if (!catMan) return nullptr;
6621 nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
6623 nsCString contractID;
6624 nsresult rv =
6625 catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, contractID);
6626 if (NS_SUCCEEDED(rv)) {
6627 docFactory = do_GetService(contractID.get());
6628 if (docFactory && aLoaderType) {
6629 if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
6630 *aLoaderType = TYPE_CONTENT;
6631 else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
6632 *aLoaderType = TYPE_FALLBACK;
6633 else
6634 *aLoaderType = TYPE_UNKNOWN;
6636 return docFactory.forget();
6639 if (DecoderTraits::IsSupportedInVideoDocument(aType)) {
6640 docFactory =
6641 do_GetService("@mozilla.org/content/document-loader-factory;1");
6642 if (docFactory && aLoaderType) {
6643 *aLoaderType = TYPE_CONTENT;
6645 return docFactory.forget();
6648 return nullptr;
6651 static void ReportPatternCompileFailure(nsAString& aPattern,
6652 const Document* aDocument,
6653 JS::MutableHandle<JS::Value> error,
6654 JSContext* cx) {
6655 JS::AutoSaveExceptionState savedExc(cx);
6656 JS::RootedObject exnObj(cx, &error.toObject());
6657 JS::RootedValue messageVal(cx);
6658 if (!JS_GetProperty(cx, exnObj, "message", &messageVal)) {
6659 return;
6661 JS::RootedString messageStr(cx, messageVal.toString());
6662 MOZ_ASSERT(messageStr);
6664 AutoTArray<nsString, 2> strings;
6665 strings.AppendElement(aPattern);
6666 if (!AssignJSString(cx, *strings.AppendElement(), messageStr)) {
6667 return;
6670 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns,
6671 aDocument, nsContentUtils::eDOM_PROPERTIES,
6672 "PatternAttributeCompileFailure", strings);
6673 savedExc.drop();
6676 // static
6677 Maybe<bool> nsContentUtils::IsPatternMatching(nsAString& aValue,
6678 nsAString& aPattern,
6679 const Document* aDocument) {
6680 NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
6682 // The fact that we're using a JS regexp under the hood should not be visible
6683 // to things like window onerror handlers, so we don't initialize our JSAPI
6684 // with the document's window (which may not exist anyway).
6685 AutoJSAPI jsapi;
6686 jsapi.Init();
6687 JSContext* cx = jsapi.cx();
6688 AutoDisableJSInterruptCallback disabler(cx);
6690 // We can use the junk scope here, because we're just using it for regexp
6691 // evaluation, not actual script execution, and we disable statics so that the
6692 // evaluation does not interact with the execution global.
6693 JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
6695 // Check if the pattern by itself is valid first, and not that it only becomes
6696 // valid once we add ^(?: and )$.
6697 JS::RootedValue error(cx);
6698 if (!JS::CheckRegExpSyntax(
6699 cx, static_cast<char16_t*>(aPattern.BeginWriting()),
6700 aPattern.Length(), JS::RegExpFlag::Unicode, &error)) {
6701 return Nothing();
6704 if (!error.isUndefined()) {
6705 ReportPatternCompileFailure(aPattern, aDocument, &error, cx);
6706 return Some(true);
6709 // The pattern has to match the entire value.
6710 aPattern.InsertLiteral(u"^(?:", 0);
6711 aPattern.AppendLiteral(")$");
6713 JS::Rooted<JSObject*> re(
6715 JS::NewUCRegExpObject(cx, static_cast<char16_t*>(aPattern.BeginWriting()),
6716 aPattern.Length(), JS::RegExpFlag::Unicode));
6717 if (!re) {
6718 return Nothing();
6721 JS::Rooted<JS::Value> rval(cx, JS::NullValue());
6722 size_t idx = 0;
6723 if (!JS::ExecuteRegExpNoStatics(cx, re,
6724 static_cast<char16_t*>(aValue.BeginWriting()),
6725 aValue.Length(), &idx, true, &rval)) {
6726 return Nothing();
6729 return Some(!rval.isNull());
6732 // static
6733 nsresult nsContentUtils::URIInheritsSecurityContext(nsIURI* aURI,
6734 bool* aResult) {
6735 // Note: about:blank URIs do NOT inherit the security context from the
6736 // current document, which is what this function tests for...
6737 return NS_URIChainHasFlags(
6738 aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, aResult);
6741 // static
6742 bool nsContentUtils::ChannelShouldInheritPrincipal(
6743 nsIPrincipal* aLoadingPrincipal, nsIURI* aURI, bool aInheritForAboutBlank,
6744 bool aForceInherit) {
6745 MOZ_ASSERT(aLoadingPrincipal,
6746 "Can not check inheritance without a principal");
6748 // Only tell the channel to inherit if it can't provide its own security
6749 // context.
6751 // XXX: If this is ever changed, check all callers for what owners
6752 // they're passing in. In particular, see the code and
6753 // comments in nsDocShell::LoadURI where we fall back on
6754 // inheriting the owner if called from chrome. That would be
6755 // very wrong if this code changed anything but channels that
6756 // can't provide their own security context!
6758 // If aForceInherit is true, we will inherit, even for a channel that
6759 // can provide its own security context. This is used for srcdoc loads.
6760 bool inherit = aForceInherit;
6761 if (!inherit) {
6762 bool uriInherits;
6763 // We expect URIInheritsSecurityContext to return success for an
6764 // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
6765 // This condition needs to match the one in nsDocShell::InternalLoad where
6766 // we're checking for things that will use the owner.
6767 inherit =
6768 (NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &uriInherits)) &&
6769 (uriInherits || (aInheritForAboutBlank && NS_IsAboutBlank(aURI)))) ||
6771 // file: uri special-casing
6773 // If this is a file: load opened from another file: then it may need
6774 // to inherit the owner from the referrer so they can script each other.
6775 // If we don't set the owner explicitly then each file: gets an owner
6776 // based on its own codebase later.
6778 (URIIsLocalFile(aURI) &&
6779 NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, false)) &&
6780 // One more check here. CheckMayLoad will always return true for the
6781 // system principal, but we do NOT want to inherit in that case.
6782 !aLoadingPrincipal->IsSystemPrincipal());
6784 return inherit;
6787 /* static */
6788 bool nsContentUtils::IsCutCopyAllowed(Document* aDocument,
6789 nsIPrincipal& aSubjectPrincipal) {
6790 if (StaticPrefs::dom_allow_cut_copy() && aDocument &&
6791 aDocument->HasValidTransientUserGestureActivation()) {
6792 return true;
6795 return PrincipalHasPermission(aSubjectPrincipal, nsGkAtoms::clipboardWrite);
6798 /* static */
6799 bool nsContentUtils::HaveEqualPrincipals(Document* aDoc1, Document* aDoc2) {
6800 if (!aDoc1 || !aDoc2) {
6801 return false;
6803 bool principalsEqual = false;
6804 aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
6805 return principalsEqual;
6808 /* static */
6809 bool nsContentUtils::HasPluginWithUncontrolledEventDispatch(
6810 nsIContent* aContent) {
6811 return false;
6814 /* static */
6815 void nsContentUtils::FireMutationEventsForDirectParsing(
6816 Document* aDoc, nsIContent* aDest, int32_t aOldChildCount) {
6817 // Fire mutation events. Optimize for the case when there are no listeners
6818 int32_t newChildCount = aDest->GetChildCount();
6819 if (newChildCount && nsContentUtils::HasMutationListeners(
6820 aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
6821 AutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
6822 NS_ASSERTION(newChildCount - aOldChildCount >= 0,
6823 "What, some unexpected dom mutation has happened?");
6824 childNodes.SetCapacity(newChildCount - aOldChildCount);
6825 for (nsIContent* child = aDest->GetFirstChild(); child;
6826 child = child->GetNextSibling()) {
6827 childNodes.AppendElement(child);
6829 FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
6833 /* static */
6834 Document* nsContentUtils::GetRootDocument(Document* aDoc) {
6835 if (!aDoc) {
6836 return nullptr;
6838 Document* doc = aDoc;
6839 while (doc->GetInProcessParentDocument()) {
6840 doc = doc->GetInProcessParentDocument();
6842 return doc;
6845 // static
6846 int32_t nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
6847 int32_t aOffset) {
6848 // The structure of the anonymous frames within a text control frame is
6849 // an optional block frame, followed by an optional br frame.
6851 // If the offset frame has a child, then this frame is the block which
6852 // has the text frames (containing the content) as its children. This will
6853 // be the case if we click to the right of any of the text frames, or at the
6854 // bottom of the text area.
6855 nsIFrame* firstChild = aOffsetFrame->PrincipalChildList().FirstChild();
6856 if (firstChild) {
6857 // In this case, the passed-in offset is incorrect, and we want the length
6858 // of the entire content in the text control frame.
6859 return firstChild->GetContent()->Length();
6862 if (aOffsetFrame->GetPrevSibling() && !aOffsetFrame->GetNextSibling()) {
6863 // In this case, we're actually within the last frame, which is a br
6864 // frame. Our offset should therefore be the length of the first child of
6865 // our parent.
6866 int32_t aOutOffset = aOffsetFrame->GetParent()
6867 ->PrincipalChildList()
6868 .FirstChild()
6869 ->GetContent()
6870 ->Length();
6871 return aOutOffset;
6874 // Otherwise, we're within one of the text frames, in which case our offset
6875 // has already been correctly calculated.
6876 return aOffset;
6879 // static
6880 void nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
6881 Element* aRoot,
6882 uint32_t& aOutStartOffset,
6883 uint32_t& aOutEndOffset) {
6884 MOZ_ASSERT(aSelection && aRoot);
6886 // We don't care which end of this selection is anchor and which is focus. In
6887 // fact, we explicitly want to know which is the _start_ and which is the
6888 // _end_, not anchor vs focus.
6889 const nsRange* range = aSelection->GetAnchorFocusRange();
6890 if (!range) {
6891 // Nothing selected
6892 aOutStartOffset = aOutEndOffset = 0;
6893 return;
6896 // All the node pointers here are raw pointers for performance. We shouldn't
6897 // be doing anything in this function that invalidates the node tree.
6898 nsINode* startContainer = range->GetStartContainer();
6899 uint32_t startOffset = range->StartOffset();
6900 nsINode* endContainer = range->GetEndContainer();
6901 uint32_t endOffset = range->EndOffset();
6903 // We have at most two children, consisting of an optional text node followed
6904 // by an optional <br>.
6905 NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
6906 nsIContent* firstChild = aRoot->GetFirstChild();
6907 #ifdef DEBUG
6908 nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
6909 NS_ASSERTION(startContainer == aRoot || startContainer == firstChild ||
6910 startContainer == lastChild,
6911 "Unexpected startContainer");
6912 NS_ASSERTION(endContainer == aRoot || endContainer == firstChild ||
6913 endContainer == lastChild,
6914 "Unexpected endContainer");
6915 // firstChild is either text or a <br> (hence an element).
6916 MOZ_ASSERT_IF(firstChild, firstChild->IsText() || firstChild->IsElement());
6917 #endif
6918 // Testing IsElement() is faster than testing IsNodeOfType(), since it's
6919 // non-virtual.
6920 if (!firstChild || firstChild->IsElement()) {
6921 // No text node, so everything is 0
6922 startOffset = endOffset = 0;
6923 } else {
6924 // First child is text. If the start/end is already in the text node,
6925 // or the start of the root node, no change needed. If it's in the root
6926 // node but not the start, or in the trailing <br>, we need to set the
6927 // offset to the end.
6928 if ((startContainer == aRoot && startOffset != 0) ||
6929 (startContainer != aRoot && startContainer != firstChild)) {
6930 startOffset = firstChild->Length();
6932 if ((endContainer == aRoot && endOffset != 0) ||
6933 (endContainer != aRoot && endContainer != firstChild)) {
6934 endOffset = firstChild->Length();
6938 MOZ_ASSERT(startOffset <= endOffset);
6939 aOutStartOffset = startOffset;
6940 aOutEndOffset = endOffset;
6943 // static
6944 HTMLEditor* nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext) {
6945 if (!aPresContext) {
6946 return nullptr;
6948 return GetHTMLEditor(aPresContext->GetDocShell());
6951 // static
6952 HTMLEditor* nsContentUtils::GetHTMLEditor(nsDocShell* aDocShell) {
6953 bool isEditable;
6954 if (!aDocShell || NS_FAILED(aDocShell->GetEditable(&isEditable)) ||
6955 !isEditable) {
6956 return nullptr;
6958 return aDocShell->GetHTMLEditor();
6961 // static
6962 TextEditor* nsContentUtils::GetActiveEditor(nsPresContext* aPresContext) {
6963 if (!aPresContext) {
6964 return nullptr;
6967 return GetActiveEditor(aPresContext->Document()->GetWindow());
6970 // static
6971 TextEditor* nsContentUtils::GetActiveEditor(nsPIDOMWindowOuter* aWindow) {
6972 if (!aWindow || !aWindow->GetExtantDoc()) {
6973 return nullptr;
6976 // If it's in designMode, nobody can have focus. Therefore, the HTMLEditor
6977 // handles all events. I.e., it's focused editor in this case.
6978 if (aWindow->GetExtantDoc()->HasFlag(NODE_IS_EDITABLE)) {
6979 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
6982 // If focused element is associated with TextEditor, it must be <input>
6983 // element or <textarea> element. Let's return it even if it's in a
6984 // contenteditable element.
6985 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
6986 if (Element* focusedElement = nsFocusManager::GetFocusedDescendant(
6987 aWindow, nsFocusManager::SearchRange::eOnlyCurrentWindow,
6988 getter_AddRefs(focusedWindow))) {
6989 if (TextEditor* textEditor = focusedElement->GetTextEditorInternal()) {
6990 return textEditor;
6994 // Otherwise, HTMLEditor may handle inputs even non-editable element has
6995 // focus or nobody has focus.
6996 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
6999 // static
7000 TextEditor* nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(
7001 nsIContent* aAnonymousContent) {
7002 if (!aAnonymousContent) {
7003 return nullptr;
7005 nsIContent* parent = aAnonymousContent->FindFirstNonChromeOnlyAccessContent();
7006 if (!parent || parent == aAnonymousContent) {
7007 return nullptr;
7009 if (HTMLInputElement* inputElement =
7010 HTMLInputElement::FromNodeOrNull(parent)) {
7011 return inputElement->GetTextEditorWithoutCreation();
7013 if (HTMLTextAreaElement* textareaElement =
7014 HTMLTextAreaElement::FromNodeOrNull(parent)) {
7015 return textareaElement->GetTextEditorWithoutCreation();
7017 return nullptr;
7020 // static
7021 bool nsContentUtils::IsNodeInEditableRegion(nsINode* aNode) {
7022 while (aNode) {
7023 if (aNode->IsEditable()) {
7024 return true;
7026 aNode = aNode->GetParent();
7028 return false;
7031 // static
7032 bool nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader) {
7033 if (IsForbiddenSystemRequestHeader(aHeader)) {
7034 return true;
7037 return StringBeginsWith(aHeader, "proxy-"_ns,
7038 nsCaseInsensitiveCStringComparator) ||
7039 StringBeginsWith(aHeader, "sec-"_ns,
7040 nsCaseInsensitiveCStringComparator);
7043 // static
7044 bool nsContentUtils::IsForbiddenSystemRequestHeader(const nsACString& aHeader) {
7045 static const char* kInvalidHeaders[] = {"accept-charset",
7046 "accept-encoding",
7047 "access-control-request-headers",
7048 "access-control-request-method",
7049 "connection",
7050 "content-length",
7051 "cookie",
7052 "cookie2",
7053 "date",
7054 "dnt",
7055 "expect",
7056 "host",
7057 "keep-alive",
7058 "origin",
7059 "referer",
7060 "te",
7061 "trailer",
7062 "transfer-encoding",
7063 "upgrade",
7064 "via"};
7065 for (auto& kInvalidHeader : kInvalidHeaders) {
7066 if (aHeader.LowerCaseEqualsASCII(kInvalidHeader)) {
7067 return true;
7070 return false;
7073 // static
7074 bool nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader) {
7075 return (aHeader.LowerCaseEqualsASCII("set-cookie") ||
7076 aHeader.LowerCaseEqualsASCII("set-cookie2"));
7079 // static
7080 bool nsContentUtils::IsCorsUnsafeRequestHeaderValue(
7081 const nsACString& aHeaderValue) {
7082 const char* cur = aHeaderValue.BeginReading();
7083 const char* end = aHeaderValue.EndReading();
7085 while (cur != end) {
7086 // Implementation of
7087 // https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte Is less
7088 // than a space but not a horizontal tab
7089 if ((*cur < ' ' && *cur != '\t') || *cur == '"' || *cur == '(' ||
7090 *cur == ')' || *cur == ':' || *cur == '<' || *cur == '>' ||
7091 *cur == '?' || *cur == '@' || *cur == '[' || *cur == '\\' ||
7092 *cur == ']' || *cur == '{' || *cur == '}' ||
7093 *cur == 0x7F) { // 0x75 is DEL
7094 return true;
7096 cur++;
7098 return false;
7101 // static
7102 bool nsContentUtils::IsAllowedNonCorsAccept(const nsACString& aHeaderValue) {
7103 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7104 return false;
7106 return true;
7109 // static
7110 bool nsContentUtils::IsAllowedNonCorsContentType(
7111 const nsACString& aHeaderValue) {
7112 nsAutoCString contentType;
7113 nsAutoCString unused;
7115 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7116 return false;
7119 nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
7120 if (NS_FAILED(rv)) {
7121 return false;
7124 return contentType.LowerCaseEqualsLiteral("text/plain") ||
7125 contentType.LowerCaseEqualsLiteral(
7126 "application/x-www-form-urlencoded") ||
7127 contentType.LowerCaseEqualsLiteral("multipart/form-data");
7130 // static
7131 bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
7132 const char* cur = aHeaderValue.BeginReading();
7133 const char* end = aHeaderValue.EndReading();
7135 while (cur != end) {
7136 if ((*cur >= '0' && *cur <= '9') || (*cur >= 'A' && *cur <= 'Z') ||
7137 (*cur >= 'a' && *cur <= 'z') || *cur == ' ' || *cur == '*' ||
7138 *cur == ',' || *cur == '-' || *cur == '.' || *cur == ';' ||
7139 *cur == '=') {
7140 cur++;
7141 continue;
7143 return false;
7145 return true;
7148 // static
7149 bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
7150 const nsACString& aValue) {
7151 // see https://fetch.spec.whatwg.org/#cors-safelisted-request-header
7152 if (aValue.Length() > 128) {
7153 return false;
7155 return (aName.LowerCaseEqualsLiteral("accept") &&
7156 nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
7157 (aName.LowerCaseEqualsLiteral("accept-language") &&
7158 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7159 (aName.LowerCaseEqualsLiteral("content-language") &&
7160 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7161 (aName.LowerCaseEqualsLiteral("content-type") &&
7162 nsContentUtils::IsAllowedNonCorsContentType(aValue));
7165 mozilla::LogModule* nsContentUtils::DOMDumpLog() { return sDOMDumpLog; }
7167 bool nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep,
7168 nsAString& aResult,
7169 const fallible_t& aFallible) {
7170 aResult.Truncate();
7171 return AppendNodeTextContent(aNode, aDeep, aResult, aFallible);
7174 void nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep,
7175 nsAString& aResult) {
7176 if (!GetNodeTextContent(aNode, aDeep, aResult, fallible)) {
7177 NS_ABORT_OOM(0); // Unfortunately we don't know the allocation size
7181 void nsContentUtils::DestroyMatchString(void* aData) {
7182 if (aData) {
7183 nsString* matchString = static_cast<nsString*>(aData);
7184 delete matchString;
7188 bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
7189 // Table ordered from most to least likely JS MIME types.
7190 static const char* jsTypes[] = {"text/javascript",
7191 "text/ecmascript",
7192 "application/javascript",
7193 "application/ecmascript",
7194 "application/x-javascript",
7195 "application/x-ecmascript",
7196 "text/javascript1.0",
7197 "text/javascript1.1",
7198 "text/javascript1.2",
7199 "text/javascript1.3",
7200 "text/javascript1.4",
7201 "text/javascript1.5",
7202 "text/jscript",
7203 "text/livescript",
7204 "text/x-ecmascript",
7205 "text/x-javascript",
7206 nullptr};
7208 for (uint32_t i = 0; jsTypes[i]; ++i) {
7209 if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
7210 return true;
7214 return false;
7217 nsresult nsContentUtils::GenerateUUIDInPlace(nsID& aUUID) {
7218 MOZ_ASSERT(sUUIDGenerator);
7220 nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&aUUID);
7221 if (NS_WARN_IF(NS_FAILED(rv))) {
7222 return rv;
7225 return NS_OK;
7228 nsID nsContentUtils::GenerateUUID() {
7229 MOZ_DIAGNOSTIC_ASSERT(sUUIDGenerator);
7231 nsID uuid;
7232 nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&uuid);
7233 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
7235 return uuid;
7238 bool nsContentUtils::PrefetchPreloadEnabled(nsIDocShell* aDocShell) {
7240 // SECURITY CHECK: disable prefetching and preloading from mailnews!
7242 // walk up the docshell tree to see if any containing
7243 // docshell are of type MAIL.
7246 if (!aDocShell) {
7247 return false;
7250 nsCOMPtr<nsIDocShell> docshell = aDocShell;
7251 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7253 do {
7254 auto appType = docshell->GetAppType();
7255 if (appType == nsIDocShell::APP_TYPE_MAIL) {
7256 return false; // do not prefetch, preload, preconnect from mailnews
7259 docshell->GetInProcessParent(getter_AddRefs(parentItem));
7260 if (parentItem) {
7261 docshell = do_QueryInterface(parentItem);
7262 if (!docshell) {
7263 NS_ERROR("cannot get a docshell from a treeItem!");
7264 return false;
7267 } while (parentItem);
7269 return true;
7272 uint64_t nsContentUtils::GetInnerWindowID(nsIRequest* aRequest) {
7273 // can't do anything if there's no nsIRequest!
7274 if (!aRequest) {
7275 return 0;
7278 nsCOMPtr<nsILoadGroup> loadGroup;
7279 nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
7281 if (NS_FAILED(rv) || !loadGroup) {
7282 return 0;
7285 return GetInnerWindowID(loadGroup);
7288 uint64_t nsContentUtils::GetInnerWindowID(nsILoadGroup* aLoadGroup) {
7289 if (!aLoadGroup) {
7290 return 0;
7293 nsCOMPtr<nsIInterfaceRequestor> callbacks;
7294 nsresult rv = aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
7295 if (NS_FAILED(rv) || !callbacks) {
7296 return 0;
7299 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
7300 if (!loadContext) {
7301 return 0;
7304 nsCOMPtr<mozIDOMWindowProxy> window;
7305 rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
7306 if (NS_FAILED(rv) || !window) {
7307 return 0;
7310 auto* pwindow = nsPIDOMWindowOuter::From(window);
7311 if (!pwindow) {
7312 return 0;
7315 nsPIDOMWindowInner* inner = pwindow->GetCurrentInnerWindow();
7316 return inner ? inner->WindowID() : 0;
7319 // static
7320 void nsContentUtils::MaybeFixIPv6Host(nsACString& aHost) {
7321 if (aHost.FindChar(':') != -1) { // Escape IPv6 address
7322 MOZ_ASSERT(!aHost.Length() ||
7323 (aHost[0] != '[' && aHost[aHost.Length() - 1] != ']'));
7324 aHost.Insert('[', 0);
7325 aHost.Append(']');
7329 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7330 nsACString& aHost) {
7331 aHost.Truncate();
7332 nsresult rv = aURI->GetHost(aHost);
7333 if (NS_FAILED(rv)) { // Some URIs do not have a host
7334 return rv;
7337 MaybeFixIPv6Host(aHost);
7339 return NS_OK;
7342 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7343 nsAString& aHost) {
7344 nsAutoCString hostname;
7345 nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname);
7346 if (NS_FAILED(rv)) {
7347 return rv;
7349 CopyUTF8toUTF16(hostname, aHost);
7350 return NS_OK;
7353 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIPrincipal* aPrincipal,
7354 nsACString& aHost) {
7355 nsresult rv = aPrincipal->GetAsciiHost(aHost);
7356 if (NS_FAILED(rv)) { // Some URIs do not have a host
7357 return rv;
7360 MaybeFixIPv6Host(aHost);
7361 return NS_OK;
7364 CallState nsContentUtils::CallOnAllRemoteChildren(
7365 MessageBroadcaster* aManager,
7366 const std::function<CallState(BrowserParent*)>& aCallback) {
7367 uint32_t browserChildCount = aManager->ChildCount();
7368 for (uint32_t j = 0; j < browserChildCount; ++j) {
7369 RefPtr<MessageListenerManager> childMM = aManager->GetChildAt(j);
7370 if (!childMM) {
7371 continue;
7374 RefPtr<MessageBroadcaster> nonLeafMM = MessageBroadcaster::From(childMM);
7375 if (nonLeafMM) {
7376 if (CallOnAllRemoteChildren(nonLeafMM, aCallback) == CallState::Stop) {
7377 return CallState::Stop;
7379 continue;
7382 mozilla::dom::ipc::MessageManagerCallback* cb = childMM->GetCallback();
7383 if (cb) {
7384 nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
7385 BrowserParent* remote = BrowserParent::GetFrom(fl);
7386 if (remote && aCallback) {
7387 if (aCallback(remote) == CallState::Stop) {
7388 return CallState::Stop;
7394 return CallState::Continue;
7397 void nsContentUtils::CallOnAllRemoteChildren(
7398 nsPIDOMWindowOuter* aWindow,
7399 const std::function<CallState(BrowserParent*)>& aCallback) {
7400 nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(aWindow);
7401 if (window->IsChromeWindow()) {
7402 RefPtr<MessageBroadcaster> windowMM = window->GetMessageManager();
7403 if (windowMM) {
7404 CallOnAllRemoteChildren(windowMM, aCallback);
7409 struct UIStateChangeInfo {
7410 UIStateChangeType mShowFocusRings;
7412 explicit UIStateChangeInfo(UIStateChangeType aShowFocusRings)
7413 : mShowFocusRings(aShowFocusRings) {}
7416 void nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(
7417 nsPIDOMWindowOuter* aWindow, UIStateChangeType aShowFocusRings) {
7418 UIStateChangeInfo stateInfo(aShowFocusRings);
7419 CallOnAllRemoteChildren(aWindow, [&stateInfo](BrowserParent* aBrowserParent) {
7420 Unused << aBrowserParent->SendSetKeyboardIndicators(
7421 stateInfo.mShowFocusRings);
7422 return CallState::Continue;
7426 nsresult nsContentUtils::IPCTransferableToTransferable(
7427 const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
7428 nsIPrincipal* aRequestingPrincipal,
7429 const nsContentPolicyType& aContentPolicyType,
7430 nsITransferable* aTransferable, mozilla::dom::ContentParent* aContentParent,
7431 mozilla::dom::BrowserChild* aBrowserChild) {
7432 nsresult rv;
7434 aTransferable->SetIsPrivateData(aIsPrivateData);
7436 const nsTArray<IPCDataTransferItem>& items = aDataTransfer.items();
7437 for (const auto& item : items) {
7438 aTransferable->AddDataFlavor(item.flavor().get());
7440 if (item.data().type() == IPCDataTransferData::TnsString) {
7441 nsCOMPtr<nsISupportsString> dataWrapper =
7442 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
7443 NS_ENSURE_SUCCESS(rv, rv);
7445 const nsString& text = item.data().get_nsString();
7446 rv = dataWrapper->SetData(text);
7447 NS_ENSURE_SUCCESS(rv, rv);
7449 rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper);
7451 NS_ENSURE_SUCCESS(rv, rv);
7452 } else if (item.data().type() == IPCDataTransferData::TShmem) {
7453 if (nsContentUtils::IsFlavorImage(item.flavor())) {
7454 nsCOMPtr<imgIContainer> imageContainer;
7455 rv = nsContentUtils::DataTransferItemToImage(
7456 item, getter_AddRefs(imageContainer));
7457 NS_ENSURE_SUCCESS(rv, rv);
7459 aTransferable->SetTransferData(item.flavor().get(), imageContainer);
7460 } else {
7461 nsCOMPtr<nsISupportsCString> dataWrapper =
7462 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
7463 NS_ENSURE_SUCCESS(rv, rv);
7465 // The buffer contains the terminating null.
7466 Shmem itemData = item.data().get_Shmem();
7467 const nsDependentCSubstring text(itemData.get<char>(),
7468 itemData.Size<char>());
7469 rv = dataWrapper->SetData(text);
7470 NS_ENSURE_SUCCESS(rv, rv);
7472 rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper);
7474 NS_ENSURE_SUCCESS(rv, rv);
7477 if (aContentParent) {
7478 Unused << aContentParent->DeallocShmem(item.data().get_Shmem());
7479 } else if (aBrowserChild) {
7480 Unused << aBrowserChild->DeallocShmem(item.data().get_Shmem());
7485 aTransferable->SetRequestingPrincipal(aRequestingPrincipal);
7486 aTransferable->SetContentPolicyType(aContentPolicyType);
7487 return NS_OK;
7490 void nsContentUtils::TransferablesToIPCTransferables(
7491 nsIArray* aTransferables, nsTArray<IPCDataTransfer>& aIPC,
7492 bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
7493 mozilla::dom::ContentParent* aParent) {
7494 aIPC.Clear();
7495 if (aTransferables) {
7496 uint32_t transferableCount = 0;
7497 aTransferables->GetLength(&transferableCount);
7498 for (uint32_t i = 0; i < transferableCount; ++i) {
7499 IPCDataTransfer* dt = aIPC.AppendElement();
7500 nsCOMPtr<nsITransferable> transferable =
7501 do_QueryElementAt(aTransferables, i);
7502 TransferableToIPCTransferable(transferable, dt, aInSyncMessage, aChild,
7503 aParent);
7508 nsresult nsContentUtils::SlurpFileToString(nsIFile* aFile,
7509 nsACString& aString) {
7510 aString.Truncate();
7512 nsCOMPtr<nsIURI> fileURI;
7513 nsresult rv = NS_NewFileURI(getter_AddRefs(fileURI), aFile);
7514 if (NS_FAILED(rv)) {
7515 return rv;
7518 nsCOMPtr<nsIChannel> channel;
7519 rv = NS_NewChannel(getter_AddRefs(channel), fileURI,
7520 nsContentUtils::GetSystemPrincipal(),
7521 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
7522 nsIContentPolicy::TYPE_OTHER);
7523 if (NS_FAILED(rv)) {
7524 return rv;
7527 nsCOMPtr<nsIInputStream> stream;
7528 rv = channel->Open(getter_AddRefs(stream));
7529 if (NS_FAILED(rv)) {
7530 return rv;
7533 rv = NS_ConsumeStream(stream, UINT32_MAX, aString);
7534 if (NS_FAILED(rv)) {
7535 return rv;
7538 rv = stream->Close();
7539 if (NS_FAILED(rv)) {
7540 return rv;
7543 return NS_OK;
7546 bool nsContentUtils::IsFileImage(nsIFile* aFile, nsACString& aType) {
7547 nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1");
7548 if (!mime) {
7549 return false;
7552 nsresult rv = mime->GetTypeFromFile(aFile, aType);
7553 if (NS_FAILED(rv)) {
7554 return false;
7557 return StringBeginsWith(aType, "image/"_ns);
7560 nsresult nsContentUtils::CalculateBufferSizeForImage(
7561 const uint32_t& aStride, const IntSize& aImageSize,
7562 const SurfaceFormat& aFormat, size_t* aMaxBufferSize,
7563 size_t* aUsedBufferSize) {
7564 CheckedInt32 requiredBytes =
7565 CheckedInt32(aStride) * CheckedInt32(aImageSize.height);
7567 CheckedInt32 usedBytes =
7568 requiredBytes - aStride +
7569 (CheckedInt32(aImageSize.width) * BytesPerPixel(aFormat));
7570 if (!usedBytes.isValid()) {
7571 return NS_ERROR_FAILURE;
7574 MOZ_ASSERT(requiredBytes.isValid(), "usedBytes valid but not required?");
7575 *aMaxBufferSize = requiredBytes.value();
7576 *aUsedBufferSize = usedBytes.value();
7577 return NS_OK;
7580 nsresult nsContentUtils::DataTransferItemToImage(
7581 const IPCDataTransferItem& aItem, imgIContainer** aContainer) {
7582 MOZ_ASSERT(aItem.data().type() == IPCDataTransferData::TShmem);
7583 MOZ_ASSERT(IsFlavorImage(aItem.flavor()));
7585 const IPCDataTransferImage& imageDetails = aItem.imageDetails();
7586 const IntSize size(imageDetails.width(), imageDetails.height());
7587 if (!size.width || !size.height) {
7588 return NS_ERROR_FAILURE;
7591 Shmem data = aItem.data().get_Shmem();
7593 // Validate shared memory buffer size
7594 size_t imageBufLen = 0;
7595 size_t maxBufLen = 0;
7596 nsresult rv = CalculateBufferSizeForImage(imageDetails.stride(), size,
7597 imageDetails.format(), &maxBufLen,
7598 &imageBufLen);
7599 if (NS_FAILED(rv)) {
7600 return rv;
7602 if (imageBufLen > data.Size<uint8_t>()) {
7603 return NS_ERROR_FAILURE;
7606 RefPtr<DataSourceSurface> image = CreateDataSourceSurfaceFromData(
7607 size, imageDetails.format(), data.get<uint8_t>(), imageDetails.stride());
7609 RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size);
7610 nsCOMPtr<imgIContainer> imageContainer =
7611 image::ImageOps::CreateFromDrawable(drawable);
7612 imageContainer.forget(aContainer);
7614 return NS_OK;
7617 bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
7618 return aFlavor.EqualsLiteral(kNativeImageMime) ||
7619 aFlavor.EqualsLiteral(kJPEGImageMime) ||
7620 aFlavor.EqualsLiteral(kJPGImageMime) ||
7621 aFlavor.EqualsLiteral(kPNGImageMime) ||
7622 aFlavor.EqualsLiteral(kGIFImageMime);
7625 static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
7626 mozilla::dom::ContentParent* aParent,
7627 const nsACString& aInput) {
7628 MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
7630 IShmemAllocator* allocator = aChild ? static_cast<IShmemAllocator*>(aChild)
7631 : static_cast<IShmemAllocator*>(aParent);
7633 Shmem result;
7634 if (!allocator->AllocShmem(aInput.Length(), SharedMemory::TYPE_BASIC,
7635 &result)) {
7636 return result;
7639 memcpy(result.get<char>(), aInput.BeginReading(), aInput.Length());
7641 return result;
7644 void nsContentUtils::TransferableToIPCTransferable(
7645 nsITransferable* aTransferable, IPCDataTransfer* aIPCDataTransfer,
7646 bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
7647 mozilla::dom::ContentParent* aParent) {
7648 MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
7650 if (aTransferable) {
7651 nsTArray<nsCString> flavorList;
7652 aTransferable->FlavorsTransferableCanExport(flavorList);
7654 for (uint32_t j = 0; j < flavorList.Length(); ++j) {
7655 nsCString& flavorStr = flavorList[j];
7656 if (!flavorStr.Length()) {
7657 continue;
7660 nsCOMPtr<nsISupports> data;
7661 nsresult rv =
7662 aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data));
7664 if (NS_FAILED(rv) || !data) {
7665 if (aInSyncMessage) {
7666 // Can't do anything.
7667 continue;
7670 // This is a hack to support kFilePromiseMime.
7671 // On Windows there just needs to be an entry for it,
7672 // and for OSX we need to create
7673 // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
7674 if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
7675 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7676 item->flavor() = flavorStr;
7677 item->data() = NS_ConvertUTF8toUTF16(flavorStr);
7678 continue;
7681 // Empty element, transfer only the flavor
7682 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7683 item->flavor() = flavorStr;
7684 item->data() = nsString();
7685 continue;
7688 if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(data)) {
7689 nsAutoString dataAsString;
7690 text->GetData(dataAsString);
7691 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7692 item->flavor() = flavorStr;
7693 item->data() = dataAsString;
7694 } else if (nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data)) {
7695 nsAutoCString dataAsString;
7696 ctext->GetData(dataAsString);
7698 Shmem dataAsShmem = ConvertToShmem(aChild, aParent, dataAsString);
7699 if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
7700 continue;
7703 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7704 item->flavor() = flavorStr;
7705 item->data() = std::move(dataAsShmem);
7706 } else if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
7707 // Images to be pasted on the clipboard are nsIInputStreams
7708 nsCString imageData;
7709 NS_ConsumeStream(stream, UINT32_MAX, imageData);
7711 Shmem imageDataShmem = ConvertToShmem(aChild, aParent, imageData);
7712 if (!imageDataShmem.IsReadable() || !imageDataShmem.Size<char>()) {
7713 continue;
7716 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7717 item->flavor() = flavorStr;
7718 item->data() = std::move(imageDataShmem);
7719 } else if (nsCOMPtr<imgIContainer> image = do_QueryInterface(data)) {
7720 // Images to be placed on the clipboard are imgIContainers.
7721 RefPtr<mozilla::gfx::SourceSurface> surface = image->GetFrame(
7722 imgIContainer::FRAME_CURRENT,
7723 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
7724 if (!surface) {
7725 continue;
7727 RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
7728 surface->GetDataSurface();
7729 if (!dataSurface) {
7730 continue;
7732 size_t length;
7733 int32_t stride;
7734 IShmemAllocator* allocator =
7735 aChild ? static_cast<IShmemAllocator*>(aChild)
7736 : static_cast<IShmemAllocator*>(aParent);
7737 Maybe<Shmem> surfaceData =
7738 GetSurfaceData(dataSurface, &length, &stride, allocator);
7740 if (surfaceData.isNothing()) {
7741 continue;
7744 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7745 item->flavor() = flavorStr;
7746 // Turn item->data() into an nsCString prior to accessing it.
7747 item->data() = std::move(surfaceData.ref());
7749 IPCDataTransferImage& imageDetails = item->imageDetails();
7750 mozilla::gfx::IntSize size = dataSurface->GetSize();
7751 imageDetails.width() = size.width;
7752 imageDetails.height() = size.height;
7753 imageDetails.stride() = stride;
7754 imageDetails.format() = dataSurface->GetFormat();
7755 } else {
7756 // Otherwise, handle this as a file.
7757 nsCOMPtr<BlobImpl> blobImpl;
7758 if (nsCOMPtr<nsIFile> file = do_QueryInterface(data)) {
7759 // If we can send this over as a blob, do so. Otherwise, we're
7760 // responding to a sync message and the child can't process the blob
7761 // constructor before processing our response, which would crash. In
7762 // that case, hope that the caller is nsClipboardProxy::GetData,
7763 // called from editor and send over images as raw data.
7764 if (aInSyncMessage) {
7765 nsAutoCString type;
7766 if (IsFileImage(file, type)) {
7767 nsAutoCString data;
7768 SlurpFileToString(file, data);
7770 Shmem dataAsShmem = ConvertToShmem(aChild, aParent, data);
7771 if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
7772 continue;
7775 IPCDataTransferItem* item =
7776 aIPCDataTransfer->items().AppendElement();
7777 item->flavor() = type;
7778 item->data() = std::move(dataAsShmem);
7781 continue;
7784 if (aParent) {
7785 bool isDir = false;
7786 if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
7787 nsAutoString path;
7788 if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
7789 continue;
7792 RefPtr<FileSystemSecurity> fss =
7793 FileSystemSecurity::GetOrCreate();
7794 fss->GrantAccessToContentProcess(aParent->ChildID(), path);
7798 blobImpl = new FileBlobImpl(file);
7800 IgnoredErrorResult rv;
7802 // Ensure that file data is cached no that the content process
7803 // has this data available to it when passed over:
7804 blobImpl->GetSize(rv);
7805 if (NS_WARN_IF(rv.Failed())) {
7806 continue;
7809 blobImpl->GetLastModified(rv);
7810 if (NS_WARN_IF(rv.Failed())) {
7811 continue;
7813 } else {
7814 if (aInSyncMessage) {
7815 // Can't do anything.
7816 continue;
7818 blobImpl = do_QueryInterface(data);
7820 if (blobImpl) {
7821 IPCDataTransferData data;
7822 IPCBlob ipcBlob;
7824 // If we failed to create the blob actor, then this blob probably
7825 // can't get the file size for the underlying file, ignore it for
7826 // now. TODO pass this through anyway.
7827 if (aChild) {
7828 nsresult rv = IPCBlobUtils::Serialize(blobImpl, aChild, ipcBlob);
7829 if (NS_WARN_IF(NS_FAILED(rv))) {
7830 continue;
7833 data = ipcBlob;
7834 } else if (aParent) {
7835 nsresult rv = IPCBlobUtils::Serialize(blobImpl, aParent, ipcBlob);
7836 if (NS_WARN_IF(NS_FAILED(rv))) {
7837 continue;
7840 data = ipcBlob;
7843 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7844 item->flavor() = flavorStr;
7845 item->data() = data;
7852 namespace {
7853 // The default type used for calling GetSurfaceData(). Gets surface data as
7854 // raw buffer.
7855 struct GetSurfaceDataRawBuffer {
7856 using ReturnType = mozilla::UniquePtr<char[]>;
7857 using BufferType = char*;
7859 ReturnType Allocate(size_t aSize) { return ReturnType(new char[aSize]); }
7861 static BufferType GetBuffer(const ReturnType& aReturnValue) {
7862 return aReturnValue.get();
7865 static ReturnType NullValue() { return ReturnType(); }
7868 // The type used for calling GetSurfaceData() that allocates and writes to
7869 // a shared memory buffer.
7870 struct GetSurfaceDataShmem {
7871 using ReturnType = Maybe<Shmem>;
7872 using BufferType = char*;
7874 explicit GetSurfaceDataShmem(IShmemAllocator* aAllocator)
7875 : mAllocator(aAllocator) {}
7877 ReturnType Allocate(size_t aSize) {
7878 Shmem shmem;
7879 if (!mAllocator->AllocShmem(aSize, SharedMemory::TYPE_BASIC, &shmem)) {
7880 return Nothing();
7883 return Some(shmem);
7886 static BufferType GetBuffer(const ReturnType& aReturnValue) {
7887 return aReturnValue.isSome() ? aReturnValue.ref().get<char>() : nullptr;
7890 static ReturnType NullValue() { return ReturnType(); }
7892 private:
7893 IShmemAllocator* mAllocator;
7897 * Get the pixel data from the given source surface and return it as a buffer.
7898 * The length and stride will be assigned from the surface.
7900 template <typename GetSurfaceDataContext = GetSurfaceDataRawBuffer>
7901 typename GetSurfaceDataContext::ReturnType GetSurfaceDataImpl(
7902 mozilla::gfx::DataSourceSurface* aSurface, size_t* aLength,
7903 int32_t* aStride,
7904 GetSurfaceDataContext aContext = GetSurfaceDataContext()) {
7905 mozilla::gfx::DataSourceSurface::MappedSurface map;
7906 if (!aSurface->Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map)) {
7907 return GetSurfaceDataContext::NullValue();
7910 size_t bufLen = 0;
7911 size_t maxBufLen = 0;
7912 nsresult rv = nsContentUtils::CalculateBufferSizeForImage(
7913 map.mStride, aSurface->GetSize(), aSurface->GetFormat(), &maxBufLen,
7914 &bufLen);
7915 if (NS_FAILED(rv)) {
7916 aSurface->Unmap();
7917 return GetSurfaceDataContext::NullValue();
7920 // nsDependentCString wants null-terminated string.
7921 typename GetSurfaceDataContext::ReturnType surfaceData =
7922 aContext.Allocate(maxBufLen + 1);
7923 if (GetSurfaceDataContext::GetBuffer(surfaceData)) {
7924 memcpy(GetSurfaceDataContext::GetBuffer(surfaceData),
7925 reinterpret_cast<char*>(map.mData), bufLen);
7926 memset(GetSurfaceDataContext::GetBuffer(surfaceData) + bufLen, 0,
7927 maxBufLen - bufLen + 1);
7930 *aLength = maxBufLen;
7931 *aStride = map.mStride;
7933 aSurface->Unmap();
7934 return surfaceData;
7936 } // Anonymous namespace.
7938 mozilla::UniquePtr<char[]> nsContentUtils::GetSurfaceData(
7939 NotNull<mozilla::gfx::DataSourceSurface*> aSurface, size_t* aLength,
7940 int32_t* aStride) {
7941 return GetSurfaceDataImpl(aSurface, aLength, aStride);
7944 Maybe<Shmem> nsContentUtils::GetSurfaceData(
7945 mozilla::gfx::DataSourceSurface* aSurface, size_t* aLength,
7946 int32_t* aStride, IShmemAllocator* aAllocator) {
7947 return GetSurfaceDataImpl(aSurface, aLength, aStride,
7948 GetSurfaceDataShmem(aAllocator));
7951 mozilla::Modifiers nsContentUtils::GetWidgetModifiers(int32_t aModifiers) {
7952 Modifiers result = 0;
7953 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SHIFT) {
7954 result |= mozilla::MODIFIER_SHIFT;
7956 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CONTROL) {
7957 result |= mozilla::MODIFIER_CONTROL;
7959 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALT) {
7960 result |= mozilla::MODIFIER_ALT;
7962 if (aModifiers & nsIDOMWindowUtils::MODIFIER_META) {
7963 result |= mozilla::MODIFIER_META;
7965 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALTGRAPH) {
7966 result |= mozilla::MODIFIER_ALTGRAPH;
7968 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CAPSLOCK) {
7969 result |= mozilla::MODIFIER_CAPSLOCK;
7971 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FN) {
7972 result |= mozilla::MODIFIER_FN;
7974 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FNLOCK) {
7975 result |= mozilla::MODIFIER_FNLOCK;
7977 if (aModifiers & nsIDOMWindowUtils::MODIFIER_NUMLOCK) {
7978 result |= mozilla::MODIFIER_NUMLOCK;
7980 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SCROLLLOCK) {
7981 result |= mozilla::MODIFIER_SCROLLLOCK;
7983 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOL) {
7984 result |= mozilla::MODIFIER_SYMBOL;
7986 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK) {
7987 result |= mozilla::MODIFIER_SYMBOLLOCK;
7989 if (aModifiers & nsIDOMWindowUtils::MODIFIER_OS) {
7990 result |= mozilla::MODIFIER_OS;
7992 return result;
7995 nsIWidget* nsContentUtils::GetWidget(PresShell* aPresShell, nsPoint* aOffset) {
7996 if (!aPresShell) {
7997 return nullptr;
7999 nsIFrame* frame = aPresShell->GetRootFrame();
8000 if (!frame) {
8001 return nullptr;
8003 return frame->GetView()->GetNearestWidget(aOffset);
8006 int16_t nsContentUtils::GetButtonsFlagForButton(int32_t aButton) {
8007 switch (aButton) {
8008 case -1:
8009 return MouseButtonsFlag::eNoButtons;
8010 case MouseButton::ePrimary:
8011 return MouseButtonsFlag::ePrimaryFlag;
8012 case MouseButton::eMiddle:
8013 return MouseButtonsFlag::eMiddleFlag;
8014 case MouseButton::eSecondary:
8015 return MouseButtonsFlag::eSecondaryFlag;
8016 case 3:
8017 return MouseButtonsFlag::e4thFlag;
8018 case 4:
8019 return MouseButtonsFlag::e5thFlag;
8020 default:
8021 NS_ERROR("Button not known.");
8022 return 0;
8026 LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(
8027 const CSSPoint& aPoint, const nsPoint& aOffset,
8028 nsPresContext* aPresContext) {
8029 nsPoint layoutRelative = CSSPoint::ToAppUnits(aPoint) + aOffset;
8030 nsPoint visualRelative =
8031 ViewportUtils::LayoutToVisual(layoutRelative, aPresContext->PresShell());
8032 return LayoutDeviceIntPoint::FromAppUnitsRounded(
8033 visualRelative, aPresContext->AppUnitsPerDevPixel());
8036 nsView* nsContentUtils::GetViewToDispatchEvent(nsPresContext* aPresContext,
8037 PresShell** aPresShell) {
8038 if (!aPresContext || !aPresShell) {
8039 return nullptr;
8041 RefPtr<PresShell> presShell = aPresContext->PresShell();
8042 if (NS_WARN_IF(!presShell)) {
8043 *aPresShell = nullptr;
8044 return nullptr;
8046 nsViewManager* viewManager = presShell->GetViewManager();
8047 if (!viewManager) {
8048 presShell.forget(aPresShell); // XXX Is this intentional?
8049 return nullptr;
8051 presShell.forget(aPresShell);
8052 return viewManager->GetRootView();
8055 nsresult nsContentUtils::SendMouseEvent(
8056 mozilla::PresShell* aPresShell, const nsAString& aType, float aX, float aY,
8057 int32_t aButton, int32_t aButtons, int32_t aClickCount, int32_t aModifiers,
8058 bool aIgnoreRootScrollFrame, float aPressure,
8059 unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
8060 bool* aPreventDefault, bool aIsDOMEventSynthesized,
8061 bool aIsWidgetEventSynthesized) {
8062 nsPoint offset;
8063 nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
8064 if (!widget) return NS_ERROR_FAILURE;
8066 EventMessage msg;
8067 Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
8068 bool contextMenuKey = false;
8069 if (aType.EqualsLiteral("mousedown")) {
8070 msg = eMouseDown;
8071 } else if (aType.EqualsLiteral("mouseup")) {
8072 msg = eMouseUp;
8073 } else if (aType.EqualsLiteral("mousemove")) {
8074 msg = eMouseMove;
8075 } else if (aType.EqualsLiteral("mouseover")) {
8076 msg = eMouseEnterIntoWidget;
8077 } else if (aType.EqualsLiteral("mouseout")) {
8078 msg = eMouseExitFromWidget;
8079 exitFrom = Some(WidgetMouseEvent::ePlatformChild);
8080 } else if (aType.EqualsLiteral("mousecancel")) {
8081 msg = eMouseExitFromWidget;
8082 exitFrom = Some(XRE_IsParentProcess() ? WidgetMouseEvent::ePlatformTopLevel
8083 : WidgetMouseEvent::ePuppet);
8084 } else if (aType.EqualsLiteral("mouselongtap")) {
8085 msg = eMouseLongTap;
8086 } else if (aType.EqualsLiteral("contextmenu")) {
8087 msg = eContextMenu;
8088 contextMenuKey = (aButton == 0);
8089 } else if (aType.EqualsLiteral("MozMouseHittest")) {
8090 msg = eMouseHitTest;
8091 } else {
8092 return NS_ERROR_FAILURE;
8095 if (aInputSourceArg == MouseEvent_Binding::MOZ_SOURCE_UNKNOWN) {
8096 aInputSourceArg = MouseEvent_Binding::MOZ_SOURCE_MOUSE;
8099 WidgetMouseEvent event(true, msg, widget,
8100 aIsWidgetEventSynthesized
8101 ? WidgetMouseEvent::eSynthesized
8102 : WidgetMouseEvent::eReal,
8103 contextMenuKey ? WidgetMouseEvent::eContextMenuKey
8104 : WidgetMouseEvent::eNormal);
8105 event.pointerId = aIdentifier;
8106 event.mModifiers = GetWidgetModifiers(aModifiers);
8107 event.mButton = aButton;
8108 event.mButtons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED
8109 ? aButtons
8110 : msg == eMouseUp ? 0
8111 : GetButtonsFlagForButton(aButton);
8112 event.mPressure = aPressure;
8113 event.mInputSource = aInputSourceArg;
8114 event.mClickCount = aClickCount;
8115 event.mTime = PR_IntervalNow();
8116 event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
8117 event.mExitFrom = exitFrom;
8119 nsPresContext* presContext = aPresShell->GetPresContext();
8120 if (!presContext) return NS_ERROR_FAILURE;
8122 event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
8123 event.mIgnoreRootScrollFrame = aIgnoreRootScrollFrame;
8125 nsEventStatus status = nsEventStatus_eIgnore;
8126 if (aToWindow) {
8127 RefPtr<PresShell> presShell;
8128 nsView* view =
8129 GetViewToDispatchEvent(presContext, getter_AddRefs(presShell));
8130 if (!presShell || !view) {
8131 return NS_ERROR_FAILURE;
8133 return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
8135 if (StaticPrefs::test_events_async_enabled()) {
8136 status = widget->DispatchInputEvent(&event).mContentStatus;
8137 } else {
8138 nsresult rv = widget->DispatchEvent(&event, status);
8139 NS_ENSURE_SUCCESS(rv, rv);
8141 if (aPreventDefault) {
8142 *aPreventDefault = (status == nsEventStatus_eConsumeNoDefault);
8145 return NS_OK;
8148 /* static */
8149 void nsContentUtils::FirePageHideEventForFrameLoaderSwap(
8150 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8151 bool aOnlySystemGroup) {
8152 MOZ_DIAGNOSTIC_ASSERT(aItem);
8153 MOZ_DIAGNOSTIC_ASSERT(aChromeEventHandler);
8155 RefPtr<Document> doc = aItem->GetDocument();
8156 NS_ASSERTION(doc, "What happened here?");
8157 doc->OnPageHide(true, aChromeEventHandler, aOnlySystemGroup);
8159 int32_t childCount = 0;
8160 aItem->GetInProcessChildCount(&childCount);
8161 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8162 kids.AppendElements(childCount);
8163 for (int32_t i = 0; i < childCount; ++i) {
8164 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8167 for (uint32_t i = 0; i < kids.Length(); ++i) {
8168 if (kids[i]) {
8169 FirePageHideEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8170 aOnlySystemGroup);
8175 // The pageshow event is fired for a given document only if IsShowing() returns
8176 // the same thing as aFireIfShowing. This gives us a way to fire pageshow only
8177 // on documents that are still loading or only on documents that are already
8178 // loaded.
8179 /* static */
8180 void nsContentUtils::FirePageShowEventForFrameLoaderSwap(
8181 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8182 bool aFireIfShowing, bool aOnlySystemGroup) {
8183 int32_t childCount = 0;
8184 aItem->GetInProcessChildCount(&childCount);
8185 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8186 kids.AppendElements(childCount);
8187 for (int32_t i = 0; i < childCount; ++i) {
8188 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8191 for (uint32_t i = 0; i < kids.Length(); ++i) {
8192 if (kids[i]) {
8193 FirePageShowEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8194 aFireIfShowing, aOnlySystemGroup);
8198 RefPtr<Document> doc = aItem->GetDocument();
8199 NS_ASSERTION(doc, "What happened here?");
8200 if (doc->IsShowing() == aFireIfShowing) {
8201 doc->OnPageShow(true, aChromeEventHandler, aOnlySystemGroup);
8205 /* static */
8206 already_AddRefed<nsPIWindowRoot> nsContentUtils::GetWindowRoot(Document* aDoc) {
8207 if (aDoc) {
8208 if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
8209 return win->GetTopWindowRoot();
8212 return nullptr;
8215 /* static */
8216 bool nsContentUtils::IsPreloadType(nsContentPolicyType aType) {
8217 return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
8218 aType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD ||
8219 aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
8220 aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD ||
8221 aType == nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD ||
8222 aType == nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD);
8225 /* static */
8226 bool nsContentUtils::IsUpgradableDisplayType(ExtContentPolicyType aType) {
8227 MOZ_ASSERT(NS_IsMainThread());
8228 return (aType == ExtContentPolicy::TYPE_IMAGE ||
8229 aType == ExtContentPolicy::TYPE_MEDIA);
8232 // static
8233 ReferrerPolicy nsContentUtils::GetReferrerPolicyFromChannel(
8234 nsIChannel* aChannel) {
8235 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
8236 if (!httpChannel) {
8237 return ReferrerPolicy::_empty;
8240 nsresult rv;
8241 nsAutoCString headerValue;
8242 rv = httpChannel->GetResponseHeader("referrer-policy"_ns, headerValue);
8243 if (NS_FAILED(rv) || headerValue.IsEmpty()) {
8244 return ReferrerPolicy::_empty;
8247 return ReferrerInfo::ReferrerPolicyFromHeaderString(
8248 NS_ConvertUTF8toUTF16(headerValue));
8251 // static
8252 bool nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel) {
8253 nsLoadFlags loadFlags = 0;
8254 aChannel->GetLoadFlags(&loadFlags);
8255 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
8256 return true;
8259 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
8260 nsContentPolicyType type = loadInfo->InternalContentPolicyType();
8261 return IsNonSubresourceInternalPolicyType(type);
8264 // static
8265 bool nsContentUtils::IsNonSubresourceInternalPolicyType(
8266 nsContentPolicyType aType) {
8267 return aType == nsIContentPolicy::TYPE_DOCUMENT ||
8268 aType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
8269 aType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
8270 aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
8271 aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
8274 // static public
8275 bool nsContentUtils::IsThirdPartyWindowOrChannel(nsPIDOMWindowInner* aWindow,
8276 nsIChannel* aChannel,
8277 nsIURI* aURI) {
8278 MOZ_ASSERT(!aWindow || !aChannel,
8279 "A window and channel should not both be provided.");
8281 ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
8282 if (!thirdPartyUtil) {
8283 return false;
8286 // In the absence of a window or channel, we assume that we are first-party.
8287 bool thirdParty = false;
8289 if (aWindow) {
8290 nsresult rv = thirdPartyUtil->IsThirdPartyWindow(aWindow->GetOuterWindow(),
8291 aURI, &thirdParty);
8292 if (NS_FAILED(rv)) {
8293 // Ideally we would do something similar to the channel code path here,
8294 // but existing code depends on this behaviour.
8295 return false;
8299 if (aChannel) {
8300 // Note, we must call IsThirdPartyChannel() here and not just try to
8301 // use nsILoadInfo.isThirdPartyContext. That nsILoadInfo property only
8302 // indicates if the parent loading window is third party or not. We
8303 // want to check the channel URI against the loading principal as well.
8304 nsresult rv =
8305 thirdPartyUtil->IsThirdPartyChannel(aChannel, nullptr, &thirdParty);
8306 if (NS_FAILED(rv)) {
8307 // Assume third-party in case of failure
8308 thirdParty = true;
8311 // We check isThirdPartyWindow to expand the list of domains that are
8312 // considered first party (e.g., if facebook.com includes an iframe from
8313 // fatratgames.com, all subsources included in that iframe are considered
8314 // third-party with isThirdPartyChannel, even if they are not third-party
8315 // w.r.t. facebook.com), and isThirdPartyChannel to prevent top-level
8316 // navigations from being detected as third-party.
8317 bool isThirdPartyWindow = true;
8318 nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
8319 if (NS_SUCCEEDED(rv) && chan) {
8320 nsCOMPtr<nsIURI> topWinURI;
8321 rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
8322 if (NS_SUCCEEDED(rv) && topWinURI) {
8323 rv = thirdPartyUtil->IsThirdPartyURI(aURI, topWinURI,
8324 &isThirdPartyWindow);
8325 if (NS_SUCCEEDED(rv)) {
8326 thirdParty = thirdParty && isThirdPartyWindow;
8332 return thirdParty;
8335 // static public
8336 bool nsContentUtils::IsThirdPartyTrackingResourceWindow(
8337 nsPIDOMWindowInner* aWindow) {
8338 MOZ_ASSERT(aWindow);
8340 Document* document = aWindow->GetExtantDoc();
8341 if (!document) {
8342 return false;
8345 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8346 do_QueryInterface(document->GetChannel());
8347 if (!classifiedChannel) {
8348 return false;
8351 return classifiedChannel->IsThirdPartyTrackingResource();
8354 // static public
8355 bool nsContentUtils::IsFirstPartyTrackingResourceWindow(
8356 nsPIDOMWindowInner* aWindow) {
8357 MOZ_ASSERT(aWindow);
8359 Document* document = aWindow->GetExtantDoc();
8360 if (!document) {
8361 return false;
8364 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8365 do_QueryInterface(document->GetChannel());
8366 if (!classifiedChannel) {
8367 return false;
8370 uint32_t classificationFlags =
8371 classifiedChannel->GetFirstPartyClassificationFlags();
8373 return mozilla::net::UrlClassifierCommon::IsTrackingClassificationFlag(
8374 classificationFlags);
8377 namespace {
8379 // We put StringBuilder in the anonymous namespace to prevent anything outside
8380 // this file from accidentally being linked against it.
8381 class BulkAppender {
8382 typedef typename nsAString::size_type size_type;
8384 public:
8385 explicit BulkAppender(BulkWriteHandle<char16_t>&& aHandle)
8386 : mHandle(std::move(aHandle)), mPosition(0) {}
8387 ~BulkAppender() = default;
8389 template <int N>
8390 void AppendLiteral(const char16_t (&aStr)[N]) {
8391 size_t len = N - 1;
8392 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8393 memcpy(mHandle.Elements() + mPosition, aStr, len * sizeof(char16_t));
8394 mPosition += len;
8397 void Append(Span<const char16_t> aStr) {
8398 size_t len = aStr.Length();
8399 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8400 // Both mHandle.Elements() and aStr.Elements() are guaranteed
8401 // to be non-null (by the string implementation and by Span,
8402 // respectively), so not checking the pointers for null before
8403 // memcpy does not lead to UB even if len was zero.
8404 memcpy(mHandle.Elements() + mPosition, aStr.Elements(),
8405 len * sizeof(char16_t));
8406 mPosition += len;
8409 void Append(Span<const char> aStr) {
8410 size_t len = aStr.Length();
8411 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8412 ConvertLatin1toUtf16(aStr, mHandle.AsSpan().From(mPosition));
8413 mPosition += len;
8416 void Finish() { mHandle.Finish(mPosition, false); }
8418 private:
8419 mozilla::BulkWriteHandle<char16_t> mHandle;
8420 size_type mPosition;
8423 class StringBuilder {
8424 private:
8425 // Try to keep the size of StringBuilder close to a jemalloc bucket size.
8426 static const uint32_t STRING_BUFFER_UNITS = 1020;
8427 class Unit {
8428 public:
8429 Unit() : mAtom(nullptr), mType(eUnknown), mLength(0) {
8430 MOZ_COUNT_CTOR(StringBuilder::Unit);
8432 ~Unit() {
8433 if (mType == eString || mType == eStringWithEncode) {
8434 delete mString;
8436 MOZ_COUNT_DTOR(StringBuilder::Unit);
8439 enum Type {
8440 eUnknown,
8441 eAtom,
8442 eString,
8443 eStringWithEncode,
8444 eLiteral,
8445 eTextFragment,
8446 eTextFragmentWithEncode,
8449 union {
8450 nsAtom* mAtom;
8451 const char16_t* mLiteral;
8452 nsAutoString* mString;
8453 const nsTextFragment* mTextFragment;
8455 Type mType;
8456 uint32_t mLength;
8459 public:
8460 StringBuilder() : mLast(this), mLength(0) { MOZ_COUNT_CTOR(StringBuilder); }
8462 MOZ_COUNTED_DTOR(StringBuilder)
8464 void Append(nsAtom* aAtom) {
8465 Unit* u = AddUnit();
8466 u->mAtom = aAtom;
8467 u->mType = Unit::eAtom;
8468 uint32_t len = aAtom->GetLength();
8469 u->mLength = len;
8470 mLength += len;
8473 template <int N>
8474 void Append(const char16_t (&aLiteral)[N]) {
8475 Unit* u = AddUnit();
8476 u->mLiteral = aLiteral;
8477 u->mType = Unit::eLiteral;
8478 uint32_t len = N - 1;
8479 u->mLength = len;
8480 mLength += len;
8483 void Append(const nsAString& aString) {
8484 Unit* u = AddUnit();
8485 u->mString = new nsAutoString(aString);
8486 u->mType = Unit::eString;
8487 uint32_t len = aString.Length();
8488 u->mLength = len;
8489 mLength += len;
8492 void Append(nsAutoString* aString) {
8493 Unit* u = AddUnit();
8494 u->mString = aString;
8495 u->mType = Unit::eString;
8496 uint32_t len = aString->Length();
8497 u->mLength = len;
8498 mLength += len;
8501 void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen) {
8502 Unit* u = AddUnit();
8503 u->mString = aString;
8504 u->mType = Unit::eStringWithEncode;
8505 u->mLength = aLen;
8506 mLength += aLen;
8509 void Append(const nsTextFragment* aTextFragment) {
8510 Unit* u = AddUnit();
8511 u->mTextFragment = aTextFragment;
8512 u->mType = Unit::eTextFragment;
8513 uint32_t len = aTextFragment->GetLength();
8514 u->mLength = len;
8515 mLength += len;
8518 void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen) {
8519 Unit* u = AddUnit();
8520 u->mTextFragment = aTextFragment;
8521 u->mType = Unit::eTextFragmentWithEncode;
8522 u->mLength = aLen;
8523 mLength += aLen;
8526 bool ToString(nsAString& aOut) {
8527 if (!mLength.isValid()) {
8528 return false;
8530 auto appenderOrErr = aOut.BulkWrite(mLength.value(), 0, true);
8531 if (appenderOrErr.isErr()) {
8532 return false;
8535 BulkAppender appender{appenderOrErr.unwrap()};
8537 for (StringBuilder* current = this; current;
8538 current = current->mNext.get()) {
8539 uint32_t len = current->mUnits.Length();
8540 for (uint32_t i = 0; i < len; ++i) {
8541 Unit& u = current->mUnits[i];
8542 switch (u.mType) {
8543 case Unit::eAtom:
8544 appender.Append(*(u.mAtom));
8545 break;
8546 case Unit::eString:
8547 appender.Append(*(u.mString));
8548 break;
8549 case Unit::eStringWithEncode:
8550 EncodeAttrString(*(u.mString), appender);
8551 break;
8552 case Unit::eLiteral:
8553 appender.Append(Span(u.mLiteral, u.mLength));
8554 break;
8555 case Unit::eTextFragment:
8556 if (u.mTextFragment->Is2b()) {
8557 appender.Append(
8558 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()));
8559 } else {
8560 appender.Append(
8561 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()));
8563 break;
8564 case Unit::eTextFragmentWithEncode:
8565 if (u.mTextFragment->Is2b()) {
8566 EncodeTextFragment(
8567 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()),
8568 appender);
8569 } else {
8570 EncodeTextFragment(
8571 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()),
8572 appender);
8574 break;
8575 default:
8576 MOZ_CRASH("Unknown unit type?");
8580 appender.Finish();
8581 return true;
8584 private:
8585 Unit* AddUnit() {
8586 if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
8587 new StringBuilder(this);
8589 return mLast->mUnits.AppendElement();
8592 explicit StringBuilder(StringBuilder* aFirst) : mLast(nullptr), mLength(0) {
8593 MOZ_COUNT_CTOR(StringBuilder);
8594 aFirst->mLast->mNext = WrapUnique(this);
8595 aFirst->mLast = this;
8598 void EncodeAttrString(Span<const char16_t> aStr, BulkAppender& aAppender) {
8599 size_t flushedUntil = 0;
8600 size_t currentPosition = 0;
8601 for (char16_t c : aStr) {
8602 switch (c) {
8603 case '"':
8604 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8605 aAppender.AppendLiteral(u"&quot;");
8606 flushedUntil = currentPosition + 1;
8607 break;
8608 case '&':
8609 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8610 aAppender.AppendLiteral(u"&amp;");
8611 flushedUntil = currentPosition + 1;
8612 break;
8613 case 0x00A0:
8614 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8615 aAppender.AppendLiteral(u"&nbsp;");
8616 flushedUntil = currentPosition + 1;
8617 break;
8618 default:
8619 break;
8621 currentPosition++;
8623 if (currentPosition > flushedUntil) {
8624 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8628 template <class T>
8629 void EncodeTextFragment(Span<const T> aStr, BulkAppender& aAppender) {
8630 size_t flushedUntil = 0;
8631 size_t currentPosition = 0;
8632 for (T c : aStr) {
8633 switch (c) {
8634 case '<':
8635 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8636 aAppender.AppendLiteral(u"&lt;");
8637 flushedUntil = currentPosition + 1;
8638 break;
8639 case '>':
8640 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8641 aAppender.AppendLiteral(u"&gt;");
8642 flushedUntil = currentPosition + 1;
8643 break;
8644 case '&':
8645 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8646 aAppender.AppendLiteral(u"&amp;");
8647 flushedUntil = currentPosition + 1;
8648 break;
8649 case T(0xA0):
8650 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8651 aAppender.AppendLiteral(u"&nbsp;");
8652 flushedUntil = currentPosition + 1;
8653 break;
8654 default:
8655 break;
8657 currentPosition++;
8659 if (currentPosition > flushedUntil) {
8660 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8664 AutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
8665 mozilla::UniquePtr<StringBuilder> mNext;
8666 StringBuilder* mLast;
8667 // mLength is used only in the first StringBuilder object in the linked list.
8668 CheckedInt<uint32_t> mLength;
8671 } // namespace
8673 static void AppendEncodedCharacters(const nsTextFragment* aText,
8674 StringBuilder& aBuilder) {
8675 uint32_t extraSpaceNeeded = 0;
8676 uint32_t len = aText->GetLength();
8677 if (aText->Is2b()) {
8678 const char16_t* data = aText->Get2b();
8679 for (uint32_t i = 0; i < len; ++i) {
8680 const char16_t c = data[i];
8681 switch (c) {
8682 case '<':
8683 extraSpaceNeeded += ArrayLength("&lt;") - 2;
8684 break;
8685 case '>':
8686 extraSpaceNeeded += ArrayLength("&gt;") - 2;
8687 break;
8688 case '&':
8689 extraSpaceNeeded += ArrayLength("&amp;") - 2;
8690 break;
8691 case 0x00A0:
8692 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
8693 break;
8694 default:
8695 break;
8698 } else {
8699 const char* data = aText->Get1b();
8700 for (uint32_t i = 0; i < len; ++i) {
8701 const unsigned char c = data[i];
8702 switch (c) {
8703 case '<':
8704 extraSpaceNeeded += ArrayLength("&lt;") - 2;
8705 break;
8706 case '>':
8707 extraSpaceNeeded += ArrayLength("&gt;") - 2;
8708 break;
8709 case '&':
8710 extraSpaceNeeded += ArrayLength("&amp;") - 2;
8711 break;
8712 case 0x00A0:
8713 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
8714 break;
8715 default:
8716 break;
8721 if (extraSpaceNeeded) {
8722 aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
8723 } else {
8724 aBuilder.Append(aText);
8728 static void AppendEncodedAttributeValue(nsAutoString* aValue,
8729 StringBuilder& aBuilder) {
8730 const char16_t* c = aValue->BeginReading();
8731 const char16_t* end = aValue->EndReading();
8733 uint32_t extraSpaceNeeded = 0;
8734 while (c < end) {
8735 switch (*c) {
8736 case '"':
8737 extraSpaceNeeded += ArrayLength("&quot;") - 2;
8738 break;
8739 case '&':
8740 extraSpaceNeeded += ArrayLength("&amp;") - 2;
8741 break;
8742 case 0x00A0:
8743 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
8744 break;
8745 default:
8746 break;
8748 ++c;
8751 if (extraSpaceNeeded) {
8752 aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
8753 } else {
8754 aBuilder.Append(aValue);
8758 static void StartElement(Element* aContent, StringBuilder& aBuilder) {
8759 nsAtom* localName = aContent->NodeInfo()->NameAtom();
8760 int32_t tagNS = aContent->GetNameSpaceID();
8762 aBuilder.Append(u"<");
8763 if (aContent->IsHTMLElement() || aContent->IsSVGElement() ||
8764 aContent->IsMathMLElement()) {
8765 aBuilder.Append(localName);
8766 } else {
8767 aBuilder.Append(aContent->NodeName());
8770 CustomElementData* ceData = aContent->GetCustomElementData();
8771 if (ceData) {
8772 nsAtom* isAttr = ceData->GetIs(aContent);
8773 if (isAttr && !aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
8774 aBuilder.Append(uR"( is=")");
8775 aBuilder.Append(nsDependentAtomString(isAttr));
8776 aBuilder.Append(uR"(")");
8780 int32_t count = aContent->GetAttrCount();
8781 for (int32_t i = 0; i < count; i++) {
8782 const nsAttrName* name = aContent->GetAttrNameAt(i);
8783 int32_t attNs = name->NamespaceID();
8784 nsAtom* attName = name->LocalName();
8786 // Filter out any attribute starting with [-|_]moz
8787 nsDependentAtomString attrNameStr(attName);
8788 if (StringBeginsWith(attrNameStr, u"_moz"_ns) ||
8789 StringBeginsWith(attrNameStr, u"-moz"_ns)) {
8790 continue;
8793 auto* attValue = new nsAutoString();
8794 aContent->GetAttr(attNs, attName, *attValue);
8796 // Filter out special case of <br type="_moz*"> used by the editor.
8797 // Bug 16988. Yuck.
8798 if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
8799 attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
8800 StringBeginsWith(*attValue, u"_moz"_ns)) {
8801 delete attValue;
8802 continue;
8805 aBuilder.Append(u" ");
8807 if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
8808 (attNs == kNameSpaceID_XMLNS && attName == nsGkAtoms::xmlns)) {
8809 // Nothing else required
8810 } else if (attNs == kNameSpaceID_XML) {
8811 aBuilder.Append(u"xml:");
8812 } else if (attNs == kNameSpaceID_XMLNS) {
8813 aBuilder.Append(u"xmlns:");
8814 } else if (attNs == kNameSpaceID_XLink) {
8815 aBuilder.Append(u"xlink:");
8816 } else {
8817 nsAtom* prefix = name->GetPrefix();
8818 if (prefix) {
8819 aBuilder.Append(prefix);
8820 aBuilder.Append(u":");
8824 aBuilder.Append(attName);
8825 aBuilder.Append(uR"(=")");
8826 AppendEncodedAttributeValue(attValue, aBuilder);
8827 aBuilder.Append(uR"(")");
8830 aBuilder.Append(u">");
8833 // Per HTML spec we should append one \n if the first child of
8834 // pre/textarea/listing is a textnode and starts with a \n.
8835 // But because browsers haven't traditionally had that behavior,
8836 // we're not changing our behavior either - yet.
8837 if (aContent->IsHTMLElement()) {
8838 if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
8839 localName == nsGkAtoms::listing) {
8840 nsIContent* fc = aContent->GetFirstChild();
8841 if (fc &&
8842 (fc->NodeType() == nsINode::TEXT_NODE ||
8843 fc->NodeType() == nsINode::CDATA_SECTION_NODE)) {
8844 const nsTextFragment* text = fc->GetText();
8845 if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
8846 aBuilder.Append("\n");
8853 static inline bool ShouldEscape(nsIContent* aParent) {
8854 if (!aParent || !aParent->IsHTMLElement()) {
8855 return true;
8858 static const nsAtom* nonEscapingElements[] = {
8859 nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
8860 nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
8861 nsGkAtoms::plaintext, nsGkAtoms::noscript};
8862 static mozilla::BitBloomFilter<12, nsAtom> sFilter;
8863 static bool sInitialized = false;
8864 if (!sInitialized) {
8865 sInitialized = true;
8866 for (auto& nonEscapingElement : nonEscapingElements) {
8867 sFilter.add(nonEscapingElement);
8871 nsAtom* tag = aParent->NodeInfo()->NameAtom();
8872 if (sFilter.mightContain(tag)) {
8873 for (auto& nonEscapingElement : nonEscapingElements) {
8874 if (tag == nonEscapingElement) {
8875 if (MOZ_UNLIKELY(tag == nsGkAtoms::noscript) &&
8876 MOZ_UNLIKELY(!aParent->OwnerDoc()->IsScriptEnabled())) {
8877 return true;
8879 return false;
8883 return true;
8886 static inline bool IsVoidTag(Element* aElement) {
8887 if (!aElement->IsHTMLElement()) {
8888 return false;
8890 return FragmentOrElement::IsHTMLVoid(aElement->NodeInfo()->NameAtom());
8893 bool nsContentUtils::SerializeNodeToMarkup(nsINode* aRoot,
8894 bool aDescendentsOnly,
8895 nsAString& aOut) {
8896 // If you pass in a DOCUMENT_NODE, you must pass aDescendentsOnly as true
8897 MOZ_ASSERT(aDescendentsOnly || aRoot->NodeType() != nsINode::DOCUMENT_NODE);
8899 nsINode* current =
8900 aDescendentsOnly ? aRoot->GetFirstChildOfTemplateOrNode() : aRoot;
8902 if (!current) {
8903 return true;
8906 StringBuilder builder;
8907 nsIContent* next;
8908 while (true) {
8909 bool isVoid = false;
8910 switch (current->NodeType()) {
8911 case nsINode::ELEMENT_NODE: {
8912 Element* elem = current->AsElement();
8913 StartElement(elem, builder);
8914 isVoid = IsVoidTag(elem);
8915 if (!isVoid && (next = current->GetFirstChildOfTemplateOrNode())) {
8916 current = next;
8917 continue;
8919 break;
8922 case nsINode::TEXT_NODE:
8923 case nsINode::CDATA_SECTION_NODE: {
8924 const nsTextFragment* text = &current->AsText()->TextFragment();
8925 nsIContent* parent = current->GetParent();
8926 if (ShouldEscape(parent)) {
8927 AppendEncodedCharacters(text, builder);
8928 } else {
8929 builder.Append(text);
8931 break;
8934 case nsINode::COMMENT_NODE: {
8935 builder.Append(u"<!--");
8936 builder.Append(static_cast<nsIContent*>(current)->GetText());
8937 builder.Append(u"-->");
8938 break;
8941 case nsINode::DOCUMENT_TYPE_NODE: {
8942 builder.Append(u"<!DOCTYPE ");
8943 builder.Append(current->NodeName());
8944 builder.Append(u">");
8945 break;
8948 case nsINode::PROCESSING_INSTRUCTION_NODE: {
8949 builder.Append(u"<?");
8950 builder.Append(current->NodeName());
8951 builder.Append(u" ");
8952 builder.Append(static_cast<nsIContent*>(current)->GetText());
8953 builder.Append(u">");
8954 break;
8958 while (true) {
8959 if (!isVoid && current->NodeType() == nsINode::ELEMENT_NODE) {
8960 builder.Append(u"</");
8961 nsIContent* elem = static_cast<nsIContent*>(current);
8962 if (elem->IsHTMLElement() || elem->IsSVGElement() ||
8963 elem->IsMathMLElement()) {
8964 builder.Append(elem->NodeInfo()->NameAtom());
8965 } else {
8966 builder.Append(current->NodeName());
8968 builder.Append(u">");
8970 isVoid = false;
8972 if (current == aRoot) {
8973 return builder.ToString(aOut);
8976 if ((next = current->GetNextSibling())) {
8977 current = next;
8978 break;
8981 current = current->GetParentNode();
8983 // Handle template element. If the parent is a template's content,
8984 // then adjust the parent to be the template element.
8985 if (current != aRoot &&
8986 current->NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
8987 DocumentFragment* frag = static_cast<DocumentFragment*>(current);
8988 nsIContent* fragHost = frag->GetHost();
8989 if (fragHost && fragHost->IsTemplateElement()) {
8990 current = fragHost;
8994 if (aDescendentsOnly && current == aRoot) {
8995 return builder.ToString(aOut);
9001 bool nsContentUtils::IsSpecificAboutPage(JSObject* aGlobal, const char* aUri) {
9002 // aUri must start with about: or this isn't the right function to be using.
9003 MOZ_ASSERT(strncmp(aUri, "about:", 6) == 0);
9005 // Make sure the global is a window
9006 MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
9007 nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
9008 if (!win) {
9009 return false;
9012 nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
9013 NS_ENSURE_TRUE(principal, false);
9015 // First check the scheme to avoid getting long specs in the common case.
9016 if (!principal->SchemeIs("about")) {
9017 return false;
9020 nsAutoCString spec;
9021 principal->GetAsciiSpec(spec);
9023 return spec.EqualsASCII(aUri);
9026 /* static */
9027 void nsContentUtils::SetScrollbarsVisibility(nsIDocShell* aDocShell,
9028 bool aVisible) {
9029 if (!aDocShell) {
9030 return;
9032 auto pref = aVisible ? ScrollbarPreference::Auto : ScrollbarPreference::Never;
9033 nsDocShell::Cast(aDocShell)->SetScrollbarPreference(pref);
9036 /* static */
9037 nsIDocShell* nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget) {
9038 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
9040 if (nsCOMPtr<nsINode> node = do_QueryInterface(aTarget)) {
9041 bool ignore;
9042 innerWindow =
9043 do_QueryInterface(node->OwnerDoc()->GetScriptHandlingObject(ignore));
9044 } else if ((innerWindow = do_QueryInterface(aTarget))) {
9045 // Nothing else to do
9046 } else {
9047 nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(aTarget);
9048 if (helper) {
9049 innerWindow = helper->GetOwner();
9053 if (innerWindow) {
9054 return innerWindow->GetDocShell();
9057 return nullptr;
9061 * Note: this function only relates to figuring out HTTPS state, which is an
9062 * input to the Secure Context algorithm. We are not actually implementing any
9063 * part of the Secure Context algorithm itself here.
9065 * This is a bit of a hack. Ideally we'd propagate HTTPS state through
9066 * nsIChannel as described in the Fetch and HTML specs, but making channels
9067 * know about whether they should inherit HTTPS state, propagating information
9068 * about who the channel's "client" is, exposing GetHttpsState API on channels
9069 * and modifying the various cache implementations to store and retrieve HTTPS
9070 * state involves a huge amount of code (see bug 1220687). We avoid that for
9071 * now using this function.
9073 * This function takes advantage of the observation that we can return true if
9074 * nsIContentSecurityManager::IsOriginPotentiallyTrustworthy returns true for
9075 * the document's origin (e.g. the origin has a scheme of 'https' or host
9076 * 'localhost' etc.). Since we generally propagate a creator document's origin
9077 * onto data:, blob:, etc. documents, this works for them too.
9079 * The scenario where this observation breaks down is sandboxing without the
9080 * 'allow-same-origin' flag, since in this case a document is given a unique
9081 * origin (IsOriginPotentiallyTrustworthy would return false). We handle that
9082 * by using the origin that the document would have had had it not been
9083 * sandboxed.
9085 * DEFICIENCIES: Note that this function uses nsIScriptSecurityManager's
9086 * getChannelResultPrincipalIfNotSandboxed, and that method's ignoring of
9087 * sandboxing is limited to the immediate sandbox. In the case that aDocument
9088 * should inherit its origin (e.g. data: URI) but its parent has ended up
9089 * with a unique origin due to sandboxing further up the parent chain we may
9090 * end up returning false when we would ideally return true (since we will
9091 * examine the parent's origin for 'https' and not finding it.) This means
9092 * that we may restrict the privileges of some pages unnecessarily in this
9093 * edge case.
9095 /* static */
9096 bool nsContentUtils::HttpsStateIsModern(Document* aDocument) {
9097 if (!aDocument) {
9098 return false;
9101 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
9103 if (principal->IsSystemPrincipal()) {
9104 return true;
9107 // If aDocument is sandboxed, try and get the principal that it would have
9108 // been given had it not been sandboxed:
9109 if (principal->GetIsNullPrincipal() &&
9110 (aDocument->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
9111 nsIChannel* channel = aDocument->GetChannel();
9112 if (channel) {
9113 nsCOMPtr<nsIScriptSecurityManager> ssm =
9114 nsContentUtils::GetSecurityManager();
9115 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9116 channel, getter_AddRefs(principal));
9117 if (NS_FAILED(rv)) {
9118 return false;
9120 if (principal->IsSystemPrincipal()) {
9121 // If a document with the system principal is sandboxing a subdocument
9122 // that would normally inherit the embedding element's principal (e.g.
9123 // a srcdoc document) then the embedding document does not trust the
9124 // content that is written to the embedded document. Unlike when the
9125 // embedding document is https, in this case we have no indication as
9126 // to whether the embedded document's contents are delivered securely
9127 // or not, and the sandboxing would possibly indicate that they were
9128 // not. To play it safe we return false here. (See bug 1162772
9129 // comment 73-80.)
9130 return false;
9135 if (principal->GetIsNullPrincipal()) {
9136 return false;
9139 MOZ_ASSERT(principal->GetIsContentPrincipal());
9141 return principal->GetIsOriginPotentiallyTrustworthy();
9144 /* static */
9145 bool nsContentUtils::ComputeIsSecureContext(nsIChannel* aChannel) {
9146 MOZ_ASSERT(aChannel);
9148 nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
9149 nsCOMPtr<nsIPrincipal> principal;
9150 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9151 aChannel, getter_AddRefs(principal));
9152 if (NS_FAILED(rv)) {
9153 return false;
9156 const RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
9158 if (principal->IsSystemPrincipal()) {
9159 // If the load would've been sandboxed, treat this load as an untrusted
9160 // load, as system code considers sandboxed resources insecure.
9161 return !loadInfo->GetLoadingSandboxed();
9164 if (principal->GetIsNullPrincipal()) {
9165 return false;
9168 if (const RefPtr<WindowContext> windowContext =
9169 WindowContext::GetById(loadInfo->GetInnerWindowID())) {
9170 if (!windowContext->GetIsSecureContext()) {
9171 return false;
9175 return principal->GetIsOriginPotentiallyTrustworthy();
9178 /* static */
9179 void nsContentUtils::TryToUpgradeElement(Element* aElement) {
9180 NodeInfo* nodeInfo = aElement->NodeInfo();
9181 RefPtr<nsAtom> typeAtom =
9182 aElement->GetCustomElementData()->GetCustomElementType();
9184 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9185 CustomElementDefinition* definition =
9186 nsContentUtils::LookupCustomElementDefinition(
9187 nodeInfo->GetDocument(), nodeInfo->NameAtom(),
9188 nodeInfo->NamespaceID(), typeAtom);
9189 if (definition) {
9190 nsContentUtils::EnqueueUpgradeReaction(aElement, definition);
9191 } else {
9192 // Add an unresolved custom element that is a candidate for upgrade when a
9193 // custom element is connected to the document.
9194 nsContentUtils::RegisterUnresolvedElement(aElement, typeAtom);
9198 MOZ_CAN_RUN_SCRIPT
9199 static void DoCustomElementCreate(Element** aElement, JSContext* aCx,
9200 Document* aDoc, NodeInfo* aNodeInfo,
9201 CustomElementConstructor* aConstructor,
9202 ErrorResult& aRv) {
9203 JS::Rooted<JS::Value> constructResult(aCx);
9204 aConstructor->Construct(&constructResult, aRv, "Custom Element Create",
9205 CallbackFunction::eRethrowExceptions);
9206 if (aRv.Failed()) {
9207 return;
9210 RefPtr<Element> element;
9211 // constructResult is an ObjectValue because construction with a callback
9212 // always forms the return value from a JSObject.
9213 UNWRAP_OBJECT(Element, &constructResult, element);
9214 if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9215 if (!element || !element->IsHTMLElement()) {
9216 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9217 "HTMLElement");
9218 return;
9220 } else {
9221 if (!element || !element->IsXULElement()) {
9222 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9223 "XULElement");
9224 return;
9228 nsAtom* localName = aNodeInfo->NameAtom();
9230 if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
9231 element->HasChildren() || element->GetAttrCount() ||
9232 element->NodeInfo()->NameAtom() != localName) {
9233 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
9234 return;
9237 element.forget(aElement);
9240 /* static */
9241 nsresult nsContentUtils::NewXULOrHTMLElement(
9242 Element** aResult, mozilla::dom::NodeInfo* aNodeInfo,
9243 FromParser aFromParser, nsAtom* aIsAtom,
9244 mozilla::dom::CustomElementDefinition* aDefinition) {
9245 RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
9246 MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
9247 nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
9248 "Can only create XUL or XHTML elements.");
9250 nsAtom* name = nodeInfo->NameAtom();
9251 int32_t tag = eHTMLTag_unknown;
9252 bool isCustomElementName = false;
9253 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9254 tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
9255 isCustomElementName =
9256 (tag == eHTMLTag_userdefined &&
9257 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML));
9258 } else { // kNameSpaceID_XUL
9259 if (aIsAtom) {
9260 // Make sure the customized built-in element to be constructed confirms
9261 // to our naming requirement, i.e. [is] must be a dashed name and
9262 // the tag name must not.
9263 // if so, set isCustomElementName to false to kick off all the logics
9264 // that pick up aIsAtom.
9265 if (nsContentUtils::IsNameWithDash(aIsAtom) &&
9266 !nsContentUtils::IsNameWithDash(name)) {
9267 isCustomElementName = false;
9268 } else {
9269 isCustomElementName =
9270 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9272 } else {
9273 isCustomElementName =
9274 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9278 nsAtom* tagAtom = nodeInfo->NameAtom();
9279 nsAtom* typeAtom = nullptr;
9280 bool isCustomElement = isCustomElementName || aIsAtom;
9281 if (isCustomElement) {
9282 typeAtom = isCustomElementName ? tagAtom : aIsAtom;
9285 MOZ_ASSERT_IF(aDefinition, isCustomElement);
9287 // https://dom.spec.whatwg.org/#concept-create-element
9288 // We only handle the "synchronous custom elements flag is set" now.
9289 // For the unset case (e.g. cloning a node), see bug 1319342 for that.
9290 // Step 4.
9291 RefPtr<CustomElementDefinition> definition = aDefinition;
9292 if (isCustomElement && !definition) {
9293 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9294 definition = nsContentUtils::LookupCustomElementDefinition(
9295 nodeInfo->GetDocument(), nodeInfo->NameAtom(), nodeInfo->NamespaceID(),
9296 typeAtom);
9299 // It might be a problem that parser synchronously calls constructor, so filed
9300 // bug 1378079 to figure out what we should do for parser case.
9301 if (definition) {
9303 * Synchronous custom elements flag is determined by 3 places in spec,
9304 * 1) create an element for a token, the flag is determined by
9305 * "will execute script" which is not originally created
9306 * for the HTML fragment parsing algorithm.
9307 * 2) createElement and createElementNS, the flag is the same as
9308 * NOT_FROM_PARSER.
9309 * 3) clone a node, our implementation will not go into this function.
9310 * For the unset case which is non-synchronous only applied for
9311 * inner/outerHTML.
9313 bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT;
9314 // Per discussion in https://github.com/w3c/webcomponents/issues/635,
9315 // use entry global in those places that are called from JS APIs and use the
9316 // node document's global object if it is called from parser.
9317 nsIGlobalObject* global;
9318 if (aFromParser == dom::NOT_FROM_PARSER) {
9319 global = GetEntryGlobal();
9321 // Documents created from the PrototypeDocumentSink always use
9322 // NOT_FROM_PARSER for non-XUL elements. We can get the global from the
9323 // document in that case.
9324 if (!global) {
9325 Document* doc = nodeInfo->GetDocument();
9326 if (doc && doc->LoadedFromPrototype()) {
9327 global = doc->GetScopeObject();
9330 } else {
9331 global = nodeInfo->GetDocument()->GetScopeObject();
9333 if (!global) {
9334 // In browser chrome code, one may have access to a document which doesn't
9335 // have scope object anymore.
9336 return NS_ERROR_FAILURE;
9339 AutoAllowLegacyScriptExecution exemption;
9340 AutoEntryScript aes(global, "create custom elements");
9341 JSContext* cx = aes.cx();
9342 ErrorResult rv;
9344 // Step 5.
9345 if (definition->IsCustomBuiltIn()) {
9346 // SetupCustomElement() should be called with an element that don't have
9347 // CustomElementData setup, if not we will hit the assertion in
9348 // SetCustomElementData().
9349 // Built-in element
9350 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9351 *aResult =
9352 CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9353 } else {
9354 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9356 (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
9357 if (synchronousCustomElements) {
9358 CustomElementRegistry::Upgrade(*aResult, definition, rv);
9359 if (rv.MaybeSetPendingException(cx)) {
9360 aes.ReportException();
9362 } else {
9363 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9366 return NS_OK;
9369 // Step 6.1.
9370 if (synchronousCustomElements) {
9371 definition->mPrefixStack.AppendElement(nodeInfo->GetPrefixAtom());
9372 RefPtr<Document> doc = nodeInfo->GetDocument();
9373 DoCustomElementCreate(aResult, cx, doc, nodeInfo,
9374 MOZ_KnownLive(definition->mConstructor), rv);
9375 if (rv.MaybeSetPendingException(cx)) {
9376 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9377 NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(),
9378 aFromParser));
9379 } else {
9380 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9382 (*aResult)->SetDefined(false);
9384 definition->mPrefixStack.RemoveLastElement();
9385 return NS_OK;
9388 // Step 6.2.
9389 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9390 NS_IF_ADDREF(*aResult =
9391 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9392 } else {
9393 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9395 (*aResult)->SetCustomElementData(new CustomElementData(definition->mType));
9396 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9397 return NS_OK;
9400 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9401 // Per the Custom Element specification, unknown tags that are valid custom
9402 // element names should be HTMLElement instead of HTMLUnknownElement.
9403 if (isCustomElementName) {
9404 NS_IF_ADDREF(*aResult =
9405 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9406 } else {
9407 *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9409 } else {
9410 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9413 if (!*aResult) {
9414 return NS_ERROR_OUT_OF_MEMORY;
9417 if (isCustomElement) {
9418 (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
9419 nsContentUtils::RegisterCallbackUpgradeElement(*aResult, typeAtom);
9422 return NS_OK;
9425 CustomElementRegistry* nsContentUtils::GetCustomElementRegistry(
9426 Document* aDoc) {
9427 MOZ_ASSERT(aDoc);
9429 if (!aDoc->GetDocShell()) {
9430 return nullptr;
9433 nsPIDOMWindowInner* window = aDoc->GetInnerWindow();
9434 if (!window) {
9435 return nullptr;
9438 return window->CustomElements();
9441 /* static */
9442 CustomElementDefinition* nsContentUtils::LookupCustomElementDefinition(
9443 Document* aDoc, nsAtom* aNameAtom, uint32_t aNameSpaceID,
9444 nsAtom* aTypeAtom) {
9445 if (aNameSpaceID != kNameSpaceID_XUL && aNameSpaceID != kNameSpaceID_XHTML) {
9446 return nullptr;
9449 RefPtr<CustomElementRegistry> registry = GetCustomElementRegistry(aDoc);
9450 if (!registry) {
9451 return nullptr;
9454 return registry->LookupCustomElementDefinition(aNameAtom, aNameSpaceID,
9455 aTypeAtom);
9458 /* static */
9459 void nsContentUtils::RegisterCallbackUpgradeElement(Element* aElement,
9460 nsAtom* aTypeName) {
9461 MOZ_ASSERT(aElement);
9463 Document* doc = aElement->OwnerDoc();
9464 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9465 if (registry) {
9466 registry->RegisterCallbackUpgradeElement(aElement, aTypeName);
9470 /* static */
9471 void nsContentUtils::RegisterUnresolvedElement(Element* aElement,
9472 nsAtom* aTypeName) {
9473 MOZ_ASSERT(aElement);
9475 Document* doc = aElement->OwnerDoc();
9476 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9477 if (registry) {
9478 registry->RegisterUnresolvedElement(aElement, aTypeName);
9482 /* static */
9483 void nsContentUtils::UnregisterUnresolvedElement(Element* aElement) {
9484 MOZ_ASSERT(aElement);
9486 nsAtom* typeAtom = aElement->GetCustomElementData()->GetCustomElementType();
9487 Document* doc = aElement->OwnerDoc();
9488 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9489 if (registry) {
9490 registry->UnregisterUnresolvedElement(aElement, typeAtom);
9494 /* static */
9495 void nsContentUtils::EnqueueUpgradeReaction(
9496 Element* aElement, CustomElementDefinition* aDefinition) {
9497 MOZ_ASSERT(aElement);
9499 Document* doc = aElement->OwnerDoc();
9501 // No DocGroup means no custom element reactions stack.
9502 if (!doc->GetDocGroup()) {
9503 return;
9506 CustomElementReactionsStack* stack =
9507 doc->GetDocGroup()->CustomElementReactionsStack();
9508 stack->EnqueueUpgradeReaction(aElement, aDefinition);
9511 /* static */
9512 void nsContentUtils::EnqueueLifecycleCallback(
9513 ElementCallbackType aType, Element* aCustomElement,
9514 LifecycleCallbackArgs* aArgs,
9515 LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
9516 CustomElementDefinition* aDefinition) {
9517 // No DocGroup means no custom element reactions stack.
9518 if (!aCustomElement->OwnerDoc()->GetDocGroup()) {
9519 return;
9522 CustomElementRegistry::EnqueueLifecycleCallback(
9523 aType, aCustomElement, aArgs, aAdoptedCallbackArgs, aDefinition);
9526 /* static */
9527 void nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
9528 Document* aDocument, nsTArray<nsIContent*>& aElements) {
9529 MOZ_ASSERT(aDocument);
9530 #ifdef DEBUG
9531 size_t oldLength = aElements.Length();
9532 #endif
9534 if (PresShell* presShell = aDocument->GetPresShell()) {
9535 if (nsIFrame* scrollFrame = presShell->GetRootScrollFrame()) {
9536 nsIAnonymousContentCreator* creator = do_QueryFrame(scrollFrame);
9537 MOZ_ASSERT(
9538 creator,
9539 "scroll frame should always implement nsIAnonymousContentCreator");
9540 creator->AppendAnonymousContentTo(aElements, 0);
9542 if (nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame()) {
9543 canvasFrame->AppendAnonymousContentTo(aElements, 0);
9547 #ifdef DEBUG
9548 for (size_t i = oldLength; i < aElements.Length(); i++) {
9549 MOZ_ASSERT(
9550 aElements[i]->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent),
9551 "Someone here has lied, or missed to flag the node");
9553 #endif
9556 static void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame,
9557 nsTArray<nsIContent*>& aKids,
9558 uint32_t aFlags) {
9559 if (nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame)) {
9560 ac->AppendAnonymousContentTo(aKids, aFlags);
9564 /* static */
9565 void nsContentUtils::AppendNativeAnonymousChildren(const nsIContent* aContent,
9566 nsTArray<nsIContent*>& aKids,
9567 uint32_t aFlags) {
9568 if (aContent->MayHaveAnonymousChildren()) {
9569 if (nsIFrame* primaryFrame = aContent->GetPrimaryFrame()) {
9570 // NAC created by the element's primary frame.
9571 AppendNativeAnonymousChildrenFromFrame(primaryFrame, aKids, aFlags);
9573 // NAC created by any other non-primary frames for the element.
9574 AutoTArray<nsIFrame::OwnedAnonBox, 8> ownedAnonBoxes;
9575 primaryFrame->AppendOwnedAnonBoxes(ownedAnonBoxes);
9576 for (nsIFrame::OwnedAnonBox& box : ownedAnonBoxes) {
9577 MOZ_ASSERT(box.mAnonBoxFrame->GetContent() == aContent);
9578 AppendNativeAnonymousChildrenFromFrame(box.mAnonBoxFrame, aKids,
9579 aFlags);
9583 // Get manually created NAC (editor resize handles, etc.).
9584 if (auto nac = static_cast<ManualNACArray*>(
9585 aContent->GetProperty(nsGkAtoms::manualNACProperty))) {
9586 aKids.AppendElements(*nac);
9590 // The root scroll frame is not the primary frame of the root element.
9591 // Detect and handle this case.
9592 if (!(aFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
9593 aContent == aContent->OwnerDoc()->GetRootElement()) {
9594 AppendDocumentLevelNativeAnonymousContentTo(aContent->OwnerDoc(), aKids);
9598 /* static */
9599 bool nsContentUtils::QueryTriggeringPrincipal(
9600 nsIContent* aLoadingNode, nsIPrincipal* aDefaultPrincipal,
9601 nsIPrincipal** aTriggeringPrincipal) {
9602 MOZ_ASSERT(aLoadingNode);
9603 MOZ_ASSERT(aTriggeringPrincipal);
9605 bool result = false;
9606 nsCOMPtr<nsIPrincipal> loadingPrincipal = aDefaultPrincipal;
9607 if (!loadingPrincipal) {
9608 loadingPrincipal = aLoadingNode->NodePrincipal();
9611 // If aLoadingNode is content, bail out early.
9612 if (!aLoadingNode->NodePrincipal()->IsSystemPrincipal()) {
9613 loadingPrincipal.forget(aTriggeringPrincipal);
9614 return result;
9617 nsAutoString loadingStr;
9618 if (aLoadingNode->IsElement()) {
9619 aLoadingNode->AsElement()->GetAttr(
9620 kNameSpaceID_None, nsGkAtoms::triggeringprincipal, loadingStr);
9623 // Fall back if 'triggeringprincipal' isn't specified,
9624 if (loadingStr.IsEmpty()) {
9625 loadingPrincipal.forget(aTriggeringPrincipal);
9626 return result;
9629 nsCString binary;
9630 nsresult rv = Base64Decode(NS_ConvertUTF16toUTF8(loadingStr), binary);
9631 if (NS_SUCCEEDED(rv)) {
9632 nsCOMPtr<nsIPrincipal> serializedPrin = BasePrincipal::FromJSON(binary);
9633 if (serializedPrin) {
9634 result = true;
9635 serializedPrin.forget(aTriggeringPrincipal);
9637 } else {
9638 MOZ_ASSERT(false, "Unable to deserialize base64 principal");
9641 if (!result) {
9642 // Fallback if the deserialization is failed.
9643 loadingPrincipal.forget(aTriggeringPrincipal);
9646 return result;
9649 /* static */
9650 void nsContentUtils::GetContentPolicyTypeForUIImageLoading(
9651 nsIContent* aLoadingNode, nsIPrincipal** aTriggeringPrincipal,
9652 nsContentPolicyType& aContentPolicyType, uint64_t* aRequestContextID) {
9653 MOZ_ASSERT(aRequestContextID);
9655 bool result = QueryTriggeringPrincipal(aLoadingNode, aTriggeringPrincipal);
9656 if (result) {
9657 // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for
9658 // indicating it's a favicon loading.
9659 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
9661 nsAutoString requestContextID;
9662 if (aLoadingNode->IsElement()) {
9663 aLoadingNode->AsElement()->GetAttr(
9664 kNameSpaceID_None, nsGkAtoms::requestcontextid, requestContextID);
9666 nsresult rv;
9667 int64_t val = requestContextID.ToInteger64(&rv);
9668 *aRequestContextID = NS_SUCCEEDED(rv) ? val : 0;
9669 } else {
9670 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
9674 /* static */
9675 nsresult nsContentUtils::CreateJSValueFromSequenceOfObject(
9676 JSContext* aCx, const Sequence<JSObject*>& aTransfer,
9677 JS::MutableHandle<JS::Value> aValue) {
9678 if (aTransfer.IsEmpty()) {
9679 return NS_OK;
9682 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, aTransfer.Length()));
9683 if (!array) {
9684 return NS_ERROR_OUT_OF_MEMORY;
9687 for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
9688 JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
9689 if (!object) {
9690 continue;
9693 if (NS_WARN_IF(
9694 !JS_DefineElement(aCx, array, i, object, JSPROP_ENUMERATE))) {
9695 return NS_ERROR_OUT_OF_MEMORY;
9699 aValue.setObject(*array);
9700 return NS_OK;
9703 /* static */
9704 bool nsContentUtils::ShouldBlockReservedKeys(WidgetKeyboardEvent* aKeyEvent) {
9705 nsCOMPtr<nsIPrincipal> principal;
9706 nsCOMPtr<Element> targetElement =
9707 do_QueryInterface(aKeyEvent->mOriginalTarget);
9708 nsCOMPtr<nsIBrowser> targetBrowser;
9709 if (targetElement) {
9710 targetBrowser = targetElement->AsBrowser();
9712 bool isRemoteBrowser = false;
9713 if (targetBrowser) {
9714 targetBrowser->GetIsRemoteBrowser(&isRemoteBrowser);
9717 if (isRemoteBrowser) {
9718 targetBrowser->GetContentPrincipal(getter_AddRefs(principal));
9719 return principal ? nsContentUtils::IsSitePermDeny(principal, "shortcuts"_ns)
9720 : false;
9723 nsCOMPtr<nsIContent> content = do_QueryInterface(aKeyEvent->mOriginalTarget);
9724 if (content) {
9725 Document* doc = content->GetUncomposedDoc();
9726 if (doc) {
9727 RefPtr<WindowContext> wc = doc->GetWindowContext();
9728 if (wc) {
9729 return wc->TopWindowContext()->GetShortcutsPermission() ==
9730 nsIPermissionManager::DENY_ACTION;
9735 return false;
9739 * Checks whether the given type is a supported document type for
9740 * loading within the nsObjectLoadingContent specified by aContent.
9742 * NOTE Helper method for nsContentUtils::HtmlObjectContentTypeForMIMEType.
9743 * NOTE Does not take content policy or capabilities into account
9745 static bool HtmlObjectContentSupportsDocument(const nsCString& aMimeType,
9746 nsIContent* aContent) {
9747 nsCOMPtr<nsIWebNavigationInfo> info(
9748 do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID));
9749 if (!info) {
9750 return false;
9753 nsCOMPtr<nsIWebNavigation> webNav;
9754 if (aContent) {
9755 Document* currentDoc = aContent->GetComposedDoc();
9756 if (currentDoc) {
9757 webNav = do_GetInterface(currentDoc->GetWindow());
9761 uint32_t supported;
9762 nsresult rv = info->IsTypeSupported(aMimeType, webNav, &supported);
9764 if (NS_FAILED(rv)) {
9765 return false;
9768 if (supported != nsIWebNavigationInfo::UNSUPPORTED) {
9769 // Don't want to support plugins as documents
9770 return supported != nsIWebNavigationInfo::FALLBACK;
9773 // Try a stream converter
9774 // NOTE: We treat any type we can convert from as a supported type. If a
9775 // type is not actually supported, the URI loader will detect that and
9776 // return an error, and we'll fallback.
9777 nsCOMPtr<nsIStreamConverterService> convServ =
9778 do_GetService("@mozilla.org/streamConverters;1");
9779 bool canConvert = false;
9780 if (convServ) {
9781 rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
9783 return NS_SUCCEEDED(rv) && canConvert;
9786 /* static */
9787 already_AddRefed<nsIPluginTag> nsContentUtils::PluginTagForType(
9788 const nsCString& aMIMEType, bool aNoFakePlugin) {
9789 RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
9790 nsCOMPtr<nsIPluginTag> tag;
9791 NS_ENSURE_TRUE(pluginHost, nullptr);
9793 // ShouldPlay will handle the case where the plugin is disabled
9794 pluginHost->GetPluginTagForType(
9795 aMIMEType,
9796 aNoFakePlugin ? nsPluginHost::eExcludeFake : nsPluginHost::eExcludeNone,
9797 getter_AddRefs(tag));
9799 return tag.forget();
9802 /* static */
9803 uint32_t nsContentUtils::HtmlObjectContentTypeForMIMEType(
9804 const nsCString& aMIMEType, bool aNoFakePlugin, nsIContent* aContent) {
9805 if (aMIMEType.IsEmpty()) {
9806 return nsIObjectLoadingContent::TYPE_NULL;
9809 if (imgLoader::SupportImageWithMimeType(aMIMEType)) {
9810 return nsIObjectLoadingContent::TYPE_IMAGE;
9813 // Faking support of the PDF content as a document for EMBED tags
9814 // when internal PDF viewer is enabled.
9815 if (aMIMEType.LowerCaseEqualsLiteral("application/pdf") && IsPDFJSEnabled()) {
9816 return nsIObjectLoadingContent::TYPE_DOCUMENT;
9819 if (HtmlObjectContentSupportsDocument(aMIMEType, aContent)) {
9820 return nsIObjectLoadingContent::TYPE_DOCUMENT;
9823 bool isSpecialPlugin = nsPluginHost::GetSpecialType(aMIMEType) !=
9824 nsPluginHost::eSpecialType_None;
9825 if (isSpecialPlugin) {
9826 return nsIObjectLoadingContent::TYPE_FALLBACK;
9829 return nsIObjectLoadingContent::TYPE_NULL;
9832 /* static */
9833 already_AddRefed<nsISerialEventTarget> nsContentUtils::GetEventTargetByLoadInfo(
9834 nsILoadInfo* aLoadInfo, TaskCategory aCategory) {
9835 if (NS_WARN_IF(!aLoadInfo)) {
9836 return nullptr;
9839 RefPtr<Document> doc;
9840 aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
9841 nsCOMPtr<nsISerialEventTarget> target;
9842 if (doc) {
9843 if (DocGroup* group = doc->GetDocGroup()) {
9844 target = group->EventTargetFor(aCategory);
9846 } else {
9847 target = GetMainThreadSerialEventTarget();
9850 return target.forget();
9853 /* static */
9854 bool nsContentUtils::IsLocalRefURL(const nsAString& aString) {
9855 return !aString.IsEmpty() && aString[0] == '#';
9858 // We use only 53 bits for the ID so that it can be converted to and from a JS
9859 // value without loss of precision. The upper bits of the ID hold the process
9860 // ID. The lower bits identify the object itself.
9861 static constexpr uint64_t kIdTotalBits = 53;
9862 static constexpr uint64_t kIdProcessBits = 22;
9863 static constexpr uint64_t kIdBits = kIdTotalBits - kIdProcessBits;
9865 /* static */
9866 uint64_t nsContentUtils::GenerateProcessSpecificId(uint64_t aId) {
9867 uint64_t processId = 0;
9868 if (XRE_IsContentProcess()) {
9869 ContentChild* cc = ContentChild::GetSingleton();
9870 processId = cc->GetID();
9873 MOZ_RELEASE_ASSERT(processId < (uint64_t(1) << kIdProcessBits));
9874 uint64_t processBits = processId & ((uint64_t(1) << kIdProcessBits) - 1);
9876 uint64_t id = aId;
9877 MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
9878 uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
9880 return (processBits << kIdBits) | bits;
9883 /* static */
9884 std::tuple<uint64_t, uint64_t> nsContentUtils::SplitProcessSpecificId(
9885 uint64_t aId) {
9886 return {aId >> kIdBits, aId & ((uint64_t(1) << kIdBits) - 1)};
9889 // Next process-local Tab ID.
9890 static uint64_t gNextTabId = 0;
9892 /* static */
9893 uint64_t nsContentUtils::GenerateTabId() {
9894 return GenerateProcessSpecificId(++gNextTabId);
9897 // Next process-local Browser ID.
9898 static uint64_t gNextBrowserId = 0;
9900 /* static */
9901 uint64_t nsContentUtils::GenerateBrowserId() {
9902 return GenerateProcessSpecificId(++gNextBrowserId);
9905 // Next process-local Browsing Context ID.
9906 static uint64_t gNextBrowsingContextId = 0;
9908 /* static */
9909 uint64_t nsContentUtils::GenerateBrowsingContextId() {
9910 return GenerateProcessSpecificId(++gNextBrowsingContextId);
9913 // Next process-local Window ID.
9914 static uint64_t gNextWindowId = 0;
9916 /* static */
9917 uint64_t nsContentUtils::GenerateWindowId() {
9918 return GenerateProcessSpecificId(++gNextWindowId);
9921 // Next process-local load.
9922 static Atomic<uint64_t> gNextLoadIdentifier(0);
9924 /* static */
9925 uint64_t nsContentUtils::GenerateLoadIdentifier() {
9926 return GenerateProcessSpecificId(++gNextLoadIdentifier);
9929 /* static */
9930 bool nsContentUtils::GetUserIsInteracting() {
9931 return UserInteractionObserver::sUserActive;
9934 /* static */
9935 bool nsContentUtils::GetSourceMapURL(nsIHttpChannel* aChannel,
9936 nsACString& aResult) {
9937 nsresult rv = aChannel->GetResponseHeader("SourceMap"_ns, aResult);
9938 if (NS_FAILED(rv)) {
9939 rv = aChannel->GetResponseHeader("X-SourceMap"_ns, aResult);
9941 return NS_SUCCEEDED(rv);
9944 /* static */
9945 bool nsContentUtils::IsMessageInputEvent(const IPC::Message& aMsg) {
9946 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
9947 mozilla::dom::PBrowser::PBrowserStart) {
9948 switch (aMsg.type()) {
9949 case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
9950 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
9951 case mozilla::dom::PBrowser::Msg_RealMouseEnterExitWidgetEvent__ID:
9952 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
9953 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
9954 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
9955 case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
9956 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
9957 case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
9958 case mozilla::dom::PBrowser::Msg_MouseEvent__ID:
9959 return true;
9962 return false;
9965 /* static */
9966 bool nsContentUtils::IsMessageCriticalInputEvent(const IPC::Message& aMsg) {
9967 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
9968 mozilla::dom::PBrowser::PBrowserStart) {
9969 switch (aMsg.type()) {
9970 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
9971 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
9972 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
9973 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
9974 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
9975 return true;
9978 return false;
9981 static const char* kUserInteractionInactive = "user-interaction-inactive";
9982 static const char* kUserInteractionActive = "user-interaction-active";
9984 void nsContentUtils::UserInteractionObserver::Init() {
9985 // Listen for the observer messages from EventStateManager which are telling
9986 // us whether or not the user is interacting.
9987 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
9988 obs->AddObserver(this, kUserInteractionInactive, false);
9989 obs->AddObserver(this, kUserInteractionActive, false);
9991 // We can't register ourselves as an annotator yet, as the
9992 // BackgroundHangMonitor hasn't started yet. It will have started by the
9993 // time we have the chance to spin the event loop.
9994 RefPtr<UserInteractionObserver> self = this;
9995 NS_DispatchToMainThread(NS_NewRunnableFunction(
9996 "nsContentUtils::UserInteractionObserver::Init",
9997 [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
10000 void nsContentUtils::UserInteractionObserver::Shutdown() {
10001 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10002 if (obs) {
10003 obs->RemoveObserver(this, kUserInteractionInactive);
10004 obs->RemoveObserver(this, kUserInteractionActive);
10007 BackgroundHangMonitor::UnregisterAnnotator(*this);
10011 * NB: This function is always called by the BackgroundHangMonitor thread.
10012 * Plan accordingly
10014 void nsContentUtils::UserInteractionObserver::AnnotateHang(
10015 BackgroundHangAnnotations& aAnnotations) {
10016 // NOTE: Only annotate the hang report if the user is known to be interacting.
10017 if (sUserActive) {
10018 aAnnotations.AddAnnotation(u"UserInteracting"_ns, true);
10022 NS_IMETHODIMP
10023 nsContentUtils::UserInteractionObserver::Observe(nsISupports* aSubject,
10024 const char* aTopic,
10025 const char16_t* aData) {
10026 if (!strcmp(aTopic, kUserInteractionInactive)) {
10027 sUserActive = false;
10028 } else if (!strcmp(aTopic, kUserInteractionActive)) {
10029 sUserActive = true;
10030 } else {
10031 NS_WARNING("Unexpected observer notification");
10033 return NS_OK;
10036 Atomic<bool> nsContentUtils::UserInteractionObserver::sUserActive(false);
10037 NS_IMPL_ISUPPORTS(nsContentUtils::UserInteractionObserver, nsIObserver)
10039 /* static */
10040 bool nsContentUtils::IsSpecialName(const nsAString& aName) {
10041 return aName.LowerCaseEqualsLiteral("_blank") ||
10042 aName.LowerCaseEqualsLiteral("_top") ||
10043 aName.LowerCaseEqualsLiteral("_parent") ||
10044 aName.LowerCaseEqualsLiteral("_self");
10047 /* static */
10048 bool nsContentUtils::IsOverridingWindowName(const nsAString& aName) {
10049 return !aName.IsEmpty() && !IsSpecialName(aName);
10052 // Unfortunately, we can't unwrap an IDL object using only a concrete type.
10053 // We need to calculate type data based on the IDL typename. Which means
10054 // wrapping our templated function in a macro.
10055 #define EXTRACT_EXN_VALUES(T, ...) \
10056 ExtractExceptionValues<mozilla::dom::prototypes::id::T, \
10057 T##_Binding::NativeType, T>(__VA_ARGS__) \
10058 .isOk()
10060 template <prototypes::ID PrototypeID, class NativeType, typename T>
10061 static Result<Ok, nsresult> ExtractExceptionValues(
10062 JSContext* aCx, JS::HandleObject aObj, nsAString& aSourceSpecOut,
10063 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10064 AssertStaticUnwrapOK<PrototypeID>();
10065 RefPtr<T> exn;
10066 MOZ_TRY((UnwrapObject<PrototypeID, NativeType>(aObj, exn, nullptr)));
10068 exn->GetFilename(aCx, aSourceSpecOut);
10069 if (!aSourceSpecOut.IsEmpty()) {
10070 *aLineOut = exn->LineNumber(aCx);
10071 *aColumnOut = exn->ColumnNumber();
10074 exn->GetName(aMessageOut);
10075 aMessageOut.AppendLiteral(": ");
10077 nsAutoString message;
10078 exn->GetMessageMoz(message);
10079 aMessageOut.Append(message);
10080 return Ok();
10083 /* static */
10084 void nsContentUtils::ExtractErrorValues(
10085 JSContext* aCx, JS::Handle<JS::Value> aValue, nsACString& aSourceSpecOut,
10086 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10087 nsAutoString sourceSpec;
10088 ExtractErrorValues(aCx, aValue, sourceSpec, aLineOut, aColumnOut,
10089 aMessageOut);
10090 CopyUTF16toUTF8(sourceSpec, aSourceSpecOut);
10093 /* static */
10094 void nsContentUtils::ExtractErrorValues(
10095 JSContext* aCx, JS::Handle<JS::Value> aValue, nsAString& aSourceSpecOut,
10096 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10097 MOZ_ASSERT(aLineOut);
10098 MOZ_ASSERT(aColumnOut);
10100 if (aValue.isObject()) {
10101 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
10103 // Try to process as an Error object. Use the file/line/column values
10104 // from the Error as they will be more specific to the root cause of
10105 // the problem.
10106 JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
10107 if (err) {
10108 // Use xpc to extract the error message only. We don't actually send
10109 // this report anywhere.
10110 RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
10111 report->Init(err,
10112 nullptr, // toString result
10113 false, // chrome
10114 0); // window ID
10116 if (!report->mFileName.IsEmpty()) {
10117 aSourceSpecOut = report->mFileName;
10118 *aLineOut = report->mLineNumber;
10119 *aColumnOut = report->mColumn;
10121 aMessageOut.Assign(report->mErrorMsg);
10124 // Next, try to unwrap the rejection value as a DOMException.
10125 else if (EXTRACT_EXN_VALUES(DOMException, aCx, obj, aSourceSpecOut,
10126 aLineOut, aColumnOut, aMessageOut)) {
10127 return;
10130 // Next, try to unwrap the rejection value as an XPC Exception.
10131 else if (EXTRACT_EXN_VALUES(Exception, aCx, obj, aSourceSpecOut, aLineOut,
10132 aColumnOut, aMessageOut)) {
10133 return;
10137 // If we could not unwrap a specific error type, then perform default safe
10138 // string conversions on primitives. Objects will result in "[Object]"
10139 // unfortunately.
10140 if (aMessageOut.IsEmpty()) {
10141 nsAutoJSString jsString;
10142 if (jsString.init(aCx, aValue)) {
10143 aMessageOut = jsString;
10144 } else {
10145 JS_ClearPendingException(aCx);
10150 #undef EXTRACT_EXN_VALUES
10152 /* static */
10153 bool nsContentUtils::ContentIsLink(nsIContent* aContent) {
10154 if (!aContent || !aContent->IsElement()) {
10155 return false;
10158 if (aContent->IsHTMLElement(nsGkAtoms::a)) {
10159 return true;
10162 return aContent->AsElement()->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
10163 nsGkAtoms::simple, eCaseMatters);
10166 /* static */
10167 already_AddRefed<ContentFrameMessageManager>
10168 nsContentUtils::TryGetBrowserChildGlobal(nsISupports* aFrom) {
10169 RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(aFrom);
10170 if (!frameLoaderOwner) {
10171 return nullptr;
10174 RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
10175 if (!frameLoader) {
10176 return nullptr;
10179 RefPtr<ContentFrameMessageManager> manager =
10180 frameLoader->GetBrowserChildMessageManager();
10181 return manager.forget();
10184 /* static */
10185 uint32_t nsContentUtils::InnerOrOuterWindowCreated() {
10186 MOZ_ASSERT(NS_IsMainThread());
10187 ++sInnerOrOuterWindowCount;
10188 return ++sInnerOrOuterWindowSerialCounter;
10191 /* static */
10192 void nsContentUtils::InnerOrOuterWindowDestroyed() {
10193 MOZ_ASSERT(NS_IsMainThread());
10194 MOZ_ASSERT(sInnerOrOuterWindowCount > 0);
10195 --sInnerOrOuterWindowCount;
10198 static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
10199 nsAString* result = static_cast<nsAString*>(aData);
10200 result->Append(static_cast<const char16_t*>(aBuf),
10201 static_cast<uint32_t>(aLen));
10202 return true;
10205 /* static */
10206 bool nsContentUtils::StringifyJSON(JSContext* aCx,
10207 JS::MutableHandle<JS::Value> aValue,
10208 nsAString& aOutStr) {
10209 MOZ_ASSERT(aCx);
10210 aOutStr.Truncate();
10211 JS::RootedValue value(aCx, aValue.get());
10212 nsAutoString serializedValue;
10213 NS_ENSURE_TRUE(JS_Stringify(aCx, &value, nullptr, JS::NullHandleValue,
10214 JSONCreator, &serializedValue),
10215 false);
10216 aOutStr = serializedValue;
10217 return true;
10220 /* static */
10221 bool nsContentUtils::
10222 HighPriorityEventPendingForTopLevelDocumentBeforeContentfulPaint(
10223 Document* aDocument) {
10224 MOZ_ASSERT(XRE_IsContentProcess(),
10225 "This function only makes sense in content processes");
10227 if (aDocument && !aDocument->IsLoadedAsData()) {
10228 if (nsPresContext* presContext = FindPresContextForDocument(aDocument)) {
10229 MOZ_ASSERT(!presContext->IsChrome(),
10230 "Should never have a chrome PresContext in a content process");
10232 return !presContext->GetInProcessRootContentDocumentPresContext()
10233 ->HadContentfulPaint() &&
10234 nsThreadManager::MainThreadHasPendingHighPriorityEvents();
10237 return false;
10240 /* static */
10241 nsGlobalWindowInner* nsContentUtils::CallerInnerWindow() {
10242 nsIGlobalObject* global = GetIncumbentGlobal();
10243 NS_ENSURE_TRUE(global, nullptr);
10245 if (auto* window = global->AsInnerWindow()) {
10246 return nsGlobalWindowInner::Cast(window);
10249 // When Extensions run content scripts inside a sandbox, it uses
10250 // sandboxPrototype to make them appear as though they're running in the
10251 // scope of the page. So when a content script invokes postMessage, it expects
10252 // the |source| of the received message to be the window set as the
10253 // sandboxPrototype. This used to work incidentally for unrelated reasons, but
10254 // now we need to do some special handling to support it.
10255 JS::Rooted<JSObject*> scope(RootingCx(), global->GetGlobalJSObject());
10256 NS_ENSURE_TRUE(scope, nullptr);
10258 if (xpc::IsSandbox(scope)) {
10259 AutoJSAPI jsapi;
10260 MOZ_ALWAYS_TRUE(jsapi.Init(scope));
10261 JSContext* cx = jsapi.cx();
10262 // Our current Realm on aCx is the sandbox. Using that for unwrapping
10263 // makes sense: if the sandbox can unwrap the window, we can use it.
10264 return xpc::SandboxWindowOrNull(scope, cx);
10267 // The calling window must be holding a reference, so we can return a weak
10268 // pointer.
10269 return nsGlobalWindowInner::Cast(global->AsInnerWindow());
10272 /* static */
10273 bool nsContentUtils::IsURIInPrefList(nsIURI* aURI, const char* aPrefName) {
10274 MOZ_ASSERT(aPrefName);
10276 nsAutoCString list;
10277 Preferences::GetCString(aPrefName, list);
10278 ToLowerCase(list);
10279 return IsURIInList(aURI, list);
10282 /* static */
10283 bool nsContentUtils::IsURIInList(nsIURI* aURI, const nsCString& aList) {
10284 #ifdef DEBUG
10285 nsAutoCString listLowerCase(aList);
10286 ToLowerCase(listLowerCase);
10287 MOZ_ASSERT(listLowerCase.Equals(aList),
10288 "The aList argument should be lower-case");
10289 #endif
10291 if (!aURI) {
10292 return false;
10295 nsAutoCString scheme;
10296 aURI->GetScheme(scheme);
10297 if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
10298 return false;
10301 if (aList.IsEmpty()) {
10302 return false;
10305 // The list is comma separated domain list. Each item may start with "*.".
10306 // If starts with "*.", it matches any sub-domains.
10308 nsCCharSeparatedTokenizer tokenizer(aList, ',');
10309 while (tokenizer.hasMoreTokens()) {
10310 const nsCString token(tokenizer.nextToken());
10312 nsAutoCString host;
10313 aURI->GetHost(host);
10314 if (host.IsEmpty()) {
10315 return false;
10317 ToLowerCase(host);
10319 for (;;) {
10320 int32_t index = token.Find(host, false);
10321 if (index >= 0 &&
10322 static_cast<uint32_t>(index) + host.Length() <= token.Length()) {
10323 // If we found a full match, return true.
10324 size_t indexAfterHost = index + host.Length();
10325 if (index == 0 && indexAfterHost == token.Length()) {
10326 return true;
10328 // If next character is '/', we need to check the path too.
10329 // We assume the path in the list means "/foo" + "*".
10330 if (token[indexAfterHost] == '/') {
10331 nsDependentCSubstring pathInList(
10332 token, indexAfterHost,
10333 static_cast<nsDependentCSubstring::size_type>(-1));
10334 nsAutoCString filePath;
10335 aURI->GetFilePath(filePath);
10336 ToLowerCase(filePath);
10337 if (StringBeginsWith(filePath, pathInList) &&
10338 (filePath.Length() == pathInList.Length() ||
10339 pathInList.EqualsLiteral("/") ||
10340 filePath[pathInList.Length() - 1] == '/' ||
10341 filePath[pathInList.Length() - 1] == '?' ||
10342 filePath[pathInList.Length() - 1] == '#')) {
10343 return true;
10347 int32_t startIndexOfCurrentLevel = host[0] == '*' ? 1 : 0;
10348 int32_t startIndexOfNextLevel =
10349 host.Find(".", false, startIndexOfCurrentLevel + 1);
10350 if (startIndexOfNextLevel <= 0) {
10351 break;
10353 host = "*"_ns + nsDependentCSubstring(host, startIndexOfNextLevel);
10357 return false;
10360 /* static */
10361 ScreenIntMargin nsContentUtils::GetWindowSafeAreaInsets(
10362 nsIScreen* aScreen, const ScreenIntMargin& aSafeAreaInsets,
10363 const LayoutDeviceIntRect& aWindowRect) {
10364 // This calculates safe area insets of window from screen rectangle, window
10365 // rectangle and safe area insets of screen.
10367 // +----------------------------------------+ <-- screen
10368 // | +-------------------------------+ <------- window
10369 // | | window's safe area inset top) | |
10370 // +--+-------------------------------+--+ |
10371 // | | | |<------ safe area rectangle of
10372 // | | | | | screen
10373 // +--+-------------------------------+--+ |
10374 // | |window's safe area inset bottom| |
10375 // | +-------------------------------+ |
10376 // +----------------------------------------+
10378 ScreenIntMargin windowSafeAreaInsets;
10380 if (windowSafeAreaInsets == aSafeAreaInsets) {
10381 // no safe area insets.
10382 return windowSafeAreaInsets;
10385 int32_t screenLeft, screenTop, screenWidth, screenHeight;
10386 nsresult rv =
10387 aScreen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
10388 if (NS_WARN_IF(NS_FAILED(rv))) {
10389 return windowSafeAreaInsets;
10392 // Screen's rect of safe area
10393 LayoutDeviceIntRect safeAreaRect(
10394 screenLeft + aSafeAreaInsets.left, screenTop + aSafeAreaInsets.top,
10395 screenWidth - aSafeAreaInsets.right - aSafeAreaInsets.left,
10396 screenHeight - aSafeAreaInsets.bottom - aSafeAreaInsets.top);
10397 // window's rect of safe area
10398 safeAreaRect = safeAreaRect.Intersect(aWindowRect);
10400 windowSafeAreaInsets.top =
10401 aSafeAreaInsets.top ? std::max(safeAreaRect.y - aWindowRect.y, 0) : 0;
10402 windowSafeAreaInsets.left =
10403 aSafeAreaInsets.left ? std::max(safeAreaRect.x - aWindowRect.x, 0) : 0;
10404 windowSafeAreaInsets.right =
10405 aSafeAreaInsets.right
10406 ? std::max((aWindowRect.x + aWindowRect.width) -
10407 (safeAreaRect.x + safeAreaRect.width),
10409 : 0;
10410 windowSafeAreaInsets.bottom =
10411 aSafeAreaInsets.bottom
10412 ? std::max(aWindowRect.y + aWindowRect.height -
10413 (safeAreaRect.y + safeAreaRect.height),
10415 : 0;
10417 return windowSafeAreaInsets;
10420 /* static */
10421 nsContentUtils::SubresourceCacheValidationInfo
10422 nsContentUtils::GetSubresourceCacheValidationInfo(nsIRequest* aRequest) {
10423 SubresourceCacheValidationInfo info;
10424 if (nsCOMPtr<nsICacheInfoChannel> cache = do_QueryInterface(aRequest)) {
10425 uint32_t value = 0;
10426 if (NS_SUCCEEDED(cache->GetCacheTokenExpirationTime(&value))) {
10427 info.mExpirationTime.emplace(value);
10431 // Determine whether the cache entry must be revalidated when we try to use
10432 // it. Currently, only HTTP specifies this information...
10433 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest)) {
10434 Unused << httpChannel->IsNoStoreResponse(&info.mMustRevalidate);
10436 if (!info.mMustRevalidate) {
10437 Unused << httpChannel->IsNoCacheResponse(&info.mMustRevalidate);
10441 return info;
10444 nsCString nsContentUtils::TruncatedURLForDisplay(nsIURI* aURL,
10445 uint32_t aMaxLen) {
10446 nsCString spec;
10447 if (aURL) {
10448 aURL->GetSpec(spec);
10449 spec.Truncate(std::min(aMaxLen, spec.Length()));
10451 return spec;