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