Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* A namespace class for static layout utilities. */
9 #include "nsContentUtils.h"
11 #include <algorithm>
12 #include <math.h>
14 #include "prprf.h"
15 #include "nsCxPusher.h"
16 #include "DecoderTraits.h"
17 #include "harfbuzz/hb.h"
18 #include "imgICache.h"
19 #include "imgIContainer.h"
20 #include "imgINotificationObserver.h"
21 #include "imgLoader.h"
22 #include "imgRequestProxy.h"
23 #include "jsapi.h"
24 #include "jsfriendapi.h"
25 #include "js/OldDebugAPI.h"
26 #include "js/Value.h"
27 #include "Layers.h"
28 #include "MediaDecoder.h"
29 // nsNPAPIPluginInstance must be included before nsIDocument.h, which is included in mozAutoDocUpdate.h.
30 #include "nsNPAPIPluginInstance.h"
31 #include "mozAutoDocUpdate.h"
32 #include "mozilla/ArrayUtils.h"
33 #include "mozilla/Attributes.h"
34 #include "mozilla/AutoRestore.h"
35 #include "mozilla/Base64.h"
36 #include "mozilla/DebugOnly.h"
37 #include "mozilla/dom/DocumentFragment.h"
38 #include "mozilla/dom/Element.h"
39 #include "mozilla/dom/HTMLMediaElement.h"
40 #include "mozilla/dom/HTMLTemplateElement.h"
41 #include "mozilla/dom/HTMLContentElement.h"
42 #include "mozilla/dom/TextDecoder.h"
43 #include "mozilla/dom/TouchEvent.h"
44 #include "mozilla/dom/ShadowRoot.h"
45 #include "mozilla/EventDispatcher.h"
46 #include "mozilla/EventListenerManager.h"
47 #include "mozilla/EventStateManager.h"
48 #include "mozilla/IMEStateManager.h"
49 #include "mozilla/InternalMutationEvent.h"
50 #include "mozilla/Likely.h"
51 #include "mozilla/MouseEvents.h"
52 #include "mozilla/Preferences.h"
53 #include "mozilla/dom/Selection.h"
54 #include "mozilla/TextEvents.h"
55 #include "nsAString.h"
56 #include "nsAttrName.h"
57 #include "nsAttrValue.h"
58 #include "nsAttrValueInlines.h"
59 #include "nsBindingManager.h"
60 #include "nsCCUncollectableMarker.h"
61 #include "nsChannelPolicy.h"
62 #include "nsCharSeparatedTokenizer.h"
63 #include "nsCOMPtr.h"
64 #include "nsContentCreatorFunctions.h"
65 #include "nsContentDLF.h"
66 #include "nsContentList.h"
67 #include "nsContentPolicyUtils.h"
68 #include "nsCPrefetchService.h"
69 #include "nsCRT.h"
70 #include "nsCycleCollectionParticipant.h"
71 #include "nsCycleCollector.h"
72 #include "nsDataHashtable.h"
73 #include "nsDocShellCID.h"
74 #include "nsDocument.h"
75 #include "nsDOMCID.h"
76 #include "mozilla/dom/DataTransfer.h"
77 #include "nsDOMJSUtils.h"
78 #include "nsDOMMutationObserver.h"
79 #include "nsError.h"
80 #include "nsFocusManager.h"
81 #include "nsGenericHTMLElement.h"
82 #include "nsGenericHTMLFrameElement.h"
83 #include "nsGkAtoms.h"
84 #include "nsHostObjectProtocolHandler.h"
85 #include "nsHtml5Module.h"
86 #include "nsHtml5StringParser.h"
87 #include "nsIAsyncVerifyRedirectCallback.h"
88 #include "nsICategoryManager.h"
89 #include "nsIChannelEventSink.h"
90 #include "nsIChannelPolicy.h"
91 #include "nsIChromeRegistry.h"
92 #include "nsIConsoleService.h"
93 #include "nsIContent.h"
94 #include "nsIContentSecurityPolicy.h"
95 #include "nsIContentSink.h"
96 #include "nsIContentViewer.h"
97 #include "nsIDocShell.h"
98 #include "nsIDocument.h"
99 #include "nsIDocumentEncoder.h"
100 #include "nsIDOMDocument.h"
101 #include "nsIDOMDocumentType.h"
102 #include "nsIDOMEvent.h"
103 #include "nsIDOMHTMLElement.h"
104 #include "nsIDOMHTMLFormElement.h"
105 #include "nsIDOMHTMLInputElement.h"
106 #include "nsIDOMNode.h"
107 #include "nsIDOMNodeList.h"
108 #include "nsIDOMScriptObjectFactory.h"
109 #include "nsIDOMUserDataHandler.h"
110 #include "nsIDOMXULCommandEvent.h"
111 #include "nsIDragService.h"
112 #include "nsIEditor.h"
113 #include "nsIFormControl.h"
114 #include "nsIForm.h"
115 #include "nsIFragmentContentSink.h"
116 #include "nsIFrame.h"
117 #include "nsIHTMLDocument.h"
118 #include "nsIIdleService.h"
119 #include "nsIImageLoadingContent.h"
120 #include "nsIInterfaceRequestor.h"
121 #include "nsIInterfaceRequestorUtils.h"
122 #include "nsIIOService.h"
123 #include "nsIJSRuntimeService.h"
124 #include "nsILineBreaker.h"
125 #include "nsILoadContext.h"
126 #include "nsILoadGroup.h"
127 #include "nsIMemoryReporter.h"
128 #include "nsIMIMEService.h"
129 #include "nsINode.h"
130 #include "nsINodeInfo.h"
131 #include "nsIObjectLoadingContent.h"
132 #include "nsIObserver.h"
133 #include "nsIObserverService.h"
134 #include "nsIOfflineCacheUpdate.h"
135 #include "nsIParser.h"
136 #include "nsIParserService.h"
137 #include "nsIPermissionManager.h"
138 #include "nsIPluginHost.h"
139 #include "nsIRunnable.h"
140 #include "nsIScriptContext.h"
141 #include "nsIScriptError.h"
142 #include "nsIScriptGlobalObject.h"
143 #include "nsIScriptObjectPrincipal.h"
144 #include "nsIScriptSecurityManager.h"
145 #include "nsIStringBundle.h"
146 #include "nsIURI.h"
147 #include "nsIURL.h"
148 #include "nsIWebNavigation.h"
149 #include "nsIWordBreaker.h"
150 #include "nsIXPConnect.h"
151 #include "nsJSUtils.h"
152 #include "nsLWBrkCIID.h"
153 #include "nsNetCID.h"
154 #include "nsNetUtil.h"
155 #include "nsNodeInfoManager.h"
156 #include "nsNullPrincipal.h"
157 #include "nsParserCIID.h"
158 #include "nsParserConstants.h"
159 #include "nsPIDOMWindow.h"
160 #include "nsPresContext.h"
161 #include "nsPrintfCString.h"
162 #include "nsReferencedElement.h"
163 #include "nsSandboxFlags.h"
164 #include "nsScriptSecurityManager.h"
165 #include "nsSVGFeatures.h"
166 #include "nsTextEditorState.h"
167 #include "nsTextFragment.h"
168 #include "nsTextNode.h"
169 #include "nsThreadUtils.h"
170 #include "nsUnicharUtilCIID.h"
171 #include "nsUnicodeProperties.h"
172 #include "nsViewManager.h"
173 #include "nsViewportInfo.h"
174 #include "nsWrapperCacheInlines.h"
175 #include "nsXULPopupManager.h"
176 #include "xpcprivate.h" // nsXPConnect
177 #include "HTMLSplitOnSpacesTokenizer.h"
178 #include "nsContentTypeParser.h"
179 #include "mozIThirdPartyUtil.h"
181 #include "nsIBidiKeyboard.h"
183 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
184 const char** next, char16_t* result);
185 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end,
186 int ns_aware, const char** colon);
188 class imgLoader;
190 using namespace mozilla::dom;
191 using namespace mozilla::layers;
192 using namespace mozilla::widget;
193 using namespace mozilla;
195 const char kLoadAsData[] = "loadAsData";
197 nsIXPConnect *nsContentUtils::sXPConnect;
198 nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
199 nsIParserService *nsContentUtils::sParserService = nullptr;
200 nsNameSpaceManager *nsContentUtils::sNameSpaceManager;
201 nsIIOService *nsContentUtils::sIOService;
202 nsIConsoleService *nsContentUtils::sConsoleService;
203 nsDataHashtable<nsISupportsHashKey, EventNameMapping>* nsContentUtils::sAtomEventTable = nullptr;
204 nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr;
205 nsCOMArray<nsIAtom>* nsContentUtils::sUserDefinedEvents = nullptr;
206 nsIStringBundleService *nsContentUtils::sStringBundleService;
207 nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT];
208 nsIContentPolicy *nsContentUtils::sContentPolicyService;
209 bool nsContentUtils::sTriedToGetContentPolicy = false;
210 nsILineBreaker *nsContentUtils::sLineBreaker;
211 nsIWordBreaker *nsContentUtils::sWordBreaker;
212 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nullptr;
213 uint32_t nsContentUtils::sScriptBlockerCount = 0;
214 #ifdef DEBUG
215 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
216 #endif
217 uint32_t nsContentUtils::sMicroTaskLevel = 0;
218 nsTArray< nsCOMPtr<nsIRunnable> >* nsContentUtils::sBlockedScriptRunners = nullptr;
219 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
220 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
222 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
223 bool nsContentUtils::sAllowXULXBL_for_file = false;
225 nsString* nsContentUtils::sShiftText = nullptr;
226 nsString* nsContentUtils::sControlText = nullptr;
227 nsString* nsContentUtils::sMetaText = nullptr;
228 nsString* nsContentUtils::sOSText = nullptr;
229 nsString* nsContentUtils::sAltText = nullptr;
230 nsString* nsContentUtils::sModifierSeparator = nullptr;
232 bool nsContentUtils::sInitialized = false;
233 bool nsContentUtils::sIsFullScreenApiEnabled = false;
234 bool nsContentUtils::sTrustedFullScreenOnly = true;
235 bool nsContentUtils::sFullscreenApiIsContentOnly = false;
236 bool nsContentUtils::sIsIdleObserverAPIEnabled = false;
237 bool nsContentUtils::sIsPerformanceTimingEnabled = false;
238 bool nsContentUtils::sIsResourceTimingEnabled = false;
240 uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
242 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
243 nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
244 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
245 bool nsContentUtils::sFragmentParsingActive = false;
247 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
248 bool nsContentUtils::sDOMWindowDumpEnabled;
249 #endif
251 namespace {
253 static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
254 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
256 static PLDHashTable sEventListenerManagersHash;
258 class DOMEventListenerManagersHashReporter MOZ_FINAL : public nsIMemoryReporter
259 {
260 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
262 public:
263 NS_DECL_ISUPPORTS
265 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
266 nsISupports* aData)
267 {
268 // We don't measure the |EventListenerManager| objects pointed to by the
269 // entries because those references are non-owning.
270 int64_t amount = sEventListenerManagersHash.ops
271 ? PL_DHashTableSizeOfExcludingThis(
272 &sEventListenerManagersHash, nullptr, MallocSizeOf)
273 : 0;
275 return MOZ_COLLECT_REPORT(
276 "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
277 amount,
278 "Memory used by the event listener manager's hash table.");
279 }
280 };
282 NS_IMPL_ISUPPORTS(DOMEventListenerManagersHashReporter, nsIMemoryReporter)
284 class EventListenerManagerMapEntry : public PLDHashEntryHdr
285 {
286 public:
287 EventListenerManagerMapEntry(const void *aKey)
288 : mKey(aKey)
289 {
290 }
292 ~EventListenerManagerMapEntry()
293 {
294 NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
295 }
297 protected: // declared protected to silence clang warnings
298 const void *mKey; // must be first, to look like PLDHashEntryStub
300 public:
301 nsRefPtr<EventListenerManager> mListenerManager;
302 };
304 static bool
305 EventListenerManagerHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
306 const void *key)
307 {
308 // Initialize the entry with placement new
309 new (entry) EventListenerManagerMapEntry(key);
310 return true;
311 }
313 static void
314 EventListenerManagerHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
315 {
316 EventListenerManagerMapEntry *lm =
317 static_cast<EventListenerManagerMapEntry *>(entry);
319 // Let the EventListenerManagerMapEntry clean itself up...
320 lm->~EventListenerManagerMapEntry();
321 }
323 class SameOriginChecker MOZ_FINAL : public nsIChannelEventSink,
324 public nsIInterfaceRequestor
325 {
326 NS_DECL_ISUPPORTS
327 NS_DECL_NSICHANNELEVENTSINK
328 NS_DECL_NSIINTERFACEREQUESTOR
329 };
331 class CharsetDetectionObserver MOZ_FINAL : public nsICharsetDetectionObserver
332 {
333 public:
334 NS_DECL_ISUPPORTS
336 NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf)
337 {
338 mCharset = aCharset;
339 return NS_OK;
340 }
342 const nsACString& GetResult() const
343 {
344 return mCharset;
345 }
347 private:
348 nsCString mCharset;
349 };
351 } // anonymous namespace
353 /* static */
354 TimeDuration
355 nsContentUtils::HandlingUserInputTimeout()
356 {
357 return TimeDuration::FromMilliseconds(sHandlingInputTimeout);
358 }
360 // static
361 nsresult
362 nsContentUtils::Init()
363 {
364 if (sInitialized) {
365 NS_WARNING("Init() called twice");
367 return NS_OK;
368 }
370 sNameSpaceManager = nsNameSpaceManager::GetInstance();
371 NS_ENSURE_TRUE(sNameSpaceManager, NS_ERROR_OUT_OF_MEMORY);
373 sXPConnect = nsXPConnect::XPConnect();
375 sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
376 if(!sSecurityManager)
377 return NS_ERROR_FAILURE;
378 NS_ADDREF(sSecurityManager);
380 // Getting the first context can trigger GC, so do this non-lazily.
381 sXPConnect->InitSafeJSContext();
383 nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
384 if (NS_FAILED(rv)) {
385 // This makes life easier, but we can live without it.
387 sIOService = nullptr;
388 }
390 rv = CallGetService(NS_LBRK_CONTRACTID, &sLineBreaker);
391 NS_ENSURE_SUCCESS(rv, rv);
393 rv = CallGetService(NS_WBRK_CONTRACTID, &sWordBreaker);
394 NS_ENSURE_SUCCESS(rv, rv);
396 if (!InitializeEventTable())
397 return NS_ERROR_FAILURE;
399 if (!sEventListenerManagersHash.ops) {
400 static const PLDHashTableOps hash_table_ops =
401 {
402 PL_DHashAllocTable,
403 PL_DHashFreeTable,
404 PL_DHashVoidPtrKeyStub,
405 PL_DHashMatchEntryStub,
406 PL_DHashMoveEntryStub,
407 EventListenerManagerHashClearEntry,
408 PL_DHashFinalizeStub,
409 EventListenerManagerHashInitEntry
410 };
412 PL_DHashTableInit(&sEventListenerManagersHash, &hash_table_ops,
413 nullptr, sizeof(EventListenerManagerMapEntry), 16);
415 RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
416 }
418 sBlockedScriptRunners = new nsTArray< nsCOMPtr<nsIRunnable> >;
420 Preferences::AddBoolVarCache(&sAllowXULXBL_for_file,
421 "dom.allow_XUL_XBL_for_file");
423 Preferences::AddBoolVarCache(&sIsFullScreenApiEnabled,
424 "full-screen-api.enabled");
426 // Note: We deliberately read this pref here because this code runs
427 // before the profile loads, so users' changes to this pref in about:config
428 // won't have any effect on behaviour. We don't really want users messing
429 // with this pref, as it affects the security model of the fullscreen API.
430 sFullscreenApiIsContentOnly = Preferences::GetBool("full-screen-api.content-only", false);
432 Preferences::AddBoolVarCache(&sTrustedFullScreenOnly,
433 "full-screen-api.allow-trusted-requests-only");
435 sIsIdleObserverAPIEnabled = Preferences::GetBool("dom.idle-observers-api.enabled", true);
437 Preferences::AddBoolVarCache(&sIsPerformanceTimingEnabled,
438 "dom.enable_performance", true);
440 Preferences::AddBoolVarCache(&sIsResourceTimingEnabled,
441 "dom.enable_resource_timing", true);
443 Preferences::AddUintVarCache(&sHandlingInputTimeout,
444 "dom.event.handling-user-input-time-limit",
445 1000);
447 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
448 Preferences::AddBoolVarCache(&sDOMWindowDumpEnabled,
449 "browser.dom.window.dump.enabled");
450 #endif
452 Element::InitCCCallbacks();
454 sInitialized = true;
456 return NS_OK;
457 }
459 void
460 nsContentUtils::GetShiftText(nsAString& text)
461 {
462 if (!sShiftText)
463 InitializeModifierStrings();
464 text.Assign(*sShiftText);
465 }
467 void
468 nsContentUtils::GetControlText(nsAString& text)
469 {
470 if (!sControlText)
471 InitializeModifierStrings();
472 text.Assign(*sControlText);
473 }
475 void
476 nsContentUtils::GetMetaText(nsAString& text)
477 {
478 if (!sMetaText)
479 InitializeModifierStrings();
480 text.Assign(*sMetaText);
481 }
483 void
484 nsContentUtils::GetOSText(nsAString& text)
485 {
486 if (!sOSText) {
487 InitializeModifierStrings();
488 }
489 text.Assign(*sOSText);
490 }
492 void
493 nsContentUtils::GetAltText(nsAString& text)
494 {
495 if (!sAltText)
496 InitializeModifierStrings();
497 text.Assign(*sAltText);
498 }
500 void
501 nsContentUtils::GetModifierSeparatorText(nsAString& text)
502 {
503 if (!sModifierSeparator)
504 InitializeModifierStrings();
505 text.Assign(*sModifierSeparator);
506 }
508 void
509 nsContentUtils::InitializeModifierStrings()
510 {
511 //load the display strings for the keyboard accelerators
512 nsCOMPtr<nsIStringBundleService> bundleService =
513 mozilla::services::GetStringBundleService();
514 nsCOMPtr<nsIStringBundle> bundle;
515 DebugOnly<nsresult> rv = NS_OK;
516 if (bundleService) {
517 rv = bundleService->CreateBundle( "chrome://global-platform/locale/platformKeys.properties",
518 getter_AddRefs(bundle));
519 }
521 NS_ASSERTION(NS_SUCCEEDED(rv) && bundle, "chrome://global/locale/platformKeys.properties could not be loaded");
522 nsXPIDLString shiftModifier;
523 nsXPIDLString metaModifier;
524 nsXPIDLString osModifier;
525 nsXPIDLString altModifier;
526 nsXPIDLString controlModifier;
527 nsXPIDLString modifierSeparator;
528 if (bundle) {
529 //macs use symbols for each modifier key, so fetch each from the bundle, which also covers i18n
530 bundle->GetStringFromName(MOZ_UTF16("VK_SHIFT"), getter_Copies(shiftModifier));
531 bundle->GetStringFromName(MOZ_UTF16("VK_META"), getter_Copies(metaModifier));
532 bundle->GetStringFromName(MOZ_UTF16("VK_WIN"), getter_Copies(osModifier));
533 bundle->GetStringFromName(MOZ_UTF16("VK_ALT"), getter_Copies(altModifier));
534 bundle->GetStringFromName(MOZ_UTF16("VK_CONTROL"), getter_Copies(controlModifier));
535 bundle->GetStringFromName(MOZ_UTF16("MODIFIER_SEPARATOR"), getter_Copies(modifierSeparator));
536 }
537 //if any of these don't exist, we get an empty string
538 sShiftText = new nsString(shiftModifier);
539 sMetaText = new nsString(metaModifier);
540 sOSText = new nsString(osModifier);
541 sAltText = new nsString(altModifier);
542 sControlText = new nsString(controlModifier);
543 sModifierSeparator = new nsString(modifierSeparator);
544 }
546 bool
547 nsContentUtils::InitializeEventTable() {
548 NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
549 NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
551 static const EventNameMapping eventArray[] = {
552 #define EVENT(name_, _id, _type, _struct) \
553 { nsGkAtoms::on##name_, _id, _type, _struct },
554 #define WINDOW_ONLY_EVENT EVENT
555 #define NON_IDL_EVENT EVENT
556 #include "mozilla/EventNameList.h"
557 #undef WINDOW_ONLY_EVENT
558 #undef EVENT
559 { nullptr }
560 };
562 sAtomEventTable = new nsDataHashtable<nsISupportsHashKey, EventNameMapping>(
563 int(ArrayLength(eventArray) / 0.75) + 1);
564 sStringEventTable = new nsDataHashtable<nsStringHashKey, EventNameMapping>(
565 int(ArrayLength(eventArray) / 0.75) + 1);
566 sUserDefinedEvents = new nsCOMArray<nsIAtom>(64);
568 // Subtract one from the length because of the trailing null
569 for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) {
570 sAtomEventTable->Put(eventArray[i].mAtom, eventArray[i]);
571 sStringEventTable->Put(Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
572 eventArray[i]);
573 }
575 return true;
576 }
578 void
579 nsContentUtils::InitializeTouchEventTable()
580 {
581 static bool sEventTableInitialized = false;
582 if (!sEventTableInitialized && sAtomEventTable && sStringEventTable) {
583 sEventTableInitialized = true;
584 static const EventNameMapping touchEventArray[] = {
585 #define EVENT(name_, _id, _type, _struct)
586 #define TOUCH_EVENT(name_, _id, _type, _struct) \
587 { nsGkAtoms::on##name_, _id, _type, _struct },
588 #include "mozilla/EventNameList.h"
589 #undef TOUCH_EVENT
590 #undef EVENT
591 { nullptr }
592 };
593 // Subtract one from the length because of the trailing null
594 for (uint32_t i = 0; i < ArrayLength(touchEventArray) - 1; ++i) {
595 sAtomEventTable->Put(touchEventArray[i].mAtom, touchEventArray[i]);
596 sStringEventTable->Put(Substring(nsDependentAtomString(touchEventArray[i].mAtom), 2),
597 touchEventArray[i]);
598 }
599 }
600 }
602 static bool
603 Is8bit(const nsAString& aString)
604 {
605 static const char16_t EIGHT_BIT = char16_t(~0x00FF);
607 nsAString::const_iterator done_reading;
608 aString.EndReading(done_reading);
610 // for each chunk of |aString|...
611 uint32_t fragmentLength = 0;
612 nsAString::const_iterator iter;
613 for (aString.BeginReading(iter); iter != done_reading;
614 iter.advance(int32_t(fragmentLength))) {
615 fragmentLength = uint32_t(iter.size_forward());
616 const char16_t* c = iter.get();
617 const char16_t* fragmentEnd = c + fragmentLength;
619 // for each character in this chunk...
620 while (c < fragmentEnd) {
621 if (*c++ & EIGHT_BIT) {
622 return false;
623 }
624 }
625 }
627 return true;
628 }
630 nsresult
631 nsContentUtils::Btoa(const nsAString& aBinaryData,
632 nsAString& aAsciiBase64String)
633 {
634 if (!Is8bit(aBinaryData)) {
635 aAsciiBase64String.Truncate();
636 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
637 }
639 return Base64Encode(aBinaryData, aAsciiBase64String);
640 }
642 nsresult
643 nsContentUtils::Atob(const nsAString& aAsciiBase64String,
644 nsAString& aBinaryData)
645 {
646 if (!Is8bit(aAsciiBase64String)) {
647 aBinaryData.Truncate();
648 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
649 }
651 const char16_t* start = aAsciiBase64String.BeginReading();
652 const char16_t* end = aAsciiBase64String.EndReading();
653 nsString trimmedString;
654 if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible_t())) {
655 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
656 }
657 while (start < end) {
658 if (!nsContentUtils::IsHTMLWhitespace(*start)) {
659 trimmedString.Append(*start);
660 }
661 start++;
662 }
663 nsresult rv = Base64Decode(trimmedString, aBinaryData);
664 if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
665 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
666 }
667 return rv;
668 }
670 bool
671 nsContentUtils::IsAutocompleteEnabled(nsIDOMHTMLInputElement* aInput)
672 {
673 NS_PRECONDITION(aInput, "aInput should not be null!");
675 nsAutoString autocomplete;
676 aInput->GetAutocomplete(autocomplete);
678 if (autocomplete.IsEmpty()) {
679 nsCOMPtr<nsIDOMHTMLFormElement> form;
680 aInput->GetForm(getter_AddRefs(form));
681 if (!form) {
682 return true;
683 }
685 form->GetAutocomplete(autocomplete);
686 }
688 return autocomplete.EqualsLiteral("on");
689 }
691 #define SKIP_WHITESPACE(iter, end_iter, end_res) \
692 while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
693 ++(iter); \
694 } \
695 if ((iter) == (end_iter)) { \
696 return (end_res); \
697 }
699 #define SKIP_ATTR_NAME(iter, end_iter) \
700 while ((iter) != (end_iter) && !nsCRT::IsAsciiSpace(*(iter)) && \
701 *(iter) != '=') { \
702 ++(iter); \
703 }
705 bool
706 nsContentUtils::GetPseudoAttributeValue(const nsString& aSource, nsIAtom *aName,
707 nsAString& aValue)
708 {
709 aValue.Truncate();
711 const char16_t *start = aSource.get();
712 const char16_t *end = start + aSource.Length();
713 const char16_t *iter;
715 while (start != end) {
716 SKIP_WHITESPACE(start, end, false)
717 iter = start;
718 SKIP_ATTR_NAME(iter, end)
720 if (start == iter) {
721 return false;
722 }
724 // Remember the attr name.
725 const nsDependentSubstring & attrName = Substring(start, iter);
727 // Now check whether this is a valid name="value" pair.
728 start = iter;
729 SKIP_WHITESPACE(start, end, false)
730 if (*start != '=') {
731 // No '=', so this is not a name="value" pair. We don't know
732 // what it is, and we have no way to handle it.
733 return false;
734 }
736 // Have to skip the value.
737 ++start;
738 SKIP_WHITESPACE(start, end, false)
739 char16_t q = *start;
740 if (q != kQuote && q != kApostrophe) {
741 // Not a valid quoted value, so bail.
742 return false;
743 }
745 ++start; // Point to the first char of the value.
746 iter = start;
748 while (iter != end && *iter != q) {
749 ++iter;
750 }
752 if (iter == end) {
753 // Oops, unterminated quoted string.
754 return false;
755 }
757 // At this point attrName holds the name of the "attribute" and
758 // the value is between start and iter.
760 if (aName->Equals(attrName)) {
761 // We'll accumulate as many characters as possible (until we hit either
762 // the end of the string or the beginning of an entity). Chunks will be
763 // delimited by start and chunkEnd.
764 const char16_t *chunkEnd = start;
765 while (chunkEnd != iter) {
766 if (*chunkEnd == kLessThan) {
767 aValue.Truncate();
769 return false;
770 }
772 if (*chunkEnd == kAmpersand) {
773 aValue.Append(start, chunkEnd - start);
775 // Point to first character after the ampersand.
776 ++chunkEnd;
778 const char16_t *afterEntity = nullptr;
779 char16_t result[2];
780 uint32_t count =
781 MOZ_XMLTranslateEntity(reinterpret_cast<const char*>(chunkEnd),
782 reinterpret_cast<const char*>(iter),
783 reinterpret_cast<const char**>(&afterEntity),
784 result);
785 if (count == 0) {
786 aValue.Truncate();
788 return false;
789 }
791 aValue.Append(result, count);
793 // Advance to after the entity and begin a new chunk.
794 start = chunkEnd = afterEntity;
795 }
796 else {
797 ++chunkEnd;
798 }
799 }
801 // Append remainder.
802 aValue.Append(start, iter - start);
804 return true;
805 }
807 // Resume scanning after the end of the attribute value (past the quote
808 // char).
809 start = iter + 1;
810 }
812 return false;
813 }
815 bool
816 nsContentUtils::IsJavaScriptLanguage(const nsString& aName)
817 {
818 return aName.LowerCaseEqualsLiteral("javascript") ||
819 aName.LowerCaseEqualsLiteral("livescript") ||
820 aName.LowerCaseEqualsLiteral("mocha") ||
821 aName.LowerCaseEqualsLiteral("javascript1.0") ||
822 aName.LowerCaseEqualsLiteral("javascript1.1") ||
823 aName.LowerCaseEqualsLiteral("javascript1.2") ||
824 aName.LowerCaseEqualsLiteral("javascript1.3") ||
825 aName.LowerCaseEqualsLiteral("javascript1.4") ||
826 aName.LowerCaseEqualsLiteral("javascript1.5");
827 }
829 JSVersion
830 nsContentUtils::ParseJavascriptVersion(const nsAString& aVersionStr)
831 {
832 if (aVersionStr.Length() != 3 || aVersionStr[0] != '1' ||
833 aVersionStr[1] != '.') {
834 return JSVERSION_UNKNOWN;
835 }
837 switch (aVersionStr[2]) {
838 case '0': /* fall through */
839 case '1': /* fall through */
840 case '2': /* fall through */
841 case '3': /* fall through */
842 case '4': /* fall through */
843 case '5': return JSVERSION_DEFAULT;
844 case '6': return JSVERSION_1_6;
845 case '7': return JSVERSION_1_7;
846 case '8': return JSVERSION_1_8;
847 default: return JSVERSION_UNKNOWN;
848 }
849 }
851 void
852 nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType,
853 nsString& aParams)
854 {
855 aType.Truncate();
856 aParams.Truncate();
857 int32_t semiIndex = aValue.FindChar(char16_t(';'));
858 if (-1 != semiIndex) {
859 aType = Substring(aValue, 0, semiIndex);
860 aParams = Substring(aValue, semiIndex + 1,
861 aValue.Length() - (semiIndex + 1));
862 aParams.StripWhitespace();
863 }
864 else {
865 aType = aValue;
866 }
867 aType.StripWhitespace();
868 }
870 nsresult
871 nsContentUtils::IsUserIdle(uint32_t aRequestedIdleTimeInMS, bool* aUserIsIdle)
872 {
873 nsresult rv;
874 nsCOMPtr<nsIIdleService> idleService =
875 do_GetService("@mozilla.org/widget/idleservice;1", &rv);
876 NS_ENSURE_SUCCESS(rv, rv);
878 uint32_t idleTimeInMS;
879 rv = idleService->GetIdleTime(&idleTimeInMS);
880 NS_ENSURE_SUCCESS(rv, rv);
882 *aUserIsIdle = idleTimeInMS >= aRequestedIdleTimeInMS;
883 return NS_OK;
884 }
886 /**
887 * Access a cached parser service. Don't addref. We need only one
888 * reference to it and this class has that one.
889 */
890 /* static */
891 nsIParserService*
892 nsContentUtils::GetParserService()
893 {
894 // XXX: This isn't accessed from several threads, is it?
895 if (!sParserService) {
896 // Lock, recheck sCachedParserService and aquire if this should be
897 // safe for multiple threads.
898 nsresult rv = CallGetService(kParserServiceCID, &sParserService);
899 if (NS_FAILED(rv)) {
900 sParserService = nullptr;
901 }
902 }
904 return sParserService;
905 }
907 /**
908 * A helper function that parses a sandbox attribute (of an <iframe> or
909 * a CSP directive) and converts it to the set of flags used internally.
910 *
911 * @param sandboxAttr the sandbox attribute
912 * @return the set of flags (0 if sandboxAttr is null)
913 */
914 uint32_t
915 nsContentUtils::ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr)
916 {
917 // No sandbox attribute, no sandbox flags.
918 if (!sandboxAttr) { return 0; }
920 // Start off by setting all the restriction flags.
921 uint32_t out = SANDBOXED_NAVIGATION
922 | SANDBOXED_AUXILIARY_NAVIGATION
923 | SANDBOXED_TOPLEVEL_NAVIGATION
924 | SANDBOXED_PLUGINS
925 | SANDBOXED_ORIGIN
926 | SANDBOXED_FORMS
927 | SANDBOXED_SCRIPTS
928 | SANDBOXED_AUTOMATIC_FEATURES
929 | SANDBOXED_POINTER_LOCK
930 | SANDBOXED_DOMAIN;
932 // Macro for updating the flag according to the keywords
933 #define IF_KEYWORD(atom, flags) \
934 if (sandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { out &= ~(flags); }
936 IF_KEYWORD(allowsameorigin, SANDBOXED_ORIGIN)
937 IF_KEYWORD(allowforms, SANDBOXED_FORMS)
938 IF_KEYWORD(allowscripts, SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES)
939 IF_KEYWORD(allowtopnavigation, SANDBOXED_TOPLEVEL_NAVIGATION)
940 IF_KEYWORD(allowpointerlock, SANDBOXED_POINTER_LOCK)
941 IF_KEYWORD(allowpopups, SANDBOXED_AUXILIARY_NAVIGATION)
943 return out;
944 #undef IF_KEYWORD
945 }
947 nsIBidiKeyboard*
948 nsContentUtils::GetBidiKeyboard()
949 {
950 if (!sBidiKeyboard) {
951 nsresult rv = CallGetService("@mozilla.org/widget/bidikeyboard;1", &sBidiKeyboard);
952 if (NS_FAILED(rv)) {
953 sBidiKeyboard = nullptr;
954 }
955 }
956 return sBidiKeyboard;
957 }
959 template <class OutputIterator>
960 struct NormalizeNewlinesCharTraits {
961 public:
962 typedef typename OutputIterator::value_type value_type;
964 public:
965 NormalizeNewlinesCharTraits(OutputIterator& aIterator) : mIterator(aIterator) { }
966 void writechar(typename OutputIterator::value_type aChar) {
967 *mIterator++ = aChar;
968 }
970 private:
971 OutputIterator mIterator;
972 };
974 template <class CharT>
975 struct NormalizeNewlinesCharTraits<CharT*> {
976 public:
977 typedef CharT value_type;
979 public:
980 NormalizeNewlinesCharTraits(CharT* aCharPtr) : mCharPtr(aCharPtr) { }
981 void writechar(CharT aChar) {
982 *mCharPtr++ = aChar;
983 }
985 private:
986 CharT* mCharPtr;
987 };
989 template <class OutputIterator>
990 class CopyNormalizeNewlines
991 {
992 public:
993 typedef typename OutputIterator::value_type value_type;
995 public:
996 CopyNormalizeNewlines(OutputIterator* aDestination,
997 bool aLastCharCR=false) :
998 mLastCharCR(aLastCharCR),
999 mDestination(aDestination),
1000 mWritten(0)
1001 { }
1003 uint32_t GetCharsWritten() {
1004 return mWritten;
1005 }
1007 bool IsLastCharCR() {
1008 return mLastCharCR;
1009 }
1011 void write(const typename OutputIterator::value_type* aSource, uint32_t aSourceLength) {
1013 const typename OutputIterator::value_type* done_writing = aSource + aSourceLength;
1015 // If the last source buffer ended with a CR...
1016 if (mLastCharCR) {
1017 // ..and if the next one is a LF, then skip it since
1018 // we've already written out a newline
1019 if (aSourceLength && (*aSource == value_type('\n'))) {
1020 ++aSource;
1021 }
1022 mLastCharCR = false;
1023 }
1025 uint32_t num_written = 0;
1026 while ( aSource < done_writing ) {
1027 if (*aSource == value_type('\r')) {
1028 mDestination->writechar('\n');
1029 ++aSource;
1030 // If we've reached the end of the buffer, record
1031 // that we wrote out a CR
1032 if (aSource == done_writing) {
1033 mLastCharCR = true;
1034 }
1035 // If the next character is a LF, skip it
1036 else if (*aSource == value_type('\n')) {
1037 ++aSource;
1038 }
1039 }
1040 else {
1041 mDestination->writechar(*aSource++);
1042 }
1043 ++num_written;
1044 }
1046 mWritten += num_written;
1047 }
1049 private:
1050 bool mLastCharCR;
1051 OutputIterator* mDestination;
1052 uint32_t mWritten;
1053 };
1055 // static
1056 uint32_t
1057 nsContentUtils::CopyNewlineNormalizedUnicodeTo(const nsAString& aSource,
1058 uint32_t aSrcOffset,
1059 char16_t* aDest,
1060 uint32_t aLength,
1061 bool& aLastCharCR)
1062 {
1063 typedef NormalizeNewlinesCharTraits<char16_t*> sink_traits;
1065 sink_traits dest_traits(aDest);
1066 CopyNormalizeNewlines<sink_traits> normalizer(&dest_traits,aLastCharCR);
1067 nsReadingIterator<char16_t> fromBegin, fromEnd;
1068 copy_string(aSource.BeginReading(fromBegin).advance( int32_t(aSrcOffset) ),
1069 aSource.BeginReading(fromEnd).advance( int32_t(aSrcOffset+aLength) ),
1070 normalizer);
1071 aLastCharCR = normalizer.IsLastCharCR();
1072 return normalizer.GetCharsWritten();
1073 }
1075 // static
1076 uint32_t
1077 nsContentUtils::CopyNewlineNormalizedUnicodeTo(nsReadingIterator<char16_t>& aSrcStart, const nsReadingIterator<char16_t>& aSrcEnd, nsAString& aDest)
1078 {
1079 typedef nsWritingIterator<char16_t> WritingIterator;
1080 typedef NormalizeNewlinesCharTraits<WritingIterator> sink_traits;
1082 WritingIterator iter;
1083 aDest.BeginWriting(iter);
1084 sink_traits dest_traits(iter);
1085 CopyNormalizeNewlines<sink_traits> normalizer(&dest_traits);
1086 copy_string(aSrcStart, aSrcEnd, normalizer);
1087 return normalizer.GetCharsWritten();
1088 }
1090 /**
1091 * This is used to determine whether a character is in one of the punctuation
1092 * mark classes which CSS says should be part of the first-letter.
1093 * See http://www.w3.org/TR/CSS2/selector.html#first-letter and
1094 * http://www.w3.org/TR/selectors/#first-letter
1095 */
1097 // static
1098 bool
1099 nsContentUtils::IsFirstLetterPunctuation(uint32_t aChar)
1100 {
1101 uint8_t cat = mozilla::unicode::GetGeneralCategory(aChar);
1103 return (cat == HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION || // Ps
1104 cat == HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION || // Pe
1105 cat == HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION || // Pi
1106 cat == HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION || // Pf
1107 cat == HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION); // Po
1108 }
1110 // static
1111 bool
1112 nsContentUtils::IsFirstLetterPunctuationAt(const nsTextFragment* aFrag, uint32_t aOffset)
1113 {
1114 char16_t h = aFrag->CharAt(aOffset);
1115 if (!IS_SURROGATE(h)) {
1116 return IsFirstLetterPunctuation(h);
1117 }
1118 if (NS_IS_HIGH_SURROGATE(h) && aOffset + 1 < aFrag->GetLength()) {
1119 char16_t l = aFrag->CharAt(aOffset + 1);
1120 if (NS_IS_LOW_SURROGATE(l)) {
1121 return IsFirstLetterPunctuation(SURROGATE_TO_UCS4(h, l));
1122 }
1123 }
1124 return false;
1125 }
1127 // static
1128 bool nsContentUtils::IsAlphanumeric(uint32_t aChar)
1129 {
1130 nsIUGenCategory::nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
1132 return (cat == nsIUGenCategory::kLetter || cat == nsIUGenCategory::kNumber);
1133 }
1135 // static
1136 bool nsContentUtils::IsAlphanumericAt(const nsTextFragment* aFrag, uint32_t aOffset)
1137 {
1138 char16_t h = aFrag->CharAt(aOffset);
1139 if (!IS_SURROGATE(h)) {
1140 return IsAlphanumeric(h);
1141 }
1142 if (NS_IS_HIGH_SURROGATE(h) && aOffset + 1 < aFrag->GetLength()) {
1143 char16_t l = aFrag->CharAt(aOffset + 1);
1144 if (NS_IS_LOW_SURROGATE(l)) {
1145 return IsAlphanumeric(SURROGATE_TO_UCS4(h, l));
1146 }
1147 }
1148 return false;
1149 }
1151 /* static */
1152 bool
1153 nsContentUtils::IsHTMLWhitespace(char16_t aChar)
1154 {
1155 return aChar == char16_t(0x0009) ||
1156 aChar == char16_t(0x000A) ||
1157 aChar == char16_t(0x000C) ||
1158 aChar == char16_t(0x000D) ||
1159 aChar == char16_t(0x0020);
1160 }
1162 /* static */
1163 bool
1164 nsContentUtils::IsHTMLWhitespaceOrNBSP(char16_t aChar)
1165 {
1166 return IsHTMLWhitespace(aChar) || aChar == char16_t(0xA0);
1167 }
1169 /* static */
1170 bool
1171 nsContentUtils::IsHTMLBlock(nsIAtom* aLocalName)
1172 {
1173 return
1174 (aLocalName == nsGkAtoms::address) ||
1175 (aLocalName == nsGkAtoms::article) ||
1176 (aLocalName == nsGkAtoms::aside) ||
1177 (aLocalName == nsGkAtoms::blockquote) ||
1178 (aLocalName == nsGkAtoms::center) ||
1179 (aLocalName == nsGkAtoms::dir) ||
1180 (aLocalName == nsGkAtoms::div) ||
1181 (aLocalName == nsGkAtoms::dl) || // XXX why not dt and dd?
1182 (aLocalName == nsGkAtoms::fieldset) ||
1183 (aLocalName == nsGkAtoms::figure) || // XXX shouldn't figcaption be on this list
1184 (aLocalName == nsGkAtoms::footer) ||
1185 (aLocalName == nsGkAtoms::form) ||
1186 (aLocalName == nsGkAtoms::h1) ||
1187 (aLocalName == nsGkAtoms::h2) ||
1188 (aLocalName == nsGkAtoms::h3) ||
1189 (aLocalName == nsGkAtoms::h4) ||
1190 (aLocalName == nsGkAtoms::h5) ||
1191 (aLocalName == nsGkAtoms::h6) ||
1192 (aLocalName == nsGkAtoms::header) ||
1193 (aLocalName == nsGkAtoms::hgroup) ||
1194 (aLocalName == nsGkAtoms::hr) ||
1195 (aLocalName == nsGkAtoms::li) ||
1196 (aLocalName == nsGkAtoms::listing) ||
1197 (aLocalName == nsGkAtoms::menu) ||
1198 (aLocalName == nsGkAtoms::multicol) || // XXX get rid of this one?
1199 (aLocalName == nsGkAtoms::nav) ||
1200 (aLocalName == nsGkAtoms::ol) ||
1201 (aLocalName == nsGkAtoms::p) ||
1202 (aLocalName == nsGkAtoms::pre) ||
1203 (aLocalName == nsGkAtoms::section) ||
1204 (aLocalName == nsGkAtoms::table) ||
1205 (aLocalName == nsGkAtoms::ul) ||
1206 (aLocalName == nsGkAtoms::xmp);
1207 }
1209 /* static */
1210 bool
1211 nsContentUtils::IsHTMLVoid(nsIAtom* aLocalName)
1212 {
1213 return
1214 (aLocalName == nsGkAtoms::area) ||
1215 (aLocalName == nsGkAtoms::base) ||
1216 (aLocalName == nsGkAtoms::basefont) ||
1217 (aLocalName == nsGkAtoms::bgsound) ||
1218 (aLocalName == nsGkAtoms::br) ||
1219 (aLocalName == nsGkAtoms::col) ||
1220 (aLocalName == nsGkAtoms::command) ||
1221 (aLocalName == nsGkAtoms::embed) ||
1222 (aLocalName == nsGkAtoms::frame) ||
1223 (aLocalName == nsGkAtoms::hr) ||
1224 (aLocalName == nsGkAtoms::img) ||
1225 (aLocalName == nsGkAtoms::input) ||
1226 (aLocalName == nsGkAtoms::keygen) ||
1227 (aLocalName == nsGkAtoms::link) ||
1228 (aLocalName == nsGkAtoms::meta) ||
1229 (aLocalName == nsGkAtoms::param) ||
1230 (aLocalName == nsGkAtoms::source) ||
1231 (aLocalName == nsGkAtoms::track) ||
1232 (aLocalName == nsGkAtoms::wbr);
1233 }
1235 /* static */
1236 bool
1237 nsContentUtils::ParseIntMarginValue(const nsAString& aString, nsIntMargin& result)
1238 {
1239 nsAutoString marginStr(aString);
1240 marginStr.CompressWhitespace(true, true);
1241 if (marginStr.IsEmpty()) {
1242 return false;
1243 }
1245 int32_t start = 0, end = 0;
1246 for (int count = 0; count < 4; count++) {
1247 if ((uint32_t)end >= marginStr.Length())
1248 return false;
1250 // top, right, bottom, left
1251 if (count < 3)
1252 end = Substring(marginStr, start).FindChar(',');
1253 else
1254 end = Substring(marginStr, start).Length();
1256 if (end <= 0)
1257 return false;
1259 nsresult ec;
1260 int32_t val = nsString(Substring(marginStr, start, end)).ToInteger(&ec);
1261 if (NS_FAILED(ec))
1262 return false;
1264 switch(count) {
1265 case 0:
1266 result.top = val;
1267 break;
1268 case 1:
1269 result.right = val;
1270 break;
1271 case 2:
1272 result.bottom = val;
1273 break;
1274 case 3:
1275 result.left = val;
1276 break;
1277 }
1278 start += end + 1;
1279 }
1280 return true;
1281 }
1283 // static
1284 int32_t
1285 nsContentUtils::ParseLegacyFontSize(const nsAString& aValue)
1286 {
1287 nsAString::const_iterator iter, end;
1288 aValue.BeginReading(iter);
1289 aValue.EndReading(end);
1291 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1292 ++iter;
1293 }
1295 if (iter == end) {
1296 return 0;
1297 }
1299 bool relative = false;
1300 bool negate = false;
1301 if (*iter == char16_t('-')) {
1302 relative = true;
1303 negate = true;
1304 ++iter;
1305 } else if (*iter == char16_t('+')) {
1306 relative = true;
1307 ++iter;
1308 }
1310 if (*iter < char16_t('0') || *iter > char16_t('9')) {
1311 return 0;
1312 }
1314 // We don't have to worry about overflow, since we can bail out as soon as
1315 // we're bigger than 7.
1316 int32_t value = 0;
1317 while (iter != end && *iter >= char16_t('0') && *iter <= char16_t('9')) {
1318 value = 10*value + (*iter - char16_t('0'));
1319 if (value >= 7) {
1320 break;
1321 }
1322 ++iter;
1323 }
1325 if (relative) {
1326 if (negate) {
1327 value = 3 - value;
1328 } else {
1329 value = 3 + value;
1330 }
1331 }
1333 return clamped(value, 1, 7);
1334 }
1336 /* static */
1337 void
1338 nsContentUtils::GetOfflineAppManifest(nsIDocument *aDocument, nsIURI **aURI)
1339 {
1340 Element* docElement = aDocument->GetRootElement();
1341 if (!docElement) {
1342 return;
1343 }
1345 nsAutoString manifestSpec;
1346 docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
1348 // Manifest URIs can't have fragment identifiers.
1349 if (manifestSpec.IsEmpty() ||
1350 manifestSpec.FindChar('#') != kNotFound) {
1351 return;
1352 }
1354 nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec,
1355 aDocument,
1356 aDocument->GetDocBaseURI());
1357 }
1359 /* static */
1360 bool
1361 nsContentUtils::OfflineAppAllowed(nsIURI *aURI)
1362 {
1363 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1364 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
1365 if (!updateService) {
1366 return false;
1367 }
1369 bool allowed;
1370 nsresult rv =
1371 updateService->OfflineAppAllowedForURI(aURI,
1372 Preferences::GetRootBranch(),
1373 &allowed);
1374 return NS_SUCCEEDED(rv) && allowed;
1375 }
1377 /* static */
1378 bool
1379 nsContentUtils::OfflineAppAllowed(nsIPrincipal *aPrincipal)
1380 {
1381 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1382 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
1383 if (!updateService) {
1384 return false;
1385 }
1387 bool allowed;
1388 nsresult rv = updateService->OfflineAppAllowed(aPrincipal,
1389 Preferences::GetRootBranch(),
1390 &allowed);
1391 return NS_SUCCEEDED(rv) && allowed;
1392 }
1394 bool
1395 nsContentUtils::MaybeAllowOfflineAppByDefault(nsIPrincipal *aPrincipal,
1396 nsIDOMWindow *aWindow)
1397 {
1398 if (!Preferences::GetRootBranch())
1399 return false;
1401 nsresult rv;
1403 bool allowedByDefault;
1404 rv = Preferences::GetRootBranch()->GetBoolPref(
1405 "offline-apps.allow_by_default", &allowedByDefault);
1406 if (NS_FAILED(rv))
1407 return false;
1409 if (!allowedByDefault)
1410 return false;
1412 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1413 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
1414 if (!updateService) {
1415 return false;
1416 }
1418 rv = updateService->AllowOfflineApp(aWindow, aPrincipal);
1419 return NS_SUCCEEDED(rv);
1420 }
1422 // static
1423 void
1424 nsContentUtils::Shutdown()
1425 {
1426 sInitialized = false;
1428 NS_IF_RELEASE(sContentPolicyService);
1429 sTriedToGetContentPolicy = false;
1430 uint32_t i;
1431 for (i = 0; i < PropertiesFile_COUNT; ++i)
1432 NS_IF_RELEASE(sStringBundles[i]);
1434 NS_IF_RELEASE(sStringBundleService);
1435 NS_IF_RELEASE(sConsoleService);
1436 sXPConnect = nullptr;
1437 NS_IF_RELEASE(sSecurityManager);
1438 NS_IF_RELEASE(sParserService);
1439 NS_IF_RELEASE(sIOService);
1440 NS_IF_RELEASE(sLineBreaker);
1441 NS_IF_RELEASE(sWordBreaker);
1442 NS_IF_RELEASE(sBidiKeyboard);
1444 delete sAtomEventTable;
1445 sAtomEventTable = nullptr;
1446 delete sStringEventTable;
1447 sStringEventTable = nullptr;
1448 delete sUserDefinedEvents;
1449 sUserDefinedEvents = nullptr;
1451 if (sEventListenerManagersHash.ops) {
1452 NS_ASSERTION(sEventListenerManagersHash.entryCount == 0,
1453 "Event listener manager hash not empty at shutdown!");
1455 // See comment above.
1457 // However, we have to handle this table differently. If it still
1458 // has entries, we want to leak it too, so that we can keep it alive
1459 // in case any elements are destroyed. Because if they are, we need
1460 // their event listener managers to be destroyed too, or otherwise
1461 // it could leave dangling references in DOMClassInfo's preserved
1462 // wrapper table.
1464 if (sEventListenerManagersHash.entryCount == 0) {
1465 PL_DHashTableFinish(&sEventListenerManagersHash);
1466 sEventListenerManagersHash.ops = nullptr;
1467 }
1468 }
1470 NS_ASSERTION(!sBlockedScriptRunners ||
1471 sBlockedScriptRunners->Length() == 0,
1472 "How'd this happen?");
1473 delete sBlockedScriptRunners;
1474 sBlockedScriptRunners = nullptr;
1476 delete sShiftText;
1477 sShiftText = nullptr;
1478 delete sControlText;
1479 sControlText = nullptr;
1480 delete sMetaText;
1481 sMetaText = nullptr;
1482 delete sOSText;
1483 sOSText = nullptr;
1484 delete sAltText;
1485 sAltText = nullptr;
1486 delete sModifierSeparator;
1487 sModifierSeparator = nullptr;
1489 NS_IF_RELEASE(sSameOriginChecker);
1490 }
1492 /**
1493 * Checks whether two nodes come from the same origin. aTrustedNode is
1494 * considered 'safe' in that a user can operate on it and that it isn't
1495 * a js-object that implements nsIDOMNode.
1496 * Never call this function with the first node provided by script, it
1497 * must always be known to be a 'real' node!
1498 */
1499 // static
1500 nsresult
1501 nsContentUtils::CheckSameOrigin(const nsINode *aTrustedNode,
1502 nsIDOMNode *aUnTrustedNode)
1503 {
1504 MOZ_ASSERT(aTrustedNode);
1506 // Make sure it's a real node.
1507 nsCOMPtr<nsINode> unTrustedNode = do_QueryInterface(aUnTrustedNode);
1508 NS_ENSURE_TRUE(unTrustedNode, NS_ERROR_UNEXPECTED);
1509 return CheckSameOrigin(aTrustedNode, unTrustedNode);
1510 }
1512 nsresult
1513 nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
1514 const nsINode* unTrustedNode)
1515 {
1516 MOZ_ASSERT(aTrustedNode);
1517 MOZ_ASSERT(unTrustedNode);
1519 bool isSystem = false;
1520 nsresult rv = sSecurityManager->SubjectPrincipalIsSystem(&isSystem);
1521 NS_ENSURE_SUCCESS(rv, rv);
1523 if (isSystem) {
1524 // we're running as system, grant access to the node.
1526 return NS_OK;
1527 }
1529 /*
1530 * Get hold of each node's principal
1531 */
1533 nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
1534 nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
1536 if (trustedPrincipal == unTrustedPrincipal) {
1537 return NS_OK;
1538 }
1540 bool equal;
1541 // XXXbz should we actually have a Subsumes() check here instead? Or perhaps
1542 // a separate method for that, with callers using one or the other?
1543 if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
1544 !equal) {
1545 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
1546 }
1548 return NS_OK;
1549 }
1551 // static
1552 bool
1553 nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
1554 nsIPrincipal* aPrincipal)
1555 {
1556 bool subsumes;
1557 nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
1558 NS_ENSURE_SUCCESS(rv, false);
1560 if (subsumes) {
1561 return true;
1562 }
1564 // The subject doesn't subsume aPrincipal. Allow access only if the subject
1565 // is chrome.
1566 return IsCallerChrome();
1567 }
1569 // static
1570 bool
1571 nsContentUtils::CanCallerAccess(nsIDOMNode *aNode)
1572 {
1573 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
1574 NS_ENSURE_TRUE(node, false);
1575 return CanCallerAccess(node);
1576 }
1578 // static
1579 bool
1580 nsContentUtils::CanCallerAccess(nsINode* aNode)
1581 {
1582 // XXXbz why not check the IsCapabilityEnabled thing up front, and not bother
1583 // with the system principal games? But really, there should be a simpler
1584 // API here, dammit.
1585 nsCOMPtr<nsIPrincipal> subjectPrincipal;
1586 nsresult rv = sSecurityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1587 NS_ENSURE_SUCCESS(rv, false);
1589 if (!subjectPrincipal) {
1590 // we're running as system, grant access to the node.
1592 return true;
1593 }
1595 return CanCallerAccess(subjectPrincipal, aNode->NodePrincipal());
1596 }
1598 // static
1599 bool
1600 nsContentUtils::CanCallerAccess(nsPIDOMWindow* aWindow)
1601 {
1602 // XXXbz why not check the IsCapabilityEnabled thing up front, and not bother
1603 // with the system principal games? But really, there should be a simpler
1604 // API here, dammit.
1605 nsCOMPtr<nsIPrincipal> subjectPrincipal;
1606 nsresult rv = sSecurityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1607 NS_ENSURE_SUCCESS(rv, false);
1609 if (!subjectPrincipal) {
1610 // we're running as system, grant access to the node.
1612 return true;
1613 }
1615 nsCOMPtr<nsIScriptObjectPrincipal> scriptObject =
1616 do_QueryInterface(aWindow->IsOuterWindow() ?
1617 aWindow->GetCurrentInnerWindow() : aWindow);
1618 NS_ENSURE_TRUE(scriptObject, false);
1620 return CanCallerAccess(subjectPrincipal, scriptObject->GetPrincipal());
1621 }
1623 //static
1624 bool
1625 nsContentUtils::InProlog(nsINode *aNode)
1626 {
1627 NS_PRECONDITION(aNode, "missing node to nsContentUtils::InProlog");
1629 nsINode* parent = aNode->GetParentNode();
1630 if (!parent || !parent->IsNodeOfType(nsINode::eDOCUMENT)) {
1631 return false;
1632 }
1634 nsIDocument* doc = static_cast<nsIDocument*>(parent);
1635 nsIContent* root = doc->GetRootElement();
1637 return !root || doc->IndexOf(aNode) < doc->IndexOf(root);
1638 }
1640 JSContext *
1641 nsContentUtils::GetContextFromDocument(nsIDocument *aDocument)
1642 {
1643 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aDocument->GetScopeObject());
1644 if (!sgo) {
1645 // No script global, no context.
1646 return nullptr;
1647 }
1649 nsIScriptContext *scx = sgo->GetContext();
1650 if (!scx) {
1651 // No context left in the scope...
1652 return nullptr;
1653 }
1655 return scx->GetNativeContext();
1656 }
1658 //static
1659 void
1660 nsContentUtils::TraceSafeJSContext(JSTracer* aTrc)
1661 {
1662 JSContext* cx = GetSafeJSContext();
1663 if (!cx) {
1664 return;
1665 }
1666 if (JSObject* global = js::DefaultObjectForContextOrNull(cx)) {
1667 JS::AssertGCThingMustBeTenured(global);
1668 JS_CallObjectTracer(aTrc, &global, "safe context");
1669 MOZ_ASSERT(global == js::DefaultObjectForContextOrNull(cx));
1670 }
1671 }
1673 nsPIDOMWindow *
1674 nsContentUtils::GetWindowFromCaller()
1675 {
1676 JSContext *cx = GetCurrentJSContext();
1677 if (cx) {
1678 nsCOMPtr<nsPIDOMWindow> win =
1679 do_QueryInterface(nsJSUtils::GetDynamicScriptGlobal(cx));
1680 return win;
1681 }
1683 return nullptr;
1684 }
1686 nsIDocument*
1687 nsContentUtils::GetDocumentFromCaller()
1688 {
1689 AutoJSContext cx;
1691 nsCOMPtr<nsPIDOMWindow> win =
1692 do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(cx)));
1693 if (!win) {
1694 return nullptr;
1695 }
1697 return win->GetExtantDoc();
1698 }
1700 nsIDocument*
1701 nsContentUtils::GetDocumentFromContext()
1702 {
1703 JSContext *cx = GetCurrentJSContext();
1704 if (cx) {
1705 nsIScriptGlobalObject *sgo = nsJSUtils::GetDynamicScriptGlobal(cx);
1707 if (sgo) {
1708 nsCOMPtr<nsPIDOMWindow> pwin = do_QueryInterface(sgo);
1709 if (pwin) {
1710 return pwin->GetExtantDoc();
1711 }
1712 }
1713 }
1715 return nullptr;
1716 }
1718 bool
1719 nsContentUtils::IsCallerChrome()
1720 {
1721 MOZ_ASSERT(NS_IsMainThread());
1722 bool is_caller_chrome = false;
1723 nsresult rv = sSecurityManager->SubjectPrincipalIsSystem(&is_caller_chrome);
1724 if (NS_FAILED(rv)) {
1725 return false;
1726 }
1727 if (is_caller_chrome) {
1728 return true;
1729 }
1731 // If the check failed, look for UniversalXPConnect on the cx compartment.
1732 return xpc::IsUniversalXPConnectEnabled(GetCurrentJSContext());
1733 }
1735 namespace mozilla {
1736 namespace dom {
1737 namespace workers {
1738 extern bool IsCurrentThreadRunningChromeWorker();
1739 extern JSContext* GetCurrentThreadJSContext();
1740 }
1741 }
1742 }
1744 bool
1745 nsContentUtils::ThreadsafeIsCallerChrome()
1746 {
1747 return NS_IsMainThread() ?
1748 IsCallerChrome() :
1749 mozilla::dom::workers::IsCurrentThreadRunningChromeWorker();
1750 }
1752 bool
1753 nsContentUtils::IsCallerXBL()
1754 {
1755 JSContext *cx = GetCurrentJSContext();
1756 if (!cx)
1757 return false;
1759 JSCompartment *c = js::GetContextCompartment(cx);
1761 // For remote XUL, we run XBL in the XUL scope. Given that we care about
1762 // compat and not security for remote XUL, just always claim to be XBL.
1763 if (!xpc::AllowXBLScope(c)) {
1764 MOZ_ASSERT(nsContentUtils::AllowXULXBLForPrincipal(xpc::GetCompartmentPrincipal(c)));
1765 return true;
1766 }
1768 return xpc::IsXBLScope(c);
1769 }
1772 bool
1773 nsContentUtils::IsImageSrcSetDisabled()
1774 {
1775 return Preferences::GetBool("dom.disable_image_src_set") &&
1776 !IsCallerChrome();
1777 }
1779 // static
1780 bool
1781 nsContentUtils::LookupBindingMember(JSContext* aCx, nsIContent *aContent,
1782 JS::Handle<jsid> aId,
1783 JS::MutableHandle<JSPropertyDescriptor> aDesc)
1784 {
1785 nsXBLBinding* binding = aContent->GetXBLBinding();
1786 if (!binding)
1787 return true;
1788 return binding->LookupMember(aCx, aId, aDesc);
1789 }
1791 // static
1792 nsINode*
1793 nsContentUtils::GetCrossDocParentNode(nsINode* aChild)
1794 {
1795 NS_PRECONDITION(aChild, "The child is null!");
1797 nsINode* parent = aChild->GetParentNode();
1798 if (parent || !aChild->IsNodeOfType(nsINode::eDOCUMENT))
1799 return parent;
1801 nsIDocument* doc = static_cast<nsIDocument*>(aChild);
1802 nsIDocument* parentDoc = doc->GetParentDocument();
1803 return parentDoc ? parentDoc->FindContentForSubDocument(doc) : nullptr;
1804 }
1806 // static
1807 bool
1808 nsContentUtils::ContentIsDescendantOf(const nsINode* aPossibleDescendant,
1809 const nsINode* aPossibleAncestor)
1810 {
1811 NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
1812 NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
1814 do {
1815 if (aPossibleDescendant == aPossibleAncestor)
1816 return true;
1817 aPossibleDescendant = aPossibleDescendant->GetParentNode();
1818 } while (aPossibleDescendant);
1820 return false;
1821 }
1823 bool
1824 nsContentUtils::ContentIsHostIncludingDescendantOf(
1825 const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor)
1826 {
1827 NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
1828 NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
1830 do {
1831 if (aPossibleDescendant == aPossibleAncestor)
1832 return true;
1833 if (aPossibleDescendant->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
1834 aPossibleDescendant =
1835 static_cast<const DocumentFragment*>(aPossibleDescendant)->GetHost();
1836 } else {
1837 aPossibleDescendant = aPossibleDescendant->GetParentNode();
1838 }
1839 } while (aPossibleDescendant);
1841 return false;
1842 }
1844 // static
1845 bool
1846 nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
1847 nsINode* aPossibleAncestor)
1848 {
1849 NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
1850 NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
1852 do {
1853 if (aPossibleDescendant == aPossibleAncestor)
1854 return true;
1855 aPossibleDescendant = GetCrossDocParentNode(aPossibleDescendant);
1856 } while (aPossibleDescendant);
1858 return false;
1859 }
1862 // static
1863 nsresult
1864 nsContentUtils::GetAncestors(nsINode* aNode,
1865 nsTArray<nsINode*>& aArray)
1866 {
1867 while (aNode) {
1868 aArray.AppendElement(aNode);
1869 aNode = aNode->GetParentNode();
1870 }
1871 return NS_OK;
1872 }
1874 // static
1875 nsresult
1876 nsContentUtils::GetAncestorsAndOffsets(nsIDOMNode* aNode,
1877 int32_t aOffset,
1878 nsTArray<nsIContent*>* aAncestorNodes,
1879 nsTArray<int32_t>* aAncestorOffsets)
1880 {
1881 NS_ENSURE_ARG_POINTER(aNode);
1883 nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
1885 if (!content) {
1886 return NS_ERROR_FAILURE;
1887 }
1889 if (!aAncestorNodes->IsEmpty()) {
1890 NS_WARNING("aAncestorNodes is not empty");
1891 aAncestorNodes->Clear();
1892 }
1894 if (!aAncestorOffsets->IsEmpty()) {
1895 NS_WARNING("aAncestorOffsets is not empty");
1896 aAncestorOffsets->Clear();
1897 }
1899 // insert the node itself
1900 aAncestorNodes->AppendElement(content.get());
1901 aAncestorOffsets->AppendElement(aOffset);
1903 // insert all the ancestors
1904 nsIContent* child = content;
1905 nsIContent* parent = child->GetParent();
1906 while (parent) {
1907 aAncestorNodes->AppendElement(parent);
1908 aAncestorOffsets->AppendElement(parent->IndexOf(child));
1909 child = parent;
1910 parent = parent->GetParent();
1911 }
1913 return NS_OK;
1914 }
1916 // static
1917 nsresult
1918 nsContentUtils::GetCommonAncestor(nsIDOMNode *aNode,
1919 nsIDOMNode *aOther,
1920 nsIDOMNode** aCommonAncestor)
1921 {
1922 *aCommonAncestor = nullptr;
1924 nsCOMPtr<nsINode> node1 = do_QueryInterface(aNode);
1925 nsCOMPtr<nsINode> node2 = do_QueryInterface(aOther);
1927 NS_ENSURE_TRUE(node1 && node2, NS_ERROR_UNEXPECTED);
1929 nsINode* common = GetCommonAncestor(node1, node2);
1930 NS_ENSURE_TRUE(common, NS_ERROR_NOT_AVAILABLE);
1932 return CallQueryInterface(common, aCommonAncestor);
1933 }
1935 // static
1936 nsINode*
1937 nsContentUtils::GetCommonAncestor(nsINode* aNode1,
1938 nsINode* aNode2)
1939 {
1940 if (aNode1 == aNode2) {
1941 return aNode1;
1942 }
1944 // Build the chain of parents
1945 nsAutoTArray<nsINode*, 30> parents1, parents2;
1946 do {
1947 parents1.AppendElement(aNode1);
1948 aNode1 = aNode1->GetParentNode();
1949 } while (aNode1);
1950 do {
1951 parents2.AppendElement(aNode2);
1952 aNode2 = aNode2->GetParentNode();
1953 } while (aNode2);
1955 // Find where the parent chain differs
1956 uint32_t pos1 = parents1.Length();
1957 uint32_t pos2 = parents2.Length();
1958 nsINode* parent = nullptr;
1959 uint32_t len;
1960 for (len = std::min(pos1, pos2); len > 0; --len) {
1961 nsINode* child1 = parents1.ElementAt(--pos1);
1962 nsINode* child2 = parents2.ElementAt(--pos2);
1963 if (child1 != child2) {
1964 break;
1965 }
1966 parent = child1;
1967 }
1969 return parent;
1970 }
1972 /* static */
1973 bool
1974 nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2)
1975 {
1976 return (aNode2->CompareDocumentPosition(*aNode1) &
1977 (nsIDOMNode::DOCUMENT_POSITION_PRECEDING |
1978 nsIDOMNode::DOCUMENT_POSITION_DISCONNECTED)) ==
1979 nsIDOMNode::DOCUMENT_POSITION_PRECEDING;
1980 }
1982 /* static */
1983 int32_t
1984 nsContentUtils::ComparePoints(nsINode* aParent1, int32_t aOffset1,
1985 nsINode* aParent2, int32_t aOffset2,
1986 bool* aDisconnected)
1987 {
1988 if (aParent1 == aParent2) {
1989 return aOffset1 < aOffset2 ? -1 :
1990 aOffset1 > aOffset2 ? 1 :
1991 0;
1992 }
1994 nsAutoTArray<nsINode*, 32> parents1, parents2;
1995 nsINode* node1 = aParent1;
1996 nsINode* node2 = aParent2;
1997 do {
1998 parents1.AppendElement(node1);
1999 node1 = node1->GetParentNode();
2000 } while (node1);
2001 do {
2002 parents2.AppendElement(node2);
2003 node2 = node2->GetParentNode();
2004 } while (node2);
2006 uint32_t pos1 = parents1.Length() - 1;
2007 uint32_t pos2 = parents2.Length() - 1;
2009 bool disconnected = parents1.ElementAt(pos1) != parents2.ElementAt(pos2);
2010 if (aDisconnected) {
2011 *aDisconnected = disconnected;
2012 }
2013 if (disconnected) {
2014 NS_ASSERTION(aDisconnected, "unexpected disconnected nodes");
2015 return 1;
2016 }
2018 // Find where the parent chains differ
2019 nsINode* parent = parents1.ElementAt(pos1);
2020 uint32_t len;
2021 for (len = std::min(pos1, pos2); len > 0; --len) {
2022 nsINode* child1 = parents1.ElementAt(--pos1);
2023 nsINode* child2 = parents2.ElementAt(--pos2);
2024 if (child1 != child2) {
2025 return parent->IndexOf(child1) < parent->IndexOf(child2) ? -1 : 1;
2026 }
2027 parent = child1;
2028 }
2031 // The parent chains never differed, so one of the nodes is an ancestor of
2032 // the other
2034 NS_ASSERTION(!pos1 || !pos2,
2035 "should have run out of parent chain for one of the nodes");
2037 if (!pos1) {
2038 nsINode* child2 = parents2.ElementAt(--pos2);
2039 return aOffset1 <= parent->IndexOf(child2) ? -1 : 1;
2040 }
2042 nsINode* child1 = parents1.ElementAt(--pos1);
2043 return parent->IndexOf(child1) < aOffset2 ? -1 : 1;
2044 }
2046 /* static */
2047 int32_t
2048 nsContentUtils::ComparePoints(nsIDOMNode* aParent1, int32_t aOffset1,
2049 nsIDOMNode* aParent2, int32_t aOffset2,
2050 bool* aDisconnected)
2051 {
2052 nsCOMPtr<nsINode> parent1 = do_QueryInterface(aParent1);
2053 nsCOMPtr<nsINode> parent2 = do_QueryInterface(aParent2);
2054 NS_ENSURE_TRUE(parent1 && parent2, -1);
2055 return ComparePoints(parent1, aOffset1, parent2, aOffset2);
2056 }
2058 inline bool
2059 IsCharInSet(const char* aSet,
2060 const char16_t aChar)
2061 {
2062 char16_t ch;
2063 while ((ch = *aSet)) {
2064 if (aChar == char16_t(ch)) {
2065 return true;
2066 }
2067 ++aSet;
2068 }
2069 return false;
2070 }
2072 /**
2073 * This method strips leading/trailing chars, in given set, from string.
2074 */
2076 // static
2077 const nsDependentSubstring
2078 nsContentUtils::TrimCharsInSet(const char* aSet,
2079 const nsAString& aValue)
2080 {
2081 nsAString::const_iterator valueCurrent, valueEnd;
2083 aValue.BeginReading(valueCurrent);
2084 aValue.EndReading(valueEnd);
2086 // Skip characters in the beginning
2087 while (valueCurrent != valueEnd) {
2088 if (!IsCharInSet(aSet, *valueCurrent)) {
2089 break;
2090 }
2091 ++valueCurrent;
2092 }
2094 if (valueCurrent != valueEnd) {
2095 for (;;) {
2096 --valueEnd;
2097 if (!IsCharInSet(aSet, *valueEnd)) {
2098 break;
2099 }
2100 }
2101 ++valueEnd; // Step beyond the last character we want in the value.
2102 }
2104 // valueEnd should point to the char after the last to copy
2105 return Substring(valueCurrent, valueEnd);
2106 }
2108 /**
2109 * This method strips leading and trailing whitespace from a string.
2110 */
2112 // static
2113 template<bool IsWhitespace(char16_t)>
2114 const nsDependentSubstring
2115 nsContentUtils::TrimWhitespace(const nsAString& aStr, bool aTrimTrailing)
2116 {
2117 nsAString::const_iterator start, end;
2119 aStr.BeginReading(start);
2120 aStr.EndReading(end);
2122 // Skip whitespace characters in the beginning
2123 while (start != end && IsWhitespace(*start)) {
2124 ++start;
2125 }
2127 if (aTrimTrailing) {
2128 // Skip whitespace characters in the end.
2129 while (end != start) {
2130 --end;
2132 if (!IsWhitespace(*end)) {
2133 // Step back to the last non-whitespace character.
2134 ++end;
2136 break;
2137 }
2138 }
2139 }
2141 // Return a substring for the string w/o leading and/or trailing
2142 // whitespace
2144 return Substring(start, end);
2145 }
2147 // Declaring the templates we are going to use avoid linking issues without
2148 // inlining the method. Considering there is not so much spaces checking
2149 // methods we can consider this to be better than inlining.
2150 template
2151 const nsDependentSubstring
2152 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, bool);
2153 template
2154 const nsDependentSubstring
2155 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(const nsAString&, bool);
2156 template
2157 const nsDependentSubstring
2158 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespaceOrNBSP>(const nsAString&, bool);
2160 static inline void KeyAppendSep(nsACString& aKey)
2161 {
2162 if (!aKey.IsEmpty()) {
2163 aKey.Append('>');
2164 }
2165 }
2167 static inline void KeyAppendString(const nsAString& aString, nsACString& aKey)
2168 {
2169 KeyAppendSep(aKey);
2171 // Could escape separator here if collisions happen. > is not a legal char
2172 // for a name or type attribute, so we should be safe avoiding that extra work.
2174 AppendUTF16toUTF8(aString, aKey);
2175 }
2177 static inline void KeyAppendString(const nsACString& aString, nsACString& aKey)
2178 {
2179 KeyAppendSep(aKey);
2181 // Could escape separator here if collisions happen. > is not a legal char
2182 // for a name or type attribute, so we should be safe avoiding that extra work.
2184 aKey.Append(aString);
2185 }
2187 static inline void KeyAppendInt(int32_t aInt, nsACString& aKey)
2188 {
2189 KeyAppendSep(aKey);
2191 aKey.Append(nsPrintfCString("%d", aInt));
2192 }
2194 static inline bool IsAutocompleteOff(const nsIContent* aElement)
2195 {
2196 return aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocomplete,
2197 NS_LITERAL_STRING("off"), eIgnoreCase);
2198 }
2200 /*static*/ nsresult
2201 nsContentUtils::GenerateStateKey(nsIContent* aContent,
2202 const nsIDocument* aDocument,
2203 nsACString& aKey)
2204 {
2205 aKey.Truncate();
2207 uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
2209 // We must have content if we're not using a special state id
2210 NS_ENSURE_TRUE(aContent, NS_ERROR_FAILURE);
2212 // Don't capture state for anonymous content
2213 if (aContent->IsInAnonymousSubtree()) {
2214 return NS_OK;
2215 }
2217 if (IsAutocompleteOff(aContent)) {
2218 return NS_OK;
2219 }
2221 nsCOMPtr<nsIHTMLDocument> htmlDocument(do_QueryInterface(aContent->GetCurrentDoc()));
2223 KeyAppendInt(partID, aKey); // first append a partID
2224 bool generatedUniqueKey = false;
2226 if (htmlDocument) {
2227 // Flush our content model so it'll be up to date
2228 // If this becomes unnecessary and the following line is removed,
2229 // please also remove the corresponding flush operation from
2230 // nsHtml5TreeBuilderCppSupplement.h. (Look for "See bug 497861." there.)
2231 aContent->GetCurrentDoc()->FlushPendingNotifications(Flush_Content);
2233 nsContentList *htmlForms = htmlDocument->GetForms();
2234 nsContentList *htmlFormControls = htmlDocument->GetFormControls();
2236 NS_ENSURE_TRUE(htmlForms && htmlFormControls, NS_ERROR_OUT_OF_MEMORY);
2238 // If we have a form control and can calculate form information, use that
2239 // as the key - it is more reliable than just recording position in the
2240 // DOM.
2241 // XXXbz Is it, really? We have bugs on this, I think...
2242 // Important to have a unique key, and tag/type/name may not be.
2243 //
2244 // If the control has a form, the format of the key is:
2245 // f>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
2246 // else:
2247 // d>type>IndOfControlInDoc>name
2248 //
2249 // XXX We don't need to use index if name is there
2250 // XXXbz We don't? Why not? I don't follow.
2251 //
2252 nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
2253 if (control && htmlFormControls && htmlForms) {
2255 // Append the control type
2256 KeyAppendInt(control->GetType(), aKey);
2258 // If in a form, add form name / index of form / index in form
2259 int32_t index = -1;
2260 Element *formElement = control->GetFormElement();
2261 if (formElement) {
2262 if (IsAutocompleteOff(formElement)) {
2263 aKey.Truncate();
2264 return NS_OK;
2265 }
2267 KeyAppendString(NS_LITERAL_CSTRING("f"), aKey);
2269 // Append the index of the form in the document
2270 index = htmlForms->IndexOf(formElement, false);
2271 if (index <= -1) {
2272 //
2273 // XXX HACK this uses some state that was dumped into the document
2274 // specifically to fix bug 138892. What we are trying to do is *guess*
2275 // which form this control's state is found in, with the highly likely
2276 // guess that the highest form parsed so far is the one.
2277 // This code should not be on trunk, only branch.
2278 //
2279 index = htmlDocument->GetNumFormsSynchronous() - 1;
2280 }
2281 if (index > -1) {
2282 KeyAppendInt(index, aKey);
2284 // Append the index of the control in the form
2285 nsCOMPtr<nsIForm> form(do_QueryInterface(formElement));
2286 index = form->IndexOfControl(control);
2288 if (index > -1) {
2289 KeyAppendInt(index, aKey);
2290 generatedUniqueKey = true;
2291 }
2292 }
2294 // Append the form name
2295 nsAutoString formName;
2296 formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
2297 KeyAppendString(formName, aKey);
2299 } else {
2301 KeyAppendString(NS_LITERAL_CSTRING("d"), aKey);
2303 // If not in a form, add index of control in document
2304 // Less desirable than indexing by form info.
2306 // Hash by index of control in doc (we are not in a form)
2307 // These are important as they are unique, and type/name may not be.
2309 // We have to flush sink notifications at this point to make
2310 // sure that htmlFormControls is up to date.
2311 index = htmlFormControls->IndexOf(aContent, true);
2312 if (index > -1) {
2313 KeyAppendInt(index, aKey);
2314 generatedUniqueKey = true;
2315 }
2316 }
2318 // Append the control name
2319 nsAutoString name;
2320 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
2321 KeyAppendString(name, aKey);
2322 }
2323 }
2325 if (!generatedUniqueKey) {
2326 // Either we didn't have a form control or we aren't in an HTML document so
2327 // we can't figure out form info. Append the tag name if it's an element
2328 // to avoid restoring state for one type of element on another type.
2329 if (aContent->IsElement()) {
2330 KeyAppendString(nsDependentAtomString(aContent->Tag()), aKey);
2331 }
2332 else {
2333 // Append a character that is not "d" or "f" to disambiguate from
2334 // the case when we were a form control in an HTML document.
2335 KeyAppendString(NS_LITERAL_CSTRING("o"), aKey);
2336 }
2338 // Now start at aContent and append the indices of it and all its ancestors
2339 // in their containers. That should at least pin down its position in the
2340 // DOM...
2341 nsINode* parent = aContent->GetParentNode();
2342 nsINode* content = aContent;
2343 while (parent) {
2344 KeyAppendInt(parent->IndexOf(content), aKey);
2345 content = parent;
2346 parent = content->GetParentNode();
2347 }
2348 }
2350 return NS_OK;
2351 }
2353 // static
2354 nsIPrincipal*
2355 nsContentUtils::GetSubjectPrincipal()
2356 {
2357 nsCOMPtr<nsIPrincipal> subject;
2358 sSecurityManager->GetSubjectPrincipal(getter_AddRefs(subject));
2360 // When the ssm says the subject is null, that means system principal.
2361 if (!subject)
2362 sSecurityManager->GetSystemPrincipal(getter_AddRefs(subject));
2364 return subject;
2365 }
2367 // static
2368 nsIPrincipal*
2369 nsContentUtils::GetObjectPrincipal(JSObject* aObj)
2370 {
2371 // This is duplicated from nsScriptSecurityManager. We don't call through there
2372 // because the API unnecessarily requires a JSContext for historical reasons.
2373 JSCompartment *compartment = js::GetObjectCompartment(aObj);
2374 JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
2375 return nsJSPrincipals::get(principals);
2376 }
2378 // static
2379 nsresult
2380 nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
2381 const nsAString& aSpec,
2382 nsIDocument* aDocument,
2383 nsIURI* aBaseURI)
2384 {
2385 return NS_NewURI(aResult, aSpec,
2386 aDocument ? aDocument->GetDocumentCharacterSet().get() : nullptr,
2387 aBaseURI, sIOService);
2388 }
2390 // static
2391 bool
2392 nsContentUtils::IsCustomElementName(nsIAtom* aName)
2393 {
2394 // The custom element name identifies a custom element and is a sequence of
2395 // alphanumeric ASCII characters that must match the NCName production and
2396 // contain a U+002D HYPHEN-MINUS character.
2397 nsDependentAtomString str(aName);
2398 const char16_t* colon;
2399 if (NS_FAILED(nsContentUtils::CheckQName(str, false, &colon)) || colon ||
2400 str.FindChar('-') == -1) {
2401 return false;
2402 }
2404 // The custom element name must not be one of the following values:
2405 // annotation-xml
2406 // color-profile
2407 // font-face
2408 // font-face-src
2409 // font-face-uri
2410 // font-face-format
2411 // font-face-name
2412 // missing-glyph
2413 return aName != nsGkAtoms::annotation_xml_ &&
2414 aName != nsGkAtoms::colorProfile &&
2415 aName != nsGkAtoms::font_face &&
2416 aName != nsGkAtoms::font_face_src &&
2417 aName != nsGkAtoms::font_face_uri &&
2418 aName != nsGkAtoms::font_face_format &&
2419 aName != nsGkAtoms::font_face_name &&
2420 aName != nsGkAtoms::missingGlyph;
2421 }
2423 // static
2424 nsresult
2425 nsContentUtils::CheckQName(const nsAString& aQualifiedName,
2426 bool aNamespaceAware,
2427 const char16_t** aColon)
2428 {
2429 const char* colon = nullptr;
2430 const char16_t* begin = aQualifiedName.BeginReading();
2431 const char16_t* end = aQualifiedName.EndReading();
2433 int result = MOZ_XMLCheckQName(reinterpret_cast<const char*>(begin),
2434 reinterpret_cast<const char*>(end),
2435 aNamespaceAware, &colon);
2437 if (!result) {
2438 if (aColon) {
2439 *aColon = reinterpret_cast<const char16_t*>(colon);
2440 }
2442 return NS_OK;
2443 }
2445 // MOZ_EXPAT_EMPTY_QNAME || MOZ_EXPAT_INVALID_CHARACTER
2446 if (result == (1 << 0) || result == (1 << 1)) {
2447 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
2448 }
2450 return NS_ERROR_DOM_NAMESPACE_ERR;
2451 }
2453 //static
2454 nsresult
2455 nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
2456 const nsAFlatString& aQName,
2457 int32_t *aNamespace, nsIAtom **aLocalName)
2458 {
2459 const char16_t* colon;
2460 nsresult rv = nsContentUtils::CheckQName(aQName, true, &colon);
2461 NS_ENSURE_SUCCESS(rv, rv);
2463 if (colon) {
2464 const char16_t* end;
2465 aQName.EndReading(end);
2466 nsAutoString nameSpace;
2467 rv = aNamespaceResolver->LookupNamespaceURIInternal(Substring(aQName.get(),
2468 colon),
2469 nameSpace);
2470 NS_ENSURE_SUCCESS(rv, rv);
2472 *aNamespace = NameSpaceManager()->GetNameSpaceID(nameSpace);
2473 if (*aNamespace == kNameSpaceID_Unknown)
2474 return NS_ERROR_FAILURE;
2476 *aLocalName = NS_NewAtom(Substring(colon + 1, end)).take();
2477 }
2478 else {
2479 *aNamespace = kNameSpaceID_None;
2480 *aLocalName = NS_NewAtom(aQName).take();
2481 }
2482 NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
2483 return NS_OK;
2484 }
2486 // static
2487 nsresult
2488 nsContentUtils::GetNodeInfoFromQName(const nsAString& aNamespaceURI,
2489 const nsAString& aQualifiedName,
2490 nsNodeInfoManager* aNodeInfoManager,
2491 uint16_t aNodeType,
2492 nsINodeInfo** aNodeInfo)
2493 {
2494 const nsAFlatString& qName = PromiseFlatString(aQualifiedName);
2495 const char16_t* colon;
2496 nsresult rv = nsContentUtils::CheckQName(qName, true, &colon);
2497 NS_ENSURE_SUCCESS(rv, rv);
2499 int32_t nsID;
2500 sNameSpaceManager->RegisterNameSpace(aNamespaceURI, nsID);
2501 if (colon) {
2502 const char16_t* end;
2503 qName.EndReading(end);
2505 nsCOMPtr<nsIAtom> prefix = do_GetAtom(Substring(qName.get(), colon));
2507 rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix,
2508 nsID, aNodeType, aNodeInfo);
2509 }
2510 else {
2511 rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID,
2512 aNodeType, aNodeInfo);
2513 }
2514 NS_ENSURE_SUCCESS(rv, rv);
2516 return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
2517 (*aNodeInfo)->GetPrefixAtom(),
2518 (*aNodeInfo)->NamespaceID()) ?
2519 NS_OK : NS_ERROR_DOM_NAMESPACE_ERR;
2520 }
2522 // static
2523 void
2524 nsContentUtils::SplitExpatName(const char16_t *aExpatName, nsIAtom **aPrefix,
2525 nsIAtom **aLocalName, int32_t* aNameSpaceID)
2526 {
2527 /**
2528 * Expat can send the following:
2529 * localName
2530 * namespaceURI<separator>localName
2531 * namespaceURI<separator>localName<separator>prefix
2532 *
2533 * and we use 0xFFFF for the <separator>.
2534 *
2535 */
2537 const char16_t *uriEnd = nullptr;
2538 const char16_t *nameEnd = nullptr;
2539 const char16_t *pos;
2540 for (pos = aExpatName; *pos; ++pos) {
2541 if (*pos == 0xFFFF) {
2542 if (uriEnd) {
2543 nameEnd = pos;
2544 }
2545 else {
2546 uriEnd = pos;
2547 }
2548 }
2549 }
2551 const char16_t *nameStart;
2552 if (uriEnd) {
2553 if (sNameSpaceManager) {
2554 sNameSpaceManager->RegisterNameSpace(nsDependentSubstring(aExpatName,
2555 uriEnd),
2556 *aNameSpaceID);
2557 }
2558 else {
2559 *aNameSpaceID = kNameSpaceID_Unknown;
2560 }
2562 nameStart = (uriEnd + 1);
2563 if (nameEnd) {
2564 const char16_t *prefixStart = nameEnd + 1;
2565 *aPrefix = NS_NewAtom(Substring(prefixStart, pos)).take();
2566 }
2567 else {
2568 nameEnd = pos;
2569 *aPrefix = nullptr;
2570 }
2571 }
2572 else {
2573 *aNameSpaceID = kNameSpaceID_None;
2574 nameStart = aExpatName;
2575 nameEnd = pos;
2576 *aPrefix = nullptr;
2577 }
2578 *aLocalName = NS_NewAtom(Substring(nameStart, nameEnd)).take();
2579 }
2581 // static
2582 nsPresContext*
2583 nsContentUtils::GetContextForContent(const nsIContent* aContent)
2584 {
2585 nsIDocument* doc = aContent->GetCurrentDoc();
2586 if (doc) {
2587 nsIPresShell *presShell = doc->GetShell();
2588 if (presShell) {
2589 return presShell->GetPresContext();
2590 }
2591 }
2592 return nullptr;
2593 }
2595 // static
2596 bool
2597 nsContentUtils::CanLoadImage(nsIURI* aURI, nsISupports* aContext,
2598 nsIDocument* aLoadingDocument,
2599 nsIPrincipal* aLoadingPrincipal,
2600 int16_t* aImageBlockingStatus)
2601 {
2602 NS_PRECONDITION(aURI, "Must have a URI");
2603 NS_PRECONDITION(aLoadingDocument, "Must have a document");
2604 NS_PRECONDITION(aLoadingPrincipal, "Must have a loading principal");
2606 nsresult rv;
2608 uint32_t appType = nsIDocShell::APP_TYPE_UNKNOWN;
2610 {
2611 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = aLoadingDocument->GetDocShell();
2612 if (docShellTreeItem) {
2613 nsCOMPtr<nsIDocShellTreeItem> root;
2614 docShellTreeItem->GetRootTreeItem(getter_AddRefs(root));
2616 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
2618 if (!docShell || NS_FAILED(docShell->GetAppType(&appType))) {
2619 appType = nsIDocShell::APP_TYPE_UNKNOWN;
2620 }
2621 }
2622 }
2624 if (appType != nsIDocShell::APP_TYPE_EDITOR) {
2625 // Editor apps get special treatment here, editors can load images
2626 // from anywhere. This allows editor to insert images from file://
2627 // into documents that are being edited.
2628 rv = sSecurityManager->
2629 CheckLoadURIWithPrincipal(aLoadingPrincipal, aURI,
2630 nsIScriptSecurityManager::ALLOW_CHROME);
2631 if (NS_FAILED(rv)) {
2632 if (aImageBlockingStatus) {
2633 // Reject the request itself, not all requests to the relevant
2634 // server...
2635 *aImageBlockingStatus = nsIContentPolicy::REJECT_REQUEST;
2636 }
2637 return false;
2638 }
2639 }
2641 int16_t decision = nsIContentPolicy::ACCEPT;
2643 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_IMAGE,
2644 aURI,
2645 aLoadingPrincipal,
2646 aContext,
2647 EmptyCString(), //mime guess
2648 nullptr, //extra
2649 &decision,
2650 GetContentPolicy(),
2651 sSecurityManager);
2653 if (aImageBlockingStatus) {
2654 *aImageBlockingStatus =
2655 NS_FAILED(rv) ? nsIContentPolicy::REJECT_REQUEST : decision;
2656 }
2657 return NS_FAILED(rv) ? false : NS_CP_ACCEPTED(decision);
2658 }
2660 imgLoader*
2661 nsContentUtils::GetImgLoaderForDocument(nsIDocument* aDoc)
2662 {
2663 if (!aDoc)
2664 return imgLoader::Singleton();
2665 bool isPrivate = false;
2666 nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
2667 nsCOMPtr<nsIInterfaceRequestor> callbacks;
2668 if (loadGroup) {
2669 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
2670 if (callbacks) {
2671 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
2672 isPrivate = loadContext && loadContext->UsePrivateBrowsing();
2673 }
2674 } else {
2675 nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
2676 isPrivate = channel && NS_UsePrivateBrowsing(channel);
2677 }
2678 return isPrivate ? imgLoader::PBSingleton() : imgLoader::Singleton();
2679 }
2681 // static
2682 imgLoader*
2683 nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel)
2684 {
2685 if (!aChannel)
2686 return imgLoader::Singleton();
2687 nsCOMPtr<nsILoadContext> context;
2688 NS_QueryNotificationCallbacks(aChannel, context);
2689 return context && context->UsePrivateBrowsing() ? imgLoader::PBSingleton() : imgLoader::Singleton();
2690 }
2692 // static
2693 bool
2694 nsContentUtils::IsImageInCache(nsIURI* aURI, nsIDocument* aDocument)
2695 {
2696 imgILoader* loader = GetImgLoaderForDocument(aDocument);
2697 nsCOMPtr<imgICache> cache = do_QueryInterface(loader);
2699 // If something unexpected happened we return false, otherwise if props
2700 // is set, the image is cached and we return true
2701 nsCOMPtr<nsIProperties> props;
2702 nsresult rv = cache->FindEntryProperties(aURI, getter_AddRefs(props));
2703 return (NS_SUCCEEDED(rv) && props);
2704 }
2706 // static
2707 nsresult
2708 nsContentUtils::LoadImage(nsIURI* aURI, nsIDocument* aLoadingDocument,
2709 nsIPrincipal* aLoadingPrincipal, nsIURI* aReferrer,
2710 imgINotificationObserver* aObserver, int32_t aLoadFlags,
2711 const nsAString& initiatorType,
2712 imgRequestProxy** aRequest)
2713 {
2714 NS_PRECONDITION(aURI, "Must have a URI");
2715 NS_PRECONDITION(aLoadingDocument, "Must have a document");
2716 NS_PRECONDITION(aLoadingPrincipal, "Must have a principal");
2717 NS_PRECONDITION(aRequest, "Null out param");
2719 imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
2720 if (!imgLoader) {
2721 // nothing we can do here
2722 return NS_OK;
2723 }
2725 nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
2727 nsIURI *documentURI = aLoadingDocument->GetDocumentURI();
2729 NS_ASSERTION(loadGroup || IsFontTableURI(documentURI),
2730 "Could not get loadgroup; onload may fire too early");
2732 // check for a Content Security Policy to pass down to the channel that
2733 // will get created to load the image
2734 nsCOMPtr<nsIChannelPolicy> channelPolicy;
2735 nsCOMPtr<nsIContentSecurityPolicy> csp;
2736 if (aLoadingPrincipal) {
2737 nsresult rv = aLoadingPrincipal->GetCsp(getter_AddRefs(csp));
2738 NS_ENSURE_SUCCESS(rv, rv);
2739 if (csp) {
2740 channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
2741 channelPolicy->SetContentSecurityPolicy(csp);
2742 channelPolicy->SetLoadType(nsIContentPolicy::TYPE_IMAGE);
2743 }
2744 }
2746 // Make the URI immutable so people won't change it under us
2747 NS_TryToSetImmutable(aURI);
2749 nsCOMPtr<nsIURI> firstPartyIsolationURI;
2750 nsCOMPtr<mozIThirdPartyUtil> thirdPartySvc
2751 = do_GetService(THIRDPARTYUTIL_CONTRACTID);
2752 thirdPartySvc->GetFirstPartyIsolationURI(nullptr, aLoadingDocument,
2753 getter_AddRefs(firstPartyIsolationURI));
2755 return imgLoader->LoadImage(aURI, /* uri to load */
2756 firstPartyIsolationURI, /* firstPartyIsolationURI */
2757 aReferrer, /* referrer */
2758 aLoadingPrincipal, /* loading principal */
2759 loadGroup, /* loadgroup */
2760 aObserver, /* imgINotificationObserver */
2761 aLoadingDocument, /* uniquification key */
2762 aLoadFlags, /* load flags */
2763 nullptr, /* cache key */
2764 channelPolicy, /* CSP info */
2765 initiatorType, /* the load initiator */
2766 aRequest);
2767 }
2769 // static
2770 already_AddRefed<imgIContainer>
2771 nsContentUtils::GetImageFromContent(nsIImageLoadingContent* aContent,
2772 imgIRequest **aRequest)
2773 {
2774 if (aRequest) {
2775 *aRequest = nullptr;
2776 }
2778 NS_ENSURE_TRUE(aContent, nullptr);
2780 nsCOMPtr<imgIRequest> imgRequest;
2781 aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
2782 getter_AddRefs(imgRequest));
2783 if (!imgRequest) {
2784 return nullptr;
2785 }
2787 nsCOMPtr<imgIContainer> imgContainer;
2788 imgRequest->GetImage(getter_AddRefs(imgContainer));
2790 if (!imgContainer) {
2791 return nullptr;
2792 }
2794 if (aRequest) {
2795 imgRequest.swap(*aRequest);
2796 }
2798 return imgContainer.forget();
2799 }
2801 //static
2802 already_AddRefed<imgRequestProxy>
2803 nsContentUtils::GetStaticRequest(imgRequestProxy* aRequest)
2804 {
2805 NS_ENSURE_TRUE(aRequest, nullptr);
2806 nsRefPtr<imgRequestProxy> retval;
2807 aRequest->GetStaticRequest(getter_AddRefs(retval));
2808 return retval.forget();
2809 }
2811 // static
2812 bool
2813 nsContentUtils::ContentIsDraggable(nsIContent* aContent)
2814 {
2815 nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(aContent);
2816 if (htmlElement) {
2817 bool draggable = false;
2818 htmlElement->GetDraggable(&draggable);
2819 if (draggable)
2820 return true;
2822 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
2823 nsGkAtoms::_false, eIgnoreCase))
2824 return false;
2825 }
2827 // special handling for content area image and link dragging
2828 return IsDraggableImage(aContent) || IsDraggableLink(aContent);
2829 }
2831 // static
2832 bool
2833 nsContentUtils::IsDraggableImage(nsIContent* aContent)
2834 {
2835 NS_PRECONDITION(aContent, "Must have content node to test");
2837 nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(aContent));
2838 if (!imageContent) {
2839 return false;
2840 }
2842 nsCOMPtr<imgIRequest> imgRequest;
2843 imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
2844 getter_AddRefs(imgRequest));
2846 // XXXbz It may be draggable even if the request resulted in an error. Why?
2847 // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
2848 return imgRequest != nullptr;
2849 }
2851 // static
2852 bool
2853 nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
2854 nsCOMPtr<nsIURI> absURI;
2855 return aContent->IsLink(getter_AddRefs(absURI));
2856 }
2858 // static
2859 nsresult
2860 nsContentUtils::NameChanged(nsINodeInfo* aNodeInfo, nsIAtom* aName,
2861 nsINodeInfo** aResult)
2862 {
2863 nsNodeInfoManager *niMgr = aNodeInfo->NodeInfoManager();
2865 *aResult = niMgr->GetNodeInfo(aName, aNodeInfo->GetPrefixAtom(),
2866 aNodeInfo->NamespaceID(),
2867 aNodeInfo->NodeType(),
2868 aNodeInfo->GetExtraName()).take();
2869 return NS_OK;
2870 }
2873 static bool
2874 TestSitePerm(nsIPrincipal* aPrincipal, const char* aType, uint32_t aPerm, bool aExactHostMatch)
2875 {
2876 if (!aPrincipal) {
2877 // We always deny (i.e. don't allow) the permission if we don't have a
2878 // principal.
2879 return aPerm != nsIPermissionManager::ALLOW_ACTION;
2880 }
2882 nsCOMPtr<nsIPermissionManager> permMgr =
2883 do_GetService("@mozilla.org/permissionmanager;1");
2884 NS_ENSURE_TRUE(permMgr, false);
2886 uint32_t perm;
2887 nsresult rv;
2888 if (aExactHostMatch) {
2889 rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
2890 } else {
2891 rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
2892 }
2893 NS_ENSURE_SUCCESS(rv, false);
2895 return perm == aPerm;
2896 }
2898 bool
2899 nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal, const char* aType)
2900 {
2901 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION, false);
2902 }
2904 bool
2905 nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal, const char* aType)
2906 {
2907 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION, false);
2908 }
2910 bool
2911 nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal, const char* aType)
2912 {
2913 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION, true);
2914 }
2916 bool
2917 nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal, const char* aType)
2918 {
2919 return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION, true);
2920 }
2922 static const char *gEventNames[] = {"event"};
2923 static const char *gSVGEventNames[] = {"evt"};
2924 // for b/w compat, the first name to onerror is still 'event', even though it
2925 // is actually the error message. (pre this code, the other 2 were not avail.)
2926 // XXXmarkh - a quick lxr shows no affected code - should we correct this?
2927 static const char *gOnErrorNames[] = {"event", "source", "lineno"};
2929 // static
2930 void
2931 nsContentUtils::GetEventArgNames(int32_t aNameSpaceID,
2932 nsIAtom *aEventName,
2933 uint32_t *aArgCount,
2934 const char*** aArgArray)
2935 {
2936 #define SET_EVENT_ARG_NAMES(names) \
2937 *aArgCount = sizeof(names)/sizeof(names[0]); \
2938 *aArgArray = names;
2940 // JSEventHandler is what does the arg magic for onerror, and it does
2941 // not seem to take the namespace into account. So we let onerror in all
2942 // namespaces get the 3 arg names.
2943 if (aEventName == nsGkAtoms::onerror) {
2944 SET_EVENT_ARG_NAMES(gOnErrorNames);
2945 } else if (aNameSpaceID == kNameSpaceID_SVG) {
2946 SET_EVENT_ARG_NAMES(gSVGEventNames);
2947 } else {
2948 SET_EVENT_ARG_NAMES(gEventNames);
2949 }
2950 }
2952 static const char gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT][56] = {
2953 // Must line up with the enum values in |PropertiesFile| enum.
2954 "chrome://global/locale/css.properties",
2955 "chrome://global/locale/xbl.properties",
2956 "chrome://global/locale/xul.properties",
2957 "chrome://global/locale/layout_errors.properties",
2958 "chrome://global/locale/layout/HtmlForm.properties",
2959 "chrome://global/locale/printing.properties",
2960 "chrome://global/locale/dom/dom.properties",
2961 "chrome://global/locale/layout/htmlparser.properties",
2962 "chrome://global/locale/svg/svg.properties",
2963 "chrome://branding/locale/brand.properties",
2964 "chrome://global/locale/commonDialogs.properties",
2965 "chrome://global/locale/mathml/mathml.properties",
2966 "chrome://global/locale/security/security.properties"
2967 };
2969 /* static */ nsresult
2970 nsContentUtils::EnsureStringBundle(PropertiesFile aFile)
2971 {
2972 if (!sStringBundles[aFile]) {
2973 if (!sStringBundleService) {
2974 nsresult rv =
2975 CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
2976 NS_ENSURE_SUCCESS(rv, rv);
2977 }
2978 nsIStringBundle *bundle;
2979 nsresult rv =
2980 sStringBundleService->CreateBundle(gPropertiesFiles[aFile], &bundle);
2981 NS_ENSURE_SUCCESS(rv, rv);
2982 sStringBundles[aFile] = bundle; // transfer ownership
2983 }
2984 return NS_OK;
2985 }
2987 /* static */
2988 nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
2989 const char* aKey,
2990 nsXPIDLString& aResult)
2991 {
2992 nsresult rv = EnsureStringBundle(aFile);
2993 NS_ENSURE_SUCCESS(rv, rv);
2994 nsIStringBundle *bundle = sStringBundles[aFile];
2996 return bundle->GetStringFromName(NS_ConvertASCIItoUTF16(aKey).get(),
2997 getter_Copies(aResult));
2998 }
3000 /* static */
3001 nsresult nsContentUtils::FormatLocalizedString(PropertiesFile aFile,
3002 const char* aKey,
3003 const char16_t **aParams,
3004 uint32_t aParamsLength,
3005 nsXPIDLString& aResult)
3006 {
3007 nsresult rv = EnsureStringBundle(aFile);
3008 NS_ENSURE_SUCCESS(rv, rv);
3009 nsIStringBundle *bundle = sStringBundles[aFile];
3011 return bundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aKey).get(),
3012 aParams, aParamsLength,
3013 getter_Copies(aResult));
3014 }
3016 /* static */ void
3017 nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
3018 const char * classification)
3019 {
3020 nsCOMPtr<nsIScriptError> scriptError =
3021 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
3022 if (scriptError) {
3023 nsCOMPtr<nsIConsoleService> console =
3024 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
3025 if (console && NS_SUCCEEDED(scriptError->Init(aErrorText, EmptyString(),
3026 EmptyString(), 0, 0,
3027 nsIScriptError::errorFlag,
3028 classification))) {
3029 console->LogMessage(scriptError);
3030 }
3031 }
3032 }
3034 /* static */ nsresult
3035 nsContentUtils::ReportToConsole(uint32_t aErrorFlags,
3036 const nsACString& aCategory,
3037 nsIDocument* aDocument,
3038 PropertiesFile aFile,
3039 const char *aMessageName,
3040 const char16_t **aParams,
3041 uint32_t aParamsLength,
3042 nsIURI* aURI,
3043 const nsAFlatString& aSourceLine,
3044 uint32_t aLineNumber,
3045 uint32_t aColumnNumber)
3046 {
3047 NS_ASSERTION((aParams && aParamsLength) || (!aParams && !aParamsLength),
3048 "Supply either both parameters and their number or no"
3049 "parameters and 0.");
3051 nsresult rv;
3052 nsXPIDLString errorText;
3053 if (aParams) {
3054 rv = FormatLocalizedString(aFile, aMessageName, aParams, aParamsLength,
3055 errorText);
3056 }
3057 else {
3058 rv = GetLocalizedString(aFile, aMessageName, errorText);
3059 }
3060 NS_ENSURE_SUCCESS(rv, rv);
3062 return ReportToConsoleNonLocalized(errorText, aErrorFlags, aCategory,
3063 aDocument, aURI, aSourceLine,
3064 aLineNumber, aColumnNumber);
3065 }
3068 /* static */ nsresult
3069 nsContentUtils::ReportToConsoleNonLocalized(const nsAString& aErrorText,
3070 uint32_t aErrorFlags,
3071 const nsACString& aCategory,
3072 nsIDocument* aDocument,
3073 nsIURI* aURI,
3074 const nsAFlatString& aSourceLine,
3075 uint32_t aLineNumber,
3076 uint32_t aColumnNumber)
3077 {
3078 uint64_t innerWindowID = 0;
3079 if (aDocument) {
3080 if (!aURI) {
3081 aURI = aDocument->GetDocumentURI();
3082 }
3083 innerWindowID = aDocument->InnerWindowID();
3084 }
3086 nsresult rv;
3087 if (!sConsoleService) { // only need to bother null-checking here
3088 rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
3089 NS_ENSURE_SUCCESS(rv, rv);
3090 }
3092 nsAutoCString spec;
3093 if (!aLineNumber) {
3094 JSContext *cx = GetCurrentJSContext();
3095 if (cx) {
3096 const char* filename;
3097 uint32_t lineno;
3098 if (nsJSUtils::GetCallingLocation(cx, &filename, &lineno)) {
3099 spec = filename;
3100 aLineNumber = lineno;
3101 }
3102 }
3103 }
3104 if (spec.IsEmpty() && aURI)
3105 aURI->GetSpec(spec);
3107 nsCOMPtr<nsIScriptError> errorObject =
3108 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
3109 NS_ENSURE_SUCCESS(rv, rv);
3111 rv = errorObject->InitWithWindowID(aErrorText,
3112 NS_ConvertUTF8toUTF16(spec), // file name
3113 aSourceLine,
3114 aLineNumber, aColumnNumber,
3115 aErrorFlags, aCategory,
3116 innerWindowID);
3117 NS_ENSURE_SUCCESS(rv, rv);
3119 return sConsoleService->LogMessage(errorObject);
3120 }
3122 void
3123 nsContentUtils::LogMessageToConsole(const char* aMsg, ...)
3124 {
3125 if (!sConsoleService) { // only need to bother null-checking here
3126 CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
3127 if (!sConsoleService) {
3128 return;
3129 }
3130 }
3132 va_list args;
3133 va_start(args, aMsg);
3134 char* formatted = PR_vsmprintf(aMsg, args);
3135 va_end(args);
3136 if (!formatted) {
3137 return;
3138 }
3140 sConsoleService->LogStringMessage(NS_ConvertUTF8toUTF16(formatted).get());
3141 PR_smprintf_free(formatted);
3142 }
3144 bool
3145 nsContentUtils::IsChromeDoc(nsIDocument *aDocument)
3146 {
3147 if (!aDocument) {
3148 return false;
3149 }
3151 nsCOMPtr<nsIPrincipal> systemPrincipal;
3152 sSecurityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
3154 return aDocument->NodePrincipal() == systemPrincipal;
3155 }
3157 bool
3158 nsContentUtils::IsChildOfSameType(nsIDocument* aDoc)
3159 {
3160 nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(aDoc->GetDocShell());
3161 nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
3162 if (docShellAsItem) {
3163 docShellAsItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
3164 }
3165 return sameTypeParent != nullptr;
3166 }
3168 bool
3169 nsContentUtils::IsPlainTextType(const nsACString& aContentType)
3170 {
3171 return aContentType.EqualsLiteral(TEXT_PLAIN) ||
3172 aContentType.EqualsLiteral(TEXT_CSS) ||
3173 aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
3174 aContentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
3175 aContentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
3176 aContentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
3177 aContentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
3178 aContentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
3179 aContentType.EqualsLiteral(APPLICATION_JSON);
3180 }
3182 bool
3183 nsContentUtils::GetWrapperSafeScriptFilename(nsIDocument *aDocument,
3184 nsIURI *aURI,
3185 nsACString& aScriptURI)
3186 {
3187 bool scriptFileNameModified = false;
3188 aURI->GetSpec(aScriptURI);
3190 if (IsChromeDoc(aDocument)) {
3191 nsCOMPtr<nsIChromeRegistry> chromeReg =
3192 mozilla::services::GetChromeRegistryService();
3194 if (!chromeReg) {
3195 // If we're running w/o a chrome registry we won't modify any
3196 // script file names.
3198 return scriptFileNameModified;
3199 }
3201 bool docWrappersEnabled =
3202 chromeReg->WrappersEnabled(aDocument->GetDocumentURI());
3204 bool uriWrappersEnabled = chromeReg->WrappersEnabled(aURI);
3206 nsIURI *docURI = aDocument->GetDocumentURI();
3208 if (docURI && docWrappersEnabled && !uriWrappersEnabled) {
3209 // aURI is a script from a URL that doesn't get wrapper
3210 // automation. aDocument is a chrome document that does get
3211 // wrapper automation. Prepend the chrome document's URI
3212 // followed by the string " -> " to the URI of the script we're
3213 // loading here so that script in that URI gets the same wrapper
3214 // automation that the chrome document expects.
3215 nsAutoCString spec;
3216 docURI->GetSpec(spec);
3217 spec.AppendLiteral(" -> ");
3218 spec.Append(aScriptURI);
3220 aScriptURI = spec;
3222 scriptFileNameModified = true;
3223 }
3224 }
3226 return scriptFileNameModified;
3227 }
3229 // static
3230 bool
3231 nsContentUtils::IsInChromeDocshell(nsIDocument *aDocument)
3232 {
3233 if (!aDocument) {
3234 return false;
3235 }
3237 if (aDocument->GetDisplayDocument()) {
3238 return IsInChromeDocshell(aDocument->GetDisplayDocument());
3239 }
3241 nsCOMPtr<nsIDocShellTreeItem> docShell = aDocument->GetDocShell();
3242 if (!docShell) {
3243 return false;
3244 }
3246 return docShell->ItemType() == nsIDocShellTreeItem::typeChrome;
3247 }
3249 // static
3250 nsIContentPolicy*
3251 nsContentUtils::GetContentPolicy()
3252 {
3253 if (!sTriedToGetContentPolicy) {
3254 CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
3255 // It's OK to not have a content policy service
3256 sTriedToGetContentPolicy = true;
3257 }
3259 return sContentPolicyService;
3260 }
3262 // static
3263 bool
3264 nsContentUtils::IsEventAttributeName(nsIAtom* aName, int32_t aType)
3265 {
3266 const char16_t* name = aName->GetUTF16String();
3267 if (name[0] != 'o' || name[1] != 'n')
3268 return false;
3270 EventNameMapping mapping;
3271 return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
3272 }
3274 // static
3275 uint32_t
3276 nsContentUtils::GetEventId(nsIAtom* aName)
3277 {
3278 if (aName) {
3279 EventNameMapping mapping;
3280 if (sAtomEventTable->Get(aName, &mapping)) {
3281 return mapping.mId;
3282 }
3283 }
3285 return NS_USER_DEFINED_EVENT;
3286 }
3288 // static
3289 uint32_t
3290 nsContentUtils::GetEventCategory(const nsAString& aName)
3291 {
3292 EventNameMapping mapping;
3293 if (sStringEventTable->Get(aName, &mapping))
3294 return mapping.mStructType;
3296 return NS_EVENT;
3297 }
3299 nsIAtom*
3300 nsContentUtils::GetEventIdAndAtom(const nsAString& aName,
3301 uint32_t aEventStruct,
3302 uint32_t* aEventID)
3303 {
3304 EventNameMapping mapping;
3305 if (sStringEventTable->Get(aName, &mapping)) {
3306 *aEventID =
3307 mapping.mStructType == aEventStruct ? mapping.mId : NS_USER_DEFINED_EVENT;
3308 return mapping.mAtom;
3309 }
3311 // If we have cached lots of user defined event names, clear some of them.
3312 if (sUserDefinedEvents->Count() > 127) {
3313 while (sUserDefinedEvents->Count() > 64) {
3314 nsIAtom* first = sUserDefinedEvents->ObjectAt(0);
3315 sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
3316 sUserDefinedEvents->RemoveObjectAt(0);
3317 }
3318 }
3320 *aEventID = NS_USER_DEFINED_EVENT;
3321 nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aName);
3322 sUserDefinedEvents->AppendObject(atom);
3323 mapping.mAtom = atom;
3324 mapping.mId = NS_USER_DEFINED_EVENT;
3325 mapping.mType = EventNameType_None;
3326 mapping.mStructType = NS_EVENT_NULL;
3327 sStringEventTable->Put(aName, mapping);
3328 return mapping.mAtom;
3329 }
3331 static
3332 nsresult GetEventAndTarget(nsIDocument* aDoc, nsISupports* aTarget,
3333 const nsAString& aEventName,
3334 bool aCanBubble, bool aCancelable,
3335 bool aTrusted, nsIDOMEvent** aEvent,
3336 EventTarget** aTargetOut)
3337 {
3338 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
3339 nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
3340 NS_ENSURE_TRUE(domDoc && target, NS_ERROR_INVALID_ARG);
3342 nsCOMPtr<nsIDOMEvent> event;
3343 nsresult rv =
3344 domDoc->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
3345 NS_ENSURE_SUCCESS(rv, rv);
3347 rv = event->InitEvent(aEventName, aCanBubble, aCancelable);
3348 NS_ENSURE_SUCCESS(rv, rv);
3350 event->SetTrusted(aTrusted);
3352 rv = event->SetTarget(target);
3353 NS_ENSURE_SUCCESS(rv, rv);
3355 event.forget(aEvent);
3356 target.forget(aTargetOut);
3357 return NS_OK;
3358 }
3360 // static
3361 nsresult
3362 nsContentUtils::DispatchTrustedEvent(nsIDocument* aDoc, nsISupports* aTarget,
3363 const nsAString& aEventName,
3364 bool aCanBubble, bool aCancelable,
3365 bool *aDefaultAction)
3366 {
3367 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
3368 true, aDefaultAction);
3369 }
3371 // static
3372 nsresult
3373 nsContentUtils::DispatchUntrustedEvent(nsIDocument* aDoc, nsISupports* aTarget,
3374 const nsAString& aEventName,
3375 bool aCanBubble, bool aCancelable,
3376 bool *aDefaultAction)
3377 {
3378 return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
3379 false, aDefaultAction);
3380 }
3382 // static
3383 nsresult
3384 nsContentUtils::DispatchEvent(nsIDocument* aDoc, nsISupports* aTarget,
3385 const nsAString& aEventName,
3386 bool aCanBubble, bool aCancelable,
3387 bool aTrusted, bool *aDefaultAction)
3388 {
3389 nsCOMPtr<nsIDOMEvent> event;
3390 nsCOMPtr<EventTarget> target;
3391 nsresult rv = GetEventAndTarget(aDoc, aTarget, aEventName, aCanBubble,
3392 aCancelable, aTrusted, getter_AddRefs(event),
3393 getter_AddRefs(target));
3394 NS_ENSURE_SUCCESS(rv, rv);
3396 bool dummy;
3397 return target->DispatchEvent(event, aDefaultAction ? aDefaultAction : &dummy);
3398 }
3400 nsresult
3401 nsContentUtils::DispatchChromeEvent(nsIDocument *aDoc,
3402 nsISupports *aTarget,
3403 const nsAString& aEventName,
3404 bool aCanBubble, bool aCancelable,
3405 bool *aDefaultAction)
3406 {
3408 nsCOMPtr<nsIDOMEvent> event;
3409 nsCOMPtr<EventTarget> target;
3410 nsresult rv = GetEventAndTarget(aDoc, aTarget, aEventName, aCanBubble,
3411 aCancelable, true, getter_AddRefs(event),
3412 getter_AddRefs(target));
3413 NS_ENSURE_SUCCESS(rv, rv);
3415 NS_ASSERTION(aDoc, "GetEventAndTarget lied?");
3416 if (!aDoc->GetWindow())
3417 return NS_ERROR_INVALID_ARG;
3419 EventTarget* piTarget = aDoc->GetWindow()->GetParentTarget();
3420 if (!piTarget)
3421 return NS_ERROR_INVALID_ARG;
3423 nsEventStatus status = nsEventStatus_eIgnore;
3424 rv = piTarget->DispatchDOMEvent(nullptr, event, nullptr, &status);
3425 if (aDefaultAction) {
3426 *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
3427 }
3428 return rv;
3429 }
3431 /* static */
3432 Element*
3433 nsContentUtils::MatchElementId(nsIContent *aContent, const nsIAtom* aId)
3434 {
3435 for (nsIContent* cur = aContent;
3436 cur;
3437 cur = cur->GetNextNode(aContent)) {
3438 if (aId == cur->GetID()) {
3439 return cur->AsElement();
3440 }
3441 }
3443 return nullptr;
3444 }
3446 /* static */
3447 Element *
3448 nsContentUtils::MatchElementId(nsIContent *aContent, const nsAString& aId)
3449 {
3450 NS_PRECONDITION(!aId.IsEmpty(), "Will match random elements");
3452 // ID attrs are generally stored as atoms, so just atomize this up front
3453 nsCOMPtr<nsIAtom> id(do_GetAtom(aId));
3454 if (!id) {
3455 // OOM, so just bail
3456 return nullptr;
3457 }
3459 return MatchElementId(aContent, id);
3460 }
3462 // Convert the string from the given encoding to Unicode.
3463 /* static */
3464 nsresult
3465 nsContentUtils::ConvertStringFromEncoding(const nsACString& aEncoding,
3466 const nsACString& aInput,
3467 nsAString& aOutput)
3468 {
3469 nsAutoCString encoding;
3470 if (aEncoding.IsEmpty()) {
3471 encoding.AssignLiteral("UTF-8");
3472 } else {
3473 encoding.Assign(aEncoding);
3474 }
3476 ErrorResult rv;
3477 nsAutoPtr<TextDecoder> decoder(new TextDecoder());
3478 decoder->InitWithEncoding(encoding, false);
3480 decoder->Decode(aInput.BeginReading(), aInput.Length(), false,
3481 aOutput, rv);
3482 return rv.ErrorCode();
3483 }
3485 /* static */
3486 bool
3487 nsContentUtils::CheckForBOM(const unsigned char* aBuffer, uint32_t aLength,
3488 nsACString& aCharset)
3489 {
3490 bool found = true;
3491 aCharset.Truncate();
3492 if (aLength >= 3 &&
3493 aBuffer[0] == 0xEF &&
3494 aBuffer[1] == 0xBB &&
3495 aBuffer[2] == 0xBF) {
3496 aCharset = "UTF-8";
3497 }
3498 else if (aLength >= 2 &&
3499 aBuffer[0] == 0xFE && aBuffer[1] == 0xFF) {
3500 aCharset = "UTF-16BE";
3501 }
3502 else if (aLength >= 2 &&
3503 aBuffer[0] == 0xFF && aBuffer[1] == 0xFE) {
3504 aCharset = "UTF-16LE";
3505 } else {
3506 found = false;
3507 }
3509 return found;
3510 }
3512 /* static */
3513 void
3514 nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver)
3515 {
3516 nsCOMPtr<nsIObserverService> observerService =
3517 mozilla::services::GetObserverService();
3518 if (observerService) {
3519 observerService->AddObserver(aObserver,
3520 NS_XPCOM_SHUTDOWN_OBSERVER_ID,
3521 false);
3522 }
3523 }
3525 /* static */
3526 void
3527 nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver)
3528 {
3529 nsCOMPtr<nsIObserverService> observerService =
3530 mozilla::services::GetObserverService();
3531 if (observerService) {
3532 observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
3533 }
3534 }
3536 /* static */
3537 bool
3538 nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent, int32_t aNameSpaceID,
3539 nsIAtom* aName)
3540 {
3541 static nsIContent::AttrValuesArray strings[] = {&nsGkAtoms::_empty, nullptr};
3542 return aContent->FindAttrValueIn(aNameSpaceID, aName, strings, eCaseMatters)
3543 == nsIContent::ATTR_VALUE_NO_MATCH;
3544 }
3546 /* static */
3547 bool
3548 nsContentUtils::HasMutationListeners(nsINode* aNode,
3549 uint32_t aType,
3550 nsINode* aTargetForSubtreeModified)
3551 {
3552 nsIDocument* doc = aNode->OwnerDoc();
3554 // global object will be null for documents that don't have windows.
3555 nsPIDOMWindow* window = doc->GetInnerWindow();
3556 // This relies on EventListenerManager::AddEventListener, which sets
3557 // all mutation bits when there is a listener for DOMSubtreeModified event.
3558 if (window && !window->HasMutationListeners(aType)) {
3559 return false;
3560 }
3562 if (aNode->IsNodeOfType(nsINode::eCONTENT) &&
3563 static_cast<nsIContent*>(aNode)->ChromeOnlyAccess()) {
3564 return false;
3565 }
3567 doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
3569 // If we have a window, we can check it for mutation listeners now.
3570 if (aNode->IsInDoc()) {
3571 nsCOMPtr<EventTarget> piTarget(do_QueryInterface(window));
3572 if (piTarget) {
3573 EventListenerManager* manager = piTarget->GetExistingListenerManager();
3574 if (manager && manager->HasMutationListeners()) {
3575 return true;
3576 }
3577 }
3578 }
3580 // If we have a window, we know a mutation listener is registered, but it
3581 // might not be in our chain. If we don't have a window, we might have a
3582 // mutation listener. Check quickly to see.
3583 while (aNode) {
3584 EventListenerManager* manager = aNode->GetExistingListenerManager();
3585 if (manager && manager->HasMutationListeners()) {
3586 return true;
3587 }
3589 if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
3590 nsIContent* content = static_cast<nsIContent*>(aNode);
3591 nsIContent* insertionParent = content->GetXBLInsertionParent();
3592 if (insertionParent) {
3593 aNode = insertionParent;
3594 continue;
3595 }
3596 }
3597 aNode = aNode->GetParentNode();
3598 }
3600 return false;
3601 }
3603 /* static */
3604 bool
3605 nsContentUtils::HasMutationListeners(nsIDocument* aDocument,
3606 uint32_t aType)
3607 {
3608 nsPIDOMWindow* window = aDocument ?
3609 aDocument->GetInnerWindow() : nullptr;
3611 // This relies on EventListenerManager::AddEventListener, which sets
3612 // all mutation bits when there is a listener for DOMSubtreeModified event.
3613 return !window || window->HasMutationListeners(aType);
3614 }
3616 void
3617 nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent,
3618 nsIDocument* aOwnerDoc)
3619 {
3620 NS_PRECONDITION(aChild, "Missing child");
3621 NS_PRECONDITION(aChild->GetParentNode() == aParent, "Wrong parent");
3622 NS_PRECONDITION(aChild->OwnerDoc() == aOwnerDoc, "Wrong owner-doc");
3624 // This checks that IsSafeToRunScript is true since we don't want to fire
3625 // events when that is false. We can't rely on EventDispatcher to assert
3626 // this in this situation since most of the time there are no mutation
3627 // event listeners, in which case we won't even attempt to dispatch events.
3628 // However this also allows for two exceptions. First off, we don't assert
3629 // if the mutation happens to native anonymous content since we never fire
3630 // mutation events on such content anyway.
3631 // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
3632 // that is a know case when we'd normally fire a mutation event, but can't
3633 // make that safe and so we suppress it at this time. Ideally this should
3634 // go away eventually.
3635 NS_ASSERTION((aChild->IsNodeOfType(nsINode::eCONTENT) &&
3636 static_cast<nsIContent*>(aChild)->
3637 IsInNativeAnonymousSubtree()) ||
3638 IsSafeToRunScript() ||
3639 sDOMNodeRemovedSuppressCount,
3640 "Want to fire DOMNodeRemoved event, but it's not safe");
3642 // Having an explicit check here since it's an easy mistake to fall into,
3643 // and there might be existing code with problems. We'd rather be safe
3644 // than fire DOMNodeRemoved in all corner cases. We also rely on it for
3645 // nsAutoScriptBlockerSuppressNodeRemoved.
3646 if (!IsSafeToRunScript()) {
3647 return;
3648 }
3650 if (HasMutationListeners(aChild,
3651 NS_EVENT_BITS_MUTATION_NODEREMOVED, aParent)) {
3652 InternalMutationEvent mutation(true, NS_MUTATION_NODEREMOVED);
3653 mutation.mRelatedNode = do_QueryInterface(aParent);
3655 mozAutoSubtreeModified subtree(aOwnerDoc, aParent);
3656 EventDispatcher::Dispatch(aChild, nullptr, &mutation);
3657 }
3658 }
3660 PLDHashOperator
3661 ListenerEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aEntry,
3662 uint32_t aNumber, void* aArg)
3663 {
3664 EventListenerManagerMapEntry* entry =
3665 static_cast<EventListenerManagerMapEntry*>(aEntry);
3666 if (entry) {
3667 nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
3668 if (n && n->IsInDoc() &&
3669 nsCCUncollectableMarker::InGeneration(n->OwnerDoc()->GetMarkedCCGeneration())) {
3670 entry->mListenerManager->MarkForCC();
3671 }
3672 }
3673 return PL_DHASH_NEXT;
3674 }
3676 void
3677 nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(uint32_t aGeneration)
3678 {
3679 if (sEventListenerManagersHash.ops) {
3680 PL_DHashTableEnumerate(&sEventListenerManagersHash, ListenerEnumerator,
3681 &aGeneration);
3682 }
3683 }
3685 /* static */
3686 void
3687 nsContentUtils::TraverseListenerManager(nsINode *aNode,
3688 nsCycleCollectionTraversalCallback &cb)
3689 {
3690 if (!sEventListenerManagersHash.ops) {
3691 // We're already shut down, just return.
3692 return;
3693 }
3695 EventListenerManagerMapEntry *entry =
3696 static_cast<EventListenerManagerMapEntry *>
3697 (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
3698 PL_DHASH_LOOKUP));
3699 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3700 CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
3701 "[via hash] mListenerManager");
3702 }
3703 }
3705 EventListenerManager*
3706 nsContentUtils::GetListenerManagerForNode(nsINode *aNode)
3707 {
3708 if (!sEventListenerManagersHash.ops) {
3709 // We're already shut down, don't bother creating an event listener
3710 // manager.
3712 return nullptr;
3713 }
3715 EventListenerManagerMapEntry *entry =
3716 static_cast<EventListenerManagerMapEntry *>
3717 (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
3718 PL_DHASH_ADD));
3720 if (!entry) {
3721 return nullptr;
3722 }
3724 if (!entry->mListenerManager) {
3725 entry->mListenerManager = new EventListenerManager(aNode);
3727 aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
3728 }
3730 return entry->mListenerManager;
3731 }
3733 EventListenerManager*
3734 nsContentUtils::GetExistingListenerManagerForNode(const nsINode *aNode)
3735 {
3736 if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
3737 return nullptr;
3738 }
3740 if (!sEventListenerManagersHash.ops) {
3741 // We're already shut down, don't bother creating an event listener
3742 // manager.
3744 return nullptr;
3745 }
3747 EventListenerManagerMapEntry *entry =
3748 static_cast<EventListenerManagerMapEntry *>
3749 (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
3750 PL_DHASH_LOOKUP));
3751 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3752 return entry->mListenerManager;
3753 }
3755 return nullptr;
3756 }
3758 /* static */
3759 void
3760 nsContentUtils::RemoveListenerManager(nsINode *aNode)
3761 {
3762 if (sEventListenerManagersHash.ops) {
3763 EventListenerManagerMapEntry *entry =
3764 static_cast<EventListenerManagerMapEntry *>
3765 (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
3766 PL_DHASH_LOOKUP));
3767 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3768 nsRefPtr<EventListenerManager> listenerManager;
3769 listenerManager.swap(entry->mListenerManager);
3770 // Remove the entry and *then* do operations that could cause further
3771 // modification of sEventListenerManagersHash. See bug 334177.
3772 PL_DHashTableRawRemove(&sEventListenerManagersHash, entry);
3773 if (listenerManager) {
3774 listenerManager->Disconnect();
3775 }
3776 }
3777 }
3778 }
3780 /* static */
3781 bool
3782 nsContentUtils::IsValidNodeName(nsIAtom *aLocalName, nsIAtom *aPrefix,
3783 int32_t aNamespaceID)
3784 {
3785 if (aNamespaceID == kNameSpaceID_Unknown) {
3786 return false;
3787 }
3789 if (!aPrefix) {
3790 // If the prefix is null, then either the QName must be xmlns or the
3791 // namespace must not be XMLNS.
3792 return (aLocalName == nsGkAtoms::xmlns) ==
3793 (aNamespaceID == kNameSpaceID_XMLNS);
3794 }
3796 // If the prefix is non-null then the namespace must not be null.
3797 if (aNamespaceID == kNameSpaceID_None) {
3798 return false;
3799 }
3801 // If the namespace is the XMLNS namespace then the prefix must be xmlns,
3802 // but the localname must not be xmlns.
3803 if (aNamespaceID == kNameSpaceID_XMLNS) {
3804 return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
3805 }
3807 // If the namespace is not the XMLNS namespace then the prefix must not be
3808 // xmlns.
3809 // If the namespace is the XML namespace then the prefix can be anything.
3810 // If the namespace is not the XML namespace then the prefix must not be xml.
3811 return aPrefix != nsGkAtoms::xmlns &&
3812 (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
3813 }
3815 /* static */
3816 nsresult
3817 nsContentUtils::CreateContextualFragment(nsINode* aContextNode,
3818 const nsAString& aFragment,
3819 bool aPreventScriptExecution,
3820 nsIDOMDocumentFragment** aReturn)
3821 {
3822 ErrorResult rv;
3823 *aReturn = CreateContextualFragment(aContextNode, aFragment,
3824 aPreventScriptExecution, rv).take();
3825 return rv.ErrorCode();
3826 }
3828 already_AddRefed<DocumentFragment>
3829 nsContentUtils::CreateContextualFragment(nsINode* aContextNode,
3830 const nsAString& aFragment,
3831 bool aPreventScriptExecution,
3832 ErrorResult& aRv)
3833 {
3834 if (!aContextNode) {
3835 aRv.Throw(NS_ERROR_INVALID_ARG);
3836 return nullptr;
3837 }
3839 // If we don't have a document here, we can't get the right security context
3840 // for compiling event handlers... so just bail out.
3841 nsCOMPtr<nsIDocument> document = aContextNode->OwnerDoc();
3842 bool isHTML = document->IsHTML();
3843 #ifdef DEBUG
3844 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document);
3845 NS_ASSERTION(!isHTML || htmlDoc, "Should have HTMLDocument here!");
3846 #endif
3848 if (isHTML) {
3849 nsRefPtr<DocumentFragment> frag =
3850 new DocumentFragment(document->NodeInfoManager());
3852 nsCOMPtr<nsIContent> contextAsContent = do_QueryInterface(aContextNode);
3853 if (contextAsContent && !contextAsContent->IsElement()) {
3854 contextAsContent = contextAsContent->GetParent();
3855 if (contextAsContent && !contextAsContent->IsElement()) {
3856 // can this even happen?
3857 contextAsContent = nullptr;
3858 }
3859 }
3861 if (contextAsContent && !contextAsContent->IsHTML(nsGkAtoms::html)) {
3862 aRv = ParseFragmentHTML(aFragment, frag,
3863 contextAsContent->Tag(),
3864 contextAsContent->GetNameSpaceID(),
3865 (document->GetCompatibilityMode() ==
3866 eCompatibility_NavQuirks),
3867 aPreventScriptExecution);
3868 } else {
3869 aRv = ParseFragmentHTML(aFragment, frag,
3870 nsGkAtoms::body,
3871 kNameSpaceID_XHTML,
3872 (document->GetCompatibilityMode() ==
3873 eCompatibility_NavQuirks),
3874 aPreventScriptExecution);
3875 }
3877 return frag.forget();
3878 }
3880 nsAutoTArray<nsString, 32> tagStack;
3881 nsAutoString uriStr, nameStr;
3882 nsCOMPtr<nsIContent> content = do_QueryInterface(aContextNode);
3883 // just in case we have a text node
3884 if (content && !content->IsElement())
3885 content = content->GetParent();
3887 while (content && content->IsElement()) {
3888 nsString& tagName = *tagStack.AppendElement();
3889 if (!&tagName) {
3890 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
3891 return nullptr;
3892 }
3894 tagName = content->NodeInfo()->QualifiedName();
3896 // see if we need to add xmlns declarations
3897 uint32_t count = content->GetAttrCount();
3898 bool setDefaultNamespace = false;
3899 if (count > 0) {
3900 uint32_t index;
3902 for (index = 0; index < count; index++) {
3903 const nsAttrName* name = content->GetAttrNameAt(index);
3904 if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
3905 content->GetAttr(kNameSpaceID_XMLNS, name->LocalName(), uriStr);
3907 // really want something like nsXMLContentSerializer::SerializeAttr
3908 tagName.Append(NS_LITERAL_STRING(" xmlns")); // space important
3909 if (name->GetPrefix()) {
3910 tagName.Append(char16_t(':'));
3911 name->LocalName()->ToString(nameStr);
3912 tagName.Append(nameStr);
3913 } else {
3914 setDefaultNamespace = true;
3915 }
3916 tagName.Append(NS_LITERAL_STRING("=\"") + uriStr +
3917 NS_LITERAL_STRING("\""));
3918 }
3919 }
3920 }
3922 if (!setDefaultNamespace) {
3923 nsINodeInfo* info = content->NodeInfo();
3924 if (!info->GetPrefixAtom() &&
3925 info->NamespaceID() != kNameSpaceID_None) {
3926 // We have no namespace prefix, but have a namespace ID. Push
3927 // default namespace attr in, so that our kids will be in our
3928 // namespace.
3929 info->GetNamespaceURI(uriStr);
3930 tagName.Append(NS_LITERAL_STRING(" xmlns=\"") + uriStr +
3931 NS_LITERAL_STRING("\""));
3932 }
3933 }
3935 content = content->GetParent();
3936 }
3938 nsCOMPtr<nsIDOMDocumentFragment> frag;
3939 aRv = ParseFragmentXML(aFragment, document, tagStack,
3940 aPreventScriptExecution, getter_AddRefs(frag));
3941 return frag.forget().downcast<DocumentFragment>();
3942 }
3944 /* static */
3945 void
3946 nsContentUtils::DropFragmentParsers()
3947 {
3948 NS_IF_RELEASE(sHTMLFragmentParser);
3949 NS_IF_RELEASE(sXMLFragmentParser);
3950 NS_IF_RELEASE(sXMLFragmentSink);
3951 }
3953 /* static */
3954 void
3955 nsContentUtils::XPCOMShutdown()
3956 {
3957 nsContentUtils::DropFragmentParsers();
3958 }
3960 /* static */
3961 nsresult
3962 nsContentUtils::ParseFragmentHTML(const nsAString& aSourceBuffer,
3963 nsIContent* aTargetNode,
3964 nsIAtom* aContextLocalName,
3965 int32_t aContextNamespace,
3966 bool aQuirks,
3967 bool aPreventScriptExecution)
3968 {
3969 if (nsContentUtils::sFragmentParsingActive) {
3970 NS_NOTREACHED("Re-entrant fragment parsing attempted.");
3971 return NS_ERROR_DOM_INVALID_STATE_ERR;
3972 }
3973 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
3974 nsContentUtils::sFragmentParsingActive = true;
3975 if (!sHTMLFragmentParser) {
3976 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
3977 // Now sHTMLFragmentParser owns the object
3978 }
3979 nsresult rv =
3980 sHTMLFragmentParser->ParseFragment(aSourceBuffer,
3981 aTargetNode,
3982 aContextLocalName,
3983 aContextNamespace,
3984 aQuirks,
3985 aPreventScriptExecution);
3986 return rv;
3987 }
3989 /* static */
3990 nsresult
3991 nsContentUtils::ParseDocumentHTML(const nsAString& aSourceBuffer,
3992 nsIDocument* aTargetDocument,
3993 bool aScriptingEnabledForNoscriptParsing)
3994 {
3995 if (nsContentUtils::sFragmentParsingActive) {
3996 NS_NOTREACHED("Re-entrant fragment parsing attempted.");
3997 return NS_ERROR_DOM_INVALID_STATE_ERR;
3998 }
3999 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
4000 nsContentUtils::sFragmentParsingActive = true;
4001 if (!sHTMLFragmentParser) {
4002 NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
4003 // Now sHTMLFragmentParser owns the object
4004 }
4005 nsresult rv =
4006 sHTMLFragmentParser->ParseDocument(aSourceBuffer,
4007 aTargetDocument,
4008 aScriptingEnabledForNoscriptParsing);
4009 return rv;
4010 }
4012 /* static */
4013 nsresult
4014 nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
4015 nsIDocument* aDocument,
4016 nsTArray<nsString>& aTagStack,
4017 bool aPreventScriptExecution,
4018 nsIDOMDocumentFragment** aReturn)
4019 {
4020 if (nsContentUtils::sFragmentParsingActive) {
4021 NS_NOTREACHED("Re-entrant fragment parsing attempted.");
4022 return NS_ERROR_DOM_INVALID_STATE_ERR;
4023 }
4024 mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
4025 nsContentUtils::sFragmentParsingActive = true;
4026 if (!sXMLFragmentParser) {
4027 nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID);
4028 parser.forget(&sXMLFragmentParser);
4029 // sXMLFragmentParser now owns the parser
4030 }
4031 if (!sXMLFragmentSink) {
4032 NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
4033 // sXMLFragmentSink now owns the sink
4034 }
4035 nsCOMPtr<nsIContentSink> contentsink = do_QueryInterface(sXMLFragmentSink);
4036 NS_ABORT_IF_FALSE(contentsink, "Sink doesn't QI to nsIContentSink!");
4037 sXMLFragmentParser->SetContentSink(contentsink);
4039 sXMLFragmentSink->SetTargetDocument(aDocument);
4040 sXMLFragmentSink->SetPreventScriptExecution(aPreventScriptExecution);
4042 nsresult rv =
4043 sXMLFragmentParser->ParseFragment(aSourceBuffer,
4044 aTagStack);
4045 if (NS_FAILED(rv)) {
4046 // Drop the fragment parser and sink that might be in an inconsistent state
4047 NS_IF_RELEASE(sXMLFragmentParser);
4048 NS_IF_RELEASE(sXMLFragmentSink);
4049 return rv;
4050 }
4052 rv = sXMLFragmentSink->FinishFragmentParsing(aReturn);
4054 sXMLFragmentParser->Reset();
4056 return rv;
4057 }
4059 /* static */
4060 nsresult
4061 nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
4062 nsAString& aResultBuffer,
4063 uint32_t aFlags,
4064 uint32_t aWrapCol)
4065 {
4066 nsCOMPtr<nsIURI> uri;
4067 NS_NewURI(getter_AddRefs(uri), "about:blank");
4068 nsCOMPtr<nsIPrincipal> principal =
4069 do_CreateInstance(NS_NULLPRINCIPAL_CONTRACTID);
4070 nsCOMPtr<nsIDOMDocument> domDocument;
4071 nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument),
4072 EmptyString(),
4073 EmptyString(),
4074 nullptr,
4075 uri,
4076 uri,
4077 principal,
4078 true,
4079 nullptr,
4080 DocumentFlavorHTML);
4081 NS_ENSURE_SUCCESS(rv, rv);
4083 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
4084 rv = nsContentUtils::ParseDocumentHTML(aSourceBuffer, document,
4085 !(aFlags & nsIDocumentEncoder::OutputNoScriptContent));
4086 NS_ENSURE_SUCCESS(rv, rv);
4088 nsCOMPtr<nsIDocumentEncoder> encoder = do_CreateInstance(
4089 "@mozilla.org/layout/documentEncoder;1?type=text/plain");
4091 rv = encoder->Init(domDocument, NS_LITERAL_STRING("text/plain"), aFlags);
4092 NS_ENSURE_SUCCESS(rv, rv);
4094 encoder->SetWrapColumn(aWrapCol);
4096 return encoder->EncodeToString(aResultBuffer);
4097 }
4099 /* static */
4100 nsresult
4101 nsContentUtils::SetNodeTextContent(nsIContent* aContent,
4102 const nsAString& aValue,
4103 bool aTryReuse)
4104 {
4105 // Fire DOMNodeRemoved mutation events before we do anything else.
4106 nsCOMPtr<nsIContent> owningContent;
4108 // Batch possible DOMSubtreeModified events.
4109 mozAutoSubtreeModified subtree(nullptr, nullptr);
4111 // Scope firing mutation events so that we don't carry any state that
4112 // might be stale
4113 {
4114 // We're relying on mozAutoSubtreeModified to keep a strong reference if
4115 // needed.
4116 nsIDocument* doc = aContent->OwnerDoc();
4118 // Optimize the common case of there being no observers
4119 if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
4120 subtree.UpdateTarget(doc, nullptr);
4121 owningContent = aContent;
4122 nsCOMPtr<nsINode> child;
4123 bool skipFirst = aTryReuse;
4124 for (child = aContent->GetFirstChild();
4125 child && child->GetParentNode() == aContent;
4126 child = child->GetNextSibling()) {
4127 if (skipFirst && child->IsNodeOfType(nsINode::eTEXT)) {
4128 skipFirst = false;
4129 continue;
4130 }
4131 nsContentUtils::MaybeFireNodeRemoved(child, aContent, doc);
4132 }
4133 }
4134 }
4136 // Might as well stick a batch around this since we're performing several
4137 // mutations.
4138 mozAutoDocUpdate updateBatch(aContent->GetCurrentDoc(),
4139 UPDATE_CONTENT_MODEL, true);
4140 nsAutoMutationBatch mb;
4142 uint32_t childCount = aContent->GetChildCount();
4144 if (aTryReuse && !aValue.IsEmpty()) {
4145 uint32_t removeIndex = 0;
4147 for (uint32_t i = 0; i < childCount; ++i) {
4148 nsIContent* child = aContent->GetChildAt(removeIndex);
4149 if (removeIndex == 0 && child && child->IsNodeOfType(nsINode::eTEXT)) {
4150 nsresult rv = child->SetText(aValue, true);
4151 NS_ENSURE_SUCCESS(rv, rv);
4153 removeIndex = 1;
4154 }
4155 else {
4156 aContent->RemoveChildAt(removeIndex, true);
4157 }
4158 }
4160 if (removeIndex == 1) {
4161 return NS_OK;
4162 }
4163 }
4164 else {
4165 mb.Init(aContent, true, false);
4166 for (uint32_t i = 0; i < childCount; ++i) {
4167 aContent->RemoveChildAt(0, true);
4168 }
4169 }
4170 mb.RemovalDone();
4172 if (aValue.IsEmpty()) {
4173 return NS_OK;
4174 }
4176 nsRefPtr<nsTextNode> textContent =
4177 new nsTextNode(aContent->NodeInfo()->NodeInfoManager());
4179 textContent->SetText(aValue, true);
4181 nsresult rv = aContent->AppendChildTo(textContent, true);
4182 mb.NodesAdded();
4183 return rv;
4184 }
4186 static bool
4187 AppendNodeTextContentsRecurse(nsINode* aNode, nsAString& aResult,
4188 const mozilla::fallible_t&)
4189 {
4190 for (nsIContent* child = aNode->GetFirstChild();
4191 child;
4192 child = child->GetNextSibling()) {
4193 if (child->IsElement()) {
4194 bool ok = AppendNodeTextContentsRecurse(child, aResult,
4195 mozilla::fallible_t());
4196 if (!ok) {
4197 return false;
4198 }
4199 }
4200 else if (child->IsNodeOfType(nsINode::eTEXT)) {
4201 bool ok = child->AppendTextTo(aResult, mozilla::fallible_t());
4202 if (!ok) {
4203 return false;
4204 }
4205 }
4206 }
4208 return true;
4209 }
4211 /* static */
4212 bool
4213 nsContentUtils::AppendNodeTextContent(nsINode* aNode, bool aDeep,
4214 nsAString& aResult,
4215 const mozilla::fallible_t&)
4216 {
4217 if (aNode->IsNodeOfType(nsINode::eTEXT)) {
4218 return static_cast<nsIContent*>(aNode)->AppendTextTo(aResult,
4219 mozilla::fallible_t());
4220 }
4221 else if (aDeep) {
4222 return AppendNodeTextContentsRecurse(aNode, aResult, mozilla::fallible_t());
4223 }
4224 else {
4225 for (nsIContent* child = aNode->GetFirstChild();
4226 child;
4227 child = child->GetNextSibling()) {
4228 if (child->IsNodeOfType(nsINode::eTEXT)) {
4229 bool ok = child->AppendTextTo(aResult, mozilla::fallible_t());
4230 if (!ok) {
4231 return false;
4232 }
4233 }
4234 }
4235 }
4237 return true;
4238 }
4240 bool
4241 nsContentUtils::HasNonEmptyTextContent(nsINode* aNode)
4242 {
4243 for (nsIContent* child = aNode->GetFirstChild();
4244 child;
4245 child = child->GetNextSibling()) {
4246 if (child->IsNodeOfType(nsINode::eTEXT) &&
4247 child->TextLength() > 0) {
4248 return true;
4249 }
4250 }
4252 return false;
4253 }
4255 /* static */
4256 bool
4257 nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
4258 const nsIContent* aContent)
4259 {
4260 NS_PRECONDITION(aNode,
4261 "Must have a node to work with");
4262 NS_PRECONDITION(aContent,
4263 "Must have a content to work with");
4265 if (!aNode->IsNodeOfType(nsINode::eCONTENT)) {
4266 /**
4267 * The root isn't an nsIContent, so it's a document or attribute. The only
4268 * nodes in the same anonymous subtree as it will have a null
4269 * bindingParent.
4270 *
4271 * XXXbz strictly speaking, that's not true for attribute nodes.
4272 */
4273 return aContent->GetBindingParent() == nullptr;
4274 }
4276 const nsIContent* nodeAsContent = static_cast<const nsIContent*>(aNode);
4278 // For nodes in a shadow tree, it is insufficient to simply compare
4279 // the binding parent because a node may host multiple ShadowRoots,
4280 // thus nodes in different shadow tree may have the same binding parent.
4281 if (aNode->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
4282 return nodeAsContent->GetContainingShadow() ==
4283 aContent->GetContainingShadow();
4284 }
4286 return nodeAsContent->GetBindingParent() == aContent->GetBindingParent();
4287 }
4289 class AnonymousContentDestroyer : public nsRunnable {
4290 public:
4291 AnonymousContentDestroyer(nsCOMPtr<nsIContent>* aContent) {
4292 mContent.swap(*aContent);
4293 mParent = mContent->GetParent();
4294 mDoc = mContent->OwnerDoc();
4295 }
4296 AnonymousContentDestroyer(nsCOMPtr<Element>* aElement) {
4297 mContent = aElement->forget();
4298 mParent = mContent->GetParent();
4299 mDoc = mContent->OwnerDoc();
4300 }
4301 NS_IMETHOD Run() {
4302 mContent->UnbindFromTree();
4303 return NS_OK;
4304 }
4305 private:
4306 nsCOMPtr<nsIContent> mContent;
4307 // Hold strong refs to the parent content and document so that they
4308 // don't die unexpectedly
4309 nsCOMPtr<nsIDocument> mDoc;
4310 nsCOMPtr<nsIContent> mParent;
4311 };
4313 /* static */
4314 void
4315 nsContentUtils::DestroyAnonymousContent(nsCOMPtr<nsIContent>* aContent)
4316 {
4317 if (*aContent) {
4318 AddScriptRunner(new AnonymousContentDestroyer(aContent));
4319 }
4320 }
4322 /* static */
4323 void
4324 nsContentUtils::DestroyAnonymousContent(nsCOMPtr<Element>* aElement)
4325 {
4326 if (*aElement) {
4327 AddScriptRunner(new AnonymousContentDestroyer(aElement));
4328 }
4329 }
4331 /* static */
4332 void
4333 nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling)
4334 {
4335 IMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
4336 }
4338 static bool SchemeIs(nsIURI* aURI, const char* aScheme)
4339 {
4340 nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
4341 NS_ENSURE_TRUE(baseURI, false);
4343 bool isScheme = false;
4344 return NS_SUCCEEDED(baseURI->SchemeIs(aScheme, &isScheme)) && isScheme;
4345 }
4347 /* static */
4348 nsresult
4349 nsContentUtils::CheckSecurityBeforeLoad(nsIURI* aURIToLoad,
4350 nsIPrincipal* aLoadingPrincipal,
4351 uint32_t aCheckLoadFlags,
4352 bool aAllowData,
4353 uint32_t aContentPolicyType,
4354 nsISupports* aContext,
4355 const nsAFlatCString& aMimeGuess,
4356 nsISupports* aExtra)
4357 {
4358 NS_PRECONDITION(aLoadingPrincipal, "Must have a loading principal here");
4360 bool isSystemPrin = false;
4361 if (NS_SUCCEEDED(sSecurityManager->IsSystemPrincipal(aLoadingPrincipal,
4362 &isSystemPrin)) &&
4363 isSystemPrin) {
4364 return NS_OK;
4365 }
4367 // XXXbz do we want to fast-path skin stylesheets loading XBL here somehow?
4368 // CheckLoadURIWithPrincipal
4369 nsresult rv = sSecurityManager->
4370 CheckLoadURIWithPrincipal(aLoadingPrincipal, aURIToLoad, aCheckLoadFlags);
4371 NS_ENSURE_SUCCESS(rv, rv);
4373 // Content Policy
4374 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
4375 rv = NS_CheckContentLoadPolicy(aContentPolicyType,
4376 aURIToLoad,
4377 aLoadingPrincipal,
4378 aContext,
4379 aMimeGuess,
4380 aExtra,
4381 &shouldLoad,
4382 GetContentPolicy(),
4383 sSecurityManager);
4384 NS_ENSURE_SUCCESS(rv, rv);
4385 if (NS_CP_REJECTED(shouldLoad)) {
4386 return NS_ERROR_CONTENT_BLOCKED;
4387 }
4389 // Same Origin
4390 if ((aAllowData && SchemeIs(aURIToLoad, "data")) ||
4391 ((aCheckLoadFlags & nsIScriptSecurityManager::ALLOW_CHROME) &&
4392 SchemeIs(aURIToLoad, "chrome"))) {
4393 return NS_OK;
4394 }
4396 return aLoadingPrincipal->CheckMayLoad(aURIToLoad, true, false);
4397 }
4399 bool
4400 nsContentUtils::IsSystemPrincipal(nsIPrincipal* aPrincipal)
4401 {
4402 bool isSystem;
4403 nsresult rv = sSecurityManager->IsSystemPrincipal(aPrincipal, &isSystem);
4404 return NS_SUCCEEDED(rv) && isSystem;
4405 }
4407 bool
4408 nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal)
4409 {
4410 nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
4411 return !!ep;
4412 }
4414 nsIPrincipal*
4415 nsContentUtils::GetSystemPrincipal()
4416 {
4417 nsCOMPtr<nsIPrincipal> sysPrin;
4418 DebugOnly<nsresult> rv =
4419 sSecurityManager->GetSystemPrincipal(getter_AddRefs(sysPrin));
4420 MOZ_ASSERT(NS_SUCCEEDED(rv) && sysPrin);
4421 return sysPrin;
4422 }
4424 bool
4425 nsContentUtils::CombineResourcePrincipals(nsCOMPtr<nsIPrincipal>* aResourcePrincipal,
4426 nsIPrincipal* aExtraPrincipal)
4427 {
4428 if (!aExtraPrincipal) {
4429 return false;
4430 }
4431 if (!*aResourcePrincipal) {
4432 *aResourcePrincipal = aExtraPrincipal;
4433 return true;
4434 }
4435 if (*aResourcePrincipal == aExtraPrincipal) {
4436 return false;
4437 }
4438 bool subsumes;
4439 if (NS_SUCCEEDED((*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
4440 subsumes) {
4441 return false;
4442 }
4443 sSecurityManager->GetSystemPrincipal(getter_AddRefs(*aResourcePrincipal));
4444 return true;
4445 }
4447 /* static */
4448 void
4449 nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
4450 nsIURI *aLinkURI, const nsString &aTargetSpec,
4451 bool aClick, bool aIsUserTriggered,
4452 bool aIsTrusted)
4453 {
4454 NS_ASSERTION(aPresContext, "Need a nsPresContext");
4455 NS_PRECONDITION(aLinkURI, "No link URI");
4457 if (aContent->IsEditable()) {
4458 return;
4459 }
4461 nsILinkHandler *handler = aPresContext->GetLinkHandler();
4462 if (!handler) {
4463 return;
4464 }
4466 if (!aClick) {
4467 handler->OnOverLink(aContent, aLinkURI, aTargetSpec.get());
4468 return;
4469 }
4471 // Check that this page is allowed to load this URI.
4472 nsresult proceed = NS_OK;
4474 if (sSecurityManager) {
4475 uint32_t flag =
4476 aIsUserTriggered ?
4477 (uint32_t)nsIScriptSecurityManager::STANDARD :
4478 (uint32_t)nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT;
4479 proceed =
4480 sSecurityManager->CheckLoadURIWithPrincipal(aContent->NodePrincipal(),
4481 aLinkURI, flag);
4482 }
4484 // Only pass off the click event if the script security manager says it's ok.
4485 // We need to rest aTargetSpec for forced downloads.
4486 if (NS_SUCCEEDED(proceed)) {
4488 // A link/area element with a download attribute is allowed to set
4489 // a pseudo Content-Disposition header.
4490 // For security reasons we only allow websites to declare same-origin resources
4491 // as downloadable. If this check fails we will just do the normal thing
4492 // (i.e. navigate to the resource).
4493 nsAutoString fileName;
4494 if ((!aContent->IsHTML(nsGkAtoms::a) && !aContent->IsHTML(nsGkAtoms::area) &&
4495 !aContent->IsSVG(nsGkAtoms::a)) ||
4496 !aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::download, fileName) ||
4497 NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, false, true))) {
4498 fileName.SetIsVoid(true); // No actionable download attribute was found.
4499 }
4501 handler->OnLinkClick(aContent, aLinkURI,
4502 fileName.IsVoid() ? aTargetSpec.get() : EmptyString().get(),
4503 fileName, nullptr, nullptr, aIsTrusted);
4504 }
4505 }
4507 /* static */
4508 void
4509 nsContentUtils::GetLinkLocation(Element* aElement, nsString& aLocationString)
4510 {
4511 nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
4512 if (hrefURI) {
4513 nsAutoCString specUTF8;
4514 nsresult rv = hrefURI->GetSpec(specUTF8);
4515 if (NS_SUCCEEDED(rv))
4516 CopyUTF8toUTF16(specUTF8, aLocationString);
4517 }
4518 }
4520 /* static */
4521 nsIWidget*
4522 nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget)
4523 {
4524 if (!aWidget)
4525 return nullptr;
4527 return aWidget->GetTopLevelWidget();
4528 }
4530 /* static */
4531 const nsDependentString
4532 nsContentUtils::GetLocalizedEllipsis()
4533 {
4534 static char16_t sBuf[4] = { 0, 0, 0, 0 };
4535 if (!sBuf[0]) {
4536 nsAdoptingString tmp = Preferences::GetLocalizedString("intl.ellipsis");
4537 uint32_t len = std::min(uint32_t(tmp.Length()),
4538 uint32_t(ArrayLength(sBuf) - 1));
4539 CopyUnicodeTo(tmp, 0, sBuf, len);
4540 if (!sBuf[0])
4541 sBuf[0] = char16_t(0x2026);
4542 }
4543 return nsDependentString(sBuf);
4544 }
4546 static bool
4547 HasASCIIDigit(const nsTArray<nsShortcutCandidate>& aCandidates)
4548 {
4549 for (uint32_t i = 0; i < aCandidates.Length(); ++i) {
4550 uint32_t ch = aCandidates[i].mCharCode;
4551 if (ch >= '0' && ch <= '9')
4552 return true;
4553 }
4554 return false;
4555 }
4557 static bool
4558 CharsCaseInsensitiveEqual(uint32_t aChar1, uint32_t aChar2)
4559 {
4560 return aChar1 == aChar2 ||
4561 (IS_IN_BMP(aChar1) && IS_IN_BMP(aChar2) &&
4562 ToLowerCase(char16_t(aChar1)) == ToLowerCase(char16_t(aChar2)));
4563 }
4565 static bool
4566 IsCaseChangeableChar(uint32_t aChar)
4567 {
4568 return IS_IN_BMP(aChar) &&
4569 ToLowerCase(char16_t(aChar)) != ToUpperCase(char16_t(aChar));
4570 }
4572 /* static */
4573 void
4574 nsContentUtils::GetAccelKeyCandidates(nsIDOMKeyEvent* aDOMKeyEvent,
4575 nsTArray<nsShortcutCandidate>& aCandidates)
4576 {
4577 NS_PRECONDITION(aCandidates.IsEmpty(), "aCandidates must be empty");
4579 nsAutoString eventType;
4580 aDOMKeyEvent->GetType(eventType);
4581 // Don't process if aDOMKeyEvent is not a keypress event.
4582 if (!eventType.EqualsLiteral("keypress"))
4583 return;
4585 WidgetKeyboardEvent* nativeKeyEvent =
4586 aDOMKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
4587 if (nativeKeyEvent) {
4588 NS_ASSERTION(nativeKeyEvent->eventStructType == NS_KEY_EVENT,
4589 "wrong type of native event");
4590 // nsShortcutCandidate::mCharCode is a candidate charCode.
4591 // nsShoftcutCandidate::mIgnoreShift means the mCharCode should be tried to
4592 // execute a command with/without shift key state. If this is TRUE, the
4593 // shifted key state should be ignored. Otherwise, don't ignore the state.
4594 // the priority of the charCodes are (shift key is not pressed):
4595 // 0: charCode/false,
4596 // 1: unshiftedCharCodes[0]/false, 2: unshiftedCharCodes[1]/false...
4597 // the priority of the charCodes are (shift key is pressed):
4598 // 0: charCode/false,
4599 // 1: shiftedCharCodes[0]/false, 2: shiftedCharCodes[0]/true,
4600 // 3: shiftedCharCodes[1]/false, 4: shiftedCharCodes[1]/true...
4601 if (nativeKeyEvent->charCode) {
4602 nsShortcutCandidate key(nativeKeyEvent->charCode, false);
4603 aCandidates.AppendElement(key);
4604 }
4606 uint32_t len = nativeKeyEvent->alternativeCharCodes.Length();
4607 if (!nativeKeyEvent->IsShift()) {
4608 for (uint32_t i = 0; i < len; ++i) {
4609 uint32_t ch =
4610 nativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode;
4611 if (!ch || ch == nativeKeyEvent->charCode)
4612 continue;
4614 nsShortcutCandidate key(ch, false);
4615 aCandidates.AppendElement(key);
4616 }
4617 // If unshiftedCharCodes doesn't have numeric but shiftedCharCode has it,
4618 // this keyboard layout is AZERTY or similar layout, probably.
4619 // In this case, Accel+[0-9] should be accessible without shift key.
4620 // However, the priority should be lowest.
4621 if (!HasASCIIDigit(aCandidates)) {
4622 for (uint32_t i = 0; i < len; ++i) {
4623 uint32_t ch =
4624 nativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode;
4625 if (ch >= '0' && ch <= '9') {
4626 nsShortcutCandidate key(ch, false);
4627 aCandidates.AppendElement(key);
4628 break;
4629 }
4630 }
4631 }
4632 } else {
4633 for (uint32_t i = 0; i < len; ++i) {
4634 uint32_t ch = nativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode;
4635 if (!ch)
4636 continue;
4638 if (ch != nativeKeyEvent->charCode) {
4639 nsShortcutCandidate key(ch, false);
4640 aCandidates.AppendElement(key);
4641 }
4643 // If the char is an alphabet, the shift key state should not be
4644 // ignored. E.g., Ctrl+Shift+C should not execute Ctrl+C.
4646 // And checking the charCode is same as unshiftedCharCode too.
4647 // E.g., for Ctrl+Shift+(Plus of Numpad) should not run Ctrl+Plus.
4648 uint32_t unshiftCh =
4649 nativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode;
4650 if (CharsCaseInsensitiveEqual(ch, unshiftCh))
4651 continue;
4653 // On the Hebrew keyboard layout on Windows, the unshifted char is a
4654 // localized character but the shifted char is a Latin alphabet,
4655 // then, we should not execute without the shift state. See bug 433192.
4656 if (IsCaseChangeableChar(ch))
4657 continue;
4659 // Setting the alternative charCode candidates for retry without shift
4660 // key state only when the shift key is pressed.
4661 nsShortcutCandidate key(ch, true);
4662 aCandidates.AppendElement(key);
4663 }
4664 }
4665 } else {
4666 uint32_t charCode;
4667 aDOMKeyEvent->GetCharCode(&charCode);
4668 if (charCode) {
4669 nsShortcutCandidate key(charCode, false);
4670 aCandidates.AppendElement(key);
4671 }
4672 }
4673 }
4675 /* static */
4676 void
4677 nsContentUtils::GetAccessKeyCandidates(WidgetKeyboardEvent* aNativeKeyEvent,
4678 nsTArray<uint32_t>& aCandidates)
4679 {
4680 NS_PRECONDITION(aCandidates.IsEmpty(), "aCandidates must be empty");
4682 // return the lower cased charCode candidates for access keys.
4683 // the priority of the charCodes are:
4684 // 0: charCode, 1: unshiftedCharCodes[0], 2: shiftedCharCodes[0]
4685 // 3: unshiftedCharCodes[1], 4: shiftedCharCodes[1],...
4686 if (aNativeKeyEvent->charCode) {
4687 uint32_t ch = aNativeKeyEvent->charCode;
4688 if (IS_IN_BMP(ch))
4689 ch = ToLowerCase(char16_t(ch));
4690 aCandidates.AppendElement(ch);
4691 }
4692 for (uint32_t i = 0;
4693 i < aNativeKeyEvent->alternativeCharCodes.Length(); ++i) {
4694 uint32_t ch[2] =
4695 { aNativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode,
4696 aNativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode };
4697 for (uint32_t j = 0; j < 2; ++j) {
4698 if (!ch[j])
4699 continue;
4700 if (IS_IN_BMP(ch[j]))
4701 ch[j] = ToLowerCase(char16_t(ch[j]));
4702 // Don't append the charCode that was already appended.
4703 if (aCandidates.IndexOf(ch[j]) == aCandidates.NoIndex)
4704 aCandidates.AppendElement(ch[j]);
4705 }
4706 }
4707 return;
4708 }
4710 /* static */
4711 void
4712 nsContentUtils::AddScriptBlocker()
4713 {
4714 if (!sScriptBlockerCount) {
4715 NS_ASSERTION(sRunnersCountAtFirstBlocker == 0,
4716 "Should not already have a count");
4717 sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Length();
4718 }
4719 ++sScriptBlockerCount;
4720 }
4722 #ifdef DEBUG
4723 static bool sRemovingScriptBlockers = false;
4724 #endif
4726 /* static */
4727 void
4728 nsContentUtils::RemoveScriptBlocker()
4729 {
4730 MOZ_ASSERT(!sRemovingScriptBlockers);
4731 NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
4732 --sScriptBlockerCount;
4733 if (sScriptBlockerCount) {
4734 return;
4735 }
4737 uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
4738 uint32_t lastBlocker = sBlockedScriptRunners->Length();
4739 uint32_t originalFirstBlocker = firstBlocker;
4740 uint32_t blockersCount = lastBlocker - firstBlocker;
4741 sRunnersCountAtFirstBlocker = 0;
4742 NS_ASSERTION(firstBlocker <= lastBlocker,
4743 "bad sRunnersCountAtFirstBlocker");
4745 while (firstBlocker < lastBlocker) {
4746 nsCOMPtr<nsIRunnable> runnable;
4747 runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
4748 ++firstBlocker;
4750 // Calling the runnable can reenter us
4751 runnable->Run();
4752 // So can dropping the reference to the runnable
4753 runnable = nullptr;
4755 NS_ASSERTION(sRunnersCountAtFirstBlocker == 0,
4756 "Bad count");
4757 NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
4758 }
4759 #ifdef DEBUG
4760 AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
4761 sRemovingScriptBlockers = true;
4762 #endif
4763 sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
4764 }
4766 /* static */
4767 bool
4768 nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
4769 {
4770 if (!aRunnable) {
4771 return false;
4772 }
4774 if (sScriptBlockerCount) {
4775 return sBlockedScriptRunners->AppendElement(aRunnable) != nullptr;
4776 }
4778 nsCOMPtr<nsIRunnable> run = aRunnable;
4779 run->Run();
4781 return true;
4782 }
4784 void
4785 nsContentUtils::EnterMicroTask()
4786 {
4787 MOZ_ASSERT(NS_IsMainThread());
4788 ++sMicroTaskLevel;
4789 }
4791 void
4792 nsContentUtils::LeaveMicroTask()
4793 {
4794 MOZ_ASSERT(NS_IsMainThread());
4795 if (--sMicroTaskLevel == 0) {
4796 nsDOMMutationObserver::HandleMutations();
4797 nsDocument::ProcessBaseElementQueue();
4798 }
4799 }
4801 bool
4802 nsContentUtils::IsInMicroTask()
4803 {
4804 MOZ_ASSERT(NS_IsMainThread());
4805 return sMicroTaskLevel != 0;
4806 }
4808 uint32_t
4809 nsContentUtils::MicroTaskLevel()
4810 {
4811 MOZ_ASSERT(NS_IsMainThread());
4812 return sMicroTaskLevel;
4813 }
4815 void
4816 nsContentUtils::SetMicroTaskLevel(uint32_t aLevel)
4817 {
4818 MOZ_ASSERT(NS_IsMainThread());
4819 sMicroTaskLevel = aLevel;
4820 }
4822 /*
4823 * Helper function for nsContentUtils::ProcessViewportInfo.
4824 *
4825 * Handles a single key=value pair. If it corresponds to a valid viewport
4826 * attribute, add it to the document header data. No validation is done on the
4827 * value itself (this is done at display time).
4828 */
4829 static void ProcessViewportToken(nsIDocument *aDocument,
4830 const nsAString &token) {
4832 /* Iterators. */
4833 nsAString::const_iterator tip, tail, end;
4834 token.BeginReading(tip);
4835 tail = tip;
4836 token.EndReading(end);
4838 /* Move tip to the '='. */
4839 while ((tip != end) && (*tip != '='))
4840 ++tip;
4842 /* If we didn't find an '=', punt. */
4843 if (tip == end)
4844 return;
4846 /* Extract the key and value. */
4847 const nsAString &key =
4848 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(Substring(tail, tip),
4849 true);
4850 const nsAString &value =
4851 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(Substring(++tip, end),
4852 true);
4854 /* Check for known keys. If we find a match, insert the appropriate
4855 * information into the document header. */
4856 nsCOMPtr<nsIAtom> key_atom = do_GetAtom(key);
4857 if (key_atom == nsGkAtoms::height)
4858 aDocument->SetHeaderData(nsGkAtoms::viewport_height, value);
4859 else if (key_atom == nsGkAtoms::width)
4860 aDocument->SetHeaderData(nsGkAtoms::viewport_width, value);
4861 else if (key_atom == nsGkAtoms::initial_scale)
4862 aDocument->SetHeaderData(nsGkAtoms::viewport_initial_scale, value);
4863 else if (key_atom == nsGkAtoms::minimum_scale)
4864 aDocument->SetHeaderData(nsGkAtoms::viewport_minimum_scale, value);
4865 else if (key_atom == nsGkAtoms::maximum_scale)
4866 aDocument->SetHeaderData(nsGkAtoms::viewport_maximum_scale, value);
4867 else if (key_atom == nsGkAtoms::user_scalable)
4868 aDocument->SetHeaderData(nsGkAtoms::viewport_user_scalable, value);
4869 }
4871 #define IS_SEPARATOR(c) ((c == '=') || (c == ',') || (c == ';') || \
4872 (c == '\t') || (c == '\n') || (c == '\r'))
4874 /* static */
4875 nsViewportInfo
4876 nsContentUtils::GetViewportInfo(nsIDocument *aDocument,
4877 const ScreenIntSize& aDisplaySize)
4878 {
4879 return aDocument->GetViewportInfo(aDisplaySize);
4880 }
4882 /* static */
4883 nsresult
4884 nsContentUtils::ProcessViewportInfo(nsIDocument *aDocument,
4885 const nsAString &viewportInfo) {
4887 /* We never fail. */
4888 nsresult rv = NS_OK;
4890 aDocument->SetHeaderData(nsGkAtoms::viewport, viewportInfo);
4892 /* Iterators. */
4893 nsAString::const_iterator tip, tail, end;
4894 viewportInfo.BeginReading(tip);
4895 tail = tip;
4896 viewportInfo.EndReading(end);
4898 /* Read the tip to the first non-separator character. */
4899 while ((tip != end) && (IS_SEPARATOR(*tip) || nsCRT::IsAsciiSpace(*tip)))
4900 ++tip;
4902 /* Read through and find tokens separated by separators. */
4903 while (tip != end) {
4905 /* Synchronize tip and tail. */
4906 tail = tip;
4908 /* Advance tip past non-separator characters. */
4909 while ((tip != end) && !IS_SEPARATOR(*tip))
4910 ++tip;
4912 /* Allow white spaces that surround the '=' character */
4913 if ((tip != end) && (*tip == '=')) {
4914 ++tip;
4916 while ((tip != end) && nsCRT::IsAsciiSpace(*tip))
4917 ++tip;
4919 while ((tip != end) && !(IS_SEPARATOR(*tip) || nsCRT::IsAsciiSpace(*tip)))
4920 ++tip;
4921 }
4923 /* Our token consists of the characters between tail and tip. */
4924 ProcessViewportToken(aDocument, Substring(tail, tip));
4926 /* Skip separators. */
4927 while ((tip != end) && (IS_SEPARATOR(*tip) || nsCRT::IsAsciiSpace(*tip)))
4928 ++tip;
4929 }
4931 return rv;
4933 }
4935 #undef IS_SEPARATOR
4937 /* static */
4938 void
4939 nsContentUtils::HidePopupsInDocument(nsIDocument* aDocument)
4940 {
4941 #ifdef MOZ_XUL
4942 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
4943 if (pm && aDocument) {
4944 nsCOMPtr<nsIDocShellTreeItem> docShellToHide = aDocument->GetDocShell();
4945 if (docShellToHide)
4946 pm->HidePopupsInDocShell(docShellToHide);
4947 }
4948 #endif
4949 }
4951 /* static */
4952 already_AddRefed<nsIDragSession>
4953 nsContentUtils::GetDragSession()
4954 {
4955 nsCOMPtr<nsIDragSession> dragSession;
4956 nsCOMPtr<nsIDragService> dragService =
4957 do_GetService("@mozilla.org/widget/dragservice;1");
4958 if (dragService)
4959 dragService->GetCurrentSession(getter_AddRefs(dragSession));
4960 return dragSession.forget();
4961 }
4963 /* static */
4964 nsresult
4965 nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent)
4966 {
4967 if (aDragEvent->dataTransfer || !aDragEvent->mFlags.mIsTrusted)
4968 return NS_OK;
4970 // For draggesture and dragstart events, the data transfer object is
4971 // created before the event fires, so it should already be set. For other
4972 // drag events, get the object from the drag session.
4973 NS_ASSERTION(aDragEvent->message != NS_DRAGDROP_GESTURE &&
4974 aDragEvent->message != NS_DRAGDROP_START,
4975 "draggesture event created without a dataTransfer");
4977 nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
4978 NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
4980 nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
4981 nsCOMPtr<DataTransfer> initialDataTransfer;
4982 dragSession->GetDataTransfer(getter_AddRefs(dataTransfer));
4983 if (dataTransfer) {
4984 initialDataTransfer = do_QueryInterface(dataTransfer);
4985 if (!initialDataTransfer) {
4986 return NS_ERROR_FAILURE;
4987 }
4988 } else {
4989 // A dataTransfer won't exist when a drag was started by some other
4990 // means, for instance calling the drag service directly, or a drag
4991 // from another application. In either case, a new dataTransfer should
4992 // be created that reflects the data.
4993 initialDataTransfer = new DataTransfer(aDragEvent->target, aDragEvent->message, true, -1);
4995 // now set it in the drag session so we don't need to create it again
4996 dragSession->SetDataTransfer(initialDataTransfer);
4997 }
4999 bool isCrossDomainSubFrameDrop = false;
5000 if (aDragEvent->message == NS_DRAGDROP_DROP ||
5001 aDragEvent->message == NS_DRAGDROP_DRAGDROP) {
5002 isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
5003 }
5005 // each event should use a clone of the original dataTransfer.
5006 initialDataTransfer->Clone(aDragEvent->target, aDragEvent->message, aDragEvent->userCancelled,
5007 isCrossDomainSubFrameDrop,
5008 getter_AddRefs(aDragEvent->dataTransfer));
5009 NS_ENSURE_TRUE(aDragEvent->dataTransfer, NS_ERROR_OUT_OF_MEMORY);
5011 // for the dragenter and dragover events, initialize the drop effect
5012 // from the drop action, which platform specific widget code sets before
5013 // the event is fired based on the keyboard state.
5014 if (aDragEvent->message == NS_DRAGDROP_ENTER ||
5015 aDragEvent->message == NS_DRAGDROP_OVER) {
5016 uint32_t action, effectAllowed;
5017 dragSession->GetDragAction(&action);
5018 aDragEvent->dataTransfer->GetEffectAllowedInt(&effectAllowed);
5019 aDragEvent->dataTransfer->SetDropEffectInt(FilterDropEffect(action, effectAllowed));
5020 }
5021 else if (aDragEvent->message == NS_DRAGDROP_DROP ||
5022 aDragEvent->message == NS_DRAGDROP_DRAGDROP ||
5023 aDragEvent->message == NS_DRAGDROP_END) {
5024 // For the drop and dragend events, set the drop effect based on the
5025 // last value that the dropEffect had. This will have been set in
5026 // EventStateManager::PostHandleEvent for the last dragenter or
5027 // dragover event.
5028 uint32_t dropEffect;
5029 initialDataTransfer->GetDropEffectInt(&dropEffect);
5030 aDragEvent->dataTransfer->SetDropEffectInt(dropEffect);
5031 }
5033 return NS_OK;
5034 }
5036 /* static */
5037 uint32_t
5038 nsContentUtils::FilterDropEffect(uint32_t aAction, uint32_t aEffectAllowed)
5039 {
5040 // It is possible for the drag action to include more than one action, but
5041 // the widget code which sets the action from the keyboard state should only
5042 // be including one. If multiple actions were set, we just consider them in
5043 // the following order:
5044 // copy, link, move
5045 if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
5046 aAction = nsIDragService::DRAGDROP_ACTION_COPY;
5047 else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
5048 aAction = nsIDragService::DRAGDROP_ACTION_LINK;
5049 else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
5050 aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
5052 // Filter the action based on the effectAllowed. If the effectAllowed
5053 // doesn't include the action, then that action cannot be done, so adjust
5054 // the action to something that is allowed. For a copy, adjust to move or
5055 // link. For a move, adjust to copy or link. For a link, adjust to move or
5056 // link. Otherwise, use none.
5057 if (aAction & aEffectAllowed ||
5058 aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
5059 return aAction;
5060 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
5061 return nsIDragService::DRAGDROP_ACTION_MOVE;
5062 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
5063 return nsIDragService::DRAGDROP_ACTION_COPY;
5064 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
5065 return nsIDragService::DRAGDROP_ACTION_LINK;
5066 return nsIDragService::DRAGDROP_ACTION_NONE;
5067 }
5069 /* static */
5070 bool
5071 nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession,
5072 WidgetDragEvent* aDropEvent)
5073 {
5074 nsCOMPtr<nsIContent> target = do_QueryInterface(aDropEvent->originalTarget);
5075 if (!target) {
5076 return true;
5077 }
5079 nsIDocument* targetDoc = target->OwnerDoc();
5080 nsCOMPtr<nsIWebNavigation> twebnav = do_GetInterface(targetDoc->GetWindow());
5081 nsCOMPtr<nsIDocShellTreeItem> tdsti = do_QueryInterface(twebnav);
5082 if (!tdsti) {
5083 return true;
5084 }
5086 // Always allow dropping onto chrome shells.
5087 if (tdsti->ItemType() == nsIDocShellTreeItem::typeChrome) {
5088 return false;
5089 }
5091 // If there is no source node, then this is a drag from another
5092 // application, which should be allowed.
5093 nsCOMPtr<nsIDOMDocument> sourceDocument;
5094 aDragSession->GetSourceDocument(getter_AddRefs(sourceDocument));
5095 nsCOMPtr<nsIDocument> doc = do_QueryInterface(sourceDocument);
5096 if (doc) {
5097 // Get each successive parent of the source document and compare it to
5098 // the drop document. If they match, then this is a drag from a child frame.
5099 do {
5100 doc = doc->GetParentDocument();
5101 if (doc == targetDoc) {
5102 // The drag is from a child frame.
5103 return true;
5104 }
5105 } while (doc);
5106 }
5108 return false;
5109 }
5111 /* static */
5112 bool
5113 nsContentUtils::URIIsLocalFile(nsIURI *aURI)
5114 {
5115 bool isFile;
5116 nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
5118 // Important: we do NOT test the entire URI chain here!
5119 return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
5120 nsIProtocolHandler::URI_IS_LOCAL_FILE,
5121 &isFile)) &&
5122 isFile;
5123 }
5125 nsresult
5126 nsContentUtils::SplitURIAtHash(nsIURI *aURI,
5127 nsACString &aBeforeHash,
5128 nsACString &aAfterHash)
5129 {
5130 // See bug 225910 for why we can't do this using nsIURL.
5132 aBeforeHash.Truncate();
5133 aAfterHash.Truncate();
5135 NS_ENSURE_ARG_POINTER(aURI);
5137 nsAutoCString spec;
5138 nsresult rv = aURI->GetSpec(spec);
5139 NS_ENSURE_SUCCESS(rv, rv);
5141 int32_t index = spec.FindChar('#');
5142 if (index == -1) {
5143 index = spec.Length();
5144 }
5146 aBeforeHash.Assign(Substring(spec, 0, index));
5147 aAfterHash.Assign(Substring(spec, index));
5148 return NS_OK;
5149 }
5151 /* static */
5152 nsIScriptContext*
5153 nsContentUtils::GetContextForEventHandlers(nsINode* aNode,
5154 nsresult* aRv)
5155 {
5156 *aRv = NS_OK;
5157 bool hasHadScriptObject = true;
5158 nsIScriptGlobalObject* sgo =
5159 aNode->OwnerDoc()->GetScriptHandlingObject(hasHadScriptObject);
5160 // It is bad if the document doesn't have event handling context,
5161 // but it used to have one.
5162 if (!sgo && hasHadScriptObject) {
5163 *aRv = NS_ERROR_UNEXPECTED;
5164 return nullptr;
5165 }
5167 if (sgo) {
5168 nsIScriptContext* scx = sgo->GetContext();
5169 // Bad, no context from script global object!
5170 if (!scx) {
5171 *aRv = NS_ERROR_UNEXPECTED;
5172 return nullptr;
5173 }
5174 return scx;
5175 }
5177 return nullptr;
5178 }
5180 /* static */
5181 JSContext *
5182 nsContentUtils::GetCurrentJSContext()
5183 {
5184 MOZ_ASSERT(NS_IsMainThread());
5185 return sXPConnect->GetCurrentJSContext();
5186 }
5188 /* static */
5189 JSContext *
5190 nsContentUtils::GetSafeJSContext()
5191 {
5192 MOZ_ASSERT(NS_IsMainThread());
5193 return sXPConnect->GetSafeJSContext();
5194 }
5196 /* static */
5197 JSContext *
5198 nsContentUtils::GetDefaultJSContextForThread()
5199 {
5200 if (MOZ_LIKELY(NS_IsMainThread())) {
5201 return GetSafeJSContext();
5202 } else {
5203 return workers::GetCurrentThreadJSContext();
5204 }
5205 }
5207 /* static */
5208 JSContext *
5209 nsContentUtils::GetCurrentJSContextForThread()
5210 {
5211 if (MOZ_LIKELY(NS_IsMainThread())) {
5212 return GetCurrentJSContext();
5213 } else {
5214 return workers::GetCurrentThreadJSContext();
5215 }
5216 }
5218 /* static */
5219 nsresult
5220 nsContentUtils::ASCIIToLower(nsAString& aStr)
5221 {
5222 char16_t* iter = aStr.BeginWriting();
5223 char16_t* end = aStr.EndWriting();
5224 if (MOZ_UNLIKELY(!iter || !end)) {
5225 return NS_ERROR_OUT_OF_MEMORY;
5226 }
5227 while (iter != end) {
5228 char16_t c = *iter;
5229 if (c >= 'A' && c <= 'Z') {
5230 *iter = c + ('a' - 'A');
5231 }
5232 ++iter;
5233 }
5234 return NS_OK;
5235 }
5237 /* static */
5238 nsresult
5239 nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest)
5240 {
5241 uint32_t len = aSource.Length();
5242 aDest.SetLength(len);
5243 if (aDest.Length() == len) {
5244 char16_t* dest = aDest.BeginWriting();
5245 if (MOZ_UNLIKELY(!dest)) {
5246 return NS_ERROR_OUT_OF_MEMORY;
5247 }
5248 const char16_t* iter = aSource.BeginReading();
5249 const char16_t* end = aSource.EndReading();
5250 while (iter != end) {
5251 char16_t c = *iter;
5252 *dest = (c >= 'A' && c <= 'Z') ?
5253 c + ('a' - 'A') : c;
5254 ++iter;
5255 ++dest;
5256 }
5257 return NS_OK;
5258 }
5259 return NS_ERROR_OUT_OF_MEMORY;
5260 }
5262 /* static */
5263 nsresult
5264 nsContentUtils::ASCIIToUpper(nsAString& aStr)
5265 {
5266 char16_t* iter = aStr.BeginWriting();
5267 char16_t* end = aStr.EndWriting();
5268 if (MOZ_UNLIKELY(!iter || !end)) {
5269 return NS_ERROR_OUT_OF_MEMORY;
5270 }
5271 while (iter != end) {
5272 char16_t c = *iter;
5273 if (c >= 'a' && c <= 'z') {
5274 *iter = c + ('A' - 'a');
5275 }
5276 ++iter;
5277 }
5278 return NS_OK;
5279 }
5281 /* static */
5282 nsresult
5283 nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest)
5284 {
5285 uint32_t len = aSource.Length();
5286 aDest.SetLength(len);
5287 if (aDest.Length() == len) {
5288 char16_t* dest = aDest.BeginWriting();
5289 if (MOZ_UNLIKELY(!dest)) {
5290 return NS_ERROR_OUT_OF_MEMORY;
5291 }
5292 const char16_t* iter = aSource.BeginReading();
5293 const char16_t* end = aSource.EndReading();
5294 while (iter != end) {
5295 char16_t c = *iter;
5296 *dest = (c >= 'a' && c <= 'z') ?
5297 c + ('A' - 'a') : c;
5298 ++iter;
5299 ++dest;
5300 }
5301 return NS_OK;
5302 }
5303 return NS_ERROR_OUT_OF_MEMORY;
5304 }
5306 /* static */
5307 bool
5308 nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
5309 const nsAString& aStr2)
5310 {
5311 uint32_t len = aStr1.Length();
5312 if (len != aStr2.Length()) {
5313 return false;
5314 }
5316 const char16_t* str1 = aStr1.BeginReading();
5317 const char16_t* str2 = aStr2.BeginReading();
5318 const char16_t* end = str1 + len;
5320 while (str1 < end) {
5321 char16_t c1 = *str1++;
5322 char16_t c2 = *str2++;
5324 // First check if any bits other than the 0x0020 differs
5325 if ((c1 ^ c2) & 0xffdf) {
5326 return false;
5327 }
5329 // We know they can only differ in the 0x0020 bit.
5330 // Likely the two chars are the same, so check that first
5331 if (c1 != c2) {
5332 // They do differ, but since it's only in the 0x0020 bit, check if it's
5333 // the same ascii char, but just differing in case
5334 char16_t c1Upper = c1 & 0xffdf;
5335 if (!('A' <= c1Upper && c1Upper <= 'Z')) {
5336 return false;
5337 }
5338 }
5339 }
5341 return true;
5342 }
5344 /* static */
5345 bool
5346 nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr)
5347 {
5348 const char16_t* iter = aStr.BeginReading();
5349 const char16_t* end = aStr.EndReading();
5350 while (iter != end) {
5351 char16_t c = *iter;
5352 if (c >= 'A' && c <= 'Z') {
5353 return true;
5354 }
5355 ++iter;
5356 }
5358 return false;
5359 }
5361 /* static */
5362 nsIInterfaceRequestor*
5363 nsContentUtils::GetSameOriginChecker()
5364 {
5365 if (!sSameOriginChecker) {
5366 sSameOriginChecker = new SameOriginChecker();
5367 NS_IF_ADDREF(sSameOriginChecker);
5368 }
5369 return sSameOriginChecker;
5370 }
5372 /* static */
5373 nsresult
5374 nsContentUtils::CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel)
5375 {
5376 if (!nsContentUtils::GetSecurityManager())
5377 return NS_ERROR_NOT_AVAILABLE;
5379 nsCOMPtr<nsIPrincipal> oldPrincipal;
5380 nsContentUtils::GetSecurityManager()->
5381 GetChannelPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
5383 nsCOMPtr<nsIURI> newURI;
5384 aNewChannel->GetURI(getter_AddRefs(newURI));
5385 nsCOMPtr<nsIURI> newOriginalURI;
5386 aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
5388 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
5390 nsresult rv = oldPrincipal->CheckMayLoad(newURI, false, false);
5391 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
5392 rv = oldPrincipal->CheckMayLoad(newOriginalURI, false, false);
5393 }
5395 return rv;
5396 }
5398 NS_IMPL_ISUPPORTS(SameOriginChecker,
5399 nsIChannelEventSink,
5400 nsIInterfaceRequestor)
5402 NS_IMETHODIMP
5403 SameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
5404 nsIChannel *aNewChannel,
5405 uint32_t aFlags,
5406 nsIAsyncVerifyRedirectCallback *cb)
5407 {
5408 NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
5410 nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
5411 if (NS_SUCCEEDED(rv)) {
5412 cb->OnRedirectVerifyCallback(NS_OK);
5413 }
5415 return rv;
5416 }
5418 NS_IMETHODIMP
5419 SameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
5420 {
5421 return QueryInterface(aIID, aResult);
5422 }
5424 /* static */
5425 nsresult
5426 nsContentUtils::GetASCIIOrigin(nsIPrincipal* aPrincipal, nsCString& aOrigin)
5427 {
5428 NS_PRECONDITION(aPrincipal, "missing principal");
5430 aOrigin.Truncate();
5432 nsCOMPtr<nsIURI> uri;
5433 nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
5434 NS_ENSURE_SUCCESS(rv, rv);
5436 if (uri) {
5437 return GetASCIIOrigin(uri, aOrigin);
5438 }
5440 aOrigin.AssignLiteral("null");
5442 return NS_OK;
5443 }
5445 /* static */
5446 nsresult
5447 nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsCString& aOrigin)
5448 {
5449 NS_PRECONDITION(aURI, "missing uri");
5451 aOrigin.Truncate();
5453 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
5454 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
5456 nsCString host;
5457 nsresult rv = uri->GetAsciiHost(host);
5459 if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
5460 nsCString scheme;
5461 rv = uri->GetScheme(scheme);
5462 NS_ENSURE_SUCCESS(rv, rv);
5464 int32_t port = -1;
5465 uri->GetPort(&port);
5466 if (port != -1 && port == NS_GetDefaultPort(scheme.get()))
5467 port = -1;
5469 nsCString hostPort;
5470 rv = NS_GenerateHostPort(host, port, hostPort);
5471 NS_ENSURE_SUCCESS(rv, rv);
5473 aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort;
5474 }
5475 else {
5476 aOrigin.AssignLiteral("null");
5477 }
5479 return NS_OK;
5480 }
5482 /* static */
5483 nsresult
5484 nsContentUtils::GetUTFOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin)
5485 {
5486 NS_PRECONDITION(aPrincipal, "missing principal");
5488 aOrigin.Truncate();
5490 nsCOMPtr<nsIURI> uri;
5491 nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
5492 NS_ENSURE_SUCCESS(rv, rv);
5494 if (uri) {
5495 return GetUTFOrigin(uri, aOrigin);
5496 }
5498 aOrigin.AssignLiteral("null");
5500 return NS_OK;
5501 }
5503 /* static */
5504 nsresult
5505 nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsString& aOrigin)
5506 {
5507 NS_PRECONDITION(aURI, "missing uri");
5509 aOrigin.Truncate();
5511 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
5512 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
5514 nsCString host;
5515 nsresult rv = uri->GetHost(host);
5517 if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
5518 nsCString scheme;
5519 rv = uri->GetScheme(scheme);
5520 NS_ENSURE_SUCCESS(rv, rv);
5522 int32_t port = -1;
5523 uri->GetPort(&port);
5524 if (port != -1 && port == NS_GetDefaultPort(scheme.get()))
5525 port = -1;
5527 nsCString hostPort;
5528 rv = NS_GenerateHostPort(host, port, hostPort);
5529 NS_ENSURE_SUCCESS(rv, rv);
5531 aOrigin = NS_ConvertUTF8toUTF16(
5532 scheme + NS_LITERAL_CSTRING("://") + hostPort);
5533 }
5534 else {
5535 aOrigin.AssignLiteral("null");
5536 }
5538 return NS_OK;
5539 }
5541 /* static */
5542 void
5543 nsContentUtils::GetUTFNonNullOrigin(nsIURI* aURI, nsString& aOrigin)
5544 {
5545 aOrigin.Truncate();
5547 nsString origin;
5548 nsresult rv = GetUTFOrigin(aURI, origin);
5549 if (NS_SUCCEEDED(rv) && !origin.EqualsLiteral("null")) {
5550 aOrigin.Assign(origin);
5551 }
5552 }
5554 /* static */
5555 nsIDocument*
5556 nsContentUtils::GetDocumentFromScriptContext(nsIScriptContext *aScriptContext)
5557 {
5558 if (!aScriptContext) {
5559 return nullptr;
5560 }
5562 nsCOMPtr<nsPIDOMWindow> window =
5563 do_QueryInterface(aScriptContext->GetGlobalObject());
5564 if (!window) {
5565 return nullptr;
5566 }
5568 return window->GetDoc();
5569 }
5571 /* static */
5572 bool
5573 nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal, nsIChannel* aChannel, bool aAllowIfInheritsPrincipal)
5574 {
5575 nsCOMPtr<nsIURI> channelURI;
5576 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
5577 NS_ENSURE_SUCCESS(rv, false);
5579 return NS_SUCCEEDED(aPrincipal->CheckMayLoad(channelURI, false, aAllowIfInheritsPrincipal));
5580 }
5582 nsContentTypeParser::nsContentTypeParser(const nsAString& aString)
5583 : mString(aString), mService(nullptr)
5584 {
5585 CallGetService("@mozilla.org/network/mime-hdrparam;1", &mService);
5586 }
5588 nsContentTypeParser::~nsContentTypeParser()
5589 {
5590 NS_IF_RELEASE(mService);
5591 }
5593 nsresult
5594 nsContentTypeParser::GetParameter(const char* aParameterName, nsAString& aResult)
5595 {
5596 NS_ENSURE_TRUE(mService, NS_ERROR_FAILURE);
5597 return mService->GetParameterHTTP(mString, aParameterName,
5598 EmptyCString(), false, nullptr,
5599 aResult);
5600 }
5602 /* static */
5604 bool
5605 nsContentUtils::CanAccessNativeAnon()
5606 {
5607 return IsCallerChrome() || IsCallerXBL();
5608 }
5610 /* static */ nsresult
5611 nsContentUtils::DispatchXULCommand(nsIContent* aTarget,
5612 bool aTrusted,
5613 nsIDOMEvent* aSourceEvent,
5614 nsIPresShell* aShell,
5615 bool aCtrl,
5616 bool aAlt,
5617 bool aShift,
5618 bool aMeta)
5619 {
5620 NS_ENSURE_STATE(aTarget);
5621 nsIDocument* doc = aTarget->OwnerDoc();
5622 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
5623 NS_ENSURE_STATE(domDoc);
5624 nsCOMPtr<nsIDOMEvent> event;
5625 domDoc->CreateEvent(NS_LITERAL_STRING("xulcommandevent"),
5626 getter_AddRefs(event));
5627 nsCOMPtr<nsIDOMXULCommandEvent> xulCommand = do_QueryInterface(event);
5628 nsresult rv = xulCommand->InitCommandEvent(NS_LITERAL_STRING("command"),
5629 true, true, doc->GetWindow(),
5630 0, aCtrl, aAlt, aShift, aMeta,
5631 aSourceEvent);
5632 NS_ENSURE_SUCCESS(rv, rv);
5634 if (aShell) {
5635 nsEventStatus status = nsEventStatus_eIgnore;
5636 nsCOMPtr<nsIPresShell> kungFuDeathGrip = aShell;
5637 return aShell->HandleDOMEventWithTarget(aTarget, event, &status);
5638 }
5640 nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
5641 NS_ENSURE_STATE(target);
5642 bool dummy;
5643 return target->DispatchEvent(event, &dummy);
5644 }
5646 // static
5647 nsresult
5648 nsContentUtils::WrapNative(JSContext *cx, nsISupports *native,
5649 nsWrapperCache *cache, const nsIID* aIID,
5650 JS::MutableHandle<JS::Value> vp, bool aAllowWrapping)
5651 {
5652 if (!native) {
5653 vp.setNull();
5655 return NS_OK;
5656 }
5658 JSObject *wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
5659 if (wrapper) {
5660 return NS_OK;
5661 }
5663 NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
5665 if (!NS_IsMainThread()) {
5666 MOZ_CRASH();
5667 }
5669 nsresult rv = NS_OK;
5670 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
5671 AutoPushJSContext context(cx);
5672 rv = sXPConnect->WrapNativeToJSVal(context, scope, native, cache, aIID,
5673 aAllowWrapping, vp);
5674 return rv;
5675 }
5677 nsresult
5678 nsContentUtils::CreateArrayBuffer(JSContext *aCx, const nsACString& aData,
5679 JSObject** aResult)
5680 {
5681 if (!aCx) {
5682 return NS_ERROR_FAILURE;
5683 }
5685 int32_t dataLen = aData.Length();
5686 *aResult = JS_NewArrayBuffer(aCx, dataLen);
5687 if (!*aResult) {
5688 return NS_ERROR_FAILURE;
5689 }
5691 if (dataLen > 0) {
5692 NS_ASSERTION(JS_IsArrayBufferObject(*aResult), "What happened?");
5693 memcpy(JS_GetArrayBufferData(*aResult), aData.BeginReading(), dataLen);
5694 }
5696 return NS_OK;
5697 }
5699 // Initial implementation: only stores to RAM, not file
5700 // TODO: bug 704447: large file support
5701 nsresult
5702 nsContentUtils::CreateBlobBuffer(JSContext* aCx,
5703 const nsACString& aData,
5704 JS::MutableHandle<JS::Value> aBlob)
5705 {
5706 uint32_t blobLen = aData.Length();
5707 void* blobData = moz_malloc(blobLen);
5708 nsCOMPtr<nsIDOMBlob> blob;
5709 if (blobData) {
5710 memcpy(blobData, aData.BeginReading(), blobLen);
5711 blob = new nsDOMMemoryFile(blobData, blobLen, EmptyString());
5712 } else {
5713 return NS_ERROR_OUT_OF_MEMORY;
5714 }
5715 return nsContentUtils::WrapNative(aCx, blob, aBlob);
5716 }
5718 void
5719 nsContentUtils::StripNullChars(const nsAString& aInStr, nsAString& aOutStr)
5720 {
5721 // In common cases where we don't have nulls in the
5722 // string we can simple simply bypass the checking code.
5723 int32_t firstNullPos = aInStr.FindChar('\0');
5724 if (firstNullPos == kNotFound) {
5725 aOutStr.Assign(aInStr);
5726 return;
5727 }
5729 aOutStr.SetCapacity(aInStr.Length() - 1);
5730 nsAString::const_iterator start, end;
5731 aInStr.BeginReading(start);
5732 aInStr.EndReading(end);
5733 while (start != end) {
5734 if (*start != '\0')
5735 aOutStr.Append(*start);
5736 ++start;
5737 }
5738 }
5740 struct ClassMatchingInfo {
5741 nsAttrValue::AtomArray mClasses;
5742 nsCaseTreatment mCaseTreatment;
5743 };
5745 // static
5746 bool
5747 nsContentUtils::MatchClassNames(nsIContent* aContent, int32_t aNamespaceID,
5748 nsIAtom* aAtom, void* aData)
5749 {
5750 // We can't match if there are no class names
5751 const nsAttrValue* classAttr = aContent->GetClasses();
5752 if (!classAttr) {
5753 return false;
5754 }
5756 // need to match *all* of the classes
5757 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
5758 uint32_t length = info->mClasses.Length();
5759 if (!length) {
5760 // If we actually had no classes, don't match.
5761 return false;
5762 }
5763 uint32_t i;
5764 for (i = 0; i < length; ++i) {
5765 if (!classAttr->Contains(info->mClasses[i],
5766 info->mCaseTreatment)) {
5767 return false;
5768 }
5769 }
5771 return true;
5772 }
5774 // static
5775 void
5776 nsContentUtils::DestroyClassNameArray(void* aData)
5777 {
5778 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
5779 delete info;
5780 }
5782 // static
5783 void*
5784 nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
5785 const nsString* aClasses)
5786 {
5787 nsAttrValue attrValue;
5788 attrValue.ParseAtomArray(*aClasses);
5789 // nsAttrValue::Equals is sensitive to order, so we'll send an array
5790 ClassMatchingInfo* info = new ClassMatchingInfo;
5791 if (attrValue.Type() == nsAttrValue::eAtomArray) {
5792 info->mClasses.SwapElements(*(attrValue.GetAtomArrayValue()));
5793 } else if (attrValue.Type() == nsAttrValue::eAtom) {
5794 info->mClasses.AppendElement(attrValue.GetAtomValue());
5795 }
5797 info->mCaseTreatment =
5798 aRootNode->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks ?
5799 eIgnoreCase : eCaseMatters;
5800 return info;
5801 }
5803 // static
5804 void
5805 nsContentUtils::DeferredFinalize(nsISupports* aSupports)
5806 {
5807 cyclecollector::DeferredFinalize(aSupports);
5808 }
5810 // static
5811 void
5812 nsContentUtils::DeferredFinalize(mozilla::DeferredFinalizeAppendFunction aAppendFunc,
5813 mozilla::DeferredFinalizeFunction aFunc,
5814 void* aThing)
5815 {
5816 cyclecollector::DeferredFinalize(aAppendFunc, aFunc, aThing);
5817 }
5819 // static
5820 bool
5821 nsContentUtils::IsFocusedContent(const nsIContent* aContent)
5822 {
5823 nsFocusManager* fm = nsFocusManager::GetFocusManager();
5825 return fm && fm->GetFocusedContent() == aContent;
5826 }
5828 bool
5829 nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent)
5830 {
5831 nsIDocument* doc = aContent->GetCurrentDoc();
5832 if (!doc) {
5833 return false;
5834 }
5836 // If the subdocument lives in another process, the frame is
5837 // tabbable.
5838 if (EventStateManager::IsRemoteTarget(aContent)) {
5839 return true;
5840 }
5842 // XXXbz should this use OwnerDoc() for GetSubDocumentFor?
5843 // sXBL/XBL2 issue!
5844 nsIDocument* subDoc = doc->GetSubDocumentFor(aContent);
5845 if (!subDoc) {
5846 return false;
5847 }
5849 nsCOMPtr<nsIDocShell> docShell = subDoc->GetDocShell();
5850 if (!docShell) {
5851 return false;
5852 }
5854 nsCOMPtr<nsIContentViewer> contentViewer;
5855 docShell->GetContentViewer(getter_AddRefs(contentViewer));
5856 if (!contentViewer) {
5857 return false;
5858 }
5860 nsCOMPtr<nsIContentViewer> zombieViewer;
5861 contentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
5863 // If there are 2 viewers for the current docshell, that
5864 // means the current document is a zombie document.
5865 // Only navigate into the subdocument if it's not a zombie.
5866 return !zombieViewer;
5867 }
5869 bool
5870 nsContentUtils::IsUserFocusIgnored(nsINode* aNode)
5871 {
5872 if (!nsGenericHTMLFrameElement::BrowserFramesEnabled()) {
5873 return false;
5874 }
5876 // Check if our mozbrowser iframe ancestors has ignoreuserfocus attribute.
5877 while (aNode) {
5878 nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aNode);
5879 if (browserFrame &&
5880 aNode->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::ignoreuserfocus) &&
5881 browserFrame->GetReallyIsBrowserOrApp()) {
5882 return true;
5883 }
5884 nsPIDOMWindow* win = aNode->OwnerDoc()->GetWindow();
5885 aNode = win ? win->GetFrameElementInternal() : nullptr;
5886 }
5888 return false;
5889 }
5891 bool
5892 nsContentUtils::HasScrollgrab(nsIContent* aContent)
5893 {
5894 nsGenericHTMLElement* element = nsGenericHTMLElement::FromContentOrNull(aContent);
5895 return element && element->Scrollgrab();
5896 }
5898 void
5899 nsContentUtils::FlushLayoutForTree(nsIDOMWindow* aWindow)
5900 {
5901 nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
5902 if (!piWin)
5903 return;
5905 // Note that because FlushPendingNotifications flushes parents, this
5906 // is O(N^2) in docshell tree depth. However, the docshell tree is
5907 // usually pretty shallow.
5909 nsCOMPtr<nsIDOMDocument> domDoc;
5910 aWindow->GetDocument(getter_AddRefs(domDoc));
5911 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
5912 if (doc) {
5913 doc->FlushPendingNotifications(Flush_Layout);
5914 }
5916 nsCOMPtr<nsIDocShell> docShell = piWin->GetDocShell();
5917 if (docShell) {
5918 int32_t i = 0, i_end;
5919 docShell->GetChildCount(&i_end);
5920 for (; i < i_end; ++i) {
5921 nsCOMPtr<nsIDocShellTreeItem> item;
5922 docShell->GetChildAt(i, getter_AddRefs(item));
5923 nsCOMPtr<nsIDOMWindow> win = do_GetInterface(item);
5924 if (win) {
5925 FlushLayoutForTree(win);
5926 }
5927 }
5928 }
5929 }
5931 void nsContentUtils::RemoveNewlines(nsString &aString)
5932 {
5933 // strip CR/LF and null
5934 static const char badChars[] = {'\r', '\n', 0};
5935 aString.StripChars(badChars);
5936 }
5938 void
5939 nsContentUtils::PlatformToDOMLineBreaks(nsString &aString)
5940 {
5941 if (aString.FindChar(char16_t('\r')) != -1) {
5942 // Windows linebreaks: Map CRLF to LF:
5943 aString.ReplaceSubstring(MOZ_UTF16("\r\n"),
5944 MOZ_UTF16("\n"));
5946 // Mac linebreaks: Map any remaining CR to LF:
5947 aString.ReplaceSubstring(MOZ_UTF16("\r"),
5948 MOZ_UTF16("\n"));
5949 }
5950 }
5952 void
5953 nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
5954 nsAString& aResultString)
5955 {
5956 MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
5958 uint32_t stringLen = NS_strlen(static_cast<char16_t*>(aBuf->Data()));
5960 // SANITY CHECK: In case the nsStringBuffer isn't correctly
5961 // null-terminated, let's clamp its length using the allocated size, to be
5962 // sure the resulting string doesn't sample past the end of the the buffer.
5963 // (Note that StorageSize() is in units of bytes, so we have to convert that
5964 // to units of PRUnichars, and subtract 1 for the null-terminator.)
5965 uint32_t allocStringLen = (aBuf->StorageSize() / sizeof(char16_t)) - 1;
5966 MOZ_ASSERT(stringLen <= allocStringLen,
5967 "string buffer lacks null terminator!");
5968 stringLen = std::min(stringLen, allocStringLen);
5970 aBuf->ToString(stringLen, aResultString);
5971 }
5973 nsIPresShell*
5974 nsContentUtils::FindPresShellForDocument(const nsIDocument* aDoc)
5975 {
5976 const nsIDocument* doc = aDoc;
5977 nsIDocument* displayDoc = doc->GetDisplayDocument();
5978 if (displayDoc) {
5979 doc = displayDoc;
5980 }
5982 nsIPresShell* shell = doc->GetShell();
5983 if (shell) {
5984 return shell;
5985 }
5987 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
5988 while (docShellTreeItem) {
5989 // We may be in a display:none subdocument, or we may not have a presshell
5990 // created yet.
5991 // Walk the docshell tree to find the nearest container that has a presshell,
5992 // and return that.
5993 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
5994 nsIPresShell* presShell = docShell->GetPresShell();
5995 if (presShell) {
5996 return presShell;
5997 }
5998 nsCOMPtr<nsIDocShellTreeItem> parent;
5999 docShellTreeItem->GetParent(getter_AddRefs(parent));
6000 docShellTreeItem = parent;
6001 }
6003 return nullptr;
6004 }
6006 nsIWidget*
6007 nsContentUtils::WidgetForDocument(const nsIDocument* aDoc)
6008 {
6009 nsIPresShell* shell = FindPresShellForDocument(aDoc);
6010 if (shell) {
6011 nsViewManager* VM = shell->GetViewManager();
6012 if (VM) {
6013 nsView* rootView = VM->GetRootView();
6014 if (rootView) {
6015 nsView* displayRoot = nsViewManager::GetDisplayRootFor(rootView);
6016 if (displayRoot) {
6017 return displayRoot->GetNearestWidget(nullptr);
6018 }
6019 }
6020 }
6021 }
6023 return nullptr;
6024 }
6026 static already_AddRefed<LayerManager>
6027 LayerManagerForDocumentInternal(const nsIDocument *aDoc, bool aRequirePersistent,
6028 bool* aAllowRetaining)
6029 {
6030 nsIWidget *widget = nsContentUtils::WidgetForDocument(aDoc);
6031 if (widget) {
6032 nsRefPtr<LayerManager> manager =
6033 widget->GetLayerManager(aRequirePersistent ? nsIWidget::LAYER_MANAGER_PERSISTENT :
6034 nsIWidget::LAYER_MANAGER_CURRENT,
6035 aAllowRetaining);
6036 return manager.forget();
6037 }
6039 return nullptr;
6040 }
6042 already_AddRefed<LayerManager>
6043 nsContentUtils::LayerManagerForDocument(const nsIDocument *aDoc, bool *aAllowRetaining)
6044 {
6045 return LayerManagerForDocumentInternal(aDoc, false, aAllowRetaining);
6046 }
6048 already_AddRefed<LayerManager>
6049 nsContentUtils::PersistentLayerManagerForDocument(nsIDocument *aDoc, bool *aAllowRetaining)
6050 {
6051 return LayerManagerForDocumentInternal(aDoc, true, aAllowRetaining);
6052 }
6054 bool
6055 nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal)
6056 {
6057 if (IsSystemPrincipal(aPrincipal)) {
6058 return true;
6059 }
6061 nsCOMPtr<nsIURI> princURI;
6062 aPrincipal->GetURI(getter_AddRefs(princURI));
6064 return princURI &&
6065 ((sAllowXULXBL_for_file && SchemeIs(princURI, "file")) ||
6066 IsSitePermAllow(aPrincipal, "allowXULXBL"));
6067 }
6069 already_AddRefed<nsIDocumentLoaderFactory>
6070 nsContentUtils::FindInternalContentViewer(const char* aType,
6071 ContentViewerType* aLoaderType)
6072 {
6073 if (aLoaderType) {
6074 *aLoaderType = TYPE_UNSUPPORTED;
6075 }
6077 // one helper factory, please
6078 nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
6079 if (!catMan)
6080 return nullptr;
6082 nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
6084 nsXPIDLCString contractID;
6085 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, getter_Copies(contractID));
6086 if (NS_SUCCEEDED(rv)) {
6087 docFactory = do_GetService(contractID);
6088 if (docFactory && aLoaderType) {
6089 if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
6090 *aLoaderType = TYPE_CONTENT;
6091 else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
6092 *aLoaderType = TYPE_PLUGIN;
6093 else
6094 *aLoaderType = TYPE_UNKNOWN;
6095 }
6096 return docFactory.forget();
6097 }
6099 if (DecoderTraits::IsSupportedInVideoDocument(nsDependentCString(aType))) {
6100 docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
6101 if (docFactory && aLoaderType) {
6102 *aLoaderType = TYPE_CONTENT;
6103 }
6104 return docFactory.forget();
6105 }
6107 return nullptr;
6108 }
6110 bool
6111 nsContentUtils::GetContentSecurityPolicy(JSContext* aCx,
6112 nsIContentSecurityPolicy** aCSP)
6113 {
6114 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
6116 // Get the security manager
6117 nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
6119 if (!ssm) {
6120 NS_ERROR("Failed to get security manager service");
6121 return false;
6122 }
6124 nsCOMPtr<nsIPrincipal> subjectPrincipal = ssm->GetCxSubjectPrincipal(aCx);
6125 NS_ASSERTION(subjectPrincipal, "Failed to get subjectPrincipal");
6127 nsCOMPtr<nsIContentSecurityPolicy> csp;
6128 nsresult rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
6129 if (NS_FAILED(rv)) {
6130 NS_ERROR("CSP: Failed to get CSP from principal.");
6131 return false;
6132 }
6134 csp.forget(aCSP);
6135 return true;
6136 }
6138 // static
6139 bool
6140 nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
6141 nsIDocument* aDocument)
6142 {
6143 NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
6144 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aDocument->GetWindow());
6145 NS_ENSURE_TRUE(sgo, true);
6147 AutoPushJSContext cx(sgo->GetContext()->GetNativeContext());
6148 NS_ENSURE_TRUE(cx, true);
6150 // The pattern has to match the entire value.
6151 aPattern.Insert(NS_LITERAL_STRING("^(?:"), 0);
6152 aPattern.Append(NS_LITERAL_STRING(")$"));
6154 JS::Rooted<JSObject*> re(cx,
6155 JS_NewUCRegExpObjectNoStatics(cx,
6156 static_cast<jschar*>(aPattern.BeginWriting()),
6157 aPattern.Length(), 0));
6158 if (!re) {
6159 JS_ClearPendingException(cx);
6160 return true;
6161 }
6163 JS::Rooted<JS::Value> rval(cx, JS::NullValue());
6164 size_t idx = 0;
6165 if (!JS_ExecuteRegExpNoStatics(cx, re,
6166 static_cast<jschar*>(aValue.BeginWriting()),
6167 aValue.Length(), &idx, true, &rval)) {
6168 JS_ClearPendingException(cx);
6169 return true;
6170 }
6172 return !rval.isNull();
6173 }
6175 // static
6176 nsresult
6177 nsContentUtils::URIInheritsSecurityContext(nsIURI *aURI, bool *aResult)
6178 {
6179 // Note: about:blank URIs do NOT inherit the security context from the
6180 // current document, which is what this function tests for...
6181 return NS_URIChainHasFlags(aURI,
6182 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
6183 aResult);
6184 }
6186 // static
6187 bool
6188 nsContentUtils::SetUpChannelOwner(nsIPrincipal* aLoadingPrincipal,
6189 nsIChannel* aChannel,
6190 nsIURI* aURI,
6191 bool aSetUpForAboutBlank,
6192 bool aForceOwner)
6193 {
6194 //
6195 // Set the owner of the channel, but only for channels that can't
6196 // provide their own security context.
6197 //
6198 // XXX: It seems wrong that the owner is ignored - even if one is
6199 // supplied) unless the URI is javascript or data or about:blank.
6200 // XXX: If this is ever changed, check all callers for what owners
6201 // they're passing in. In particular, see the code and
6202 // comments in nsDocShell::LoadURI where we fall back on
6203 // inheriting the owner if called from chrome. That would be
6204 // very wrong if this code changed anything but channels that
6205 // can't provide their own security context!
6206 //
6207 // (Currently chrome URIs set the owner when they are created!
6208 // So setting a nullptr owner would be bad!)
6209 //
6210 // If aForceOwner is true, the owner will be set, even for a channel that
6211 // can provide its own security context. This is used for the HTML5 IFRAME
6212 // sandbox attribute, so we can force the channel (and its document) to
6213 // explicitly have a null principal.
6214 bool inherit;
6215 // We expect URIInheritsSecurityContext to return success for an
6216 // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
6217 // This condition needs to match the one in nsDocShell::InternalLoad where
6218 // we're checking for things that will use the owner.
6219 if (aForceOwner || ((NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &inherit)) &&
6220 (inherit || (aSetUpForAboutBlank && NS_IsAboutBlank(aURI)))))) {
6221 #ifdef DEBUG
6222 // Assert that aForceOwner is only set for null principals for non-srcdoc
6223 // loads. (Strictly speaking not all uses of about:srcdoc would be
6224 // srcdoc loads, but the URI is non-resolvable in cases where it is not).
6225 if (aForceOwner) {
6226 nsAutoCString uriStr;
6227 aURI->GetSpec(uriStr);
6228 if(!uriStr.EqualsLiteral("about:srcdoc") &&
6229 !uriStr.EqualsLiteral("view-source:about:srcdoc")) {
6230 nsCOMPtr<nsIURI> ownerURI;
6231 nsresult rv = aLoadingPrincipal->GetURI(getter_AddRefs(ownerURI));
6232 MOZ_ASSERT(NS_SUCCEEDED(rv) && SchemeIs(ownerURI, NS_NULLPRINCIPAL_SCHEME));
6233 }
6234 }
6235 #endif
6236 aChannel->SetOwner(aLoadingPrincipal);
6237 return true;
6238 }
6240 //
6241 // file: uri special-casing
6242 //
6243 // If this is a file: load opened from another file: then it may need
6244 // to inherit the owner from the referrer so they can script each other.
6245 // If we don't set the owner explicitly then each file: gets an owner
6246 // based on its own codebase later.
6247 //
6248 if (URIIsLocalFile(aURI) && aLoadingPrincipal &&
6249 NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, false, false)) &&
6250 // One more check here. CheckMayLoad will always return true for the
6251 // system principal, but we do NOT want to inherit in that case.
6252 !IsSystemPrincipal(aLoadingPrincipal)) {
6253 aChannel->SetOwner(aLoadingPrincipal);
6254 return true;
6255 }
6257 return false;
6258 }
6260 /* static */
6261 bool
6262 nsContentUtils::IsFullScreenApiEnabled()
6263 {
6264 return sIsFullScreenApiEnabled;
6265 }
6267 /* static */
6268 bool
6269 nsContentUtils::IsRequestFullScreenAllowed()
6270 {
6271 return !sTrustedFullScreenOnly ||
6272 EventStateManager::IsHandlingUserInput() ||
6273 IsCallerChrome();
6274 }
6276 /* static */
6277 bool
6278 nsContentUtils::IsFullscreenApiContentOnly()
6279 {
6280 return sFullscreenApiIsContentOnly;
6281 }
6283 /* static */
6284 bool
6285 nsContentUtils::HaveEqualPrincipals(nsIDocument* aDoc1, nsIDocument* aDoc2)
6286 {
6287 if (!aDoc1 || !aDoc2) {
6288 return false;
6289 }
6290 bool principalsEqual = false;
6291 aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
6292 return principalsEqual;
6293 }
6295 static void
6296 CheckForWindowedPlugins(nsIContent* aContent, void* aResult)
6297 {
6298 if (!aContent->IsInDoc()) {
6299 return;
6300 }
6301 nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aContent));
6302 if (!olc) {
6303 return;
6304 }
6305 nsRefPtr<nsNPAPIPluginInstance> plugin;
6306 olc->GetPluginInstance(getter_AddRefs(plugin));
6307 if (!plugin) {
6308 return;
6309 }
6310 bool isWindowless = false;
6311 nsresult res = plugin->IsWindowless(&isWindowless);
6312 if (NS_SUCCEEDED(res) && !isWindowless) {
6313 *static_cast<bool*>(aResult) = true;
6314 }
6315 }
6317 static bool
6318 DocTreeContainsWindowedPlugins(nsIDocument* aDoc, void* aResult)
6319 {
6320 if (!nsContentUtils::IsChromeDoc(aDoc)) {
6321 aDoc->EnumerateFreezableElements(CheckForWindowedPlugins, aResult);
6322 }
6323 if (*static_cast<bool*>(aResult)) {
6324 // Return false to stop iteration, we found a windowed plugin.
6325 return false;
6326 }
6327 aDoc->EnumerateSubDocuments(DocTreeContainsWindowedPlugins, aResult);
6328 // Return false to stop iteration if we found a windowed plugin in
6329 // the sub documents.
6330 return !*static_cast<bool*>(aResult);
6331 }
6333 /* static */
6334 bool
6335 nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc)
6336 {
6337 #ifdef XP_MACOSX
6338 // We control dispatch to all mac plugins.
6339 return false;
6340 #endif
6341 bool result = false;
6343 // Find the top of the document's branch, the child of the chrome document.
6344 nsIDocument* doc = aDoc;
6345 nsIDocument* parent = nullptr;
6346 while (doc && (parent = doc->GetParentDocument()) && !IsChromeDoc(parent)) {
6347 doc = parent;
6348 }
6350 DocTreeContainsWindowedPlugins(doc, &result);
6351 return result;
6352 }
6354 /* static */
6355 bool
6356 nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIContent* aContent)
6357 {
6358 #ifdef XP_MACOSX
6359 // We control dispatch to all mac plugins.
6360 return false;
6361 #endif
6362 bool result = false;
6363 CheckForWindowedPlugins(aContent, &result);
6364 return result;
6365 }
6367 /* static */
6368 void
6369 nsContentUtils::FireMutationEventsForDirectParsing(nsIDocument* aDoc,
6370 nsIContent* aDest,
6371 int32_t aOldChildCount)
6372 {
6373 // Fire mutation events. Optimize for the case when there are no listeners
6374 int32_t newChildCount = aDest->GetChildCount();
6375 if (newChildCount && nsContentUtils::
6376 HasMutationListeners(aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
6377 nsAutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
6378 NS_ASSERTION(newChildCount - aOldChildCount >= 0,
6379 "What, some unexpected dom mutation has happened?");
6380 childNodes.SetCapacity(newChildCount - aOldChildCount);
6381 for (nsIContent* child = aDest->GetFirstChild();
6382 child;
6383 child = child->GetNextSibling()) {
6384 childNodes.AppendElement(child);
6385 }
6386 FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
6387 }
6388 }
6390 /* static */
6391 nsIDocument*
6392 nsContentUtils::GetFullscreenAncestor(nsIDocument* aDoc)
6393 {
6394 nsIDocument* doc = aDoc;
6395 while (doc) {
6396 if (doc->IsFullScreenDoc()) {
6397 return doc;
6398 }
6399 doc = doc->GetParentDocument();
6400 }
6401 return nullptr;
6402 }
6404 /* static */
6405 bool
6406 nsContentUtils::IsInPointerLockContext(nsIDOMWindow* aWin)
6407 {
6408 if (!aWin) {
6409 return false;
6410 }
6412 nsCOMPtr<nsIDocument> pointerLockedDoc =
6413 do_QueryReferent(EventStateManager::sPointerLockedDoc);
6414 if (!pointerLockedDoc || !pointerLockedDoc->GetWindow()) {
6415 return false;
6416 }
6418 nsCOMPtr<nsIDOMWindow> lockTop;
6419 pointerLockedDoc->GetWindow()->GetScriptableTop(getter_AddRefs(lockTop));
6421 nsCOMPtr<nsIDOMWindow> top;
6422 aWin->GetScriptableTop(getter_AddRefs(top));
6424 return top == lockTop;
6425 }
6427 // static
6428 int32_t
6429 nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
6430 int32_t aOffset)
6431 {
6432 // The structure of the anonymous frames within a text control frame is
6433 // an optional block frame, followed by an optional br frame.
6435 // If the offset frame has a child, then this frame is the block which
6436 // has the text frames (containing the content) as its children. This will
6437 // be the case if we click to the right of any of the text frames, or at the
6438 // bottom of the text area.
6439 nsIFrame* firstChild = aOffsetFrame->GetFirstPrincipalChild();
6440 if (firstChild) {
6441 // In this case, the passed-in offset is incorrect, and we want the length
6442 // of the entire content in the text control frame.
6443 return firstChild->GetContent()->Length();
6444 }
6446 if (aOffsetFrame->GetPrevSibling() &&
6447 !aOffsetFrame->GetNextSibling()) {
6448 // In this case, we're actually within the last frame, which is a br
6449 // frame. Our offset should therefore be the length of the first child of
6450 // our parent.
6451 int32_t aOutOffset =
6452 aOffsetFrame->GetParent()->GetFirstPrincipalChild()->GetContent()->Length();
6453 return aOutOffset;
6454 }
6456 // Otherwise, we're within one of the text frames, in which case our offset
6457 // has already been correctly calculated.
6458 return aOffset;
6459 }
6461 // static
6462 void
6463 nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
6464 Element* aRoot,
6465 int32_t& aOutStartOffset,
6466 int32_t& aOutEndOffset)
6467 {
6468 MOZ_ASSERT(aSelection && aRoot);
6470 if (!aSelection->GetRangeCount()) {
6471 // Nothing selected
6472 aOutStartOffset = aOutEndOffset = 0;
6473 return;
6474 }
6476 nsCOMPtr<nsINode> anchorNode = aSelection->GetAnchorNode();
6477 uint32_t anchorOffset = aSelection->AnchorOffset();
6478 nsCOMPtr<nsINode> focusNode = aSelection->GetFocusNode();
6479 uint32_t focusOffset = aSelection->FocusOffset();
6481 // We have at most two children, consisting of an optional text node followed
6482 // by an optional <br>.
6483 NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
6484 nsCOMPtr<nsIContent> firstChild = aRoot->GetFirstChild();
6485 #ifdef DEBUG
6486 nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
6487 NS_ASSERTION(anchorNode == aRoot || anchorNode == firstChild ||
6488 anchorNode == lastChild, "Unexpected anchorNode");
6489 NS_ASSERTION(focusNode == aRoot || focusNode == firstChild ||
6490 focusNode == lastChild, "Unexpected focusNode");
6491 #endif
6492 if (!firstChild || !firstChild->IsNodeOfType(nsINode::eTEXT)) {
6493 // No text node, so everything is 0
6494 anchorOffset = focusOffset = 0;
6495 } else {
6496 // First child is text. If the anchor/focus is already in the text node,
6497 // or the start of the root node, no change needed. If it's in the root
6498 // node but not the start, or in the trailing <br>, we need to set the
6499 // offset to the end.
6500 if ((anchorNode == aRoot && anchorOffset != 0) ||
6501 (anchorNode != aRoot && anchorNode != firstChild)) {
6502 anchorOffset = firstChild->Length();
6503 }
6504 if ((focusNode == aRoot && focusOffset != 0) ||
6505 (focusNode != aRoot && focusNode != firstChild)) {
6506 focusOffset = firstChild->Length();
6507 }
6508 }
6510 // Make sure aOutStartOffset <= aOutEndOffset.
6511 aOutStartOffset = std::min(anchorOffset, focusOffset);
6512 aOutEndOffset = std::max(anchorOffset, focusOffset);
6513 }
6515 nsIEditor*
6516 nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext)
6517 {
6518 nsCOMPtr<nsIDocShell> docShell(aPresContext->GetDocShell());
6519 bool isEditable;
6520 if (!docShell ||
6521 NS_FAILED(docShell->GetEditable(&isEditable)) || !isEditable)
6522 return nullptr;
6524 nsCOMPtr<nsIEditor> editor;
6525 docShell->GetEditor(getter_AddRefs(editor));
6526 return editor;
6527 }
6529 bool
6530 nsContentUtils::InternalIsSupported(nsISupports* aObject,
6531 const nsAString& aFeature,
6532 const nsAString& aVersion)
6533 {
6534 // If it looks like an SVG feature string, forward to nsSVGFeatures
6535 if (StringBeginsWith(aFeature,
6536 NS_LITERAL_STRING("http://www.w3.org/TR/SVG"),
6537 nsASCIICaseInsensitiveStringComparator()) ||
6538 StringBeginsWith(aFeature, NS_LITERAL_STRING("org.w3c.dom.svg"),
6539 nsASCIICaseInsensitiveStringComparator()) ||
6540 StringBeginsWith(aFeature, NS_LITERAL_STRING("org.w3c.svg"),
6541 nsASCIICaseInsensitiveStringComparator())) {
6542 return (aVersion.IsEmpty() || aVersion.EqualsLiteral("1.0") ||
6543 aVersion.EqualsLiteral("1.1")) &&
6544 nsSVGFeatures::HasFeature(aObject, aFeature);
6545 }
6547 // Otherwise, we claim to support everything
6548 return true;
6549 }
6551 bool
6552 nsContentUtils::IsContentInsertionPoint(const nsIContent* aContent)
6553 {
6554 // Check if the content is a XBL insertion point.
6555 if (aContent->IsActiveChildrenElement()) {
6556 return true;
6557 }
6559 // Check if the content is a web components content insertion point.
6560 if (aContent->IsHTML(nsGkAtoms::content)) {
6561 return static_cast<const HTMLContentElement*>(aContent)->IsInsertionPoint();
6562 }
6564 return false;
6565 }
6567 bool
6568 nsContentUtils::DOMWindowDumpEnabled()
6569 {
6570 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
6571 // In optimized builds we check a pref that controls if we should
6572 // enable output from dump() or not, in debug builds it's always
6573 // enabled.
6574 return nsContentUtils::sDOMWindowDumpEnabled;
6575 #else
6576 return true;
6577 #endif
6578 }
6580 bool
6581 nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep, nsAString& aResult)
6582 {
6583 aResult.Truncate();
6584 return AppendNodeTextContent(aNode, aDeep, aResult, mozilla::fallible_t());
6585 }
6587 void
6588 nsContentUtils::DestroyMatchString(void* aData)
6589 {
6590 if (aData) {
6591 nsString* matchString = static_cast<nsString*>(aData);
6592 delete matchString;
6593 }
6594 }
6596 bool
6597 nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType)
6598 {
6599 // Table ordered from most to least likely JS MIME types.
6600 static const char* jsTypes[] = {
6601 "text/javascript",
6602 "text/ecmascript",
6603 "application/javascript",
6604 "application/ecmascript",
6605 "application/x-javascript",
6606 "application/x-ecmascript",
6607 "text/javascript1.0",
6608 "text/javascript1.1",
6609 "text/javascript1.2",
6610 "text/javascript1.3",
6611 "text/javascript1.4",
6612 "text/javascript1.5",
6613 "text/jscript",
6614 "text/livescript",
6615 "text/x-ecmascript",
6616 "text/x-javascript",
6617 nullptr
6618 };
6620 for (uint32_t i = 0; jsTypes[i]; ++i) {
6621 if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
6622 return true;
6623 }
6624 }
6626 return false;
6627 }