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/. */
6
7 /* A namespace class for static layout utilities. */
8
9 #include "nsContentUtils.h"
10
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 "nsIParser.h"
301 #include "nsIParserUtils.h"
302 #include "nsIPermissionManager.h"
303 #include "nsIPluginTag.h"
304 #include "nsIPrincipal.h"
305 #include "nsIProperties.h"
306 #include "nsIProtocolHandler.h"
307 #include "nsIRequest.h"
308 #include "nsIRunnable.h"
309 #include "nsIScreen.h"
310 #include "nsIScriptError.h"
311 #include "nsIScriptGlobalObject.h"
312 #include "nsIScriptObjectPrincipal.h"
313 #include "nsIScriptSecurityManager.h"
314 #include "nsISerialEventTarget.h"
315 #include "nsIStreamConverter.h"
316 #include "nsIStreamConverterService.h"
317 #include "nsIStringBundle.h"
318 #include "nsISupports.h"
319 #include "nsISupportsPrimitives.h"
320 #include "nsISupportsUtils.h"
321 #include "nsITransferable.h"
322 #include "nsIURI.h"
323 #include "nsIURIMutator.h"
324 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
325 # include "nsIURIWithSpecialOrigin.h"
326 #endif
327 #include "nsIUUIDGenerator.h"
328 #include "nsIUserIdleService.h"
329 #include "nsIWeakReferenceUtils.h"
330 #include "nsIWebNavigation.h"
331 #include "nsIWebNavigationInfo.h"
332 #include "nsIWidget.h"
333 #include "nsIWindowMediator.h"
334 #include "nsIXPConnect.h"
335 #include "nsJSPrincipals.h"
336 #include "nsJSUtils.h"
337 #include "nsLayoutUtils.h"
338 #include "nsLiteralString.h"
339 #include "nsMappedAttributes.h"
340 #include "nsMargin.h"
341 #include "nsMimeTypes.h"
342 #include "nsNameSpaceManager.h"
343 #include "nsNetCID.h"
344 #include "nsNetUtil.h"
345 #include "nsNodeInfoManager.h"
346 #include "nsPIDOMWindow.h"
347 #include "nsPIDOMWindowInlines.h"
348 #include "nsParserCIID.h"
349 #include "nsParserConstants.h"
350 #include "nsPluginHost.h"
351 #include "nsPoint.h"
352 #include "nsPointerHashKeys.h"
353 #include "nsPresContext.h"
354 #include "nsQueryFrame.h"
355 #include "nsQueryObject.h"
356 #include "nsRFPService.h"
357 #include "nsRange.h"
358 #include "nsRefPtrHashtable.h"
359 #include "nsSandboxFlags.h"
360 #include "nsScriptSecurityManager.h"
361 #include "nsServiceManagerUtils.h"
362 #include "nsStreamUtils.h"
363 #include "nsString.h"
364 #include "nsStringBuffer.h"
365 #include "nsStringFlags.h"
366 #include "nsStringFwd.h"
367 #include "nsStringIterator.h"
368 #include "nsTArray.h"
369 #include "nsTLiteralString.h"
370 #include "nsTPromiseFlatString.h"
371 #include "nsTStringRepr.h"
372 #include "nsTextFragment.h"
373 #include "nsTextNode.h"
374 #include "nsThreadManager.h"
375 #include "nsThreadUtils.h"
376 #include "nsTreeSanitizer.h"
377 #include "nsUGenCategory.h"
378 #include "nsURLHelper.h"
379 #include "nsUnicodeProperties.h"
380 #include "nsView.h"
381 #include "nsViewManager.h"
382 #include "nsXPCOM.h"
383 #include "nsXPCOMCID.h"
384 #include "nsXULAppAPI.h"
385 #include "nsXULElement.h"
386 #include "nsXULPopupManager.h"
387 #include "nscore.h"
388 #include "prinrval.h"
389 #include "xpcprivate.h"
390 #include "xpcpublic.h"
391
392 #if defined(XP_WIN)
393 // Undefine LoadImage to prevent naming conflict with Windows.
394 # undef LoadImage
395 #endif
396
397 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
398 const char** next, char16_t* result);
399 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end, int ns_aware,
400 const char** colon);
401
402 using namespace mozilla::dom;
403 using namespace mozilla::ipc;
404 using namespace mozilla::gfx;
405 using namespace mozilla::layers;
406 using namespace mozilla::widget;
407 using namespace mozilla;
408
409 const char kLoadAsData[] = "loadAsData";
410
411 nsIXPConnect* nsContentUtils::sXPConnect;
412 nsIScriptSecurityManager* nsContentUtils::sSecurityManager;
413 nsIPrincipal* nsContentUtils::sSystemPrincipal;
414 nsIPrincipal* nsContentUtils::sNullSubjectPrincipal;
415 nsNameSpaceManager* nsContentUtils::sNameSpaceManager;
416 nsIIOService* nsContentUtils::sIOService;
417 nsIUUIDGenerator* nsContentUtils::sUUIDGenerator;
418 nsIConsoleService* nsContentUtils::sConsoleService;
419 nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>*
420 nsContentUtils::sAtomEventTable = nullptr;
421 nsTHashMap<nsStringHashKey, EventNameMapping>*
422 nsContentUtils::sStringEventTable = nullptr;
423 nsTArray<RefPtr<nsAtom>>* nsContentUtils::sUserDefinedEvents = nullptr;
424 nsIStringBundleService* nsContentUtils::sStringBundleService;
425 nsIStringBundle* nsContentUtils::sStringBundles[PropertiesFile_COUNT];
426 nsIContentPolicy* nsContentUtils::sContentPolicyService;
427 bool nsContentUtils::sTriedToGetContentPolicy = false;
428 RefPtr<mozilla::intl::LineBreaker> nsContentUtils::sLineBreaker;
429 RefPtr<mozilla::intl::WordBreaker> nsContentUtils::sWordBreaker;
430 StaticRefPtr<nsIBidiKeyboard> nsContentUtils::sBidiKeyboard;
431 uint32_t nsContentUtils::sScriptBlockerCount = 0;
432 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
433 AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners =
434 nullptr;
435 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
436 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
437
438 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
439
440 nsString* nsContentUtils::sShiftText = nullptr;
441 nsString* nsContentUtils::sControlText = nullptr;
442 nsString* nsContentUtils::sMetaText = nullptr;
443 nsString* nsContentUtils::sOSText = nullptr;
444 nsString* nsContentUtils::sAltText = nullptr;
445 nsString* nsContentUtils::sModifierSeparator = nullptr;
446
447 bool nsContentUtils::sInitialized = false;
448 #ifndef RELEASE_OR_BETA
449 bool nsContentUtils::sBypassCSSOMOriginCheck = false;
450 #endif
451
452 nsCString* nsContentUtils::sJSBytecodeMimeType = nullptr;
453
454 nsContentUtils::UserInteractionObserver*
455 nsContentUtils::sUserInteractionObserver = nullptr;
456
457 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
458 nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
459 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
460 bool nsContentUtils::sFragmentParsingActive = false;
461
462 mozilla::LazyLogModule nsContentUtils::gResistFingerprintingLog(
463 "nsResistFingerprinting");
464 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
465
466 int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
467 uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
468
469 template Maybe<int32_t> nsContentUtils::ComparePoints(
470 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary);
471 template Maybe<int32_t> nsContentUtils::ComparePoints(
472 const RangeBoundary& aFirstBoundary,
473 const RawRangeBoundary& aSecondBoundary);
474 template Maybe<int32_t> nsContentUtils::ComparePoints(
475 const RawRangeBoundary& aFirstBoundary,
476 const RangeBoundary& aSecondBoundary);
477 template Maybe<int32_t> nsContentUtils::ComparePoints(
478 const RawRangeBoundary& aFirstBoundary,
479 const RawRangeBoundary& aSecondBoundary);
480
481 template int32_t nsContentUtils::ComparePoints_Deprecated(
482 const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
483 bool* aDisconnected);
484 template int32_t nsContentUtils::ComparePoints_Deprecated(
485 const RangeBoundary& aFirstBoundary,
486 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
487 template int32_t nsContentUtils::ComparePoints_Deprecated(
488 const RawRangeBoundary& aFirstBoundary,
489 const RangeBoundary& aSecondBoundary, bool* aDisconnected);
490 template int32_t nsContentUtils::ComparePoints_Deprecated(
491 const RawRangeBoundary& aFirstBoundary,
492 const RawRangeBoundary& aSecondBoundary, bool* aDisconnected);
493
494 // Subset of
495 // http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
496 enum AutocompleteUnsupportedFieldName : uint8_t {
497 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
498 eAutocompleteUnsupportedFieldName_##name_,
499 #include "AutocompleteFieldList.h"
500 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
501 };
502
503 enum AutocompleteNoPersistFieldName : uint8_t {
504 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
505 eAutocompleteNoPersistFieldName_##name_,
506 #include "AutocompleteFieldList.h"
507 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
508 };
509
510 enum AutocompleteUnsupportFieldContactHint : uint8_t {
511 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
512 eAutocompleteUnsupportedFieldContactHint_##name_,
513 #include "AutocompleteFieldList.h"
514 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
515 };
516
517 enum AutocompleteFieldName : uint8_t {
518 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) eAutocompleteFieldName_##name_,
519 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
520 AUTOCOMPLETE_FIELD_NAME(name_, value_)
521 #include "AutocompleteFieldList.h"
522 #undef AUTOCOMPLETE_FIELD_NAME
523 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
524 };
525
526 enum AutocompleteFieldHint : uint8_t {
527 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) eAutocompleteFieldHint_##name_,
528 #include "AutocompleteFieldList.h"
529 #undef AUTOCOMPLETE_FIELD_HINT
530 };
531
532 enum AutocompleteFieldContactHint : uint8_t {
533 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
534 eAutocompleteFieldContactHint_##name_,
535 #include "AutocompleteFieldList.h"
536 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
537 };
538
539 enum AutocompleteCategory {
540 #define AUTOCOMPLETE_CATEGORY(name_, value_) eAutocompleteCategory_##name_,
541 #include "AutocompleteFieldList.h"
542 #undef AUTOCOMPLETE_CATEGORY
543 };
544
545 static const nsAttrValue::EnumTable kAutocompleteUnsupportedFieldNameTable[] = {
546 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
547 {value_, eAutocompleteUnsupportedFieldName_##name_},
548 #include "AutocompleteFieldList.h"
549 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
550 {nullptr, 0}};
551
552 static const nsAttrValue::EnumTable kAutocompleteNoPersistFieldNameTable[] = {
553 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
554 {value_, eAutocompleteNoPersistFieldName_##name_},
555 #include "AutocompleteFieldList.h"
556 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
557 {nullptr, 0}};
558
559 static const nsAttrValue::EnumTable
560 kAutocompleteUnsupportedContactFieldHintTable[] = {
561 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
562 {value_, eAutocompleteUnsupportedFieldContactHint_##name_},
563 #include "AutocompleteFieldList.h"
564 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
565 {nullptr, 0}};
566
567 static const nsAttrValue::EnumTable kAutocompleteFieldNameTable[] = {
568 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
569 {value_, eAutocompleteFieldName_##name_},
570 #include "AutocompleteFieldList.h"
571 #undef AUTOCOMPLETE_FIELD_NAME
572 {nullptr, 0}};
573
574 static const nsAttrValue::EnumTable kAutocompleteContactFieldNameTable[] = {
575 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
576 {value_, eAutocompleteFieldName_##name_},
577 #include "AutocompleteFieldList.h"
578 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
579 {nullptr, 0}};
580
581 static const nsAttrValue::EnumTable kAutocompleteFieldHintTable[] = {
582 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
583 {value_, eAutocompleteFieldHint_##name_},
584 #include "AutocompleteFieldList.h"
585 #undef AUTOCOMPLETE_FIELD_HINT
586 {nullptr, 0}};
587
588 static const nsAttrValue::EnumTable kAutocompleteContactFieldHintTable[] = {
589 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
590 {value_, eAutocompleteFieldContactHint_##name_},
591 #include "AutocompleteFieldList.h"
592 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
593 {nullptr, 0}};
594
595 namespace {
596
597 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
598
599 static PLDHashTable* sEventListenerManagersHash;
600
601 // A global hashtable to for keeping the arena alive for cross docGroup node
602 // adoption.
603 static nsRefPtrHashtable<nsPtrHashKey<const nsINode>, mozilla::dom::DOMArena>*
604 sDOMArenaHashtable;
605
606 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter {
607 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
608
609 ~DOMEventListenerManagersHashReporter() = default;
610
611 public:
612 NS_DECL_ISUPPORTS
613
CollectReports(nsIHandleReportCallback * aHandleReport,nsISupports * aData,bool aAnonymize)614 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
615 nsISupports* aData, bool aAnonymize) override {
616 // We don't measure the |EventListenerManager| objects pointed to by the
617 // entries because those references are non-owning.
618 int64_t amount =
619 sEventListenerManagersHash
620 ? sEventListenerManagersHash->ShallowSizeOfIncludingThis(
621 MallocSizeOf)
622 : 0;
623
624 MOZ_COLLECT_REPORT(
625 "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
626 amount, "Memory used by the event listener manager's hash table.");
627
628 return NS_OK;
629 }
630 };
631
632 NS_IMPL_ISUPPORTS(DOMEventListenerManagersHashReporter, nsIMemoryReporter)
633
634 class EventListenerManagerMapEntry : public PLDHashEntryHdr {
635 public:
EventListenerManagerMapEntry(const void * aKey)636 explicit EventListenerManagerMapEntry(const void* aKey) : mKey(aKey) {}
637
~EventListenerManagerMapEntry()638 ~EventListenerManagerMapEntry() {
639 NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
640 }
641
642 protected: // declared protected to silence clang warnings
643 const void* mKey; // must be first, to look like PLDHashEntryStub
644
645 public:
646 RefPtr<EventListenerManager> mListenerManager;
647 };
648
EventListenerManagerHashInitEntry(PLDHashEntryHdr * entry,const void * key)649 static void EventListenerManagerHashInitEntry(PLDHashEntryHdr* entry,
650 const void* key) {
651 // Initialize the entry with placement new
652 new (entry) EventListenerManagerMapEntry(key);
653 }
654
EventListenerManagerHashClearEntry(PLDHashTable * table,PLDHashEntryHdr * entry)655 static void EventListenerManagerHashClearEntry(PLDHashTable* table,
656 PLDHashEntryHdr* entry) {
657 EventListenerManagerMapEntry* lm =
658 static_cast<EventListenerManagerMapEntry*>(entry);
659
660 // Let the EventListenerManagerMapEntry clean itself up...
661 lm->~EventListenerManagerMapEntry();
662 }
663
664 class SameOriginCheckerImpl final : public nsIChannelEventSink,
665 public nsIInterfaceRequestor {
666 ~SameOriginCheckerImpl() = default;
667
668 NS_DECL_ISUPPORTS
669 NS_DECL_NSICHANNELEVENTSINK
670 NS_DECL_NSIINTERFACEREQUESTOR
671 };
672
673 } // namespace
674
SuppressDocument(Document * aDoc)675 void AutoSuppressEventHandlingAndSuspend::SuppressDocument(Document* aDoc) {
676 // Note: Document::SuppressEventHandling will also automatically suppress
677 // event handling for any in-process sub-documents. However, since we need
678 // to deal with cases where remote BrowsingContexts may be interleaved
679 // with in-process ones, we still need to walk the entire tree ourselves.
680 // This may be slightly redundant in some cases, but since event handling
681 // suppressions maintain a count of current blockers, it does not cause
682 // any problems.
683 aDoc->SuppressEventHandling();
684 if (nsCOMPtr<nsPIDOMWindowInner> win = aDoc->GetInnerWindow()) {
685 win->Suspend();
686 mWindows.AppendElement(win);
687 }
688 }
689
UnsuppressDocument(Document * aDoc)690 void AutoSuppressEventHandlingAndSuspend::UnsuppressDocument(Document* aDoc) {
691 aDoc->UnsuppressEventHandlingAndFireEvents(true);
692 }
693
~AutoSuppressEventHandlingAndSuspend()694 AutoSuppressEventHandlingAndSuspend::~AutoSuppressEventHandlingAndSuspend() {
695 for (const auto& win : mWindows) {
696 win->Resume();
697 }
698 UnsuppressDocuments();
699 }
700
701 /**
702 * This class is used to determine whether or not the user is currently
703 * interacting with the browser. It listens to observer events to toggle the
704 * value of the sUserActive static.
705 *
706 * This class is an internal implementation detail.
707 * nsContentUtils::GetUserIsInteracting() should be used to access current
708 * user interaction status.
709 */
710 class nsContentUtils::UserInteractionObserver final
711 : public nsIObserver,
712 public BackgroundHangAnnotator {
713 public:
714 NS_DECL_ISUPPORTS
715 NS_DECL_NSIOBSERVER
716
717 void Init();
718 void Shutdown();
719 void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
720
721 static Atomic<bool> sUserActive;
722
723 private:
724 ~UserInteractionObserver() = default;
725 };
726
727 // static
Init()728 nsresult nsContentUtils::Init() {
729 if (sInitialized) {
730 NS_WARNING("Init() called twice");
731
732 return NS_OK;
733 }
734
735 nsHTMLTags::AddRefTable();
736
737 sNameSpaceManager = nsNameSpaceManager::GetInstance();
738 NS_ENSURE_TRUE(sNameSpaceManager, NS_ERROR_OUT_OF_MEMORY);
739 NS_ADDREF(sNameSpaceManager);
740
741 sXPConnect = nsXPConnect::XPConnect();
742 // We hold a strong ref to sXPConnect to ensure that it does not go away until
743 // nsLayoutStatics::Shutdown is happening. Otherwise ~nsXPConnect can be
744 // triggered by xpcModuleDtor late in shutdown and cause crashes due to
745 // various stuff already being torn down by then. Note that this means that
746 // we are effectively making sure that if we leak nsLayoutStatics then we also
747 // leak nsXPConnect.
748 NS_ADDREF(sXPConnect);
749
750 sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
751 if (!sSecurityManager) return NS_ERROR_FAILURE;
752 NS_ADDREF(sSecurityManager);
753
754 sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
755 MOZ_ASSERT(sSystemPrincipal);
756
757 RefPtr<NullPrincipal> nullPrincipal =
758 NullPrincipal::CreateWithoutOriginAttributes();
759 if (!nullPrincipal) {
760 return NS_ERROR_FAILURE;
761 }
762
763 nullPrincipal.forget(&sNullSubjectPrincipal);
764
765 nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
766 if (NS_FAILED(rv)) {
767 // This makes life easier, but we can live without it.
768
769 sIOService = nullptr;
770 }
771
772 sLineBreaker = mozilla::intl::LineBreaker::Create();
773
774 sWordBreaker = mozilla::intl::WordBreaker::Create();
775
776 if (!InitializeEventTable()) return NS_ERROR_FAILURE;
777
778 if (!sEventListenerManagersHash) {
779 static const PLDHashTableOps hash_table_ops = {
780 PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
781 PLDHashTable::MoveEntryStub, EventListenerManagerHashClearEntry,
782 EventListenerManagerHashInitEntry};
783
784 sEventListenerManagersHash =
785 new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
786
787 RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
788 }
789
790 sBlockedScriptRunners = new AutoTArray<nsCOMPtr<nsIRunnable>, 8>;
791
792 #ifndef RELEASE_OR_BETA
793 sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
794 #endif
795
796 Element::InitCCCallbacks();
797
798 Unused << nsRFPService::GetOrCreate();
799
800 nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
801 do_GetService("@mozilla.org/uuid-generator;1", &rv);
802 if (NS_WARN_IF(NS_FAILED(rv))) {
803 return rv;
804 }
805 uuidGenerator.forget(&sUUIDGenerator);
806
807 if (XRE_IsParentProcess()) {
808 AsyncPrecreateStringBundles();
809 }
810
811 RefPtr<UserInteractionObserver> uio = new UserInteractionObserver();
812 uio->Init();
813 uio.forget(&sUserInteractionObserver);
814
815 sInitialized = true;
816
817 return NS_OK;
818 }
819
InitJSBytecodeMimeType()820 bool nsContentUtils::InitJSBytecodeMimeType() {
821 MOZ_ASSERT(NS_IsMainThread());
822 MOZ_ASSERT(!sJSBytecodeMimeType);
823
824 JS::BuildIdCharVector jsBuildId;
825 if (!JS::GetScriptTranscodingBuildId(&jsBuildId)) {
826 return false;
827 }
828
829 nsDependentCSubstring jsBuildIdStr(jsBuildId.begin(), jsBuildId.length());
830 sJSBytecodeMimeType =
831 new nsCString("javascript/moz-bytecode-"_ns + jsBuildIdStr);
832 return true;
833 }
834
GetShiftText(nsAString & text)835 void nsContentUtils::GetShiftText(nsAString& text) {
836 if (!sShiftText) InitializeModifierStrings();
837 text.Assign(*sShiftText);
838 }
839
GetControlText(nsAString & text)840 void nsContentUtils::GetControlText(nsAString& text) {
841 if (!sControlText) InitializeModifierStrings();
842 text.Assign(*sControlText);
843 }
844
GetMetaText(nsAString & text)845 void nsContentUtils::GetMetaText(nsAString& text) {
846 if (!sMetaText) InitializeModifierStrings();
847 text.Assign(*sMetaText);
848 }
849
GetOSText(nsAString & text)850 void nsContentUtils::GetOSText(nsAString& text) {
851 if (!sOSText) {
852 InitializeModifierStrings();
853 }
854 text.Assign(*sOSText);
855 }
856
GetAltText(nsAString & text)857 void nsContentUtils::GetAltText(nsAString& text) {
858 if (!sAltText) InitializeModifierStrings();
859 text.Assign(*sAltText);
860 }
861
GetModifierSeparatorText(nsAString & text)862 void nsContentUtils::GetModifierSeparatorText(nsAString& text) {
863 if (!sModifierSeparator) InitializeModifierStrings();
864 text.Assign(*sModifierSeparator);
865 }
866
InitializeModifierStrings()867 void nsContentUtils::InitializeModifierStrings() {
868 // load the display strings for the keyboard accelerators
869 nsCOMPtr<nsIStringBundleService> bundleService =
870 mozilla::components::StringBundle::Service();
871 nsCOMPtr<nsIStringBundle> bundle;
872 DebugOnly<nsresult> rv = NS_OK;
873 if (bundleService) {
874 rv = bundleService->CreateBundle(
875 "chrome://global-platform/locale/platformKeys.properties",
876 getter_AddRefs(bundle));
877 }
878
879 NS_ASSERTION(
880 NS_SUCCEEDED(rv) && bundle,
881 "chrome://global/locale/platformKeys.properties could not be loaded");
882 nsAutoString shiftModifier;
883 nsAutoString metaModifier;
884 nsAutoString osModifier;
885 nsAutoString altModifier;
886 nsAutoString controlModifier;
887 nsAutoString modifierSeparator;
888 if (bundle) {
889 // macs use symbols for each modifier key, so fetch each from the bundle,
890 // which also covers i18n
891 bundle->GetStringFromName("VK_SHIFT", shiftModifier);
892 bundle->GetStringFromName("VK_META", metaModifier);
893 bundle->GetStringFromName("VK_WIN", osModifier);
894 bundle->GetStringFromName("VK_ALT", altModifier);
895 bundle->GetStringFromName("VK_CONTROL", controlModifier);
896 bundle->GetStringFromName("MODIFIER_SEPARATOR", modifierSeparator);
897 }
898 // if any of these don't exist, we get an empty string
899 sShiftText = new nsString(shiftModifier);
900 sMetaText = new nsString(metaModifier);
901 sOSText = new nsString(osModifier);
902 sAltText = new nsString(altModifier);
903 sControlText = new nsString(controlModifier);
904 sModifierSeparator = new nsString(modifierSeparator);
905 }
906
GetEventClassIDFromMessage(EventMessage aEventMessage)907 mozilla::EventClassID nsContentUtils::GetEventClassIDFromMessage(
908 EventMessage aEventMessage) {
909 switch (aEventMessage) {
910 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
911 case message_: \
912 return struct_;
913 #include "mozilla/EventNameList.h"
914 #undef MESSAGE_TO_EVENT
915 default:
916 MOZ_ASSERT_UNREACHABLE("Invalid event message?");
917 return eBasicEventClass;
918 }
919 }
920
GetEventTypeFromMessage(EventMessage aEventMessage)921 static nsAtom* GetEventTypeFromMessage(EventMessage aEventMessage) {
922 switch (aEventMessage) {
923 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
924 case message_: \
925 return nsGkAtoms::on##name_;
926 #include "mozilla/EventNameList.h"
927 #undef MESSAGE_TO_EVENT
928 default:
929 return nullptr;
930 }
931 }
932
933 // Because of SVG/SMIL we have several atoms mapped to the same
934 // id, but we can rely on MESSAGE_TO_EVENT to map id to only one atom.
ShouldAddEventToStringEventTable(const EventNameMapping & aMapping)935 static bool ShouldAddEventToStringEventTable(const EventNameMapping& aMapping) {
936 MOZ_ASSERT(aMapping.mAtom);
937 return GetEventTypeFromMessage(aMapping.mMessage) == aMapping.mAtom;
938 }
939
InitializeEventTable()940 bool nsContentUtils::InitializeEventTable() {
941 NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
942 NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
943
944 static const EventNameMapping eventArray[] = {
945 #define EVENT(name_, _message, _type, _class) \
946 {nsGkAtoms::on##name_, _type, _message, _class, false},
947 #define WINDOW_ONLY_EVENT EVENT
948 #define DOCUMENT_ONLY_EVENT EVENT
949 #define NON_IDL_EVENT EVENT
950 #include "mozilla/EventNameList.h"
951 #undef WINDOW_ONLY_EVENT
952 #undef NON_IDL_EVENT
953 #undef EVENT
954 {nullptr}};
955
956 sAtomEventTable = new nsTHashMap<nsRefPtrHashKey<nsAtom>, EventNameMapping>(
957 ArrayLength(eventArray));
958 sStringEventTable = new nsTHashMap<nsStringHashKey, EventNameMapping>(
959 ArrayLength(eventArray));
960 sUserDefinedEvents = new nsTArray<RefPtr<nsAtom>>(64);
961
962 // Subtract one from the length because of the trailing null
963 for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) {
964 MOZ_ASSERT(!sAtomEventTable->Contains(eventArray[i].mAtom),
965 "Double-defining event name; fix your EventNameList.h");
966 sAtomEventTable->InsertOrUpdate(eventArray[i].mAtom, eventArray[i]);
967 if (ShouldAddEventToStringEventTable(eventArray[i])) {
968 sStringEventTable->InsertOrUpdate(
969 Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
970 eventArray[i]);
971 }
972 }
973
974 return true;
975 }
976
InitializeTouchEventTable()977 void nsContentUtils::InitializeTouchEventTable() {
978 static bool sEventTableInitialized = false;
979 if (!sEventTableInitialized && sAtomEventTable && sStringEventTable) {
980 sEventTableInitialized = true;
981 static const EventNameMapping touchEventArray[] = {
982 #define EVENT(name_, _message, _type, _class)
983 #define TOUCH_EVENT(name_, _message, _type, _class) \
984 {nsGkAtoms::on##name_, _type, _message, _class},
985 #include "mozilla/EventNameList.h"
986 #undef TOUCH_EVENT
987 #undef EVENT
988 {nullptr}};
989 // Subtract one from the length because of the trailing null
990 for (uint32_t i = 0; i < ArrayLength(touchEventArray) - 1; ++i) {
991 sAtomEventTable->InsertOrUpdate(touchEventArray[i].mAtom,
992 touchEventArray[i]);
993 sStringEventTable->InsertOrUpdate(
994 Substring(nsDependentAtomString(touchEventArray[i].mAtom), 2),
995 touchEventArray[i]);
996 }
997 }
998 }
999
Is8bit(const nsAString & aString)1000 static bool Is8bit(const nsAString& aString) {
1001 static const char16_t EIGHT_BIT = char16_t(~0x00FF);
1002
1003 for (nsAString::const_char_iterator start = aString.BeginReading(),
1004 end = aString.EndReading();
1005 start != end; ++start) {
1006 if (*start & EIGHT_BIT) {
1007 return false;
1008 }
1009 }
1010
1011 return true;
1012 }
1013
Btoa(const nsAString & aBinaryData,nsAString & aAsciiBase64String)1014 nsresult nsContentUtils::Btoa(const nsAString& aBinaryData,
1015 nsAString& aAsciiBase64String) {
1016 if (!Is8bit(aBinaryData)) {
1017 aAsciiBase64String.Truncate();
1018 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1019 }
1020
1021 return Base64Encode(aBinaryData, aAsciiBase64String);
1022 }
1023
Atob(const nsAString & aAsciiBase64String,nsAString & aBinaryData)1024 nsresult nsContentUtils::Atob(const nsAString& aAsciiBase64String,
1025 nsAString& aBinaryData) {
1026 if (!Is8bit(aAsciiBase64String)) {
1027 aBinaryData.Truncate();
1028 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1029 }
1030
1031 const char16_t* start = aAsciiBase64String.BeginReading();
1032 const char16_t* cur = start;
1033 const char16_t* end = aAsciiBase64String.EndReading();
1034 bool hasWhitespace = false;
1035
1036 while (cur < end) {
1037 if (nsContentUtils::IsHTMLWhitespace(*cur)) {
1038 hasWhitespace = true;
1039 break;
1040 }
1041 cur++;
1042 }
1043
1044 nsresult rv;
1045
1046 if (hasWhitespace) {
1047 nsString trimmedString;
1048
1049 if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible)) {
1050 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1051 }
1052
1053 trimmedString.Append(start, cur - start);
1054
1055 while (cur < end) {
1056 if (!nsContentUtils::IsHTMLWhitespace(*cur)) {
1057 trimmedString.Append(*cur);
1058 }
1059 cur++;
1060 }
1061 rv = Base64Decode(trimmedString, aBinaryData);
1062 } else {
1063 rv = Base64Decode(aAsciiBase64String, aBinaryData);
1064 }
1065
1066 if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
1067 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
1068 }
1069 return rv;
1070 }
1071
IsAutocompleteEnabled(mozilla::dom::HTMLInputElement * aInput)1072 bool nsContentUtils::IsAutocompleteEnabled(
1073 mozilla::dom::HTMLInputElement* aInput) {
1074 MOZ_ASSERT(aInput, "aInput should not be null!");
1075
1076 nsAutoString autocomplete;
1077 aInput->GetAutocomplete(autocomplete);
1078
1079 if (autocomplete.IsEmpty()) {
1080 auto* form = aInput->GetForm();
1081 if (!form) {
1082 return true;
1083 }
1084
1085 form->GetAutocomplete(autocomplete);
1086 }
1087
1088 return !autocomplete.EqualsLiteral("off");
1089 }
1090
1091 nsContentUtils::AutocompleteAttrState
SerializeAutocompleteAttribute(const nsAttrValue * aAttr,nsAString & aResult,AutocompleteAttrState aCachedState)1092 nsContentUtils::SerializeAutocompleteAttribute(
1093 const nsAttrValue* aAttr, nsAString& aResult,
1094 AutocompleteAttrState aCachedState) {
1095 if (!aAttr ||
1096 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1097 return aCachedState;
1098 }
1099
1100 if (aCachedState == nsContentUtils::eAutocompleteAttrState_Valid) {
1101 uint32_t atomCount = aAttr->GetAtomCount();
1102 for (uint32_t i = 0; i < atomCount; i++) {
1103 if (i != 0) {
1104 aResult.Append(' ');
1105 }
1106 aResult.Append(nsDependentAtomString(aAttr->AtomAt(i)));
1107 }
1108 nsContentUtils::ASCIIToLower(aResult);
1109 return aCachedState;
1110 }
1111
1112 aResult.Truncate();
1113
1114 mozilla::dom::AutocompleteInfo info;
1115 AutocompleteAttrState state =
1116 InternalSerializeAutocompleteAttribute(aAttr, info);
1117 if (state == eAutocompleteAttrState_Valid) {
1118 // Concatenate the info fields.
1119 aResult = info.mSection;
1120
1121 if (!info.mAddressType.IsEmpty()) {
1122 if (!aResult.IsEmpty()) {
1123 aResult += ' ';
1124 }
1125 aResult += info.mAddressType;
1126 }
1127
1128 if (!info.mContactType.IsEmpty()) {
1129 if (!aResult.IsEmpty()) {
1130 aResult += ' ';
1131 }
1132 aResult += info.mContactType;
1133 }
1134
1135 if (!info.mFieldName.IsEmpty()) {
1136 if (!aResult.IsEmpty()) {
1137 aResult += ' ';
1138 }
1139 aResult += info.mFieldName;
1140 }
1141 }
1142
1143 return state;
1144 }
1145
1146 nsContentUtils::AutocompleteAttrState
SerializeAutocompleteAttribute(const nsAttrValue * aAttr,mozilla::dom::AutocompleteInfo & aInfo,AutocompleteAttrState aCachedState,bool aGrantAllValidValue)1147 nsContentUtils::SerializeAutocompleteAttribute(
1148 const nsAttrValue* aAttr, mozilla::dom::AutocompleteInfo& aInfo,
1149 AutocompleteAttrState aCachedState, bool aGrantAllValidValue) {
1150 if (!aAttr ||
1151 aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
1152 return aCachedState;
1153 }
1154
1155 return InternalSerializeAutocompleteAttribute(aAttr, aInfo,
1156 aGrantAllValidValue);
1157 }
1158
1159 /**
1160 * Helper to validate the @autocomplete tokens.
1161 *
1162 * @return {AutocompleteAttrState} The state of the attribute (invalid/valid).
1163 */
1164 nsContentUtils::AutocompleteAttrState
InternalSerializeAutocompleteAttribute(const nsAttrValue * aAttrVal,mozilla::dom::AutocompleteInfo & aInfo,bool aGrantAllValidValue)1165 nsContentUtils::InternalSerializeAutocompleteAttribute(
1166 const nsAttrValue* aAttrVal, mozilla::dom::AutocompleteInfo& aInfo,
1167 bool aGrantAllValidValue) {
1168 // No autocomplete attribute so we are done
1169 if (!aAttrVal) {
1170 return eAutocompleteAttrState_Invalid;
1171 }
1172
1173 uint32_t numTokens = aAttrVal->GetAtomCount();
1174 if (!numTokens) {
1175 return eAutocompleteAttrState_Invalid;
1176 }
1177
1178 uint32_t index = numTokens - 1;
1179 nsString tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1180 AutocompleteCategory category;
1181 nsAttrValue enumValue;
1182
1183 bool unsupported = false;
1184 if (!aGrantAllValidValue) {
1185 unsupported = enumValue.ParseEnumValue(
1186 tokenString, kAutocompleteUnsupportedFieldNameTable, false);
1187 if (unsupported) {
1188 return eAutocompleteAttrState_Invalid;
1189 }
1190 }
1191
1192 nsAutoString str;
1193 bool result =
1194 enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
1195 if (result) {
1196 // Off/Automatic/Normal categories.
1197 if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
1198 enumValue.Equals(u"on"_ns, eIgnoreCase)) {
1199 if (numTokens > 1) {
1200 return eAutocompleteAttrState_Invalid;
1201 }
1202 enumValue.ToString(str);
1203 ASCIIToLower(str);
1204 aInfo.mFieldName.Assign(str);
1205 aInfo.mCanAutomaticallyPersist =
1206 !enumValue.Equals(u"off"_ns, eIgnoreCase);
1207 return eAutocompleteAttrState_Valid;
1208 }
1209
1210 // Only allow on/off if form autofill @autocomplete values aren't enabled
1211 // and it doesn't grant all valid values.
1212 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1213 !aGrantAllValidValue) {
1214 return eAutocompleteAttrState_Invalid;
1215 }
1216
1217 // Normal category
1218 if (numTokens > 3) {
1219 return eAutocompleteAttrState_Invalid;
1220 }
1221 category = eAutocompleteCategory_NORMAL;
1222 } else { // Check if the last token is of the contact category instead.
1223 // Only allow on/off if form autofill @autocomplete values aren't enabled
1224 // and it doesn't grant all valid values.
1225 if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
1226 !aGrantAllValidValue) {
1227 return eAutocompleteAttrState_Invalid;
1228 }
1229
1230 result = enumValue.ParseEnumValue(
1231 tokenString, kAutocompleteContactFieldNameTable, false);
1232 if (!result || numTokens > 4) {
1233 return eAutocompleteAttrState_Invalid;
1234 }
1235
1236 category = eAutocompleteCategory_CONTACT;
1237 }
1238
1239 enumValue.ToString(str);
1240 ASCIIToLower(str);
1241 aInfo.mFieldName.Assign(str);
1242
1243 aInfo.mCanAutomaticallyPersist = !enumValue.ParseEnumValue(
1244 tokenString, kAutocompleteNoPersistFieldNameTable, false);
1245
1246 // We are done if this was the only token.
1247 if (numTokens == 1) {
1248 return eAutocompleteAttrState_Valid;
1249 }
1250
1251 --index;
1252 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1253
1254 if (category == eAutocompleteCategory_CONTACT) {
1255 if (!aGrantAllValidValue) {
1256 unsupported = enumValue.ParseEnumValue(
1257 tokenString, kAutocompleteUnsupportedContactFieldHintTable, false);
1258 if (unsupported) {
1259 return eAutocompleteAttrState_Invalid;
1260 }
1261 }
1262
1263 nsAttrValue contactFieldHint;
1264 result = contactFieldHint.ParseEnumValue(
1265 tokenString, kAutocompleteContactFieldHintTable, false);
1266 if (result) {
1267 nsAutoString contactFieldHintString;
1268 contactFieldHint.ToString(contactFieldHintString);
1269 ASCIIToLower(contactFieldHintString);
1270 aInfo.mContactType.Assign(contactFieldHintString);
1271 if (index == 0) {
1272 return eAutocompleteAttrState_Valid;
1273 }
1274 --index;
1275 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1276 }
1277 }
1278
1279 // Check for billing/shipping tokens
1280 nsAttrValue fieldHint;
1281 if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable,
1282 false)) {
1283 nsString fieldHintString;
1284 fieldHint.ToString(fieldHintString);
1285 ASCIIToLower(fieldHintString);
1286 aInfo.mAddressType.Assign(fieldHintString);
1287 if (index == 0) {
1288 return eAutocompleteAttrState_Valid;
1289 }
1290 --index;
1291 tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
1292 }
1293
1294 // Check for section-* token
1295 const nsDependentSubstring& section = Substring(tokenString, 0, 8);
1296 if (section.LowerCaseEqualsASCII("section-")) {
1297 ASCIIToLower(tokenString);
1298 aInfo.mSection.Assign(tokenString);
1299 if (index == 0) {
1300 return eAutocompleteAttrState_Valid;
1301 }
1302 }
1303
1304 // Clear the fields as the autocomplete attribute is invalid.
1305 aInfo.mSection.Truncate();
1306 aInfo.mAddressType.Truncate();
1307 aInfo.mContactType.Truncate();
1308 aInfo.mFieldName.Truncate();
1309
1310 return eAutocompleteAttrState_Invalid;
1311 }
1312
1313 // Parse an integer according to HTML spec
1314 template <class StringT>
ParseHTMLIntegerImpl(const StringT & aValue,ParseHTMLIntegerResultFlags * aResult)1315 int32_t nsContentUtils::ParseHTMLIntegerImpl(
1316 const StringT& aValue, ParseHTMLIntegerResultFlags* aResult) {
1317 using CharT = typename StringT::char_type;
1318
1319 int result = eParseHTMLInteger_NoFlags;
1320
1321 typename StringT::const_iterator iter, end;
1322 aValue.BeginReading(iter);
1323 aValue.EndReading(end);
1324
1325 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1326 result |= eParseHTMLInteger_NonStandard;
1327 ++iter;
1328 }
1329
1330 if (iter == end) {
1331 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1332 *aResult = (ParseHTMLIntegerResultFlags)result;
1333 return 0;
1334 }
1335
1336 int sign = 1;
1337 if (*iter == CharT('-')) {
1338 sign = -1;
1339 result |= eParseHTMLInteger_Negative;
1340 ++iter;
1341 } else if (*iter == CharT('+')) {
1342 result |= eParseHTMLInteger_NonStandard;
1343 ++iter;
1344 }
1345
1346 bool foundValue = false;
1347 CheckedInt32 value = 0;
1348
1349 // Check for leading zeros first.
1350 uint64_t leadingZeros = 0;
1351 while (iter != end) {
1352 if (*iter != CharT('0')) {
1353 break;
1354 }
1355
1356 ++leadingZeros;
1357 foundValue = true;
1358 ++iter;
1359 }
1360
1361 while (iter != end) {
1362 if (*iter >= CharT('0') && *iter <= CharT('9')) {
1363 value = (value * 10) + (*iter - CharT('0')) * sign;
1364 ++iter;
1365 if (!value.isValid()) {
1366 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
1367 break;
1368 }
1369 foundValue = true;
1370 } else {
1371 break;
1372 }
1373 }
1374
1375 if (!foundValue) {
1376 result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
1377 }
1378
1379 if (value.isValid() &&
1380 ((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
1381 (sign == -1 && value == 0))) {
1382 result |= eParseHTMLInteger_NonStandard;
1383 }
1384
1385 if (iter != end) {
1386 result |= eParseHTMLInteger_DidNotConsumeAllInput;
1387 }
1388
1389 *aResult = (ParseHTMLIntegerResultFlags)result;
1390 return value.isValid() ? value.value() : 0;
1391 }
1392
1393 // Parse an integer according to HTML spec
ParseHTMLInteger(const nsAString & aValue,ParseHTMLIntegerResultFlags * aResult)1394 int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
1395 ParseHTMLIntegerResultFlags* aResult) {
1396 return ParseHTMLIntegerImpl(aValue, aResult);
1397 }
1398
ParseHTMLInteger(const nsACString & aValue,ParseHTMLIntegerResultFlags * aResult)1399 int32_t nsContentUtils::ParseHTMLInteger(const nsACString& aValue,
1400 ParseHTMLIntegerResultFlags* aResult) {
1401 return ParseHTMLIntegerImpl(aValue, aResult);
1402 }
1403
1404 #define SKIP_WHITESPACE(iter, end_iter, end_res) \
1405 while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
1406 ++(iter); \
1407 } \
1408 if ((iter) == (end_iter)) { \
1409 return (end_res); \
1410 }
1411
1412 #define SKIP_ATTR_NAME(iter, end_iter) \
1413 while ((iter) != (end_iter) && !nsCRT::IsAsciiSpace(*(iter)) && \
1414 *(iter) != '=') { \
1415 ++(iter); \
1416 }
1417
GetPseudoAttributeValue(const nsString & aSource,nsAtom * aName,nsAString & aValue)1418 bool nsContentUtils::GetPseudoAttributeValue(const nsString& aSource,
1419 nsAtom* aName, nsAString& aValue) {
1420 aValue.Truncate();
1421
1422 const char16_t* start = aSource.get();
1423 const char16_t* end = start + aSource.Length();
1424 const char16_t* iter;
1425
1426 while (start != end) {
1427 SKIP_WHITESPACE(start, end, false)
1428 iter = start;
1429 SKIP_ATTR_NAME(iter, end)
1430
1431 if (start == iter) {
1432 return false;
1433 }
1434
1435 // Remember the attr name.
1436 const nsDependentSubstring& attrName = Substring(start, iter);
1437
1438 // Now check whether this is a valid name="value" pair.
1439 start = iter;
1440 SKIP_WHITESPACE(start, end, false)
1441 if (*start != '=') {
1442 // No '=', so this is not a name="value" pair. We don't know
1443 // what it is, and we have no way to handle it.
1444 return false;
1445 }
1446
1447 // Have to skip the value.
1448 ++start;
1449 SKIP_WHITESPACE(start, end, false)
1450 char16_t q = *start;
1451 if (q != kQuote && q != kApostrophe) {
1452 // Not a valid quoted value, so bail.
1453 return false;
1454 }
1455
1456 ++start; // Point to the first char of the value.
1457 iter = start;
1458
1459 while (iter != end && *iter != q) {
1460 ++iter;
1461 }
1462
1463 if (iter == end) {
1464 // Oops, unterminated quoted string.
1465 return false;
1466 }
1467
1468 // At this point attrName holds the name of the "attribute" and
1469 // the value is between start and iter.
1470
1471 if (aName->Equals(attrName)) {
1472 // We'll accumulate as many characters as possible (until we hit either
1473 // the end of the string or the beginning of an entity). Chunks will be
1474 // delimited by start and chunkEnd.
1475 const char16_t* chunkEnd = start;
1476 while (chunkEnd != iter) {
1477 if (*chunkEnd == kLessThan) {
1478 aValue.Truncate();
1479
1480 return false;
1481 }
1482
1483 if (*chunkEnd == kAmpersand) {
1484 aValue.Append(start, chunkEnd - start);
1485
1486 const char16_t* afterEntity = nullptr;
1487 char16_t result[2];
1488 uint32_t count = MOZ_XMLTranslateEntity(
1489 reinterpret_cast<const char*>(chunkEnd),
1490 reinterpret_cast<const char*>(iter),
1491 reinterpret_cast<const char**>(&afterEntity), result);
1492 if (count == 0) {
1493 aValue.Truncate();
1494
1495 return false;
1496 }
1497
1498 aValue.Append(result, count);
1499
1500 // Advance to after the entity and begin a new chunk.
1501 start = chunkEnd = afterEntity;
1502 } else {
1503 ++chunkEnd;
1504 }
1505 }
1506
1507 // Append remainder.
1508 aValue.Append(start, iter - start);
1509
1510 return true;
1511 }
1512
1513 // Resume scanning after the end of the attribute value (past the quote
1514 // char).
1515 start = iter + 1;
1516 }
1517
1518 return false;
1519 }
1520
IsJavaScriptLanguage(const nsString & aName)1521 bool nsContentUtils::IsJavaScriptLanguage(const nsString& aName) {
1522 return aName.LowerCaseEqualsLiteral("javascript") ||
1523 aName.LowerCaseEqualsLiteral("livescript") ||
1524 aName.LowerCaseEqualsLiteral("mocha") ||
1525 aName.LowerCaseEqualsLiteral("javascript1.0") ||
1526 aName.LowerCaseEqualsLiteral("javascript1.1") ||
1527 aName.LowerCaseEqualsLiteral("javascript1.2") ||
1528 aName.LowerCaseEqualsLiteral("javascript1.3") ||
1529 aName.LowerCaseEqualsLiteral("javascript1.4") ||
1530 aName.LowerCaseEqualsLiteral("javascript1.5");
1531 }
1532
SplitMimeType(const nsAString & aValue,nsString & aType,nsString & aParams)1533 void nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType,
1534 nsString& aParams) {
1535 aType.Truncate();
1536 aParams.Truncate();
1537 int32_t semiIndex = aValue.FindChar(char16_t(';'));
1538 if (-1 != semiIndex) {
1539 aType = Substring(aValue, 0, semiIndex);
1540 aParams =
1541 Substring(aValue, semiIndex + 1, aValue.Length() - (semiIndex + 1));
1542 aParams.StripWhitespace();
1543 } else {
1544 aType = aValue;
1545 }
1546 aType.StripWhitespace();
1547 }
1548
IsUserIdle(uint32_t aRequestedIdleTimeInMS,bool * aUserIsIdle)1549 nsresult nsContentUtils::IsUserIdle(uint32_t aRequestedIdleTimeInMS,
1550 bool* aUserIsIdle) {
1551 nsresult rv;
1552 nsCOMPtr<nsIUserIdleService> idleService =
1553 do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
1554 NS_ENSURE_SUCCESS(rv, rv);
1555
1556 uint32_t idleTimeInMS;
1557 rv = idleService->GetIdleTime(&idleTimeInMS);
1558 NS_ENSURE_SUCCESS(rv, rv);
1559
1560 *aUserIsIdle = idleTimeInMS >= aRequestedIdleTimeInMS;
1561 return NS_OK;
1562 }
1563
1564 /**
1565 * A helper function that parses a sandbox attribute (of an <iframe> or a CSP
1566 * directive) and converts it to the set of flags used internally.
1567 *
1568 * @param aSandboxAttr the sandbox attribute
1569 * @return the set of flags (SANDBOXED_NONE if aSandboxAttr is
1570 * null)
1571 */
ParseSandboxAttributeToFlags(const nsAttrValue * aSandboxAttr)1572 uint32_t nsContentUtils::ParseSandboxAttributeToFlags(
1573 const nsAttrValue* aSandboxAttr) {
1574 if (!aSandboxAttr) {
1575 return SANDBOXED_NONE;
1576 }
1577
1578 uint32_t out = SANDBOX_ALL_FLAGS;
1579
1580 #define SANDBOX_KEYWORD(string, atom, flags) \
1581 if (aSandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { \
1582 out &= ~(flags); \
1583 }
1584 #include "IframeSandboxKeywordList.h"
1585 #undef SANDBOX_KEYWORD
1586
1587 return out;
1588 }
1589
1590 /**
1591 * A helper function that checks if a string matches a valid sandbox flag.
1592 *
1593 * @param aFlag the potential sandbox flag.
1594 * @return true if the flag is a sandbox flag.
1595 */
IsValidSandboxFlag(const nsAString & aFlag)1596 bool nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag) {
1597 #define SANDBOX_KEYWORD(string, atom, flags) \
1598 if (EqualsIgnoreASCIICase(nsDependentAtomString(nsGkAtoms::atom), aFlag)) { \
1599 return true; \
1600 }
1601 #include "IframeSandboxKeywordList.h"
1602 #undef SANDBOX_KEYWORD
1603 return false;
1604 }
1605
1606 /**
1607 * A helper function that returns a string attribute corresponding to the
1608 * sandbox flags.
1609 *
1610 * @param aFlags the sandbox flags
1611 * @param aString the attribute corresponding to the flags (null if aFlags
1612 * is zero)
1613 */
SandboxFlagsToString(uint32_t aFlags,nsAString & aString)1614 void nsContentUtils::SandboxFlagsToString(uint32_t aFlags, nsAString& aString) {
1615 if (!aFlags) {
1616 SetDOMStringToNull(aString);
1617 return;
1618 }
1619
1620 aString.Truncate();
1621
1622 #define SANDBOX_KEYWORD(string, atom, flags) \
1623 if (!(aFlags & (flags))) { \
1624 if (!aString.IsEmpty()) { \
1625 aString.AppendLiteral(u" "); \
1626 } \
1627 aString.Append(nsDependentAtomString(nsGkAtoms::atom)); \
1628 }
1629 #include "IframeSandboxKeywordList.h"
1630 #undef SANDBOX_KEYWORD
1631 }
1632
GetBidiKeyboard()1633 nsIBidiKeyboard* nsContentUtils::GetBidiKeyboard() {
1634 if (!sBidiKeyboard) {
1635 sBidiKeyboard = nsIWidget::CreateBidiKeyboard();
1636 }
1637 return sBidiKeyboard;
1638 }
1639
1640 /**
1641 * This is used to determine whether a character is in one of the classes
1642 * which CSS says should be part of the first-letter. Currently, that is
1643 * all punctuation classes (P*). Note that this is a change from CSS2
1644 * which excluded Pc and Pd.
1645 *
1646 * https://www.w3.org/TR/css-pseudo-4/#first-letter-pseudo
1647 * "Punctuation (i.e, characters that belong to the Punctuation (P*) Unicode
1648 * general category [UAX44]) [...]"
1649 */
1650
1651 // static
IsFirstLetterPunctuation(uint32_t aChar)1652 bool nsContentUtils::IsFirstLetterPunctuation(uint32_t aChar) {
1653 switch (mozilla::unicode::GetGeneralCategory(aChar)) {
1654 case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
1655 case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION: /* Pd */
1656 case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION: /* Pe */
1657 case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION: /* Pf */
1658 case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
1659 case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION: /* Po */
1660 case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION: /* Ps */
1661 return true;
1662 default:
1663 return false;
1664 }
1665 }
1666
1667 // static
IsAlphanumeric(uint32_t aChar)1668 bool nsContentUtils::IsAlphanumeric(uint32_t aChar) {
1669 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1670
1671 return (cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber);
1672 }
1673
1674 // static
IsAlphanumericOrSymbol(uint32_t aChar)1675 bool nsContentUtils::IsAlphanumericOrSymbol(uint32_t aChar) {
1676 nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1677
1678 return cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber ||
1679 cat == nsUGenCategory::kSymbol;
1680 }
1681
1682 // static
IsAlphanumericOrSymbolAt(const nsTextFragment * aFrag,uint32_t aOffset)1683 bool nsContentUtils::IsAlphanumericOrSymbolAt(const nsTextFragment* aFrag,
1684 uint32_t aOffset) {
1685 char16_t h = aFrag->CharAt(aOffset);
1686 if (!IS_SURROGATE(h)) {
1687 return IsAlphanumericOrSymbol(h);
1688 }
1689 if (NS_IS_HIGH_SURROGATE(h) && aOffset + 1 < aFrag->GetLength()) {
1690 char16_t l = aFrag->CharAt(aOffset + 1);
1691 if (NS_IS_LOW_SURROGATE(l)) {
1692 return IsAlphanumericOrSymbol(SURROGATE_TO_UCS4(h, l));
1693 }
1694 }
1695 return false;
1696 }
1697
1698 /* static */
IsHTMLWhitespace(char16_t aChar)1699 bool nsContentUtils::IsHTMLWhitespace(char16_t aChar) {
1700 return aChar == char16_t(0x0009) || aChar == char16_t(0x000A) ||
1701 aChar == char16_t(0x000C) || aChar == char16_t(0x000D) ||
1702 aChar == char16_t(0x0020);
1703 }
1704
1705 /* static */
IsHTMLWhitespaceOrNBSP(char16_t aChar)1706 bool nsContentUtils::IsHTMLWhitespaceOrNBSP(char16_t aChar) {
1707 return IsHTMLWhitespace(aChar) || aChar == char16_t(0xA0);
1708 }
1709
1710 /* static */
IsHTMLBlockLevelElement(nsIContent * aContent)1711 bool nsContentUtils::IsHTMLBlockLevelElement(nsIContent* aContent) {
1712 return aContent->IsAnyOfHTMLElements(
1713 nsGkAtoms::address, nsGkAtoms::article, nsGkAtoms::aside,
1714 nsGkAtoms::blockquote, nsGkAtoms::center, nsGkAtoms::dir, nsGkAtoms::div,
1715 nsGkAtoms::dl, // XXX why not dt and dd?
1716 nsGkAtoms::fieldset,
1717 nsGkAtoms::figure, // XXX shouldn't figcaption be on this list
1718 nsGkAtoms::footer, nsGkAtoms::form, nsGkAtoms::h1, nsGkAtoms::h2,
1719 nsGkAtoms::h3, nsGkAtoms::h4, nsGkAtoms::h5, nsGkAtoms::h6,
1720 nsGkAtoms::header, nsGkAtoms::hgroup, nsGkAtoms::hr, nsGkAtoms::li,
1721 nsGkAtoms::listing, nsGkAtoms::menu, nsGkAtoms::nav, nsGkAtoms::ol,
1722 nsGkAtoms::p, nsGkAtoms::pre, nsGkAtoms::section, nsGkAtoms::table,
1723 nsGkAtoms::ul, nsGkAtoms::xmp);
1724 }
1725
1726 /* static */
ParseIntMarginValue(const nsAString & aString,nsIntMargin & result)1727 bool nsContentUtils::ParseIntMarginValue(const nsAString& aString,
1728 nsIntMargin& result) {
1729 nsAutoString marginStr(aString);
1730 marginStr.CompressWhitespace(true, true);
1731 if (marginStr.IsEmpty()) {
1732 return false;
1733 }
1734
1735 int32_t start = 0, end = 0;
1736 for (int count = 0; count < 4; count++) {
1737 if ((uint32_t)end >= marginStr.Length()) return false;
1738
1739 // top, right, bottom, left
1740 if (count < 3)
1741 end = Substring(marginStr, start).FindChar(',');
1742 else
1743 end = Substring(marginStr, start).Length();
1744
1745 if (end <= 0) return false;
1746
1747 nsresult ec;
1748 int32_t val = nsString(Substring(marginStr, start, end)).ToInteger(&ec);
1749 if (NS_FAILED(ec)) return false;
1750
1751 switch (count) {
1752 case 0:
1753 result.top = val;
1754 break;
1755 case 1:
1756 result.right = val;
1757 break;
1758 case 2:
1759 result.bottom = val;
1760 break;
1761 case 3:
1762 result.left = val;
1763 break;
1764 }
1765 start += end + 1;
1766 }
1767 return true;
1768 }
1769
1770 // static
ParseLegacyFontSize(const nsAString & aValue)1771 int32_t nsContentUtils::ParseLegacyFontSize(const nsAString& aValue) {
1772 nsAString::const_iterator iter, end;
1773 aValue.BeginReading(iter);
1774 aValue.EndReading(end);
1775
1776 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1777 ++iter;
1778 }
1779
1780 if (iter == end) {
1781 return 0;
1782 }
1783
1784 bool relative = false;
1785 bool negate = false;
1786 if (*iter == char16_t('-')) {
1787 relative = true;
1788 negate = true;
1789 ++iter;
1790 } else if (*iter == char16_t('+')) {
1791 relative = true;
1792 ++iter;
1793 }
1794
1795 if (iter == end || *iter < char16_t('0') || *iter > char16_t('9')) {
1796 return 0;
1797 }
1798
1799 // We don't have to worry about overflow, since we can bail out as soon as
1800 // we're bigger than 7.
1801 int32_t value = 0;
1802 while (iter != end && *iter >= char16_t('0') && *iter <= char16_t('9')) {
1803 value = 10 * value + (*iter - char16_t('0'));
1804 if (value >= 7) {
1805 break;
1806 }
1807 ++iter;
1808 }
1809
1810 if (relative) {
1811 if (negate) {
1812 value = 3 - value;
1813 } else {
1814 value = 3 + value;
1815 }
1816 }
1817
1818 return clamped(value, 1, 7);
1819 }
1820
1821 /* static */
GetOfflineAppManifest(Document * aDocument,nsIURI ** aURI)1822 void nsContentUtils::GetOfflineAppManifest(Document* aDocument, nsIURI** aURI) {
1823 MOZ_ASSERT(NS_IsMainThread());
1824 MOZ_ASSERT(aDocument);
1825 *aURI = nullptr;
1826
1827 if (aDocument->GetController().isSome()) {
1828 return;
1829 }
1830
1831 Element* docElement = aDocument->GetRootElement();
1832 if (!docElement) {
1833 return;
1834 }
1835
1836 nsAutoString manifestSpec;
1837 docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
1838
1839 // Manifest URIs can't have fragment identifiers.
1840 if (manifestSpec.IsEmpty() || manifestSpec.Contains('#')) {
1841 return;
1842 }
1843
1844 nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec, aDocument,
1845 aDocument->GetDocBaseURI());
1846 }
1847
1848 /* static */
OfflineAppAllowed(nsIURI * aURI)1849 bool nsContentUtils::OfflineAppAllowed(nsIURI* aURI) { return false; }
1850
1851 /* static */
OfflineAppAllowed(nsIPrincipal * aPrincipal)1852 bool nsContentUtils::OfflineAppAllowed(nsIPrincipal* aPrincipal) {
1853 return false;
1854 }
1855 // Static
IsErrorPage(nsIURI * aURI)1856 bool nsContentUtils::IsErrorPage(nsIURI* aURI) {
1857 if (!aURI) {
1858 return false;
1859 }
1860
1861 if (!aURI->SchemeIs("about")) {
1862 return false;
1863 }
1864
1865 nsAutoCString name;
1866 nsresult rv = NS_GetAboutModuleName(aURI, name);
1867 NS_ENSURE_SUCCESS(rv, false);
1868
1869 return name.EqualsLiteral("certerror") || name.EqualsLiteral("neterror") ||
1870 name.EqualsLiteral("blocked");
1871 }
1872
1873 // static
Shutdown()1874 void nsContentUtils::Shutdown() {
1875 sInitialized = false;
1876
1877 nsHTMLTags::ReleaseTable();
1878
1879 NS_IF_RELEASE(sContentPolicyService);
1880 sTriedToGetContentPolicy = false;
1881 uint32_t i;
1882 for (i = 0; i < PropertiesFile_COUNT; ++i) NS_IF_RELEASE(sStringBundles[i]);
1883
1884 NS_IF_RELEASE(sStringBundleService);
1885 NS_IF_RELEASE(sConsoleService);
1886 NS_IF_RELEASE(sXPConnect);
1887 NS_IF_RELEASE(sSecurityManager);
1888 NS_IF_RELEASE(sSystemPrincipal);
1889 NS_IF_RELEASE(sNullSubjectPrincipal);
1890 NS_IF_RELEASE(sIOService);
1891 NS_IF_RELEASE(sUUIDGenerator);
1892 NS_IF_RELEASE(sNameSpaceManager);
1893 sLineBreaker = nullptr;
1894 sWordBreaker = nullptr;
1895 sBidiKeyboard = nullptr;
1896
1897 delete sAtomEventTable;
1898 sAtomEventTable = nullptr;
1899 delete sStringEventTable;
1900 sStringEventTable = nullptr;
1901 delete sUserDefinedEvents;
1902 sUserDefinedEvents = nullptr;
1903
1904 if (sEventListenerManagersHash) {
1905 NS_ASSERTION(sEventListenerManagersHash->EntryCount() == 0,
1906 "Event listener manager hash not empty at shutdown!");
1907
1908 // See comment above.
1909
1910 // However, we have to handle this table differently. If it still
1911 // has entries, we want to leak it too, so that we can keep it alive
1912 // in case any elements are destroyed. Because if they are, we need
1913 // their event listener managers to be destroyed too, or otherwise
1914 // it could leave dangling references in DOMClassInfo's preserved
1915 // wrapper table.
1916
1917 if (sEventListenerManagersHash->EntryCount() == 0) {
1918 delete sEventListenerManagersHash;
1919 sEventListenerManagersHash = nullptr;
1920 }
1921 }
1922
1923 if (sDOMArenaHashtable) {
1924 MOZ_ASSERT(sDOMArenaHashtable->Count() == 0);
1925 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
1926 delete sDOMArenaHashtable;
1927 sDOMArenaHashtable = nullptr;
1928 }
1929
1930 NS_ASSERTION(!sBlockedScriptRunners || sBlockedScriptRunners->Length() == 0,
1931 "How'd this happen?");
1932 delete sBlockedScriptRunners;
1933 sBlockedScriptRunners = nullptr;
1934
1935 delete sShiftText;
1936 sShiftText = nullptr;
1937 delete sControlText;
1938 sControlText = nullptr;
1939 delete sMetaText;
1940 sMetaText = nullptr;
1941 delete sOSText;
1942 sOSText = nullptr;
1943 delete sAltText;
1944 sAltText = nullptr;
1945 delete sModifierSeparator;
1946 sModifierSeparator = nullptr;
1947
1948 delete sJSBytecodeMimeType;
1949 sJSBytecodeMimeType = nullptr;
1950
1951 NS_IF_RELEASE(sSameOriginChecker);
1952
1953 if (sUserInteractionObserver) {
1954 sUserInteractionObserver->Shutdown();
1955 NS_RELEASE(sUserInteractionObserver);
1956 }
1957
1958 TextControlState::Shutdown();
1959 nsMappedAttributes::Shutdown();
1960 }
1961
1962 /**
1963 * Checks whether two nodes come from the same origin. aTrustedNode is
1964 * considered 'safe' in that a user can operate on it.
1965 */
1966 // static
CheckSameOrigin(const nsINode * aTrustedNode,const nsINode * unTrustedNode)1967 nsresult nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
1968 const nsINode* unTrustedNode) {
1969 MOZ_ASSERT(aTrustedNode);
1970 MOZ_ASSERT(unTrustedNode);
1971
1972 /*
1973 * Get hold of each node's principal
1974 */
1975
1976 nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
1977 nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
1978
1979 if (trustedPrincipal == unTrustedPrincipal) {
1980 return NS_OK;
1981 }
1982
1983 bool equal;
1984 // XXXbz should we actually have a Subsumes() check here instead? Or perhaps
1985 // a separate method for that, with callers using one or the other?
1986 if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
1987 !equal) {
1988 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
1989 }
1990
1991 return NS_OK;
1992 }
1993
1994 // static
CanCallerAccess(nsIPrincipal * aSubjectPrincipal,nsIPrincipal * aPrincipal)1995 bool nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
1996 nsIPrincipal* aPrincipal) {
1997 bool subsumes;
1998 nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
1999 NS_ENSURE_SUCCESS(rv, false);
2000
2001 if (subsumes) {
2002 return true;
2003 }
2004
2005 // The subject doesn't subsume aPrincipal. Allow access only if the subject
2006 // is chrome.
2007 return IsCallerChrome();
2008 }
2009
2010 // static
CanCallerAccess(const nsINode * aNode)2011 bool nsContentUtils::CanCallerAccess(const nsINode* aNode) {
2012 nsIPrincipal* subject = SubjectPrincipal();
2013 if (subject->IsSystemPrincipal()) {
2014 return true;
2015 }
2016
2017 if (aNode->ChromeOnlyAccess()) {
2018 return false;
2019 }
2020
2021 return CanCallerAccess(subject, aNode->NodePrincipal());
2022 }
2023
2024 // static
CanCallerAccess(nsPIDOMWindowInner * aWindow)2025 bool nsContentUtils::CanCallerAccess(nsPIDOMWindowInner* aWindow) {
2026 nsCOMPtr<nsIScriptObjectPrincipal> scriptObject = do_QueryInterface(aWindow);
2027 NS_ENSURE_TRUE(scriptObject, false);
2028
2029 return CanCallerAccess(SubjectPrincipal(), scriptObject->GetPrincipal());
2030 }
2031
2032 // static
PrincipalHasPermission(nsIPrincipal & aPrincipal,const nsAtom * aPerm)2033 bool nsContentUtils::PrincipalHasPermission(nsIPrincipal& aPrincipal,
2034 const nsAtom* aPerm) {
2035 // Chrome gets access by default.
2036 if (aPrincipal.IsSystemPrincipal()) {
2037 return true;
2038 }
2039
2040 // Otherwise, only allow if caller is an addon with the permission.
2041 return BasePrincipal::Cast(aPrincipal).AddonHasPermission(aPerm);
2042 }
2043
2044 // static
CallerHasPermission(JSContext * aCx,const nsAtom * aPerm)2045 bool nsContentUtils::CallerHasPermission(JSContext* aCx, const nsAtom* aPerm) {
2046 return PrincipalHasPermission(*SubjectPrincipal(aCx), aPerm);
2047 }
2048
2049 // static
GetAttrTriggeringPrincipal(nsIContent * aContent,const nsAString & aAttrValue,nsIPrincipal * aSubjectPrincipal)2050 nsIPrincipal* nsContentUtils::GetAttrTriggeringPrincipal(
2051 nsIContent* aContent, const nsAString& aAttrValue,
2052 nsIPrincipal* aSubjectPrincipal) {
2053 nsIPrincipal* contentPrin = aContent ? aContent->NodePrincipal() : nullptr;
2054
2055 // If the subject principal is the same as the content principal, or no
2056 // explicit subject principal was provided, we don't need to do any further
2057 // checks. Just return the content principal.
2058 if (contentPrin == aSubjectPrincipal || !aSubjectPrincipal) {
2059 return contentPrin;
2060 }
2061
2062 // Only use the subject principal if the URL string we are going to end up
2063 // fetching is under the control of that principal, which is never the case
2064 // for relative URLs.
2065 if (aAttrValue.IsEmpty() ||
2066 !IsAbsoluteURL(NS_ConvertUTF16toUTF8(aAttrValue))) {
2067 return contentPrin;
2068 }
2069
2070 // Only use the subject principal as the attr triggering principal if it
2071 // should override the CSP of the node's principal.
2072 if (BasePrincipal::Cast(aSubjectPrincipal)->OverridesCSP(contentPrin)) {
2073 return aSubjectPrincipal;
2074 }
2075
2076 return contentPrin;
2077 }
2078
2079 // static
IsAbsoluteURL(const nsACString & aURL)2080 bool nsContentUtils::IsAbsoluteURL(const nsACString& aURL) {
2081 nsAutoCString scheme;
2082 if (NS_FAILED(net_ExtractURLScheme(aURL, scheme))) {
2083 // If we can't extract a scheme, it's not an absolute URL.
2084 return false;
2085 }
2086
2087 // If it parses as an absolute StandardURL, it's definitely an absolute URL,
2088 // so no need to check with the IO service.
2089 if (net_IsAbsoluteURL(aURL)) {
2090 return true;
2091 }
2092
2093 uint32_t flags;
2094 if (NS_SUCCEEDED(sIOService->GetProtocolFlags(scheme.get(), &flags))) {
2095 return flags & nsIProtocolHandler::URI_NORELATIVE;
2096 }
2097
2098 return false;
2099 }
2100
2101 // static
InProlog(nsINode * aNode)2102 bool nsContentUtils::InProlog(nsINode* aNode) {
2103 MOZ_ASSERT(aNode, "missing node to nsContentUtils::InProlog");
2104
2105 nsINode* parent = aNode->GetParentNode();
2106 if (!parent || !parent->IsDocument()) {
2107 return false;
2108 }
2109
2110 Document* doc = parent->AsDocument();
2111 nsIContent* root = doc->GetRootElement();
2112
2113 return !root || doc->ComputeIndexOf(aNode) < doc->ComputeIndexOf(root);
2114 }
2115
IsCallerChrome()2116 bool nsContentUtils::IsCallerChrome() {
2117 MOZ_ASSERT(NS_IsMainThread());
2118 return SubjectPrincipal() == sSystemPrincipal;
2119 }
2120
2121 #ifdef FUZZING
IsFuzzingEnabled()2122 bool nsContentUtils::IsFuzzingEnabled() {
2123 return StaticPrefs::fuzzing_enabled();
2124 }
2125 #endif
2126
2127 /* static */
IsCallerChromeOrElementTransformGettersEnabled(JSContext * aCx,JSObject *)2128 bool nsContentUtils::IsCallerChromeOrElementTransformGettersEnabled(
2129 JSContext* aCx, JSObject*) {
2130 return ThreadsafeIsSystemCaller(aCx) ||
2131 StaticPrefs::dom_element_transform_getters_enabled();
2132 }
2133
2134 /* static */
IsCallerChromeOrErrorPage(JSContext * aCx,JSObject * aObject)2135 bool nsContentUtils::IsCallerChromeOrErrorPage(JSContext* aCx,
2136 JSObject* aObject) {
2137 if (ThreadsafeIsSystemCaller(aCx)) {
2138 return true;
2139 }
2140 nsGlobalWindowInner* win =
2141 xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject));
2142 if (!win) {
2143 return false;
2144 }
2145 Document* doc = win->GetExtantDoc();
2146 return doc && IsErrorPage(doc->GetDocumentURI());
2147 }
2148
2149 /* static */
ShouldResistFingerprinting()2150 bool nsContentUtils::ShouldResistFingerprinting() {
2151 return StaticPrefs::privacy_resistFingerprinting();
2152 }
2153
ShouldResistFingerprinting(nsIDocShell * aDocShell)2154 bool nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell) {
2155 if (!aDocShell) {
2156 return ShouldResistFingerprinting();
2157 }
2158 return ShouldResistFingerprinting(aDocShell->GetDocument());
2159 }
2160
2161 /* static */
ShouldResistFingerprinting(const Document * aDoc)2162 bool nsContentUtils::ShouldResistFingerprinting(const Document* aDoc) {
2163 if (!aDoc) {
2164 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2165 ("Called nsContentUtils::ShouldResistFingerprinting(const "
2166 "Document* aDoc) with NULL document"));
2167 return ShouldResistFingerprinting();
2168 }
2169 bool isChrome = nsContentUtils::IsChromeDoc(aDoc);
2170 if (isChrome) {
2171 return false;
2172 }
2173 return ShouldResistFingerprinting(aDoc->GetChannel());
2174 }
2175
2176 /* static */
ShouldResistFingerprinting(nsIPrincipal * aPrincipal)2177 bool nsContentUtils::ShouldResistFingerprinting(nsIPrincipal* aPrincipal) {
2178 if (!aPrincipal) {
2179 return ShouldResistFingerprinting();
2180 }
2181 bool isChrome = aPrincipal->IsSystemPrincipal();
2182 return !isChrome && ShouldResistFingerprinting();
2183 }
2184
2185 /* static */
ShouldResistFingerprinting(WorkerPrivate * aWorkerPrivate)2186 bool nsContentUtils::ShouldResistFingerprinting(WorkerPrivate* aWorkerPrivate) {
2187 if (!aWorkerPrivate) {
2188 // We may be on a non-worker thread!
2189 return ShouldResistFingerprinting();
2190 }
2191 bool isChrome = aWorkerPrivate->UsesSystemPrincipal();
2192 if (isChrome) {
2193 return false;
2194 }
2195 return ShouldResistFingerprinting(aWorkerPrivate->GetDocument());
2196 }
2197
LogDomainAndPrefList(const char * exemptedDomainsPrefName,nsAutoCString & url,bool isExemptDomain)2198 inline void LogDomainAndPrefList(const char* exemptedDomainsPrefName,
2199 nsAutoCString& url, bool isExemptDomain) {
2200 nsAutoCString list;
2201 Preferences::GetCString(exemptedDomainsPrefName, list);
2202 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
2203 ("Domain \"%s\" is %s the exempt list \"%s\"",
2204 PromiseFlatCString(url).get(), isExemptDomain ? "in" : "NOT in",
2205 PromiseFlatCString(list).get()));
2206 }
2207
2208 /* static */
ShouldResistFingerprinting(nsIChannel * aChannel)2209 bool nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel) {
2210 const unsigned int webExtensionExemptMask = 0x01;
2211 const unsigned int nonPBMExemptMask = 0x02;
2212 const unsigned int specificDomainsExemptMask = 0x04;
2213
2214 if (!ShouldResistFingerprinting()) {
2215 return false;
2216 }
2217
2218 if (!aChannel) {
2219 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2220 ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2221 "aChannel) with NULL channel"));
2222 return true;
2223 }
2224
2225 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
2226
2227 if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2228 nonPBMExemptMask) {
2229 // if non-PBM exempt mask is true, exempt non-PBM channels.
2230 if (loadInfo->GetOriginAttributes().mPrivateBrowsingId == 0) {
2231 return false;
2232 }
2233 }
2234
2235 bool isExemptDomain = false;
2236 const char* exemptedDomainsPrefName =
2237 "privacy.resistFingerprinting.exemptedDomains";
2238
2239 if (loadInfo->GetExternalContentPolicyType() ==
2240 ExtContentPolicy::TYPE_DOCUMENT) {
2241 // Case 1: Top Level Load
2242
2243 nsCOMPtr<nsIURI> channelURI;
2244 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
2245 MOZ_ASSERT(
2246 NS_SUCCEEDED(rv),
2247 "Failed to get URI in "
2248 "nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel)");
2249 // this check is to ensure that we do not crash in non-debug builds.
2250 if (NS_FAILED(rv)) {
2251 return true;
2252 }
2253
2254 // Exclude internal schemes
2255 if (channelURI->SchemeIs("about") || channelURI->SchemeIs("chrome") ||
2256 channelURI->SchemeIs("resource") ||
2257 channelURI->SchemeIs("view-source")) {
2258 return false;
2259 }
2260
2261 if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2262 webExtensionExemptMask) {
2263 if (channelURI->SchemeIs("web-extension")) {
2264 return false;
2265 }
2266 }
2267
2268 if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2269 specificDomainsExemptMask) {
2270 nsAutoCString list;
2271 Preferences::GetCString(exemptedDomainsPrefName, list);
2272 ToLowerCase(list);
2273 isExemptDomain = IsURIInList(channelURI, list);
2274
2275 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2276 mozilla::LogLevel::Debug)) {
2277 nsAutoCString url;
2278 channelURI->GetHost(url);
2279 LogDomainAndPrefList(exemptedDomainsPrefName, url, isExemptDomain);
2280 }
2281 }
2282
2283 return !isExemptDomain;
2284 }
2285 // Case 2: Subresource Load
2286
2287 nsIPrincipal* loadingPrincipal = loadInfo->GetLoadingPrincipal();
2288 // If it is not a document load then we are promised that the
2289 // loadingPrincipal is not null.
2290 MOZ_RELEASE_ASSERT(
2291 loadingPrincipal,
2292 "Got NULL loadingPrincipal in "
2293 "nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel)");
2294
2295 if (loadingPrincipal->IsSystemPrincipal()) {
2296 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
2297 ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
2298 "aChannel) with a system loadingPrincipal"));
2299 return false;
2300 }
2301
2302 if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2303 webExtensionExemptMask) {
2304 if (BasePrincipal::Cast(loadingPrincipal)->AddonPolicy()) {
2305 return false;
2306 }
2307 }
2308
2309 if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
2310 specificDomainsExemptMask) {
2311 loadingPrincipal->IsURIInPrefList(exemptedDomainsPrefName, &isExemptDomain);
2312
2313 if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
2314 mozilla::LogLevel::Debug)) {
2315 nsAutoCString origin;
2316 loadingPrincipal->GetAsciiOrigin(origin);
2317 LogDomainAndPrefList(exemptedDomainsPrefName, origin, isExemptDomain);
2318 }
2319 }
2320
2321 return !isExemptDomain;
2322 }
2323
2324 /* static */
UseStandinsForNativeColors()2325 bool nsContentUtils::UseStandinsForNativeColors() {
2326 return ShouldResistFingerprinting() ||
2327 StaticPrefs::ui_use_standins_for_native_colors();
2328 }
2329
2330 /* static */
CalcRoundedWindowSizeForResistingFingerprinting(int32_t aChromeWidth,int32_t aChromeHeight,int32_t aScreenWidth,int32_t aScreenHeight,int32_t aInputWidth,int32_t aInputHeight,bool aSetOuterWidth,bool aSetOuterHeight,int32_t * aOutputWidth,int32_t * aOutputHeight)2331 void nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
2332 int32_t aChromeWidth, int32_t aChromeHeight, int32_t aScreenWidth,
2333 int32_t aScreenHeight, int32_t aInputWidth, int32_t aInputHeight,
2334 bool aSetOuterWidth, bool aSetOuterHeight, int32_t* aOutputWidth,
2335 int32_t* aOutputHeight) {
2336 MOZ_ASSERT(aOutputWidth);
2337 MOZ_ASSERT(aOutputHeight);
2338
2339 int32_t availContentWidth = 0;
2340 int32_t availContentHeight = 0;
2341
2342 availContentWidth = std::min(StaticPrefs::privacy_window_maxInnerWidth(),
2343 aScreenWidth - aChromeWidth);
2344 #ifdef MOZ_WIDGET_GTK
2345 // In the GTK window, it will not report outside system decorations
2346 // when we get available window size, see Bug 581863. So, we leave a
2347 // 40 pixels space for them when calculating the available content
2348 // height. It is not necessary for the width since the content width
2349 // is usually pretty much the same as the chrome width.
2350 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2351 (-40 + aScreenHeight) - aChromeHeight);
2352 #else
2353 availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
2354 aScreenHeight - aChromeHeight);
2355 #endif
2356
2357 // Ideally, we'd like to round window size to 1000x1000, but the
2358 // screen space could be too small to accommodate this size in some
2359 // cases. If it happens, we would round the window size to the nearest
2360 // 200x100.
2361 availContentWidth = availContentWidth - (availContentWidth % 200);
2362 availContentHeight = availContentHeight - (availContentHeight % 100);
2363
2364 // If aIsOuter is true, we are setting the outer window. So we
2365 // have to consider the chrome UI.
2366 int32_t chromeOffsetWidth = aSetOuterWidth ? aChromeWidth : 0;
2367 int32_t chromeOffsetHeight = aSetOuterHeight ? aChromeHeight : 0;
2368 int32_t resultWidth = 0, resultHeight = 0;
2369
2370 // if the original size is greater than the maximum available size, we set
2371 // it to the maximum size. And if the original value is less than the
2372 // minimum rounded size, we set it to the minimum 200x100.
2373 if (aInputWidth > (availContentWidth + chromeOffsetWidth)) {
2374 resultWidth = availContentWidth + chromeOffsetWidth;
2375 } else if (aInputWidth < (200 + chromeOffsetWidth)) {
2376 resultWidth = 200 + chromeOffsetWidth;
2377 } else {
2378 // Otherwise, we round the window to the nearest upper rounded 200x100.
2379 resultWidth = NSToIntCeil((aInputWidth - chromeOffsetWidth) / 200.0) * 200 +
2380 chromeOffsetWidth;
2381 }
2382
2383 if (aInputHeight > (availContentHeight + chromeOffsetHeight)) {
2384 resultHeight = availContentHeight + chromeOffsetHeight;
2385 } else if (aInputHeight < (100 + chromeOffsetHeight)) {
2386 resultHeight = 100 + chromeOffsetHeight;
2387 } else {
2388 resultHeight =
2389 NSToIntCeil((aInputHeight - chromeOffsetHeight) / 100.0) * 100 +
2390 chromeOffsetHeight;
2391 }
2392
2393 *aOutputWidth = resultWidth;
2394 *aOutputHeight = resultHeight;
2395 }
2396
ThreadsafeIsCallerChrome()2397 bool nsContentUtils::ThreadsafeIsCallerChrome() {
2398 return NS_IsMainThread() ? IsCallerChrome()
2399 : IsCurrentThreadRunningChromeWorker();
2400 }
2401
IsCallerUAWidget()2402 bool nsContentUtils::IsCallerUAWidget() {
2403 JSContext* cx = GetCurrentJSContext();
2404 if (!cx) {
2405 return false;
2406 }
2407
2408 JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
2409 if (!realm) {
2410 return false;
2411 }
2412
2413 return xpc::IsUAWidgetScope(realm);
2414 }
2415
IsSystemCaller(JSContext * aCx)2416 bool nsContentUtils::IsSystemCaller(JSContext* aCx) {
2417 // Note that SubjectPrincipal() assumes we are in a compartment here.
2418 return SubjectPrincipal(aCx) == sSystemPrincipal;
2419 }
2420
ThreadsafeIsSystemCaller(JSContext * aCx)2421 bool nsContentUtils::ThreadsafeIsSystemCaller(JSContext* aCx) {
2422 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
2423 MOZ_ASSERT(ccjscx->Context() == aCx);
2424
2425 return ccjscx->IsSystemCaller();
2426 }
2427
2428 // static
LookupBindingMember(JSContext * aCx,nsIContent * aContent,JS::Handle<jsid> aId,JS::MutableHandle<JS::PropertyDescriptor> aDesc)2429 bool nsContentUtils::LookupBindingMember(
2430 JSContext* aCx, nsIContent* aContent, JS::Handle<jsid> aId,
2431 JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
2432 return true;
2433 }
2434
GetNearestInProcessCrossDocParentNode(nsINode * aChild)2435 nsINode* nsContentUtils::GetNearestInProcessCrossDocParentNode(
2436 nsINode* aChild) {
2437 if (aChild->IsDocument()) {
2438 for (BrowsingContext* bc = aChild->AsDocument()->GetBrowsingContext(); bc;
2439 bc = bc->GetParent()) {
2440 if (bc->GetEmbedderElement()) {
2441 return bc->GetEmbedderElement();
2442 }
2443 }
2444 return nullptr;
2445 }
2446
2447 nsINode* parent = aChild->GetParentNode();
2448 if (parent && parent->IsContent() && aChild->IsContent()) {
2449 parent = aChild->AsContent()->GetFlattenedTreeParent();
2450 }
2451
2452 return parent;
2453 }
2454
ContentIsHostIncludingDescendantOf(const nsINode * aPossibleDescendant,const nsINode * aPossibleAncestor)2455 bool nsContentUtils::ContentIsHostIncludingDescendantOf(
2456 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2457 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2458 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2459
2460 do {
2461 if (aPossibleDescendant == aPossibleAncestor) return true;
2462 if (aPossibleDescendant->IsDocumentFragment()) {
2463 aPossibleDescendant =
2464 aPossibleDescendant->AsDocumentFragment()->GetHost();
2465 } else {
2466 aPossibleDescendant = aPossibleDescendant->GetParentNode();
2467 }
2468 } while (aPossibleDescendant);
2469
2470 return false;
2471 }
2472
2473 // static
ContentIsCrossDocDescendantOf(nsINode * aPossibleDescendant,nsINode * aPossibleAncestor)2474 bool nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
2475 nsINode* aPossibleAncestor) {
2476 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2477 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2478
2479 do {
2480 if (aPossibleDescendant == aPossibleAncestor) {
2481 return true;
2482 }
2483
2484 aPossibleDescendant =
2485 GetNearestInProcessCrossDocParentNode(aPossibleDescendant);
2486 } while (aPossibleDescendant);
2487
2488 return false;
2489 }
2490
2491 // static
ContentIsFlattenedTreeDescendantOf(const nsINode * aPossibleDescendant,const nsINode * aPossibleAncestor)2492 bool nsContentUtils::ContentIsFlattenedTreeDescendantOf(
2493 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2494 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2495 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2496
2497 do {
2498 if (aPossibleDescendant == aPossibleAncestor) {
2499 return true;
2500 }
2501 aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
2502 } while (aPossibleDescendant);
2503
2504 return false;
2505 }
2506
2507 // static
ContentIsFlattenedTreeDescendantOfForStyle(const nsINode * aPossibleDescendant,const nsINode * aPossibleAncestor)2508 bool nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
2509 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
2510 MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
2511 MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
2512
2513 do {
2514 if (aPossibleDescendant == aPossibleAncestor) {
2515 return true;
2516 }
2517 aPossibleDescendant =
2518 aPossibleDescendant->GetFlattenedTreeParentNodeForStyle();
2519 } while (aPossibleDescendant);
2520
2521 return false;
2522 }
2523
2524 // static
Retarget(nsINode * aTargetA,nsINode * aTargetB)2525 nsINode* nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB) {
2526 while (true && aTargetA) {
2527 // If A's root is not a shadow root...
2528 nsINode* root = aTargetA->SubtreeRoot();
2529 if (!root->IsShadowRoot()) {
2530 // ...then return A.
2531 return aTargetA;
2532 }
2533
2534 // or A's root is a shadow-including inclusive ancestor of B...
2535 if (aTargetB->IsShadowIncludingInclusiveDescendantOf(root)) {
2536 // ...then return A.
2537 return aTargetA;
2538 }
2539
2540 aTargetA = ShadowRoot::FromNode(root)->GetHost();
2541 }
2542
2543 return nullptr;
2544 }
2545
2546 // static
GetInclusiveAncestors(nsINode * aNode,nsTArray<nsINode * > & aArray)2547 nsresult nsContentUtils::GetInclusiveAncestors(nsINode* aNode,
2548 nsTArray<nsINode*>& aArray) {
2549 while (aNode) {
2550 aArray.AppendElement(aNode);
2551 aNode = aNode->GetParentNode();
2552 }
2553 return NS_OK;
2554 }
2555
2556 // static
GetInclusiveAncestorsAndOffsets(nsINode * aNode,int32_t aOffset,nsTArray<nsIContent * > * aAncestorNodes,nsTArray<int32_t> * aAncestorOffsets)2557 nsresult nsContentUtils::GetInclusiveAncestorsAndOffsets(
2558 nsINode* aNode, int32_t aOffset, nsTArray<nsIContent*>* aAncestorNodes,
2559 nsTArray<int32_t>* aAncestorOffsets) {
2560 NS_ENSURE_ARG_POINTER(aNode);
2561
2562 if (!aNode->IsContent()) {
2563 return NS_ERROR_FAILURE;
2564 }
2565 nsIContent* content = aNode->AsContent();
2566
2567 if (!aAncestorNodes->IsEmpty()) {
2568 NS_WARNING("aAncestorNodes is not empty");
2569 aAncestorNodes->Clear();
2570 }
2571
2572 if (!aAncestorOffsets->IsEmpty()) {
2573 NS_WARNING("aAncestorOffsets is not empty");
2574 aAncestorOffsets->Clear();
2575 }
2576
2577 // insert the node itself
2578 aAncestorNodes->AppendElement(content);
2579 aAncestorOffsets->AppendElement(aOffset);
2580
2581 // insert all the ancestors
2582 nsIContent* child = content;
2583 nsIContent* parent = child->GetParent();
2584 while (parent) {
2585 aAncestorNodes->AppendElement(parent);
2586 aAncestorOffsets->AppendElement(parent->ComputeIndexOf(child));
2587 child = parent;
2588 parent = parent->GetParent();
2589 }
2590
2591 return NS_OK;
2592 }
2593
2594 template <typename Node, typename GetParentFunc>
GetCommonAncestorInternal(Node * aNode1,Node * aNode2,GetParentFunc aGetParentFunc)2595 static Node* GetCommonAncestorInternal(Node* aNode1, Node* aNode2,
2596 GetParentFunc aGetParentFunc) {
2597 MOZ_ASSERT(aNode1 != aNode2);
2598
2599 // Build the chain of parents
2600 AutoTArray<Node*, 30> parents1, parents2;
2601 do {
2602 parents1.AppendElement(aNode1);
2603 aNode1 = aGetParentFunc(aNode1);
2604 } while (aNode1);
2605 do {
2606 parents2.AppendElement(aNode2);
2607 aNode2 = aGetParentFunc(aNode2);
2608 } while (aNode2);
2609
2610 // Find where the parent chain differs
2611 uint32_t pos1 = parents1.Length();
2612 uint32_t pos2 = parents2.Length();
2613 Node* parent = nullptr;
2614 uint32_t len;
2615 for (len = std::min(pos1, pos2); len > 0; --len) {
2616 Node* child1 = parents1.ElementAt(--pos1);
2617 Node* child2 = parents2.ElementAt(--pos2);
2618 if (child1 != child2) {
2619 break;
2620 }
2621 parent = child1;
2622 }
2623
2624 return parent;
2625 }
2626
2627 /* static */
GetCommonAncestorHelper(nsINode * aNode1,nsINode * aNode2)2628 nsINode* nsContentUtils::GetCommonAncestorHelper(nsINode* aNode1,
2629 nsINode* aNode2) {
2630 return GetCommonAncestorInternal(
2631 aNode1, aNode2, [](nsINode* aNode) { return aNode->GetParentNode(); });
2632 }
2633
2634 /* static */
GetCommonFlattenedTreeAncestorHelper(nsIContent * aContent1,nsIContent * aContent2)2635 nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorHelper(
2636 nsIContent* aContent1, nsIContent* aContent2) {
2637 return GetCommonAncestorInternal(
2638 aContent1, aContent2,
2639 [](nsIContent* aContent) { return aContent->GetFlattenedTreeParent(); });
2640 }
2641
2642 /* static */
GetCommonFlattenedTreeAncestorForStyle(Element * aElement1,Element * aElement2)2643 Element* nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
2644 Element* aElement1, Element* aElement2) {
2645 return GetCommonAncestorInternal(aElement1, aElement2, [](Element* aElement) {
2646 return aElement->GetFlattenedTreeParentElementForStyle();
2647 });
2648 }
2649
2650 /* static */
PositionIsBefore(nsINode * aNode1,nsINode * aNode2,int32_t * aNode1Index,int32_t * aNode2Index)2651 bool nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2,
2652 int32_t* aNode1Index,
2653 int32_t* aNode2Index) {
2654 // Note, CompareDocumentPosition takes the latter params in different order.
2655 return (aNode2->CompareDocumentPosition(*aNode1, aNode2Index, aNode1Index) &
2656 (Node_Binding::DOCUMENT_POSITION_PRECEDING |
2657 Node_Binding::DOCUMENT_POSITION_DISCONNECTED)) ==
2658 Node_Binding::DOCUMENT_POSITION_PRECEDING;
2659 }
2660
2661 /* static */
ComparePoints(const nsINode * aParent1,int32_t aOffset1,const nsINode * aParent2,int32_t aOffset2,ComparePointsCache * aParent1Cache)2662 Maybe<int32_t> nsContentUtils::ComparePoints(
2663 const nsINode* aParent1, int32_t aOffset1, const nsINode* aParent2,
2664 int32_t aOffset2, ComparePointsCache* aParent1Cache) {
2665 bool disconnected{false};
2666
2667 const int32_t order = ComparePoints_Deprecated(
2668 aParent1, aOffset1, aParent2, aOffset2, &disconnected, aParent1Cache);
2669 if (disconnected) {
2670 return Nothing();
2671 }
2672
2673 return Some(order);
2674 }
2675
2676 /* static */
ComparePoints_Deprecated(const nsINode * aParent1,int32_t aOffset1,const nsINode * aParent2,int32_t aOffset2,bool * aDisconnected,ComparePointsCache * aParent1Cache)2677 int32_t nsContentUtils::ComparePoints_Deprecated(
2678 const nsINode* aParent1, int32_t aOffset1, const nsINode* aParent2,
2679 int32_t aOffset2, bool* aDisconnected, ComparePointsCache* aParent1Cache) {
2680 if (aParent1 == aParent2) {
2681 // XXX This is odd. aOffset1 and/or aOffset2 may be -1, e.g., it's result
2682 // of nsINode::ComputeIndexOf(), but this compares such invalid
2683 // offset with valid offset.
2684 return aOffset1 < aOffset2 ? -1 : aOffset1 > aOffset2 ? 1 : 0;
2685 }
2686
2687 AutoTArray<const nsINode*, 32> parents1, parents2;
2688 const nsINode* node1 = aParent1;
2689 const nsINode* node2 = aParent2;
2690 do {
2691 parents1.AppendElement(node1);
2692 node1 = node1->GetParentNode();
2693 } while (node1);
2694 do {
2695 parents2.AppendElement(node2);
2696 node2 = node2->GetParentNode();
2697 } while (node2);
2698
2699 uint32_t pos1 = parents1.Length() - 1;
2700 uint32_t pos2 = parents2.Length() - 1;
2701
2702 bool disconnected = parents1.ElementAt(pos1) != parents2.ElementAt(pos2);
2703 if (aDisconnected) {
2704 *aDisconnected = disconnected;
2705 }
2706 if (disconnected) {
2707 NS_ASSERTION(aDisconnected, "unexpected disconnected nodes");
2708 return 1;
2709 }
2710
2711 // Find where the parent chains differ
2712 const nsINode* parent = parents1.ElementAt(pos1);
2713 uint32_t len;
2714 for (len = std::min(pos1, pos2); len > 0; --len) {
2715 const nsINode* child1 = parents1.ElementAt(--pos1);
2716 const nsINode* child2 = parents2.ElementAt(--pos2);
2717 if (child1 != child2) {
2718 int32_t child1index = aParent1Cache
2719 ? aParent1Cache->ComputeIndexOf(parent, child1)
2720 : parent->ComputeIndexOf(child1);
2721 return child1index < parent->ComputeIndexOf(child2) ? -1 : 1;
2722 }
2723 parent = child1;
2724 }
2725
2726 // The parent chains never differed, so one of the nodes is an ancestor of
2727 // the other
2728
2729 NS_ASSERTION(!pos1 || !pos2,
2730 "should have run out of parent chain for one of the nodes");
2731
2732 if (!pos1) {
2733 const nsINode* child2 = parents2.ElementAt(--pos2);
2734 // XXX aOffset1 may be -1 as mentioned above. So, why does this return
2735 // it's *before* of the valid DOM point?
2736 return aOffset1 <= parent->ComputeIndexOf(child2) ? -1 : 1;
2737 }
2738
2739 const nsINode* child1 = parents1.ElementAt(--pos1);
2740 // XXX aOffset2 may be -1 as mentioned above. So, why does this return it's
2741 // *after* of the valid DOM point?
2742 int32_t child1index = aParent1Cache
2743 ? aParent1Cache->ComputeIndexOf(parent, child1)
2744 : parent->ComputeIndexOf(child1);
2745 return child1index < aOffset2 ? -1 : 1;
2746 }
2747
2748 // static
GetCommonAncestorUnderInteractiveContent(nsINode * aNode1,nsINode * aNode2)2749 nsINode* nsContentUtils::GetCommonAncestorUnderInteractiveContent(
2750 nsINode* aNode1, nsINode* aNode2) {
2751 if (!aNode1 || !aNode2) {
2752 return nullptr;
2753 }
2754
2755 if (aNode1 == aNode2) {
2756 return aNode1;
2757 }
2758
2759 // Build the chain of parents
2760 AutoTArray<nsINode*, 30> parents1;
2761 do {
2762 parents1.AppendElement(aNode1);
2763 if (aNode1->IsElement() &&
2764 aNode1->AsElement()->IsInteractiveHTMLContent()) {
2765 break;
2766 }
2767 aNode1 = aNode1->GetFlattenedTreeParentNode();
2768 } while (aNode1);
2769
2770 AutoTArray<nsINode*, 30> parents2;
2771 do {
2772 parents2.AppendElement(aNode2);
2773 if (aNode2->IsElement() &&
2774 aNode2->AsElement()->IsInteractiveHTMLContent()) {
2775 break;
2776 }
2777 aNode2 = aNode2->GetFlattenedTreeParentNode();
2778 } while (aNode2);
2779
2780 // Find where the parent chain differs
2781 uint32_t pos1 = parents1.Length();
2782 uint32_t pos2 = parents2.Length();
2783 nsINode* parent = nullptr;
2784 for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
2785 nsINode* child1 = parents1.ElementAt(--pos1);
2786 nsINode* child2 = parents2.ElementAt(--pos2);
2787 if (child1 != child2) {
2788 break;
2789 }
2790 parent = child1;
2791 }
2792
2793 return parent;
2794 }
2795
2796 /* static */
GetCommonBrowserParentAncestor(BrowserParent * aBrowserParent1,BrowserParent * aBrowserParent2)2797 BrowserParent* nsContentUtils::GetCommonBrowserParentAncestor(
2798 BrowserParent* aBrowserParent1, BrowserParent* aBrowserParent2) {
2799 return GetCommonAncestorInternal(
2800 aBrowserParent1, aBrowserParent2, [](BrowserParent* aBrowserParent) {
2801 return aBrowserParent->GetBrowserBridgeParent()
2802 ? aBrowserParent->GetBrowserBridgeParent()->Manager()
2803 : nullptr;
2804 });
2805 }
2806
2807 /* static */
2808 template <typename FPT, typename FRT, typename SPT, typename SRT>
ComparePoints(const RangeBoundaryBase<FPT,FRT> & aFirstBoundary,const RangeBoundaryBase<SPT,SRT> & aSecondBoundary)2809 Maybe<int32_t> nsContentUtils::ComparePoints(
2810 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
2811 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary) {
2812 if (!aFirstBoundary.IsSet() || !aSecondBoundary.IsSet()) {
2813 return Nothing{};
2814 }
2815
2816 bool disconnected{false};
2817 const int32_t order =
2818 ComparePoints_Deprecated(aFirstBoundary, aSecondBoundary, &disconnected);
2819
2820 if (disconnected) {
2821 return Nothing{};
2822 }
2823
2824 return Some(order);
2825 }
2826
2827 /* static */
2828 template <typename FPT, typename FRT, typename SPT, typename SRT>
ComparePoints_Deprecated(const RangeBoundaryBase<FPT,FRT> & aFirstBoundary,const RangeBoundaryBase<SPT,SRT> & aSecondBoundary,bool * aDisconnected)2829 int32_t nsContentUtils::ComparePoints_Deprecated(
2830 const RangeBoundaryBase<FPT, FRT>& aFirstBoundary,
2831 const RangeBoundaryBase<SPT, SRT>& aSecondBoundary, bool* aDisconnected) {
2832 if (NS_WARN_IF(!aFirstBoundary.IsSet()) ||
2833 NS_WARN_IF(!aSecondBoundary.IsSet())) {
2834 return -1;
2835 }
2836 // XXX Re-implement this without calling `Offset()` as far as possible,
2837 // and the other overload should be an alias of this.
2838 return ComparePoints_Deprecated(
2839 aFirstBoundary.Container(),
2840 *aFirstBoundary.Offset(
2841 RangeBoundaryBase<FPT, FRT>::OffsetFilter::kValidOrInvalidOffsets),
2842 aSecondBoundary.Container(),
2843 *aSecondBoundary.Offset(
2844 RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOrInvalidOffsets),
2845 aDisconnected);
2846 }
2847
IsCharInSet(const char * aSet,const char16_t aChar)2848 inline bool IsCharInSet(const char* aSet, const char16_t aChar) {
2849 char16_t ch;
2850 while ((ch = *aSet)) {
2851 if (aChar == char16_t(ch)) {
2852 return true;
2853 }
2854 ++aSet;
2855 }
2856 return false;
2857 }
2858
2859 /**
2860 * This method strips leading/trailing chars, in given set, from string.
2861 */
2862
2863 // static
TrimCharsInSet(const char * aSet,const nsAString & aValue)2864 const nsDependentSubstring nsContentUtils::TrimCharsInSet(
2865 const char* aSet, const nsAString& aValue) {
2866 nsAString::const_iterator valueCurrent, valueEnd;
2867
2868 aValue.BeginReading(valueCurrent);
2869 aValue.EndReading(valueEnd);
2870
2871 // Skip characters in the beginning
2872 while (valueCurrent != valueEnd) {
2873 if (!IsCharInSet(aSet, *valueCurrent)) {
2874 break;
2875 }
2876 ++valueCurrent;
2877 }
2878
2879 if (valueCurrent != valueEnd) {
2880 for (;;) {
2881 --valueEnd;
2882 if (!IsCharInSet(aSet, *valueEnd)) {
2883 break;
2884 }
2885 }
2886 ++valueEnd; // Step beyond the last character we want in the value.
2887 }
2888
2889 // valueEnd should point to the char after the last to copy
2890 return Substring(valueCurrent, valueEnd);
2891 }
2892
2893 /**
2894 * This method strips leading and trailing whitespace from a string.
2895 */
2896
2897 // static
2898 template <bool IsWhitespace(char16_t)>
TrimWhitespace(const nsAString & aStr,bool aTrimTrailing)2899 const nsDependentSubstring nsContentUtils::TrimWhitespace(const nsAString& aStr,
2900 bool aTrimTrailing) {
2901 nsAString::const_iterator start, end;
2902
2903 aStr.BeginReading(start);
2904 aStr.EndReading(end);
2905
2906 // Skip whitespace characters in the beginning
2907 while (start != end && IsWhitespace(*start)) {
2908 ++start;
2909 }
2910
2911 if (aTrimTrailing) {
2912 // Skip whitespace characters in the end.
2913 while (end != start) {
2914 --end;
2915
2916 if (!IsWhitespace(*end)) {
2917 // Step back to the last non-whitespace character.
2918 ++end;
2919
2920 break;
2921 }
2922 }
2923 }
2924
2925 // Return a substring for the string w/o leading and/or trailing
2926 // whitespace
2927
2928 return Substring(start, end);
2929 }
2930
2931 // Declaring the templates we are going to use avoid linking issues without
2932 // inlining the method. Considering there is not so much spaces checking
2933 // methods we can consider this to be better than inlining.
2934 template const nsDependentSubstring
2935 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, bool);
2936 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
2937 nsContentUtils::IsHTMLWhitespace>(const nsAString&, bool);
2938 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
2939 nsContentUtils::IsHTMLWhitespaceOrNBSP>(const nsAString&, bool);
2940
KeyAppendSep(nsACString & aKey)2941 static inline void KeyAppendSep(nsACString& aKey) {
2942 if (!aKey.IsEmpty()) {
2943 aKey.Append('>');
2944 }
2945 }
2946
KeyAppendString(const nsAString & aString,nsACString & aKey)2947 static inline void KeyAppendString(const nsAString& aString, nsACString& aKey) {
2948 KeyAppendSep(aKey);
2949
2950 // Could escape separator here if collisions happen. > is not a legal char
2951 // for a name or type attribute, so we should be safe avoiding that extra
2952 // work.
2953
2954 AppendUTF16toUTF8(aString, aKey);
2955 }
2956
KeyAppendString(const nsACString & aString,nsACString & aKey)2957 static inline void KeyAppendString(const nsACString& aString,
2958 nsACString& aKey) {
2959 KeyAppendSep(aKey);
2960
2961 // Could escape separator here if collisions happen. > is not a legal char
2962 // for a name or type attribute, so we should be safe avoiding that extra
2963 // work.
2964
2965 aKey.Append(aString);
2966 }
2967
KeyAppendInt(int32_t aInt,nsACString & aKey)2968 static inline void KeyAppendInt(int32_t aInt, nsACString& aKey) {
2969 KeyAppendSep(aKey);
2970
2971 aKey.AppendInt(aInt);
2972 }
2973
IsAutocompleteOff(const nsIContent * aContent)2974 static inline bool IsAutocompleteOff(const nsIContent* aContent) {
2975 return aContent->IsElement() &&
2976 aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
2977 nsGkAtoms::autocomplete, u"off"_ns,
2978 eIgnoreCase);
2979 }
2980
2981 /*static*/
GenerateStateKey(nsIContent * aContent,Document * aDocument,nsACString & aKey)2982 void nsContentUtils::GenerateStateKey(nsIContent* aContent, Document* aDocument,
2983 nsACString& aKey) {
2984 MOZ_ASSERT(aContent);
2985
2986 aKey.Truncate();
2987
2988 uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
2989
2990 // Don't capture state for anonymous content
2991 if (aContent->IsInNativeAnonymousSubtree()) {
2992 return;
2993 }
2994
2995 if (IsAutocompleteOff(aContent)) {
2996 return;
2997 }
2998
2999 RefPtr<Document> doc = aContent->GetUncomposedDoc();
3000
3001 KeyAppendInt(partID, aKey); // first append a partID
3002 bool generatedUniqueKey = false;
3003
3004 if (doc && doc->IsHTMLOrXHTML()) {
3005 nsHTMLDocument* htmlDoc = doc->AsHTMLDocument();
3006
3007 // If we have a form control and can calculate form information, use that
3008 // as the key - it is more reliable than just recording position in the
3009 // DOM.
3010 // XXXbz Is it, really? We have bugs on this, I think...
3011 // Important to have a unique key, and tag/type/name may not be.
3012 //
3013 // The format of the key depends on whether the control has a form,
3014 // and whether the element was parser inserted:
3015 //
3016 // [Has Form, Parser Inserted]:
3017 // fp>type>FormNum>IndOfControlInForm>FormName>name
3018 //
3019 // [No Form, Parser Inserted]:
3020 // dp>type>ControlNum>name
3021 //
3022 // [Has Form, Not Parser Inserted]:
3023 // fn>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
3024 //
3025 // [No Form, Not Parser Inserted]:
3026 // dn>type>IndOfControlInDoc>name
3027 //
3028 // XXX We don't need to use index if name is there
3029 // XXXbz We don't? Why not? I don't follow.
3030 //
3031 nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
3032 if (control) {
3033 // Get the control number if this was a parser inserted element from the
3034 // network.
3035 int32_t controlNumber =
3036 control->GetParserInsertedControlNumberForStateKey();
3037 bool parserInserted = controlNumber != -1;
3038
3039 RefPtr<nsContentList> htmlForms;
3040 RefPtr<nsContentList> htmlFormControls;
3041 if (!parserInserted) {
3042 // Getting these lists is expensive, as we need to keep them up to date
3043 // as the document loads, so we avoid it if we don't need them.
3044 htmlDoc->GetFormsAndFormControls(getter_AddRefs(htmlForms),
3045 getter_AddRefs(htmlFormControls));
3046 }
3047
3048 // Append the control type
3049 KeyAppendInt(int32_t(control->ControlType()), aKey);
3050
3051 // If in a form, add form name / index of form / index in form
3052 HTMLFormElement* formElement = control->GetFormElement();
3053 if (formElement) {
3054 if (IsAutocompleteOff(formElement)) {
3055 aKey.Truncate();
3056 return;
3057 }
3058
3059 // Append the form number, if this is a parser inserted control, or
3060 // the index of the form in the document otherwise.
3061 bool appendedForm = false;
3062 if (parserInserted) {
3063 MOZ_ASSERT(formElement->GetFormNumberForStateKey() != -1,
3064 "when generating a state key for a parser inserted form "
3065 "control we should have a parser inserted <form> element");
3066 KeyAppendString("fp"_ns, aKey);
3067 KeyAppendInt(formElement->GetFormNumberForStateKey(), aKey);
3068 appendedForm = true;
3069 } else {
3070 KeyAppendString("fn"_ns, aKey);
3071 int32_t index = htmlForms->IndexOf(formElement, false);
3072 if (index <= -1) {
3073 //
3074 // XXX HACK this uses some state that was dumped into the document
3075 // specifically to fix bug 138892. What we are trying to do is
3076 // *guess* which form this control's state is found in, with the
3077 // highly likely guess that the highest form parsed so far is the
3078 // one. This code should not be on trunk, only branch.
3079 //
3080 index = htmlDoc->GetNumFormsSynchronous() - 1;
3081 }
3082 if (index > -1) {
3083 KeyAppendInt(index, aKey);
3084 appendedForm = true;
3085 }
3086 }
3087
3088 if (appendedForm) {
3089 // Append the index of the control in the form
3090 int32_t index = formElement->IndexOfControl(control);
3091
3092 if (index > -1) {
3093 KeyAppendInt(index, aKey);
3094 generatedUniqueKey = true;
3095 }
3096 }
3097
3098 // Append the form name
3099 nsAutoString formName;
3100 formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
3101 KeyAppendString(formName, aKey);
3102 } else {
3103 // Not in a form. Append the control number, if this is a parser
3104 // inserted control, or the index of the control in the document
3105 // otherwise.
3106 if (parserInserted) {
3107 KeyAppendString("dp"_ns, aKey);
3108 KeyAppendInt(control->GetParserInsertedControlNumberForStateKey(),
3109 aKey);
3110 generatedUniqueKey = true;
3111 } else {
3112 KeyAppendString("dn"_ns, aKey);
3113 int32_t index = htmlFormControls->IndexOf(aContent, true);
3114 if (index > -1) {
3115 KeyAppendInt(index, aKey);
3116 generatedUniqueKey = true;
3117 }
3118 }
3119
3120 // Append the control name
3121 nsAutoString name;
3122 aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name,
3123 name);
3124 KeyAppendString(name, aKey);
3125 }
3126 }
3127 }
3128
3129 if (!generatedUniqueKey) {
3130 // Either we didn't have a form control or we aren't in an HTML document so
3131 // we can't figure out form info. Append the tag name if it's an element
3132 // to avoid restoring state for one type of element on another type.
3133 if (aContent->IsElement()) {
3134 KeyAppendString(nsDependentAtomString(aContent->NodeInfo()->NameAtom()),
3135 aKey);
3136 } else {
3137 // Append a character that is not "d" or "f" to disambiguate from
3138 // the case when we were a form control in an HTML document.
3139 KeyAppendString("o"_ns, aKey);
3140 }
3141
3142 // Now start at aContent and append the indices of it and all its ancestors
3143 // in their containers. That should at least pin down its position in the
3144 // DOM...
3145 nsINode* parent = aContent->GetParentNode();
3146 nsINode* content = aContent;
3147 while (parent) {
3148 KeyAppendInt(parent->ComputeIndexOf(content), aKey);
3149 content = parent;
3150 parent = content->GetParentNode();
3151 }
3152 }
3153 }
3154
3155 // static
SubjectPrincipal(JSContext * aCx)3156 nsIPrincipal* nsContentUtils::SubjectPrincipal(JSContext* aCx) {
3157 MOZ_ASSERT(NS_IsMainThread());
3158
3159 // As opposed to SubjectPrincipal(), we do in fact assume that
3160 // we're in a realm here; anyone who calls this function in
3161 // situations where that's not the case is doing it wrong.
3162 JS::Realm* realm = js::GetContextRealm(aCx);
3163 MOZ_ASSERT(realm);
3164
3165 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3166 return nsJSPrincipals::get(principals);
3167 }
3168
3169 // static
SubjectPrincipal()3170 nsIPrincipal* nsContentUtils::SubjectPrincipal() {
3171 MOZ_ASSERT(IsInitialized());
3172 MOZ_ASSERT(NS_IsMainThread());
3173 JSContext* cx = GetCurrentJSContext();
3174 if (!cx) {
3175 MOZ_CRASH(
3176 "Accessing the Subject Principal without an AutoJSAPI on the stack is "
3177 "forbidden");
3178 }
3179
3180 JS::Realm* realm = js::GetContextRealm(cx);
3181
3182 // When an AutoJSAPI is instantiated, we are in a null realm until the
3183 // first JSAutoRealm, which is kind of a purgatory as far as permissions
3184 // go. It would be nice to just hard-abort if somebody does a security check
3185 // in this purgatory zone, but that would be too fragile, since it could be
3186 // triggered by random IsCallerChrome() checks 20-levels deep.
3187 //
3188 // So we want to return _something_ here - and definitely not the System
3189 // Principal, since that would make an AutoJSAPI a very dangerous thing to
3190 // instantiate.
3191 //
3192 // The natural thing to return is a null principal. Ideally, we'd return a
3193 // different null principal each time, to avoid any unexpected interactions
3194 // when the principal accidentally gets inherited somewhere. But
3195 // SubjectPrincipal doesn't return strong references, so there's no way to
3196 // sanely manage the lifetime of multiple null principals.
3197 //
3198 // So we use a singleton null principal. To avoid it being accidentally
3199 // inherited and becoming a "real" subject or object principal, we do a
3200 // release-mode assert during realm creation against using this principal on
3201 // an actual global.
3202 if (!realm) {
3203 return sNullSubjectPrincipal;
3204 }
3205
3206 return SubjectPrincipal(cx);
3207 }
3208
3209 // static
ObjectPrincipal(JSObject * aObj)3210 nsIPrincipal* nsContentUtils::ObjectPrincipal(JSObject* aObj) {
3211 MOZ_ASSERT(NS_IsMainThread());
3212
3213 #ifdef DEBUG
3214 JS::AssertObjectBelongsToCurrentThread(aObj);
3215 #endif
3216
3217 MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(aObj));
3218
3219 JS::Realm* realm = js::GetNonCCWObjectRealm(aObj);
3220 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
3221 return nsJSPrincipals::get(principals);
3222 }
3223
3224 // static
NewURIWithDocumentCharset(nsIURI ** aResult,const nsAString & aSpec,Document * aDocument,nsIURI * aBaseURI)3225 nsresult nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
3226 const nsAString& aSpec,
3227 Document* aDocument,
3228 nsIURI* aBaseURI) {
3229 if (aDocument) {
3230 return NS_NewURI(aResult, aSpec, aDocument->GetDocumentCharacterSet(),
3231 aBaseURI);
3232 }
3233 return NS_NewURI(aResult, aSpec, nullptr, aBaseURI);
3234 }
3235
3236 // static
IsNameWithDash(nsAtom * aName)3237 bool nsContentUtils::IsNameWithDash(nsAtom* aName) {
3238 // A valid custom element name is a sequence of characters name which
3239 // must match the PotentialCustomElementName production:
3240 // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
3241 const char16_t* name = aName->GetUTF16String();
3242 uint32_t len = aName->GetLength();
3243 bool hasDash = false;
3244
3245 if (!len || name[0] < 'a' || name[0] > 'z') {
3246 return false;
3247 }
3248
3249 uint32_t i = 1;
3250 while (i < len) {
3251 if (i + 1 < len && NS_IS_SURROGATE_PAIR(name[i], name[i + 1])) {
3252 // Merged two 16-bit surrogate pairs into code point.
3253 char32_t code = SURROGATE_TO_UCS4(name[i], name[i + 1]);
3254
3255 if (code < 0x10000 || code > 0xEFFFF) {
3256 return false;
3257 }
3258
3259 i += 2;
3260 } else {
3261 if (name[i] == '-') {
3262 hasDash = true;
3263 }
3264
3265 if (name[i] != '-' && name[i] != '.' && name[i] != '_' &&
3266 name[i] != 0xB7 && (name[i] < '0' || name[i] > '9') &&
3267 (name[i] < 'a' || name[i] > 'z') &&
3268 (name[i] < 0xC0 || name[i] > 0xD6) &&
3269 (name[i] < 0xF8 || name[i] > 0x37D) &&
3270 (name[i] < 0x37F || name[i] > 0x1FFF) &&
3271 (name[i] < 0x200C || name[i] > 0x200D) &&
3272 (name[i] < 0x203F || name[i] > 0x2040) &&
3273 (name[i] < 0x2070 || name[i] > 0x218F) &&
3274 (name[i] < 0x2C00 || name[i] > 0x2FEF) &&
3275 (name[i] < 0x3001 || name[i] > 0xD7FF) &&
3276 (name[i] < 0xF900 || name[i] > 0xFDCF) &&
3277 (name[i] < 0xFDF0 || name[i] > 0xFFFD)) {
3278 return false;
3279 }
3280
3281 i++;
3282 }
3283 }
3284
3285 return hasDash;
3286 }
3287
3288 // static
IsCustomElementName(nsAtom * aName,uint32_t aNameSpaceID)3289 bool nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID) {
3290 // Allow non-dashed names in XUL for XBL to Custom Element migrations.
3291 if (aNameSpaceID == kNameSpaceID_XUL) {
3292 return true;
3293 }
3294
3295 bool hasDash = IsNameWithDash(aName);
3296 if (!hasDash) {
3297 return false;
3298 }
3299
3300 // The custom element name must not be one of the following values:
3301 // annotation-xml
3302 // color-profile
3303 // font-face
3304 // font-face-src
3305 // font-face-uri
3306 // font-face-format
3307 // font-face-name
3308 // missing-glyph
3309 return aName != nsGkAtoms::annotation_xml_ &&
3310 aName != nsGkAtoms::colorProfile && aName != nsGkAtoms::font_face &&
3311 aName != nsGkAtoms::font_face_src &&
3312 aName != nsGkAtoms::font_face_uri &&
3313 aName != nsGkAtoms::font_face_format &&
3314 aName != nsGkAtoms::font_face_name && aName != nsGkAtoms::missingGlyph;
3315 }
3316
3317 // static
CheckQName(const nsAString & aQualifiedName,bool aNamespaceAware,const char16_t ** aColon)3318 nsresult nsContentUtils::CheckQName(const nsAString& aQualifiedName,
3319 bool aNamespaceAware,
3320 const char16_t** aColon) {
3321 const char* colon = nullptr;
3322 const char16_t* begin = aQualifiedName.BeginReading();
3323 const char16_t* end = aQualifiedName.EndReading();
3324
3325 int result = MOZ_XMLCheckQName(reinterpret_cast<const char*>(begin),
3326 reinterpret_cast<const char*>(end),
3327 aNamespaceAware, &colon);
3328
3329 if (!result) {
3330 if (aColon) {
3331 *aColon = reinterpret_cast<const char16_t*>(colon);
3332 }
3333
3334 return NS_OK;
3335 }
3336
3337 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
3338 }
3339
3340 // static
SplitQName(const nsIContent * aNamespaceResolver,const nsString & aQName,int32_t * aNamespace,nsAtom ** aLocalName)3341 nsresult nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
3342 const nsString& aQName, int32_t* aNamespace,
3343 nsAtom** aLocalName) {
3344 const char16_t* colon;
3345 nsresult rv = nsContentUtils::CheckQName(aQName, true, &colon);
3346 NS_ENSURE_SUCCESS(rv, rv);
3347
3348 if (colon) {
3349 const char16_t* end;
3350 aQName.EndReading(end);
3351 nsAutoString nameSpace;
3352 rv = aNamespaceResolver->LookupNamespaceURIInternal(
3353 Substring(aQName.get(), colon), nameSpace);
3354 NS_ENSURE_SUCCESS(rv, rv);
3355
3356 *aNamespace = NameSpaceManager()->GetNameSpaceID(
3357 nameSpace, nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
3358 if (*aNamespace == kNameSpaceID_Unknown) return NS_ERROR_FAILURE;
3359
3360 *aLocalName = NS_AtomizeMainThread(Substring(colon + 1, end)).take();
3361 } else {
3362 *aNamespace = kNameSpaceID_None;
3363 *aLocalName = NS_AtomizeMainThread(aQName).take();
3364 }
3365 NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
3366 return NS_OK;
3367 }
3368
3369 // static
GetNodeInfoFromQName(const nsAString & aNamespaceURI,const nsAString & aQualifiedName,nsNodeInfoManager * aNodeInfoManager,uint16_t aNodeType,mozilla::dom::NodeInfo ** aNodeInfo)3370 nsresult nsContentUtils::GetNodeInfoFromQName(
3371 const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
3372 nsNodeInfoManager* aNodeInfoManager, uint16_t aNodeType,
3373 mozilla::dom::NodeInfo** aNodeInfo) {
3374 const nsString& qName = PromiseFlatString(aQualifiedName);
3375 const char16_t* colon;
3376 nsresult rv = nsContentUtils::CheckQName(qName, true, &colon);
3377 NS_ENSURE_SUCCESS(rv, rv);
3378
3379 int32_t nsID;
3380 sNameSpaceManager->RegisterNameSpace(aNamespaceURI, nsID);
3381 if (colon) {
3382 const char16_t* end;
3383 qName.EndReading(end);
3384
3385 RefPtr<nsAtom> prefix = NS_AtomizeMainThread(Substring(qName.get(), colon));
3386
3387 rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix, nsID,
3388 aNodeType, aNodeInfo);
3389 } else {
3390 rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID, aNodeType,
3391 aNodeInfo);
3392 }
3393 NS_ENSURE_SUCCESS(rv, rv);
3394
3395 return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
3396 (*aNodeInfo)->GetPrefixAtom(),
3397 (*aNodeInfo)->NamespaceID())
3398 ? NS_OK
3399 : NS_ERROR_DOM_NAMESPACE_ERR;
3400 }
3401
3402 // static
SplitExpatName(const char16_t * aExpatName,nsAtom ** aPrefix,nsAtom ** aLocalName,int32_t * aNameSpaceID)3403 void nsContentUtils::SplitExpatName(const char16_t* aExpatName,
3404 nsAtom** aPrefix, nsAtom** aLocalName,
3405 int32_t* aNameSpaceID) {
3406 /**
3407 * Expat can send the following:
3408 * localName
3409 * namespaceURI<separator>localName
3410 * namespaceURI<separator>localName<separator>prefix
3411 *
3412 * and we use 0xFFFF for the <separator>.
3413 *
3414 */
3415
3416 const char16_t* uriEnd = nullptr;
3417 const char16_t* nameEnd = nullptr;
3418 const char16_t* pos;
3419 for (pos = aExpatName; *pos; ++pos) {
3420 if (*pos == 0xFFFF) {
3421 if (uriEnd) {
3422 nameEnd = pos;
3423 } else {
3424 uriEnd = pos;
3425 }
3426 }
3427 }
3428
3429 const char16_t* nameStart;
3430 if (uriEnd) {
3431 if (sNameSpaceManager) {
3432 sNameSpaceManager->RegisterNameSpace(
3433 nsDependentSubstring(aExpatName, uriEnd), *aNameSpaceID);
3434 } else {
3435 *aNameSpaceID = kNameSpaceID_Unknown;
3436 }
3437
3438 nameStart = (uriEnd + 1);
3439 if (nameEnd) {
3440 const char16_t* prefixStart = nameEnd + 1;
3441 *aPrefix = NS_AtomizeMainThread(Substring(prefixStart, pos)).take();
3442 } else {
3443 nameEnd = pos;
3444 *aPrefix = nullptr;
3445 }
3446 } else {
3447 *aNameSpaceID = kNameSpaceID_None;
3448 nameStart = aExpatName;
3449 nameEnd = pos;
3450 *aPrefix = nullptr;
3451 }
3452 *aLocalName = NS_AtomizeMainThread(Substring(nameStart, nameEnd)).take();
3453 }
3454
3455 // static
GetPresShellForContent(const nsIContent * aContent)3456 PresShell* nsContentUtils::GetPresShellForContent(const nsIContent* aContent) {
3457 Document* doc = aContent->GetComposedDoc();
3458 if (!doc) {
3459 return nullptr;
3460 }
3461 return doc->GetPresShell();
3462 }
3463
3464 // static
GetContextForContent(const nsIContent * aContent)3465 nsPresContext* nsContentUtils::GetContextForContent(
3466 const nsIContent* aContent) {
3467 PresShell* presShell = GetPresShellForContent(aContent);
3468 if (!presShell) {
3469 return nullptr;
3470 }
3471 return presShell->GetPresContext();
3472 }
3473
3474 // static
CanLoadImage(nsIURI * aURI,nsINode * aNode,Document * aLoadingDocument,nsIPrincipal * aLoadingPrincipal)3475 bool nsContentUtils::CanLoadImage(nsIURI* aURI, nsINode* aNode,
3476 Document* aLoadingDocument,
3477 nsIPrincipal* aLoadingPrincipal) {
3478 MOZ_ASSERT(aURI, "Must have a URI");
3479 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3480 MOZ_ASSERT(aLoadingPrincipal, "Must have a loading principal");
3481
3482 nsresult rv;
3483
3484 auto appType = nsIDocShell::APP_TYPE_UNKNOWN;
3485
3486 {
3487 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
3488 aLoadingDocument->GetDocShell();
3489 if (docShellTreeItem) {
3490 nsCOMPtr<nsIDocShellTreeItem> root;
3491 docShellTreeItem->GetInProcessRootTreeItem(getter_AddRefs(root));
3492
3493 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
3494
3495 if (docShell) {
3496 appType = docShell->GetAppType();
3497 }
3498 }
3499 }
3500
3501 if (appType != nsIDocShell::APP_TYPE_EDITOR) {
3502 // Editor apps get special treatment here, editors can load images
3503 // from anywhere. This allows editor to insert images from file://
3504 // into documents that are being edited.
3505 rv = sSecurityManager->CheckLoadURIWithPrincipal(
3506 aLoadingPrincipal, aURI, nsIScriptSecurityManager::ALLOW_CHROME,
3507 aLoadingDocument->InnerWindowID());
3508 if (NS_FAILED(rv)) {
3509 return false;
3510 }
3511 }
3512
3513 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new mozilla::net::LoadInfo(
3514 aLoadingPrincipal,
3515 aLoadingPrincipal, // triggering principal
3516 aNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
3517 nsIContentPolicy::TYPE_INTERNAL_IMAGE);
3518
3519 int16_t decision = nsIContentPolicy::ACCEPT;
3520
3521 rv = NS_CheckContentLoadPolicy(aURI, secCheckLoadInfo,
3522 ""_ns, // mime guess
3523 &decision, GetContentPolicy());
3524
3525 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(decision);
3526 }
3527
3528 // static
IsInPrivateBrowsing(Document * aDoc)3529 bool nsContentUtils::IsInPrivateBrowsing(Document* aDoc) {
3530 if (!aDoc) {
3531 return false;
3532 }
3533
3534 nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
3535 if (loadGroup) {
3536 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3537 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3538 if (callbacks) {
3539 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3540 if (loadContext) {
3541 return loadContext->UsePrivateBrowsing();
3542 }
3543 }
3544 }
3545
3546 nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
3547 return channel && NS_UsePrivateBrowsing(channel);
3548 }
3549
3550 // static
IsInPrivateBrowsing(nsILoadGroup * aLoadGroup)3551 bool nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup) {
3552 if (!aLoadGroup) {
3553 return false;
3554 }
3555 bool isPrivate = false;
3556 nsCOMPtr<nsIInterfaceRequestor> callbacks;
3557 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
3558 if (callbacks) {
3559 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
3560 isPrivate = loadContext && loadContext->UsePrivateBrowsing();
3561 }
3562 return isPrivate;
3563 }
3564
3565 // FIXME(emilio): This is (effectively) almost but not quite the same as
3566 // Document::ShouldLoadImages(), which one is right?
DocumentInactiveForImageLoads(Document * aDocument)3567 bool nsContentUtils::DocumentInactiveForImageLoads(Document* aDocument) {
3568 if (!aDocument) {
3569 return false;
3570 }
3571 if (IsChromeDoc(aDocument) || aDocument->IsResourceDoc() ||
3572 aDocument->IsStaticDocument()) {
3573 return false;
3574 }
3575 nsCOMPtr<nsPIDOMWindowInner> win =
3576 do_QueryInterface(aDocument->GetScopeObject());
3577 return !win || !win->GetDocShell();
3578 }
3579
GetImgLoaderForDocument(Document * aDoc)3580 imgLoader* nsContentUtils::GetImgLoaderForDocument(Document* aDoc) {
3581 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
3582
3583 if (!aDoc) {
3584 return imgLoader::NormalLoader();
3585 }
3586 bool isPrivate = IsInPrivateBrowsing(aDoc);
3587 return isPrivate ? imgLoader::PrivateBrowsingLoader()
3588 : imgLoader::NormalLoader();
3589 }
3590
3591 // static
GetImgLoaderForChannel(nsIChannel * aChannel,Document * aContext)3592 imgLoader* nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
3593 Document* aContext) {
3594 NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
3595
3596 if (!aChannel) {
3597 return imgLoader::NormalLoader();
3598 }
3599 nsCOMPtr<nsILoadContext> context;
3600 NS_QueryNotificationCallbacks(aChannel, context);
3601 return context && context->UsePrivateBrowsing()
3602 ? imgLoader::PrivateBrowsingLoader()
3603 : imgLoader::NormalLoader();
3604 }
3605
3606 // static
IsImageInCache(nsIURI * aURI,Document * aDocument)3607 bool nsContentUtils::IsImageInCache(nsIURI* aURI, Document* aDocument) {
3608 imgILoader* loader = GetImgLoaderForDocument(aDocument);
3609 nsCOMPtr<imgICache> cache = do_QueryInterface(loader);
3610
3611 // If something unexpected happened we return false, otherwise if props
3612 // is set, the image is cached and we return true
3613 nsCOMPtr<nsIProperties> props;
3614 nsresult rv =
3615 cache->FindEntryProperties(aURI, aDocument, getter_AddRefs(props));
3616 return (NS_SUCCEEDED(rv) && props);
3617 }
3618
3619 // static
CORSModeToLoadImageFlags(mozilla::CORSMode aMode)3620 int32_t nsContentUtils::CORSModeToLoadImageFlags(mozilla::CORSMode aMode) {
3621 switch (aMode) {
3622 case CORS_ANONYMOUS:
3623 return imgILoader::LOAD_CORS_ANONYMOUS;
3624 case CORS_USE_CREDENTIALS:
3625 return imgILoader::LOAD_CORS_USE_CREDENTIALS;
3626 default:
3627 return 0;
3628 }
3629 }
3630
3631 // static
LoadImage(nsIURI * aURI,nsINode * aContext,Document * aLoadingDocument,nsIPrincipal * aLoadingPrincipal,uint64_t aRequestContextID,nsIReferrerInfo * aReferrerInfo,imgINotificationObserver * aObserver,int32_t aLoadFlags,const nsAString & initiatorType,imgRequestProxy ** aRequest,nsContentPolicyType aContentPolicyType,bool aUseUrgentStartForChannel,bool aLinkPreload)3632 nsresult nsContentUtils::LoadImage(
3633 nsIURI* aURI, nsINode* aContext, Document* aLoadingDocument,
3634 nsIPrincipal* aLoadingPrincipal, uint64_t aRequestContextID,
3635 nsIReferrerInfo* aReferrerInfo, imgINotificationObserver* aObserver,
3636 int32_t aLoadFlags, const nsAString& initiatorType,
3637 imgRequestProxy** aRequest, nsContentPolicyType aContentPolicyType,
3638 bool aUseUrgentStartForChannel, bool aLinkPreload) {
3639 MOZ_ASSERT(aURI, "Must have a URI");
3640 MOZ_ASSERT(aContext, "Must have a context");
3641 MOZ_ASSERT(aLoadingDocument, "Must have a document");
3642 MOZ_ASSERT(aLoadingPrincipal, "Must have a principal");
3643 MOZ_ASSERT(aRequest, "Null out param");
3644
3645 imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
3646 if (!imgLoader) {
3647 // nothing we can do here
3648 return NS_ERROR_FAILURE;
3649 }
3650
3651 nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
3652
3653 nsIURI* documentURI = aLoadingDocument->GetDocumentURI();
3654
3655 NS_ASSERTION(loadGroup || IsFontTableURI(documentURI),
3656 "Could not get loadgroup; onload may fire too early");
3657
3658 // XXXbz using "documentURI" for the initialDocumentURI is not quite
3659 // right, but the best we can do here...
3660 return imgLoader->LoadImage(aURI, /* uri to load */
3661 documentURI, /* initialDocumentURI */
3662 aReferrerInfo, /* referrerInfo */
3663 aLoadingPrincipal, /* loading principal */
3664 aRequestContextID, /* request context ID */
3665 loadGroup, /* loadgroup */
3666 aObserver, /* imgINotificationObserver */
3667 aContext, /* loading context */
3668 aLoadingDocument, /* uniquification key */
3669 aLoadFlags, /* load flags */
3670 nullptr, /* cache key */
3671 aContentPolicyType, /* content policy type */
3672 initiatorType, /* the load initiator */
3673 aUseUrgentStartForChannel, /* urgent-start flag */
3674 aLinkPreload, /* <link preload> initiator */
3675 aRequest);
3676 }
3677
3678 // static
GetImageFromContent(nsIImageLoadingContent * aContent,imgIRequest ** aRequest)3679 already_AddRefed<imgIContainer> nsContentUtils::GetImageFromContent(
3680 nsIImageLoadingContent* aContent, imgIRequest** aRequest) {
3681 if (aRequest) {
3682 *aRequest = nullptr;
3683 }
3684
3685 NS_ENSURE_TRUE(aContent, nullptr);
3686
3687 nsCOMPtr<imgIRequest> imgRequest;
3688 aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3689 getter_AddRefs(imgRequest));
3690 if (!imgRequest) {
3691 return nullptr;
3692 }
3693
3694 nsCOMPtr<imgIContainer> imgContainer;
3695 imgRequest->GetImage(getter_AddRefs(imgContainer));
3696
3697 if (!imgContainer) {
3698 return nullptr;
3699 }
3700
3701 if (aRequest) {
3702 // If the consumer wants the request, verify it has actually loaded
3703 // successfully.
3704 uint32_t imgStatus;
3705 imgRequest->GetImageStatus(&imgStatus);
3706 if (imgStatus & imgIRequest::STATUS_FRAME_COMPLETE &&
3707 !(imgStatus & imgIRequest::STATUS_ERROR)) {
3708 imgRequest.swap(*aRequest);
3709 }
3710 }
3711
3712 return imgContainer.forget();
3713 }
3714
3715 // static
ContentIsDraggable(nsIContent * aContent)3716 bool nsContentUtils::ContentIsDraggable(nsIContent* aContent) {
3717 MOZ_ASSERT(aContent);
3718
3719 if (auto htmlElement = nsGenericHTMLElement::FromNode(aContent)) {
3720 if (htmlElement->Draggable()) {
3721 return true;
3722 }
3723
3724 if (htmlElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
3725 nsGkAtoms::_false, eIgnoreCase)) {
3726 return false;
3727 }
3728 }
3729 if (aContent->IsSVGElement()) {
3730 return false;
3731 }
3732
3733 // special handling for content area image and link dragging
3734 return IsDraggableImage(aContent) || IsDraggableLink(aContent);
3735 }
3736
3737 // static
IsDraggableImage(nsIContent * aContent)3738 bool nsContentUtils::IsDraggableImage(nsIContent* aContent) {
3739 MOZ_ASSERT(aContent, "Must have content node to test");
3740
3741 nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(aContent));
3742 if (!imageContent) {
3743 return false;
3744 }
3745
3746 nsCOMPtr<imgIRequest> imgRequest;
3747 imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
3748 getter_AddRefs(imgRequest));
3749
3750 // XXXbz It may be draggable even if the request resulted in an error. Why?
3751 // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
3752 return imgRequest != nullptr;
3753 }
3754
3755 // static
IsDraggableLink(const nsIContent * aContent)3756 bool nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
3757 nsCOMPtr<nsIURI> absURI;
3758 return aContent->IsLink(getter_AddRefs(absURI));
3759 }
3760
3761 // static
QNameChanged(mozilla::dom::NodeInfo * aNodeInfo,nsAtom * aName,mozilla::dom::NodeInfo ** aResult)3762 nsresult nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo,
3763 nsAtom* aName,
3764 mozilla::dom::NodeInfo** aResult) {
3765 nsNodeInfoManager* niMgr = aNodeInfo->NodeInfoManager();
3766
3767 *aResult = niMgr
3768 ->GetNodeInfo(aName, nullptr, aNodeInfo->NamespaceID(),
3769 aNodeInfo->NodeType(), aNodeInfo->GetExtraName())
3770 .take();
3771 return NS_OK;
3772 }
3773
TestSitePerm(nsIPrincipal * aPrincipal,const nsACString & aType,uint32_t aPerm,bool aExactHostMatch)3774 static bool TestSitePerm(nsIPrincipal* aPrincipal, const nsACString& aType,
3775 uint32_t aPerm, bool aExactHostMatch) {
3776 if (!aPrincipal) {
3777 // We always deny (i.e. don't allow) the permission if we don't have a
3778 // principal.
3779 return aPerm != nsIPermissionManager::ALLOW_ACTION;
3780 }
3781
3782 nsCOMPtr<nsIPermissionManager> permMgr =
3783 components::PermissionManager::Service();
3784 NS_ENSURE_TRUE(permMgr, false);
3785
3786 uint32_t perm;
3787 nsresult rv;
3788 if (aExactHostMatch) {
3789 rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
3790 } else {
3791 rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
3792 }
3793 NS_ENSURE_SUCCESS(rv, false);
3794
3795 return perm == aPerm;
3796 }
3797
IsSitePermAllow(nsIPrincipal * aPrincipal,const nsACString & aType)3798 bool nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal,
3799 const nsACString& aType) {
3800 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
3801 false);
3802 }
3803
IsSitePermDeny(nsIPrincipal * aPrincipal,const nsACString & aType)3804 bool nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal,
3805 const nsACString& aType) {
3806 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
3807 false);
3808 }
3809
IsExactSitePermAllow(nsIPrincipal * aPrincipal,const nsACString & aType)3810 bool nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal,
3811 const nsACString& aType) {
3812 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
3813 true);
3814 }
3815
IsExactSitePermDeny(nsIPrincipal * aPrincipal,const nsACString & aType)3816 bool nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal,
3817 const nsACString& aType) {
3818 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
3819 true);
3820 }
3821
3822 static const char* gEventNames[] = {"event"};
3823 static const char* gSVGEventNames[] = {"evt"};
3824 // for b/w compat, the first name to onerror is still 'event', even though it
3825 // is actually the error message
3826 static const char* gOnErrorNames[] = {"event", "source", "lineno", "colno",
3827 "error"};
3828
3829 // static
GetEventArgNames(int32_t aNameSpaceID,nsAtom * aEventName,bool aIsForWindow,uint32_t * aArgCount,const char *** aArgArray)3830 void nsContentUtils::GetEventArgNames(int32_t aNameSpaceID, nsAtom* aEventName,
3831 bool aIsForWindow, uint32_t* aArgCount,
3832 const char*** aArgArray) {
3833 #define SET_EVENT_ARG_NAMES(names) \
3834 *aArgCount = sizeof(names) / sizeof(names[0]); \
3835 *aArgArray = names;
3836
3837 // JSEventHandler is what does the arg magic for onerror, and it does
3838 // not seem to take the namespace into account. So we let onerror in all
3839 // namespaces get the 3 arg names.
3840 if (aEventName == nsGkAtoms::onerror && aIsForWindow) {
3841 SET_EVENT_ARG_NAMES(gOnErrorNames);
3842 } else if (aNameSpaceID == kNameSpaceID_SVG) {
3843 SET_EVENT_ARG_NAMES(gSVGEventNames);
3844 } else {
3845 SET_EVENT_ARG_NAMES(gEventNames);
3846 }
3847 }
3848
3849 // Note: The list of content bundles in nsStringBundle.cpp should be updated
3850 // whenever entries are added or removed from this list.
3851 static const char* gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT] = {
3852 // Must line up with the enum values in |PropertiesFile| enum.
3853 "chrome://global/locale/css.properties",
3854 "chrome://global/locale/xul.properties",
3855 "chrome://global/locale/layout_errors.properties",
3856 "chrome://global/locale/layout/HtmlForm.properties",
3857 "chrome://global/locale/printing.properties",
3858 "chrome://global/locale/dom/dom.properties",
3859 "chrome://global/locale/layout/htmlparser.properties",
3860 "chrome://global/locale/svg/svg.properties",
3861 "chrome://branding/locale/brand.properties",
3862 "chrome://global/locale/commonDialogs.properties",
3863 "chrome://global/locale/mathml/mathml.properties",
3864 "chrome://global/locale/security/security.properties",
3865 "chrome://necko/locale/necko.properties",
3866 "resource://gre/res/locale/layout/HtmlForm.properties",
3867 "resource://gre/res/locale/dom/dom.properties"};
3868
3869 /* static */
EnsureStringBundle(PropertiesFile aFile)3870 nsresult nsContentUtils::EnsureStringBundle(PropertiesFile aFile) {
3871 if (!sStringBundles[aFile]) {
3872 if (!sStringBundleService) {
3873 nsresult rv =
3874 CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
3875 NS_ENSURE_SUCCESS(rv, rv);
3876 }
3877 nsIStringBundle* bundle;
3878 nsresult rv =
3879 sStringBundleService->CreateBundle(gPropertiesFiles[aFile], &bundle);
3880 NS_ENSURE_SUCCESS(rv, rv);
3881 sStringBundles[aFile] = bundle; // transfer ownership
3882 }
3883 return NS_OK;
3884 }
3885
3886 /* static */
AsyncPrecreateStringBundles()3887 void nsContentUtils::AsyncPrecreateStringBundles() {
3888 // We only ever want to pre-create bundles in the parent process.
3889 //
3890 // All nsContentUtils bundles are shared between the parent and child
3891 // precesses, and the shared memory regions that back them *must* be created
3892 // in the parent, and then sent to all children.
3893 //
3894 // If we attempt to create a bundle in the child before its memory region is
3895 // available, we need to create a temporary non-shared bundle, and later
3896 // replace that with the shared memory copy. So attempting to pre-load in the
3897 // child is wasteful and unnecessary.
3898 MOZ_ASSERT(XRE_IsParentProcess());
3899
3900 for (uint32_t bundleIndex = 0; bundleIndex < PropertiesFile_COUNT;
3901 ++bundleIndex) {
3902 nsresult rv = NS_DispatchToCurrentThreadQueue(
3903 NS_NewRunnableFunction("AsyncPrecreateStringBundles",
3904 [bundleIndex]() {
3905 PropertiesFile file =
3906 static_cast<PropertiesFile>(bundleIndex);
3907 EnsureStringBundle(file);
3908 nsIStringBundle* bundle = sStringBundles[file];
3909 bundle->AsyncPreload();
3910 }),
3911 EventQueuePriority::Idle);
3912 Unused << NS_WARN_IF(NS_FAILED(rv));
3913 }
3914 }
3915
3916 /* static */
SpoofLocaleEnglish()3917 bool nsContentUtils::SpoofLocaleEnglish() {
3918 // 0 - will prompt
3919 // 1 - don't spoof
3920 // 2 - spoof
3921 return StaticPrefs::privacy_spoof_english() == 2;
3922 }
3923
GetMaybeSpoofedPropertiesFile(nsContentUtils::PropertiesFile aFile,const char * aKey,Document * aDocument)3924 static nsContentUtils::PropertiesFile GetMaybeSpoofedPropertiesFile(
3925 nsContentUtils::PropertiesFile aFile, const char* aKey,
3926 Document* aDocument) {
3927 // When we spoof English, use en-US properties in strings that are accessible
3928 // by content.
3929 bool spoofLocale = nsContentUtils::SpoofLocaleEnglish() &&
3930 (!aDocument || !aDocument->AllowsL10n());
3931 if (spoofLocale) {
3932 switch (aFile) {
3933 case nsContentUtils::eFORMS_PROPERTIES:
3934 return nsContentUtils::eFORMS_PROPERTIES_en_US;
3935 case nsContentUtils::eDOM_PROPERTIES:
3936 return nsContentUtils::eDOM_PROPERTIES_en_US;
3937 default:
3938 break;
3939 }
3940 }
3941 return aFile;
3942 }
3943
3944 /* static */
GetMaybeLocalizedString(PropertiesFile aFile,const char * aKey,Document * aDocument,nsAString & aResult)3945 nsresult nsContentUtils::GetMaybeLocalizedString(PropertiesFile aFile,
3946 const char* aKey,
3947 Document* aDocument,
3948 nsAString& aResult) {
3949 return GetLocalizedString(
3950 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aResult);
3951 }
3952
3953 /* static */
GetLocalizedString(PropertiesFile aFile,const char * aKey,nsAString & aResult)3954 nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
3955 const char* aKey,
3956 nsAString& aResult) {
3957 nsresult rv = EnsureStringBundle(aFile);
3958 NS_ENSURE_SUCCESS(rv, rv);
3959 nsIStringBundle* bundle = sStringBundles[aFile];
3960 return bundle->GetStringFromName(aKey, aResult);
3961 }
3962
3963 /* static */
FormatMaybeLocalizedString(PropertiesFile aFile,const char * aKey,Document * aDocument,const nsTArray<nsString> & aParams,nsAString & aResult)3964 nsresult nsContentUtils::FormatMaybeLocalizedString(
3965 PropertiesFile aFile, const char* aKey, Document* aDocument,
3966 const nsTArray<nsString>& aParams, nsAString& aResult) {
3967 return FormatLocalizedString(
3968 GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aParams,
3969 aResult);
3970 }
3971
3972 /* static */
FormatLocalizedString(PropertiesFile aFile,const char * aKey,const nsTArray<nsString> & aParams,nsAString & aResult)3973 nsresult nsContentUtils::FormatLocalizedString(
3974 PropertiesFile aFile, const char* aKey, const nsTArray<nsString>& aParams,
3975 nsAString& aResult) {
3976 nsresult rv = EnsureStringBundle(aFile);
3977 NS_ENSURE_SUCCESS(rv, rv);
3978 nsIStringBundle* bundle = sStringBundles[aFile];
3979
3980 if (aParams.IsEmpty()) {
3981 return bundle->GetStringFromName(aKey, aResult);
3982 }
3983
3984 return bundle->FormatStringFromName(aKey, aParams, aResult);
3985 }
3986
3987 /* static */
LogSimpleConsoleError(const nsAString & aErrorText,const char * aCategory,bool aFromPrivateWindow,bool aFromChromeContext,uint32_t aErrorFlags)3988 void nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
3989 const char* aCategory,
3990 bool aFromPrivateWindow,
3991 bool aFromChromeContext,
3992 uint32_t aErrorFlags) {
3993 nsCOMPtr<nsIScriptError> scriptError =
3994 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
3995 if (scriptError) {
3996 nsCOMPtr<nsIConsoleService> console =
3997 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
3998 if (console && NS_SUCCEEDED(scriptError->Init(
3999 aErrorText, u""_ns, u""_ns, 0, 0, aErrorFlags, aCategory,
4000 aFromPrivateWindow, aFromChromeContext))) {
4001 console->LogMessage(scriptError);
4002 }
4003 }
4004 }
4005
4006 /* static */
ReportToConsole(uint32_t aErrorFlags,const nsACString & aCategory,const Document * aDocument,PropertiesFile aFile,const char * aMessageName,const nsTArray<nsString> & aParams,nsIURI * aURI,const nsString & aSourceLine,uint32_t aLineNumber,uint32_t aColumnNumber)4007 nsresult nsContentUtils::ReportToConsole(
4008 uint32_t aErrorFlags, const nsACString& aCategory,
4009 const Document* aDocument, PropertiesFile aFile, const char* aMessageName,
4010 const nsTArray<nsString>& aParams, nsIURI* aURI,
4011 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber) {
4012 nsresult rv;
4013 nsAutoString errorText;
4014 if (!aParams.IsEmpty()) {
4015 rv = FormatLocalizedString(aFile, aMessageName, aParams, errorText);
4016 } else {
4017 rv = GetLocalizedString(aFile, aMessageName, errorText);
4018 }
4019 NS_ENSURE_SUCCESS(rv, rv);
4020
4021 return ReportToConsoleNonLocalized(errorText, aErrorFlags, aCategory,
4022 aDocument, aURI, aSourceLine, aLineNumber,
4023 aColumnNumber);
4024 }
4025
4026 /* static */
ReportEmptyGetElementByIdArg(const Document * aDoc)4027 void nsContentUtils::ReportEmptyGetElementByIdArg(const Document* aDoc) {
4028 ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, aDoc,
4029 nsContentUtils::eDOM_PROPERTIES, "EmptyGetElementByIdParam");
4030 }
4031
4032 /* static */
ReportToConsoleNonLocalized(const nsAString & aErrorText,uint32_t aErrorFlags,const nsACString & aCategory,const Document * aDocument,nsIURI * aURI,const nsString & aSourceLine,uint32_t aLineNumber,uint32_t aColumnNumber,MissingErrorLocationMode aLocationMode)4033 nsresult nsContentUtils::ReportToConsoleNonLocalized(
4034 const nsAString& aErrorText, uint32_t aErrorFlags,
4035 const nsACString& aCategory, const Document* aDocument, nsIURI* aURI,
4036 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4037 MissingErrorLocationMode aLocationMode) {
4038 uint64_t innerWindowID = 0;
4039 if (aDocument) {
4040 if (!aURI) {
4041 aURI = aDocument->GetDocumentURI();
4042 }
4043 innerWindowID = aDocument->InnerWindowID();
4044 }
4045
4046 return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
4047 innerWindowID, aURI, aSourceLine,
4048 aLineNumber, aColumnNumber, aLocationMode);
4049 }
4050
4051 /* static */
ReportToConsoleByWindowID(const nsAString & aErrorText,uint32_t aErrorFlags,const nsACString & aCategory,uint64_t aInnerWindowID,nsIURI * aURI,const nsString & aSourceLine,uint32_t aLineNumber,uint32_t aColumnNumber,MissingErrorLocationMode aLocationMode)4052 nsresult nsContentUtils::ReportToConsoleByWindowID(
4053 const nsAString& aErrorText, uint32_t aErrorFlags,
4054 const nsACString& aCategory, uint64_t aInnerWindowID, nsIURI* aURI,
4055 const nsString& aSourceLine, uint32_t aLineNumber, uint32_t aColumnNumber,
4056 MissingErrorLocationMode aLocationMode) {
4057 nsresult rv;
4058 if (!sConsoleService) { // only need to bother null-checking here
4059 rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4060 NS_ENSURE_SUCCESS(rv, rv);
4061 }
4062
4063 nsAutoString spec;
4064 if (!aLineNumber && aLocationMode == eUSE_CALLING_LOCATION) {
4065 JSContext* cx = GetCurrentJSContext();
4066 if (cx) {
4067 nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber, &aColumnNumber);
4068 }
4069 }
4070
4071 nsCOMPtr<nsIScriptError> errorObject =
4072 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
4073 NS_ENSURE_SUCCESS(rv, rv);
4074
4075 if (!spec.IsEmpty()) {
4076 rv = errorObject->InitWithWindowID(aErrorText,
4077 spec, // file name
4078 aSourceLine, aLineNumber, aColumnNumber,
4079 aErrorFlags, aCategory, aInnerWindowID);
4080 } else {
4081 rv = errorObject->InitWithSourceURI(aErrorText, aURI, aSourceLine,
4082 aLineNumber, aColumnNumber, aErrorFlags,
4083 aCategory, aInnerWindowID);
4084 }
4085 NS_ENSURE_SUCCESS(rv, rv);
4086
4087 return sConsoleService->LogMessage(errorObject);
4088 }
4089
LogMessageToConsole(const char * aMsg)4090 void nsContentUtils::LogMessageToConsole(const char* aMsg) {
4091 if (!sConsoleService) { // only need to bother null-checking here
4092 CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
4093 if (!sConsoleService) {
4094 return;
4095 }
4096 }
4097 sConsoleService->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
4098 }
4099
IsChromeDoc(const Document * aDocument)4100 bool nsContentUtils::IsChromeDoc(const Document* aDocument) {
4101 return aDocument && aDocument->NodePrincipal() == sSystemPrincipal;
4102 }
4103
IsChildOfSameType(Document * aDoc)4104 bool nsContentUtils::IsChildOfSameType(Document* aDoc) {
4105 if (BrowsingContext* bc = aDoc->GetBrowsingContext()) {
4106 return bc->GetParent();
4107 }
4108 return false;
4109 }
4110
IsPlainTextType(const nsACString & aContentType)4111 bool nsContentUtils::IsPlainTextType(const nsACString& aContentType) {
4112 // NOTE: if you add a type here, add it to the CONTENTDLF_CATEGORIES
4113 // define in nsContentDLF.h as well.
4114 return aContentType.EqualsLiteral(TEXT_PLAIN) ||
4115 aContentType.EqualsLiteral(TEXT_CSS) ||
4116 aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4117 aContentType.EqualsLiteral(TEXT_VTT) ||
4118 aContentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
4119 aContentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
4120 aContentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
4121 aContentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
4122 aContentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
4123 aContentType.EqualsLiteral(APPLICATION_JSON) ||
4124 aContentType.EqualsLiteral(TEXT_JSON);
4125 }
4126
IsUtf8OnlyPlainTextType(const nsACString & aContentType)4127 bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) {
4128 // NOTE: This must be a subset of the list in IsPlainTextType().
4129 return aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
4130 aContentType.EqualsLiteral(APPLICATION_JSON) ||
4131 aContentType.EqualsLiteral(TEXT_JSON) ||
4132 aContentType.EqualsLiteral(TEXT_VTT);
4133 }
4134
IsInChromeDocshell(const Document * aDocument)4135 bool nsContentUtils::IsInChromeDocshell(const Document* aDocument) {
4136 return aDocument && aDocument->IsInChromeDocShell();
4137 }
4138
4139 // static
GetContentPolicy()4140 nsIContentPolicy* nsContentUtils::GetContentPolicy() {
4141 if (!sTriedToGetContentPolicy) {
4142 CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
4143 // It's OK to not have a content policy service
4144 sTriedToGetContentPolicy = true;
4145 }
4146
4147 return sContentPolicyService;
4148 }
4149
4150 // static
IsEventAttributeName(nsAtom * aName,int32_t aType)4151 bool nsContentUtils::IsEventAttributeName(nsAtom* aName, int32_t aType) {
4152 const char16_t* name = aName->GetUTF16String();
4153 if (name[0] != 'o' || name[1] != 'n') {
4154 return false;
4155 }
4156
4157 EventNameMapping mapping;
4158 return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
4159 }
4160
4161 // static
GetEventMessage(nsAtom * aName)4162 EventMessage nsContentUtils::GetEventMessage(nsAtom* aName) {
4163 MOZ_ASSERT(NS_IsMainThread(), "sAtomEventTable is not threadsafe");
4164 if (aName) {
4165 EventNameMapping mapping;
4166 if (sAtomEventTable->Get(aName, &mapping)) {
4167 return mapping.mMessage;
4168 }
4169 }
4170
4171 return eUnidentifiedEvent;
4172 }
4173
4174 // static
GetEventClassID(const nsAString & aName)4175 mozilla::EventClassID nsContentUtils::GetEventClassID(const nsAString& aName) {
4176 EventNameMapping mapping;
4177 if (sStringEventTable->Get(aName, &mapping)) return mapping.mEventClassID;
4178
4179 return eBasicEventClass;
4180 }
4181
GetEventMessageAndAtom(const nsAString & aName,mozilla::EventClassID aEventClassID,EventMessage * aEventMessage)4182 nsAtom* nsContentUtils::GetEventMessageAndAtom(
4183 const nsAString& aName, mozilla::EventClassID aEventClassID,
4184 EventMessage* aEventMessage) {
4185 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4186 EventNameMapping mapping;
4187 if (sStringEventTable->Get(aName, &mapping)) {
4188 *aEventMessage = mapping.mEventClassID == aEventClassID
4189 ? mapping.mMessage
4190 : eUnidentifiedEvent;
4191 return mapping.mAtom;
4192 }
4193
4194 // If we have cached lots of user defined event names, clear some of them.
4195 if (sUserDefinedEvents->Length() > 127) {
4196 while (sUserDefinedEvents->Length() > 64) {
4197 nsAtom* first = sUserDefinedEvents->ElementAt(0);
4198 sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
4199 sUserDefinedEvents->RemoveElementAt(0);
4200 }
4201 }
4202
4203 *aEventMessage = eUnidentifiedEvent;
4204 RefPtr<nsAtom> atom = NS_AtomizeMainThread(u"on"_ns + aName);
4205 sUserDefinedEvents->AppendElement(atom);
4206 mapping.mAtom = atom;
4207 mapping.mMessage = eUnidentifiedEvent;
4208 mapping.mType = EventNameType_None;
4209 mapping.mEventClassID = eBasicEventClass;
4210 // This is a slow hashtable call, but at least we cache the result for the
4211 // following calls. Because GetEventMessageAndAtomForListener utilizes
4212 // sStringEventTable, it needs to know in which cases sStringEventTable
4213 // doesn't contain the information it needs so that it can use
4214 // sAtomEventTable instead.
4215 mapping.mMaybeSpecialSVGorSMILEvent =
4216 GetEventMessage(atom) != eUnidentifiedEvent;
4217 sStringEventTable->InsertOrUpdate(aName, mapping);
4218 return mapping.mAtom;
4219 }
4220
4221 // static
GetEventMessageAndAtomForListener(const nsAString & aName,nsAtom ** aOnName)4222 EventMessage nsContentUtils::GetEventMessageAndAtomForListener(
4223 const nsAString& aName, nsAtom** aOnName) {
4224 MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
4225
4226 // Because of SVG/SMIL sStringEventTable contains a subset of the event names
4227 // comparing to the sAtomEventTable. However, usually sStringEventTable
4228 // contains the information we need, so in order to reduce hashtable
4229 // lookups, start from it.
4230 EventNameMapping mapping;
4231 EventMessage msg = eUnidentifiedEvent;
4232 RefPtr<nsAtom> atom;
4233 if (sStringEventTable->Get(aName, &mapping)) {
4234 if (mapping.mMaybeSpecialSVGorSMILEvent) {
4235 // Try the atom version so that we should get the right message for
4236 // SVG/SMIL.
4237 atom = NS_AtomizeMainThread(u"on"_ns + aName);
4238 msg = GetEventMessage(atom);
4239 } else {
4240 atom = mapping.mAtom;
4241 msg = mapping.mMessage;
4242 }
4243 atom.forget(aOnName);
4244 return msg;
4245 }
4246
4247 // GetEventMessageAndAtom will cache the event type for the future usage...
4248 GetEventMessageAndAtom(aName, eBasicEventClass, &msg);
4249
4250 // ...and then call this method recursively to get the message and atom from
4251 // now updated sStringEventTable.
4252 return GetEventMessageAndAtomForListener(aName, aOnName);
4253 }
4254
GetEventAndTarget(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,Composed aComposed,Trusted aTrusted,Event ** aEvent,EventTarget ** aTargetOut)4255 static nsresult GetEventAndTarget(Document* aDoc, nsISupports* aTarget,
4256 const nsAString& aEventName,
4257 CanBubble aCanBubble, Cancelable aCancelable,
4258 Composed aComposed, Trusted aTrusted,
4259 Event** aEvent, EventTarget** aTargetOut) {
4260 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4261 NS_ENSURE_TRUE(aDoc && target, NS_ERROR_INVALID_ARG);
4262
4263 ErrorResult err;
4264 RefPtr<Event> event =
4265 aDoc->CreateEvent(u"Events"_ns, CallerType::System, err);
4266 if (NS_WARN_IF(err.Failed())) {
4267 return err.StealNSResult();
4268 }
4269
4270 event->InitEvent(aEventName, aCanBubble, aCancelable, aComposed);
4271 event->SetTrusted(aTrusted == Trusted::eYes);
4272
4273 event->SetTarget(target);
4274
4275 event.forget(aEvent);
4276 target.forget(aTargetOut);
4277 return NS_OK;
4278 }
4279
4280 // static
DispatchTrustedEvent(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,Composed aComposed,bool * aDefaultAction)4281 nsresult nsContentUtils::DispatchTrustedEvent(
4282 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4283 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4284 bool* aDefaultAction) {
4285 MOZ_ASSERT(!aEventName.EqualsLiteral("input") &&
4286 !aEventName.EqualsLiteral("beforeinput"),
4287 "Use DispatchInputEvent() instead");
4288 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4289 aComposed, Trusted::eYes, aDefaultAction);
4290 }
4291
4292 // static
DispatchUntrustedEvent(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,bool * aDefaultAction)4293 nsresult nsContentUtils::DispatchUntrustedEvent(
4294 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4295 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4296 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4297 Composed::eDefault, Trusted::eNo, aDefaultAction);
4298 }
4299
4300 // static
DispatchEvent(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,Composed aComposed,Trusted aTrusted,bool * aDefaultAction,ChromeOnlyDispatch aOnlyChromeDispatch)4301 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4302 const nsAString& aEventName,
4303 CanBubble aCanBubble,
4304 Cancelable aCancelable,
4305 Composed aComposed, Trusted aTrusted,
4306 bool* aDefaultAction,
4307 ChromeOnlyDispatch aOnlyChromeDispatch) {
4308 RefPtr<Event> event;
4309 nsCOMPtr<EventTarget> target;
4310 nsresult rv = GetEventAndTarget(
4311 aDoc, aTarget, aEventName, aCanBubble, aCancelable, aComposed, aTrusted,
4312 getter_AddRefs(event), getter_AddRefs(target));
4313 NS_ENSURE_SUCCESS(rv, rv);
4314 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch =
4315 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4316
4317 ErrorResult err;
4318 bool doDefault = target->DispatchEvent(*event, CallerType::System, err);
4319 if (aDefaultAction) {
4320 *aDefaultAction = doDefault;
4321 }
4322 return err.StealNSResult();
4323 }
4324
4325 // static
DispatchEvent(Document * aDoc,nsISupports * aTarget,WidgetEvent & aEvent,EventMessage aEventMessage,CanBubble aCanBubble,Cancelable aCancelable,Trusted aTrusted,bool * aDefaultAction,ChromeOnlyDispatch aOnlyChromeDispatch)4326 nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
4327 WidgetEvent& aEvent,
4328 EventMessage aEventMessage,
4329 CanBubble aCanBubble,
4330 Cancelable aCancelable, Trusted aTrusted,
4331 bool* aDefaultAction,
4332 ChromeOnlyDispatch aOnlyChromeDispatch) {
4333 MOZ_ASSERT_IF(aOnlyChromeDispatch == ChromeOnlyDispatch::eYes,
4334 aTrusted == Trusted::eYes);
4335
4336 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
4337
4338 aEvent.mTime = PR_Now();
4339
4340 aEvent.mSpecifiedEventType = GetEventTypeFromMessage(aEventMessage);
4341 aEvent.SetDefaultComposed();
4342 aEvent.SetDefaultComposedInNativeAnonymousContent();
4343
4344 aEvent.mFlags.mBubbles = aCanBubble == CanBubble::eYes;
4345 aEvent.mFlags.mCancelable = aCancelable == Cancelable::eYes;
4346 aEvent.mFlags.mOnlyChromeDispatch =
4347 aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
4348
4349 aEvent.mTarget = target;
4350
4351 nsEventStatus status = nsEventStatus_eIgnore;
4352 nsresult rv = EventDispatcher::DispatchDOMEvent(target, &aEvent, nullptr,
4353 nullptr, &status);
4354 if (aDefaultAction) {
4355 *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
4356 }
4357 return rv;
4358 }
4359
4360 // static
DispatchInputEvent(Element * aEventTarget)4361 nsresult nsContentUtils::DispatchInputEvent(Element* aEventTarget) {
4362 return DispatchInputEvent(aEventTarget, mozilla::eEditorInput,
4363 mozilla::EditorInputType::eUnknown, nullptr,
4364 InputEventOptions());
4365 }
4366
4367 // static
DispatchInputEvent(Element * aEventTargetElement,EventMessage aEventMessage,EditorInputType aEditorInputType,EditorBase * aEditorBase,InputEventOptions && aOptions,nsEventStatus * aEventStatus)4368 nsresult nsContentUtils::DispatchInputEvent(
4369 Element* aEventTargetElement, EventMessage aEventMessage,
4370 EditorInputType aEditorInputType, EditorBase* aEditorBase,
4371 InputEventOptions&& aOptions, nsEventStatus* aEventStatus /* = nullptr */) {
4372 MOZ_ASSERT(aEventMessage == eEditorInput ||
4373 aEventMessage == eEditorBeforeInput);
4374
4375 if (NS_WARN_IF(!aEventTargetElement)) {
4376 return NS_ERROR_INVALID_ARG;
4377 }
4378
4379 // If this is called from editor, the instance should be set to aEditorBase.
4380 // Otherwise, we need to look for an editor for aEventTargetElement.
4381 // However, we don't need to do it for HTMLEditor since nobody shouldn't
4382 // dispatch "beforeinput" nor "input" event for HTMLEditor except HTMLEditor
4383 // itself.
4384 bool useInputEvent = false;
4385 if (aEditorBase) {
4386 useInputEvent = true;
4387 } else if (HTMLTextAreaElement* textAreaElement =
4388 HTMLTextAreaElement::FromNode(aEventTargetElement)) {
4389 aEditorBase = textAreaElement->GetTextEditorWithoutCreation();
4390 useInputEvent = true;
4391 } else if (HTMLInputElement* inputElement =
4392 HTMLInputElement::FromNode(aEventTargetElement)) {
4393 if (inputElement->IsInputEventTarget()) {
4394 aEditorBase = inputElement->GetTextEditorWithoutCreation();
4395 useInputEvent = true;
4396 }
4397 }
4398 #ifdef DEBUG
4399 else {
4400 MOZ_ASSERT(!aEventTargetElement->IsTextControlElement(),
4401 "The event target may have editor, but we've not known it yet.");
4402 }
4403 #endif // #ifdef DEBUG
4404
4405 if (!useInputEvent) {
4406 MOZ_ASSERT(aEventMessage == eEditorInput);
4407 MOZ_ASSERT(aEditorInputType == EditorInputType::eUnknown);
4408 MOZ_ASSERT(!aOptions.mNeverCancelable);
4409 // Dispatch "input" event with Event instance.
4410 WidgetEvent widgetEvent(true, eUnidentifiedEvent);
4411 widgetEvent.mSpecifiedEventType = nsGkAtoms::oninput;
4412 widgetEvent.mFlags.mCancelable = false;
4413 widgetEvent.mFlags.mComposed = true;
4414 // Using same time as nsContentUtils::DispatchEvent() for backward
4415 // compatibility.
4416 widgetEvent.mTime = PR_Now();
4417 (new AsyncEventDispatcher(aEventTargetElement, widgetEvent))
4418 ->RunDOMEventWhenSafe();
4419 return NS_OK;
4420 }
4421
4422 MOZ_ASSERT_IF(aEventMessage != eEditorBeforeInput,
4423 !aOptions.mNeverCancelable);
4424 MOZ_ASSERT_IF(
4425 aEventMessage == eEditorBeforeInput && aOptions.mNeverCancelable,
4426 aEditorInputType == EditorInputType::eInsertReplacementText);
4427
4428 nsCOMPtr<nsIWidget> widget;
4429 if (aEditorBase) {
4430 widget = aEditorBase->GetWidget();
4431 if (NS_WARN_IF(!widget)) {
4432 return NS_ERROR_FAILURE;
4433 }
4434 } else {
4435 Document* document = aEventTargetElement->OwnerDoc();
4436 if (NS_WARN_IF(!document)) {
4437 return NS_ERROR_FAILURE;
4438 }
4439 // If we're running xpcshell tests, we fail to get presShell here.
4440 // Even in such case, we need to dispatch "input" event without widget.
4441 PresShell* presShell = document->GetPresShell();
4442 if (presShell) {
4443 nsPresContext* presContext = presShell->GetPresContext();
4444 if (NS_WARN_IF(!presContext)) {
4445 return NS_ERROR_FAILURE;
4446 }
4447 widget = presContext->GetRootWidget();
4448 if (NS_WARN_IF(!widget)) {
4449 return NS_ERROR_FAILURE;
4450 }
4451 }
4452 }
4453
4454 // Dispatch "input" event with InputEvent instance.
4455 InternalEditorInputEvent inputEvent(true, aEventMessage, widget);
4456
4457 inputEvent.mFlags.mCancelable =
4458 !aOptions.mNeverCancelable && aEventMessage == eEditorBeforeInput &&
4459 IsCancelableBeforeInputEvent(aEditorInputType);
4460 MOZ_ASSERT(!inputEvent.mFlags.mCancelable || aEventStatus);
4461
4462 // Using same time as old event dispatcher in EditorBase for backward
4463 // compatibility.
4464 inputEvent.mTime = static_cast<uint64_t>(PR_Now() / 1000);
4465
4466 // If there is an editor, set isComposing to true when it has composition.
4467 // Note that EditorBase::IsIMEComposing() may return false even when we
4468 // need to set it to true.
4469 // Otherwise, i.e., editor hasn't been created for the element yet,
4470 // we should set isComposing to false since the element can never has
4471 // composition without editor.
4472 inputEvent.mIsComposing = aEditorBase && aEditorBase->GetComposition();
4473
4474 if (!aEditorBase || aEditorBase->IsTextEditor()) {
4475 if (IsDataAvailableOnTextEditor(aEditorInputType)) {
4476 inputEvent.mData = std::move(aOptions.mData);
4477 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4478 "inputEvent.mData shouldn't be void");
4479 }
4480 #ifdef DEBUG
4481 else {
4482 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4483 }
4484 #endif // #ifdef DEBUG
4485 MOZ_ASSERT(
4486 aOptions.mTargetRanges.IsEmpty(),
4487 "Target ranges for <input> and <textarea> should always be empty");
4488 } else {
4489 MOZ_ASSERT(aEditorBase->IsHTMLEditor());
4490 if (IsDataAvailableOnHTMLEditor(aEditorInputType)) {
4491 inputEvent.mData = std::move(aOptions.mData);
4492 MOZ_ASSERT(!inputEvent.mData.IsVoid(),
4493 "inputEvent.mData shouldn't be void");
4494 } else {
4495 MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
4496 if (IsDataTransferAvailableOnHTMLEditor(aEditorInputType)) {
4497 inputEvent.mDataTransfer = std::move(aOptions.mDataTransfer);
4498 MOZ_ASSERT(inputEvent.mDataTransfer,
4499 "inputEvent.mDataTransfer shouldn't be nullptr");
4500 MOZ_ASSERT(inputEvent.mDataTransfer->IsReadOnly(),
4501 "inputEvent.mDataTransfer should be read only");
4502 }
4503 #ifdef DEBUG
4504 else {
4505 MOZ_ASSERT(!inputEvent.mDataTransfer,
4506 "inputEvent.mDataTransfer should be nullptr");
4507 }
4508 #endif // #ifdef DEBUG
4509 }
4510 if (aEventMessage == eEditorBeforeInput &&
4511 MayHaveTargetRangesOnHTMLEditor(aEditorInputType)) {
4512 inputEvent.mTargetRanges = std::move(aOptions.mTargetRanges);
4513 }
4514 #ifdef DEBUG
4515 else {
4516 MOZ_ASSERT(aOptions.mTargetRanges.IsEmpty(),
4517 "Target ranges shouldn't be set for the dispatching event");
4518 }
4519 #endif // #ifdef DEBUG
4520 }
4521
4522 inputEvent.mInputType = aEditorInputType;
4523
4524 if (!IsSafeToRunScript()) {
4525 // If we cannot dispatch an event right now, we cannot make it cancelable.
4526 NS_ASSERTION(
4527 !inputEvent.mFlags.mCancelable,
4528 "Cancelable beforeinput event dispatcher should run when it's safe");
4529 inputEvent.mFlags.mCancelable = false;
4530 (new AsyncEventDispatcher(aEventTargetElement, inputEvent))
4531 ->RunDOMEventWhenSafe();
4532 return NS_OK;
4533 }
4534
4535 // If we're running xpcshell tests, we fail to get presShell here.
4536 // Even in such case, we need to dispatch "input" event without widget.
4537 RefPtr<nsPresContext> presContext =
4538 aEventTargetElement->OwnerDoc()->GetPresContext();
4539 nsresult rv = EventDispatcher::Dispatch(aEventTargetElement, presContext,
4540 &inputEvent, nullptr, aEventStatus);
4541 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
4542 "Dispatching `beforeinput` or `input` event failed");
4543 return rv;
4544 }
4545
DispatchChromeEvent(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,bool * aDefaultAction)4546 nsresult nsContentUtils::DispatchChromeEvent(
4547 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4548 CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
4549 RefPtr<Event> event;
4550 nsCOMPtr<EventTarget> target;
4551 nsresult rv = GetEventAndTarget(
4552 aDoc, aTarget, aEventName, aCanBubble, aCancelable, Composed::eDefault,
4553 Trusted::eYes, getter_AddRefs(event), getter_AddRefs(target));
4554 NS_ENSURE_SUCCESS(rv, rv);
4555
4556 NS_ASSERTION(aDoc, "GetEventAndTarget lied?");
4557 if (!aDoc->GetWindow()) return NS_ERROR_INVALID_ARG;
4558
4559 EventTarget* piTarget = aDoc->GetWindow()->GetParentTarget();
4560 if (!piTarget) return NS_ERROR_INVALID_ARG;
4561
4562 ErrorResult err;
4563 bool defaultActionEnabled =
4564 piTarget->DispatchEvent(*event, CallerType::System, err);
4565 if (aDefaultAction) {
4566 *aDefaultAction = defaultActionEnabled;
4567 }
4568 return err.StealNSResult();
4569 }
4570
RequestFrameFocus(Element & aFrameElement,bool aCanRaise,CallerType aCallerType)4571 void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise,
4572 CallerType aCallerType) {
4573 RefPtr<Element> target = &aFrameElement;
4574 bool defaultAction = true;
4575 if (aCanRaise) {
4576 DispatchEventOnlyToChrome(target->OwnerDoc(), target,
4577 u"framefocusrequested"_ns, CanBubble::eYes,
4578 Cancelable::eYes, &defaultAction);
4579 }
4580 if (!defaultAction) {
4581 return;
4582 }
4583
4584 RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
4585 if (!fm) {
4586 return;
4587 }
4588
4589 uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
4590 if (aCanRaise) {
4591 flags |= nsIFocusManager::FLAG_RAISE;
4592 }
4593
4594 if (aCallerType == CallerType::NonSystem) {
4595 flags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
4596 }
4597
4598 fm->SetFocus(target, flags);
4599 }
4600
DispatchEventOnlyToChrome(Document * aDoc,nsISupports * aTarget,const nsAString & aEventName,CanBubble aCanBubble,Cancelable aCancelable,Composed aComposed,bool * aDefaultAction)4601 nsresult nsContentUtils::DispatchEventOnlyToChrome(
4602 Document* aDoc, nsISupports* aTarget, const nsAString& aEventName,
4603 CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
4604 bool* aDefaultAction) {
4605 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
4606 aComposed, Trusted::eYes, aDefaultAction,
4607 ChromeOnlyDispatch::eYes);
4608 }
4609
4610 /* static */
MatchElementId(nsIContent * aContent,const nsAtom * aId)4611 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4612 const nsAtom* aId) {
4613 for (nsIContent* cur = aContent; cur; cur = cur->GetNextNode(aContent)) {
4614 if (aId == cur->GetID()) {
4615 return cur->AsElement();
4616 }
4617 }
4618
4619 return nullptr;
4620 }
4621
4622 /* static */
MatchElementId(nsIContent * aContent,const nsAString & aId)4623 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
4624 const nsAString& aId) {
4625 MOZ_ASSERT(!aId.IsEmpty(), "Will match random elements");
4626
4627 // ID attrs are generally stored as atoms, so just atomize this up front
4628 RefPtr<nsAtom> id(NS_Atomize(aId));
4629 if (!id) {
4630 // OOM, so just bail
4631 return nullptr;
4632 }
4633
4634 return MatchElementId(aContent, id);
4635 }
4636
4637 /* static */
RegisterShutdownObserver(nsIObserver * aObserver)4638 void nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver) {
4639 nsCOMPtr<nsIObserverService> observerService =
4640 mozilla::services::GetObserverService();
4641 if (observerService) {
4642 observerService->AddObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
4643 false);
4644 }
4645 }
4646
4647 /* static */
UnregisterShutdownObserver(nsIObserver * aObserver)4648 void nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver) {
4649 nsCOMPtr<nsIObserverService> observerService =
4650 mozilla::services::GetObserverService();
4651 if (observerService) {
4652 observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
4653 }
4654 }
4655
4656 /* static */
HasNonEmptyAttr(const nsIContent * aContent,int32_t aNameSpaceID,nsAtom * aName)4657 bool nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent,
4658 int32_t aNameSpaceID, nsAtom* aName) {
4659 static Element::AttrValuesArray strings[] = {nsGkAtoms::_empty, nullptr};
4660 return aContent->IsElement() &&
4661 aContent->AsElement()->FindAttrValueIn(aNameSpaceID, aName, strings,
4662 eCaseMatters) ==
4663 Element::ATTR_VALUE_NO_MATCH;
4664 }
4665
4666 /* static */
HasMutationListeners(nsINode * aNode,uint32_t aType,nsINode * aTargetForSubtreeModified)4667 bool nsContentUtils::HasMutationListeners(nsINode* aNode, uint32_t aType,
4668 nsINode* aTargetForSubtreeModified) {
4669 Document* doc = aNode->OwnerDoc();
4670
4671 // global object will be null for documents that don't have windows.
4672 nsPIDOMWindowInner* window = doc->GetInnerWindow();
4673 // This relies on EventListenerManager::AddEventListener, which sets
4674 // all mutation bits when there is a listener for DOMSubtreeModified event.
4675 if (window && !window->HasMutationListeners(aType)) {
4676 return false;
4677 }
4678
4679 if (aNode->ChromeOnlyAccess() || aNode->IsInShadowTree()) {
4680 return false;
4681 }
4682
4683 doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
4684
4685 // If we have a window, we can check it for mutation listeners now.
4686 if (aNode->IsInUncomposedDoc()) {
4687 nsCOMPtr<EventTarget> piTarget(do_QueryInterface(window));
4688 if (piTarget) {
4689 EventListenerManager* manager = piTarget->GetExistingListenerManager();
4690 if (manager && manager->HasMutationListeners()) {
4691 return true;
4692 }
4693 }
4694 }
4695
4696 // If we have a window, we know a mutation listener is registered, but it
4697 // might not be in our chain. If we don't have a window, we might have a
4698 // mutation listener. Check quickly to see.
4699 while (aNode) {
4700 EventListenerManager* manager = aNode->GetExistingListenerManager();
4701 if (manager && manager->HasMutationListeners()) {
4702 return true;
4703 }
4704
4705 aNode = aNode->GetParentNode();
4706 }
4707
4708 return false;
4709 }
4710
4711 /* static */
HasMutationListeners(Document * aDocument,uint32_t aType)4712 bool nsContentUtils::HasMutationListeners(Document* aDocument, uint32_t aType) {
4713 nsPIDOMWindowInner* window =
4714 aDocument ? aDocument->GetInnerWindow() : nullptr;
4715
4716 // This relies on EventListenerManager::AddEventListener, which sets
4717 // all mutation bits when there is a listener for DOMSubtreeModified event.
4718 return !window || window->HasMutationListeners(aType);
4719 }
4720
MaybeFireNodeRemoved(nsINode * aChild,nsINode * aParent)4721 void nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent) {
4722 MOZ_ASSERT(aChild, "Missing child");
4723 MOZ_ASSERT(aChild->GetParentNode() == aParent, "Wrong parent");
4724 MOZ_ASSERT(aChild->OwnerDoc() == aParent->OwnerDoc(), "Wrong owner-doc");
4725
4726 // Having an explicit check here since it's an easy mistake to fall into,
4727 // and there might be existing code with problems. We'd rather be safe
4728 // than fire DOMNodeRemoved in all corner cases. We also rely on it for
4729 // nsAutoScriptBlockerSuppressNodeRemoved.
4730 if (!IsSafeToRunScript()) {
4731 // This checks that IsSafeToRunScript is true since we don't want to fire
4732 // events when that is false. We can't rely on EventDispatcher to assert
4733 // this in this situation since most of the time there are no mutation
4734 // event listeners, in which case we won't even attempt to dispatch events.
4735 // However this also allows for two exceptions. First off, we don't assert
4736 // if the mutation happens to native anonymous content since we never fire
4737 // mutation events on such content anyway.
4738 // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
4739 // that is a know case when we'd normally fire a mutation event, but can't
4740 // make that safe and so we suppress it at this time. Ideally this should
4741 // go away eventually.
4742 if (!aChild->IsInNativeAnonymousSubtree() &&
4743 !sDOMNodeRemovedSuppressCount) {
4744 NS_ERROR("Want to fire DOMNodeRemoved event, but it's not safe");
4745 WarnScriptWasIgnored(aChild->OwnerDoc());
4746 }
4747 return;
4748 }
4749
4750 {
4751 Document* doc = aParent->OwnerDoc();
4752 if (MOZ_UNLIKELY(doc->DevToolsWatchingDOMMutations()) &&
4753 aChild->IsInComposedDoc() && !aChild->ChromeOnlyAccess()) {
4754 DispatchChromeEvent(doc, aChild, u"devtoolschildremoved"_ns,
4755 CanBubble::eNo, Cancelable::eNo);
4756 }
4757 }
4758
4759 if (HasMutationListeners(aChild, NS_EVENT_BITS_MUTATION_NODEREMOVED,
4760 aParent)) {
4761 InternalMutationEvent mutation(true, eLegacyNodeRemoved);
4762 mutation.mRelatedNode = aParent;
4763
4764 mozAutoSubtreeModified subtree(aParent->OwnerDoc(), aParent);
4765 EventDispatcher::Dispatch(aChild, nullptr, &mutation);
4766 }
4767 }
4768
UnmarkGrayJSListenersInCCGenerationDocuments()4769 void nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments() {
4770 if (!sEventListenerManagersHash) {
4771 return;
4772 }
4773
4774 for (auto i = sEventListenerManagersHash->Iter(); !i.Done(); i.Next()) {
4775 auto entry = static_cast<EventListenerManagerMapEntry*>(i.Get());
4776 nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
4777 if (n && n->IsInComposedDoc() &&
4778 nsCCUncollectableMarker::InGeneration(
4779 n->OwnerDoc()->GetMarkedCCGeneration())) {
4780 entry->mListenerManager->MarkForCC();
4781 }
4782 }
4783 }
4784
4785 /* static */
TraverseListenerManager(nsINode * aNode,nsCycleCollectionTraversalCallback & cb)4786 void nsContentUtils::TraverseListenerManager(
4787 nsINode* aNode, nsCycleCollectionTraversalCallback& cb) {
4788 if (!sEventListenerManagersHash) {
4789 // We're already shut down, just return.
4790 return;
4791 }
4792
4793 auto entry = static_cast<EventListenerManagerMapEntry*>(
4794 sEventListenerManagersHash->Search(aNode));
4795 if (entry) {
4796 CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
4797 "[via hash] mListenerManager");
4798 }
4799 }
4800
GetListenerManagerForNode(nsINode * aNode)4801 EventListenerManager* nsContentUtils::GetListenerManagerForNode(
4802 nsINode* aNode) {
4803 if (!sEventListenerManagersHash) {
4804 // We're already shut down, don't bother creating an event listener
4805 // manager.
4806
4807 return nullptr;
4808 }
4809
4810 auto entry = static_cast<EventListenerManagerMapEntry*>(
4811 sEventListenerManagersHash->Add(aNode, fallible));
4812
4813 if (!entry) {
4814 return nullptr;
4815 }
4816
4817 if (!entry->mListenerManager) {
4818 entry->mListenerManager = new EventListenerManager(aNode);
4819
4820 aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
4821 }
4822
4823 return entry->mListenerManager;
4824 }
4825
GetExistingListenerManagerForNode(const nsINode * aNode)4826 EventListenerManager* nsContentUtils::GetExistingListenerManagerForNode(
4827 const nsINode* aNode) {
4828 if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
4829 return nullptr;
4830 }
4831
4832 if (!sEventListenerManagersHash) {
4833 // We're already shut down, don't bother creating an event listener
4834 // manager.
4835
4836 return nullptr;
4837 }
4838
4839 auto entry = static_cast<EventListenerManagerMapEntry*>(
4840 sEventListenerManagersHash->Search(aNode));
4841 if (entry) {
4842 return entry->mListenerManager;
4843 }
4844
4845 return nullptr;
4846 }
4847
AddEntryToDOMArenaTable(nsINode * aNode,DOMArena * aDOMArena)4848 void nsContentUtils::AddEntryToDOMArenaTable(nsINode* aNode,
4849 DOMArena* aDOMArena) {
4850 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
4851 MOZ_ASSERT_IF(sDOMArenaHashtable, !sDOMArenaHashtable->Contains(aNode));
4852 MOZ_ASSERT(!aNode->HasFlag(NODE_KEEPS_DOMARENA));
4853 if (!sDOMArenaHashtable) {
4854 sDOMArenaHashtable =
4855 new nsRefPtrHashtable<nsPtrHashKey<const nsINode>, dom::DOMArena>();
4856 }
4857 aNode->SetFlags(NODE_KEEPS_DOMARENA);
4858 sDOMArenaHashtable->InsertOrUpdate(aNode, RefPtr<DOMArena>(aDOMArena));
4859 }
4860
TakeEntryFromDOMArenaTable(const nsINode * aNode)4861 already_AddRefed<DOMArena> nsContentUtils::TakeEntryFromDOMArenaTable(
4862 const nsINode* aNode) {
4863 MOZ_ASSERT(sDOMArenaHashtable->Contains(aNode));
4864 MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
4865 RefPtr<DOMArena> arena;
4866 sDOMArenaHashtable->Remove(aNode, getter_AddRefs(arena));
4867 return arena.forget();
4868 }
4869
4870 /* static */
RemoveListenerManager(nsINode * aNode)4871 void nsContentUtils::RemoveListenerManager(nsINode* aNode) {
4872 if (sEventListenerManagersHash) {
4873 auto entry = static_cast<EventListenerManagerMapEntry*>(
4874 sEventListenerManagersHash->Search(aNode));
4875 if (entry) {
4876 RefPtr<EventListenerManager> listenerManager;
4877 listenerManager.swap(entry->mListenerManager);
4878 // Remove the entry and *then* do operations that could cause further
4879 // modification of sEventListenerManagersHash. See bug 334177.
4880 sEventListenerManagersHash->RawRemove(entry);
4881 if (listenerManager) {
4882 listenerManager->Disconnect();
4883 }
4884 }
4885 }
4886 }
4887
4888 /* static */
IsValidNodeName(nsAtom * aLocalName,nsAtom * aPrefix,int32_t aNamespaceID)4889 bool nsContentUtils::IsValidNodeName(nsAtom* aLocalName, nsAtom* aPrefix,
4890 int32_t aNamespaceID) {
4891 if (aNamespaceID == kNameSpaceID_Unknown) {
4892 return false;
4893 }
4894
4895 if (!aPrefix) {
4896 // If the prefix is null, then either the QName must be xmlns or the
4897 // namespace must not be XMLNS.
4898 return (aLocalName == nsGkAtoms::xmlns) ==
4899 (aNamespaceID == kNameSpaceID_XMLNS);
4900 }
4901
4902 // If the prefix is non-null then the namespace must not be null.
4903 if (aNamespaceID == kNameSpaceID_None) {
4904 return false;
4905 }
4906
4907 // If the namespace is the XMLNS namespace then the prefix must be xmlns,
4908 // but the localname must not be xmlns.
4909 if (aNamespaceID == kNameSpaceID_XMLNS) {
4910 return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
4911 }
4912
4913 // If the namespace is not the XMLNS namespace then the prefix must not be
4914 // xmlns.
4915 // If the namespace is the XML namespace then the prefix can be anything.
4916 // If the namespace is not the XML namespace then the prefix must not be xml.
4917 return aPrefix != nsGkAtoms::xmlns &&
4918 (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
4919 }
4920
CreateContextualFragment(nsINode * aContextNode,const nsAString & aFragment,bool aPreventScriptExecution,ErrorResult & aRv)4921 already_AddRefed<DocumentFragment> nsContentUtils::CreateContextualFragment(
4922 nsINode* aContextNode, const nsAString& aFragment,
4923 bool aPreventScriptExecution, ErrorResult& aRv) {
4924 if (!aContextNode) {
4925 aRv.Throw(NS_ERROR_INVALID_ARG);
4926 return nullptr;
4927 }
4928
4929 // If we don't have a document here, we can't get the right security context
4930 // for compiling event handlers... so just bail out.
4931 RefPtr<Document> document = aContextNode->OwnerDoc();
4932 bool isHTML = document->IsHTMLDocument();
4933
4934 if (isHTML) {
4935 RefPtr<DocumentFragment> frag = new (document->NodeInfoManager())
4936 DocumentFragment(document->NodeInfoManager());
4937
4938 Element* element = aContextNode->GetAsElementOrParentElement();
4939 if (element && !element->IsHTMLElement(nsGkAtoms::html)) {
4940 aRv = ParseFragmentHTML(
4941 aFragment, frag, element->NodeInfo()->NameAtom(),
4942 element->GetNameSpaceID(),
4943 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
4944 aPreventScriptExecution);
4945 } else {
4946 aRv = ParseFragmentHTML(
4947 aFragment, frag, nsGkAtoms::body, kNameSpaceID_XHTML,
4948 (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
4949 aPreventScriptExecution);
4950 }
4951
4952 return frag.forget();
4953 }
4954
4955 AutoTArray<nsString, 32> tagStack;
4956 nsAutoString uriStr, nameStr;
4957 for (Element* element : aContextNode->InclusiveAncestorsOfType<Element>()) {
4958 nsString& tagName = *tagStack.AppendElement();
4959 // It mostly doesn't actually matter what tag name we use here: XML doesn't
4960 // have parsing that depends on the open tag stack, apart from namespace
4961 // declarations. So this whole tagStack bit is just there to get the right
4962 // namespace declarations to the XML parser. That said, the parser _is_
4963 // going to create elements with the tag names we provide here, so we need
4964 // to make sure they are not names that can trigger custom element
4965 // constructors. Just make up a name that is never going to be a valid
4966 // custom element name.
4967 //
4968 // The principled way to do this would probably be to add a new FromParser
4969 // value and make sure we use it when creating the context elements, then
4970 // make sure we teach all FromParser consumers (and in particular the custom
4971 // element code) about it as needed. But right now the XML parser never
4972 // actually uses FromParser values other than NOT_FROM_PARSER, and changing
4973 // that is pretty complicated.
4974 tagName.AssignLiteral("notacustomelement");
4975
4976 // see if we need to add xmlns declarations
4977 uint32_t count = element->GetAttrCount();
4978 bool setDefaultNamespace = false;
4979 if (count > 0) {
4980 uint32_t index;
4981
4982 for (index = 0; index < count; index++) {
4983 const BorrowedAttrInfo info = element->GetAttrInfoAt(index);
4984 const nsAttrName* name = info.mName;
4985 if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
4986 info.mValue->ToString(uriStr);
4987
4988 // really want something like nsXMLContentSerializer::SerializeAttr
4989 tagName.AppendLiteral(" xmlns"); // space important
4990 if (name->GetPrefix()) {
4991 tagName.Append(char16_t(':'));
4992 name->LocalName()->ToString(nameStr);
4993 tagName.Append(nameStr);
4994 } else {
4995 setDefaultNamespace = true;
4996 }
4997 tagName.AppendLiteral(R"(=")");
4998 tagName.Append(uriStr);
4999 tagName.Append('"');
5000 }
5001 }
5002 }
5003
5004 if (!setDefaultNamespace) {
5005 mozilla::dom::NodeInfo* info = element->NodeInfo();
5006 if (!info->GetPrefixAtom() && info->NamespaceID() != kNameSpaceID_None) {
5007 // We have no namespace prefix, but have a namespace ID. Push
5008 // default namespace attr in, so that our kids will be in our
5009 // namespace.
5010 info->GetNamespaceURI(uriStr);
5011 tagName.AppendLiteral(R"( xmlns=")");
5012 tagName.Append(uriStr);
5013 tagName.Append('"');
5014 }
5015 }
5016 }
5017
5018 RefPtr<DocumentFragment> frag;
5019 aRv = ParseFragmentXML(aFragment, document, tagStack, aPreventScriptExecution,
5020 -1, getter_AddRefs(frag));
5021 return frag.forget();
5022 }
5023
5024 /* static */
5025 void nsContentUtils::DropFragmentParsers() {
5026 NS_IF_RELEASE(sHTMLFragmentParser);
5027 NS_IF_RELEASE(sXMLFragmentParser);
5028 NS_IF_RELEASE(sXMLFragmentSink);
5029 }
5030
5031 /* static */
5032 void nsContentUtils::XPCOMShutdown() { nsContentUtils::DropFragmentParsers(); }
5033
5034 /* Helper function to compuate Sanitization Flags for ParseFramentHTML/XML */
5035 uint32_t computeSanitizationFlags(nsIPrincipal* aPrincipal, int32_t aFlags) {
5036 uint32_t sanitizationFlags = 0;
5037 if (aPrincipal->IsSystemPrincipal()) {
5038 if (aFlags < 0) {
5039 // if this is a chrome-privileged document and no explicit flags
5040 // were passed, then use this sanitization flags.
5041 sanitizationFlags = nsIParserUtils::SanitizerAllowStyle |
5042 nsIParserUtils::SanitizerAllowComments |
5043 nsIParserUtils::SanitizerDropForms |
5044 nsIParserUtils::SanitizerLogRemovals;
5045 } else {
5046 // if the caller explicitly passes flags, then we use those
5047 // flags but additionally drop forms.
5048 sanitizationFlags = aFlags | nsIParserUtils::SanitizerDropForms;
5049 }
5050 } else if (aFlags >= 0) {
5051 // aFlags by default is -1 and is only ever non equal to -1 if the
5052 // caller of ParseFragmentHTML/ParseFragmentXML is
5053 // ParserUtils::ParseFragment(). Only in that case we should use
5054 // the sanitization flags passed within aFlags.
5055 sanitizationFlags = aFlags;
5056 }
5057 return sanitizationFlags;
5058 }
5059
5060 /* static */
5061 bool AllowsUnsanitizedContentForAboutNewTab(nsIPrincipal* aPrincipal) {
5062 if (StaticPrefs::dom_about_newtab_sanitization_enabled() ||
5063 !aPrincipal->SchemeIs("about")) {
5064 return false;
5065 }
5066 uint32_t aboutModuleFlags = 0;
5067 aPrincipal->GetAboutModuleFlags(&aboutModuleFlags);
5068 return aboutModuleFlags & nsIAboutModule::ALLOW_UNSANITIZED_CONTENT;
5069 }
5070
5071 /* static */
5072 nsresult nsContentUtils::ParseFragmentHTML(
5073 const nsAString& aSourceBuffer, nsIContent* aTargetNode,
5074 nsAtom* aContextLocalName, int32_t aContextNamespace, bool aQuirks,
5075 bool aPreventScriptExecution, int32_t aFlags) {
5076 AutoTimelineMarker m(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");
5077
5078 if (nsContentUtils::sFragmentParsingActive) {
5079 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5080 return NS_ERROR_DOM_INVALID_STATE_ERR;
5081 }
5082 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5083 nsContentUtils::sFragmentParsingActive = true;
5084 if (!sHTMLFragmentParser) {
5085 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5086 // Now sHTMLFragmentParser owns the object
5087 }
5088
5089 nsCOMPtr<nsIPrincipal> nodePrincipal = aTargetNode->NodePrincipal();
5090
5091 #ifdef DEBUG
5092 // aFlags should always be -1 unless the caller of ParseFragmentHTML
5093 // is ParserUtils::ParseFragment() which is the only caller that intends
5094 // sanitization. For all other callers we need to ensure to call
5095 // AuditParsingOfHTMLXMLFragments.
5096 if (aFlags < 0) {
5097 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5098 aSourceBuffer);
5099 }
5100 #endif
5101
5102 nsIContent* target = aTargetNode;
5103
5104 RefPtr<Document> doc = aTargetNode->OwnerDoc();
5105 RefPtr<DocumentFragment> fragment;
5106 // We sanitize if the fragment occurs in a system privileged
5107 // context, an about: page, or if there are explicit sanitization flags.
5108 // Please note that about:blank and about:srcdoc inherit the security
5109 // context from the embedding context and hence are not loaded using
5110 // an about: scheme principal.
5111 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5112 nodePrincipal->SchemeIs("about") || aFlags >= 0;
5113 if (shouldSanitize &&
5114 !AllowsUnsanitizedContentForAboutNewTab(nodePrincipal)) {
5115 if (!doc->IsLoadedAsData()) {
5116 doc = nsContentUtils::CreateInertHTMLDocument(doc);
5117 if (!doc) {
5118 return NS_ERROR_FAILURE;
5119 }
5120 }
5121 fragment =
5122 new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager());
5123 target = fragment;
5124 }
5125
5126 nsresult rv = sHTMLFragmentParser->ParseFragment(
5127 aSourceBuffer, target, aContextLocalName, aContextNamespace, aQuirks,
5128 aPreventScriptExecution);
5129 NS_ENSURE_SUCCESS(rv, rv);
5130
5131 if (fragment) {
5132 uint32_t sanitizationFlags =
5133 computeSanitizationFlags(nodePrincipal, aFlags);
5134 // Don't fire mutation events for nodes removed by the sanitizer.
5135 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5136 nsTreeSanitizer sanitizer(sanitizationFlags);
5137 sanitizer.Sanitize(fragment);
5138
5139 ErrorResult error;
5140 aTargetNode->AppendChild(*fragment, error);
5141 rv = error.StealNSResult();
5142 }
5143
5144 return rv;
5145 }
5146
5147 /* static */
5148 nsresult nsContentUtils::ParseDocumentHTML(
5149 const nsAString& aSourceBuffer, Document* aTargetDocument,
5150 bool aScriptingEnabledForNoscriptParsing) {
5151 AutoTimelineMarker m(aTargetDocument->GetDocShell(), "Parse HTML");
5152
5153 if (nsContentUtils::sFragmentParsingActive) {
5154 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5155 return NS_ERROR_DOM_INVALID_STATE_ERR;
5156 }
5157 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5158 nsContentUtils::sFragmentParsingActive = true;
5159 if (!sHTMLFragmentParser) {
5160 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
5161 // Now sHTMLFragmentParser owns the object
5162 }
5163 nsresult rv = sHTMLFragmentParser->ParseDocument(
5164 aSourceBuffer, aTargetDocument, aScriptingEnabledForNoscriptParsing);
5165 return rv;
5166 }
5167
5168 /* static */
5169 nsresult nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
5170 Document* aDocument,
5171 nsTArray<nsString>& aTagStack,
5172 bool aPreventScriptExecution,
5173 int32_t aFlags,
5174 DocumentFragment** aReturn) {
5175 AutoTimelineMarker m(aDocument->GetDocShell(), "Parse XML");
5176
5177 if (nsContentUtils::sFragmentParsingActive) {
5178 MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
5179 return NS_ERROR_DOM_INVALID_STATE_ERR;
5180 }
5181 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
5182 nsContentUtils::sFragmentParsingActive = true;
5183 if (!sXMLFragmentParser) {
5184 nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID);
5185 parser.forget(&sXMLFragmentParser);
5186 // sXMLFragmentParser now owns the parser
5187 }
5188 if (!sXMLFragmentSink) {
5189 NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
5190 // sXMLFragmentSink now owns the sink
5191 }
5192 nsCOMPtr<nsIContentSink> contentsink = do_QueryInterface(sXMLFragmentSink);
5193 MOZ_ASSERT(contentsink, "Sink doesn't QI to nsIContentSink!");
5194 sXMLFragmentParser->SetContentSink(contentsink);
5195
5196 RefPtr<Document> doc;
5197 nsCOMPtr<nsIPrincipal> nodePrincipal = aDocument->NodePrincipal();
5198
5199 #ifdef DEBUG
5200 // aFlags should always be -1 unless the caller of ParseFragmentXML
5201 // is ParserUtils::ParseFragment() which is the only caller that intends
5202 // sanitization. For all other callers we need to ensure to call
5203 // AuditParsingOfHTMLXMLFragments.
5204 if (aFlags < 0) {
5205 DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
5206 aSourceBuffer);
5207 }
5208 #endif
5209
5210 // We sanitize if the fragment occurs in a system privileged
5211 // context, an about: page, or if there are explicit sanitization flags.
5212 // Please note that about:blank and about:srcdoc inherit the security
5213 // context from the embedding context and hence are not loaded using
5214 // an about: scheme principal.
5215 bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
5216 nodePrincipal->SchemeIs("about") || aFlags >= 0;
5217 if (shouldSanitize && !aDocument->IsLoadedAsData()) {
5218 doc = nsContentUtils::CreateInertXMLDocument(aDocument);
5219 } else {
5220 doc = aDocument;
5221 }
5222
5223 sXMLFragmentSink->SetTargetDocument(doc);
5224 sXMLFragmentSink->SetPreventScriptExecution(aPreventScriptExecution);
5225
5226 nsresult rv = sXMLFragmentParser->ParseFragment(aSourceBuffer, aTagStack);
5227 if (NS_FAILED(rv)) {
5228 // Drop the fragment parser and sink that might be in an inconsistent state
5229 NS_IF_RELEASE(sXMLFragmentParser);
5230 NS_IF_RELEASE(sXMLFragmentSink);
5231 return rv;
5232 }
5233
5234 rv = sXMLFragmentSink->FinishFragmentParsing(aReturn);
5235
5236 sXMLFragmentParser->Reset();
5237 NS_ENSURE_SUCCESS(rv, rv);
5238
5239 if (shouldSanitize) {
5240 uint32_t sanitizationFlags =
5241 computeSanitizationFlags(nodePrincipal, aFlags);
5242 // Don't fire mutation events for nodes removed by the sanitizer.
5243 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
5244 nsTreeSanitizer sanitizer(sanitizationFlags);
5245 sanitizer.Sanitize(*aReturn);
5246 }
5247
5248 return rv;
5249 }
5250
5251 /* static */
5252 nsresult nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
5253 nsAString& aResultBuffer,
5254 uint32_t aFlags,
5255 uint32_t aWrapCol) {
5256 RefPtr<Document> document = nsContentUtils::CreateInertHTMLDocument(nullptr);
5257 if (!document) {
5258 return NS_ERROR_FAILURE;
5259 }
5260
5261 nsresult rv = nsContentUtils::ParseDocumentHTML(
5262 aSourceBuffer, document,
5263 !(aFlags & nsIDocumentEncoder::OutputNoScriptContent));
5264 NS_ENSURE_SUCCESS(rv, rv);
5265
5266 nsCOMPtr<nsIDocumentEncoder> encoder = do_createDocumentEncoder("text/plain");
5267
5268 rv = encoder->Init(document, u"text/plain"_ns, aFlags);
5269 NS_ENSURE_SUCCESS(rv, rv);
5270
5271 encoder->SetWrapColumn(aWrapCol);
5272
5273 return encoder->EncodeToString(aResultBuffer);
5274 }
5275
5276 static already_AddRefed<Document> CreateInertDocument(const Document* aTemplate,
5277 DocumentFlavor aFlavor) {
5278 if (aTemplate) {
5279 bool hasHad = true;
5280 nsIScriptGlobalObject* sgo = aTemplate->GetScriptHandlingObject(hasHad);
5281 NS_ENSURE_TRUE(sgo || !hasHad, nullptr);
5282
5283 nsCOMPtr<Document> doc;
5284 nsresult rv = NS_NewDOMDocument(
5285 getter_AddRefs(doc), u""_ns, u""_ns, nullptr,
5286 aTemplate->GetDocumentURI(), aTemplate->GetDocBaseURI(),
5287 aTemplate->NodePrincipal(), true, sgo, aFlavor);
5288 if (NS_FAILED(rv)) {
5289 return nullptr;
5290 }
5291 return doc.forget();
5292 }
5293 nsCOMPtr<nsIURI> uri;
5294 NS_NewURI(getter_AddRefs(uri), "about:blank"_ns);
5295 if (!uri) {
5296 return nullptr;
5297 }
5298
5299 RefPtr<NullPrincipal> nullPrincipal =
5300 NullPrincipal::CreateWithoutOriginAttributes();
5301 if (!nullPrincipal) {
5302 return nullptr;
5303 }
5304
5305 nsCOMPtr<Document> doc;
5306 nsresult rv =
5307 NS_NewDOMDocument(getter_AddRefs(doc), u""_ns, u""_ns, nullptr, uri, uri,
5308 nullPrincipal, true, nullptr, aFlavor);
5309 if (NS_FAILED(rv)) {
5310 return nullptr;
5311 }
5312 return doc.forget();
5313 }
5314
5315 /* static */
5316 already_AddRefed<Document> nsContentUtils::CreateInertXMLDocument(
5317 const Document* aTemplate) {
5318 return CreateInertDocument(aTemplate, DocumentFlavorXML);
5319 }
5320
5321 /* static */
5322 already_AddRefed<Document> nsContentUtils::CreateInertHTMLDocument(
5323 const Document* aTemplate) {
5324 return CreateInertDocument(aTemplate, DocumentFlavorHTML);
5325 }
5326
5327 /* static */
5328 nsresult nsContentUtils::SetNodeTextContent(nsIContent* aContent,
5329 const nsAString& aValue,
5330 bool aTryReuse) {
5331 // Fire DOMNodeRemoved mutation events before we do anything else.
5332 nsCOMPtr<nsIContent> owningContent;
5333
5334 // Batch possible DOMSubtreeModified events.
5335 mozAutoSubtreeModified subtree(nullptr, nullptr);
5336
5337 // Scope firing mutation events so that we don't carry any state that
5338 // might be stale
5339 {
5340 // We're relying on mozAutoSubtreeModified to keep a strong reference if
5341 // needed.
5342 Document* doc = aContent->OwnerDoc();
5343
5344 // Optimize the common case of there being no observers
5345 if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
5346 subtree.UpdateTarget(doc, nullptr);
5347 owningContent = aContent;
5348 nsCOMPtr<nsINode> child;
5349 bool skipFirst = aTryReuse;
5350 for (child = aContent->GetFirstChild();
5351 child && child->GetParentNode() == aContent;
5352 child = child->GetNextSibling()) {
5353 if (skipFirst && child->IsText()) {
5354 skipFirst = false;
5355 continue;
5356 }
5357 nsContentUtils::MaybeFireNodeRemoved(child, aContent);
5358 }
5359 }
5360 }
5361
5362 // Might as well stick a batch around this since we're performing several
5363 // mutations.
5364 mozAutoDocUpdate updateBatch(aContent->GetComposedDoc(), true);
5365 nsAutoMutationBatch mb;
5366
5367 if (aTryReuse && !aValue.IsEmpty()) {
5368 // Let's remove nodes until we find a eTEXT.
5369 while (aContent->HasChildren()) {
5370 nsIContent* child = aContent->GetFirstChild();
5371 if (child->IsText()) {
5372 break;
5373 }
5374 aContent->RemoveChildNode(child, true);
5375 }
5376
5377 // If we have a node, it must be a eTEXT and we reuse it.
5378 if (aContent->HasChildren()) {
5379 nsIContent* child = aContent->GetFirstChild();
5380 nsresult rv = child->AsText()->SetText(aValue, true);
5381 NS_ENSURE_SUCCESS(rv, rv);
5382
5383 // All the following nodes, if they exist, must be deleted.
5384 while (nsIContent* nextChild = child->GetNextSibling()) {
5385 aContent->RemoveChildNode(nextChild, true);
5386 }
5387 }
5388
5389 if (aContent->HasChildren()) {
5390 return NS_OK;
5391 }
5392 } else {
5393 mb.Init(aContent, true, false);
5394 while (aContent->HasChildren()) {
5395 aContent->RemoveChildNode(aContent->GetFirstChild(), true);
5396 }
5397 }
5398 mb.RemovalDone();
5399
5400 if (aValue.IsEmpty()) {
5401 return NS_OK;
5402 }
5403
5404 RefPtr<nsTextNode> textContent = new (aContent->NodeInfo()->NodeInfoManager())
5405 nsTextNode(aContent->NodeInfo()->NodeInfoManager());
5406
5407 textContent->SetText(aValue, true);
5408
5409 ErrorResult rv;
5410 aContent->AppendChildTo(textContent, true, rv);
5411 mb.NodesAdded();
5412 return rv.StealNSResult();
5413 }
5414
5415 static bool AppendNodeTextContentsRecurse(nsINode* aNode, nsAString& aResult,
5416 const fallible_t& aFallible) {
5417 for (nsIContent* child = aNode->GetFirstChild(); child;
5418 child = child->GetNextSibling()) {
5419 if (child->IsElement()) {
5420 bool ok = AppendNodeTextContentsRecurse(child, aResult, aFallible);
5421 if (!ok) {
5422 return false;
5423 }
5424 } else if (Text* text = child->GetAsText()) {
5425 bool ok = text->AppendTextTo(aResult, aFallible);
5426 if (!ok) {
5427 return false;
5428 }
5429 }
5430 }
5431
5432 return true;
5433 }
5434
5435 /* static */
5436 bool nsContentUtils::AppendNodeTextContent(nsINode* aNode, bool aDeep,
5437 nsAString& aResult,
5438 const fallible_t& aFallible) {
5439 if (Text* text = aNode->GetAsText()) {
5440 return text->AppendTextTo(aResult, aFallible);
5441 }
5442 if (aDeep) {
5443 return AppendNodeTextContentsRecurse(aNode, aResult, aFallible);
5444 }
5445
5446 for (nsIContent* child = aNode->GetFirstChild(); child;
5447 child = child->GetNextSibling()) {
5448 if (Text* text = child->GetAsText()) {
5449 bool ok = text->AppendTextTo(aResult, fallible);
5450 if (!ok) {
5451 return false;
5452 }
5453 }
5454 }
5455 return true;
5456 }
5457
5458 bool nsContentUtils::HasNonEmptyTextContent(
5459 nsINode* aNode, TextContentDiscoverMode aDiscoverMode) {
5460 for (nsIContent* child = aNode->GetFirstChild(); child;
5461 child = child->GetNextSibling()) {
5462 if (child->IsText() && child->TextLength() > 0) {
5463 return true;
5464 }
5465
5466 if (aDiscoverMode == eRecurseIntoChildren &&
5467 HasNonEmptyTextContent(child, aDiscoverMode)) {
5468 return true;
5469 }
5470 }
5471
5472 return false;
5473 }
5474
5475 /* static */
5476 bool nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
5477 const nsIContent* aContent) {
5478 MOZ_ASSERT(aNode, "Must have a node to work with");
5479 MOZ_ASSERT(aContent, "Must have a content to work with");
5480
5481 if (aNode->IsInNativeAnonymousSubtree() !=
5482 aContent->IsInNativeAnonymousSubtree()) {
5483 return false;
5484 }
5485
5486 if (aNode->IsInNativeAnonymousSubtree()) {
5487 return aContent->GetClosestNativeAnonymousSubtreeRoot() ==
5488 aNode->GetClosestNativeAnonymousSubtreeRoot();
5489 }
5490
5491 // FIXME: This doesn't deal with disconnected nodes whatsoever, but it didn't
5492 // use to either. Maybe that's fine.
5493 return aNode->GetContainingShadow() == aContent->GetContainingShadow();
5494 }
5495
5496 /* static */
5497 bool nsContentUtils::IsInInteractiveHTMLContent(const Element* aElement,
5498 const Element* aStop) {
5499 const Element* element = aElement;
5500 while (element && element != aStop) {
5501 if (element->IsInteractiveHTMLContent()) {
5502 return true;
5503 }
5504 element = element->GetFlattenedTreeParentElement();
5505 }
5506 return false;
5507 }
5508
5509 /* static */
5510 void nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling) {
5511 IMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
5512 }
5513
5514 /* static */
5515 bool nsContentUtils::SchemeIs(nsIURI* aURI, const char* aScheme) {
5516 nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
5517 NS_ENSURE_TRUE(baseURI, false);
5518 return baseURI->SchemeIs(aScheme);
5519 }
5520
5521 bool nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal) {
5522 nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
5523 return !!ep;
5524 }
5525
5526 bool nsContentUtils::IsSystemOrExpandedPrincipal(nsIPrincipal* aPrincipal) {
5527 return (aPrincipal && aPrincipal->IsSystemPrincipal()) ||
5528 IsExpandedPrincipal(aPrincipal);
5529 }
5530
5531 nsIPrincipal* nsContentUtils::GetSystemPrincipal() {
5532 MOZ_ASSERT(IsInitialized());
5533 return sSystemPrincipal;
5534 }
5535
5536 bool nsContentUtils::CombineResourcePrincipals(
5537 nsCOMPtr<nsIPrincipal>* aResourcePrincipal, nsIPrincipal* aExtraPrincipal) {
5538 if (!aExtraPrincipal) {
5539 return false;
5540 }
5541 if (!*aResourcePrincipal) {
5542 *aResourcePrincipal = aExtraPrincipal;
5543 return true;
5544 }
5545 if (*aResourcePrincipal == aExtraPrincipal) {
5546 return false;
5547 }
5548 bool subsumes;
5549 if (NS_SUCCEEDED(
5550 (*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
5551 subsumes) {
5552 return false;
5553 }
5554 *aResourcePrincipal = sSystemPrincipal;
5555 return true;
5556 }
5557
5558 /* static */
5559 void nsContentUtils::TriggerLink(nsIContent* aContent, nsIURI* aLinkURI,
5560 const nsString& aTargetSpec, bool aClick,
5561 bool aIsTrusted) {
5562 MOZ_ASSERT(aLinkURI, "No link URI");
5563
5564 if (aContent->IsEditable() || !aContent->OwnerDoc()->LinkHandlingEnabled()) {
5565 return;
5566 }
5567
5568 nsCOMPtr<nsIDocShell> docShell = aContent->OwnerDoc()->GetDocShell();
5569 if (!docShell) {
5570 return;
5571 }
5572
5573 if (!aClick) {
5574 nsDocShell::Cast(docShell)->OnOverLink(aContent, aLinkURI, aTargetSpec);
5575 return;
5576 }
5577
5578 // Check that this page is allowed to load this URI.
5579 nsresult proceed = NS_OK;
5580
5581 if (sSecurityManager) {
5582 uint32_t flag = static_cast<uint32_t>(nsIScriptSecurityManager::STANDARD);
5583 proceed = sSecurityManager->CheckLoadURIWithPrincipal(
5584 aContent->NodePrincipal(), aLinkURI, flag,
5585 aContent->OwnerDoc()->InnerWindowID());
5586 }
5587
5588 // Only pass off the click event if the script security manager says it's ok.
5589 // We need to rest aTargetSpec for forced downloads.
5590 if (NS_SUCCEEDED(proceed)) {
5591 // A link/area element with a download attribute is allowed to set
5592 // a pseudo Content-Disposition header.
5593 // For security reasons we only allow websites to declare same-origin
5594 // resources as downloadable. If this check fails we will just do the normal
5595 // thing (i.e. navigate to the resource).
5596 nsAutoString fileName;
5597 if ((!aContent->IsHTMLElement(nsGkAtoms::a) &&
5598 !aContent->IsHTMLElement(nsGkAtoms::area) &&
5599 !aContent->IsSVGElement(nsGkAtoms::a)) ||
5600 !aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::download,
5601 fileName) ||
5602 NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, true))) {
5603 fileName.SetIsVoid(true); // No actionable download attribute was found.
5604 }
5605
5606 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aContent->NodePrincipal();
5607 nsCOMPtr<nsIContentSecurityPolicy> csp = aContent->GetCsp();
5608
5609 // Sanitize fileNames containing null characters by replacing them with
5610 // underscores.
5611 if (!fileName.IsVoid()) {
5612 fileName.ReplaceChar(char16_t(0), '_');
5613 }
5614 nsDocShell::Cast(docShell)->OnLinkClick(
5615 aContent, aLinkURI, fileName.IsVoid() ? aTargetSpec : u""_ns, fileName,
5616 nullptr, nullptr, UserActivation::IsHandlingUserInput(), aIsTrusted,
5617 triggeringPrincipal, csp);
5618 }
5619 }
5620
5621 /* static */
5622 void nsContentUtils::GetLinkLocation(Element* aElement,
5623 nsString& aLocationString) {
5624 nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
5625 if (hrefURI) {
5626 nsAutoCString specUTF8;
5627 nsresult rv = hrefURI->GetSpec(specUTF8);
5628 if (NS_SUCCEEDED(rv)) CopyUTF8toUTF16(specUTF8, aLocationString);
5629 }
5630 }
5631
5632 /* static */
5633 nsIWidget* nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget) {
5634 if (!aWidget) return nullptr;
5635
5636 return aWidget->GetTopLevelWidget();
5637 }
5638
5639 /* static */
5640 const nsDependentString nsContentUtils::GetLocalizedEllipsis() {
5641 static char16_t sBuf[4] = {0, 0, 0, 0};
5642 if (!sBuf[0]) {
5643 if (!SpoofLocaleEnglish()) {
5644 nsAutoString tmp;
5645 Preferences::GetLocalizedString("intl.ellipsis", tmp);
5646 uint32_t len =
5647 std::min(uint32_t(tmp.Length()), uint32_t(ArrayLength(sBuf) - 1));
5648 CopyUnicodeTo(tmp, 0, sBuf, len);
5649 }
5650 if (!sBuf[0]) sBuf[0] = char16_t(0x2026);
5651 }
5652 return nsDependentString(sBuf);
5653 }
5654
5655 /* static */
5656 void nsContentUtils::AddScriptBlocker() {
5657 MOZ_ASSERT(NS_IsMainThread());
5658 if (!sScriptBlockerCount) {
5659 MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
5660 "Should not already have a count");
5661 sRunnersCountAtFirstBlocker =
5662 sBlockedScriptRunners ? sBlockedScriptRunners->Length() : 0;
5663 }
5664 ++sScriptBlockerCount;
5665 }
5666
5667 #ifdef DEBUG
5668 static bool sRemovingScriptBlockers = false;
5669 #endif
5670
5671 /* static */
5672 void nsContentUtils::RemoveScriptBlocker() {
5673 MOZ_ASSERT(NS_IsMainThread());
5674 MOZ_ASSERT(!sRemovingScriptBlockers);
5675 NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
5676 --sScriptBlockerCount;
5677 if (sScriptBlockerCount) {
5678 return;
5679 }
5680
5681 if (!sBlockedScriptRunners) {
5682 return;
5683 }
5684
5685 uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
5686 uint32_t lastBlocker = sBlockedScriptRunners->Length();
5687 uint32_t originalFirstBlocker = firstBlocker;
5688 uint32_t blockersCount = lastBlocker - firstBlocker;
5689 sRunnersCountAtFirstBlocker = 0;
5690 NS_ASSERTION(firstBlocker <= lastBlocker, "bad sRunnersCountAtFirstBlocker");
5691
5692 while (firstBlocker < lastBlocker) {
5693 nsCOMPtr<nsIRunnable> runnable;
5694 runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
5695 ++firstBlocker;
5696
5697 // Calling the runnable can reenter us
5698 {
5699 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
5700 runnable->Run();
5701 }
5702 // So can dropping the reference to the runnable
5703 runnable = nullptr;
5704
5705 NS_ASSERTION(sRunnersCountAtFirstBlocker == 0, "Bad count");
5706 NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
5707 }
5708 #ifdef DEBUG
5709 AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
5710 sRemovingScriptBlockers = true;
5711 #endif
5712 sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
5713 }
5714
5715 /* static */
5716 already_AddRefed<nsPIDOMWindowOuter>
5717 nsContentUtils::GetMostRecentNonPBWindow() {
5718 nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
5719
5720 nsCOMPtr<mozIDOMWindowProxy> window;
5721 wm->GetMostRecentNonPBWindow(u"navigator:browser", getter_AddRefs(window));
5722 nsCOMPtr<nsPIDOMWindowOuter> pwindow;
5723 pwindow = do_QueryInterface(window);
5724
5725 return pwindow.forget();
5726 }
5727
5728 /* static */
5729 void nsContentUtils::WarnScriptWasIgnored(Document* aDocument) {
5730 nsAutoString msg;
5731 bool privateBrowsing = false;
5732 bool chromeContext = false;
5733
5734 if (aDocument) {
5735 nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
5736 if (uri) {
5737 msg.Append(NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()));
5738 msg.AppendLiteral(" : ");
5739 }
5740 privateBrowsing =
5741 !!aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
5742 chromeContext = aDocument->NodePrincipal()->IsSystemPrincipal();
5743 }
5744
5745 msg.AppendLiteral(
5746 "Unable to run script because scripts are blocked internally.");
5747 LogSimpleConsoleError(msg, "DOM", privateBrowsing, chromeContext);
5748 }
5749
5750 /* static */
5751 void nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable) {
5752 nsCOMPtr<nsIRunnable> runnable = aRunnable;
5753 if (!runnable) {
5754 return;
5755 }
5756
5757 if (sScriptBlockerCount) {
5758 sBlockedScriptRunners->AppendElement(runnable.forget());
5759 return;
5760 }
5761
5762 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
5763 runnable->Run();
5764 }
5765
5766 /* static */
5767 void nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) {
5768 nsCOMPtr<nsIRunnable> runnable = aRunnable;
5769 AddScriptRunner(runnable.forget());
5770 }
5771
5772 /* static */ bool nsContentUtils::IsSafeToRunScript() {
5773 MOZ_ASSERT(NS_IsMainThread(),
5774 "This static variable only makes sense on the main thread!");
5775 return sScriptBlockerCount == 0;
5776 }
5777
5778 /* static */
5779 void nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable) {
5780 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5781 CycleCollectedJSContext::Get()->RunInStableState(std::move(aRunnable));
5782 }
5783
5784 /* static */
5785 void nsContentUtils::AddPendingIDBTransaction(
5786 already_AddRefed<nsIRunnable> aTransaction) {
5787 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5788 CycleCollectedJSContext::Get()->AddPendingIDBTransaction(
5789 std::move(aTransaction));
5790 }
5791
5792 /* static */
5793 bool nsContentUtils::IsInStableOrMetaStableState() {
5794 MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
5795 return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
5796 }
5797
5798 /* static */
5799 void nsContentUtils::HidePopupsInDocument(Document* aDocument) {
5800 #ifdef MOZ_XUL
5801 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
5802 if (pm && aDocument) {
5803 nsCOMPtr<nsIDocShellTreeItem> docShellToHide = aDocument->GetDocShell();
5804 if (docShellToHide) pm->HidePopupsInDocShell(docShellToHide);
5805 }
5806 #endif
5807 }
5808
5809 /* static */
5810 already_AddRefed<nsIDragSession> nsContentUtils::GetDragSession() {
5811 nsCOMPtr<nsIDragSession> dragSession;
5812 nsCOMPtr<nsIDragService> dragService =
5813 do_GetService("@mozilla.org/widget/dragservice;1");
5814 if (dragService) dragService->GetCurrentSession(getter_AddRefs(dragSession));
5815 return dragSession.forget();
5816 }
5817
5818 /* static */
5819 nsresult nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) {
5820 if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) {
5821 return NS_OK;
5822 }
5823
5824 // For dragstart events, the data transfer object is
5825 // created before the event fires, so it should already be set. For other
5826 // drag events, get the object from the drag session.
5827 NS_ASSERTION(aDragEvent->mMessage != eDragStart,
5828 "draggesture event created without a dataTransfer");
5829
5830 nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
5831 NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
5832
5833 RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
5834 if (!initialDataTransfer) {
5835 // A dataTransfer won't exist when a drag was started by some other
5836 // means, for instance calling the drag service directly, or a drag
5837 // from another application. In either case, a new dataTransfer should
5838 // be created that reflects the data.
5839 initialDataTransfer =
5840 new DataTransfer(aDragEvent->mTarget, aDragEvent->mMessage, true, -1);
5841
5842 // now set it in the drag session so we don't need to create it again
5843 dragSession->SetDataTransfer(initialDataTransfer);
5844 }
5845
5846 bool isCrossDomainSubFrameDrop = false;
5847 if (aDragEvent->mMessage == eDrop) {
5848 isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
5849 }
5850
5851 // each event should use a clone of the original dataTransfer.
5852 initialDataTransfer->Clone(
5853 aDragEvent->mTarget, aDragEvent->mMessage, aDragEvent->mUserCancelled,
5854 isCrossDomainSubFrameDrop, getter_AddRefs(aDragEvent->mDataTransfer));
5855 if (NS_WARN_IF(!aDragEvent->mDataTransfer)) {
5856 return NS_ERROR_OUT_OF_MEMORY;
5857 }
5858
5859 // for the dragenter and dragover events, initialize the drop effect
5860 // from the drop action, which platform specific widget code sets before
5861 // the event is fired based on the keyboard state.
5862 if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
5863 uint32_t action;
5864 dragSession->GetDragAction(&action);
5865 uint32_t effectAllowed = aDragEvent->mDataTransfer->EffectAllowedInt();
5866 aDragEvent->mDataTransfer->SetDropEffectInt(
5867 FilterDropEffect(action, effectAllowed));
5868 } else if (aDragEvent->mMessage == eDrop ||
5869 aDragEvent->mMessage == eDragEnd) {
5870 // For the drop and dragend events, set the drop effect based on the
5871 // last value that the dropEffect had. This will have been set in
5872 // EventStateManager::PostHandleEvent for the last dragenter or
5873 // dragover event.
5874 aDragEvent->mDataTransfer->SetDropEffectInt(
5875 initialDataTransfer->DropEffectInt());
5876 }
5877
5878 return NS_OK;
5879 }
5880
5881 /* static */
5882 uint32_t nsContentUtils::FilterDropEffect(uint32_t aAction,
5883 uint32_t aEffectAllowed) {
5884 // It is possible for the drag action to include more than one action, but
5885 // the widget code which sets the action from the keyboard state should only
5886 // be including one. If multiple actions were set, we just consider them in
5887 // the following order:
5888 // copy, link, move
5889 if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
5890 aAction = nsIDragService::DRAGDROP_ACTION_COPY;
5891 else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
5892 aAction = nsIDragService::DRAGDROP_ACTION_LINK;
5893 else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
5894 aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
5895
5896 // Filter the action based on the effectAllowed. If the effectAllowed
5897 // doesn't include the action, then that action cannot be done, so adjust
5898 // the action to something that is allowed. For a copy, adjust to move or
5899 // link. For a move, adjust to copy or link. For a link, adjust to move or
5900 // link. Otherwise, use none.
5901 if (aAction & aEffectAllowed ||
5902 aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
5903 return aAction;
5904 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
5905 return nsIDragService::DRAGDROP_ACTION_MOVE;
5906 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
5907 return nsIDragService::DRAGDROP_ACTION_COPY;
5908 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
5909 return nsIDragService::DRAGDROP_ACTION_LINK;
5910 return nsIDragService::DRAGDROP_ACTION_NONE;
5911 }
5912
5913 /* static */
5914 bool nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession,
5915 WidgetDragEvent* aDropEvent) {
5916 nsCOMPtr<nsIContent> target = do_QueryInterface(aDropEvent->mOriginalTarget);
5917 if (!target) {
5918 return true;
5919 }
5920
5921 // Always allow dropping onto chrome shells.
5922 BrowsingContext* targetBC = target->OwnerDoc()->GetBrowsingContext();
5923 if (targetBC->IsChrome()) {
5924 return false;
5925 }
5926
5927 // If there is no source node, then this is a drag from another
5928 // application, which should be allowed.
5929 RefPtr<Document> doc(aDragSession->GetSourceDocument());
5930 if (doc && doc->GetBrowsingContext()) {
5931 // Get each successive parent of the source document and compare it to
5932 // the drop document. If they match, then this is a drag from a child frame.
5933 for (BrowsingContext* bc = doc->GetBrowsingContext()->GetParent(); bc;
5934 bc = bc->GetParent()) {
5935 if (bc == targetBC) {
5936 // The drag is from a descendant frame.
5937 return true;
5938 }
5939 }
5940 }
5941
5942 return false;
5943 }
5944
5945 /* static */
5946 bool nsContentUtils::URIIsLocalFile(nsIURI* aURI) {
5947 bool isFile;
5948 nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
5949
5950 // Important: we do NOT test the entire URI chain here!
5951 return util &&
5952 NS_SUCCEEDED(util->ProtocolHasFlags(
5953 aURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, &isFile)) &&
5954 isFile;
5955 }
5956
5957 /* static */
5958 JSContext* nsContentUtils::GetCurrentJSContext() {
5959 MOZ_ASSERT(IsInitialized());
5960 if (!IsJSAPIActive()) {
5961 return nullptr;
5962 }
5963 return danger::GetJSContext();
5964 }
5965
5966 template <typename StringType, typename CharType>
5967 void _ASCIIToLowerInSitu(StringType& aStr) {
5968 CharType* iter = aStr.BeginWriting();
5969 CharType* end = aStr.EndWriting();
5970 MOZ_ASSERT(iter && end);
5971
5972 while (iter != end) {
5973 CharType c = *iter;
5974 if (c >= 'A' && c <= 'Z') {
5975 *iter = c + ('a' - 'A');
5976 }
5977 ++iter;
5978 }
5979 }
5980
5981 /* static */
5982 void nsContentUtils::ASCIIToLower(nsAString& aStr) {
5983 return _ASCIIToLowerInSitu<nsAString, char16_t>(aStr);
5984 }
5985
5986 /* static */
5987 void nsContentUtils::ASCIIToLower(nsACString& aStr) {
5988 return _ASCIIToLowerInSitu<nsACString, char>(aStr);
5989 }
5990
5991 template <typename StringType, typename CharType>
5992 void _ASCIIToLowerCopy(const StringType& aSource, StringType& aDest) {
5993 uint32_t len = aSource.Length();
5994 aDest.SetLength(len);
5995 MOZ_ASSERT(aDest.Length() == len);
5996
5997 CharType* dest = aDest.BeginWriting();
5998 MOZ_ASSERT(dest);
5999
6000 const CharType* iter = aSource.BeginReading();
6001 const CharType* end = aSource.EndReading();
6002 while (iter != end) {
6003 CharType c = *iter;
6004 *dest = (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
6005 ++iter;
6006 ++dest;
6007 }
6008 }
6009
6010 /* static */
6011 void nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest) {
6012 return _ASCIIToLowerCopy<nsAString, char16_t>(aSource, aDest);
6013 }
6014
6015 /* static */
6016 void nsContentUtils::ASCIIToLower(const nsACString& aSource,
6017 nsACString& aDest) {
6018 return _ASCIIToLowerCopy<nsACString, char>(aSource, aDest);
6019 }
6020
6021 template <typename StringType, typename CharType>
6022 void _ASCIIToUpperInSitu(StringType& aStr) {
6023 CharType* iter = aStr.BeginWriting();
6024 CharType* end = aStr.EndWriting();
6025 MOZ_ASSERT(iter && end);
6026
6027 while (iter != end) {
6028 CharType c = *iter;
6029 if (c >= 'a' && c <= 'z') {
6030 *iter = c + ('A' - 'a');
6031 }
6032 ++iter;
6033 }
6034 }
6035
6036 /* static */
6037 void nsContentUtils::ASCIIToUpper(nsAString& aStr) {
6038 return _ASCIIToUpperInSitu<nsAString, char16_t>(aStr);
6039 }
6040
6041 /* static */
6042 void nsContentUtils::ASCIIToUpper(nsACString& aStr) {
6043 return _ASCIIToUpperInSitu<nsACString, char>(aStr);
6044 }
6045
6046 template <typename StringType, typename CharType>
6047 void _ASCIIToUpperCopy(const StringType& aSource, StringType& aDest) {
6048 uint32_t len = aSource.Length();
6049 aDest.SetLength(len);
6050 MOZ_ASSERT(aDest.Length() == len);
6051
6052 CharType* dest = aDest.BeginWriting();
6053 MOZ_ASSERT(dest);
6054
6055 const CharType* iter = aSource.BeginReading();
6056 const CharType* end = aSource.EndReading();
6057 while (iter != end) {
6058 CharType c = *iter;
6059 *dest = (c >= 'a' && c <= 'z') ? c + ('A' - 'a') : c;
6060 ++iter;
6061 ++dest;
6062 }
6063 }
6064
6065 /* static */
6066 void nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest) {
6067 return _ASCIIToUpperCopy<nsAString, char16_t>(aSource, aDest);
6068 }
6069
6070 /* static */
6071 void nsContentUtils::ASCIIToUpper(const nsACString& aSource,
6072 nsACString& aDest) {
6073 return _ASCIIToUpperCopy<nsACString, char>(aSource, aDest);
6074 }
6075
6076 /* static */
6077 bool nsContentUtils::EqualsIgnoreASCIICase(nsAtom* aAtom1, nsAtom* aAtom2) {
6078 if (aAtom1 == aAtom2) {
6079 return true;
6080 }
6081
6082 // If both are ascii lowercase already, we know that the slow comparison
6083 // below is going to return false.
6084 if (aAtom1->IsAsciiLowercase() && aAtom2->IsAsciiLowercase()) {
6085 return false;
6086 }
6087
6088 return EqualsIgnoreASCIICase(nsDependentAtomString(aAtom1),
6089 nsDependentAtomString(aAtom2));
6090 }
6091
6092 /* static */
6093 bool nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
6094 const nsAString& aStr2) {
6095 uint32_t len = aStr1.Length();
6096 if (len != aStr2.Length()) {
6097 return false;
6098 }
6099
6100 const char16_t* str1 = aStr1.BeginReading();
6101 const char16_t* str2 = aStr2.BeginReading();
6102 const char16_t* end = str1 + len;
6103
6104 while (str1 < end) {
6105 char16_t c1 = *str1++;
6106 char16_t c2 = *str2++;
6107
6108 // First check if any bits other than the 0x0020 differs
6109 if ((c1 ^ c2) & 0xffdf) {
6110 return false;
6111 }
6112
6113 // We know they can only differ in the 0x0020 bit.
6114 // Likely the two chars are the same, so check that first
6115 if (c1 != c2) {
6116 // They do differ, but since it's only in the 0x0020 bit, check if it's
6117 // the same ascii char, but just differing in case
6118 char16_t c1Upper = c1 & 0xffdf;
6119 if (!('A' <= c1Upper && c1Upper <= 'Z')) {
6120 return false;
6121 }
6122 }
6123 }
6124
6125 return true;
6126 }
6127
6128 /* static */
6129 bool nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr) {
6130 const char16_t* iter = aStr.BeginReading();
6131 const char16_t* end = aStr.EndReading();
6132 while (iter != end) {
6133 char16_t c = *iter;
6134 if (c >= 'A' && c <= 'Z') {
6135 return true;
6136 }
6137 ++iter;
6138 }
6139
6140 return false;
6141 }
6142
6143 /* static */
6144 nsIInterfaceRequestor* nsContentUtils::SameOriginChecker() {
6145 if (!sSameOriginChecker) {
6146 sSameOriginChecker = new SameOriginCheckerImpl();
6147 NS_ADDREF(sSameOriginChecker);
6148 }
6149 return sSameOriginChecker;
6150 }
6151
6152 /* static */
6153 nsresult nsContentUtils::CheckSameOrigin(nsIChannel* aOldChannel,
6154 nsIChannel* aNewChannel) {
6155 if (!nsContentUtils::GetSecurityManager()) return NS_ERROR_NOT_AVAILABLE;
6156
6157 nsCOMPtr<nsIPrincipal> oldPrincipal;
6158 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
6159 aOldChannel, getter_AddRefs(oldPrincipal));
6160
6161 nsCOMPtr<nsIURI> newURI;
6162 aNewChannel->GetURI(getter_AddRefs(newURI));
6163 nsCOMPtr<nsIURI> newOriginalURI;
6164 aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
6165
6166 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
6167
6168 nsresult rv = oldPrincipal->CheckMayLoad(newURI, false);
6169 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
6170 rv = oldPrincipal->CheckMayLoad(newOriginalURI, false);
6171 }
6172
6173 return rv;
6174 }
6175
6176 NS_IMPL_ISUPPORTS(SameOriginCheckerImpl, nsIChannelEventSink,
6177 nsIInterfaceRequestor)
6178
6179 NS_IMETHODIMP
6180 SameOriginCheckerImpl::AsyncOnChannelRedirect(
6181 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
6182 nsIAsyncVerifyRedirectCallback* cb) {
6183 MOZ_ASSERT(aNewChannel, "Redirecting to null channel?");
6184
6185 nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
6186 if (NS_SUCCEEDED(rv)) {
6187 cb->OnRedirectVerifyCallback(NS_OK);
6188 }
6189
6190 return rv;
6191 }
6192
6193 NS_IMETHODIMP
6194 SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult) {
6195 return QueryInterface(aIID, aResult);
6196 }
6197
6198 /* static */
6199 nsresult nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsACString& aOrigin) {
6200 MOZ_ASSERT(aURI, "missing uri");
6201
6202 // For Blob URI, the path is the URL of the owning page.
6203 if (aURI->SchemeIs(BLOBURI_SCHEME)) {
6204 nsAutoCString path;
6205 nsresult rv = aURI->GetPathQueryRef(path);
6206 NS_ENSURE_SUCCESS(rv, rv);
6207
6208 nsCOMPtr<nsIURI> uri;
6209 rv = NS_NewURI(getter_AddRefs(uri), path);
6210 if (NS_FAILED(rv)) {
6211 aOrigin.AssignLiteral("null");
6212 return NS_OK;
6213 }
6214
6215 return GetASCIIOrigin(uri, aOrigin);
6216 }
6217
6218 aOrigin.Truncate();
6219
6220 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
6221 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
6222
6223 nsAutoCString host;
6224 nsresult rv = uri->GetAsciiHost(host);
6225
6226 if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
6227 nsAutoCString userPass;
6228 uri->GetUserPass(userPass);
6229
6230 nsAutoCString prePath;
6231 if (!userPass.IsEmpty()) {
6232 rv = NS_MutateURI(uri).SetUserPass(""_ns).Finalize(uri);
6233 NS_ENSURE_SUCCESS(rv, rv);
6234 }
6235
6236 rv = uri->GetPrePath(prePath);
6237 NS_ENSURE_SUCCESS(rv, rv);
6238
6239 aOrigin = prePath;
6240 } else {
6241 aOrigin.AssignLiteral("null");
6242 }
6243
6244 return NS_OK;
6245 }
6246
6247 /* static */
6248 nsresult nsContentUtils::GetUTFOrigin(nsIPrincipal* aPrincipal,
6249 nsAString& aOrigin) {
6250 MOZ_ASSERT(aPrincipal, "missing principal");
6251
6252 aOrigin.Truncate();
6253 nsAutoCString asciiOrigin;
6254
6255 nsresult rv = aPrincipal->GetAsciiOrigin(asciiOrigin);
6256 if (NS_FAILED(rv)) {
6257 asciiOrigin.AssignLiteral("null");
6258 }
6259
6260 CopyUTF8toUTF16(asciiOrigin, aOrigin);
6261 return NS_OK;
6262 }
6263
6264 /* static */
6265 nsresult nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsAString& aOrigin) {
6266 MOZ_ASSERT(aURI, "missing uri");
6267 nsresult rv;
6268
6269 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
6270 // Check if either URI has a special origin.
6271 nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin =
6272 do_QueryInterface(aURI);
6273 if (uriWithSpecialOrigin) {
6274 nsCOMPtr<nsIURI> origin;
6275 rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin));
6276 NS_ENSURE_SUCCESS(rv, rv);
6277
6278 return GetUTFOrigin(origin, aOrigin);
6279 }
6280 #endif
6281
6282 nsAutoCString asciiOrigin;
6283 rv = GetASCIIOrigin(aURI, asciiOrigin);
6284 NS_ENSURE_SUCCESS(rv, rv);
6285
6286 CopyUTF8toUTF16(asciiOrigin, aOrigin);
6287 return NS_OK;
6288 }
6289
6290 /* static */
6291 bool nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal,
6292 nsIChannel* aChannel,
6293 bool aAllowIfInheritsPrincipal) {
6294 nsCOMPtr<nsIURI> channelURI;
6295 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
6296 NS_ENSURE_SUCCESS(rv, false);
6297
6298 return NS_SUCCEEDED(
6299 aPrincipal->CheckMayLoad(channelURI, aAllowIfInheritsPrincipal));
6300 }
6301
6302 /* static */
6303 bool nsContentUtils::CanAccessNativeAnon() {
6304 return LegacyIsCallerChromeOrNativeCode();
6305 }
6306
6307 /* static */
6308 nsresult nsContentUtils::DispatchXULCommand(nsIContent* aTarget, bool aTrusted,
6309 Event* aSourceEvent,
6310 PresShell* aPresShell, bool aCtrl,
6311 bool aAlt, bool aShift, bool aMeta,
6312 uint16_t aInputSource,
6313 int16_t aButton) {
6314 NS_ENSURE_STATE(aTarget);
6315 Document* doc = aTarget->OwnerDoc();
6316 nsPresContext* presContext = doc->GetPresContext();
6317
6318 RefPtr<XULCommandEvent> xulCommand =
6319 new XULCommandEvent(doc, presContext, nullptr);
6320 xulCommand->InitCommandEvent(u"command"_ns, true, true,
6321 nsGlobalWindowInner::Cast(doc->GetInnerWindow()),
6322 0, aCtrl, aAlt, aShift, aMeta, aButton,
6323 aSourceEvent, aInputSource, IgnoreErrors());
6324
6325 if (aPresShell) {
6326 nsEventStatus status = nsEventStatus_eIgnore;
6327 return aPresShell->HandleDOMEventWithTarget(aTarget, xulCommand, &status);
6328 }
6329
6330 ErrorResult rv;
6331 aTarget->DispatchEvent(*xulCommand, rv);
6332 return rv.StealNSResult();
6333 }
6334
6335 // static
6336 nsresult nsContentUtils::WrapNative(JSContext* cx, nsISupports* native,
6337 nsWrapperCache* cache, const nsIID* aIID,
6338 JS::MutableHandle<JS::Value> vp,
6339 bool aAllowWrapping) {
6340 MOZ_ASSERT(cx == GetCurrentJSContext());
6341
6342 if (!native) {
6343 vp.setNull();
6344
6345 return NS_OK;
6346 }
6347
6348 JSObject* wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
6349 if (wrapper) {
6350 return NS_OK;
6351 }
6352
6353 NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
6354
6355 if (!NS_IsMainThread()) {
6356 MOZ_CRASH();
6357 }
6358
6359 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
6360 nsresult rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
6361 aAllowWrapping, vp);
6362 return rv;
6363 }
6364
6365 nsresult nsContentUtils::CreateArrayBuffer(JSContext* aCx,
6366 const nsACString& aData,
6367 JSObject** aResult) {
6368 if (!aCx) {
6369 return NS_ERROR_FAILURE;
6370 }
6371
6372 size_t dataLen = aData.Length();
6373 *aResult = JS::NewArrayBuffer(aCx, dataLen);
6374 if (!*aResult) {
6375 return NS_ERROR_FAILURE;
6376 }
6377
6378 if (dataLen > 0) {
6379 NS_ASSERTION(JS::IsArrayBufferObject(*aResult), "What happened?");
6380 JS::AutoCheckCannotGC nogc;
6381 bool isShared;
6382 memcpy(JS::GetArrayBufferData(*aResult, &isShared, nogc),
6383 aData.BeginReading(), dataLen);
6384 MOZ_ASSERT(!isShared);
6385 }
6386
6387 return NS_OK;
6388 }
6389
6390 void nsContentUtils::StripNullChars(const nsAString& aInStr,
6391 nsAString& aOutStr) {
6392 // In common cases where we don't have nulls in the
6393 // string we can simple simply bypass the checking code.
6394 int32_t firstNullPos = aInStr.FindChar('\0');
6395 if (firstNullPos == kNotFound) {
6396 aOutStr.Assign(aInStr);
6397 return;
6398 }
6399
6400 aOutStr.SetCapacity(aInStr.Length() - 1);
6401 nsAString::const_iterator start, end;
6402 aInStr.BeginReading(start);
6403 aInStr.EndReading(end);
6404 while (start != end) {
6405 if (*start != '\0') aOutStr.Append(*start);
6406 ++start;
6407 }
6408 }
6409
6410 struct ClassMatchingInfo {
6411 AtomArray mClasses;
6412 nsCaseTreatment mCaseTreatment;
6413 };
6414
6415 // static
6416 bool nsContentUtils::MatchClassNames(Element* aElement, int32_t aNamespaceID,
6417 nsAtom* aAtom, void* aData) {
6418 // We can't match if there are no class names
6419 const nsAttrValue* classAttr = aElement->GetClasses();
6420 if (!classAttr) {
6421 return false;
6422 }
6423
6424 // need to match *all* of the classes
6425 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6426 uint32_t length = info->mClasses.Length();
6427 if (!length) {
6428 // If we actually had no classes, don't match.
6429 return false;
6430 }
6431 uint32_t i;
6432 for (i = 0; i < length; ++i) {
6433 if (!classAttr->Contains(info->mClasses[i], info->mCaseTreatment)) {
6434 return false;
6435 }
6436 }
6437
6438 return true;
6439 }
6440
6441 // static
6442 void nsContentUtils::DestroyClassNameArray(void* aData) {
6443 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6444 delete info;
6445 }
6446
6447 // static
6448 void* nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
6449 const nsString* aClasses) {
6450 nsAttrValue attrValue;
6451 attrValue.ParseAtomArray(*aClasses);
6452 // nsAttrValue::Equals is sensitive to order, so we'll send an array
6453 auto* info = new ClassMatchingInfo;
6454 if (attrValue.Type() == nsAttrValue::eAtomArray) {
6455 info->mClasses = std::move(*(attrValue.GetAtomArrayValue()));
6456 } else if (attrValue.Type() == nsAttrValue::eAtom) {
6457 info->mClasses.AppendElement(attrValue.GetAtomValue());
6458 }
6459
6460 info->mCaseTreatment =
6461 aRootNode->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks
6462 ? eIgnoreCase
6463 : eCaseMatters;
6464 return info;
6465 }
6466
6467 // static
6468 bool nsContentUtils::IsFocusedContent(const nsIContent* aContent) {
6469 nsFocusManager* fm = nsFocusManager::GetFocusManager();
6470
6471 return fm && fm->GetFocusedElement() == aContent;
6472 }
6473
6474 bool nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent) {
6475 Document* doc = aContent->GetComposedDoc();
6476 if (!doc) {
6477 return false;
6478 }
6479
6480 // If the subdocument lives in another process, the frame is
6481 // tabbable.
6482 if (EventStateManager::IsRemoteTarget(aContent)) {
6483 return true;
6484 }
6485
6486 // XXXbz should this use OwnerDoc() for GetSubDocumentFor?
6487 // sXBL/XBL2 issue!
6488 Document* subDoc = doc->GetSubDocumentFor(aContent);
6489 if (!subDoc) {
6490 return false;
6491 }
6492
6493 nsCOMPtr<nsIDocShell> docShell = subDoc->GetDocShell();
6494 if (!docShell) {
6495 return false;
6496 }
6497
6498 nsCOMPtr<nsIContentViewer> contentViewer;
6499 docShell->GetContentViewer(getter_AddRefs(contentViewer));
6500 if (!contentViewer) {
6501 return false;
6502 }
6503
6504 // If there are 2 viewers for the current docshell, that
6505 // means the current document may be a zombie document.
6506 // While load and pageshow events are dispatched, zombie viewer is the old,
6507 // to be hidden document.
6508 if (contentViewer->GetPreviousViewer()) {
6509 bool inOnLoad = false;
6510 docShell->GetIsExecutingOnLoadHandler(&inOnLoad);
6511 return inOnLoad;
6512 }
6513
6514 return true;
6515 }
6516
6517 bool nsContentUtils::HasScrollgrab(nsIContent* aContent) {
6518 // If we ever standardize this feature we'll want to hook this up properly
6519 // again. For now we're removing all the DOM-side code related to it but
6520 // leaving the layout and APZ handling for it in place.
6521 return false;
6522 }
6523
6524 void nsContentUtils::FlushLayoutForTree(nsPIDOMWindowOuter* aWindow) {
6525 if (!aWindow) {
6526 return;
6527 }
6528
6529 // Note that because FlushPendingNotifications flushes parents, this
6530 // is O(N^2) in docshell tree depth. However, the docshell tree is
6531 // usually pretty shallow.
6532
6533 if (RefPtr<Document> doc = aWindow->GetDoc()) {
6534 doc->FlushPendingNotifications(FlushType::Layout);
6535 }
6536
6537 if (nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell()) {
6538 int32_t i = 0, i_end;
6539 docShell->GetInProcessChildCount(&i_end);
6540 for (; i < i_end; ++i) {
6541 nsCOMPtr<nsIDocShellTreeItem> item;
6542 if (docShell->GetInProcessChildAt(i, getter_AddRefs(item)) == NS_OK &&
6543 item) {
6544 if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
6545 FlushLayoutForTree(win);
6546 }
6547 }
6548 }
6549 }
6550 }
6551
6552 void nsContentUtils::RemoveNewlines(nsString& aString) { aString.StripCRLF(); }
6553
6554 void nsContentUtils::PlatformToDOMLineBreaks(nsString& aString) {
6555 if (!PlatformToDOMLineBreaks(aString, fallible)) {
6556 aString.AllocFailed(aString.Length());
6557 }
6558 }
6559
6560 bool nsContentUtils::PlatformToDOMLineBreaks(nsString& aString,
6561 const fallible_t& aFallible) {
6562 if (aString.FindChar(char16_t('\r')) != -1) {
6563 // Windows linebreaks: Map CRLF to LF:
6564 if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
6565 return false;
6566 }
6567
6568 // Mac linebreaks: Map any remaining CR to LF:
6569 if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
6570 return false;
6571 }
6572 }
6573
6574 return true;
6575 }
6576
6577 void nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
6578 nsAString& aResultString) {
6579 MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
6580
6581 uint32_t stringLen = NS_strlen(static_cast<char16_t*>(aBuf->Data()));
6582
6583 // SANITY CHECK: In case the nsStringBuffer isn't correctly
6584 // null-terminated, let's clamp its length using the allocated size, to be
6585 // sure the resulting string doesn't sample past the end of the the buffer.
6586 // (Note that StorageSize() is in units of bytes, so we have to convert that
6587 // to units of PRUnichars, and subtract 1 for the null-terminator.)
6588 uint32_t allocStringLen = (aBuf->StorageSize() / sizeof(char16_t)) - 1;
6589 MOZ_ASSERT(stringLen <= allocStringLen,
6590 "string buffer lacks null terminator!");
6591 stringLen = std::min(stringLen, allocStringLen);
6592
6593 aBuf->ToString(stringLen, aResultString);
6594 }
6595
6596 already_AddRefed<nsContentList> nsContentUtils::GetElementsByClassName(
6597 nsINode* aRootNode, const nsAString& aClasses) {
6598 MOZ_ASSERT(aRootNode, "Must have root node");
6599
6600 return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(
6601 aRootNode, MatchClassNames, DestroyClassNameArray, AllocClassMatchingInfo,
6602 aClasses);
6603 }
6604
6605 PresShell* nsContentUtils::FindPresShellForDocument(const Document* aDocument) {
6606 const Document* doc = aDocument;
6607 Document* displayDoc = doc->GetDisplayDocument();
6608 if (displayDoc) {
6609 doc = displayDoc;
6610 }
6611
6612 PresShell* presShell = doc->GetPresShell();
6613 if (presShell) {
6614 return presShell;
6615 }
6616
6617 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
6618 while (docShellTreeItem) {
6619 // We may be in a display:none subdocument, or we may not have a presshell
6620 // created yet.
6621 // Walk the docshell tree to find the nearest container that has a
6622 // presshell, and return that.
6623 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
6624 if (PresShell* presShell = docShell->GetPresShell()) {
6625 return presShell;
6626 }
6627 nsCOMPtr<nsIDocShellTreeItem> parent;
6628 docShellTreeItem->GetInProcessParent(getter_AddRefs(parent));
6629 docShellTreeItem = parent;
6630 }
6631
6632 return nullptr;
6633 }
6634
6635 /* static */
6636 nsPresContext* nsContentUtils::FindPresContextForDocument(
6637 const Document* aDocument) {
6638 if (PresShell* presShell = FindPresShellForDocument(aDocument)) {
6639 return presShell->GetPresContext();
6640 }
6641 return nullptr;
6642 }
6643
6644 nsIWidget* nsContentUtils::WidgetForDocument(const Document* aDocument) {
6645 PresShell* presShell = FindPresShellForDocument(aDocument);
6646 if (!presShell) {
6647 return nullptr;
6648 }
6649 nsViewManager* vm = presShell->GetViewManager();
6650 if (!vm) {
6651 return nullptr;
6652 }
6653 nsView* rootView = vm->GetRootView();
6654 if (!rootView) {
6655 return nullptr;
6656 }
6657 nsView* displayRoot = nsViewManager::GetDisplayRootFor(rootView);
6658 if (!displayRoot) {
6659 return nullptr;
6660 }
6661 return displayRoot->GetNearestWidget(nullptr);
6662 }
6663
6664 nsIWidget* nsContentUtils::WidgetForContent(const nsIContent* aContent) {
6665 nsIFrame* frame = aContent->GetPrimaryFrame();
6666 if (frame) {
6667 frame = nsLayoutUtils::GetDisplayRootFrame(frame);
6668
6669 nsView* view = frame->GetView();
6670 if (view) {
6671 return view->GetWidget();
6672 }
6673 }
6674
6675 return nullptr;
6676 }
6677
6678 already_AddRefed<LayerManager> nsContentUtils::LayerManagerForContent(
6679 const nsIContent* aContent) {
6680 nsIWidget* widget = nsContentUtils::WidgetForContent(aContent);
6681 if (widget) {
6682 RefPtr<LayerManager> manager = widget->GetLayerManager();
6683 return manager.forget();
6684 }
6685
6686 return nullptr;
6687 }
6688
6689 static already_AddRefed<LayerManager> LayerManagerForDocumentInternal(
6690 const Document* aDoc, bool aRequirePersistent) {
6691 nsIWidget* widget = nsContentUtils::WidgetForDocument(aDoc);
6692 if (widget) {
6693 RefPtr<LayerManager> manager = widget->GetLayerManager(
6694 aRequirePersistent ? nsIWidget::LAYER_MANAGER_PERSISTENT
6695 : nsIWidget::LAYER_MANAGER_CURRENT);
6696 return manager.forget();
6697 }
6698
6699 return nullptr;
6700 }
6701
6702 already_AddRefed<LayerManager> nsContentUtils::LayerManagerForDocument(
6703 const Document* aDoc) {
6704 return LayerManagerForDocumentInternal(aDoc, false);
6705 }
6706
6707 already_AddRefed<LayerManager>
6708 nsContentUtils::PersistentLayerManagerForDocument(Document* aDoc) {
6709 return LayerManagerForDocumentInternal(aDoc, true);
6710 }
6711
6712 bool nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal) {
6713 if (!aPrincipal) {
6714 return false;
6715 }
6716
6717 if (aPrincipal->IsSystemPrincipal()) {
6718 return true;
6719 }
6720
6721 return (StaticPrefs::dom_allow_XUL_XBL_for_file() &&
6722 aPrincipal->SchemeIs("file")) ||
6723 IsSitePermAllow(aPrincipal, "allowXULXBL"_ns);
6724 }
6725
6726 bool nsContentUtils::IsPDFJSEnabled() {
6727 nsCOMPtr<nsIStreamConverter> conv = do_CreateInstance(
6728 "@mozilla.org/streamconv;1?from=application/pdf&to=text/html");
6729 return conv;
6730 }
6731
6732 bool nsContentUtils::IsPDFJS(nsIPrincipal* aPrincipal) {
6733 if (!aPrincipal) {
6734 return false;
6735 }
6736 nsAutoCString spec;
6737 nsresult rv = aPrincipal->GetAsciiSpec(spec);
6738 NS_ENSURE_SUCCESS(rv, false);
6739 return spec.EqualsLiteral("resource://pdf.js/web/viewer.html");
6740 }
6741
6742 bool nsContentUtils::IsPDFJS(JSContext* aCx, JSObject*) {
6743 return IsPDFJS(SubjectPrincipal(aCx));
6744 }
6745
6746 already_AddRefed<nsIDocumentLoaderFactory>
6747 nsContentUtils::FindInternalContentViewer(const nsACString& aType,
6748 ContentViewerType* aLoaderType) {
6749 if (aLoaderType) {
6750 *aLoaderType = TYPE_UNSUPPORTED;
6751 }
6752
6753 // one helper factory, please
6754 nsCOMPtr<nsICategoryManager> catMan(
6755 do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
6756 if (!catMan) return nullptr;
6757
6758 nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
6759
6760 nsCString contractID;
6761 nsresult rv =
6762 catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, contractID);
6763 if (NS_SUCCEEDED(rv)) {
6764 docFactory = do_GetService(contractID.get());
6765 if (docFactory && aLoaderType) {
6766 if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
6767 *aLoaderType = TYPE_CONTENT;
6768 else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
6769 *aLoaderType = TYPE_FALLBACK;
6770 else
6771 *aLoaderType = TYPE_UNKNOWN;
6772 }
6773 return docFactory.forget();
6774 }
6775
6776 if (DecoderTraits::IsSupportedInVideoDocument(aType)) {
6777 docFactory =
6778 do_GetService("@mozilla.org/content/document-loader-factory;1");
6779 if (docFactory && aLoaderType) {
6780 *aLoaderType = TYPE_CONTENT;
6781 }
6782 return docFactory.forget();
6783 }
6784
6785 return nullptr;
6786 }
6787
6788 static void ReportPatternCompileFailure(nsAString& aPattern,
6789 const Document* aDocument,
6790 JS::MutableHandle<JS::Value> error,
6791 JSContext* cx) {
6792 JS::AutoSaveExceptionState savedExc(cx);
6793 JS::RootedObject exnObj(cx, &error.toObject());
6794 JS::RootedValue messageVal(cx);
6795 if (!JS_GetProperty(cx, exnObj, "message", &messageVal)) {
6796 return;
6797 }
6798 JS::RootedString messageStr(cx, messageVal.toString());
6799 MOZ_ASSERT(messageStr);
6800
6801 AutoTArray<nsString, 2> strings;
6802 strings.AppendElement(aPattern);
6803 if (!AssignJSString(cx, *strings.AppendElement(), messageStr)) {
6804 return;
6805 }
6806
6807 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns,
6808 aDocument, nsContentUtils::eDOM_PROPERTIES,
6809 "PatternAttributeCompileFailure", strings);
6810 savedExc.drop();
6811 }
6812
6813 // static
6814 Maybe<bool> nsContentUtils::IsPatternMatching(nsAString& aValue,
6815 nsAString& aPattern,
6816 const Document* aDocument) {
6817 NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
6818
6819 // The fact that we're using a JS regexp under the hood should not be visible
6820 // to things like window onerror handlers, so we don't initialize our JSAPI
6821 // with the document's window (which may not exist anyway).
6822 AutoJSAPI jsapi;
6823 jsapi.Init();
6824 JSContext* cx = jsapi.cx();
6825 AutoDisableJSInterruptCallback disabler(cx);
6826
6827 // We can use the junk scope here, because we're just using it for regexp
6828 // evaluation, not actual script execution, and we disable statics so that the
6829 // evaluation does not interact with the execution global.
6830 JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
6831
6832 // Check if the pattern by itself is valid first, and not that it only becomes
6833 // valid once we add ^(?: and )$.
6834 JS::RootedValue error(cx);
6835 if (!JS::CheckRegExpSyntax(
6836 cx, static_cast<char16_t*>(aPattern.BeginWriting()),
6837 aPattern.Length(), JS::RegExpFlag::Unicode, &error)) {
6838 return Nothing();
6839 }
6840
6841 if (!error.isUndefined()) {
6842 ReportPatternCompileFailure(aPattern, aDocument, &error, cx);
6843 return Some(true);
6844 }
6845
6846 // The pattern has to match the entire value.
6847 aPattern.InsertLiteral(u"^(?:", 0);
6848 aPattern.AppendLiteral(")$");
6849
6850 JS::Rooted<JSObject*> re(
6851 cx,
6852 JS::NewUCRegExpObject(cx, static_cast<char16_t*>(aPattern.BeginWriting()),
6853 aPattern.Length(), JS::RegExpFlag::Unicode));
6854 if (!re) {
6855 return Nothing();
6856 }
6857
6858 JS::Rooted<JS::Value> rval(cx, JS::NullValue());
6859 size_t idx = 0;
6860 if (!JS::ExecuteRegExpNoStatics(cx, re,
6861 static_cast<char16_t*>(aValue.BeginWriting()),
6862 aValue.Length(), &idx, true, &rval)) {
6863 return Nothing();
6864 }
6865
6866 return Some(!rval.isNull());
6867 }
6868
6869 // static
6870 nsresult nsContentUtils::URIInheritsSecurityContext(nsIURI* aURI,
6871 bool* aResult) {
6872 // Note: about:blank URIs do NOT inherit the security context from the
6873 // current document, which is what this function tests for...
6874 return NS_URIChainHasFlags(
6875 aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, aResult);
6876 }
6877
6878 // static
6879 bool nsContentUtils::ChannelShouldInheritPrincipal(
6880 nsIPrincipal* aLoadingPrincipal, nsIURI* aURI, bool aInheritForAboutBlank,
6881 bool aForceInherit) {
6882 MOZ_ASSERT(aLoadingPrincipal,
6883 "Can not check inheritance without a principal");
6884
6885 // Only tell the channel to inherit if it can't provide its own security
6886 // context.
6887 //
6888 // XXX: If this is ever changed, check all callers for what owners
6889 // they're passing in. In particular, see the code and
6890 // comments in nsDocShell::LoadURI where we fall back on
6891 // inheriting the owner if called from chrome. That would be
6892 // very wrong if this code changed anything but channels that
6893 // can't provide their own security context!
6894 //
6895 // If aForceInherit is true, we will inherit, even for a channel that
6896 // can provide its own security context. This is used for srcdoc loads.
6897 bool inherit = aForceInherit;
6898 if (!inherit) {
6899 bool uriInherits;
6900 // We expect URIInheritsSecurityContext to return success for an
6901 // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
6902 // This condition needs to match the one in nsDocShell::InternalLoad where
6903 // we're checking for things that will use the owner.
6904 inherit =
6905 (NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &uriInherits)) &&
6906 (uriInherits || (aInheritForAboutBlank && NS_IsAboutBlank(aURI)))) ||
6907 //
6908 // file: uri special-casing
6909 //
6910 // If this is a file: load opened from another file: then it may need
6911 // to inherit the owner from the referrer so they can script each other.
6912 // If we don't set the owner explicitly then each file: gets an owner
6913 // based on its own codebase later.
6914 //
6915 (URIIsLocalFile(aURI) &&
6916 NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, false)) &&
6917 // One more check here. CheckMayLoad will always return true for the
6918 // system principal, but we do NOT want to inherit in that case.
6919 !aLoadingPrincipal->IsSystemPrincipal());
6920 }
6921 return inherit;
6922 }
6923
6924 /* static */
6925 bool nsContentUtils::IsCutCopyAllowed(Document* aDocument,
6926 nsIPrincipal& aSubjectPrincipal) {
6927 if (StaticPrefs::dom_allow_cut_copy() && aDocument &&
6928 aDocument->HasValidTransientUserGestureActivation()) {
6929 return true;
6930 }
6931
6932 return PrincipalHasPermission(aSubjectPrincipal, nsGkAtoms::clipboardWrite);
6933 }
6934
6935 /* static */
6936 bool nsContentUtils::HaveEqualPrincipals(Document* aDoc1, Document* aDoc2) {
6937 if (!aDoc1 || !aDoc2) {
6938 return false;
6939 }
6940 bool principalsEqual = false;
6941 aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
6942 return principalsEqual;
6943 }
6944
6945 /* static */
6946 bool nsContentUtils::HasPluginWithUncontrolledEventDispatch(
6947 nsIContent* aContent) {
6948 return false;
6949 }
6950
6951 /* static */
6952 void nsContentUtils::FireMutationEventsForDirectParsing(
6953 Document* aDoc, nsIContent* aDest, int32_t aOldChildCount) {
6954 // Fire mutation events. Optimize for the case when there are no listeners
6955 int32_t newChildCount = aDest->GetChildCount();
6956 if (newChildCount && nsContentUtils::HasMutationListeners(
6957 aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
6958 AutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
6959 NS_ASSERTION(newChildCount - aOldChildCount >= 0,
6960 "What, some unexpected dom mutation has happened?");
6961 childNodes.SetCapacity(newChildCount - aOldChildCount);
6962 for (nsIContent* child = aDest->GetFirstChild(); child;
6963 child = child->GetNextSibling()) {
6964 childNodes.AppendElement(child);
6965 }
6966 FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
6967 }
6968 }
6969
6970 /* static */
6971 Document* nsContentUtils::GetInProcessSubtreeRootDocument(Document* aDoc) {
6972 if (!aDoc) {
6973 return nullptr;
6974 }
6975 Document* doc = aDoc;
6976 while (doc->GetInProcessParentDocument()) {
6977 doc = doc->GetInProcessParentDocument();
6978 }
6979 return doc;
6980 }
6981
6982 // static
6983 int32_t nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
6984 int32_t aOffset) {
6985 // The structure of the anonymous frames within a text control frame is
6986 // an optional block frame, followed by an optional br frame.
6987
6988 // If the offset frame has a child, then this frame is the block which
6989 // has the text frames (containing the content) as its children. This will
6990 // be the case if we click to the right of any of the text frames, or at the
6991 // bottom of the text area.
6992 nsIFrame* firstChild = aOffsetFrame->PrincipalChildList().FirstChild();
6993 if (firstChild) {
6994 // In this case, the passed-in offset is incorrect, and we want the length
6995 // of the entire content in the text control frame.
6996 return firstChild->GetContent()->Length();
6997 }
6998
6999 if (aOffsetFrame->GetPrevSibling() && !aOffsetFrame->GetNextSibling()) {
7000 // In this case, we're actually within the last frame, which is a br
7001 // frame. Our offset should therefore be the length of the first child of
7002 // our parent.
7003 int32_t aOutOffset = aOffsetFrame->GetParent()
7004 ->PrincipalChildList()
7005 .FirstChild()
7006 ->GetContent()
7007 ->Length();
7008 return aOutOffset;
7009 }
7010
7011 // Otherwise, we're within one of the text frames, in which case our offset
7012 // has already been correctly calculated.
7013 return aOffset;
7014 }
7015
7016 // static
7017 void nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
7018 Element* aRoot,
7019 uint32_t& aOutStartOffset,
7020 uint32_t& aOutEndOffset) {
7021 MOZ_ASSERT(aSelection && aRoot);
7022
7023 // We don't care which end of this selection is anchor and which is focus. In
7024 // fact, we explicitly want to know which is the _start_ and which is the
7025 // _end_, not anchor vs focus.
7026 const nsRange* range = aSelection->GetAnchorFocusRange();
7027 if (!range) {
7028 // Nothing selected
7029 aOutStartOffset = aOutEndOffset = 0;
7030 return;
7031 }
7032
7033 // All the node pointers here are raw pointers for performance. We shouldn't
7034 // be doing anything in this function that invalidates the node tree.
7035 nsINode* startContainer = range->GetStartContainer();
7036 uint32_t startOffset = range->StartOffset();
7037 nsINode* endContainer = range->GetEndContainer();
7038 uint32_t endOffset = range->EndOffset();
7039
7040 // We have at most two children, consisting of an optional text node followed
7041 // by an optional <br>.
7042 NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
7043 nsIContent* firstChild = aRoot->GetFirstChild();
7044 #ifdef DEBUG
7045 nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
7046 NS_ASSERTION(startContainer == aRoot || startContainer == firstChild ||
7047 startContainer == lastChild,
7048 "Unexpected startContainer");
7049 NS_ASSERTION(endContainer == aRoot || endContainer == firstChild ||
7050 endContainer == lastChild,
7051 "Unexpected endContainer");
7052 // firstChild is either text or a <br> (hence an element).
7053 MOZ_ASSERT_IF(firstChild, firstChild->IsText() || firstChild->IsElement());
7054 #endif
7055 // Testing IsElement() is faster than testing IsNodeOfType(), since it's
7056 // non-virtual.
7057 if (!firstChild || firstChild->IsElement()) {
7058 // No text node, so everything is 0
7059 startOffset = endOffset = 0;
7060 } else {
7061 // First child is text. If the start/end is already in the text node,
7062 // or the start of the root node, no change needed. If it's in the root
7063 // node but not the start, or in the trailing <br>, we need to set the
7064 // offset to the end.
7065 if ((startContainer == aRoot && startOffset != 0) ||
7066 (startContainer != aRoot && startContainer != firstChild)) {
7067 startOffset = firstChild->Length();
7068 }
7069 if ((endContainer == aRoot && endOffset != 0) ||
7070 (endContainer != aRoot && endContainer != firstChild)) {
7071 endOffset = firstChild->Length();
7072 }
7073 }
7074
7075 MOZ_ASSERT(startOffset <= endOffset);
7076 aOutStartOffset = startOffset;
7077 aOutEndOffset = endOffset;
7078 }
7079
7080 // static
7081 HTMLEditor* nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext) {
7082 if (!aPresContext) {
7083 return nullptr;
7084 }
7085 return GetHTMLEditor(aPresContext->GetDocShell());
7086 }
7087
7088 // static
7089 HTMLEditor* nsContentUtils::GetHTMLEditor(nsDocShell* aDocShell) {
7090 bool isEditable;
7091 if (!aDocShell || NS_FAILED(aDocShell->GetEditable(&isEditable)) ||
7092 !isEditable) {
7093 return nullptr;
7094 }
7095 return aDocShell->GetHTMLEditor();
7096 }
7097
7098 // static
7099 EditorBase* nsContentUtils::GetActiveEditor(nsPresContext* aPresContext) {
7100 if (!aPresContext) {
7101 return nullptr;
7102 }
7103
7104 return GetActiveEditor(aPresContext->Document()->GetWindow());
7105 }
7106
7107 // static
7108 EditorBase* nsContentUtils::GetActiveEditor(nsPIDOMWindowOuter* aWindow) {
7109 if (!aWindow || !aWindow->GetExtantDoc()) {
7110 return nullptr;
7111 }
7112
7113 // If it's in designMode, nobody can have focus. Therefore, the HTMLEditor
7114 // handles all events. I.e., it's focused editor in this case.
7115 if (aWindow->GetExtantDoc()->HasFlag(NODE_IS_EDITABLE)) {
7116 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7117 }
7118
7119 // If focused element is associated with TextEditor, it must be <input>
7120 // element or <textarea> element. Let's return it even if it's in a
7121 // contenteditable element.
7122 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
7123 if (Element* focusedElement = nsFocusManager::GetFocusedDescendant(
7124 aWindow, nsFocusManager::SearchRange::eOnlyCurrentWindow,
7125 getter_AddRefs(focusedWindow))) {
7126 if (TextEditor* textEditor = focusedElement->GetTextEditorInternal()) {
7127 return textEditor;
7128 }
7129 }
7130
7131 // Otherwise, HTMLEditor may handle inputs even non-editable element has
7132 // focus or nobody has focus.
7133 return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
7134 }
7135
7136 // static
7137 TextEditor* nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(
7138 nsIContent* aAnonymousContent) {
7139 if (!aAnonymousContent) {
7140 return nullptr;
7141 }
7142 nsIContent* parent = aAnonymousContent->FindFirstNonChromeOnlyAccessContent();
7143 if (!parent || parent == aAnonymousContent) {
7144 return nullptr;
7145 }
7146 if (HTMLInputElement* inputElement =
7147 HTMLInputElement::FromNodeOrNull(parent)) {
7148 return inputElement->GetTextEditorWithoutCreation();
7149 }
7150 if (HTMLTextAreaElement* textareaElement =
7151 HTMLTextAreaElement::FromNodeOrNull(parent)) {
7152 return textareaElement->GetTextEditorWithoutCreation();
7153 }
7154 return nullptr;
7155 }
7156
7157 // static
7158 bool nsContentUtils::IsNodeInEditableRegion(nsINode* aNode) {
7159 while (aNode) {
7160 if (aNode->IsEditable()) {
7161 return true;
7162 }
7163 aNode = aNode->GetParent();
7164 }
7165 return false;
7166 }
7167
7168 // static
7169 bool nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader) {
7170 if (IsForbiddenSystemRequestHeader(aHeader)) {
7171 return true;
7172 }
7173
7174 return StringBeginsWith(aHeader, "proxy-"_ns,
7175 nsCaseInsensitiveCStringComparator) ||
7176 StringBeginsWith(aHeader, "sec-"_ns,
7177 nsCaseInsensitiveCStringComparator);
7178 }
7179
7180 // static
7181 bool nsContentUtils::IsForbiddenSystemRequestHeader(const nsACString& aHeader) {
7182 static const char* kInvalidHeaders[] = {"accept-charset",
7183 "accept-encoding",
7184 "access-control-request-headers",
7185 "access-control-request-method",
7186 "connection",
7187 "content-length",
7188 "cookie",
7189 "cookie2",
7190 "date",
7191 "dnt",
7192 "expect",
7193 "host",
7194 "keep-alive",
7195 "origin",
7196 "referer",
7197 "te",
7198 "trailer",
7199 "transfer-encoding",
7200 "upgrade",
7201 "via"};
7202 for (auto& kInvalidHeader : kInvalidHeaders) {
7203 if (aHeader.LowerCaseEqualsASCII(kInvalidHeader)) {
7204 return true;
7205 }
7206 }
7207 return false;
7208 }
7209
7210 // static
7211 bool nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader) {
7212 return (aHeader.LowerCaseEqualsASCII("set-cookie") ||
7213 aHeader.LowerCaseEqualsASCII("set-cookie2"));
7214 }
7215
7216 // static
7217 bool nsContentUtils::IsCorsUnsafeRequestHeaderValue(
7218 const nsACString& aHeaderValue) {
7219 const char* cur = aHeaderValue.BeginReading();
7220 const char* end = aHeaderValue.EndReading();
7221
7222 while (cur != end) {
7223 // Implementation of
7224 // https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte Is less
7225 // than a space but not a horizontal tab
7226 if ((*cur < ' ' && *cur != '\t') || *cur == '"' || *cur == '(' ||
7227 *cur == ')' || *cur == ':' || *cur == '<' || *cur == '>' ||
7228 *cur == '?' || *cur == '@' || *cur == '[' || *cur == '\\' ||
7229 *cur == ']' || *cur == '{' || *cur == '}' ||
7230 *cur == 0x7F) { // 0x75 is DEL
7231 return true;
7232 }
7233 cur++;
7234 }
7235 return false;
7236 }
7237
7238 // static
7239 bool nsContentUtils::IsAllowedNonCorsAccept(const nsACString& aHeaderValue) {
7240 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7241 return false;
7242 }
7243 return true;
7244 }
7245
7246 // static
7247 bool nsContentUtils::IsAllowedNonCorsContentType(
7248 const nsACString& aHeaderValue) {
7249 nsAutoCString contentType;
7250 nsAutoCString unused;
7251
7252 if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
7253 return false;
7254 }
7255
7256 nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
7257 if (NS_FAILED(rv)) {
7258 return false;
7259 }
7260
7261 return contentType.LowerCaseEqualsLiteral("text/plain") ||
7262 contentType.LowerCaseEqualsLiteral(
7263 "application/x-www-form-urlencoded") ||
7264 contentType.LowerCaseEqualsLiteral("multipart/form-data");
7265 }
7266
7267 // static
7268 bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
7269 const char* cur = aHeaderValue.BeginReading();
7270 const char* end = aHeaderValue.EndReading();
7271
7272 while (cur != end) {
7273 if ((*cur >= '0' && *cur <= '9') || (*cur >= 'A' && *cur <= 'Z') ||
7274 (*cur >= 'a' && *cur <= 'z') || *cur == ' ' || *cur == '*' ||
7275 *cur == ',' || *cur == '-' || *cur == '.' || *cur == ';' ||
7276 *cur == '=') {
7277 cur++;
7278 continue;
7279 }
7280 return false;
7281 }
7282 return true;
7283 }
7284
7285 // static
7286 bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
7287 const nsACString& aValue) {
7288 // see https://fetch.spec.whatwg.org/#cors-safelisted-request-header
7289 if (aValue.Length() > 128) {
7290 return false;
7291 }
7292 return (aName.LowerCaseEqualsLiteral("accept") &&
7293 nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
7294 (aName.LowerCaseEqualsLiteral("accept-language") &&
7295 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7296 (aName.LowerCaseEqualsLiteral("content-language") &&
7297 nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
7298 (aName.LowerCaseEqualsLiteral("content-type") &&
7299 nsContentUtils::IsAllowedNonCorsContentType(aValue));
7300 }
7301
7302 mozilla::LogModule* nsContentUtils::ResistFingerprintingLog() {
7303 return gResistFingerprintingLog;
7304 }
7305 mozilla::LogModule* nsContentUtils::DOMDumpLog() { return sDOMDumpLog; }
7306
7307 bool nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep,
7308 nsAString& aResult,
7309 const fallible_t& aFallible) {
7310 aResult.Truncate();
7311 return AppendNodeTextContent(aNode, aDeep, aResult, aFallible);
7312 }
7313
7314 void nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep,
7315 nsAString& aResult) {
7316 if (!GetNodeTextContent(aNode, aDeep, aResult, fallible)) {
7317 NS_ABORT_OOM(0); // Unfortunately we don't know the allocation size
7318 }
7319 }
7320
7321 void nsContentUtils::DestroyMatchString(void* aData) {
7322 if (aData) {
7323 nsString* matchString = static_cast<nsString*>(aData);
7324 delete matchString;
7325 }
7326 }
7327
7328 bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
7329 // Table ordered from most to least likely JS MIME types.
7330 static const char* jsTypes[] = {"text/javascript",
7331 "text/ecmascript",
7332 "application/javascript",
7333 "application/ecmascript",
7334 "application/x-javascript",
7335 "application/x-ecmascript",
7336 "text/javascript1.0",
7337 "text/javascript1.1",
7338 "text/javascript1.2",
7339 "text/javascript1.3",
7340 "text/javascript1.4",
7341 "text/javascript1.5",
7342 "text/jscript",
7343 "text/livescript",
7344 "text/x-ecmascript",
7345 "text/x-javascript",
7346 nullptr};
7347
7348 for (uint32_t i = 0; jsTypes[i]; ++i) {
7349 if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
7350 return true;
7351 }
7352 }
7353
7354 return false;
7355 }
7356
7357 nsresult nsContentUtils::GenerateUUIDInPlace(nsID& aUUID) {
7358 MOZ_ASSERT(sUUIDGenerator);
7359
7360 nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&aUUID);
7361 if (NS_WARN_IF(NS_FAILED(rv))) {
7362 return rv;
7363 }
7364
7365 return NS_OK;
7366 }
7367
7368 nsID nsContentUtils::GenerateUUID() {
7369 MOZ_DIAGNOSTIC_ASSERT(sUUIDGenerator);
7370
7371 nsID uuid;
7372 nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&uuid);
7373 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
7374
7375 return uuid;
7376 }
7377
7378 bool nsContentUtils::PrefetchPreloadEnabled(nsIDocShell* aDocShell) {
7379 //
7380 // SECURITY CHECK: disable prefetching and preloading from mailnews!
7381 //
7382 // walk up the docshell tree to see if any containing
7383 // docshell are of type MAIL.
7384 //
7385
7386 if (!aDocShell) {
7387 return false;
7388 }
7389
7390 nsCOMPtr<nsIDocShell> docshell = aDocShell;
7391 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7392
7393 do {
7394 auto appType = docshell->GetAppType();
7395 if (appType == nsIDocShell::APP_TYPE_MAIL) {
7396 return false; // do not prefetch, preload, preconnect from mailnews
7397 }
7398
7399 docshell->GetInProcessParent(getter_AddRefs(parentItem));
7400 if (parentItem) {
7401 docshell = do_QueryInterface(parentItem);
7402 if (!docshell) {
7403 NS_ERROR("cannot get a docshell from a treeItem!");
7404 return false;
7405 }
7406 }
7407 } while (parentItem);
7408
7409 return true;
7410 }
7411
7412 uint64_t nsContentUtils::GetInnerWindowID(nsIRequest* aRequest) {
7413 // can't do anything if there's no nsIRequest!
7414 if (!aRequest) {
7415 return 0;
7416 }
7417
7418 nsCOMPtr<nsILoadGroup> loadGroup;
7419 nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
7420
7421 if (NS_FAILED(rv) || !loadGroup) {
7422 return 0;
7423 }
7424
7425 return GetInnerWindowID(loadGroup);
7426 }
7427
7428 uint64_t nsContentUtils::GetInnerWindowID(nsILoadGroup* aLoadGroup) {
7429 if (!aLoadGroup) {
7430 return 0;
7431 }
7432
7433 nsCOMPtr<nsIInterfaceRequestor> callbacks;
7434 nsresult rv = aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
7435 if (NS_FAILED(rv) || !callbacks) {
7436 return 0;
7437 }
7438
7439 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
7440 if (!loadContext) {
7441 return 0;
7442 }
7443
7444 nsCOMPtr<mozIDOMWindowProxy> window;
7445 rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
7446 if (NS_FAILED(rv) || !window) {
7447 return 0;
7448 }
7449
7450 auto* pwindow = nsPIDOMWindowOuter::From(window);
7451 if (!pwindow) {
7452 return 0;
7453 }
7454
7455 nsPIDOMWindowInner* inner = pwindow->GetCurrentInnerWindow();
7456 return inner ? inner->WindowID() : 0;
7457 }
7458
7459 // static
7460 void nsContentUtils::MaybeFixIPv6Host(nsACString& aHost) {
7461 if (aHost.FindChar(':') != -1) { // Escape IPv6 address
7462 MOZ_ASSERT(!aHost.Length() ||
7463 (aHost[0] != '[' && aHost[aHost.Length() - 1] != ']'));
7464 aHost.Insert('[', 0);
7465 aHost.Append(']');
7466 }
7467 }
7468
7469 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7470 nsACString& aHost) {
7471 aHost.Truncate();
7472 nsresult rv = aURI->GetHost(aHost);
7473 if (NS_FAILED(rv)) { // Some URIs do not have a host
7474 return rv;
7475 }
7476
7477 MaybeFixIPv6Host(aHost);
7478
7479 return NS_OK;
7480 }
7481
7482 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
7483 nsAString& aHost) {
7484 nsAutoCString hostname;
7485 nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname);
7486 if (NS_FAILED(rv)) {
7487 return rv;
7488 }
7489 CopyUTF8toUTF16(hostname, aHost);
7490 return NS_OK;
7491 }
7492
7493 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIPrincipal* aPrincipal,
7494 nsACString& aHost) {
7495 nsresult rv = aPrincipal->GetAsciiHost(aHost);
7496 if (NS_FAILED(rv)) { // Some URIs do not have a host
7497 return rv;
7498 }
7499
7500 MaybeFixIPv6Host(aHost);
7501 return NS_OK;
7502 }
7503
7504 CallState nsContentUtils::CallOnAllRemoteChildren(
7505 MessageBroadcaster* aManager,
7506 const std::function<CallState(BrowserParent*)>& aCallback) {
7507 uint32_t browserChildCount = aManager->ChildCount();
7508 for (uint32_t j = 0; j < browserChildCount; ++j) {
7509 RefPtr<MessageListenerManager> childMM = aManager->GetChildAt(j);
7510 if (!childMM) {
7511 continue;
7512 }
7513
7514 RefPtr<MessageBroadcaster> nonLeafMM = MessageBroadcaster::From(childMM);
7515 if (nonLeafMM) {
7516 if (CallOnAllRemoteChildren(nonLeafMM, aCallback) == CallState::Stop) {
7517 return CallState::Stop;
7518 }
7519 continue;
7520 }
7521
7522 mozilla::dom::ipc::MessageManagerCallback* cb = childMM->GetCallback();
7523 if (cb) {
7524 nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
7525 BrowserParent* remote = BrowserParent::GetFrom(fl);
7526 if (remote && aCallback) {
7527 if (aCallback(remote) == CallState::Stop) {
7528 return CallState::Stop;
7529 }
7530 }
7531 }
7532 }
7533
7534 return CallState::Continue;
7535 }
7536
7537 void nsContentUtils::CallOnAllRemoteChildren(
7538 nsPIDOMWindowOuter* aWindow,
7539 const std::function<CallState(BrowserParent*)>& aCallback) {
7540 nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(aWindow);
7541 if (window->IsChromeWindow()) {
7542 RefPtr<MessageBroadcaster> windowMM = window->GetMessageManager();
7543 if (windowMM) {
7544 CallOnAllRemoteChildren(windowMM, aCallback);
7545 }
7546 }
7547 }
7548
7549 struct UIStateChangeInfo {
7550 UIStateChangeType mShowFocusRings;
7551
7552 explicit UIStateChangeInfo(UIStateChangeType aShowFocusRings)
7553 : mShowFocusRings(aShowFocusRings) {}
7554 };
7555
7556 void nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(
7557 nsPIDOMWindowOuter* aWindow, UIStateChangeType aShowFocusRings) {
7558 UIStateChangeInfo stateInfo(aShowFocusRings);
7559 CallOnAllRemoteChildren(aWindow, [&stateInfo](BrowserParent* aBrowserParent) {
7560 Unused << aBrowserParent->SendSetKeyboardIndicators(
7561 stateInfo.mShowFocusRings);
7562 return CallState::Continue;
7563 });
7564 }
7565
7566 nsresult nsContentUtils::IPCTransferableToTransferable(
7567 const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
7568 nsIPrincipal* aRequestingPrincipal,
7569 const nsContentPolicyType& aContentPolicyType,
7570 nsITransferable* aTransferable, mozilla::dom::ContentParent* aContentParent,
7571 mozilla::dom::BrowserChild* aBrowserChild) {
7572 nsresult rv;
7573
7574 aTransferable->SetIsPrivateData(aIsPrivateData);
7575
7576 const nsTArray<IPCDataTransferItem>& items = aDataTransfer.items();
7577 for (const auto& item : items) {
7578 aTransferable->AddDataFlavor(item.flavor().get());
7579
7580 if (item.data().type() == IPCDataTransferData::TnsString) {
7581 nsCOMPtr<nsISupportsString> dataWrapper =
7582 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
7583 NS_ENSURE_SUCCESS(rv, rv);
7584
7585 const nsString& text = item.data().get_nsString();
7586 rv = dataWrapper->SetData(text);
7587 NS_ENSURE_SUCCESS(rv, rv);
7588
7589 rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper);
7590
7591 NS_ENSURE_SUCCESS(rv, rv);
7592 } else if (item.data().type() == IPCDataTransferData::TShmem) {
7593 if (nsContentUtils::IsFlavorImage(item.flavor())) {
7594 nsCOMPtr<imgIContainer> imageContainer;
7595 rv = nsContentUtils::DataTransferItemToImage(
7596 item, getter_AddRefs(imageContainer));
7597 NS_ENSURE_SUCCESS(rv, rv);
7598
7599 aTransferable->SetTransferData(item.flavor().get(), imageContainer);
7600 } else {
7601 nsCOMPtr<nsISupportsCString> dataWrapper =
7602 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
7603 NS_ENSURE_SUCCESS(rv, rv);
7604
7605 // The buffer contains the terminating null.
7606 Shmem itemData = item.data().get_Shmem();
7607 const nsDependentCSubstring text(itemData.get<char>(),
7608 itemData.Size<char>());
7609 rv = dataWrapper->SetData(text);
7610 NS_ENSURE_SUCCESS(rv, rv);
7611
7612 rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper);
7613
7614 NS_ENSURE_SUCCESS(rv, rv);
7615 }
7616
7617 if (aContentParent) {
7618 Unused << aContentParent->DeallocShmem(item.data().get_Shmem());
7619 } else if (aBrowserChild) {
7620 Unused << aBrowserChild->DeallocShmem(item.data().get_Shmem());
7621 }
7622 }
7623 }
7624
7625 aTransferable->SetRequestingPrincipal(aRequestingPrincipal);
7626 aTransferable->SetContentPolicyType(aContentPolicyType);
7627 return NS_OK;
7628 }
7629
7630 void nsContentUtils::TransferablesToIPCTransferables(
7631 nsIArray* aTransferables, nsTArray<IPCDataTransfer>& aIPC,
7632 bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
7633 mozilla::dom::ContentParent* aParent) {
7634 aIPC.Clear();
7635 if (aTransferables) {
7636 uint32_t transferableCount = 0;
7637 aTransferables->GetLength(&transferableCount);
7638 for (uint32_t i = 0; i < transferableCount; ++i) {
7639 IPCDataTransfer* dt = aIPC.AppendElement();
7640 nsCOMPtr<nsITransferable> transferable =
7641 do_QueryElementAt(aTransferables, i);
7642 TransferableToIPCTransferable(transferable, dt, aInSyncMessage, aChild,
7643 aParent);
7644 }
7645 }
7646 }
7647
7648 nsresult nsContentUtils::SlurpFileToString(nsIFile* aFile,
7649 nsACString& aString) {
7650 aString.Truncate();
7651
7652 nsCOMPtr<nsIURI> fileURI;
7653 nsresult rv = NS_NewFileURI(getter_AddRefs(fileURI), aFile);
7654 if (NS_FAILED(rv)) {
7655 return rv;
7656 }
7657
7658 nsCOMPtr<nsIChannel> channel;
7659 rv = NS_NewChannel(getter_AddRefs(channel), fileURI,
7660 nsContentUtils::GetSystemPrincipal(),
7661 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
7662 nsIContentPolicy::TYPE_OTHER);
7663 if (NS_FAILED(rv)) {
7664 return rv;
7665 }
7666
7667 nsCOMPtr<nsIInputStream> stream;
7668 rv = channel->Open(getter_AddRefs(stream));
7669 if (NS_FAILED(rv)) {
7670 return rv;
7671 }
7672
7673 rv = NS_ConsumeStream(stream, UINT32_MAX, aString);
7674 if (NS_FAILED(rv)) {
7675 return rv;
7676 }
7677
7678 rv = stream->Close();
7679 if (NS_FAILED(rv)) {
7680 return rv;
7681 }
7682
7683 return NS_OK;
7684 }
7685
7686 bool nsContentUtils::IsFileImage(nsIFile* aFile, nsACString& aType) {
7687 nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1");
7688 if (!mime) {
7689 return false;
7690 }
7691
7692 nsresult rv = mime->GetTypeFromFile(aFile, aType);
7693 if (NS_FAILED(rv)) {
7694 return false;
7695 }
7696
7697 return StringBeginsWith(aType, "image/"_ns);
7698 }
7699
7700 nsresult nsContentUtils::CalculateBufferSizeForImage(
7701 const uint32_t& aStride, const IntSize& aImageSize,
7702 const SurfaceFormat& aFormat, size_t* aMaxBufferSize,
7703 size_t* aUsedBufferSize) {
7704 CheckedInt32 requiredBytes =
7705 CheckedInt32(aStride) * CheckedInt32(aImageSize.height);
7706
7707 CheckedInt32 usedBytes =
7708 requiredBytes - aStride +
7709 (CheckedInt32(aImageSize.width) * BytesPerPixel(aFormat));
7710 if (!usedBytes.isValid()) {
7711 return NS_ERROR_FAILURE;
7712 }
7713
7714 MOZ_ASSERT(requiredBytes.isValid(), "usedBytes valid but not required?");
7715 *aMaxBufferSize = requiredBytes.value();
7716 *aUsedBufferSize = usedBytes.value();
7717 return NS_OK;
7718 }
7719
7720 nsresult nsContentUtils::DataTransferItemToImage(
7721 const IPCDataTransferItem& aItem, imgIContainer** aContainer) {
7722 MOZ_ASSERT(aItem.data().type() == IPCDataTransferData::TShmem);
7723 MOZ_ASSERT(IsFlavorImage(aItem.flavor()));
7724
7725 const IPCDataTransferImage& imageDetails = aItem.imageDetails();
7726 const IntSize size(imageDetails.width(), imageDetails.height());
7727 if (!size.width || !size.height) {
7728 return NS_ERROR_FAILURE;
7729 }
7730
7731 Shmem data = aItem.data().get_Shmem();
7732
7733 // Validate shared memory buffer size
7734 size_t imageBufLen = 0;
7735 size_t maxBufLen = 0;
7736 nsresult rv = CalculateBufferSizeForImage(imageDetails.stride(), size,
7737 imageDetails.format(), &maxBufLen,
7738 &imageBufLen);
7739 if (NS_FAILED(rv)) {
7740 return rv;
7741 }
7742 if (imageBufLen > data.Size<uint8_t>()) {
7743 return NS_ERROR_FAILURE;
7744 }
7745
7746 RefPtr<DataSourceSurface> image = CreateDataSourceSurfaceFromData(
7747 size, imageDetails.format(), data.get<uint8_t>(), imageDetails.stride());
7748
7749 RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size);
7750 nsCOMPtr<imgIContainer> imageContainer =
7751 image::ImageOps::CreateFromDrawable(drawable);
7752 imageContainer.forget(aContainer);
7753
7754 return NS_OK;
7755 }
7756
7757 bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
7758 return aFlavor.EqualsLiteral(kNativeImageMime) ||
7759 aFlavor.EqualsLiteral(kJPEGImageMime) ||
7760 aFlavor.EqualsLiteral(kJPGImageMime) ||
7761 aFlavor.EqualsLiteral(kPNGImageMime) ||
7762 aFlavor.EqualsLiteral(kGIFImageMime);
7763 }
7764
7765 static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
7766 mozilla::dom::ContentParent* aParent,
7767 const nsACString& aInput) {
7768 MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
7769
7770 IShmemAllocator* allocator = aChild ? static_cast<IShmemAllocator*>(aChild)
7771 : static_cast<IShmemAllocator*>(aParent);
7772
7773 Shmem result;
7774 if (!allocator->AllocShmem(aInput.Length(), SharedMemory::TYPE_BASIC,
7775 &result)) {
7776 return result;
7777 }
7778
7779 memcpy(result.get<char>(), aInput.BeginReading(), aInput.Length());
7780
7781 return result;
7782 }
7783
7784 void nsContentUtils::TransferableToIPCTransferable(
7785 nsITransferable* aTransferable, IPCDataTransfer* aIPCDataTransfer,
7786 bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
7787 mozilla::dom::ContentParent* aParent) {
7788 MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
7789
7790 if (aTransferable) {
7791 nsTArray<nsCString> flavorList;
7792 aTransferable->FlavorsTransferableCanExport(flavorList);
7793
7794 for (uint32_t j = 0; j < flavorList.Length(); ++j) {
7795 nsCString& flavorStr = flavorList[j];
7796 if (!flavorStr.Length()) {
7797 continue;
7798 }
7799
7800 nsCOMPtr<nsISupports> data;
7801 nsresult rv =
7802 aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data));
7803
7804 if (NS_FAILED(rv) || !data) {
7805 if (aInSyncMessage) {
7806 // Can't do anything.
7807 continue;
7808 }
7809
7810 // This is a hack to support kFilePromiseMime.
7811 // On Windows there just needs to be an entry for it,
7812 // and for OSX we need to create
7813 // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
7814 if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
7815 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7816 item->flavor() = flavorStr;
7817 item->data() = NS_ConvertUTF8toUTF16(flavorStr);
7818 continue;
7819 }
7820
7821 // Empty element, transfer only the flavor
7822 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7823 item->flavor() = flavorStr;
7824 item->data() = nsString();
7825 continue;
7826 }
7827
7828 if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(data)) {
7829 nsAutoString dataAsString;
7830 text->GetData(dataAsString);
7831 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7832 item->flavor() = flavorStr;
7833 item->data() = dataAsString;
7834 } else if (nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data)) {
7835 nsAutoCString dataAsString;
7836 ctext->GetData(dataAsString);
7837
7838 Shmem dataAsShmem = ConvertToShmem(aChild, aParent, dataAsString);
7839 if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
7840 continue;
7841 }
7842
7843 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7844 item->flavor() = flavorStr;
7845 item->data() = std::move(dataAsShmem);
7846 } else if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
7847 // Images to be pasted on the clipboard are nsIInputStreams
7848 nsCString imageData;
7849 NS_ConsumeStream(stream, UINT32_MAX, imageData);
7850
7851 Shmem imageDataShmem = ConvertToShmem(aChild, aParent, imageData);
7852 if (!imageDataShmem.IsReadable() || !imageDataShmem.Size<char>()) {
7853 continue;
7854 }
7855
7856 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7857 item->flavor() = flavorStr;
7858 item->data() = std::move(imageDataShmem);
7859 } else if (nsCOMPtr<imgIContainer> image = do_QueryInterface(data)) {
7860 // Images to be placed on the clipboard are imgIContainers.
7861 RefPtr<mozilla::gfx::SourceSurface> surface = image->GetFrame(
7862 imgIContainer::FRAME_CURRENT,
7863 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
7864 if (!surface) {
7865 continue;
7866 }
7867 RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
7868 surface->GetDataSurface();
7869 if (!dataSurface) {
7870 continue;
7871 }
7872 size_t length;
7873 int32_t stride;
7874 IShmemAllocator* allocator =
7875 aChild ? static_cast<IShmemAllocator*>(aChild)
7876 : static_cast<IShmemAllocator*>(aParent);
7877 Maybe<Shmem> surfaceData =
7878 GetSurfaceData(dataSurface, &length, &stride, allocator);
7879
7880 if (surfaceData.isNothing()) {
7881 continue;
7882 }
7883
7884 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7885 item->flavor() = flavorStr;
7886 // Turn item->data() into an nsCString prior to accessing it.
7887 item->data() = std::move(surfaceData.ref());
7888
7889 IPCDataTransferImage& imageDetails = item->imageDetails();
7890 mozilla::gfx::IntSize size = dataSurface->GetSize();
7891 imageDetails.width() = size.width;
7892 imageDetails.height() = size.height;
7893 imageDetails.stride() = stride;
7894 imageDetails.format() = dataSurface->GetFormat();
7895 } else {
7896 // Otherwise, handle this as a file.
7897 nsCOMPtr<BlobImpl> blobImpl;
7898 if (nsCOMPtr<nsIFile> file = do_QueryInterface(data)) {
7899 // If we can send this over as a blob, do so. Otherwise, we're
7900 // responding to a sync message and the child can't process the blob
7901 // constructor before processing our response, which would crash. In
7902 // that case, hope that the caller is nsClipboardProxy::GetData,
7903 // called from editor and send over images as raw data.
7904 if (aInSyncMessage) {
7905 nsAutoCString type;
7906 if (IsFileImage(file, type)) {
7907 nsAutoCString data;
7908 SlurpFileToString(file, data);
7909
7910 Shmem dataAsShmem = ConvertToShmem(aChild, aParent, data);
7911 if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
7912 continue;
7913 }
7914
7915 IPCDataTransferItem* item =
7916 aIPCDataTransfer->items().AppendElement();
7917 item->flavor() = type;
7918 item->data() = std::move(dataAsShmem);
7919 }
7920
7921 continue;
7922 }
7923
7924 if (aParent) {
7925 bool isDir = false;
7926 if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
7927 nsAutoString path;
7928 if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
7929 continue;
7930 }
7931
7932 RefPtr<FileSystemSecurity> fss =
7933 FileSystemSecurity::GetOrCreate();
7934 fss->GrantAccessToContentProcess(aParent->ChildID(), path);
7935 }
7936 }
7937
7938 blobImpl = new FileBlobImpl(file);
7939
7940 IgnoredErrorResult rv;
7941
7942 // Ensure that file data is cached no that the content process
7943 // has this data available to it when passed over:
7944 blobImpl->GetSize(rv);
7945 if (NS_WARN_IF(rv.Failed())) {
7946 continue;
7947 }
7948
7949 blobImpl->GetLastModified(rv);
7950 if (NS_WARN_IF(rv.Failed())) {
7951 continue;
7952 }
7953 } else {
7954 if (aInSyncMessage) {
7955 // Can't do anything.
7956 continue;
7957 }
7958 blobImpl = do_QueryInterface(data);
7959 }
7960 if (blobImpl) {
7961 IPCDataTransferData data;
7962 IPCBlob ipcBlob;
7963
7964 // If we failed to create the blob actor, then this blob probably
7965 // can't get the file size for the underlying file, ignore it for
7966 // now. TODO pass this through anyway.
7967 if (aChild) {
7968 nsresult rv = IPCBlobUtils::Serialize(blobImpl, aChild, ipcBlob);
7969 if (NS_WARN_IF(NS_FAILED(rv))) {
7970 continue;
7971 }
7972
7973 data = ipcBlob;
7974 } else if (aParent) {
7975 nsresult rv = IPCBlobUtils::Serialize(blobImpl, aParent, ipcBlob);
7976 if (NS_WARN_IF(NS_FAILED(rv))) {
7977 continue;
7978 }
7979
7980 data = ipcBlob;
7981 }
7982
7983 IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
7984 item->flavor() = flavorStr;
7985 item->data() = data;
7986 }
7987 }
7988 }
7989 }
7990 }
7991
7992 namespace {
7993 // The default type used for calling GetSurfaceData(). Gets surface data as
7994 // raw buffer.
7995 struct GetSurfaceDataRawBuffer {
7996 using ReturnType = mozilla::UniquePtr<char[]>;
7997 using BufferType = char*;
7998
7999 ReturnType Allocate(size_t aSize) { return ReturnType(new char[aSize]); }
8000
8001 static BufferType GetBuffer(const ReturnType& aReturnValue) {
8002 return aReturnValue.get();
8003 }
8004
8005 static ReturnType NullValue() { return ReturnType(); }
8006 };
8007
8008 // The type used for calling GetSurfaceData() that allocates and writes to
8009 // a shared memory buffer.
8010 struct GetSurfaceDataShmem {
8011 using ReturnType = Maybe<Shmem>;
8012 using BufferType = char*;
8013
8014 explicit GetSurfaceDataShmem(IShmemAllocator* aAllocator)
8015 : mAllocator(aAllocator) {}
8016
8017 ReturnType Allocate(size_t aSize) {
8018 Shmem shmem;
8019 if (!mAllocator->AllocShmem(aSize, SharedMemory::TYPE_BASIC, &shmem)) {
8020 return Nothing();
8021 }
8022
8023 return Some(shmem);
8024 }
8025
8026 static BufferType GetBuffer(const ReturnType& aReturnValue) {
8027 return aReturnValue.isSome() ? aReturnValue.ref().get<char>() : nullptr;
8028 }
8029
8030 static ReturnType NullValue() { return ReturnType(); }
8031
8032 private:
8033 IShmemAllocator* mAllocator;
8034 };
8035
8036 /*
8037 * Get the pixel data from the given source surface and return it as a buffer.
8038 * The length and stride will be assigned from the surface.
8039 */
8040 template <typename GetSurfaceDataContext = GetSurfaceDataRawBuffer>
8041 typename GetSurfaceDataContext::ReturnType GetSurfaceDataImpl(
8042 mozilla::gfx::DataSourceSurface* aSurface, size_t* aLength,
8043 int32_t* aStride,
8044 GetSurfaceDataContext aContext = GetSurfaceDataContext()) {
8045 mozilla::gfx::DataSourceSurface::MappedSurface map;
8046 if (!aSurface->Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map)) {
8047 return GetSurfaceDataContext::NullValue();
8048 }
8049
8050 size_t bufLen = 0;
8051 size_t maxBufLen = 0;
8052 nsresult rv = nsContentUtils::CalculateBufferSizeForImage(
8053 map.mStride, aSurface->GetSize(), aSurface->GetFormat(), &maxBufLen,
8054 &bufLen);
8055 if (NS_FAILED(rv)) {
8056 aSurface->Unmap();
8057 return GetSurfaceDataContext::NullValue();
8058 }
8059
8060 // nsDependentCString wants null-terminated string.
8061 typename GetSurfaceDataContext::ReturnType surfaceData =
8062 aContext.Allocate(maxBufLen + 1);
8063 if (GetSurfaceDataContext::GetBuffer(surfaceData)) {
8064 memcpy(GetSurfaceDataContext::GetBuffer(surfaceData),
8065 reinterpret_cast<char*>(map.mData), bufLen);
8066 memset(GetSurfaceDataContext::GetBuffer(surfaceData) + bufLen, 0,
8067 maxBufLen - bufLen + 1);
8068 }
8069
8070 *aLength = maxBufLen;
8071 *aStride = map.mStride;
8072
8073 aSurface->Unmap();
8074 return surfaceData;
8075 }
8076 } // Anonymous namespace.
8077
8078 mozilla::UniquePtr<char[]> nsContentUtils::GetSurfaceData(
8079 NotNull<mozilla::gfx::DataSourceSurface*> aSurface, size_t* aLength,
8080 int32_t* aStride) {
8081 return GetSurfaceDataImpl(aSurface, aLength, aStride);
8082 }
8083
8084 Maybe<Shmem> nsContentUtils::GetSurfaceData(
8085 mozilla::gfx::DataSourceSurface* aSurface, size_t* aLength,
8086 int32_t* aStride, IShmemAllocator* aAllocator) {
8087 return GetSurfaceDataImpl(aSurface, aLength, aStride,
8088 GetSurfaceDataShmem(aAllocator));
8089 }
8090
8091 mozilla::Modifiers nsContentUtils::GetWidgetModifiers(int32_t aModifiers) {
8092 Modifiers result = 0;
8093 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SHIFT) {
8094 result |= mozilla::MODIFIER_SHIFT;
8095 }
8096 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CONTROL) {
8097 result |= mozilla::MODIFIER_CONTROL;
8098 }
8099 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALT) {
8100 result |= mozilla::MODIFIER_ALT;
8101 }
8102 if (aModifiers & nsIDOMWindowUtils::MODIFIER_META) {
8103 result |= mozilla::MODIFIER_META;
8104 }
8105 if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALTGRAPH) {
8106 result |= mozilla::MODIFIER_ALTGRAPH;
8107 }
8108 if (aModifiers & nsIDOMWindowUtils::MODIFIER_CAPSLOCK) {
8109 result |= mozilla::MODIFIER_CAPSLOCK;
8110 }
8111 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FN) {
8112 result |= mozilla::MODIFIER_FN;
8113 }
8114 if (aModifiers & nsIDOMWindowUtils::MODIFIER_FNLOCK) {
8115 result |= mozilla::MODIFIER_FNLOCK;
8116 }
8117 if (aModifiers & nsIDOMWindowUtils::MODIFIER_NUMLOCK) {
8118 result |= mozilla::MODIFIER_NUMLOCK;
8119 }
8120 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SCROLLLOCK) {
8121 result |= mozilla::MODIFIER_SCROLLLOCK;
8122 }
8123 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOL) {
8124 result |= mozilla::MODIFIER_SYMBOL;
8125 }
8126 if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK) {
8127 result |= mozilla::MODIFIER_SYMBOLLOCK;
8128 }
8129 if (aModifiers & nsIDOMWindowUtils::MODIFIER_OS) {
8130 result |= mozilla::MODIFIER_OS;
8131 }
8132 return result;
8133 }
8134
8135 nsIWidget* nsContentUtils::GetWidget(PresShell* aPresShell, nsPoint* aOffset) {
8136 if (!aPresShell) {
8137 return nullptr;
8138 }
8139 nsIFrame* frame = aPresShell->GetRootFrame();
8140 if (!frame) {
8141 return nullptr;
8142 }
8143 return frame->GetView()->GetNearestWidget(aOffset);
8144 }
8145
8146 int16_t nsContentUtils::GetButtonsFlagForButton(int32_t aButton) {
8147 switch (aButton) {
8148 case -1:
8149 return MouseButtonsFlag::eNoButtons;
8150 case MouseButton::ePrimary:
8151 return MouseButtonsFlag::ePrimaryFlag;
8152 case MouseButton::eMiddle:
8153 return MouseButtonsFlag::eMiddleFlag;
8154 case MouseButton::eSecondary:
8155 return MouseButtonsFlag::eSecondaryFlag;
8156 case 3:
8157 return MouseButtonsFlag::e4thFlag;
8158 case 4:
8159 return MouseButtonsFlag::e5thFlag;
8160 case MouseButton::eEraser:
8161 return MouseButtonsFlag::eEraserFlag;
8162 default:
8163 NS_ERROR("Button not known.");
8164 return 0;
8165 }
8166 }
8167
8168 LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(
8169 const CSSPoint& aPoint, const nsPoint& aOffset,
8170 nsPresContext* aPresContext) {
8171 nsPoint layoutRelative = CSSPoint::ToAppUnits(aPoint) + aOffset;
8172 nsPoint visualRelative =
8173 ViewportUtils::LayoutToVisual(layoutRelative, aPresContext->PresShell());
8174 return LayoutDeviceIntPoint::FromAppUnitsRounded(
8175 visualRelative, aPresContext->AppUnitsPerDevPixel());
8176 }
8177
8178 nsView* nsContentUtils::GetViewToDispatchEvent(nsPresContext* aPresContext,
8179 PresShell** aPresShell) {
8180 if (!aPresContext || !aPresShell) {
8181 return nullptr;
8182 }
8183 RefPtr<PresShell> presShell = aPresContext->PresShell();
8184 if (NS_WARN_IF(!presShell)) {
8185 *aPresShell = nullptr;
8186 return nullptr;
8187 }
8188 nsViewManager* viewManager = presShell->GetViewManager();
8189 if (!viewManager) {
8190 presShell.forget(aPresShell); // XXX Is this intentional?
8191 return nullptr;
8192 }
8193 presShell.forget(aPresShell);
8194 return viewManager->GetRootView();
8195 }
8196
8197 nsresult nsContentUtils::SendMouseEvent(
8198 mozilla::PresShell* aPresShell, const nsAString& aType, float aX, float aY,
8199 int32_t aButton, int32_t aButtons, int32_t aClickCount, int32_t aModifiers,
8200 bool aIgnoreRootScrollFrame, float aPressure,
8201 unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
8202 PreventDefaultResult* aPreventDefault, bool aIsDOMEventSynthesized,
8203 bool aIsWidgetEventSynthesized) {
8204 nsPoint offset;
8205 nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
8206 if (!widget) return NS_ERROR_FAILURE;
8207
8208 EventMessage msg;
8209 Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
8210 bool contextMenuKey = false;
8211 if (aType.EqualsLiteral("mousedown")) {
8212 msg = eMouseDown;
8213 } else if (aType.EqualsLiteral("mouseup")) {
8214 msg = eMouseUp;
8215 } else if (aType.EqualsLiteral("mousemove")) {
8216 msg = eMouseMove;
8217 } else if (aType.EqualsLiteral("mouseover")) {
8218 msg = eMouseEnterIntoWidget;
8219 } else if (aType.EqualsLiteral("mouseout")) {
8220 msg = eMouseExitFromWidget;
8221 exitFrom = Some(WidgetMouseEvent::ePlatformChild);
8222 } else if (aType.EqualsLiteral("mousecancel")) {
8223 msg = eMouseExitFromWidget;
8224 exitFrom = Some(XRE_IsParentProcess() ? WidgetMouseEvent::ePlatformTopLevel
8225 : WidgetMouseEvent::ePuppet);
8226 } else if (aType.EqualsLiteral("mouselongtap")) {
8227 msg = eMouseLongTap;
8228 } else if (aType.EqualsLiteral("contextmenu")) {
8229 msg = eContextMenu;
8230 contextMenuKey = (aButton == 0);
8231 } else if (aType.EqualsLiteral("MozMouseHittest")) {
8232 msg = eMouseHitTest;
8233 } else {
8234 return NS_ERROR_FAILURE;
8235 }
8236
8237 if (aInputSourceArg == MouseEvent_Binding::MOZ_SOURCE_UNKNOWN) {
8238 aInputSourceArg = MouseEvent_Binding::MOZ_SOURCE_MOUSE;
8239 }
8240
8241 WidgetMouseEvent event(true, msg, widget,
8242 aIsWidgetEventSynthesized
8243 ? WidgetMouseEvent::eSynthesized
8244 : WidgetMouseEvent::eReal,
8245 contextMenuKey ? WidgetMouseEvent::eContextMenuKey
8246 : WidgetMouseEvent::eNormal);
8247 event.pointerId = aIdentifier;
8248 event.mModifiers = GetWidgetModifiers(aModifiers);
8249 event.mButton = aButton;
8250 event.mButtons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED
8251 ? aButtons
8252 : msg == eMouseUp ? 0
8253 : GetButtonsFlagForButton(aButton);
8254 event.mPressure = aPressure;
8255 event.mInputSource = aInputSourceArg;
8256 event.mClickCount = aClickCount;
8257 event.mTime = PR_IntervalNow();
8258 event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
8259 event.mExitFrom = exitFrom;
8260
8261 nsPresContext* presContext = aPresShell->GetPresContext();
8262 if (!presContext) return NS_ERROR_FAILURE;
8263
8264 event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
8265 event.mIgnoreRootScrollFrame = aIgnoreRootScrollFrame;
8266
8267 nsEventStatus status = nsEventStatus_eIgnore;
8268 if (aToWindow) {
8269 RefPtr<PresShell> presShell;
8270 nsView* view =
8271 GetViewToDispatchEvent(presContext, getter_AddRefs(presShell));
8272 if (!presShell || !view) {
8273 return NS_ERROR_FAILURE;
8274 }
8275 return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
8276 }
8277 if (StaticPrefs::test_events_async_enabled()) {
8278 status = widget->DispatchInputEvent(&event).mContentStatus;
8279 } else {
8280 nsresult rv = widget->DispatchEvent(&event, status);
8281 NS_ENSURE_SUCCESS(rv, rv);
8282 }
8283 if (aPreventDefault) {
8284 if (status == nsEventStatus_eConsumeNoDefault) {
8285 if (event.mFlags.mDefaultPreventedByContent) {
8286 *aPreventDefault = PreventDefaultResult::ByContent;
8287 } else {
8288 *aPreventDefault = PreventDefaultResult::ByChrome;
8289 }
8290 } else {
8291 *aPreventDefault = PreventDefaultResult::No;
8292 }
8293 }
8294
8295 return NS_OK;
8296 }
8297
8298 /* static */
8299 void nsContentUtils::FirePageHideEventForFrameLoaderSwap(
8300 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8301 bool aOnlySystemGroup) {
8302 MOZ_DIAGNOSTIC_ASSERT(aItem);
8303 MOZ_DIAGNOSTIC_ASSERT(aChromeEventHandler);
8304
8305 RefPtr<Document> doc = aItem->GetDocument();
8306 NS_ASSERTION(doc, "What happened here?");
8307 doc->OnPageHide(true, aChromeEventHandler, aOnlySystemGroup);
8308
8309 int32_t childCount = 0;
8310 aItem->GetInProcessChildCount(&childCount);
8311 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8312 kids.AppendElements(childCount);
8313 for (int32_t i = 0; i < childCount; ++i) {
8314 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8315 }
8316
8317 for (uint32_t i = 0; i < kids.Length(); ++i) {
8318 if (kids[i]) {
8319 FirePageHideEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8320 aOnlySystemGroup);
8321 }
8322 }
8323 }
8324
8325 // The pageshow event is fired for a given document only if IsShowing() returns
8326 // the same thing as aFireIfShowing. This gives us a way to fire pageshow only
8327 // on documents that are still loading or only on documents that are already
8328 // loaded.
8329 /* static */
8330 void nsContentUtils::FirePageShowEventForFrameLoaderSwap(
8331 nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
8332 bool aFireIfShowing, bool aOnlySystemGroup) {
8333 int32_t childCount = 0;
8334 aItem->GetInProcessChildCount(&childCount);
8335 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
8336 kids.AppendElements(childCount);
8337 for (int32_t i = 0; i < childCount; ++i) {
8338 aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
8339 }
8340
8341 for (uint32_t i = 0; i < kids.Length(); ++i) {
8342 if (kids[i]) {
8343 FirePageShowEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
8344 aFireIfShowing, aOnlySystemGroup);
8345 }
8346 }
8347
8348 RefPtr<Document> doc = aItem->GetDocument();
8349 NS_ASSERTION(doc, "What happened here?");
8350 if (doc->IsShowing() == aFireIfShowing) {
8351 doc->OnPageShow(true, aChromeEventHandler, aOnlySystemGroup);
8352 }
8353 }
8354
8355 /* static */
8356 already_AddRefed<nsPIWindowRoot> nsContentUtils::GetWindowRoot(Document* aDoc) {
8357 if (aDoc) {
8358 if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
8359 return win->GetTopWindowRoot();
8360 }
8361 }
8362 return nullptr;
8363 }
8364
8365 /* static */
8366 bool nsContentUtils::IsPreloadType(nsContentPolicyType aType) {
8367 return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
8368 aType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD ||
8369 aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
8370 aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD ||
8371 aType == nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD ||
8372 aType == nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD);
8373 }
8374
8375 /* static */
8376 bool nsContentUtils::IsUpgradableDisplayType(ExtContentPolicyType aType) {
8377 MOZ_ASSERT(NS_IsMainThread());
8378 return (aType == ExtContentPolicy::TYPE_IMAGE ||
8379 aType == ExtContentPolicy::TYPE_MEDIA);
8380 }
8381
8382 // static
8383 ReferrerPolicy nsContentUtils::GetReferrerPolicyFromChannel(
8384 nsIChannel* aChannel) {
8385 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
8386 if (!httpChannel) {
8387 return ReferrerPolicy::_empty;
8388 }
8389
8390 nsresult rv;
8391 nsAutoCString headerValue;
8392 rv = httpChannel->GetResponseHeader("referrer-policy"_ns, headerValue);
8393 if (NS_FAILED(rv) || headerValue.IsEmpty()) {
8394 return ReferrerPolicy::_empty;
8395 }
8396
8397 return ReferrerInfo::ReferrerPolicyFromHeaderString(
8398 NS_ConvertUTF8toUTF16(headerValue));
8399 }
8400
8401 // static
8402 bool nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel) {
8403 nsLoadFlags loadFlags = 0;
8404 aChannel->GetLoadFlags(&loadFlags);
8405 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
8406 return true;
8407 }
8408
8409 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
8410 nsContentPolicyType type = loadInfo->InternalContentPolicyType();
8411 return IsNonSubresourceInternalPolicyType(type);
8412 }
8413
8414 // static
8415 bool nsContentUtils::IsNonSubresourceInternalPolicyType(
8416 nsContentPolicyType aType) {
8417 return aType == nsIContentPolicy::TYPE_DOCUMENT ||
8418 aType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
8419 aType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
8420 aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
8421 aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
8422 }
8423
8424 // static public
8425 bool nsContentUtils::IsThirdPartyWindowOrChannel(nsPIDOMWindowInner* aWindow,
8426 nsIChannel* aChannel,
8427 nsIURI* aURI) {
8428 MOZ_ASSERT(!aWindow || !aChannel,
8429 "A window and channel should not both be provided.");
8430
8431 ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
8432 if (!thirdPartyUtil) {
8433 return false;
8434 }
8435
8436 // In the absence of a window or channel, we assume that we are first-party.
8437 bool thirdParty = false;
8438
8439 if (aWindow) {
8440 nsresult rv = thirdPartyUtil->IsThirdPartyWindow(aWindow->GetOuterWindow(),
8441 aURI, &thirdParty);
8442 if (NS_FAILED(rv)) {
8443 // Ideally we would do something similar to the channel code path here,
8444 // but existing code depends on this behaviour.
8445 return false;
8446 }
8447 }
8448
8449 if (aChannel) {
8450 // Note, we must call IsThirdPartyChannel() here and not just try to
8451 // use nsILoadInfo.isThirdPartyContext. That nsILoadInfo property only
8452 // indicates if the parent loading window is third party or not. We
8453 // want to check the channel URI against the loading principal as well.
8454 nsresult rv =
8455 thirdPartyUtil->IsThirdPartyChannel(aChannel, nullptr, &thirdParty);
8456 if (NS_FAILED(rv)) {
8457 // Assume third-party in case of failure
8458 thirdParty = true;
8459 }
8460
8461 // We check isThirdPartyWindow to expand the list of domains that are
8462 // considered first party (e.g., if facebook.com includes an iframe from
8463 // fatratgames.com, all subsources included in that iframe are considered
8464 // third-party with isThirdPartyChannel, even if they are not third-party
8465 // w.r.t. facebook.com), and isThirdPartyChannel to prevent top-level
8466 // navigations from being detected as third-party.
8467 bool isThirdPartyWindow = true;
8468 nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
8469 if (NS_SUCCEEDED(rv) && chan) {
8470 nsCOMPtr<nsIURI> topWinURI;
8471 rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
8472 if (NS_SUCCEEDED(rv) && topWinURI) {
8473 rv = thirdPartyUtil->IsThirdPartyURI(aURI, topWinURI,
8474 &isThirdPartyWindow);
8475 if (NS_SUCCEEDED(rv)) {
8476 thirdParty = thirdParty && isThirdPartyWindow;
8477 }
8478 }
8479 }
8480 }
8481
8482 return thirdParty;
8483 }
8484
8485 // static public
8486 bool nsContentUtils::IsThirdPartyTrackingResourceWindow(
8487 nsPIDOMWindowInner* aWindow) {
8488 MOZ_ASSERT(aWindow);
8489
8490 Document* document = aWindow->GetExtantDoc();
8491 if (!document) {
8492 return false;
8493 }
8494
8495 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8496 do_QueryInterface(document->GetChannel());
8497 if (!classifiedChannel) {
8498 return false;
8499 }
8500
8501 return classifiedChannel->IsThirdPartyTrackingResource();
8502 }
8503
8504 // static public
8505 bool nsContentUtils::IsFirstPartyTrackingResourceWindow(
8506 nsPIDOMWindowInner* aWindow) {
8507 MOZ_ASSERT(aWindow);
8508
8509 Document* document = aWindow->GetExtantDoc();
8510 if (!document) {
8511 return false;
8512 }
8513
8514 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
8515 do_QueryInterface(document->GetChannel());
8516 if (!classifiedChannel) {
8517 return false;
8518 }
8519
8520 uint32_t classificationFlags =
8521 classifiedChannel->GetFirstPartyClassificationFlags();
8522
8523 return mozilla::net::UrlClassifierCommon::IsTrackingClassificationFlag(
8524 classificationFlags);
8525 }
8526
8527 namespace {
8528
8529 // We put StringBuilder in the anonymous namespace to prevent anything outside
8530 // this file from accidentally being linked against it.
8531 class BulkAppender {
8532 typedef typename nsAString::size_type size_type;
8533
8534 public:
8535 explicit BulkAppender(BulkWriteHandle<char16_t>&& aHandle)
8536 : mHandle(std::move(aHandle)), mPosition(0) {}
8537 ~BulkAppender() = default;
8538
8539 template <int N>
8540 void AppendLiteral(const char16_t (&aStr)[N]) {
8541 size_t len = N - 1;
8542 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8543 memcpy(mHandle.Elements() + mPosition, aStr, len * sizeof(char16_t));
8544 mPosition += len;
8545 }
8546
8547 void Append(Span<const char16_t> aStr) {
8548 size_t len = aStr.Length();
8549 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8550 // Both mHandle.Elements() and aStr.Elements() are guaranteed
8551 // to be non-null (by the string implementation and by Span,
8552 // respectively), so not checking the pointers for null before
8553 // memcpy does not lead to UB even if len was zero.
8554 memcpy(mHandle.Elements() + mPosition, aStr.Elements(),
8555 len * sizeof(char16_t));
8556 mPosition += len;
8557 }
8558
8559 void Append(Span<const char> aStr) {
8560 size_t len = aStr.Length();
8561 MOZ_ASSERT(mPosition + len <= mHandle.Length());
8562 ConvertLatin1toUtf16(aStr, mHandle.AsSpan().From(mPosition));
8563 mPosition += len;
8564 }
8565
8566 void Finish() { mHandle.Finish(mPosition, false); }
8567
8568 private:
8569 mozilla::BulkWriteHandle<char16_t> mHandle;
8570 size_type mPosition;
8571 };
8572
8573 class StringBuilder {
8574 private:
8575 // Try to keep the size of StringBuilder close to a jemalloc bucket size.
8576 static const uint32_t STRING_BUFFER_UNITS = 1020;
8577 class Unit {
8578 public:
8579 Unit() : mAtom(nullptr), mType(eUnknown), mLength(0) {
8580 MOZ_COUNT_CTOR(StringBuilder::Unit);
8581 }
8582 ~Unit() {
8583 if (mType == eString || mType == eStringWithEncode) {
8584 delete mString;
8585 }
8586 MOZ_COUNT_DTOR(StringBuilder::Unit);
8587 }
8588
8589 enum Type {
8590 eUnknown,
8591 eAtom,
8592 eString,
8593 eStringWithEncode,
8594 eLiteral,
8595 eTextFragment,
8596 eTextFragmentWithEncode,
8597 };
8598
8599 union {
8600 nsAtom* mAtom;
8601 const char16_t* mLiteral;
8602 nsAutoString* mString;
8603 const nsTextFragment* mTextFragment;
8604 };
8605 Type mType;
8606 uint32_t mLength;
8607 };
8608
8609 public:
8610 StringBuilder() : mLast(this), mLength(0) { MOZ_COUNT_CTOR(StringBuilder); }
8611
8612 MOZ_COUNTED_DTOR(StringBuilder)
8613
8614 void Append(nsAtom* aAtom) {
8615 Unit* u = AddUnit();
8616 u->mAtom = aAtom;
8617 u->mType = Unit::eAtom;
8618 uint32_t len = aAtom->GetLength();
8619 u->mLength = len;
8620 mLength += len;
8621 }
8622
8623 template <int N>
8624 void Append(const char16_t (&aLiteral)[N]) {
8625 Unit* u = AddUnit();
8626 u->mLiteral = aLiteral;
8627 u->mType = Unit::eLiteral;
8628 uint32_t len = N - 1;
8629 u->mLength = len;
8630 mLength += len;
8631 }
8632
8633 void Append(const nsAString& aString) {
8634 Unit* u = AddUnit();
8635 u->mString = new nsAutoString(aString);
8636 u->mType = Unit::eString;
8637 uint32_t len = aString.Length();
8638 u->mLength = len;
8639 mLength += len;
8640 }
8641
8642 void Append(nsAutoString* aString) {
8643 Unit* u = AddUnit();
8644 u->mString = aString;
8645 u->mType = Unit::eString;
8646 uint32_t len = aString->Length();
8647 u->mLength = len;
8648 mLength += len;
8649 }
8650
8651 void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen) {
8652 Unit* u = AddUnit();
8653 u->mString = aString;
8654 u->mType = Unit::eStringWithEncode;
8655 u->mLength = aLen;
8656 mLength += aLen;
8657 }
8658
8659 void Append(const nsTextFragment* aTextFragment) {
8660 Unit* u = AddUnit();
8661 u->mTextFragment = aTextFragment;
8662 u->mType = Unit::eTextFragment;
8663 uint32_t len = aTextFragment->GetLength();
8664 u->mLength = len;
8665 mLength += len;
8666 }
8667
8668 void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen) {
8669 Unit* u = AddUnit();
8670 u->mTextFragment = aTextFragment;
8671 u->mType = Unit::eTextFragmentWithEncode;
8672 u->mLength = aLen;
8673 mLength += aLen;
8674 }
8675
8676 bool ToString(nsAString& aOut) {
8677 if (!mLength.isValid()) {
8678 return false;
8679 }
8680 auto appenderOrErr = aOut.BulkWrite(mLength.value(), 0, true);
8681 if (appenderOrErr.isErr()) {
8682 return false;
8683 }
8684
8685 BulkAppender appender{appenderOrErr.unwrap()};
8686
8687 for (StringBuilder* current = this; current;
8688 current = current->mNext.get()) {
8689 uint32_t len = current->mUnits.Length();
8690 for (uint32_t i = 0; i < len; ++i) {
8691 Unit& u = current->mUnits[i];
8692 switch (u.mType) {
8693 case Unit::eAtom:
8694 appender.Append(*(u.mAtom));
8695 break;
8696 case Unit::eString:
8697 appender.Append(*(u.mString));
8698 break;
8699 case Unit::eStringWithEncode:
8700 EncodeAttrString(*(u.mString), appender);
8701 break;
8702 case Unit::eLiteral:
8703 appender.Append(Span(u.mLiteral, u.mLength));
8704 break;
8705 case Unit::eTextFragment:
8706 if (u.mTextFragment->Is2b()) {
8707 appender.Append(
8708 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()));
8709 } else {
8710 appender.Append(
8711 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()));
8712 }
8713 break;
8714 case Unit::eTextFragmentWithEncode:
8715 if (u.mTextFragment->Is2b()) {
8716 EncodeTextFragment(
8717 Span(u.mTextFragment->Get2b(), u.mTextFragment->GetLength()),
8718 appender);
8719 } else {
8720 EncodeTextFragment(
8721 Span(u.mTextFragment->Get1b(), u.mTextFragment->GetLength()),
8722 appender);
8723 }
8724 break;
8725 default:
8726 MOZ_CRASH("Unknown unit type?");
8727 }
8728 }
8729 }
8730 appender.Finish();
8731 return true;
8732 }
8733
8734 private:
8735 Unit* AddUnit() {
8736 if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
8737 new StringBuilder(this);
8738 }
8739 return mLast->mUnits.AppendElement();
8740 }
8741
8742 explicit StringBuilder(StringBuilder* aFirst) : mLast(nullptr), mLength(0) {
8743 MOZ_COUNT_CTOR(StringBuilder);
8744 aFirst->mLast->mNext = WrapUnique(this);
8745 aFirst->mLast = this;
8746 }
8747
8748 void EncodeAttrString(Span<const char16_t> aStr, BulkAppender& aAppender) {
8749 size_t flushedUntil = 0;
8750 size_t currentPosition = 0;
8751 for (char16_t c : aStr) {
8752 switch (c) {
8753 case '"':
8754 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8755 aAppender.AppendLiteral(u""");
8756 flushedUntil = currentPosition + 1;
8757 break;
8758 case '&':
8759 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8760 aAppender.AppendLiteral(u"&");
8761 flushedUntil = currentPosition + 1;
8762 break;
8763 case 0x00A0:
8764 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8765 aAppender.AppendLiteral(u" ");
8766 flushedUntil = currentPosition + 1;
8767 break;
8768 default:
8769 break;
8770 }
8771 currentPosition++;
8772 }
8773 if (currentPosition > flushedUntil) {
8774 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8775 }
8776 }
8777
8778 template <class T>
8779 void EncodeTextFragment(Span<const T> aStr, BulkAppender& aAppender) {
8780 size_t flushedUntil = 0;
8781 size_t currentPosition = 0;
8782 for (T c : aStr) {
8783 switch (c) {
8784 case '<':
8785 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8786 aAppender.AppendLiteral(u"<");
8787 flushedUntil = currentPosition + 1;
8788 break;
8789 case '>':
8790 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8791 aAppender.AppendLiteral(u">");
8792 flushedUntil = currentPosition + 1;
8793 break;
8794 case '&':
8795 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8796 aAppender.AppendLiteral(u"&");
8797 flushedUntil = currentPosition + 1;
8798 break;
8799 case T(0xA0):
8800 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8801 aAppender.AppendLiteral(u" ");
8802 flushedUntil = currentPosition + 1;
8803 break;
8804 default:
8805 break;
8806 }
8807 currentPosition++;
8808 }
8809 if (currentPosition > flushedUntil) {
8810 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
8811 }
8812 }
8813
8814 AutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
8815 mozilla::UniquePtr<StringBuilder> mNext;
8816 StringBuilder* mLast;
8817 // mLength is used only in the first StringBuilder object in the linked list.
8818 CheckedInt<uint32_t> mLength;
8819 };
8820
8821 } // namespace
8822
8823 static void AppendEncodedCharacters(const nsTextFragment* aText,
8824 StringBuilder& aBuilder) {
8825 uint32_t extraSpaceNeeded = 0;
8826 uint32_t len = aText->GetLength();
8827 if (aText->Is2b()) {
8828 const char16_t* data = aText->Get2b();
8829 for (uint32_t i = 0; i < len; ++i) {
8830 const char16_t c = data[i];
8831 switch (c) {
8832 case '<':
8833 extraSpaceNeeded += ArrayLength("<") - 2;
8834 break;
8835 case '>':
8836 extraSpaceNeeded += ArrayLength(">") - 2;
8837 break;
8838 case '&':
8839 extraSpaceNeeded += ArrayLength("&") - 2;
8840 break;
8841 case 0x00A0:
8842 extraSpaceNeeded += ArrayLength(" ") - 2;
8843 break;
8844 default:
8845 break;
8846 }
8847 }
8848 } else {
8849 const char* data = aText->Get1b();
8850 for (uint32_t i = 0; i < len; ++i) {
8851 const unsigned char c = data[i];
8852 switch (c) {
8853 case '<':
8854 extraSpaceNeeded += ArrayLength("<") - 2;
8855 break;
8856 case '>':
8857 extraSpaceNeeded += ArrayLength(">") - 2;
8858 break;
8859 case '&':
8860 extraSpaceNeeded += ArrayLength("&") - 2;
8861 break;
8862 case 0x00A0:
8863 extraSpaceNeeded += ArrayLength(" ") - 2;
8864 break;
8865 default:
8866 break;
8867 }
8868 }
8869 }
8870
8871 if (extraSpaceNeeded) {
8872 aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
8873 } else {
8874 aBuilder.Append(aText);
8875 }
8876 }
8877
8878 static void AppendEncodedAttributeValue(nsAutoString* aValue,
8879 StringBuilder& aBuilder) {
8880 const char16_t* c = aValue->BeginReading();
8881 const char16_t* end = aValue->EndReading();
8882
8883 uint32_t extraSpaceNeeded = 0;
8884 while (c < end) {
8885 switch (*c) {
8886 case '"':
8887 extraSpaceNeeded += ArrayLength(""") - 2;
8888 break;
8889 case '&':
8890 extraSpaceNeeded += ArrayLength("&") - 2;
8891 break;
8892 case 0x00A0:
8893 extraSpaceNeeded += ArrayLength(" ") - 2;
8894 break;
8895 default:
8896 break;
8897 }
8898 ++c;
8899 }
8900
8901 if (extraSpaceNeeded) {
8902 aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
8903 } else {
8904 aBuilder.Append(aValue);
8905 }
8906 }
8907
8908 static void StartElement(Element* aContent, StringBuilder& aBuilder) {
8909 nsAtom* localName = aContent->NodeInfo()->NameAtom();
8910 int32_t tagNS = aContent->GetNameSpaceID();
8911
8912 aBuilder.Append(u"<");
8913 if (aContent->IsHTMLElement() || aContent->IsSVGElement() ||
8914 aContent->IsMathMLElement()) {
8915 aBuilder.Append(localName);
8916 } else {
8917 aBuilder.Append(aContent->NodeName());
8918 }
8919
8920 CustomElementData* ceData = aContent->GetCustomElementData();
8921 if (ceData) {
8922 nsAtom* isAttr = ceData->GetIs(aContent);
8923 if (isAttr && !aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
8924 aBuilder.Append(uR"( is=")");
8925 aBuilder.Append(nsDependentAtomString(isAttr));
8926 aBuilder.Append(uR"(")");
8927 }
8928 }
8929
8930 int32_t count = aContent->GetAttrCount();
8931 for (int32_t i = 0; i < count; i++) {
8932 const nsAttrName* name = aContent->GetAttrNameAt(i);
8933 int32_t attNs = name->NamespaceID();
8934 nsAtom* attName = name->LocalName();
8935
8936 // Filter out any attribute starting with [-|_]moz
8937 nsDependentAtomString attrNameStr(attName);
8938 if (StringBeginsWith(attrNameStr, u"_moz"_ns) ||
8939 StringBeginsWith(attrNameStr, u"-moz"_ns)) {
8940 continue;
8941 }
8942
8943 auto* attValue = new nsAutoString();
8944 aContent->GetAttr(attNs, attName, *attValue);
8945
8946 // Filter out special case of <br type="_moz*"> used by the editor.
8947 // Bug 16988. Yuck.
8948 if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
8949 attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
8950 StringBeginsWith(*attValue, u"_moz"_ns)) {
8951 delete attValue;
8952 continue;
8953 }
8954
8955 aBuilder.Append(u" ");
8956
8957 if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
8958 (attNs == kNameSpaceID_XMLNS && attName == nsGkAtoms::xmlns)) {
8959 // Nothing else required
8960 } else if (attNs == kNameSpaceID_XML) {
8961 aBuilder.Append(u"xml:");
8962 } else if (attNs == kNameSpaceID_XMLNS) {
8963 aBuilder.Append(u"xmlns:");
8964 } else if (attNs == kNameSpaceID_XLink) {
8965 aBuilder.Append(u"xlink:");
8966 } else {
8967 nsAtom* prefix = name->GetPrefix();
8968 if (prefix) {
8969 aBuilder.Append(prefix);
8970 aBuilder.Append(u":");
8971 }
8972 }
8973
8974 aBuilder.Append(attName);
8975 aBuilder.Append(uR"(=")");
8976 AppendEncodedAttributeValue(attValue, aBuilder);
8977 aBuilder.Append(uR"(")");
8978 }
8979
8980 aBuilder.Append(u">");
8981
8982 /*
8983 // Per HTML spec we should append one \n if the first child of
8984 // pre/textarea/listing is a textnode and starts with a \n.
8985 // But because browsers haven't traditionally had that behavior,
8986 // we're not changing our behavior either - yet.
8987 if (aContent->IsHTMLElement()) {
8988 if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
8989 localName == nsGkAtoms::listing) {
8990 nsIContent* fc = aContent->GetFirstChild();
8991 if (fc &&
8992 (fc->NodeType() == nsINode::TEXT_NODE ||
8993 fc->NodeType() == nsINode::CDATA_SECTION_NODE)) {
8994 const nsTextFragment* text = fc->GetText();
8995 if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
8996 aBuilder.Append("\n");
8997 }
8998 }
8999 }
9000 }*/
9001 }
9002
9003 static inline bool ShouldEscape(nsIContent* aParent) {
9004 if (!aParent || !aParent->IsHTMLElement()) {
9005 return true;
9006 }
9007
9008 static const nsAtom* nonEscapingElements[] = {
9009 nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
9010 nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
9011 nsGkAtoms::plaintext, nsGkAtoms::noscript};
9012 static mozilla::BitBloomFilter<12, nsAtom> sFilter;
9013 static bool sInitialized = false;
9014 if (!sInitialized) {
9015 sInitialized = true;
9016 for (auto& nonEscapingElement : nonEscapingElements) {
9017 sFilter.add(nonEscapingElement);
9018 }
9019 }
9020
9021 nsAtom* tag = aParent->NodeInfo()->NameAtom();
9022 if (sFilter.mightContain(tag)) {
9023 for (auto& nonEscapingElement : nonEscapingElements) {
9024 if (tag == nonEscapingElement) {
9025 if (MOZ_UNLIKELY(tag == nsGkAtoms::noscript) &&
9026 MOZ_UNLIKELY(!aParent->OwnerDoc()->IsScriptEnabled())) {
9027 return true;
9028 }
9029 return false;
9030 }
9031 }
9032 }
9033 return true;
9034 }
9035
9036 static inline bool IsVoidTag(Element* aElement) {
9037 if (!aElement->IsHTMLElement()) {
9038 return false;
9039 }
9040 return FragmentOrElement::IsHTMLVoid(aElement->NodeInfo()->NameAtom());
9041 }
9042
9043 bool nsContentUtils::SerializeNodeToMarkup(nsINode* aRoot,
9044 bool aDescendentsOnly,
9045 nsAString& aOut) {
9046 // If you pass in a DOCUMENT_NODE, you must pass aDescendentsOnly as true
9047 MOZ_ASSERT(aDescendentsOnly || aRoot->NodeType() != nsINode::DOCUMENT_NODE);
9048
9049 nsINode* current =
9050 aDescendentsOnly ? aRoot->GetFirstChildOfTemplateOrNode() : aRoot;
9051
9052 if (!current) {
9053 return true;
9054 }
9055
9056 StringBuilder builder;
9057 nsIContent* next;
9058 while (true) {
9059 bool isVoid = false;
9060 switch (current->NodeType()) {
9061 case nsINode::ELEMENT_NODE: {
9062 Element* elem = current->AsElement();
9063 StartElement(elem, builder);
9064 isVoid = IsVoidTag(elem);
9065 if (!isVoid && (next = current->GetFirstChildOfTemplateOrNode())) {
9066 current = next;
9067 continue;
9068 }
9069 break;
9070 }
9071
9072 case nsINode::TEXT_NODE:
9073 case nsINode::CDATA_SECTION_NODE: {
9074 const nsTextFragment* text = ¤t->AsText()->TextFragment();
9075 nsIContent* parent = current->GetParent();
9076 if (ShouldEscape(parent)) {
9077 AppendEncodedCharacters(text, builder);
9078 } else {
9079 builder.Append(text);
9080 }
9081 break;
9082 }
9083
9084 case nsINode::COMMENT_NODE: {
9085 builder.Append(u"<!--");
9086 builder.Append(static_cast<nsIContent*>(current)->GetText());
9087 builder.Append(u"-->");
9088 break;
9089 }
9090
9091 case nsINode::DOCUMENT_TYPE_NODE: {
9092 builder.Append(u"<!DOCTYPE ");
9093 builder.Append(current->NodeName());
9094 builder.Append(u">");
9095 break;
9096 }
9097
9098 case nsINode::PROCESSING_INSTRUCTION_NODE: {
9099 builder.Append(u"<?");
9100 builder.Append(current->NodeName());
9101 builder.Append(u" ");
9102 builder.Append(static_cast<nsIContent*>(current)->GetText());
9103 builder.Append(u">");
9104 break;
9105 }
9106 }
9107
9108 while (true) {
9109 if (!isVoid && current->NodeType() == nsINode::ELEMENT_NODE) {
9110 builder.Append(u"</");
9111 nsIContent* elem = static_cast<nsIContent*>(current);
9112 if (elem->IsHTMLElement() || elem->IsSVGElement() ||
9113 elem->IsMathMLElement()) {
9114 builder.Append(elem->NodeInfo()->NameAtom());
9115 } else {
9116 builder.Append(current->NodeName());
9117 }
9118 builder.Append(u">");
9119 }
9120 isVoid = false;
9121
9122 if (current == aRoot) {
9123 return builder.ToString(aOut);
9124 }
9125
9126 if ((next = current->GetNextSibling())) {
9127 current = next;
9128 break;
9129 }
9130
9131 current = current->GetParentNode();
9132
9133 // Handle template element. If the parent is a template's content,
9134 // then adjust the parent to be the template element.
9135 if (current != aRoot &&
9136 current->NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
9137 DocumentFragment* frag = static_cast<DocumentFragment*>(current);
9138 nsIContent* fragHost = frag->GetHost();
9139 if (fragHost && fragHost->IsTemplateElement()) {
9140 current = fragHost;
9141 }
9142 }
9143
9144 if (aDescendentsOnly && current == aRoot) {
9145 return builder.ToString(aOut);
9146 }
9147 }
9148 }
9149 }
9150
9151 bool nsContentUtils::IsSpecificAboutPage(JSObject* aGlobal, const char* aUri) {
9152 // aUri must start with about: or this isn't the right function to be using.
9153 MOZ_ASSERT(strncmp(aUri, "about:", 6) == 0);
9154
9155 // Make sure the global is a window
9156 MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
9157 nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
9158 if (!win) {
9159 return false;
9160 }
9161
9162 nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
9163 NS_ENSURE_TRUE(principal, false);
9164
9165 // First check the scheme to avoid getting long specs in the common case.
9166 if (!principal->SchemeIs("about")) {
9167 return false;
9168 }
9169
9170 nsAutoCString spec;
9171 principal->GetAsciiSpec(spec);
9172
9173 return spec.EqualsASCII(aUri);
9174 }
9175
9176 /* static */
9177 void nsContentUtils::SetScrollbarsVisibility(nsIDocShell* aDocShell,
9178 bool aVisible) {
9179 if (!aDocShell) {
9180 return;
9181 }
9182 auto pref = aVisible ? ScrollbarPreference::Auto : ScrollbarPreference::Never;
9183 nsDocShell::Cast(aDocShell)->SetScrollbarPreference(pref);
9184 }
9185
9186 /* static */
9187 nsIDocShell* nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget) {
9188 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
9189
9190 if (nsCOMPtr<nsINode> node = do_QueryInterface(aTarget)) {
9191 bool ignore;
9192 innerWindow =
9193 do_QueryInterface(node->OwnerDoc()->GetScriptHandlingObject(ignore));
9194 } else if ((innerWindow = do_QueryInterface(aTarget))) {
9195 // Nothing else to do
9196 } else {
9197 nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(aTarget);
9198 if (helper) {
9199 innerWindow = helper->GetOwner();
9200 }
9201 }
9202
9203 if (innerWindow) {
9204 return innerWindow->GetDocShell();
9205 }
9206
9207 return nullptr;
9208 }
9209
9210 /*
9211 * Note: this function only relates to figuring out HTTPS state, which is an
9212 * input to the Secure Context algorithm. We are not actually implementing any
9213 * part of the Secure Context algorithm itself here.
9214 *
9215 * This is a bit of a hack. Ideally we'd propagate HTTPS state through
9216 * nsIChannel as described in the Fetch and HTML specs, but making channels
9217 * know about whether they should inherit HTTPS state, propagating information
9218 * about who the channel's "client" is, exposing GetHttpsState API on channels
9219 * and modifying the various cache implementations to store and retrieve HTTPS
9220 * state involves a huge amount of code (see bug 1220687). We avoid that for
9221 * now using this function.
9222 *
9223 * This function takes advantage of the observation that we can return true if
9224 * nsIContentSecurityManager::IsOriginPotentiallyTrustworthy returns true for
9225 * the document's origin (e.g. the origin has a scheme of 'https' or host
9226 * 'localhost' etc.). Since we generally propagate a creator document's origin
9227 * onto data:, blob:, etc. documents, this works for them too.
9228 *
9229 * The scenario where this observation breaks down is sandboxing without the
9230 * 'allow-same-origin' flag, since in this case a document is given a unique
9231 * origin (IsOriginPotentiallyTrustworthy would return false). We handle that
9232 * by using the origin that the document would have had had it not been
9233 * sandboxed.
9234 *
9235 * DEFICIENCIES: Note that this function uses nsIScriptSecurityManager's
9236 * getChannelResultPrincipalIfNotSandboxed, and that method's ignoring of
9237 * sandboxing is limited to the immediate sandbox. In the case that aDocument
9238 * should inherit its origin (e.g. data: URI) but its parent has ended up
9239 * with a unique origin due to sandboxing further up the parent chain we may
9240 * end up returning false when we would ideally return true (since we will
9241 * examine the parent's origin for 'https' and not finding it.) This means
9242 * that we may restrict the privileges of some pages unnecessarily in this
9243 * edge case.
9244 */
9245 /* static */
9246 bool nsContentUtils::HttpsStateIsModern(Document* aDocument) {
9247 if (!aDocument) {
9248 return false;
9249 }
9250
9251 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
9252
9253 if (principal->IsSystemPrincipal()) {
9254 return true;
9255 }
9256
9257 // If aDocument is sandboxed, try and get the principal that it would have
9258 // been given had it not been sandboxed:
9259 if (principal->GetIsNullPrincipal() &&
9260 (aDocument->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
9261 nsIChannel* channel = aDocument->GetChannel();
9262 if (channel) {
9263 nsCOMPtr<nsIScriptSecurityManager> ssm =
9264 nsContentUtils::GetSecurityManager();
9265 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9266 channel, getter_AddRefs(principal));
9267 if (NS_FAILED(rv)) {
9268 return false;
9269 }
9270 if (principal->IsSystemPrincipal()) {
9271 // If a document with the system principal is sandboxing a subdocument
9272 // that would normally inherit the embedding element's principal (e.g.
9273 // a srcdoc document) then the embedding document does not trust the
9274 // content that is written to the embedded document. Unlike when the
9275 // embedding document is https, in this case we have no indication as
9276 // to whether the embedded document's contents are delivered securely
9277 // or not, and the sandboxing would possibly indicate that they were
9278 // not. To play it safe we return false here. (See bug 1162772
9279 // comment 73-80.)
9280 return false;
9281 }
9282 }
9283 }
9284
9285 if (principal->GetIsNullPrincipal()) {
9286 return false;
9287 }
9288
9289 MOZ_ASSERT(principal->GetIsContentPrincipal());
9290
9291 return principal->GetIsOriginPotentiallyTrustworthy();
9292 }
9293
9294 /* static */
9295 bool nsContentUtils::ComputeIsSecureContext(nsIChannel* aChannel) {
9296 MOZ_ASSERT(aChannel);
9297
9298 nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
9299 nsCOMPtr<nsIPrincipal> principal;
9300 nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
9301 aChannel, getter_AddRefs(principal));
9302 if (NS_FAILED(rv)) {
9303 return false;
9304 }
9305
9306 const RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
9307
9308 if (principal->IsSystemPrincipal()) {
9309 // If the load would've been sandboxed, treat this load as an untrusted
9310 // load, as system code considers sandboxed resources insecure.
9311 return !loadInfo->GetLoadingSandboxed();
9312 }
9313
9314 if (principal->GetIsNullPrincipal()) {
9315 return false;
9316 }
9317
9318 if (const RefPtr<WindowContext> windowContext =
9319 WindowContext::GetById(loadInfo->GetInnerWindowID())) {
9320 if (!windowContext->GetIsSecureContext()) {
9321 return false;
9322 }
9323 }
9324
9325 return principal->GetIsOriginPotentiallyTrustworthy();
9326 }
9327
9328 /* static */
9329 void nsContentUtils::TryToUpgradeElement(Element* aElement) {
9330 NodeInfo* nodeInfo = aElement->NodeInfo();
9331 RefPtr<nsAtom> typeAtom =
9332 aElement->GetCustomElementData()->GetCustomElementType();
9333
9334 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9335 CustomElementDefinition* definition =
9336 nsContentUtils::LookupCustomElementDefinition(
9337 nodeInfo->GetDocument(), nodeInfo->NameAtom(),
9338 nodeInfo->NamespaceID(), typeAtom);
9339 if (definition) {
9340 nsContentUtils::EnqueueUpgradeReaction(aElement, definition);
9341 } else {
9342 // Add an unresolved custom element that is a candidate for upgrade when a
9343 // custom element is connected to the document.
9344 nsContentUtils::RegisterUnresolvedElement(aElement, typeAtom);
9345 }
9346 }
9347
9348 MOZ_CAN_RUN_SCRIPT
9349 static void DoCustomElementCreate(Element** aElement, JSContext* aCx,
9350 Document* aDoc, NodeInfo* aNodeInfo,
9351 CustomElementConstructor* aConstructor,
9352 ErrorResult& aRv) {
9353 JS::Rooted<JS::Value> constructResult(aCx);
9354 aConstructor->Construct(&constructResult, aRv, "Custom Element Create",
9355 CallbackFunction::eRethrowExceptions);
9356 if (aRv.Failed()) {
9357 return;
9358 }
9359
9360 RefPtr<Element> element;
9361 // constructResult is an ObjectValue because construction with a callback
9362 // always forms the return value from a JSObject.
9363 UNWRAP_OBJECT(Element, &constructResult, element);
9364 if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9365 if (!element || !element->IsHTMLElement()) {
9366 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9367 "HTMLElement");
9368 return;
9369 }
9370 } else {
9371 if (!element || !element->IsXULElement()) {
9372 aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
9373 "XULElement");
9374 return;
9375 }
9376 }
9377
9378 nsAtom* localName = aNodeInfo->NameAtom();
9379
9380 if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
9381 element->HasChildren() || element->GetAttrCount() ||
9382 element->NodeInfo()->NameAtom() != localName) {
9383 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
9384 return;
9385 }
9386
9387 element.forget(aElement);
9388 }
9389
9390 /* static */
9391 nsresult nsContentUtils::NewXULOrHTMLElement(
9392 Element** aResult, mozilla::dom::NodeInfo* aNodeInfo,
9393 FromParser aFromParser, nsAtom* aIsAtom,
9394 mozilla::dom::CustomElementDefinition* aDefinition) {
9395 RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
9396 MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
9397 nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
9398 "Can only create XUL or XHTML elements.");
9399
9400 nsAtom* name = nodeInfo->NameAtom();
9401 int32_t tag = eHTMLTag_unknown;
9402 bool isCustomElementName = false;
9403 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9404 tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
9405 isCustomElementName =
9406 (tag == eHTMLTag_userdefined &&
9407 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML));
9408 } else { // kNameSpaceID_XUL
9409 if (aIsAtom) {
9410 // Make sure the customized built-in element to be constructed confirms
9411 // to our naming requirement, i.e. [is] must be a dashed name and
9412 // the tag name must not.
9413 // if so, set isCustomElementName to false to kick off all the logics
9414 // that pick up aIsAtom.
9415 if (nsContentUtils::IsNameWithDash(aIsAtom) &&
9416 !nsContentUtils::IsNameWithDash(name)) {
9417 isCustomElementName = false;
9418 } else {
9419 isCustomElementName =
9420 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9421 }
9422 } else {
9423 isCustomElementName =
9424 nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
9425 }
9426 }
9427
9428 nsAtom* tagAtom = nodeInfo->NameAtom();
9429 nsAtom* typeAtom = nullptr;
9430 bool isCustomElement = isCustomElementName || aIsAtom;
9431 if (isCustomElement) {
9432 typeAtom = isCustomElementName ? tagAtom : aIsAtom;
9433 }
9434
9435 MOZ_ASSERT_IF(aDefinition, isCustomElement);
9436
9437 // https://dom.spec.whatwg.org/#concept-create-element
9438 // We only handle the "synchronous custom elements flag is set" now.
9439 // For the unset case (e.g. cloning a node), see bug 1319342 for that.
9440 // Step 4.
9441 RefPtr<CustomElementDefinition> definition = aDefinition;
9442 if (isCustomElement && !definition) {
9443 MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
9444 definition = nsContentUtils::LookupCustomElementDefinition(
9445 nodeInfo->GetDocument(), nodeInfo->NameAtom(), nodeInfo->NamespaceID(),
9446 typeAtom);
9447 }
9448
9449 // It might be a problem that parser synchronously calls constructor, so filed
9450 // bug 1378079 to figure out what we should do for parser case.
9451 if (definition) {
9452 /*
9453 * Synchronous custom elements flag is determined by 3 places in spec,
9454 * 1) create an element for a token, the flag is determined by
9455 * "will execute script" which is not originally created
9456 * for the HTML fragment parsing algorithm.
9457 * 2) createElement and createElementNS, the flag is the same as
9458 * NOT_FROM_PARSER.
9459 * 3) clone a node, our implementation will not go into this function.
9460 * For the unset case which is non-synchronous only applied for
9461 * inner/outerHTML.
9462 */
9463 bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT;
9464 // Per discussion in https://github.com/w3c/webcomponents/issues/635,
9465 // use entry global in those places that are called from JS APIs and use the
9466 // node document's global object if it is called from parser.
9467 nsIGlobalObject* global;
9468 if (aFromParser == dom::NOT_FROM_PARSER) {
9469 global = GetEntryGlobal();
9470
9471 // Documents created from the PrototypeDocumentSink always use
9472 // NOT_FROM_PARSER for non-XUL elements. We can get the global from the
9473 // document in that case.
9474 if (!global) {
9475 Document* doc = nodeInfo->GetDocument();
9476 if (doc && doc->LoadedFromPrototype()) {
9477 global = doc->GetScopeObject();
9478 }
9479 }
9480 } else {
9481 global = nodeInfo->GetDocument()->GetScopeObject();
9482 }
9483 if (!global) {
9484 // In browser chrome code, one may have access to a document which doesn't
9485 // have scope object anymore.
9486 return NS_ERROR_FAILURE;
9487 }
9488
9489 AutoAllowLegacyScriptExecution exemption;
9490 AutoEntryScript aes(global, "create custom elements");
9491 JSContext* cx = aes.cx();
9492 ErrorResult rv;
9493
9494 // Step 5.
9495 if (definition->IsCustomBuiltIn()) {
9496 // SetupCustomElement() should be called with an element that don't have
9497 // CustomElementData setup, if not we will hit the assertion in
9498 // SetCustomElementData().
9499 // Built-in element
9500 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9501 *aResult =
9502 CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9503 } else {
9504 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9505 }
9506 (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
9507 if (synchronousCustomElements) {
9508 CustomElementRegistry::Upgrade(*aResult, definition, rv);
9509 if (rv.MaybeSetPendingException(cx)) {
9510 aes.ReportException();
9511 }
9512 } else {
9513 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9514 }
9515
9516 return NS_OK;
9517 }
9518
9519 // Step 6.1.
9520 if (synchronousCustomElements) {
9521 definition->mPrefixStack.AppendElement(nodeInfo->GetPrefixAtom());
9522 RefPtr<Document> doc = nodeInfo->GetDocument();
9523 DoCustomElementCreate(aResult, cx, doc, nodeInfo,
9524 MOZ_KnownLive(definition->mConstructor), rv);
9525 if (rv.MaybeSetPendingException(cx)) {
9526 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9527 NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(),
9528 aFromParser));
9529 } else {
9530 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9531 }
9532 (*aResult)->SetDefined(false);
9533 }
9534 definition->mPrefixStack.RemoveLastElement();
9535 return NS_OK;
9536 }
9537
9538 // Step 6.2.
9539 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9540 NS_IF_ADDREF(*aResult =
9541 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9542 } else {
9543 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9544 }
9545 (*aResult)->SetCustomElementData(new CustomElementData(definition->mType));
9546 nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
9547 return NS_OK;
9548 }
9549
9550 if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
9551 // Per the Custom Element specification, unknown tags that are valid custom
9552 // element names should be HTMLElement instead of HTMLUnknownElement.
9553 if (isCustomElementName) {
9554 NS_IF_ADDREF(*aResult =
9555 NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
9556 } else {
9557 *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
9558 }
9559 } else {
9560 NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
9561 }
9562
9563 if (!*aResult) {
9564 return NS_ERROR_OUT_OF_MEMORY;
9565 }
9566
9567 if (isCustomElement) {
9568 (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
9569 nsContentUtils::RegisterCallbackUpgradeElement(*aResult, typeAtom);
9570 }
9571
9572 return NS_OK;
9573 }
9574
9575 CustomElementRegistry* nsContentUtils::GetCustomElementRegistry(
9576 Document* aDoc) {
9577 MOZ_ASSERT(aDoc);
9578
9579 if (!aDoc->GetDocShell()) {
9580 return nullptr;
9581 }
9582
9583 nsPIDOMWindowInner* window = aDoc->GetInnerWindow();
9584 if (!window) {
9585 return nullptr;
9586 }
9587
9588 return window->CustomElements();
9589 }
9590
9591 /* static */
9592 CustomElementDefinition* nsContentUtils::LookupCustomElementDefinition(
9593 Document* aDoc, nsAtom* aNameAtom, uint32_t aNameSpaceID,
9594 nsAtom* aTypeAtom) {
9595 if (aNameSpaceID != kNameSpaceID_XUL && aNameSpaceID != kNameSpaceID_XHTML) {
9596 return nullptr;
9597 }
9598
9599 RefPtr<CustomElementRegistry> registry = GetCustomElementRegistry(aDoc);
9600 if (!registry) {
9601 return nullptr;
9602 }
9603
9604 return registry->LookupCustomElementDefinition(aNameAtom, aNameSpaceID,
9605 aTypeAtom);
9606 }
9607
9608 /* static */
9609 void nsContentUtils::RegisterCallbackUpgradeElement(Element* aElement,
9610 nsAtom* aTypeName) {
9611 MOZ_ASSERT(aElement);
9612
9613 Document* doc = aElement->OwnerDoc();
9614 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9615 if (registry) {
9616 registry->RegisterCallbackUpgradeElement(aElement, aTypeName);
9617 }
9618 }
9619
9620 /* static */
9621 void nsContentUtils::RegisterUnresolvedElement(Element* aElement,
9622 nsAtom* aTypeName) {
9623 MOZ_ASSERT(aElement);
9624
9625 Document* doc = aElement->OwnerDoc();
9626 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9627 if (registry) {
9628 registry->RegisterUnresolvedElement(aElement, aTypeName);
9629 }
9630 }
9631
9632 /* static */
9633 void nsContentUtils::UnregisterUnresolvedElement(Element* aElement) {
9634 MOZ_ASSERT(aElement);
9635
9636 nsAtom* typeAtom = aElement->GetCustomElementData()->GetCustomElementType();
9637 Document* doc = aElement->OwnerDoc();
9638 CustomElementRegistry* registry = GetCustomElementRegistry(doc);
9639 if (registry) {
9640 registry->UnregisterUnresolvedElement(aElement, typeAtom);
9641 }
9642 }
9643
9644 /* static */
9645 void nsContentUtils::EnqueueUpgradeReaction(
9646 Element* aElement, CustomElementDefinition* aDefinition) {
9647 MOZ_ASSERT(aElement);
9648
9649 Document* doc = aElement->OwnerDoc();
9650
9651 // No DocGroup means no custom element reactions stack.
9652 if (!doc->GetDocGroup()) {
9653 return;
9654 }
9655
9656 CustomElementReactionsStack* stack =
9657 doc->GetDocGroup()->CustomElementReactionsStack();
9658 stack->EnqueueUpgradeReaction(aElement, aDefinition);
9659 }
9660
9661 /* static */
9662 void nsContentUtils::EnqueueLifecycleCallback(
9663 ElementCallbackType aType, Element* aCustomElement,
9664 LifecycleCallbackArgs* aArgs,
9665 LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
9666 CustomElementDefinition* aDefinition) {
9667 // No DocGroup means no custom element reactions stack.
9668 if (!aCustomElement->OwnerDoc()->GetDocGroup()) {
9669 return;
9670 }
9671
9672 CustomElementRegistry::EnqueueLifecycleCallback(
9673 aType, aCustomElement, aArgs, aAdoptedCallbackArgs, aDefinition);
9674 }
9675
9676 /* static */
9677 void nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
9678 Document* aDocument, nsTArray<nsIContent*>& aElements) {
9679 MOZ_ASSERT(aDocument);
9680 #ifdef DEBUG
9681 size_t oldLength = aElements.Length();
9682 #endif
9683
9684 if (PresShell* presShell = aDocument->GetPresShell()) {
9685 if (nsIFrame* scrollFrame = presShell->GetRootScrollFrame()) {
9686 nsIAnonymousContentCreator* creator = do_QueryFrame(scrollFrame);
9687 MOZ_ASSERT(
9688 creator,
9689 "scroll frame should always implement nsIAnonymousContentCreator");
9690 creator->AppendAnonymousContentTo(aElements, 0);
9691 }
9692 if (nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame()) {
9693 canvasFrame->AppendAnonymousContentTo(aElements, 0);
9694 }
9695 }
9696
9697 #ifdef DEBUG
9698 for (size_t i = oldLength; i < aElements.Length(); i++) {
9699 MOZ_ASSERT(
9700 aElements[i]->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent),
9701 "Someone here has lied, or missed to flag the node");
9702 }
9703 #endif
9704 }
9705
9706 static void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame,
9707 nsTArray<nsIContent*>& aKids,
9708 uint32_t aFlags) {
9709 if (nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame)) {
9710 ac->AppendAnonymousContentTo(aKids, aFlags);
9711 }
9712 }
9713
9714 /* static */
9715 void nsContentUtils::AppendNativeAnonymousChildren(const nsIContent* aContent,
9716 nsTArray<nsIContent*>& aKids,
9717 uint32_t aFlags) {
9718 if (aContent->MayHaveAnonymousChildren()) {
9719 if (nsIFrame* primaryFrame = aContent->GetPrimaryFrame()) {
9720 // NAC created by the element's primary frame.
9721 AppendNativeAnonymousChildrenFromFrame(primaryFrame, aKids, aFlags);
9722
9723 // NAC created by any other non-primary frames for the element.
9724 AutoTArray<nsIFrame::OwnedAnonBox, 8> ownedAnonBoxes;
9725 primaryFrame->AppendOwnedAnonBoxes(ownedAnonBoxes);
9726 for (nsIFrame::OwnedAnonBox& box : ownedAnonBoxes) {
9727 MOZ_ASSERT(box.mAnonBoxFrame->GetContent() == aContent);
9728 AppendNativeAnonymousChildrenFromFrame(box.mAnonBoxFrame, aKids,
9729 aFlags);
9730 }
9731 }
9732
9733 // Get manually created NAC (editor resize handles, etc.).
9734 if (auto nac = static_cast<ManualNACArray*>(
9735 aContent->GetProperty(nsGkAtoms::manualNACProperty))) {
9736 aKids.AppendElements(*nac);
9737 }
9738 }
9739
9740 // The root scroll frame is not the primary frame of the root element.
9741 // Detect and handle this case.
9742 if (!(aFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
9743 aContent == aContent->OwnerDoc()->GetRootElement()) {
9744 AppendDocumentLevelNativeAnonymousContentTo(aContent->OwnerDoc(), aKids);
9745 }
9746 }
9747
9748 bool nsContentUtils::IsImageAvailable(nsIContent* aLoadingNode, nsIURI* aURI,
9749 nsIPrincipal* aDefaultTriggeringPrincipal,
9750 CORSMode aCORSMode) {
9751 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
9752 QueryTriggeringPrincipal(aLoadingNode, aDefaultTriggeringPrincipal,
9753 getter_AddRefs(triggeringPrincipal));
9754 MOZ_ASSERT(triggeringPrincipal);
9755
9756 Document* doc = aLoadingNode->OwnerDoc();
9757 imgLoader* imgLoader = GetImgLoaderForDocument(doc);
9758 return imgLoader->IsImageAvailable(aURI, triggeringPrincipal, aCORSMode, doc);
9759 }
9760
9761 /* static */
9762 bool nsContentUtils::QueryTriggeringPrincipal(
9763 nsIContent* aLoadingNode, nsIPrincipal* aDefaultPrincipal,
9764 nsIPrincipal** aTriggeringPrincipal) {
9765 MOZ_ASSERT(aLoadingNode);
9766 MOZ_ASSERT(aTriggeringPrincipal);
9767
9768 bool result = false;
9769 nsCOMPtr<nsIPrincipal> loadingPrincipal = aDefaultPrincipal;
9770 if (!loadingPrincipal) {
9771 loadingPrincipal = aLoadingNode->NodePrincipal();
9772 }
9773
9774 // If aLoadingNode is content, bail out early.
9775 if (!aLoadingNode->NodePrincipal()->IsSystemPrincipal()) {
9776 loadingPrincipal.forget(aTriggeringPrincipal);
9777 return result;
9778 }
9779
9780 nsAutoString loadingStr;
9781 if (aLoadingNode->IsElement()) {
9782 aLoadingNode->AsElement()->GetAttr(
9783 kNameSpaceID_None, nsGkAtoms::triggeringprincipal, loadingStr);
9784 }
9785
9786 // Fall back if 'triggeringprincipal' isn't specified,
9787 if (loadingStr.IsEmpty()) {
9788 loadingPrincipal.forget(aTriggeringPrincipal);
9789 return result;
9790 }
9791
9792 nsCString binary;
9793 nsresult rv = Base64Decode(NS_ConvertUTF16toUTF8(loadingStr), binary);
9794 if (NS_SUCCEEDED(rv)) {
9795 nsCOMPtr<nsIPrincipal> serializedPrin = BasePrincipal::FromJSON(binary);
9796 if (serializedPrin) {
9797 result = true;
9798 serializedPrin.forget(aTriggeringPrincipal);
9799 }
9800 } else {
9801 MOZ_ASSERT(false, "Unable to deserialize base64 principal");
9802 }
9803
9804 if (!result) {
9805 // Fallback if the deserialization is failed.
9806 loadingPrincipal.forget(aTriggeringPrincipal);
9807 }
9808
9809 return result;
9810 }
9811
9812 /* static */
9813 void nsContentUtils::GetContentPolicyTypeForUIImageLoading(
9814 nsIContent* aLoadingNode, nsIPrincipal** aTriggeringPrincipal,
9815 nsContentPolicyType& aContentPolicyType, uint64_t* aRequestContextID) {
9816 MOZ_ASSERT(aRequestContextID);
9817
9818 bool result = QueryTriggeringPrincipal(aLoadingNode, aTriggeringPrincipal);
9819 if (result) {
9820 // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for
9821 // indicating it's a favicon loading.
9822 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
9823
9824 nsAutoString requestContextID;
9825 if (aLoadingNode->IsElement()) {
9826 aLoadingNode->AsElement()->GetAttr(
9827 kNameSpaceID_None, nsGkAtoms::requestcontextid, requestContextID);
9828 }
9829 nsresult rv;
9830 int64_t val = requestContextID.ToInteger64(&rv);
9831 *aRequestContextID = NS_SUCCEEDED(rv) ? val : 0;
9832 } else {
9833 aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
9834 }
9835 }
9836
9837 /* static */
9838 nsresult nsContentUtils::CreateJSValueFromSequenceOfObject(
9839 JSContext* aCx, const Sequence<JSObject*>& aTransfer,
9840 JS::MutableHandle<JS::Value> aValue) {
9841 if (aTransfer.IsEmpty()) {
9842 return NS_OK;
9843 }
9844
9845 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, aTransfer.Length()));
9846 if (!array) {
9847 return NS_ERROR_OUT_OF_MEMORY;
9848 }
9849
9850 for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
9851 JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
9852 if (!object) {
9853 continue;
9854 }
9855
9856 if (NS_WARN_IF(
9857 !JS_DefineElement(aCx, array, i, object, JSPROP_ENUMERATE))) {
9858 return NS_ERROR_OUT_OF_MEMORY;
9859 }
9860 }
9861
9862 aValue.setObject(*array);
9863 return NS_OK;
9864 }
9865
9866 /* static */
9867 bool nsContentUtils::ShouldBlockReservedKeys(WidgetKeyboardEvent* aKeyEvent) {
9868 nsCOMPtr<nsIPrincipal> principal;
9869 nsCOMPtr<Element> targetElement =
9870 do_QueryInterface(aKeyEvent->mOriginalTarget);
9871 nsCOMPtr<nsIBrowser> targetBrowser;
9872 if (targetElement) {
9873 targetBrowser = targetElement->AsBrowser();
9874 }
9875 bool isRemoteBrowser = false;
9876 if (targetBrowser) {
9877 targetBrowser->GetIsRemoteBrowser(&isRemoteBrowser);
9878 }
9879
9880 if (isRemoteBrowser) {
9881 targetBrowser->GetContentPrincipal(getter_AddRefs(principal));
9882 return principal ? nsContentUtils::IsSitePermDeny(principal, "shortcuts"_ns)
9883 : false;
9884 }
9885
9886 nsCOMPtr<nsIContent> content = do_QueryInterface(aKeyEvent->mOriginalTarget);
9887 if (content) {
9888 Document* doc = content->GetUncomposedDoc();
9889 if (doc) {
9890 RefPtr<WindowContext> wc = doc->GetWindowContext();
9891 if (wc) {
9892 return wc->TopWindowContext()->GetShortcutsPermission() ==
9893 nsIPermissionManager::DENY_ACTION;
9894 }
9895 }
9896 }
9897
9898 return false;
9899 }
9900
9901 /**
9902 * Checks whether the given type is a supported document type for
9903 * loading within the nsObjectLoadingContent specified by aContent.
9904 *
9905 * NOTE Helper method for nsContentUtils::HtmlObjectContentTypeForMIMEType.
9906 * NOTE Does not take content policy or capabilities into account
9907 */
9908 static bool HtmlObjectContentSupportsDocument(const nsCString& aMimeType,
9909 nsIContent* aContent) {
9910 nsCOMPtr<nsIWebNavigationInfo> info(
9911 do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID));
9912 if (!info) {
9913 return false;
9914 }
9915
9916 nsCOMPtr<nsIWebNavigation> webNav;
9917 if (aContent) {
9918 Document* currentDoc = aContent->GetComposedDoc();
9919 if (currentDoc) {
9920 webNav = do_GetInterface(currentDoc->GetWindow());
9921 }
9922 }
9923
9924 uint32_t supported;
9925 nsresult rv = info->IsTypeSupported(aMimeType, webNav, &supported);
9926
9927 if (NS_FAILED(rv)) {
9928 return false;
9929 }
9930
9931 if (supported != nsIWebNavigationInfo::UNSUPPORTED) {
9932 // Don't want to support plugins as documents
9933 return supported != nsIWebNavigationInfo::FALLBACK;
9934 }
9935
9936 // Try a stream converter
9937 // NOTE: We treat any type we can convert from as a supported type. If a
9938 // type is not actually supported, the URI loader will detect that and
9939 // return an error, and we'll fallback.
9940 nsCOMPtr<nsIStreamConverterService> convServ =
9941 do_GetService("@mozilla.org/streamConverters;1");
9942 bool canConvert = false;
9943 if (convServ) {
9944 rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
9945 }
9946 return NS_SUCCEEDED(rv) && canConvert;
9947 }
9948
9949 /* static */
9950 already_AddRefed<nsIPluginTag> nsContentUtils::PluginTagForType(
9951 const nsCString& aMIMEType, bool aNoFakePlugin) {
9952 RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
9953 nsCOMPtr<nsIPluginTag> tag;
9954 NS_ENSURE_TRUE(pluginHost, nullptr);
9955
9956 // ShouldPlay will handle the case where the plugin is disabled
9957 pluginHost->GetPluginTagForType(
9958 aMIMEType,
9959 aNoFakePlugin ? nsPluginHost::eExcludeFake : nsPluginHost::eExcludeNone,
9960 getter_AddRefs(tag));
9961
9962 return tag.forget();
9963 }
9964
9965 /* static */
9966 uint32_t nsContentUtils::HtmlObjectContentTypeForMIMEType(
9967 const nsCString& aMIMEType, bool aNoFakePlugin, nsIContent* aContent) {
9968 if (aMIMEType.IsEmpty()) {
9969 return nsIObjectLoadingContent::TYPE_NULL;
9970 }
9971
9972 if (imgLoader::SupportImageWithMimeType(aMIMEType)) {
9973 return nsIObjectLoadingContent::TYPE_IMAGE;
9974 }
9975
9976 // Faking support of the PDF content as a document for EMBED tags
9977 // when internal PDF viewer is enabled.
9978 if (aMIMEType.LowerCaseEqualsLiteral("application/pdf") && IsPDFJSEnabled()) {
9979 return nsIObjectLoadingContent::TYPE_DOCUMENT;
9980 }
9981
9982 if (HtmlObjectContentSupportsDocument(aMIMEType, aContent)) {
9983 return nsIObjectLoadingContent::TYPE_DOCUMENT;
9984 }
9985
9986 bool isSpecialPlugin = nsPluginHost::GetSpecialType(aMIMEType) !=
9987 nsPluginHost::eSpecialType_None;
9988 if (isSpecialPlugin) {
9989 return nsIObjectLoadingContent::TYPE_FALLBACK;
9990 }
9991
9992 return nsIObjectLoadingContent::TYPE_NULL;
9993 }
9994
9995 /* static */
9996 already_AddRefed<nsISerialEventTarget> nsContentUtils::GetEventTargetByLoadInfo(
9997 nsILoadInfo* aLoadInfo, TaskCategory aCategory) {
9998 if (NS_WARN_IF(!aLoadInfo)) {
9999 return nullptr;
10000 }
10001
10002 RefPtr<Document> doc;
10003 aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
10004 nsCOMPtr<nsISerialEventTarget> target;
10005 if (doc) {
10006 if (DocGroup* group = doc->GetDocGroup()) {
10007 target = group->EventTargetFor(aCategory);
10008 }
10009 } else {
10010 target = GetMainThreadSerialEventTarget();
10011 }
10012
10013 return target.forget();
10014 }
10015
10016 /* static */
10017 bool nsContentUtils::IsLocalRefURL(const nsAString& aString) {
10018 return !aString.IsEmpty() && aString[0] == '#';
10019 }
10020
10021 // We use only 53 bits for the ID so that it can be converted to and from a JS
10022 // value without loss of precision. The upper bits of the ID hold the process
10023 // ID. The lower bits identify the object itself.
10024 static constexpr uint64_t kIdTotalBits = 53;
10025 static constexpr uint64_t kIdProcessBits = 22;
10026 static constexpr uint64_t kIdBits = kIdTotalBits - kIdProcessBits;
10027
10028 /* static */
10029 uint64_t nsContentUtils::GenerateProcessSpecificId(uint64_t aId) {
10030 uint64_t processId = 0;
10031 if (XRE_IsContentProcess()) {
10032 ContentChild* cc = ContentChild::GetSingleton();
10033 processId = cc->GetID();
10034 }
10035
10036 MOZ_RELEASE_ASSERT(processId < (uint64_t(1) << kIdProcessBits));
10037 uint64_t processBits = processId & ((uint64_t(1) << kIdProcessBits) - 1);
10038
10039 uint64_t id = aId;
10040 MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
10041 uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
10042
10043 return (processBits << kIdBits) | bits;
10044 }
10045
10046 /* static */
10047 std::tuple<uint64_t, uint64_t> nsContentUtils::SplitProcessSpecificId(
10048 uint64_t aId) {
10049 return {aId >> kIdBits, aId & ((uint64_t(1) << kIdBits) - 1)};
10050 }
10051
10052 // Next process-local Tab ID.
10053 static uint64_t gNextTabId = 0;
10054
10055 /* static */
10056 uint64_t nsContentUtils::GenerateTabId() {
10057 return GenerateProcessSpecificId(++gNextTabId);
10058 }
10059
10060 // Next process-local Browser ID.
10061 static uint64_t gNextBrowserId = 0;
10062
10063 /* static */
10064 uint64_t nsContentUtils::GenerateBrowserId() {
10065 return GenerateProcessSpecificId(++gNextBrowserId);
10066 }
10067
10068 // Next process-local Browsing Context ID.
10069 static uint64_t gNextBrowsingContextId = 0;
10070
10071 /* static */
10072 uint64_t nsContentUtils::GenerateBrowsingContextId() {
10073 return GenerateProcessSpecificId(++gNextBrowsingContextId);
10074 }
10075
10076 // Next process-local Window ID.
10077 static uint64_t gNextWindowId = 0;
10078
10079 /* static */
10080 uint64_t nsContentUtils::GenerateWindowId() {
10081 return GenerateProcessSpecificId(++gNextWindowId);
10082 }
10083
10084 // Next process-local load.
10085 static Atomic<uint64_t> gNextLoadIdentifier(0);
10086
10087 /* static */
10088 uint64_t nsContentUtils::GenerateLoadIdentifier() {
10089 return GenerateProcessSpecificId(++gNextLoadIdentifier);
10090 }
10091
10092 /* static */
10093 bool nsContentUtils::GetUserIsInteracting() {
10094 return UserInteractionObserver::sUserActive;
10095 }
10096
10097 /* static */
10098 bool nsContentUtils::GetSourceMapURL(nsIHttpChannel* aChannel,
10099 nsACString& aResult) {
10100 nsresult rv = aChannel->GetResponseHeader("SourceMap"_ns, aResult);
10101 if (NS_FAILED(rv)) {
10102 rv = aChannel->GetResponseHeader("X-SourceMap"_ns, aResult);
10103 }
10104 return NS_SUCCEEDED(rv);
10105 }
10106
10107 /* static */
10108 bool nsContentUtils::IsMessageInputEvent(const IPC::Message& aMsg) {
10109 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10110 mozilla::dom::PBrowser::PBrowserStart) {
10111 switch (aMsg.type()) {
10112 case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
10113 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10114 case mozilla::dom::PBrowser::Msg_RealMouseEnterExitWidgetEvent__ID:
10115 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10116 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10117 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10118 case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
10119 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10120 case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
10121 return true;
10122 }
10123 }
10124 return false;
10125 }
10126
10127 /* static */
10128 bool nsContentUtils::IsMessageCriticalInputEvent(const IPC::Message& aMsg) {
10129 if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
10130 mozilla::dom::PBrowser::PBrowserStart) {
10131 switch (aMsg.type()) {
10132 case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
10133 case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
10134 case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
10135 case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
10136 case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
10137 return true;
10138 }
10139 }
10140 return false;
10141 }
10142
10143 static const char* kUserInteractionInactive = "user-interaction-inactive";
10144 static const char* kUserInteractionActive = "user-interaction-active";
10145
10146 void nsContentUtils::UserInteractionObserver::Init() {
10147 // Listen for the observer messages from EventStateManager which are telling
10148 // us whether or not the user is interacting.
10149 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10150 obs->AddObserver(this, kUserInteractionInactive, false);
10151 obs->AddObserver(this, kUserInteractionActive, false);
10152
10153 // We can't register ourselves as an annotator yet, as the
10154 // BackgroundHangMonitor hasn't started yet. It will have started by the
10155 // time we have the chance to spin the event loop.
10156 RefPtr<UserInteractionObserver> self = this;
10157 NS_DispatchToMainThread(NS_NewRunnableFunction(
10158 "nsContentUtils::UserInteractionObserver::Init",
10159 [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
10160 }
10161
10162 void nsContentUtils::UserInteractionObserver::Shutdown() {
10163 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
10164 if (obs) {
10165 obs->RemoveObserver(this, kUserInteractionInactive);
10166 obs->RemoveObserver(this, kUserInteractionActive);
10167 }
10168
10169 BackgroundHangMonitor::UnregisterAnnotator(*this);
10170 }
10171
10172 /**
10173 * NB: This function is always called by the BackgroundHangMonitor thread.
10174 * Plan accordingly
10175 */
10176 void nsContentUtils::UserInteractionObserver::AnnotateHang(
10177 BackgroundHangAnnotations& aAnnotations) {
10178 // NOTE: Only annotate the hang report if the user is known to be interacting.
10179 if (sUserActive) {
10180 aAnnotations.AddAnnotation(u"UserInteracting"_ns, true);
10181 }
10182 }
10183
10184 NS_IMETHODIMP
10185 nsContentUtils::UserInteractionObserver::Observe(nsISupports* aSubject,
10186 const char* aTopic,
10187 const char16_t* aData) {
10188 if (!strcmp(aTopic, kUserInteractionInactive)) {
10189 sUserActive = false;
10190 } else if (!strcmp(aTopic, kUserInteractionActive)) {
10191 sUserActive = true;
10192 } else {
10193 NS_WARNING("Unexpected observer notification");
10194 }
10195 return NS_OK;
10196 }
10197
10198 Atomic<bool> nsContentUtils::UserInteractionObserver::sUserActive(false);
10199 NS_IMPL_ISUPPORTS(nsContentUtils::UserInteractionObserver, nsIObserver)
10200
10201 /* static */
10202 bool nsContentUtils::IsSpecialName(const nsAString& aName) {
10203 return aName.LowerCaseEqualsLiteral("_blank") ||
10204 aName.LowerCaseEqualsLiteral("_top") ||
10205 aName.LowerCaseEqualsLiteral("_parent") ||
10206 aName.LowerCaseEqualsLiteral("_self");
10207 }
10208
10209 /* static */
10210 bool nsContentUtils::IsOverridingWindowName(const nsAString& aName) {
10211 return !aName.IsEmpty() && !IsSpecialName(aName);
10212 }
10213
10214 // Unfortunately, we can't unwrap an IDL object using only a concrete type.
10215 // We need to calculate type data based on the IDL typename. Which means
10216 // wrapping our templated function in a macro.
10217 #define EXTRACT_EXN_VALUES(T, ...) \
10218 ExtractExceptionValues<mozilla::dom::prototypes::id::T, \
10219 T##_Binding::NativeType, T>(__VA_ARGS__) \
10220 .isOk()
10221
10222 template <prototypes::ID PrototypeID, class NativeType, typename T>
10223 static Result<Ok, nsresult> ExtractExceptionValues(
10224 JSContext* aCx, JS::HandleObject aObj, nsAString& aSourceSpecOut,
10225 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10226 AssertStaticUnwrapOK<PrototypeID>();
10227 RefPtr<T> exn;
10228 MOZ_TRY((UnwrapObject<PrototypeID, NativeType>(aObj, exn, nullptr)));
10229
10230 exn->GetFilename(aCx, aSourceSpecOut);
10231 if (!aSourceSpecOut.IsEmpty()) {
10232 *aLineOut = exn->LineNumber(aCx);
10233 *aColumnOut = exn->ColumnNumber();
10234 }
10235
10236 exn->GetName(aMessageOut);
10237 aMessageOut.AppendLiteral(": ");
10238
10239 nsAutoString message;
10240 exn->GetMessageMoz(message);
10241 aMessageOut.Append(message);
10242 return Ok();
10243 }
10244
10245 /* static */
10246 void nsContentUtils::ExtractErrorValues(
10247 JSContext* aCx, JS::Handle<JS::Value> aValue, nsACString& aSourceSpecOut,
10248 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10249 nsAutoString sourceSpec;
10250 ExtractErrorValues(aCx, aValue, sourceSpec, aLineOut, aColumnOut,
10251 aMessageOut);
10252 CopyUTF16toUTF8(sourceSpec, aSourceSpecOut);
10253 }
10254
10255 /* static */
10256 void nsContentUtils::ExtractErrorValues(
10257 JSContext* aCx, JS::Handle<JS::Value> aValue, nsAString& aSourceSpecOut,
10258 uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
10259 MOZ_ASSERT(aLineOut);
10260 MOZ_ASSERT(aColumnOut);
10261
10262 if (aValue.isObject()) {
10263 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
10264
10265 // Try to process as an Error object. Use the file/line/column values
10266 // from the Error as they will be more specific to the root cause of
10267 // the problem.
10268 JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
10269 if (err) {
10270 // Use xpc to extract the error message only. We don't actually send
10271 // this report anywhere.
10272 RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
10273 report->Init(err,
10274 nullptr, // toString result
10275 false, // chrome
10276 0); // window ID
10277
10278 if (!report->mFileName.IsEmpty()) {
10279 aSourceSpecOut = report->mFileName;
10280 *aLineOut = report->mLineNumber;
10281 *aColumnOut = report->mColumn;
10282 }
10283 aMessageOut.Assign(report->mErrorMsg);
10284 }
10285
10286 // Next, try to unwrap the rejection value as a DOMException.
10287 else if (EXTRACT_EXN_VALUES(DOMException, aCx, obj, aSourceSpecOut,
10288 aLineOut, aColumnOut, aMessageOut)) {
10289 return;
10290 }
10291
10292 // Next, try to unwrap the rejection value as an XPC Exception.
10293 else if (EXTRACT_EXN_VALUES(Exception, aCx, obj, aSourceSpecOut, aLineOut,
10294 aColumnOut, aMessageOut)) {
10295 return;
10296 }
10297 }
10298
10299 // If we could not unwrap a specific error type, then perform default safe
10300 // string conversions on primitives. Objects will result in "[Object]"
10301 // unfortunately.
10302 if (aMessageOut.IsEmpty()) {
10303 nsAutoJSString jsString;
10304 if (jsString.init(aCx, aValue)) {
10305 aMessageOut = jsString;
10306 } else {
10307 JS_ClearPendingException(aCx);
10308 }
10309 }
10310 }
10311
10312 #undef EXTRACT_EXN_VALUES
10313
10314 /* static */
10315 bool nsContentUtils::ContentIsLink(nsIContent* aContent) {
10316 if (!aContent || !aContent->IsElement()) {
10317 return false;
10318 }
10319
10320 if (aContent->IsHTMLElement(nsGkAtoms::a)) {
10321 return true;
10322 }
10323
10324 return aContent->AsElement()->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
10325 nsGkAtoms::simple, eCaseMatters);
10326 }
10327
10328 /* static */
10329 already_AddRefed<ContentFrameMessageManager>
10330 nsContentUtils::TryGetBrowserChildGlobal(nsISupports* aFrom) {
10331 RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(aFrom);
10332 if (!frameLoaderOwner) {
10333 return nullptr;
10334 }
10335
10336 RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
10337 if (!frameLoader) {
10338 return nullptr;
10339 }
10340
10341 RefPtr<ContentFrameMessageManager> manager =
10342 frameLoader->GetBrowserChildMessageManager();
10343 return manager.forget();
10344 }
10345
10346 /* static */
10347 uint32_t nsContentUtils::InnerOrOuterWindowCreated() {
10348 MOZ_ASSERT(NS_IsMainThread());
10349 ++sInnerOrOuterWindowCount;
10350 return ++sInnerOrOuterWindowSerialCounter;
10351 }
10352
10353 /* static */
10354 void nsContentUtils::InnerOrOuterWindowDestroyed() {
10355 MOZ_ASSERT(NS_IsMainThread());
10356 MOZ_ASSERT(sInnerOrOuterWindowCount > 0);
10357 --sInnerOrOuterWindowCount;
10358 }
10359
10360 static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
10361 nsAString* result = static_cast<nsAString*>(aData);
10362 result->Append(static_cast<const char16_t*>(aBuf),
10363 static_cast<uint32_t>(aLen));
10364 return true;
10365 }
10366
10367 /* static */
10368 bool nsContentUtils::StringifyJSON(JSContext* aCx,
10369 JS::MutableHandle<JS::Value> aValue,
10370 nsAString& aOutStr) {
10371 MOZ_ASSERT(aCx);
10372 aOutStr.Truncate();
10373 JS::RootedValue value(aCx, aValue.get());
10374 nsAutoString serializedValue;
10375 NS_ENSURE_TRUE(JS_Stringify(aCx, &value, nullptr, JS::NullHandleValue,
10376 JSONCreator, &serializedValue),
10377 false);
10378 aOutStr = serializedValue;
10379 return true;
10380 }
10381
10382 /* static */
10383 bool nsContentUtils::
10384 HighPriorityEventPendingForTopLevelDocumentBeforeContentfulPaint(
10385 Document* aDocument) {
10386 MOZ_ASSERT(XRE_IsContentProcess(),
10387 "This function only makes sense in content processes");
10388
10389 if (aDocument && !aDocument->IsLoadedAsData()) {
10390 if (nsPresContext* presContext = FindPresContextForDocument(aDocument)) {
10391 MOZ_ASSERT(!presContext->IsChrome(),
10392 "Should never have a chrome PresContext in a content process");
10393
10394 return !presContext->GetInProcessRootContentDocumentPresContext()
10395 ->HadContentfulPaint() &&
10396 nsThreadManager::MainThreadHasPendingHighPriorityEvents();
10397 }
10398 }
10399 return false;
10400 }
10401
10402 /* static */
10403 nsGlobalWindowInner* nsContentUtils::CallerInnerWindow() {
10404 nsIGlobalObject* global = GetIncumbentGlobal();
10405 NS_ENSURE_TRUE(global, nullptr);
10406
10407 if (auto* window = global->AsInnerWindow()) {
10408 return nsGlobalWindowInner::Cast(window);
10409 }
10410
10411 // When Extensions run content scripts inside a sandbox, it uses
10412 // sandboxPrototype to make them appear as though they're running in the
10413 // scope of the page. So when a content script invokes postMessage, it expects
10414 // the |source| of the received message to be the window set as the
10415 // sandboxPrototype. This used to work incidentally for unrelated reasons, but
10416 // now we need to do some special handling to support it.
10417 JS::Rooted<JSObject*> scope(RootingCx(), global->GetGlobalJSObject());
10418 NS_ENSURE_TRUE(scope, nullptr);
10419
10420 if (xpc::IsSandbox(scope)) {
10421 AutoJSAPI jsapi;
10422 MOZ_ALWAYS_TRUE(jsapi.Init(scope));
10423 JSContext* cx = jsapi.cx();
10424 // Our current Realm on aCx is the sandbox. Using that for unwrapping
10425 // makes sense: if the sandbox can unwrap the window, we can use it.
10426 return xpc::SandboxWindowOrNull(scope, cx);
10427 }
10428
10429 // The calling window must be holding a reference, so we can return a weak
10430 // pointer.
10431 return nsGlobalWindowInner::Cast(global->AsInnerWindow());
10432 }
10433
10434 /* static */
10435 bool nsContentUtils::IsURIInPrefList(nsIURI* aURI, const char* aPrefName) {
10436 MOZ_ASSERT(aPrefName);
10437
10438 nsAutoCString list;
10439 Preferences::GetCString(aPrefName, list);
10440 ToLowerCase(list);
10441 return IsURIInList(aURI, list);
10442 }
10443
10444 /* static */
10445 bool nsContentUtils::IsURIInList(nsIURI* aURI, const nsCString& aList) {
10446 #ifdef DEBUG
10447 nsAutoCString listLowerCase(aList);
10448 ToLowerCase(listLowerCase);
10449 MOZ_ASSERT(listLowerCase.Equals(aList),
10450 "The aList argument should be lower-case");
10451 #endif
10452
10453 if (!aURI) {
10454 return false;
10455 }
10456
10457 nsAutoCString scheme;
10458 aURI->GetScheme(scheme);
10459 if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
10460 return false;
10461 }
10462
10463 if (aList.IsEmpty()) {
10464 return false;
10465 }
10466
10467 // The list is comma separated domain list. Each item may start with "*.".
10468 // If starts with "*.", it matches any sub-domains.
10469
10470 nsCCharSeparatedTokenizer tokenizer(aList, ',');
10471 while (tokenizer.hasMoreTokens()) {
10472 const nsCString token(tokenizer.nextToken());
10473
10474 nsAutoCString host;
10475 aURI->GetHost(host);
10476 if (host.IsEmpty()) {
10477 return false;
10478 }
10479 ToLowerCase(host);
10480
10481 for (;;) {
10482 int32_t index = token.Find(host, false);
10483 if (index >= 0 &&
10484 static_cast<uint32_t>(index) + host.Length() <= token.Length()) {
10485 // If we found a full match, return true.
10486 size_t indexAfterHost = index + host.Length();
10487 if (index == 0 && indexAfterHost == token.Length()) {
10488 return true;
10489 }
10490 // If next character is '/', we need to check the path too.
10491 // We assume the path in the list means "/foo" + "*".
10492 if (token[indexAfterHost] == '/') {
10493 nsDependentCSubstring pathInList(
10494 token, indexAfterHost,
10495 static_cast<nsDependentCSubstring::size_type>(-1));
10496 nsAutoCString filePath;
10497 aURI->GetFilePath(filePath);
10498 ToLowerCase(filePath);
10499 if (StringBeginsWith(filePath, pathInList) &&
10500 (filePath.Length() == pathInList.Length() ||
10501 pathInList.EqualsLiteral("/") ||
10502 filePath[pathInList.Length() - 1] == '/' ||
10503 filePath[pathInList.Length() - 1] == '?' ||
10504 filePath[pathInList.Length() - 1] == '#')) {
10505 return true;
10506 }
10507 }
10508 }
10509 int32_t startIndexOfCurrentLevel = host[0] == '*' ? 1 : 0;
10510 int32_t startIndexOfNextLevel =
10511 host.Find(".", false, startIndexOfCurrentLevel + 1);
10512 if (startIndexOfNextLevel <= 0) {
10513 break;
10514 }
10515 host = "*"_ns + nsDependentCSubstring(host, startIndexOfNextLevel);
10516 }
10517 }
10518
10519 return false;
10520 }
10521
10522 /* static */
10523 ScreenIntMargin nsContentUtils::GetWindowSafeAreaInsets(
10524 nsIScreen* aScreen, const ScreenIntMargin& aSafeAreaInsets,
10525 const LayoutDeviceIntRect& aWindowRect) {
10526 // This calculates safe area insets of window from screen rectangle, window
10527 // rectangle and safe area insets of screen.
10528 //
10529 // +----------------------------------------+ <-- screen
10530 // | +-------------------------------+ <------- window
10531 // | | window's safe area inset top) | |
10532 // +--+-------------------------------+--+ |
10533 // | | | |<------ safe area rectangle of
10534 // | | | | | screen
10535 // +--+-------------------------------+--+ |
10536 // | |window's safe area inset bottom| |
10537 // | +-------------------------------+ |
10538 // +----------------------------------------+
10539 //
10540 ScreenIntMargin windowSafeAreaInsets;
10541
10542 if (windowSafeAreaInsets == aSafeAreaInsets) {
10543 // no safe area insets.
10544 return windowSafeAreaInsets;
10545 }
10546
10547 int32_t screenLeft, screenTop, screenWidth, screenHeight;
10548 nsresult rv =
10549 aScreen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
10550 if (NS_WARN_IF(NS_FAILED(rv))) {
10551 return windowSafeAreaInsets;
10552 }
10553
10554 // Screen's rect of safe area
10555 LayoutDeviceIntRect safeAreaRect(
10556 screenLeft + aSafeAreaInsets.left, screenTop + aSafeAreaInsets.top,
10557 screenWidth - aSafeAreaInsets.right - aSafeAreaInsets.left,
10558 screenHeight - aSafeAreaInsets.bottom - aSafeAreaInsets.top);
10559 // window's rect of safe area
10560 safeAreaRect = safeAreaRect.Intersect(aWindowRect);
10561
10562 windowSafeAreaInsets.top =
10563 aSafeAreaInsets.top ? std::max(safeAreaRect.y - aWindowRect.y, 0) : 0;
10564 windowSafeAreaInsets.left =
10565 aSafeAreaInsets.left ? std::max(safeAreaRect.x - aWindowRect.x, 0) : 0;
10566 windowSafeAreaInsets.right =
10567 aSafeAreaInsets.right
10568 ? std::max((aWindowRect.x + aWindowRect.width) -
10569 (safeAreaRect.x + safeAreaRect.width),
10570 0)
10571 : 0;
10572 windowSafeAreaInsets.bottom =
10573 aSafeAreaInsets.bottom
10574 ? std::max(aWindowRect.y + aWindowRect.height -
10575 (safeAreaRect.y + safeAreaRect.height),
10576 0)
10577 : 0;
10578
10579 return windowSafeAreaInsets;
10580 }
10581
10582 /* static */
10583 nsContentUtils::SubresourceCacheValidationInfo
10584 nsContentUtils::GetSubresourceCacheValidationInfo(nsIRequest* aRequest) {
10585 SubresourceCacheValidationInfo info;
10586 if (nsCOMPtr<nsICacheInfoChannel> cache = do_QueryInterface(aRequest)) {
10587 uint32_t value = 0;
10588 if (NS_SUCCEEDED(cache->GetCacheTokenExpirationTime(&value))) {
10589 info.mExpirationTime.emplace(value);
10590 }
10591 }
10592
10593 // Determine whether the cache entry must be revalidated when we try to use
10594 // it. Currently, only HTTP specifies this information...
10595 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest)) {
10596 Unused << httpChannel->IsNoStoreResponse(&info.mMustRevalidate);
10597
10598 if (!info.mMustRevalidate) {
10599 Unused << httpChannel->IsNoCacheResponse(&info.mMustRevalidate);
10600 }
10601 }
10602
10603 return info;
10604 }
10605
10606 nsCString nsContentUtils::TruncatedURLForDisplay(nsIURI* aURL, size_t aMaxLen) {
10607 nsCString spec;
10608 if (aURL) {
10609 aURL->GetSpec(spec);
10610 spec.Truncate(std::min(aMaxLen, spec.Length()));
10611 }
10612 return spec;
10613 }
10614
10615 namespace mozilla {
10616 std::ostream& operator<<(std::ostream& aOut,
10617 const PreventDefaultResult aPreventDefaultResult) {
10618 switch (aPreventDefaultResult) {
10619 case PreventDefaultResult::No:
10620 aOut << "unhandled";
10621 break;
10622 case PreventDefaultResult::ByContent:
10623 aOut << "handled-by-content";
10624 break;
10625 case PreventDefaultResult::ByChrome:
10626 aOut << "handled-by-chrome";
10627 break;
10628 }
10629 return aOut;
10630 }
10631 } // namespace mozilla
10632