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