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(""");
8897 break;
8898 case '&':
8899 aOut.AppendLiteral("&");
8900 break;
8901 case 0x00A0:
8902 aOut.AppendLiteral(" ");
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("<");
8922 break;
8923 case '>':
8924 aOut.AppendLiteral(">");
8925 break;
8926 case '&':
8927 aOut.AppendLiteral("&");
8928 break;
8929 case 0x00A0:
8930 aOut.AppendLiteral(" ");
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("<");
8944 break;
8945 case '>':
8946 aOut.AppendLiteral(">");
8947 break;
8948 case '&':
8949 aOut.AppendLiteral("&");
8950 break;
8951 case 0x00A0:
8952 aOut.AppendLiteral(" ");
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("<") - 2;
8983 break;
8984 case '>':
8985 extraSpaceNeeded += ArrayLength(">") - 2;
8986 break;
8987 case '&':
8988 extraSpaceNeeded += ArrayLength("&") - 2;
8989 break;
8990 case 0x00A0:
8991 extraSpaceNeeded += ArrayLength(" ") - 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("<") - 2;
9004 break;
9005 case '>':
9006 extraSpaceNeeded += ArrayLength(">") - 2;
9007 break;
9008 case '&':
9009 extraSpaceNeeded += ArrayLength("&") - 2;
9010 break;
9011 case 0x00A0:
9012 extraSpaceNeeded += ArrayLength(" ") - 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(""") - 2;
9038 break;
9039 case '&':
9040 extraSpaceNeeded += ArrayLength("&") - 2;
9041 break;
9042 case 0x00A0:
9043 extraSpaceNeeded += ArrayLength(" ") - 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