summaryrefslogtreecommitdiff
path: root/dom/base/nsContentUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/nsContentUtils.cpp')
-rw-r--r--dom/base/nsContentUtils.cpp9783
1 files changed, 9783 insertions, 0 deletions
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
new file mode 100644
index 0000000000..29d28f8cea
--- /dev/null
+++ b/dom/base/nsContentUtils.cpp
@@ -0,0 +1,9783 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* A namespace class for static layout utilities. */
+
+#include "nsContentUtils.h"
+
+#include <algorithm>
+#include <math.h>
+
+#include "DecoderTraits.h"
+#include "harfbuzz/hb.h"
+#include "imgICache.h"
+#include "imgIContainer.h"
+#include "imgINotificationObserver.h"
+#include "imgLoader.h"
+#include "imgRequestProxy.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "js/Value.h"
+#include "Layers.h"
+#include "MediaDecoder.h"
+// nsNPAPIPluginInstance must be included before nsIDocument.h, which is included in mozAutoDocUpdate.h.
+#include "nsNPAPIPluginInstance.h"
+#include "gfxDrawable.h"
+#include "gfxPrefs.h"
+#include "ImageOps.h"
+#include "mozAutoDocUpdate.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/AutoTimelineMarker.h"
+#include "mozilla/Base64.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/LoadInfo.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/CustomElementRegistry.h"
+#include "mozilla/dom/DocumentFragment.h"
+#include "mozilla/dom/DOMTypes.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/FileSystemSecurity.h"
+#include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/dom/HTMLTemplateElement.h"
+#include "mozilla/dom/HTMLContentElement.h"
+#include "mozilla/dom/HTMLShadowElement.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/TextDecoder.h"
+#include "mozilla/dom/TouchEvent.h"
+#include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/workers/ServiceWorkerManager.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/EventListenerManager.h"
+#include "mozilla/EventStateManager.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h"
+#include "mozilla/IMEStateManager.h"
+#include "mozilla/InternalMutationEvent.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/Selection.h"
+#include "mozilla/TextEvents.h"
+#include "nsArrayUtils.h"
+#include "nsAString.h"
+#include "nsAttrName.h"
+#include "nsAttrValue.h"
+#include "nsAttrValueInlines.h"
+#include "nsBindingManager.h"
+#include "nsCaret.h"
+#include "nsCCUncollectableMarker.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsCOMPtr.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsContentDLF.h"
+#include "nsContentList.h"
+#include "nsContentPolicyUtils.h"
+#include "nsContentSecurityManager.h"
+#include "nsCPrefetchService.h"
+#include "nsCRT.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsCycleCollector.h"
+#include "nsDataHashtable.h"
+#include "nsDocShellCID.h"
+#include "nsDocument.h"
+#include "nsDOMCID.h"
+#include "mozilla/dom/DataTransfer.h"
+#include "nsDOMJSUtils.h"
+#include "nsDOMMutationObserver.h"
+#include "nsError.h"
+#include "nsFocusManager.h"
+#include "nsGenericHTMLElement.h"
+#include "nsGenericHTMLFrameElement.h"
+#include "nsGkAtoms.h"
+#include "nsHostObjectProtocolHandler.h"
+#include "nsHtml5Module.h"
+#include "nsHtml5StringParser.h"
+#include "nsIAddonPolicyService.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsICategoryManager.h"
+#include "nsIChannelEventSink.h"
+#include "nsICharsetDetectionObserver.h"
+#include "nsIChromeRegistry.h"
+#include "nsIConsoleService.h"
+#include "nsIContent.h"
+#include "nsIContentInlines.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsIContentSink.h"
+#include "nsIContentViewer.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIDocument.h"
+#include "nsIDocumentEncoder.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMDocumentType.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMHTMLElement.h"
+#include "nsIDOMHTMLFormElement.h"
+#include "nsIDOMHTMLInputElement.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMNodeList.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsIDOMXULCommandEvent.h"
+#include "nsIDragService.h"
+#include "nsIEditor.h"
+#include "nsIFormControl.h"
+#include "nsIForm.h"
+#include "nsIFragmentContentSink.h"
+#include "nsContainerFrame.h"
+#include "nsIHTMLDocument.h"
+#include "nsIIdleService.h"
+#include "nsIImageLoadingContent.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIIOService.h"
+#include "nsILineBreaker.h"
+#include "nsILoadContext.h"
+#include "nsILoadGroup.h"
+#include "nsIMemoryReporter.h"
+#include "nsIMIMEHeaderParam.h"
+#include "nsIMIMEService.h"
+#include "nsINode.h"
+#include "mozilla/dom/NodeInfo.h"
+#include "nsIObjectLoadingContent.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIOfflineCacheUpdate.h"
+#include "nsIParser.h"
+#include "nsIParserService.h"
+#include "nsIPermissionManager.h"
+#include "nsIPluginHost.h"
+#include "nsIRequest.h"
+#include "nsIRunnable.h"
+#include "nsIScriptContext.h"
+#include "nsIScriptError.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIScrollable.h"
+#include "nsIStreamConverterService.h"
+#include "nsIStringBundle.h"
+#include "nsIURI.h"
+#include "nsIURIWithPrincipal.h"
+#include "nsIURL.h"
+#include "nsIWebNavigation.h"
+#include "nsIWindowMediator.h"
+#include "nsIWordBreaker.h"
+#include "nsIXPConnect.h"
+#include "nsJSUtils.h"
+#include "nsLWBrkCIID.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsNodeInfoManager.h"
+#include "nsNullPrincipal.h"
+#include "nsParserCIID.h"
+#include "nsParserConstants.h"
+#include "nsPIDOMWindow.h"
+#include "nsPresContext.h"
+#include "nsPrintfCString.h"
+#include "nsReferencedElement.h"
+#include "nsSandboxFlags.h"
+#include "nsScriptSecurityManager.h"
+#include "nsStreamUtils.h"
+#include "nsTextEditorState.h"
+#include "nsTextFragment.h"
+#include "nsTextNode.h"
+#include "nsThreadUtils.h"
+#include "nsUnicharUtilCIID.h"
+#include "nsUnicodeProperties.h"
+#include "nsViewManager.h"
+#include "nsViewportInfo.h"
+#include "nsWidgetsCID.h"
+#include "nsIWindowProvider.h"
+#include "nsWrapperCacheInlines.h"
+#include "nsXULPopupManager.h"
+#include "xpcprivate.h" // nsXPConnect
+#include "HTMLSplitOnSpacesTokenizer.h"
+#include "nsContentTypeParser.h"
+#include "nsICookiePermission.h"
+#include "mozIThirdPartyUtil.h"
+#include "nsICookieService.h"
+#include "mozilla/EnumSet.h"
+#include "mozilla/BloomFilter.h"
+#include "TabChild.h"
+#include "mozilla/dom/DocGroup.h"
+#include "mozilla/dom/TabGroup.h"
+
+#include "nsIBidiKeyboard.h"
+
+#if defined(XP_WIN)
+// Undefine LoadImage to prevent naming conflict with Windows.
+#undef LoadImage
+#endif
+
+extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
+ const char** next, char16_t* result);
+extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end,
+ int ns_aware, const char** colon);
+
+class imgLoader;
+
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::widget;
+using namespace mozilla;
+
+const char kLoadAsData[] = "loadAsData";
+
+nsIXPConnect *nsContentUtils::sXPConnect;
+nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
+nsIPrincipal *nsContentUtils::sSystemPrincipal;
+nsIPrincipal *nsContentUtils::sNullSubjectPrincipal;
+nsIParserService *nsContentUtils::sParserService = nullptr;
+nsNameSpaceManager *nsContentUtils::sNameSpaceManager;
+nsIIOService *nsContentUtils::sIOService;
+nsIUUIDGenerator *nsContentUtils::sUUIDGenerator;
+nsIConsoleService *nsContentUtils::sConsoleService;
+nsDataHashtable<nsISupportsHashKey, EventNameMapping>* nsContentUtils::sAtomEventTable = nullptr;
+nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr;
+nsCOMArray<nsIAtom>* nsContentUtils::sUserDefinedEvents = nullptr;
+nsIStringBundleService *nsContentUtils::sStringBundleService;
+nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT];
+nsIContentPolicy *nsContentUtils::sContentPolicyService;
+bool nsContentUtils::sTriedToGetContentPolicy = false;
+nsILineBreaker *nsContentUtils::sLineBreaker;
+nsIWordBreaker *nsContentUtils::sWordBreaker;
+nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nullptr;
+uint32_t nsContentUtils::sScriptBlockerCount = 0;
+uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
+uint32_t nsContentUtils::sMicroTaskLevel = 0;
+AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners = nullptr;
+uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
+nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
+
+bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
+bool nsContentUtils::sAllowXULXBL_for_file = false;
+
+nsString* nsContentUtils::sShiftText = nullptr;
+nsString* nsContentUtils::sControlText = nullptr;
+nsString* nsContentUtils::sMetaText = nullptr;
+nsString* nsContentUtils::sOSText = nullptr;
+nsString* nsContentUtils::sAltText = nullptr;
+nsString* nsContentUtils::sModifierSeparator = nullptr;
+
+bool nsContentUtils::sInitialized = false;
+bool nsContentUtils::sIsFullScreenApiEnabled = false;
+bool nsContentUtils::sIsUnprefixedFullscreenApiEnabled = false;
+bool nsContentUtils::sTrustedFullScreenOnly = true;
+bool nsContentUtils::sIsCutCopyAllowed = true;
+bool nsContentUtils::sIsFrameTimingPrefEnabled = false;
+bool nsContentUtils::sIsPerformanceTimingEnabled = false;
+bool nsContentUtils::sIsResourceTimingEnabled = false;
+bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
+bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
+bool nsContentUtils::sEncodeDecodeURLHash = false;
+bool nsContentUtils::sGettersDecodeURLHash = false;
+bool nsContentUtils::sPrivacyResistFingerprinting = false;
+bool nsContentUtils::sSendPerformanceTimingNotifications = false;
+bool nsContentUtils::sUseActivityCursor = false;
+
+uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
+
+uint32_t nsContentUtils::sCookiesLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
+uint32_t nsContentUtils::sCookiesBehavior = nsICookieService::BEHAVIOR_ACCEPT;
+
+nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
+nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
+nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
+bool nsContentUtils::sFragmentParsingActive = false;
+
+#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
+bool nsContentUtils::sDOMWindowDumpEnabled;
+#endif
+
+bool nsContentUtils::sDoNotTrackEnabled = false;
+
+mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
+
+// Subset of http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
+enum AutocompleteFieldName : uint8_t
+{
+ #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
+ eAutocompleteFieldName_##name_,
+ #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
+ AUTOCOMPLETE_FIELD_NAME(name_, value_)
+ #include "AutocompleteFieldList.h"
+ #undef AUTOCOMPLETE_FIELD_NAME
+ #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
+};
+
+enum AutocompleteFieldHint : uint8_t
+{
+ #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
+ eAutocompleteFieldHint_##name_,
+ #include "AutocompleteFieldList.h"
+ #undef AUTOCOMPLETE_FIELD_HINT
+};
+
+enum AutocompleteFieldContactHint : uint8_t
+{
+ #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
+ eAutocompleteFieldContactHint_##name_,
+ #include "AutocompleteFieldList.h"
+ #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
+};
+
+enum AutocompleteCategory
+{
+ #define AUTOCOMPLETE_CATEGORY(name_, value_) eAutocompleteCategory_##name_,
+ #include "AutocompleteFieldList.h"
+ #undef AUTOCOMPLETE_CATEGORY
+};
+
+static const nsAttrValue::EnumTable kAutocompleteFieldNameTable[] = {
+ #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
+ { value_, eAutocompleteFieldName_##name_ },
+ #include "AutocompleteFieldList.h"
+ #undef AUTOCOMPLETE_FIELD_NAME
+ { nullptr, 0 }
+};
+
+static const nsAttrValue::EnumTable kAutocompleteContactFieldNameTable[] = {
+ #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
+ { value_, eAutocompleteFieldName_##name_ },
+ #include "AutocompleteFieldList.h"
+ #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
+ { nullptr, 0 }
+};
+
+static const nsAttrValue::EnumTable kAutocompleteFieldHintTable[] = {
+ #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
+ { value_, eAutocompleteFieldHint_##name_ },
+ #include "AutocompleteFieldList.h"
+ #undef AUTOCOMPLETE_FIELD_HINT
+ { nullptr, 0 }
+};
+
+static const nsAttrValue::EnumTable kAutocompleteContactFieldHintTable[] = {
+ #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
+ { value_, eAutocompleteFieldContactHint_##name_ },
+ #include "AutocompleteFieldList.h"
+ #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
+ { nullptr, 0 }
+};
+
+namespace {
+
+static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
+static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
+
+static PLDHashTable* sEventListenerManagersHash;
+
+class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter
+{
+ MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
+ ~DOMEventListenerManagersHashReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ // We don't measure the |EventListenerManager| objects pointed to by the
+ // entries because those references are non-owning.
+ int64_t amount = sEventListenerManagersHash
+ ? sEventListenerManagersHash->ShallowSizeOfIncludingThis(
+ MallocSizeOf)
+ : 0;
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
+ amount,
+ "Memory used by the event listener manager's hash table.");
+
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS(DOMEventListenerManagersHashReporter, nsIMemoryReporter)
+
+class EventListenerManagerMapEntry : public PLDHashEntryHdr
+{
+public:
+ explicit EventListenerManagerMapEntry(const void* aKey)
+ : mKey(aKey)
+ {
+ }
+
+ ~EventListenerManagerMapEntry()
+ {
+ NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
+ }
+
+protected: // declared protected to silence clang warnings
+ const void *mKey; // must be first, to look like PLDHashEntryStub
+
+public:
+ RefPtr<EventListenerManager> mListenerManager;
+};
+
+static void
+EventListenerManagerHashInitEntry(PLDHashEntryHdr *entry, const void *key)
+{
+ // Initialize the entry with placement new
+ new (entry) EventListenerManagerMapEntry(key);
+}
+
+static void
+EventListenerManagerHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
+{
+ EventListenerManagerMapEntry *lm =
+ static_cast<EventListenerManagerMapEntry *>(entry);
+
+ // Let the EventListenerManagerMapEntry clean itself up...
+ lm->~EventListenerManagerMapEntry();
+}
+
+class SameOriginCheckerImpl final : public nsIChannelEventSink,
+ public nsIInterfaceRequestor
+{
+ ~SameOriginCheckerImpl() {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICHANNELEVENTSINK
+ NS_DECL_NSIINTERFACEREQUESTOR
+};
+
+class CharsetDetectionObserver final : public nsICharsetDetectionObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf) override
+ {
+ mCharset = aCharset;
+ return NS_OK;
+ }
+
+ const nsACString& GetResult() const
+ {
+ return mCharset;
+ }
+
+private:
+ nsCString mCharset;
+};
+
+} // namespace
+
+/* static */
+TimeDuration
+nsContentUtils::HandlingUserInputTimeout()
+{
+ return TimeDuration::FromMilliseconds(sHandlingInputTimeout);
+}
+
+// static
+nsresult
+nsContentUtils::Init()
+{
+ if (sInitialized) {
+ NS_WARNING("Init() called twice");
+
+ return NS_OK;
+ }
+
+ sNameSpaceManager = nsNameSpaceManager::GetInstance();
+ NS_ENSURE_TRUE(sNameSpaceManager, NS_ERROR_OUT_OF_MEMORY);
+
+ sXPConnect = nsXPConnect::XPConnect();
+
+ sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
+ if(!sSecurityManager)
+ return NS_ERROR_FAILURE;
+ NS_ADDREF(sSecurityManager);
+
+ sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
+ MOZ_ASSERT(sSystemPrincipal);
+
+ // We use the constructor here because we want infallible initialization; we
+ // apparently don't care whether sNullSubjectPrincipal has a sane URI or not.
+ RefPtr<nsNullPrincipal> nullPrincipal = new nsNullPrincipal();
+ nullPrincipal->Init();
+ nullPrincipal.forget(&sNullSubjectPrincipal);
+
+ nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
+ if (NS_FAILED(rv)) {
+ // This makes life easier, but we can live without it.
+
+ sIOService = nullptr;
+ }
+
+ rv = CallGetService(NS_LBRK_CONTRACTID, &sLineBreaker);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = CallGetService(NS_WBRK_CONTRACTID, &sWordBreaker);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!InitializeEventTable())
+ return NS_ERROR_FAILURE;
+
+ if (!sEventListenerManagersHash) {
+ static const PLDHashTableOps hash_table_ops =
+ {
+ PLDHashTable::HashVoidPtrKeyStub,
+ PLDHashTable::MatchEntryStub,
+ PLDHashTable::MoveEntryStub,
+ EventListenerManagerHashClearEntry,
+ EventListenerManagerHashInitEntry
+ };
+
+ sEventListenerManagersHash =
+ new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
+
+ RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
+ }
+
+ sBlockedScriptRunners = new AutoTArray<nsCOMPtr<nsIRunnable>, 8>;
+
+ Preferences::AddBoolVarCache(&sAllowXULXBL_for_file,
+ "dom.allow_XUL_XBL_for_file");
+
+ Preferences::AddBoolVarCache(&sIsFullScreenApiEnabled,
+ "full-screen-api.enabled");
+
+ Preferences::AddBoolVarCache(&sIsUnprefixedFullscreenApiEnabled,
+ "full-screen-api.unprefix.enabled");
+
+ Preferences::AddBoolVarCache(&sTrustedFullScreenOnly,
+ "full-screen-api.allow-trusted-requests-only");
+
+ Preferences::AddBoolVarCache(&sIsCutCopyAllowed,
+ "dom.allow_cut_copy", true);
+
+ Preferences::AddBoolVarCache(&sIsPerformanceTimingEnabled,
+ "dom.enable_performance", true);
+
+ Preferences::AddBoolVarCache(&sIsResourceTimingEnabled,
+ "dom.enable_resource_timing", true);
+
+ Preferences::AddBoolVarCache(&sIsUserTimingLoggingEnabled,
+ "dom.performance.enable_user_timing_logging", false);
+
+ Preferences::AddBoolVarCache(&sIsFrameTimingPrefEnabled,
+ "dom.enable_frame_timing", false);
+
+ Preferences::AddBoolVarCache(&sIsExperimentalAutocompleteEnabled,
+ "dom.forms.autocomplete.experimental", false);
+
+ Preferences::AddBoolVarCache(&sEncodeDecodeURLHash,
+ "dom.url.encode_decode_hash", false);
+
+ Preferences::AddBoolVarCache(&sGettersDecodeURLHash,
+ "dom.url.getters_decode_hash", false);
+
+ Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting,
+ "privacy.resistFingerprinting", false);
+
+ Preferences::AddUintVarCache(&sHandlingInputTimeout,
+ "dom.event.handling-user-input-time-limit",
+ 1000);
+
+ Preferences::AddBoolVarCache(&sSendPerformanceTimingNotifications,
+ "dom.performance.enable_notify_performance_timing", false);
+
+ Preferences::AddUintVarCache(&sCookiesLifetimePolicy,
+ "network.cookie.lifetimePolicy",
+ nsICookieService::ACCEPT_NORMALLY);
+
+ Preferences::AddUintVarCache(&sCookiesBehavior,
+ "network.cookie.cookieBehavior",
+ nsICookieService::BEHAVIOR_ACCEPT);
+
+#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
+ Preferences::AddBoolVarCache(&sDOMWindowDumpEnabled,
+ "browser.dom.window.dump.enabled");
+#endif
+
+ Preferences::AddBoolVarCache(&sDoNotTrackEnabled,
+ "privacy.donottrackheader.enabled", false);
+
+ Preferences::AddBoolVarCache(&sUseActivityCursor,
+ "ui.use_activity_cursor", false);
+
+ Element::InitCCCallbacks();
+
+ nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
+ do_GetService("@mozilla.org/uuid-generator;1", &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ uuidGenerator.forget(&sUUIDGenerator);
+
+ sInitialized = true;
+
+ return NS_OK;
+}
+
+void
+nsContentUtils::GetShiftText(nsAString& text)
+{
+ if (!sShiftText)
+ InitializeModifierStrings();
+ text.Assign(*sShiftText);
+}
+
+void
+nsContentUtils::GetControlText(nsAString& text)
+{
+ if (!sControlText)
+ InitializeModifierStrings();
+ text.Assign(*sControlText);
+}
+
+void
+nsContentUtils::GetMetaText(nsAString& text)
+{
+ if (!sMetaText)
+ InitializeModifierStrings();
+ text.Assign(*sMetaText);
+}
+
+void
+nsContentUtils::GetOSText(nsAString& text)
+{
+ if (!sOSText) {
+ InitializeModifierStrings();
+ }
+ text.Assign(*sOSText);
+}
+
+void
+nsContentUtils::GetAltText(nsAString& text)
+{
+ if (!sAltText)
+ InitializeModifierStrings();
+ text.Assign(*sAltText);
+}
+
+void
+nsContentUtils::GetModifierSeparatorText(nsAString& text)
+{
+ if (!sModifierSeparator)
+ InitializeModifierStrings();
+ text.Assign(*sModifierSeparator);
+}
+
+void
+nsContentUtils::InitializeModifierStrings()
+{
+ //load the display strings for the keyboard accelerators
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ nsCOMPtr<nsIStringBundle> bundle;
+ DebugOnly<nsresult> rv = NS_OK;
+ if (bundleService) {
+ rv = bundleService->CreateBundle( "chrome://global-platform/locale/platformKeys.properties",
+ getter_AddRefs(bundle));
+ }
+
+ NS_ASSERTION(NS_SUCCEEDED(rv) && bundle, "chrome://global/locale/platformKeys.properties could not be loaded");
+ nsXPIDLString shiftModifier;
+ nsXPIDLString metaModifier;
+ nsXPIDLString osModifier;
+ nsXPIDLString altModifier;
+ nsXPIDLString controlModifier;
+ nsXPIDLString modifierSeparator;
+ if (bundle) {
+ //macs use symbols for each modifier key, so fetch each from the bundle, which also covers i18n
+ bundle->GetStringFromName(u"VK_SHIFT", getter_Copies(shiftModifier));
+ bundle->GetStringFromName(u"VK_META", getter_Copies(metaModifier));
+ bundle->GetStringFromName(u"VK_WIN", getter_Copies(osModifier));
+ bundle->GetStringFromName(u"VK_ALT", getter_Copies(altModifier));
+ bundle->GetStringFromName(u"VK_CONTROL", getter_Copies(controlModifier));
+ bundle->GetStringFromName(u"MODIFIER_SEPARATOR", getter_Copies(modifierSeparator));
+ }
+ //if any of these don't exist, we get an empty string
+ sShiftText = new nsString(shiftModifier);
+ sMetaText = new nsString(metaModifier);
+ sOSText = new nsString(osModifier);
+ sAltText = new nsString(altModifier);
+ sControlText = new nsString(controlModifier);
+ sModifierSeparator = new nsString(modifierSeparator);
+}
+
+// Because of SVG/SMIL we have several atoms mapped to the same
+// id, but we can rely on MESSAGE_TO_EVENT to map id to only one atom.
+static bool
+ShouldAddEventToStringEventTable(const EventNameMapping& aMapping)
+{
+ switch(aMapping.mMessage) {
+#define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
+ case message_: return nsGkAtoms::on##name_ == aMapping.mAtom;
+#include "mozilla/EventNameList.h"
+#undef MESSAGE_TO_EVENT
+ default:
+ break;
+ }
+ return false;
+}
+
+bool
+nsContentUtils::InitializeEventTable() {
+ NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
+ NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
+
+ static const EventNameMapping eventArray[] = {
+#define EVENT(name_, _message, _type, _class) \
+ { nsGkAtoms::on##name_, _type, _message, _class, false },
+#define WINDOW_ONLY_EVENT EVENT
+#define NON_IDL_EVENT EVENT
+#include "mozilla/EventNameList.h"
+#undef WINDOW_ONLY_EVENT
+#undef NON_IDL_EVENT
+#undef EVENT
+ { nullptr }
+ };
+
+ sAtomEventTable = new nsDataHashtable<nsISupportsHashKey, EventNameMapping>(
+ ArrayLength(eventArray));
+ sStringEventTable = new nsDataHashtable<nsStringHashKey, EventNameMapping>(
+ ArrayLength(eventArray));
+ sUserDefinedEvents = new nsCOMArray<nsIAtom>(64);
+
+ // Subtract one from the length because of the trailing null
+ for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) {
+ sAtomEventTable->Put(eventArray[i].mAtom, eventArray[i]);
+ if (ShouldAddEventToStringEventTable(eventArray[i])) {
+ sStringEventTable->Put(
+ Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
+ eventArray[i]);
+ }
+ }
+
+ return true;
+}
+
+void
+nsContentUtils::InitializeTouchEventTable()
+{
+ static bool sEventTableInitialized = false;
+ if (!sEventTableInitialized && sAtomEventTable && sStringEventTable) {
+ sEventTableInitialized = true;
+ static const EventNameMapping touchEventArray[] = {
+#define EVENT(name_, _message, _type, _class)
+#define TOUCH_EVENT(name_, _message, _type, _class) \
+ { nsGkAtoms::on##name_, _type, _message, _class },
+#include "mozilla/EventNameList.h"
+#undef TOUCH_EVENT
+#undef EVENT
+ { nullptr }
+ };
+ // Subtract one from the length because of the trailing null
+ for (uint32_t i = 0; i < ArrayLength(touchEventArray) - 1; ++i) {
+ sAtomEventTable->Put(touchEventArray[i].mAtom, touchEventArray[i]);
+ sStringEventTable->Put(Substring(nsDependentAtomString(touchEventArray[i].mAtom), 2),
+ touchEventArray[i]);
+ }
+ }
+}
+
+static bool
+Is8bit(const nsAString& aString)
+{
+ static const char16_t EIGHT_BIT = char16_t(~0x00FF);
+
+ for (nsAString::const_char_iterator start = aString.BeginReading(),
+ end = aString.EndReading();
+ start != end;
+ ++start) {
+ if (*start & EIGHT_BIT) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+nsresult
+nsContentUtils::Btoa(const nsAString& aBinaryData,
+ nsAString& aAsciiBase64String)
+{
+ if (!Is8bit(aBinaryData)) {
+ aAsciiBase64String.Truncate();
+ return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
+ }
+
+ return Base64Encode(aBinaryData, aAsciiBase64String);
+}
+
+nsresult
+nsContentUtils::Atob(const nsAString& aAsciiBase64String,
+ nsAString& aBinaryData)
+{
+ if (!Is8bit(aAsciiBase64String)) {
+ aBinaryData.Truncate();
+ return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
+ }
+
+ const char16_t* start = aAsciiBase64String.BeginReading();
+ const char16_t* end = aAsciiBase64String.EndReading();
+ nsString trimmedString;
+ if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible)) {
+ return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
+ }
+ while (start < end) {
+ if (!nsContentUtils::IsHTMLWhitespace(*start)) {
+ trimmedString.Append(*start);
+ }
+ start++;
+ }
+ nsresult rv = Base64Decode(trimmedString, aBinaryData);
+ if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
+ return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
+ }
+ return rv;
+}
+
+bool
+nsContentUtils::IsAutocompleteEnabled(nsIDOMHTMLInputElement* aInput)
+{
+ NS_PRECONDITION(aInput, "aInput should not be null!");
+
+ nsAutoString autocomplete;
+ aInput->GetAutocomplete(autocomplete);
+
+ if (autocomplete.IsEmpty()) {
+ nsCOMPtr<nsIDOMHTMLFormElement> form;
+ aInput->GetForm(getter_AddRefs(form));
+ if (!form) {
+ return true;
+ }
+
+ form->GetAutocomplete(autocomplete);
+ }
+
+ return !autocomplete.EqualsLiteral("off");
+}
+
+nsContentUtils::AutocompleteAttrState
+nsContentUtils::SerializeAutocompleteAttribute(const nsAttrValue* aAttr,
+ nsAString& aResult,
+ AutocompleteAttrState aCachedState)
+{
+ if (!aAttr ||
+ aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
+ return aCachedState;
+ }
+
+ if (aCachedState == nsContentUtils::eAutocompleteAttrState_Valid) {
+ uint32_t atomCount = aAttr->GetAtomCount();
+ for (uint32_t i = 0; i < atomCount; i++) {
+ if (i != 0) {
+ aResult.Append(' ');
+ }
+ aResult.Append(nsDependentAtomString(aAttr->AtomAt(i)));
+ }
+ nsContentUtils::ASCIIToLower(aResult);
+ return aCachedState;
+ }
+
+ aResult.Truncate();
+
+ mozilla::dom::AutocompleteInfo info;
+ AutocompleteAttrState state =
+ InternalSerializeAutocompleteAttribute(aAttr, info);
+ if (state == eAutocompleteAttrState_Valid) {
+ // Concatenate the info fields.
+ aResult = info.mSection;
+
+ if (!info.mAddressType.IsEmpty()) {
+ if (!aResult.IsEmpty()) {
+ aResult += ' ';
+ }
+ aResult += info.mAddressType;
+ }
+
+ if (!info.mContactType.IsEmpty()) {
+ if (!aResult.IsEmpty()) {
+ aResult += ' ';
+ }
+ aResult += info.mContactType;
+ }
+
+ if (!info.mFieldName.IsEmpty()) {
+ if (!aResult.IsEmpty()) {
+ aResult += ' ';
+ }
+ aResult += info.mFieldName;
+ }
+ }
+
+ return state;
+}
+
+nsContentUtils::AutocompleteAttrState
+nsContentUtils::SerializeAutocompleteAttribute(const nsAttrValue* aAttr,
+ mozilla::dom::AutocompleteInfo& aInfo,
+ AutocompleteAttrState aCachedState)
+{
+ if (!aAttr ||
+ aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
+ return aCachedState;
+ }
+
+ return InternalSerializeAutocompleteAttribute(aAttr, aInfo);
+}
+
+/**
+ * Helper to validate the @autocomplete tokens.
+ *
+ * @return {AutocompleteAttrState} The state of the attribute (invalid/valid).
+ */
+nsContentUtils::AutocompleteAttrState
+nsContentUtils::InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrVal,
+ mozilla::dom::AutocompleteInfo& aInfo)
+{
+ // No sandbox attribute so we are done
+ if (!aAttrVal) {
+ return eAutocompleteAttrState_Invalid;
+ }
+
+ uint32_t numTokens = aAttrVal->GetAtomCount();
+ if (!numTokens) {
+ return eAutocompleteAttrState_Invalid;
+ }
+
+ uint32_t index = numTokens - 1;
+ nsString tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
+ AutocompleteCategory category;
+ nsAttrValue enumValue;
+
+ nsAutoString str;
+ bool result = enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
+ if (result) {
+ // Off/Automatic/Normal categories.
+ if (enumValue.Equals(NS_LITERAL_STRING("off"), eIgnoreCase) ||
+ enumValue.Equals(NS_LITERAL_STRING("on"), eIgnoreCase)) {
+ if (numTokens > 1) {
+ return eAutocompleteAttrState_Invalid;
+ }
+ enumValue.ToString(str);
+ ASCIIToLower(str);
+ aInfo.mFieldName.Assign(str);
+ return eAutocompleteAttrState_Valid;
+ }
+
+ // Only allow on/off if experimental @autocomplete values aren't enabled.
+ if (!sIsExperimentalAutocompleteEnabled) {
+ return eAutocompleteAttrState_Invalid;
+ }
+
+ // Normal category
+ if (numTokens > 2) {
+ return eAutocompleteAttrState_Invalid;
+ }
+ category = eAutocompleteCategory_NORMAL;
+ } else { // Check if the last token is of the contact category instead.
+ // Only allow on/off if experimental @autocomplete values aren't enabled.
+ if (!sIsExperimentalAutocompleteEnabled) {
+ return eAutocompleteAttrState_Invalid;
+ }
+
+ result = enumValue.ParseEnumValue(tokenString, kAutocompleteContactFieldNameTable, false);
+ if (!result || numTokens > 3) {
+ return eAutocompleteAttrState_Invalid;
+ }
+
+ category = eAutocompleteCategory_CONTACT;
+ }
+
+ enumValue.ToString(str);
+ ASCIIToLower(str);
+ aInfo.mFieldName.Assign(str);
+
+ // We are done if this was the only token.
+ if (numTokens == 1) {
+ return eAutocompleteAttrState_Valid;
+ }
+
+ --index;
+ tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
+
+ if (category == eAutocompleteCategory_CONTACT) {
+ nsAttrValue contactFieldHint;
+ result = contactFieldHint.ParseEnumValue(tokenString, kAutocompleteContactFieldHintTable, false);
+ if (result) {
+ nsAutoString contactFieldHintString;
+ contactFieldHint.ToString(contactFieldHintString);
+ ASCIIToLower(contactFieldHintString);
+ aInfo.mContactType.Assign(contactFieldHintString);
+ if (index == 0) {
+ return eAutocompleteAttrState_Valid;
+ }
+ --index;
+ tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
+ }
+ }
+
+ // Check for billing/shipping tokens
+ nsAttrValue fieldHint;
+ if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable, false)) {
+ nsString fieldHintString;
+ fieldHint.ToString(fieldHintString);
+ ASCIIToLower(fieldHintString);
+ aInfo.mAddressType.Assign(fieldHintString);
+ if (index == 0) {
+ return eAutocompleteAttrState_Valid;
+ }
+ --index;
+ }
+
+ // Clear the fields as the autocomplete attribute is invalid.
+ aInfo.mAddressType.Truncate();
+ aInfo.mContactType.Truncate();
+ aInfo.mFieldName.Truncate();
+
+ return eAutocompleteAttrState_Invalid;
+}
+
+// Parse an integer according to HTML spec
+int32_t
+nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
+ ParseHTMLIntegerResultFlags *aResult)
+{
+ int result = eParseHTMLInteger_NoFlags;
+
+ nsAString::const_iterator iter, end;
+ aValue.BeginReading(iter);
+ aValue.EndReading(end);
+
+ while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
+ result |= eParseHTMLInteger_NonStandard;
+ ++iter;
+ }
+
+ if (iter == end) {
+ result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
+ *aResult = (ParseHTMLIntegerResultFlags)result;
+ return 0;
+ }
+
+ int sign = 1;
+ if (*iter == char16_t('-')) {
+ sign = -1;
+ ++iter;
+ } else if (*iter == char16_t('+')) {
+ result |= eParseHTMLInteger_NonStandard;
+ ++iter;
+ }
+
+ bool foundValue = false;
+ CheckedInt32 value = 0;
+
+ // Check for leading zeros first.
+ uint64_t leadingZeros = 0;
+ while (iter != end) {
+ if (*iter != char16_t('0')) {
+ break;
+ }
+
+ ++leadingZeros;
+ foundValue = true;
+ ++iter;
+ }
+
+ while (iter != end) {
+ if (*iter >= char16_t('0') && *iter <= char16_t('9')) {
+ value = (value * 10) + (*iter - char16_t('0')) * sign;
+ ++iter;
+ if (!value.isValid()) {
+ result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
+ break;
+ } else {
+ foundValue = true;
+ }
+ } else if (*iter == char16_t('%')) {
+ ++iter;
+ result |= eParseHTMLInteger_IsPercent;
+ break;
+ } else {
+ break;
+ }
+ }
+
+ if (!foundValue) {
+ result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
+ }
+
+ if (value.isValid() &&
+ ((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
+ (sign == -1 && value == 0))) {
+ result |= eParseHTMLInteger_NonStandard;
+ }
+
+ if (iter != end) {
+ result |= eParseHTMLInteger_DidNotConsumeAllInput;
+ }
+
+ *aResult = (ParseHTMLIntegerResultFlags)result;
+ return value.isValid() ? value.value() : 0;
+}
+
+#define SKIP_WHITESPACE(iter, end_iter, end_res) \
+ while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
+ ++(iter); \
+ } \
+ if ((iter) == (end_iter)) { \
+ return (end_res); \
+ }
+
+#define SKIP_ATTR_NAME(iter, end_iter) \
+ while ((iter) != (end_iter) && !nsCRT::IsAsciiSpace(*(iter)) && \
+ *(iter) != '=') { \
+ ++(iter); \
+ }
+
+bool
+nsContentUtils::GetPseudoAttributeValue(const nsString& aSource, nsIAtom *aName,
+ nsAString& aValue)
+{
+ aValue.Truncate();
+
+ const char16_t *start = aSource.get();
+ const char16_t *end = start + aSource.Length();
+ const char16_t *iter;
+
+ while (start != end) {
+ SKIP_WHITESPACE(start, end, false)
+ iter = start;
+ SKIP_ATTR_NAME(iter, end)
+
+ if (start == iter) {
+ return false;
+ }
+
+ // Remember the attr name.
+ const nsDependentSubstring & attrName = Substring(start, iter);
+
+ // Now check whether this is a valid name="value" pair.
+ start = iter;
+ SKIP_WHITESPACE(start, end, false)
+ if (*start != '=') {
+ // No '=', so this is not a name="value" pair. We don't know
+ // what it is, and we have no way to handle it.
+ return false;
+ }
+
+ // Have to skip the value.
+ ++start;
+ SKIP_WHITESPACE(start, end, false)
+ char16_t q = *start;
+ if (q != kQuote && q != kApostrophe) {
+ // Not a valid quoted value, so bail.
+ return false;
+ }
+
+ ++start; // Point to the first char of the value.
+ iter = start;
+
+ while (iter != end && *iter != q) {
+ ++iter;
+ }
+
+ if (iter == end) {
+ // Oops, unterminated quoted string.
+ return false;
+ }
+
+ // At this point attrName holds the name of the "attribute" and
+ // the value is between start and iter.
+
+ if (aName->Equals(attrName)) {
+ // We'll accumulate as many characters as possible (until we hit either
+ // the end of the string or the beginning of an entity). Chunks will be
+ // delimited by start and chunkEnd.
+ const char16_t *chunkEnd = start;
+ while (chunkEnd != iter) {
+ if (*chunkEnd == kLessThan) {
+ aValue.Truncate();
+
+ return false;
+ }
+
+ if (*chunkEnd == kAmpersand) {
+ aValue.Append(start, chunkEnd - start);
+
+ const char16_t *afterEntity = nullptr;
+ char16_t result[2];
+ uint32_t count =
+ MOZ_XMLTranslateEntity(reinterpret_cast<const char*>(chunkEnd),
+ reinterpret_cast<const char*>(iter),
+ reinterpret_cast<const char**>(&afterEntity),
+ result);
+ if (count == 0) {
+ aValue.Truncate();
+
+ return false;
+ }
+
+ aValue.Append(result, count);
+
+ // Advance to after the entity and begin a new chunk.
+ start = chunkEnd = afterEntity;
+ }
+ else {
+ ++chunkEnd;
+ }
+ }
+
+ // Append remainder.
+ aValue.Append(start, iter - start);
+
+ return true;
+ }
+
+ // Resume scanning after the end of the attribute value (past the quote
+ // char).
+ start = iter + 1;
+ }
+
+ return false;
+}
+
+bool
+nsContentUtils::IsJavaScriptLanguage(const nsString& aName)
+{
+ return aName.LowerCaseEqualsLiteral("javascript") ||
+ aName.LowerCaseEqualsLiteral("livescript") ||
+ aName.LowerCaseEqualsLiteral("mocha") ||
+ aName.LowerCaseEqualsLiteral("javascript1.0") ||
+ aName.LowerCaseEqualsLiteral("javascript1.1") ||
+ aName.LowerCaseEqualsLiteral("javascript1.2") ||
+ aName.LowerCaseEqualsLiteral("javascript1.3") ||
+ aName.LowerCaseEqualsLiteral("javascript1.4") ||
+ aName.LowerCaseEqualsLiteral("javascript1.5");
+}
+
+JSVersion
+nsContentUtils::ParseJavascriptVersion(const nsAString& aVersionStr)
+{
+ if (aVersionStr.Length() != 3 || aVersionStr[0] != '1' ||
+ aVersionStr[1] != '.') {
+ return JSVERSION_UNKNOWN;
+ }
+
+ switch (aVersionStr[2]) {
+ case '0': /* fall through */
+ case '1': /* fall through */
+ case '2': /* fall through */
+ case '3': /* fall through */
+ case '4': /* fall through */
+ case '5': return JSVERSION_DEFAULT;
+ case '6': return JSVERSION_1_6;
+ case '7': return JSVERSION_1_7;
+ case '8': return JSVERSION_1_8;
+ default: return JSVERSION_UNKNOWN;
+ }
+}
+
+void
+nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType,
+ nsString& aParams)
+{
+ aType.Truncate();
+ aParams.Truncate();
+ int32_t semiIndex = aValue.FindChar(char16_t(';'));
+ if (-1 != semiIndex) {
+ aType = Substring(aValue, 0, semiIndex);
+ aParams = Substring(aValue, semiIndex + 1,
+ aValue.Length() - (semiIndex + 1));
+ aParams.StripWhitespace();
+ }
+ else {
+ aType = aValue;
+ }
+ aType.StripWhitespace();
+}
+
+nsresult
+nsContentUtils::IsUserIdle(uint32_t aRequestedIdleTimeInMS, bool* aUserIsIdle)
+{
+ nsresult rv;
+ nsCOMPtr<nsIIdleService> idleService =
+ do_GetService("@mozilla.org/widget/idleservice;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t idleTimeInMS;
+ rv = idleService->GetIdleTime(&idleTimeInMS);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aUserIsIdle = idleTimeInMS >= aRequestedIdleTimeInMS;
+ return NS_OK;
+}
+
+/**
+ * Access a cached parser service. Don't addref. We need only one
+ * reference to it and this class has that one.
+ */
+/* static */
+nsIParserService*
+nsContentUtils::GetParserService()
+{
+ // XXX: This isn't accessed from several threads, is it?
+ if (!sParserService) {
+ // Lock, recheck sCachedParserService and aquire if this should be
+ // safe for multiple threads.
+ nsresult rv = CallGetService(kParserServiceCID, &sParserService);
+ if (NS_FAILED(rv)) {
+ sParserService = nullptr;
+ }
+ }
+
+ return sParserService;
+}
+
+/**
+* A helper function that parses a sandbox attribute (of an <iframe> or a CSP
+* directive) and converts it to the set of flags used internally.
+*
+* @param aSandboxAttr the sandbox attribute
+* @return the set of flags (SANDBOXED_NONE if aSandboxAttr is
+* null)
+*/
+uint32_t
+nsContentUtils::ParseSandboxAttributeToFlags(const nsAttrValue* aSandboxAttr)
+{
+ if (!aSandboxAttr) {
+ return SANDBOXED_NONE;
+ }
+
+ uint32_t out = SANDBOX_ALL_FLAGS;
+
+#define SANDBOX_KEYWORD(string, atom, flags) \
+ if (aSandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { \
+ out &= ~(flags); \
+ }
+#include "IframeSandboxKeywordList.h"
+#undef SANDBOX_KEYWORD
+
+ return out;
+}
+
+/**
+* A helper function that checks if a string matches a valid sandbox flag.
+*
+* @param aFlag the potential sandbox flag.
+* @return true if the flag is a sandbox flag.
+*/
+bool
+nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag)
+{
+#define SANDBOX_KEYWORD(string, atom, flags) \
+ if (EqualsIgnoreASCIICase(nsDependentAtomString(nsGkAtoms::atom), aFlag)) { \
+ return true; \
+ }
+#include "IframeSandboxKeywordList.h"
+#undef SANDBOX_KEYWORD
+ return false;
+}
+
+/**
+ * A helper function that returns a string attribute corresponding to the
+ * sandbox flags.
+ *
+ * @param aFlags the sandbox flags
+ * @param aString the attribute corresponding to the flags (null if aFlags
+ * is zero)
+ */
+void
+nsContentUtils::SandboxFlagsToString(uint32_t aFlags, nsAString& aString)
+{
+ if (!aFlags) {
+ SetDOMStringToNull(aString);
+ return;
+ }
+
+ aString.Truncate();
+
+#define SANDBOX_KEYWORD(string, atom, flags) \
+ if (!(aFlags & (flags))) { \
+ if (!aString.IsEmpty()) { \
+ aString.Append(NS_LITERAL_STRING(" ")); \
+ } \
+ aString.Append(nsDependentAtomString(nsGkAtoms::atom)); \
+ }
+#include "IframeSandboxKeywordList.h"
+#undef SANDBOX_KEYWORD
+}
+
+nsIBidiKeyboard*
+nsContentUtils::GetBidiKeyboard()
+{
+ if (!sBidiKeyboard) {
+ nsresult rv = CallGetService("@mozilla.org/widget/bidikeyboard;1", &sBidiKeyboard);
+ if (NS_FAILED(rv)) {
+ sBidiKeyboard = nullptr;
+ }
+ }
+ return sBidiKeyboard;
+}
+
+template <class OutputIterator>
+struct NormalizeNewlinesCharTraits {
+ public:
+ typedef typename OutputIterator::value_type value_type;
+
+ public:
+ explicit NormalizeNewlinesCharTraits(OutputIterator& aIterator) : mIterator(aIterator) { }
+ void writechar(typename OutputIterator::value_type aChar) {
+ *mIterator++ = aChar;
+ }
+
+ private:
+ OutputIterator mIterator;
+};
+
+template <class CharT>
+struct NormalizeNewlinesCharTraits<CharT*> {
+ public:
+ typedef CharT value_type;
+
+ public:
+ explicit NormalizeNewlinesCharTraits(CharT* aCharPtr) : mCharPtr(aCharPtr) { }
+ void writechar(CharT aChar) {
+ *mCharPtr++ = aChar;
+ }
+
+ private:
+ CharT* mCharPtr;
+};
+
+template <class OutputIterator>
+class CopyNormalizeNewlines
+{
+ public:
+ typedef typename OutputIterator::value_type value_type;
+
+ public:
+ explicit CopyNormalizeNewlines(OutputIterator* aDestination,
+ bool aLastCharCR = false) :
+ mLastCharCR(aLastCharCR),
+ mDestination(aDestination),
+ mWritten(0)
+ { }
+
+ uint32_t GetCharsWritten() {
+ return mWritten;
+ }
+
+ bool IsLastCharCR() {
+ return mLastCharCR;
+ }
+
+ void write(const typename OutputIterator::value_type* aSource, uint32_t aSourceLength) {
+
+ const typename OutputIterator::value_type* done_writing = aSource + aSourceLength;
+
+ // If the last source buffer ended with a CR...
+ if (mLastCharCR) {
+ // ..and if the next one is a LF, then skip it since
+ // we've already written out a newline
+ if (aSourceLength && (*aSource == value_type('\n'))) {
+ ++aSource;
+ }
+ mLastCharCR = false;
+ }
+
+ uint32_t num_written = 0;
+ while ( aSource < done_writing ) {
+ if (*aSource == value_type('\r')) {
+ mDestination->writechar('\n');
+ ++aSource;
+ // If we've reached the end of the buffer, record
+ // that we wrote out a CR
+ if (aSource == done_writing) {
+ mLastCharCR = true;
+ }
+ // If the next character is a LF, skip it
+ else if (*aSource == value_type('\n')) {
+ ++aSource;
+ }
+ }
+ else {
+ mDestination->writechar(*aSource++);
+ }
+ ++num_written;
+ }
+
+ mWritten += num_written;
+ }
+
+ private:
+ bool mLastCharCR;
+ OutputIterator* mDestination;
+ uint32_t mWritten;
+};
+
+// static
+uint32_t
+nsContentUtils::CopyNewlineNormalizedUnicodeTo(const nsAString& aSource,
+ uint32_t aSrcOffset,
+ char16_t* aDest,
+ uint32_t aLength,
+ bool& aLastCharCR)
+{
+ typedef NormalizeNewlinesCharTraits<char16_t*> sink_traits;
+
+ sink_traits dest_traits(aDest);
+ CopyNormalizeNewlines<sink_traits> normalizer(&dest_traits,aLastCharCR);
+ nsReadingIterator<char16_t> fromBegin, fromEnd;
+ copy_string(aSource.BeginReading(fromBegin).advance( int32_t(aSrcOffset) ),
+ aSource.BeginReading(fromEnd).advance( int32_t(aSrcOffset+aLength) ),
+ normalizer);
+ aLastCharCR = normalizer.IsLastCharCR();
+ return normalizer.GetCharsWritten();
+}
+
+// static
+uint32_t
+nsContentUtils::CopyNewlineNormalizedUnicodeTo(nsReadingIterator<char16_t>& aSrcStart, const nsReadingIterator<char16_t>& aSrcEnd, nsAString& aDest)
+{
+ typedef nsWritingIterator<char16_t> WritingIterator;
+ typedef NormalizeNewlinesCharTraits<WritingIterator> sink_traits;
+
+ WritingIterator iter;
+ aDest.BeginWriting(iter);
+ sink_traits dest_traits(iter);
+ CopyNormalizeNewlines<sink_traits> normalizer(&dest_traits);
+ copy_string(aSrcStart, aSrcEnd, normalizer);
+ return normalizer.GetCharsWritten();
+}
+
+/**
+ * This is used to determine whether a character is in one of the classes
+ * which CSS says should be part of the first-letter. Currently, that is
+ * all punctuation classes (P*). Note that this is a change from CSS2
+ * which excluded Pc and Pd.
+ *
+ * https://www.w3.org/TR/css-pseudo-4/#first-letter-pseudo
+ * "Punctuation (i.e, characters that belong to the Punctuation (P*) Unicode
+ * general category [UAX44]) [...]"
+ */
+
+// static
+bool
+nsContentUtils::IsFirstLetterPunctuation(uint32_t aChar)
+{
+ switch (mozilla::unicode::GetGeneralCategory(aChar)) {
+ case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
+ case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION: /* Pd */
+ case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION: /* Pe */
+ case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION: /* Pf */
+ case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
+ case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION: /* Po */
+ case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION: /* Ps */
+ return true;
+ default:
+ return false;
+ }
+}
+
+// static
+bool
+nsContentUtils::IsFirstLetterPunctuationAt(const nsTextFragment* aFrag, uint32_t aOffset)
+{
+ char16_t h = aFrag->CharAt(aOffset);
+ if (!IS_SURROGATE(h)) {
+ return IsFirstLetterPunctuation(h);
+ }
+ if (NS_IS_HIGH_SURROGATE(h) && aOffset + 1 < aFrag->GetLength()) {
+ char16_t l = aFrag->CharAt(aOffset + 1);
+ if (NS_IS_LOW_SURROGATE(l)) {
+ return IsFirstLetterPunctuation(SURROGATE_TO_UCS4(h, l));
+ }
+ }
+ return false;
+}
+
+// static
+bool nsContentUtils::IsAlphanumeric(uint32_t aChar)
+{
+ nsIUGenCategory::nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
+
+ return (cat == nsIUGenCategory::kLetter || cat == nsIUGenCategory::kNumber);
+}
+
+// static
+bool nsContentUtils::IsAlphanumericAt(const nsTextFragment* aFrag, uint32_t aOffset)
+{
+ char16_t h = aFrag->CharAt(aOffset);
+ if (!IS_SURROGATE(h)) {
+ return IsAlphanumeric(h);
+ }
+ if (NS_IS_HIGH_SURROGATE(h) && aOffset + 1 < aFrag->GetLength()) {
+ char16_t l = aFrag->CharAt(aOffset + 1);
+ if (NS_IS_LOW_SURROGATE(l)) {
+ return IsAlphanumeric(SURROGATE_TO_UCS4(h, l));
+ }
+ }
+ return false;
+}
+
+/* static */
+bool
+nsContentUtils::IsHTMLWhitespace(char16_t aChar)
+{
+ return aChar == char16_t(0x0009) ||
+ aChar == char16_t(0x000A) ||
+ aChar == char16_t(0x000C) ||
+ aChar == char16_t(0x000D) ||
+ aChar == char16_t(0x0020);
+}
+
+/* static */
+bool
+nsContentUtils::IsHTMLWhitespaceOrNBSP(char16_t aChar)
+{
+ return IsHTMLWhitespace(aChar) || aChar == char16_t(0xA0);
+}
+
+/* static */
+bool
+nsContentUtils::IsHTMLBlock(nsIContent* aContent)
+{
+ return aContent->IsAnyOfHTMLElements(nsGkAtoms::address,
+ nsGkAtoms::article,
+ nsGkAtoms::aside,
+ nsGkAtoms::blockquote,
+ nsGkAtoms::center,
+ nsGkAtoms::dir,
+ nsGkAtoms::div,
+ nsGkAtoms::dl, // XXX why not dt and dd?
+ nsGkAtoms::fieldset,
+ nsGkAtoms::figure, // XXX shouldn't figcaption be on this list
+ nsGkAtoms::footer,
+ nsGkAtoms::form,
+ nsGkAtoms::h1,
+ nsGkAtoms::h2,
+ nsGkAtoms::h3,
+ nsGkAtoms::h4,
+ nsGkAtoms::h5,
+ nsGkAtoms::h6,
+ nsGkAtoms::header,
+ nsGkAtoms::hgroup,
+ nsGkAtoms::hr,
+ nsGkAtoms::li,
+ nsGkAtoms::listing,
+ nsGkAtoms::menu,
+ nsGkAtoms::multicol, // XXX get rid of this one?
+ nsGkAtoms::nav,
+ nsGkAtoms::ol,
+ nsGkAtoms::p,
+ nsGkAtoms::pre,
+ nsGkAtoms::section,
+ nsGkAtoms::table,
+ nsGkAtoms::ul,
+ nsGkAtoms::xmp);
+}
+
+/* static */
+bool
+nsContentUtils::ParseIntMarginValue(const nsAString& aString, nsIntMargin& result)
+{
+ nsAutoString marginStr(aString);
+ marginStr.CompressWhitespace(true, true);
+ if (marginStr.IsEmpty()) {
+ return false;
+ }
+
+ int32_t start = 0, end = 0;
+ for (int count = 0; count < 4; count++) {
+ if ((uint32_t)end >= marginStr.Length())
+ return false;
+
+ // top, right, bottom, left
+ if (count < 3)
+ end = Substring(marginStr, start).FindChar(',');
+ else
+ end = Substring(marginStr, start).Length();
+
+ if (end <= 0)
+ return false;
+
+ nsresult ec;
+ int32_t val = nsString(Substring(marginStr, start, end)).ToInteger(&ec);
+ if (NS_FAILED(ec))
+ return false;
+
+ switch(count) {
+ case 0:
+ result.top = val;
+ break;
+ case 1:
+ result.right = val;
+ break;
+ case 2:
+ result.bottom = val;
+ break;
+ case 3:
+ result.left = val;
+ break;
+ }
+ start += end + 1;
+ }
+ return true;
+}
+
+// static
+int32_t
+nsContentUtils::ParseLegacyFontSize(const nsAString& aValue)
+{
+ nsAString::const_iterator iter, end;
+ aValue.BeginReading(iter);
+ aValue.EndReading(end);
+
+ while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
+ ++iter;
+ }
+
+ if (iter == end) {
+ return 0;
+ }
+
+ bool relative = false;
+ bool negate = false;
+ if (*iter == char16_t('-')) {
+ relative = true;
+ negate = true;
+ ++iter;
+ } else if (*iter == char16_t('+')) {
+ relative = true;
+ ++iter;
+ }
+
+ if (iter == end || *iter < char16_t('0') || *iter > char16_t('9')) {
+ return 0;
+ }
+
+ // We don't have to worry about overflow, since we can bail out as soon as
+ // we're bigger than 7.
+ int32_t value = 0;
+ while (iter != end && *iter >= char16_t('0') && *iter <= char16_t('9')) {
+ value = 10*value + (*iter - char16_t('0'));
+ if (value >= 7) {
+ break;
+ }
+ ++iter;
+ }
+
+ if (relative) {
+ if (negate) {
+ value = 3 - value;
+ } else {
+ value = 3 + value;
+ }
+ }
+
+ return clamped(value, 1, 7);
+}
+
+/* static */
+bool
+nsContentUtils::IsControlledByServiceWorker(nsIDocument* aDocument)
+{
+ if (nsContentUtils::IsInPrivateBrowsing(aDocument)) {
+ return false;
+ }
+
+ RefPtr<workers::ServiceWorkerManager> swm =
+ workers::ServiceWorkerManager::GetInstance();
+ MOZ_ASSERT(swm);
+
+ ErrorResult rv;
+ bool controlled = swm->IsControlled(aDocument, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ return false;
+ }
+
+ return controlled;
+}
+
+/* static */
+void
+nsContentUtils::GetOfflineAppManifest(nsIDocument *aDocument, nsIURI **aURI)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aDocument);
+ *aURI = nullptr;
+
+ if (IsControlledByServiceWorker(aDocument)) {
+ return;
+ }
+
+ Element* docElement = aDocument->GetRootElement();
+ if (!docElement) {
+ return;
+ }
+
+ nsAutoString manifestSpec;
+ docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
+
+ // Manifest URIs can't have fragment identifiers.
+ if (manifestSpec.IsEmpty() ||
+ manifestSpec.Contains('#')) {
+ return;
+ }
+
+ nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec,
+ aDocument,
+ aDocument->GetDocBaseURI());
+}
+
+/* static */
+bool
+nsContentUtils::OfflineAppAllowed(nsIURI *aURI)
+{
+ nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
+ do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
+ if (!updateService) {
+ return false;
+ }
+
+ bool allowed;
+ nsresult rv =
+ updateService->OfflineAppAllowedForURI(aURI,
+ Preferences::GetRootBranch(),
+ &allowed);
+ return NS_SUCCEEDED(rv) && allowed;
+}
+
+/* static */
+bool
+nsContentUtils::OfflineAppAllowed(nsIPrincipal *aPrincipal)
+{
+ nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
+ do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
+ if (!updateService) {
+ return false;
+ }
+
+ bool allowed;
+ nsresult rv = updateService->OfflineAppAllowed(aPrincipal,
+ Preferences::GetRootBranch(),
+ &allowed);
+ return NS_SUCCEEDED(rv) && allowed;
+}
+
+bool
+nsContentUtils::MaybeAllowOfflineAppByDefault(nsIPrincipal *aPrincipal)
+{
+ if (!Preferences::GetRootBranch())
+ return false;
+
+ nsresult rv;
+
+ bool allowedByDefault;
+ rv = Preferences::GetRootBranch()->GetBoolPref(
+ "offline-apps.allow_by_default", &allowedByDefault);
+ if (NS_FAILED(rv))
+ return false;
+
+ if (!allowedByDefault)
+ return false;
+
+ nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
+ do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
+ if (!updateService) {
+ return false;
+ }
+
+ rv = updateService->AllowOfflineApp(aPrincipal);
+ return NS_SUCCEEDED(rv);
+}
+
+// static
+void
+nsContentUtils::Shutdown()
+{
+ sInitialized = false;
+
+ NS_IF_RELEASE(sContentPolicyService);
+ sTriedToGetContentPolicy = false;
+ uint32_t i;
+ for (i = 0; i < PropertiesFile_COUNT; ++i)
+ NS_IF_RELEASE(sStringBundles[i]);
+
+ NS_IF_RELEASE(sStringBundleService);
+ NS_IF_RELEASE(sConsoleService);
+ sXPConnect = nullptr;
+ NS_IF_RELEASE(sSecurityManager);
+ NS_IF_RELEASE(sSystemPrincipal);
+ NS_IF_RELEASE(sNullSubjectPrincipal);
+ NS_IF_RELEASE(sParserService);
+ NS_IF_RELEASE(sIOService);
+ NS_IF_RELEASE(sUUIDGenerator);
+ NS_IF_RELEASE(sLineBreaker);
+ NS_IF_RELEASE(sWordBreaker);
+ NS_IF_RELEASE(sBidiKeyboard);
+
+ delete sAtomEventTable;
+ sAtomEventTable = nullptr;
+ delete sStringEventTable;
+ sStringEventTable = nullptr;
+ delete sUserDefinedEvents;
+ sUserDefinedEvents = nullptr;
+
+ if (sEventListenerManagersHash) {
+ NS_ASSERTION(sEventListenerManagersHash->EntryCount() == 0,
+ "Event listener manager hash not empty at shutdown!");
+
+ // See comment above.
+
+ // However, we have to handle this table differently. If it still
+ // has entries, we want to leak it too, so that we can keep it alive
+ // in case any elements are destroyed. Because if they are, we need
+ // their event listener managers to be destroyed too, or otherwise
+ // it could leave dangling references in DOMClassInfo's preserved
+ // wrapper table.
+
+ if (sEventListenerManagersHash->EntryCount() == 0) {
+ delete sEventListenerManagersHash;
+ sEventListenerManagersHash = nullptr;
+ }
+ }
+
+ NS_ASSERTION(!sBlockedScriptRunners ||
+ sBlockedScriptRunners->Length() == 0,
+ "How'd this happen?");
+ delete sBlockedScriptRunners;
+ sBlockedScriptRunners = nullptr;
+
+ delete sShiftText;
+ sShiftText = nullptr;
+ delete sControlText;
+ sControlText = nullptr;
+ delete sMetaText;
+ sMetaText = nullptr;
+ delete sOSText;
+ sOSText = nullptr;
+ delete sAltText;
+ sAltText = nullptr;
+ delete sModifierSeparator;
+ sModifierSeparator = nullptr;
+
+ NS_IF_RELEASE(sSameOriginChecker);
+}
+
+/**
+ * Checks whether two nodes come from the same origin. aTrustedNode is
+ * considered 'safe' in that a user can operate on it and that it isn't
+ * a js-object that implements nsIDOMNode.
+ * Never call this function with the first node provided by script, it
+ * must always be known to be a 'real' node!
+ */
+// static
+nsresult
+nsContentUtils::CheckSameOrigin(const nsINode *aTrustedNode,
+ nsIDOMNode *aUnTrustedNode)
+{
+ MOZ_ASSERT(aTrustedNode);
+
+ // Make sure it's a real node.
+ nsCOMPtr<nsINode> unTrustedNode = do_QueryInterface(aUnTrustedNode);
+ NS_ENSURE_TRUE(unTrustedNode, NS_ERROR_UNEXPECTED);
+ return CheckSameOrigin(aTrustedNode, unTrustedNode);
+}
+
+nsresult
+nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
+ const nsINode* unTrustedNode)
+{
+ MOZ_ASSERT(aTrustedNode);
+ MOZ_ASSERT(unTrustedNode);
+
+ /*
+ * Get hold of each node's principal
+ */
+
+ nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
+ nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
+
+ if (trustedPrincipal == unTrustedPrincipal) {
+ return NS_OK;
+ }
+
+ bool equal;
+ // XXXbz should we actually have a Subsumes() check here instead? Or perhaps
+ // a separate method for that, with callers using one or the other?
+ if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
+ !equal) {
+ return NS_ERROR_DOM_PROP_ACCESS_DENIED;
+ }
+
+ return NS_OK;
+}
+
+// static
+bool
+nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
+ nsIPrincipal* aPrincipal)
+{
+ bool subsumes;
+ nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ if (subsumes) {
+ return true;
+ }
+
+ // The subject doesn't subsume aPrincipal. Allow access only if the subject
+ // is chrome.
+ return IsCallerChrome();
+}
+
+// static
+bool
+nsContentUtils::CanCallerAccess(nsIDOMNode *aNode)
+{
+ nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
+ NS_ENSURE_TRUE(node, false);
+ return CanCallerAccess(node);
+}
+
+// static
+bool
+nsContentUtils::CanCallerAccess(nsINode* aNode)
+{
+ return CanCallerAccess(SubjectPrincipal(), aNode->NodePrincipal());
+}
+
+// static
+bool
+nsContentUtils::CanCallerAccess(nsPIDOMWindowInner* aWindow)
+{
+ nsCOMPtr<nsIScriptObjectPrincipal> scriptObject = do_QueryInterface(aWindow);
+ NS_ENSURE_TRUE(scriptObject, false);
+
+ return CanCallerAccess(SubjectPrincipal(), scriptObject->GetPrincipal());
+}
+
+//static
+bool
+nsContentUtils::InProlog(nsINode *aNode)
+{
+ NS_PRECONDITION(aNode, "missing node to nsContentUtils::InProlog");
+
+ nsINode* parent = aNode->GetParentNode();
+ if (!parent || !parent->IsNodeOfType(nsINode::eDOCUMENT)) {
+ return false;
+ }
+
+ nsIDocument* doc = static_cast<nsIDocument*>(parent);
+ nsIContent* root = doc->GetRootElement();
+
+ return !root || doc->IndexOf(aNode) < doc->IndexOf(root);
+}
+
+nsIDocument*
+nsContentUtils::GetDocumentFromCaller()
+{
+ AutoJSContext cx;
+
+ nsCOMPtr<nsPIDOMWindowInner> win =
+ do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(cx)));
+ if (!win) {
+ return nullptr;
+ }
+
+ return win->GetExtantDoc();
+}
+
+bool
+nsContentUtils::IsCallerChrome()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (SubjectPrincipal() == sSystemPrincipal) {
+ return true;
+ }
+
+ // If the check failed, look for UniversalXPConnect on the cx compartment.
+ return xpc::IsUniversalXPConnectEnabled(GetCurrentJSContext());
+}
+
+bool
+nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell)
+{
+ if (!aDocShell) {
+ return false;
+ }
+ bool isChrome = nsContentUtils::IsChromeDoc(aDocShell->GetDocument());
+ return !isChrome && sPrivacyResistFingerprinting;
+}
+
+namespace mozilla {
+namespace dom {
+namespace workers {
+extern bool IsCurrentThreadRunningChromeWorker();
+extern JSContext* GetCurrentThreadJSContext();
+} // namespace workers
+} // namespace dom
+} // namespace mozilla
+
+bool
+nsContentUtils::ThreadsafeIsCallerChrome()
+{
+ return NS_IsMainThread() ?
+ IsCallerChrome() :
+ mozilla::dom::workers::IsCurrentThreadRunningChromeWorker();
+}
+
+bool
+nsContentUtils::IsCallerContentXBL()
+{
+ JSContext *cx = GetCurrentJSContext();
+ if (!cx)
+ return false;
+
+ JSCompartment *c = js::GetContextCompartment(cx);
+
+ // For remote XUL, we run XBL in the XUL scope. Given that we care about
+ // compat and not security for remote XUL, just always claim to be XBL.
+ if (!xpc::AllowContentXBLScope(c)) {
+ MOZ_ASSERT(nsContentUtils::AllowXULXBLForPrincipal(xpc::GetCompartmentPrincipal(c)));
+ return true;
+ }
+
+ return xpc::IsContentXBLScope(c);
+}
+
+// static
+bool
+nsContentUtils::LookupBindingMember(JSContext* aCx, nsIContent *aContent,
+ JS::Handle<jsid> aId,
+ JS::MutableHandle<JS::PropertyDescriptor> aDesc)
+{
+ nsXBLBinding* binding = aContent->GetXBLBinding();
+ if (!binding)
+ return true;
+ return binding->LookupMember(aCx, aId, aDesc);
+}
+
+// static
+nsINode*
+nsContentUtils::GetCrossDocParentNode(nsINode* aChild)
+{
+ NS_PRECONDITION(aChild, "The child is null!");
+
+ nsINode* parent = aChild->GetParentNode();
+ if (parent && parent->IsContent() && aChild->IsContent()) {
+ parent = aChild->AsContent()->GetFlattenedTreeParent();
+ }
+
+ if (parent || !aChild->IsNodeOfType(nsINode::eDOCUMENT))
+ return parent;
+
+ nsIDocument* doc = static_cast<nsIDocument*>(aChild);
+ nsIDocument* parentDoc = doc->GetParentDocument();
+ return parentDoc ? parentDoc->FindContentForSubDocument(doc) : nullptr;
+}
+
+// static
+bool
+nsContentUtils::ContentIsDescendantOf(const nsINode* aPossibleDescendant,
+ const nsINode* aPossibleAncestor)
+{
+ NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
+ NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
+
+ do {
+ if (aPossibleDescendant == aPossibleAncestor)
+ return true;
+ aPossibleDescendant = aPossibleDescendant->GetParentNode();
+ } while (aPossibleDescendant);
+
+ return false;
+}
+
+bool
+nsContentUtils::ContentIsHostIncludingDescendantOf(
+ const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor)
+{
+ NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
+ NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
+
+ do {
+ if (aPossibleDescendant == aPossibleAncestor)
+ return true;
+ if (aPossibleDescendant->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
+ aPossibleDescendant =
+ static_cast<const DocumentFragment*>(aPossibleDescendant)->GetHost();
+ } else {
+ aPossibleDescendant = aPossibleDescendant->GetParentNode();
+ }
+ } while (aPossibleDescendant);
+
+ return false;
+}
+
+// static
+bool
+nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
+ nsINode* aPossibleAncestor)
+{
+ NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
+ NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
+
+ do {
+ if (aPossibleDescendant == aPossibleAncestor)
+ return true;
+
+ aPossibleDescendant = GetCrossDocParentNode(aPossibleDescendant);
+ } while (aPossibleDescendant);
+
+ return false;
+}
+
+
+// static
+nsresult
+nsContentUtils::GetAncestors(nsINode* aNode,
+ nsTArray<nsINode*>& aArray)
+{
+ while (aNode) {
+ aArray.AppendElement(aNode);
+ aNode = aNode->GetParentNode();
+ }
+ return NS_OK;
+}
+
+// static
+nsresult
+nsContentUtils::GetAncestorsAndOffsets(nsIDOMNode* aNode,
+ int32_t aOffset,
+ nsTArray<nsIContent*>* aAncestorNodes,
+ nsTArray<int32_t>* aAncestorOffsets)
+{
+ NS_ENSURE_ARG_POINTER(aNode);
+
+ nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
+
+ if (!content) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!aAncestorNodes->IsEmpty()) {
+ NS_WARNING("aAncestorNodes is not empty");
+ aAncestorNodes->Clear();
+ }
+
+ if (!aAncestorOffsets->IsEmpty()) {
+ NS_WARNING("aAncestorOffsets is not empty");
+ aAncestorOffsets->Clear();
+ }
+
+ // insert the node itself
+ aAncestorNodes->AppendElement(content.get());
+ aAncestorOffsets->AppendElement(aOffset);
+
+ // insert all the ancestors
+ nsIContent* child = content;
+ nsIContent* parent = child->GetParent();
+ while (parent) {
+ aAncestorNodes->AppendElement(parent);
+ aAncestorOffsets->AppendElement(parent->IndexOf(child));
+ child = parent;
+ parent = parent->GetParent();
+ }
+
+ return NS_OK;
+}
+
+// static
+nsresult
+nsContentUtils::GetCommonAncestor(nsIDOMNode *aNode,
+ nsIDOMNode *aOther,
+ nsIDOMNode** aCommonAncestor)
+{
+ *aCommonAncestor = nullptr;
+
+ nsCOMPtr<nsINode> node1 = do_QueryInterface(aNode);
+ nsCOMPtr<nsINode> node2 = do_QueryInterface(aOther);
+
+ NS_ENSURE_TRUE(node1 && node2, NS_ERROR_UNEXPECTED);
+
+ nsINode* common = GetCommonAncestor(node1, node2);
+ NS_ENSURE_TRUE(common, NS_ERROR_NOT_AVAILABLE);
+
+ return CallQueryInterface(common, aCommonAncestor);
+}
+
+// static
+nsINode*
+nsContentUtils::GetCommonAncestor(nsINode* aNode1,
+ nsINode* aNode2)
+{
+ if (aNode1 == aNode2) {
+ return aNode1;
+ }
+
+ // Build the chain of parents
+ AutoTArray<nsINode*, 30> parents1, parents2;
+ do {
+ parents1.AppendElement(aNode1);
+ aNode1 = aNode1->GetParentNode();
+ } while (aNode1);
+ do {
+ parents2.AppendElement(aNode2);
+ aNode2 = aNode2->GetParentNode();
+ } while (aNode2);
+
+ // Find where the parent chain differs
+ uint32_t pos1 = parents1.Length();
+ uint32_t pos2 = parents2.Length();
+ nsINode* parent = nullptr;
+ uint32_t len;
+ for (len = std::min(pos1, pos2); len > 0; --len) {
+ nsINode* child1 = parents1.ElementAt(--pos1);
+ nsINode* child2 = parents2.ElementAt(--pos2);
+ if (child1 != child2) {
+ break;
+ }
+ parent = child1;
+ }
+
+ return parent;
+}
+
+/* static */
+bool
+nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2)
+{
+ return (aNode2->CompareDocumentPosition(*aNode1) &
+ (nsIDOMNode::DOCUMENT_POSITION_PRECEDING |
+ nsIDOMNode::DOCUMENT_POSITION_DISCONNECTED)) ==
+ nsIDOMNode::DOCUMENT_POSITION_PRECEDING;
+}
+
+/* static */
+int32_t
+nsContentUtils::ComparePoints(nsINode* aParent1, int32_t aOffset1,
+ nsINode* aParent2, int32_t aOffset2,
+ bool* aDisconnected)
+{
+ if (aParent1 == aParent2) {
+ return aOffset1 < aOffset2 ? -1 :
+ aOffset1 > aOffset2 ? 1 :
+ 0;
+ }
+
+ AutoTArray<nsINode*, 32> parents1, parents2;
+ nsINode* node1 = aParent1;
+ nsINode* node2 = aParent2;
+ do {
+ parents1.AppendElement(node1);
+ node1 = node1->GetParentNode();
+ } while (node1);
+ do {
+ parents2.AppendElement(node2);
+ node2 = node2->GetParentNode();
+ } while (node2);
+
+ uint32_t pos1 = parents1.Length() - 1;
+ uint32_t pos2 = parents2.Length() - 1;
+
+ bool disconnected = parents1.ElementAt(pos1) != parents2.ElementAt(pos2);
+ if (aDisconnected) {
+ *aDisconnected = disconnected;
+ }
+ if (disconnected) {
+ NS_ASSERTION(aDisconnected, "unexpected disconnected nodes");
+ return 1;
+ }
+
+ // Find where the parent chains differ
+ nsINode* parent = parents1.ElementAt(pos1);
+ uint32_t len;
+ for (len = std::min(pos1, pos2); len > 0; --len) {
+ nsINode* child1 = parents1.ElementAt(--pos1);
+ nsINode* child2 = parents2.ElementAt(--pos2);
+ if (child1 != child2) {
+ return parent->IndexOf(child1) < parent->IndexOf(child2) ? -1 : 1;
+ }
+ parent = child1;
+ }
+
+
+ // The parent chains never differed, so one of the nodes is an ancestor of
+ // the other
+
+ NS_ASSERTION(!pos1 || !pos2,
+ "should have run out of parent chain for one of the nodes");
+
+ if (!pos1) {
+ nsINode* child2 = parents2.ElementAt(--pos2);
+ return aOffset1 <= parent->IndexOf(child2) ? -1 : 1;
+ }
+
+ nsINode* child1 = parents1.ElementAt(--pos1);
+ return parent->IndexOf(child1) < aOffset2 ? -1 : 1;
+}
+
+/* static */
+int32_t
+nsContentUtils::ComparePoints(nsIDOMNode* aParent1, int32_t aOffset1,
+ nsIDOMNode* aParent2, int32_t aOffset2,
+ bool* aDisconnected)
+{
+ nsCOMPtr<nsINode> parent1 = do_QueryInterface(aParent1);
+ nsCOMPtr<nsINode> parent2 = do_QueryInterface(aParent2);
+ NS_ENSURE_TRUE(parent1 && parent2, -1);
+ return ComparePoints(parent1, aOffset1, parent2, aOffset2);
+}
+
+inline bool
+IsCharInSet(const char* aSet,
+ const char16_t aChar)
+{
+ char16_t ch;
+ while ((ch = *aSet)) {
+ if (aChar == char16_t(ch)) {
+ return true;
+ }
+ ++aSet;
+ }
+ return false;
+}
+
+/**
+ * This method strips leading/trailing chars, in given set, from string.
+ */
+
+// static
+const nsDependentSubstring
+nsContentUtils::TrimCharsInSet(const char* aSet,
+ const nsAString& aValue)
+{
+ nsAString::const_iterator valueCurrent, valueEnd;
+
+ aValue.BeginReading(valueCurrent);
+ aValue.EndReading(valueEnd);
+
+ // Skip characters in the beginning
+ while (valueCurrent != valueEnd) {
+ if (!IsCharInSet(aSet, *valueCurrent)) {
+ break;
+ }
+ ++valueCurrent;
+ }
+
+ if (valueCurrent != valueEnd) {
+ for (;;) {
+ --valueEnd;
+ if (!IsCharInSet(aSet, *valueEnd)) {
+ break;
+ }
+ }
+ ++valueEnd; // Step beyond the last character we want in the value.
+ }
+
+ // valueEnd should point to the char after the last to copy
+ return Substring(valueCurrent, valueEnd);
+}
+
+/**
+ * This method strips leading and trailing whitespace from a string.
+ */
+
+// static
+template<bool IsWhitespace(char16_t)>
+const nsDependentSubstring
+nsContentUtils::TrimWhitespace(const nsAString& aStr, bool aTrimTrailing)
+{
+ nsAString::const_iterator start, end;
+
+ aStr.BeginReading(start);
+ aStr.EndReading(end);
+
+ // Skip whitespace characters in the beginning
+ while (start != end && IsWhitespace(*start)) {
+ ++start;
+ }
+
+ if (aTrimTrailing) {
+ // Skip whitespace characters in the end.
+ while (end != start) {
+ --end;
+
+ if (!IsWhitespace(*end)) {
+ // Step back to the last non-whitespace character.
+ ++end;
+
+ break;
+ }
+ }
+ }
+
+ // Return a substring for the string w/o leading and/or trailing
+ // whitespace
+
+ return Substring(start, end);
+}
+
+// Declaring the templates we are going to use avoid linking issues without
+// inlining the method. Considering there is not so much spaces checking
+// methods we can consider this to be better than inlining.
+template
+const nsDependentSubstring
+nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, bool);
+template
+const nsDependentSubstring
+nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(const nsAString&, bool);
+template
+const nsDependentSubstring
+nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespaceOrNBSP>(const nsAString&, bool);
+
+static inline void KeyAppendSep(nsACString& aKey)
+{
+ if (!aKey.IsEmpty()) {
+ aKey.Append('>');
+ }
+}
+
+static inline void KeyAppendString(const nsAString& aString, nsACString& aKey)
+{
+ KeyAppendSep(aKey);
+
+ // Could escape separator here if collisions happen. > is not a legal char
+ // for a name or type attribute, so we should be safe avoiding that extra work.
+
+ AppendUTF16toUTF8(aString, aKey);
+}
+
+static inline void KeyAppendString(const nsACString& aString, nsACString& aKey)
+{
+ KeyAppendSep(aKey);
+
+ // Could escape separator here if collisions happen. > is not a legal char
+ // for a name or type attribute, so we should be safe avoiding that extra work.
+
+ aKey.Append(aString);
+}
+
+static inline void KeyAppendInt(int32_t aInt, nsACString& aKey)
+{
+ KeyAppendSep(aKey);
+
+ aKey.Append(nsPrintfCString("%d", aInt));
+}
+
+static inline bool IsAutocompleteOff(const nsIContent* aElement)
+{
+ return aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocomplete,
+ NS_LITERAL_STRING("off"), eIgnoreCase);
+}
+
+/*static*/ nsresult
+nsContentUtils::GenerateStateKey(nsIContent* aContent,
+ const nsIDocument* aDocument,
+ nsACString& aKey)
+{
+ aKey.Truncate();
+
+ uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
+
+ // We must have content if we're not using a special state id
+ NS_ENSURE_TRUE(aContent, NS_ERROR_FAILURE);
+
+ // Don't capture state for anonymous content
+ if (aContent->IsInAnonymousSubtree()) {
+ return NS_OK;
+ }
+
+ if (IsAutocompleteOff(aContent)) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIHTMLDocument> htmlDocument =
+ do_QueryInterface(aContent->GetUncomposedDoc());
+
+ KeyAppendInt(partID, aKey); // first append a partID
+ bool generatedUniqueKey = false;
+
+ if (htmlDocument) {
+ // Flush our content model so it'll be up to date
+ // If this becomes unnecessary and the following line is removed,
+ // please also remove the corresponding flush operation from
+ // nsHtml5TreeBuilderCppSupplement.h. (Look for "See bug 497861." there.)
+ aContent->GetUncomposedDoc()->FlushPendingNotifications(Flush_Content);
+
+ nsContentList *htmlForms = htmlDocument->GetForms();
+ nsContentList *htmlFormControls = htmlDocument->GetFormControls();
+
+ NS_ENSURE_TRUE(htmlForms && htmlFormControls, NS_ERROR_OUT_OF_MEMORY);
+
+ // If we have a form control and can calculate form information, use that
+ // as the key - it is more reliable than just recording position in the
+ // DOM.
+ // XXXbz Is it, really? We have bugs on this, I think...
+ // Important to have a unique key, and tag/type/name may not be.
+ //
+ // If the control has a form, the format of the key is:
+ // f>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
+ // else:
+ // d>type>IndOfControlInDoc>name
+ //
+ // XXX We don't need to use index if name is there
+ // XXXbz We don't? Why not? I don't follow.
+ //
+ nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
+ if (control && htmlFormControls && htmlForms) {
+
+ // Append the control type
+ KeyAppendInt(control->GetType(), aKey);
+
+ // If in a form, add form name / index of form / index in form
+ int32_t index = -1;
+ Element *formElement = control->GetFormElement();
+ if (formElement) {
+ if (IsAutocompleteOff(formElement)) {
+ aKey.Truncate();
+ return NS_OK;
+ }
+
+ KeyAppendString(NS_LITERAL_CSTRING("f"), aKey);
+
+ // Append the index of the form in the document
+ index = htmlForms->IndexOf(formElement, false);
+ if (index <= -1) {
+ //
+ // XXX HACK this uses some state that was dumped into the document
+ // specifically to fix bug 138892. What we are trying to do is *guess*
+ // which form this control's state is found in, with the highly likely
+ // guess that the highest form parsed so far is the one.
+ // This code should not be on trunk, only branch.
+ //
+ index = htmlDocument->GetNumFormsSynchronous() - 1;
+ }
+ if (index > -1) {
+ KeyAppendInt(index, aKey);
+
+ // Append the index of the control in the form
+ nsCOMPtr<nsIForm> form(do_QueryInterface(formElement));
+ index = form->IndexOfControl(control);
+
+ if (index > -1) {
+ KeyAppendInt(index, aKey);
+ generatedUniqueKey = true;
+ }
+ }
+
+ // Append the form name
+ nsAutoString formName;
+ formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
+ KeyAppendString(formName, aKey);
+
+ } else {
+
+ KeyAppendString(NS_LITERAL_CSTRING("d"), aKey);
+
+ // If not in a form, add index of control in document
+ // Less desirable than indexing by form info.
+
+ // Hash by index of control in doc (we are not in a form)
+ // These are important as they are unique, and type/name may not be.
+
+ // We have to flush sink notifications at this point to make
+ // sure that htmlFormControls is up to date.
+ index = htmlFormControls->IndexOf(aContent, true);
+ if (index > -1) {
+ KeyAppendInt(index, aKey);
+ generatedUniqueKey = true;
+ }
+ }
+
+ // Append the control name
+ nsAutoString name;
+ aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
+ KeyAppendString(name, aKey);
+ }
+ }
+
+ if (!generatedUniqueKey) {
+ // Either we didn't have a form control or we aren't in an HTML document so
+ // we can't figure out form info. Append the tag name if it's an element
+ // to avoid restoring state for one type of element on another type.
+ if (aContent->IsElement()) {
+ KeyAppendString(nsDependentAtomString(aContent->NodeInfo()->NameAtom()),
+ aKey);
+ }
+ else {
+ // Append a character that is not "d" or "f" to disambiguate from
+ // the case when we were a form control in an HTML document.
+ KeyAppendString(NS_LITERAL_CSTRING("o"), aKey);
+ }
+
+ // Now start at aContent and append the indices of it and all its ancestors
+ // in their containers. That should at least pin down its position in the
+ // DOM...
+ nsINode* parent = aContent->GetParentNode();
+ nsINode* content = aContent;
+ while (parent) {
+ KeyAppendInt(parent->IndexOf(content), aKey);
+ content = parent;
+ parent = content->GetParentNode();
+ }
+ }
+
+ return NS_OK;
+}
+
+// static
+nsIPrincipal*
+nsContentUtils::SubjectPrincipal()
+{
+ MOZ_ASSERT(IsInitialized());
+ MOZ_ASSERT(NS_IsMainThread());
+ JSContext* cx = GetCurrentJSContext();
+ if (!cx) {
+ MOZ_CRASH("Accessing the Subject Principal without an AutoJSAPI on the stack is forbidden");
+ }
+
+ JSCompartment *compartment = js::GetContextCompartment(cx);
+
+ // When an AutoJSAPI is instantiated, we are in a null compartment until the
+ // first JSAutoCompartment, which is kind of a purgatory as far as permissions
+ // go. It would be nice to just hard-abort if somebody does a security check
+ // in this purgatory zone, but that would be too fragile, since it could be
+ // triggered by random IsCallerChrome() checks 20-levels deep.
+ //
+ // So we want to return _something_ here - and definitely not the System
+ // Principal, since that would make an AutoJSAPI a very dangerous thing to
+ // instantiate.
+ //
+ // The natural thing to return is a null principal. Ideally, we'd return a
+ // different null principal each time, to avoid any unexpected interactions
+ // when the principal accidentally gets inherited somewhere. But
+ // GetSubjectPrincipal doesn't return strong references, so there's no way to
+ // sanely manage the lifetime of multiple null principals.
+ //
+ // So we use a singleton null principal. To avoid it being accidentally
+ // inherited and becoming a "real" subject or object principal, we do a
+ // release-mode assert during compartment creation against using this
+ // principal on an actual global.
+ if (!compartment) {
+ return sNullSubjectPrincipal;
+ }
+
+ JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
+ return nsJSPrincipals::get(principals);
+}
+
+// static
+nsIPrincipal*
+nsContentUtils::ObjectPrincipal(JSObject* aObj)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+#ifdef DEBUG
+ JS::AssertObjectBelongsToCurrentThread(aObj);
+#endif
+
+ // This is duplicated from nsScriptSecurityManager. We don't call through there
+ // because the API unnecessarily requires a JSContext for historical reasons.
+ JSCompartment *compartment = js::GetObjectCompartment(aObj);
+ JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
+ return nsJSPrincipals::get(principals);
+}
+
+// static
+nsresult
+nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
+ const nsAString& aSpec,
+ nsIDocument* aDocument,
+ nsIURI* aBaseURI)
+{
+ return NS_NewURI(aResult, aSpec,
+ aDocument ? aDocument->GetDocumentCharacterSet().get() : nullptr,
+ aBaseURI, sIOService);
+}
+
+// static
+bool
+nsContentUtils::IsCustomElementName(nsIAtom* aName)
+{
+ // A valid custom element name is a sequence of characters name which
+ // must match the PotentialCustomElementName production:
+ // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
+ const char16_t* name = aName->GetUTF16String();
+ uint32_t len = aName->GetLength();
+ bool hasDash = false;
+
+ if (!len || name[0] < 'a' || name[0] > 'z') {
+ return false;
+ }
+
+ uint32_t i = 1;
+ while (i < len) {
+ if (NS_IS_HIGH_SURROGATE(name[i]) && i + 1 < len &&
+ NS_IS_LOW_SURROGATE(name[i + 1])) {
+ // Merged two 16-bit surrogate pairs into code point.
+ char32_t code = SURROGATE_TO_UCS4(name[i], name[i + 1]);
+
+ if (code < 0x10000 || code > 0xEFFFF) {
+ return false;
+ }
+
+ i += 2;
+ } else {
+ if (name[i] == '-') {
+ hasDash = true;
+ }
+
+ if (name[i] != '-' && name[i] != '.' &&
+ name[i] != '_' && name[i] != 0xB7 &&
+ (name[i] < '0' || name[i] > '9') &&
+ (name[i] < 'a' || name[i] > 'z') &&
+ (name[i] < 0xC0 || name[i] > 0xD6) &&
+ (name[i] < 0xF8 || name[i] > 0x37D) &&
+ (name[i] < 0x37F || name[i] > 0x1FFF) &&
+ (name[i] < 0x200C || name[i] > 0x200D) &&
+ (name[i] < 0x203F || name[i] > 0x2040) &&
+ (name[i] < 0x2070 || name[i] > 0x218F) &&
+ (name[i] < 0x2C00 || name[i] > 0x2FEF) &&
+ (name[i] < 0x3001 || name[i] > 0xD7FF) &&
+ (name[i] < 0xF900 || name[i] > 0xFDCF) &&
+ (name[i] < 0xFDF0 || name[i] > 0xFFFD)) {
+ return false;
+ }
+
+ i++;
+ }
+ }
+
+ if (!hasDash) {
+ return false;
+ }
+
+ // The custom element name must not be one of the following values:
+ // annotation-xml
+ // color-profile
+ // font-face
+ // font-face-src
+ // font-face-uri
+ // font-face-format
+ // font-face-name
+ // missing-glyph
+ return aName != nsGkAtoms::annotation_xml_ &&
+ aName != nsGkAtoms::colorProfile &&
+ aName != nsGkAtoms::font_face &&
+ aName != nsGkAtoms::font_face_src &&
+ aName != nsGkAtoms::font_face_uri &&
+ aName != nsGkAtoms::font_face_format &&
+ aName != nsGkAtoms::font_face_name &&
+ aName != nsGkAtoms::missingGlyph;
+}
+
+// static
+nsresult
+nsContentUtils::CheckQName(const nsAString& aQualifiedName,
+ bool aNamespaceAware,
+ const char16_t** aColon)
+{
+ const char* colon = nullptr;
+ const char16_t* begin = aQualifiedName.BeginReading();
+ const char16_t* end = aQualifiedName.EndReading();
+
+ int result = MOZ_XMLCheckQName(reinterpret_cast<const char*>(begin),
+ reinterpret_cast<const char*>(end),
+ aNamespaceAware, &colon);
+
+ if (!result) {
+ if (aColon) {
+ *aColon = reinterpret_cast<const char16_t*>(colon);
+ }
+
+ return NS_OK;
+ }
+
+ // MOZ_EXPAT_EMPTY_QNAME || MOZ_EXPAT_INVALID_CHARACTER
+ if (result == (1 << 0) || result == (1 << 1)) {
+ return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
+ }
+
+ return NS_ERROR_DOM_NAMESPACE_ERR;
+}
+
+//static
+nsresult
+nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
+ const nsAFlatString& aQName,
+ int32_t *aNamespace, nsIAtom **aLocalName)
+{
+ const char16_t* colon;
+ nsresult rv = nsContentUtils::CheckQName(aQName, true, &colon);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (colon) {
+ const char16_t* end;
+ aQName.EndReading(end);
+ nsAutoString nameSpace;
+ rv = aNamespaceResolver->LookupNamespaceURIInternal(Substring(aQName.get(),
+ colon),
+ nameSpace);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aNamespace = NameSpaceManager()->GetNameSpaceID(nameSpace,
+ nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
+ if (*aNamespace == kNameSpaceID_Unknown)
+ return NS_ERROR_FAILURE;
+
+ *aLocalName = NS_Atomize(Substring(colon + 1, end)).take();
+ }
+ else {
+ *aNamespace = kNameSpaceID_None;
+ *aLocalName = NS_Atomize(aQName).take();
+ }
+ NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
+ return NS_OK;
+}
+
+// static
+nsresult
+nsContentUtils::GetNodeInfoFromQName(const nsAString& aNamespaceURI,
+ const nsAString& aQualifiedName,
+ nsNodeInfoManager* aNodeInfoManager,
+ uint16_t aNodeType,
+ mozilla::dom::NodeInfo** aNodeInfo)
+{
+ const nsAFlatString& qName = PromiseFlatString(aQualifiedName);
+ const char16_t* colon;
+ nsresult rv = nsContentUtils::CheckQName(qName, true, &colon);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t nsID;
+ sNameSpaceManager->RegisterNameSpace(aNamespaceURI, nsID);
+ if (colon) {
+ const char16_t* end;
+ qName.EndReading(end);
+
+ nsCOMPtr<nsIAtom> prefix = NS_Atomize(Substring(qName.get(), colon));
+
+ rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix,
+ nsID, aNodeType, aNodeInfo);
+ }
+ else {
+ rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID,
+ aNodeType, aNodeInfo);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
+ (*aNodeInfo)->GetPrefixAtom(),
+ (*aNodeInfo)->NamespaceID()) ?
+ NS_OK : NS_ERROR_DOM_NAMESPACE_ERR;
+}
+
+// static
+void
+nsContentUtils::SplitExpatName(const char16_t *aExpatName, nsIAtom **aPrefix,
+ nsIAtom **aLocalName, int32_t* aNameSpaceID)
+{
+ /**
+ * Expat can send the following:
+ * localName
+ * namespaceURI<separator>localName
+ * namespaceURI<separator>localName<separator>prefix
+ *
+ * and we use 0xFFFF for the <separator>.
+ *
+ */
+
+ const char16_t *uriEnd = nullptr;
+ const char16_t *nameEnd = nullptr;
+ const char16_t *pos;
+ for (pos = aExpatName; *pos; ++pos) {
+ if (*pos == 0xFFFF) {
+ if (uriEnd) {
+ nameEnd = pos;
+ }
+ else {
+ uriEnd = pos;
+ }
+ }
+ }
+
+ const char16_t *nameStart;
+ if (uriEnd) {
+ if (sNameSpaceManager) {
+ sNameSpaceManager->RegisterNameSpace(nsDependentSubstring(aExpatName,
+ uriEnd),
+ *aNameSpaceID);
+ }
+ else {
+ *aNameSpaceID = kNameSpaceID_Unknown;
+ }
+
+ nameStart = (uriEnd + 1);
+ if (nameEnd) {
+ const char16_t *prefixStart = nameEnd + 1;
+ *aPrefix = NS_Atomize(Substring(prefixStart, pos)).take();
+ }
+ else {
+ nameEnd = pos;
+ *aPrefix = nullptr;
+ }
+ }
+ else {
+ *aNameSpaceID = kNameSpaceID_None;
+ nameStart = aExpatName;
+ nameEnd = pos;
+ *aPrefix = nullptr;
+ }
+ *aLocalName = NS_Atomize(Substring(nameStart, nameEnd)).take();
+}
+
+// static
+nsPresContext*
+nsContentUtils::GetContextForContent(const nsIContent* aContent)
+{
+ nsIDocument* doc = aContent->GetComposedDoc();
+ if (doc) {
+ nsIPresShell *presShell = doc->GetShell();
+ if (presShell) {
+ return presShell->GetPresContext();
+ }
+ }
+ return nullptr;
+}
+
+// static
+bool
+nsContentUtils::CanLoadImage(nsIURI* aURI, nsISupports* aContext,
+ nsIDocument* aLoadingDocument,
+ nsIPrincipal* aLoadingPrincipal,
+ int16_t* aImageBlockingStatus,
+ uint32_t aContentType)
+{
+ NS_PRECONDITION(aURI, "Must have a URI");
+ NS_PRECONDITION(aLoadingDocument, "Must have a document");
+ NS_PRECONDITION(aLoadingPrincipal, "Must have a loading principal");
+
+ nsresult rv;
+
+ uint32_t appType = nsIDocShell::APP_TYPE_UNKNOWN;
+
+ {
+ nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = aLoadingDocument->GetDocShell();
+ if (docShellTreeItem) {
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ docShellTreeItem->GetRootTreeItem(getter_AddRefs(root));
+
+ nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
+
+ if (!docShell || NS_FAILED(docShell->GetAppType(&appType))) {
+ appType = nsIDocShell::APP_TYPE_UNKNOWN;
+ }
+ }
+ }
+
+ if (appType != nsIDocShell::APP_TYPE_EDITOR) {
+ // Editor apps get special treatment here, editors can load images
+ // from anywhere. This allows editor to insert images from file://
+ // into documents that are being edited.
+ rv = sSecurityManager->
+ CheckLoadURIWithPrincipal(aLoadingPrincipal, aURI,
+ nsIScriptSecurityManager::ALLOW_CHROME);
+ if (NS_FAILED(rv)) {
+ if (aImageBlockingStatus) {
+ // Reject the request itself, not all requests to the relevant
+ // server...
+ *aImageBlockingStatus = nsIContentPolicy::REJECT_REQUEST;
+ }
+ return false;
+ }
+ }
+
+ int16_t decision = nsIContentPolicy::ACCEPT;
+
+ rv = NS_CheckContentLoadPolicy(aContentType,
+ aURI,
+ aLoadingPrincipal,
+ aContext,
+ EmptyCString(), //mime guess
+ nullptr, //extra
+ &decision,
+ GetContentPolicy(),
+ sSecurityManager);
+
+ if (aImageBlockingStatus) {
+ *aImageBlockingStatus =
+ NS_FAILED(rv) ? nsIContentPolicy::REJECT_REQUEST : decision;
+ }
+ return NS_FAILED(rv) ? false : NS_CP_ACCEPTED(decision);
+}
+
+// static
+mozilla::PrincipalOriginAttributes
+nsContentUtils::GetOriginAttributes(nsIDocument* aDocument)
+{
+ if (!aDocument) {
+ return mozilla::PrincipalOriginAttributes();
+ }
+
+ nsCOMPtr<nsILoadGroup> loadGroup = aDocument->GetDocumentLoadGroup();
+ if (loadGroup) {
+ return GetOriginAttributes(loadGroup);
+ }
+
+ mozilla::PrincipalOriginAttributes attrs;
+ mozilla::NeckoOriginAttributes nattrs;
+ nsCOMPtr<nsIChannel> channel = aDocument->GetChannel();
+ if (channel && NS_GetOriginAttributes(channel, nattrs)) {
+ attrs.InheritFromNecko(nattrs);
+ }
+ return attrs;
+}
+
+// static
+mozilla::PrincipalOriginAttributes
+nsContentUtils::GetOriginAttributes(nsILoadGroup* aLoadGroup)
+{
+ if (!aLoadGroup) {
+ return mozilla::PrincipalOriginAttributes();
+ }
+ mozilla::PrincipalOriginAttributes attrs;
+ mozilla::DocShellOriginAttributes dsattrs;
+ nsCOMPtr<nsIInterfaceRequestor> callbacks;
+ aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+ if (callbacks) {
+ nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
+ if (loadContext && loadContext->GetOriginAttributes(dsattrs)) {
+ attrs.InheritFromDocShellToDoc(dsattrs, nullptr);
+ }
+ }
+ return attrs;
+}
+
+// static
+bool
+nsContentUtils::IsInPrivateBrowsing(nsIDocument* aDoc)
+{
+ if (!aDoc) {
+ return false;
+ }
+
+ nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
+ if (loadGroup) {
+ return IsInPrivateBrowsing(loadGroup);
+ }
+
+ nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
+ return channel && NS_UsePrivateBrowsing(channel);
+}
+
+// static
+bool
+nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup)
+{
+ if (!aLoadGroup) {
+ return false;
+ }
+ bool isPrivate = false;
+ nsCOMPtr<nsIInterfaceRequestor> callbacks;
+ aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+ if (callbacks) {
+ nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
+ isPrivate = loadContext && loadContext->UsePrivateBrowsing();
+ }
+ return isPrivate;
+}
+
+bool
+nsContentUtils::DocumentInactiveForImageLoads(nsIDocument* aDocument)
+{
+ if (aDocument && !IsChromeDoc(aDocument) && !aDocument->IsResourceDoc()) {
+ nsCOMPtr<nsPIDOMWindowInner> win =
+ do_QueryInterface(aDocument->GetScopeObject());
+ return !win || !win->GetDocShell();
+ }
+ return false;
+}
+
+imgLoader*
+nsContentUtils::GetImgLoaderForDocument(nsIDocument* aDoc)
+{
+ NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
+
+ if (!aDoc) {
+ return imgLoader::NormalLoader();
+ }
+ bool isPrivate = IsInPrivateBrowsing(aDoc);
+ return isPrivate ? imgLoader::PrivateBrowsingLoader()
+ : imgLoader::NormalLoader();
+}
+
+// static
+imgLoader*
+nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
+ nsIDocument* aContext)
+{
+ NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
+
+ if (!aChannel) {
+ return imgLoader::NormalLoader();
+ }
+ nsCOMPtr<nsILoadContext> context;
+ NS_QueryNotificationCallbacks(aChannel, context);
+ return context && context->UsePrivateBrowsing() ?
+ imgLoader::PrivateBrowsingLoader() :
+ imgLoader::NormalLoader();
+}
+
+// static
+bool
+nsContentUtils::IsImageInCache(nsIURI* aURI, nsIDocument* aDocument)
+{
+ imgILoader* loader = GetImgLoaderForDocument(aDocument);
+ nsCOMPtr<imgICache> cache = do_QueryInterface(loader);
+
+ // If something unexpected happened we return false, otherwise if props
+ // is set, the image is cached and we return true
+ nsCOMPtr<nsIProperties> props;
+ nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDocument);
+ nsresult rv = cache->FindEntryProperties(aURI, domDoc, getter_AddRefs(props));
+ return (NS_SUCCEEDED(rv) && props);
+}
+
+// static
+nsresult
+nsContentUtils::LoadImage(nsIURI* aURI, nsINode* aContext,
+ nsIDocument* aLoadingDocument,
+ nsIPrincipal* aLoadingPrincipal, nsIURI* aReferrer,
+ net::ReferrerPolicy aReferrerPolicy,
+ imgINotificationObserver* aObserver, int32_t aLoadFlags,
+ const nsAString& initiatorType,
+ imgRequestProxy** aRequest,
+ uint32_t aContentPolicyType)
+{
+ NS_PRECONDITION(aURI, "Must have a URI");
+ NS_PRECONDITION(aContext, "Must have a context");
+ NS_PRECONDITION(aLoadingDocument, "Must have a document");
+ NS_PRECONDITION(aLoadingPrincipal, "Must have a principal");
+ NS_PRECONDITION(aRequest, "Null out param");
+
+ imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
+ if (!imgLoader) {
+ // nothing we can do here
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
+
+ nsIURI *documentURI = aLoadingDocument->GetDocumentURI();
+
+ NS_ASSERTION(loadGroup || IsFontTableURI(documentURI),
+ "Could not get loadgroup; onload may fire too early");
+
+ // Make the URI immutable so people won't change it under us
+ NS_TryToSetImmutable(aURI);
+
+ // XXXbz using "documentURI" for the initialDocumentURI is not quite
+ // right, but the best we can do here...
+ return imgLoader->LoadImage(aURI, /* uri to load */
+ documentURI, /* initialDocumentURI */
+ aReferrer, /* referrer */
+ aReferrerPolicy, /* referrer policy */
+ aLoadingPrincipal, /* loading principal */
+ loadGroup, /* loadgroup */
+ aObserver, /* imgINotificationObserver */
+ aContext, /* loading context */
+ aLoadingDocument, /* uniquification key */
+ aLoadFlags, /* load flags */
+ nullptr, /* cache key */
+ aContentPolicyType, /* content policy type */
+ initiatorType, /* the load initiator */
+ aRequest);
+}
+
+// static
+already_AddRefed<imgIContainer>
+nsContentUtils::GetImageFromContent(nsIImageLoadingContent* aContent,
+ imgIRequest **aRequest)
+{
+ if (aRequest) {
+ *aRequest = nullptr;
+ }
+
+ NS_ENSURE_TRUE(aContent, nullptr);
+
+ nsCOMPtr<imgIRequest> imgRequest;
+ aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+ getter_AddRefs(imgRequest));
+ if (!imgRequest) {
+ return nullptr;
+ }
+
+ nsCOMPtr<imgIContainer> imgContainer;
+ imgRequest->GetImage(getter_AddRefs(imgContainer));
+
+ if (!imgContainer) {
+ return nullptr;
+ }
+
+ if (aRequest) {
+ imgRequest.swap(*aRequest);
+ }
+
+ return imgContainer.forget();
+}
+
+//static
+already_AddRefed<imgRequestProxy>
+nsContentUtils::GetStaticRequest(imgRequestProxy* aRequest)
+{
+ NS_ENSURE_TRUE(aRequest, nullptr);
+ RefPtr<imgRequestProxy> retval;
+ aRequest->GetStaticRequest(getter_AddRefs(retval));
+ return retval.forget();
+}
+
+// static
+bool
+nsContentUtils::ContentIsDraggable(nsIContent* aContent)
+{
+ nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(aContent);
+ if (htmlElement) {
+ bool draggable = false;
+ htmlElement->GetDraggable(&draggable);
+ if (draggable)
+ return true;
+
+ if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
+ nsGkAtoms::_false, eIgnoreCase))
+ return false;
+ }
+
+ // special handling for content area image and link dragging
+ return IsDraggableImage(aContent) || IsDraggableLink(aContent);
+}
+
+// static
+bool
+nsContentUtils::IsDraggableImage(nsIContent* aContent)
+{
+ NS_PRECONDITION(aContent, "Must have content node to test");
+
+ nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(aContent));
+ if (!imageContent) {
+ return false;
+ }
+
+ nsCOMPtr<imgIRequest> imgRequest;
+ imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+ getter_AddRefs(imgRequest));
+
+ // XXXbz It may be draggable even if the request resulted in an error. Why?
+ // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
+ return imgRequest != nullptr;
+}
+
+// static
+bool
+nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
+ nsCOMPtr<nsIURI> absURI;
+ return aContent->IsLink(getter_AddRefs(absURI));
+}
+
+// static
+nsresult
+nsContentUtils::NameChanged(mozilla::dom::NodeInfo* aNodeInfo, nsIAtom* aName,
+ mozilla::dom::NodeInfo** aResult)
+{
+ nsNodeInfoManager *niMgr = aNodeInfo->NodeInfoManager();
+
+ *aResult = niMgr->GetNodeInfo(aName, aNodeInfo->GetPrefixAtom(),
+ aNodeInfo->NamespaceID(),
+ aNodeInfo->NodeType(),
+ aNodeInfo->GetExtraName()).take();
+ return NS_OK;
+}
+
+
+static bool
+TestSitePerm(nsIPrincipal* aPrincipal, const char* aType, uint32_t aPerm, bool aExactHostMatch)
+{
+ if (!aPrincipal) {
+ // We always deny (i.e. don't allow) the permission if we don't have a
+ // principal.
+ return aPerm != nsIPermissionManager::ALLOW_ACTION;
+ }
+
+ nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
+ NS_ENSURE_TRUE(permMgr, false);
+
+ uint32_t perm;
+ nsresult rv;
+ if (aExactHostMatch) {
+ rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
+ } else {
+ rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
+ }
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return perm == aPerm;
+}
+
+bool
+nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal, const char* aType)
+{
+ return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION, false);
+}
+
+bool
+nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal, const char* aType)
+{
+ return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION, false);
+}
+
+bool
+nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal, const char* aType)
+{
+ return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION, true);
+}
+
+bool
+nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal, const char* aType)
+{
+ return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION, true);
+}
+
+static const char *gEventNames[] = {"event"};
+static const char *gSVGEventNames[] = {"evt"};
+// for b/w compat, the first name to onerror is still 'event', even though it
+// is actually the error message
+static const char *gOnErrorNames[] = {"event", "source", "lineno",
+ "colno", "error"};
+
+// static
+void
+nsContentUtils::GetEventArgNames(int32_t aNameSpaceID,
+ nsIAtom *aEventName,
+ bool aIsForWindow,
+ uint32_t *aArgCount,
+ const char*** aArgArray)
+{
+#define SET_EVENT_ARG_NAMES(names) \
+ *aArgCount = sizeof(names)/sizeof(names[0]); \
+ *aArgArray = names;
+
+ // JSEventHandler is what does the arg magic for onerror, and it does
+ // not seem to take the namespace into account. So we let onerror in all
+ // namespaces get the 3 arg names.
+ if (aEventName == nsGkAtoms::onerror && aIsForWindow) {
+ SET_EVENT_ARG_NAMES(gOnErrorNames);
+ } else if (aNameSpaceID == kNameSpaceID_SVG) {
+ SET_EVENT_ARG_NAMES(gSVGEventNames);
+ } else {
+ SET_EVENT_ARG_NAMES(gEventNames);
+ }
+}
+
+static const char gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT][56] = {
+ // Must line up with the enum values in |PropertiesFile| enum.
+ "chrome://global/locale/css.properties",
+ "chrome://global/locale/xbl.properties",
+ "chrome://global/locale/xul.properties",
+ "chrome://global/locale/layout_errors.properties",
+ "chrome://global/locale/layout/HtmlForm.properties",
+ "chrome://global/locale/printing.properties",
+ "chrome://global/locale/dom/dom.properties",
+ "chrome://global/locale/layout/htmlparser.properties",
+ "chrome://global/locale/svg/svg.properties",
+ "chrome://branding/locale/brand.properties",
+ "chrome://global/locale/commonDialogs.properties",
+ "chrome://global/locale/mathml/mathml.properties",
+ "chrome://global/locale/security/security.properties",
+ "chrome://necko/locale/necko.properties"
+};
+
+/* static */ nsresult
+nsContentUtils::EnsureStringBundle(PropertiesFile aFile)
+{
+ if (!sStringBundles[aFile]) {
+ if (!sStringBundleService) {
+ nsresult rv =
+ CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ nsIStringBundle *bundle;
+ nsresult rv =
+ sStringBundleService->CreateBundle(gPropertiesFiles[aFile], &bundle);
+ NS_ENSURE_SUCCESS(rv, rv);
+ sStringBundles[aFile] = bundle; // transfer ownership
+ }
+ return NS_OK;
+}
+
+/* static */
+nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
+ const char* aKey,
+ nsXPIDLString& aResult)
+{
+ nsresult rv = EnsureStringBundle(aFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsIStringBundle *bundle = sStringBundles[aFile];
+
+ return bundle->GetStringFromName(NS_ConvertASCIItoUTF16(aKey).get(),
+ getter_Copies(aResult));
+}
+
+/* static */
+nsresult nsContentUtils::FormatLocalizedString(PropertiesFile aFile,
+ const char* aKey,
+ const char16_t **aParams,
+ uint32_t aParamsLength,
+ nsXPIDLString& aResult)
+{
+ nsresult rv = EnsureStringBundle(aFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsIStringBundle *bundle = sStringBundles[aFile];
+
+ return bundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aKey).get(),
+ aParams, aParamsLength,
+ getter_Copies(aResult));
+}
+
+/* static */
+nsresult nsContentUtils::FormatLocalizedString(
+ PropertiesFile aFile,
+ const char* aKey,
+ const nsTArray<nsString>& aParamArray,
+ nsXPIDLString& aResult)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ UniquePtr<const char16_t*[]> params;
+ uint32_t paramsLength = aParamArray.Length();
+ if (paramsLength > 0) {
+ params = MakeUnique<const char16_t*[]>(paramsLength);
+ for (uint32_t i = 0; i < paramsLength; ++i) {
+ params[i] = aParamArray[i].get();
+ }
+ }
+ return FormatLocalizedString(aFile, aKey, params.get(), paramsLength,
+ aResult);
+}
+
+
+/* static */ void
+nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
+ const char * classification)
+{
+ nsCOMPtr<nsIScriptError> scriptError =
+ do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
+ if (scriptError) {
+ nsCOMPtr<nsIConsoleService> console =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ if (console && NS_SUCCEEDED(scriptError->Init(aErrorText, EmptyString(),
+ EmptyString(), 0, 0,
+ nsIScriptError::errorFlag,
+ classification))) {
+ console->LogMessage(scriptError);
+ }
+ }
+}
+
+/* static */ nsresult
+nsContentUtils::ReportToConsole(uint32_t aErrorFlags,
+ const nsACString& aCategory,
+ const nsIDocument* aDocument,
+ PropertiesFile aFile,
+ const char *aMessageName,
+ const char16_t **aParams,
+ uint32_t aParamsLength,
+ nsIURI* aURI,
+ const nsAFlatString& aSourceLine,
+ uint32_t aLineNumber,
+ uint32_t aColumnNumber)
+{
+ NS_ASSERTION((aParams && aParamsLength) || (!aParams && !aParamsLength),
+ "Supply either both parameters and their number or no"
+ "parameters and 0.");
+
+ nsresult rv;
+ nsXPIDLString errorText;
+ if (aParams) {
+ rv = FormatLocalizedString(aFile, aMessageName, aParams, aParamsLength,
+ errorText);
+ }
+ else {
+ rv = GetLocalizedString(aFile, aMessageName, errorText);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return ReportToConsoleNonLocalized(errorText, aErrorFlags, aCategory,
+ aDocument, aURI, aSourceLine,
+ aLineNumber, aColumnNumber);
+}
+
+
+/* static */ nsresult
+nsContentUtils::ReportToConsoleNonLocalized(const nsAString& aErrorText,
+ uint32_t aErrorFlags,
+ const nsACString& aCategory,
+ const nsIDocument* aDocument,
+ nsIURI* aURI,
+ const nsAFlatString& aSourceLine,
+ uint32_t aLineNumber,
+ uint32_t aColumnNumber,
+ MissingErrorLocationMode aLocationMode)
+{
+ uint64_t innerWindowID = 0;
+ if (aDocument) {
+ if (!aURI) {
+ aURI = aDocument->GetDocumentURI();
+ }
+ innerWindowID = aDocument->InnerWindowID();
+ }
+
+ nsresult rv;
+ if (!sConsoleService) { // only need to bother null-checking here
+ rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsAutoCString spec;
+ if (!aLineNumber && aLocationMode == eUSE_CALLING_LOCATION) {
+ JSContext *cx = GetCurrentJSContext();
+ if (cx) {
+ nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber, &aColumnNumber);
+ }
+ }
+ if (spec.IsEmpty() && aURI) {
+ spec = aURI->GetSpecOrDefault();
+ }
+
+ nsCOMPtr<nsIScriptError> errorObject =
+ do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = errorObject->InitWithWindowID(aErrorText,
+ NS_ConvertUTF8toUTF16(spec), // file name
+ aSourceLine,
+ aLineNumber, aColumnNumber,
+ aErrorFlags, aCategory,
+ innerWindowID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return sConsoleService->LogMessage(errorObject);
+}
+
+void
+nsContentUtils::LogMessageToConsole(const char* aMsg)
+{
+ if (!sConsoleService) { // only need to bother null-checking here
+ CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
+ if (!sConsoleService) {
+ return;
+ }
+ }
+ sConsoleService->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
+}
+
+bool
+nsContentUtils::IsChromeDoc(nsIDocument *aDocument)
+{
+ if (!aDocument) {
+ return false;
+ }
+ return aDocument->NodePrincipal() == sSystemPrincipal;
+}
+
+bool
+nsContentUtils::IsChildOfSameType(nsIDocument* aDoc)
+{
+ nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(aDoc->GetDocShell());
+ nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
+ if (docShellAsItem) {
+ docShellAsItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
+ }
+ return sameTypeParent != nullptr;
+}
+
+bool
+nsContentUtils::IsScriptType(const nsACString& aContentType)
+{
+ // NOTE: if you add a type here, add it to the CONTENTDLF_CATEGORIES
+ // define in nsContentDLF.h as well.
+ return aContentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
+ aContentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
+ aContentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
+ aContentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
+ aContentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
+ aContentType.EqualsLiteral(APPLICATION_JSON) ||
+ aContentType.EqualsLiteral(TEXT_JSON);
+}
+
+bool
+nsContentUtils::IsPlainTextType(const nsACString& aContentType)
+{
+ // NOTE: if you add a type here, add it to the CONTENTDLF_CATEGORIES
+ // define in nsContentDLF.h as well.
+ return aContentType.EqualsLiteral(TEXT_PLAIN) ||
+ aContentType.EqualsLiteral(TEXT_CSS) ||
+ aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
+ aContentType.EqualsLiteral(TEXT_VTT) ||
+ IsScriptType(aContentType);
+}
+
+bool
+nsContentUtils::GetWrapperSafeScriptFilename(nsIDocument* aDocument,
+ nsIURI* aURI,
+ nsACString& aScriptURI,
+ nsresult* aRv)
+{
+ MOZ_ASSERT(aRv);
+ bool scriptFileNameModified = false;
+ *aRv = NS_OK;
+
+ *aRv = aURI->GetSpec(aScriptURI);
+ NS_ENSURE_SUCCESS(*aRv, false);
+
+ if (IsChromeDoc(aDocument)) {
+ nsCOMPtr<nsIChromeRegistry> chromeReg =
+ mozilla::services::GetChromeRegistryService();
+
+ if (!chromeReg) {
+ // If we're running w/o a chrome registry we won't modify any
+ // script file names.
+
+ return scriptFileNameModified;
+ }
+
+ bool docWrappersEnabled =
+ chromeReg->WrappersEnabled(aDocument->GetDocumentURI());
+
+ bool uriWrappersEnabled = chromeReg->WrappersEnabled(aURI);
+
+ nsIURI *docURI = aDocument->GetDocumentURI();
+
+ if (docURI && docWrappersEnabled && !uriWrappersEnabled) {
+ // aURI is a script from a URL that doesn't get wrapper
+ // automation. aDocument is a chrome document that does get
+ // wrapper automation. Prepend the chrome document's URI
+ // followed by the string " -> " to the URI of the script we're
+ // loading here so that script in that URI gets the same wrapper
+ // automation that the chrome document expects.
+ nsAutoCString spec;
+ *aRv = docURI->GetSpec(spec);
+ if (NS_WARN_IF(NS_FAILED(*aRv))) {
+ return false;
+ }
+
+ spec.AppendLiteral(" -> ");
+ spec.Append(aScriptURI);
+
+ aScriptURI = spec;
+
+ scriptFileNameModified = true;
+ }
+ }
+
+ return scriptFileNameModified;
+}
+
+// static
+bool
+nsContentUtils::IsInChromeDocshell(nsIDocument *aDocument)
+{
+ if (!aDocument) {
+ return false;
+ }
+
+ if (aDocument->GetDisplayDocument()) {
+ return IsInChromeDocshell(aDocument->GetDisplayDocument());
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> docShell = aDocument->GetDocShell();
+ if (!docShell) {
+ return false;
+ }
+
+ return docShell->ItemType() == nsIDocShellTreeItem::typeChrome;
+}
+
+// static
+nsIContentPolicy*
+nsContentUtils::GetContentPolicy()
+{
+ if (!sTriedToGetContentPolicy) {
+ CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
+ // It's OK to not have a content policy service
+ sTriedToGetContentPolicy = true;
+ }
+
+ return sContentPolicyService;
+}
+
+// static
+bool
+nsContentUtils::IsEventAttributeName(nsIAtom* aName, int32_t aType)
+{
+ const char16_t* name = aName->GetUTF16String();
+ if (name[0] != 'o' || name[1] != 'n')
+ return false;
+
+ EventNameMapping mapping;
+ return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
+}
+
+// static
+EventMessage
+nsContentUtils::GetEventMessage(nsIAtom* aName)
+{
+ if (aName) {
+ EventNameMapping mapping;
+ if (sAtomEventTable->Get(aName, &mapping)) {
+ return mapping.mMessage;
+ }
+ }
+
+ return eUnidentifiedEvent;
+}
+
+// static
+mozilla::EventClassID
+nsContentUtils::GetEventClassID(const nsAString& aName)
+{
+ EventNameMapping mapping;
+ if (sStringEventTable->Get(aName, &mapping))
+ return mapping.mEventClassID;
+
+ return eBasicEventClass;
+}
+
+nsIAtom*
+nsContentUtils::GetEventMessageAndAtom(const nsAString& aName,
+ mozilla::EventClassID aEventClassID,
+ EventMessage* aEventMessage)
+{
+ EventNameMapping mapping;
+ if (sStringEventTable->Get(aName, &mapping)) {
+ *aEventMessage =
+ mapping.mEventClassID == aEventClassID ? mapping.mMessage :
+ eUnidentifiedEvent;
+ return mapping.mAtom;
+ }
+
+ // If we have cached lots of user defined event names, clear some of them.
+ if (sUserDefinedEvents->Count() > 127) {
+ while (sUserDefinedEvents->Count() > 64) {
+ nsIAtom* first = sUserDefinedEvents->ObjectAt(0);
+ sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
+ sUserDefinedEvents->RemoveObjectAt(0);
+ }
+ }
+
+ *aEventMessage = eUnidentifiedEvent;
+ nsCOMPtr<nsIAtom> atom = NS_Atomize(NS_LITERAL_STRING("on") + aName);
+ sUserDefinedEvents->AppendObject(atom);
+ mapping.mAtom = atom;
+ mapping.mMessage = eUnidentifiedEvent;
+ mapping.mType = EventNameType_None;
+ mapping.mEventClassID = eBasicEventClass;
+ // This is a slow hashtable call, but at least we cache the result for the
+ // following calls. Because GetEventMessageAndAtomForListener utilizes
+ // sStringEventTable, it needs to know in which cases sStringEventTable
+ // doesn't contain the information it needs so that it can use
+ // sAtomEventTable instead.
+ mapping.mMaybeSpecialSVGorSMILEvent =
+ GetEventMessage(atom) != eUnidentifiedEvent;
+ sStringEventTable->Put(aName, mapping);
+ return mapping.mAtom;
+}
+
+// static
+EventMessage
+nsContentUtils::GetEventMessageAndAtomForListener(const nsAString& aName,
+ nsIAtom** aOnName)
+{
+ // Because of SVG/SMIL sStringEventTable contains a subset of the event names
+ // comparing to the sAtomEventTable. However, usually sStringEventTable
+ // contains the information we need, so in order to reduce hashtable
+ // lookups, start from it.
+ EventNameMapping mapping;
+ EventMessage msg = eUnidentifiedEvent;
+ nsCOMPtr<nsIAtom> atom;
+ if (sStringEventTable->Get(aName, &mapping)) {
+ if (mapping.mMaybeSpecialSVGorSMILEvent) {
+ // Try the atom version so that we should get the right message for
+ // SVG/SMIL.
+ atom = NS_Atomize(NS_LITERAL_STRING("on") + aName);
+ msg = GetEventMessage(atom);
+ } else {
+ atom = mapping.mAtom;
+ msg = mapping.mMessage;
+ }
+ atom.forget(aOnName);
+ return msg;
+ }
+
+ // GetEventMessageAndAtom will cache the event type for the future usage...
+ GetEventMessageAndAtom(aName, eBasicEventClass, &msg);
+
+ // ...and then call this method recursively to get the message and atom from
+ // now updated sStringEventTable.
+ return GetEventMessageAndAtomForListener(aName, aOnName);
+}
+
+static
+nsresult GetEventAndTarget(nsIDocument* aDoc, nsISupports* aTarget,
+ const nsAString& aEventName,
+ bool aCanBubble, bool aCancelable,
+ bool aTrusted, nsIDOMEvent** aEvent,
+ EventTarget** aTargetOut)
+{
+ nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
+ nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
+ NS_ENSURE_TRUE(domDoc && target, NS_ERROR_INVALID_ARG);
+
+ nsCOMPtr<nsIDOMEvent> event;
+ nsresult rv =
+ domDoc->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ event->InitEvent(aEventName, aCanBubble, aCancelable);
+ event->SetTrusted(aTrusted);
+
+ rv = event->SetTarget(target);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ event.forget(aEvent);
+ target.forget(aTargetOut);
+ return NS_OK;
+}
+
+// static
+nsresult
+nsContentUtils::DispatchTrustedEvent(nsIDocument* aDoc, nsISupports* aTarget,
+ const nsAString& aEventName,
+ bool aCanBubble, bool aCancelable,
+ bool *aDefaultAction)
+{
+ return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
+ true, aDefaultAction);
+}
+
+// static
+nsresult
+nsContentUtils::DispatchUntrustedEvent(nsIDocument* aDoc, nsISupports* aTarget,
+ const nsAString& aEventName,
+ bool aCanBubble, bool aCancelable,
+ bool *aDefaultAction)
+{
+ return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
+ false, aDefaultAction);
+}
+
+// static
+nsresult
+nsContentUtils::DispatchEvent(nsIDocument* aDoc, nsISupports* aTarget,
+ const nsAString& aEventName,
+ bool aCanBubble, bool aCancelable,
+ bool aTrusted, bool *aDefaultAction,
+ bool aOnlyChromeDispatch)
+{
+ nsCOMPtr<nsIDOMEvent> event;
+ nsCOMPtr<EventTarget> target;
+ nsresult rv = GetEventAndTarget(aDoc, aTarget, aEventName, aCanBubble,
+ aCancelable, aTrusted, getter_AddRefs(event),
+ getter_AddRefs(target));
+ NS_ENSURE_SUCCESS(rv, rv);
+ event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = aOnlyChromeDispatch;
+
+ bool dummy;
+ return target->DispatchEvent(event, aDefaultAction ? aDefaultAction : &dummy);
+}
+
+nsresult
+nsContentUtils::DispatchChromeEvent(nsIDocument *aDoc,
+ nsISupports *aTarget,
+ const nsAString& aEventName,
+ bool aCanBubble, bool aCancelable,
+ bool *aDefaultAction)
+{
+
+ nsCOMPtr<nsIDOMEvent> event;
+ nsCOMPtr<EventTarget> target;
+ nsresult rv = GetEventAndTarget(aDoc, aTarget, aEventName, aCanBubble,
+ aCancelable, true, getter_AddRefs(event),
+ getter_AddRefs(target));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(aDoc, "GetEventAndTarget lied?");
+ if (!aDoc->GetWindow())
+ return NS_ERROR_INVALID_ARG;
+
+ EventTarget* piTarget = aDoc->GetWindow()->GetParentTarget();
+ if (!piTarget)
+ return NS_ERROR_INVALID_ARG;
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+ rv = piTarget->DispatchDOMEvent(nullptr, event, nullptr, &status);
+ if (aDefaultAction) {
+ *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
+ }
+ return rv;
+}
+
+/* static */
+nsresult
+nsContentUtils::DispatchFocusChromeEvent(nsPIDOMWindowOuter* aWindow)
+{
+ MOZ_ASSERT(aWindow);
+
+ nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
+ if (!doc) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return DispatchChromeEvent(doc, aWindow,
+ NS_LITERAL_STRING("DOMServiceWorkerFocusClient"),
+ true, true);
+}
+
+nsresult
+nsContentUtils::DispatchEventOnlyToChrome(nsIDocument* aDoc,
+ nsISupports* aTarget,
+ const nsAString& aEventName,
+ bool aCanBubble, bool aCancelable,
+ bool* aDefaultAction)
+{
+ return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
+ true, aDefaultAction, true);
+}
+
+/* static */
+Element*
+nsContentUtils::MatchElementId(nsIContent *aContent, const nsIAtom* aId)
+{
+ for (nsIContent* cur = aContent;
+ cur;
+ cur = cur->GetNextNode(aContent)) {
+ if (aId == cur->GetID()) {
+ return cur->AsElement();
+ }
+ }
+
+ return nullptr;
+}
+
+/* static */
+Element *
+nsContentUtils::MatchElementId(nsIContent *aContent, const nsAString& aId)
+{
+ NS_PRECONDITION(!aId.IsEmpty(), "Will match random elements");
+
+ // ID attrs are generally stored as atoms, so just atomize this up front
+ nsCOMPtr<nsIAtom> id(NS_Atomize(aId));
+ if (!id) {
+ // OOM, so just bail
+ return nullptr;
+ }
+
+ return MatchElementId(aContent, id);
+}
+
+/* static */
+nsIDocument*
+nsContentUtils::GetSubdocumentWithOuterWindowId(nsIDocument *aDocument,
+ uint64_t aOuterWindowId)
+{
+ if (!aDocument || !aOuterWindowId) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = nsGlobalWindow::GetOuterWindowWithId(aOuterWindowId)->AsOuter();
+ if (!window) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIDocument> foundDoc = window->GetDoc();
+ if (nsContentUtils::ContentIsCrossDocDescendantOf(foundDoc, aDocument)) {
+ // Note that ContentIsCrossDocDescendantOf will return true if
+ // foundDoc == aDocument.
+ return foundDoc;
+ }
+
+ return nullptr;
+}
+
+// Convert the string from the given encoding to Unicode.
+/* static */
+nsresult
+nsContentUtils::ConvertStringFromEncoding(const nsACString& aEncoding,
+ const nsACString& aInput,
+ nsAString& aOutput)
+{
+ nsAutoCString encoding;
+ if (aEncoding.IsEmpty()) {
+ encoding.AssignLiteral("UTF-8");
+ } else {
+ encoding.Assign(aEncoding);
+ }
+
+ ErrorResult rv;
+ nsAutoPtr<TextDecoder> decoder(new TextDecoder());
+ decoder->InitWithEncoding(encoding, false);
+
+ decoder->Decode(aInput.BeginReading(), aInput.Length(), false,
+ aOutput, rv);
+ return rv.StealNSResult();
+}
+
+/* static */
+bool
+nsContentUtils::CheckForBOM(const unsigned char* aBuffer, uint32_t aLength,
+ nsACString& aCharset)
+{
+ bool found = true;
+ aCharset.Truncate();
+ if (aLength >= 3 &&
+ aBuffer[0] == 0xEF &&
+ aBuffer[1] == 0xBB &&
+ aBuffer[2] == 0xBF) {
+ aCharset = "UTF-8";
+ }
+ else if (aLength >= 2 &&
+ aBuffer[0] == 0xFE && aBuffer[1] == 0xFF) {
+ aCharset = "UTF-16BE";
+ }
+ else if (aLength >= 2 &&
+ aBuffer[0] == 0xFF && aBuffer[1] == 0xFE) {
+ aCharset = "UTF-16LE";
+ } else {
+ found = false;
+ }
+
+ return found;
+}
+
+/* static */
+void
+nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver)
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->AddObserver(aObserver,
+ NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+ false);
+ }
+}
+
+/* static */
+void
+nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver)
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+ }
+}
+
+/* static */
+bool
+nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent, int32_t aNameSpaceID,
+ nsIAtom* aName)
+{
+ static nsIContent::AttrValuesArray strings[] = {&nsGkAtoms::_empty, nullptr};
+ return aContent->FindAttrValueIn(aNameSpaceID, aName, strings, eCaseMatters)
+ == nsIContent::ATTR_VALUE_NO_MATCH;
+}
+
+/* static */
+bool
+nsContentUtils::HasMutationListeners(nsINode* aNode,
+ uint32_t aType,
+ nsINode* aTargetForSubtreeModified)
+{
+ nsIDocument* doc = aNode->OwnerDoc();
+
+ // global object will be null for documents that don't have windows.
+ nsPIDOMWindowInner* window = doc->GetInnerWindow();
+ // This relies on EventListenerManager::AddEventListener, which sets
+ // all mutation bits when there is a listener for DOMSubtreeModified event.
+ if (window && !window->HasMutationListeners(aType)) {
+ return false;
+ }
+
+ if (aNode->IsNodeOfType(nsINode::eCONTENT) &&
+ static_cast<nsIContent*>(aNode)->ChromeOnlyAccess()) {
+ return false;
+ }
+
+ doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
+
+ // If we have a window, we can check it for mutation listeners now.
+ if (aNode->IsInUncomposedDoc()) {
+ nsCOMPtr<EventTarget> piTarget(do_QueryInterface(window));
+ if (piTarget) {
+ EventListenerManager* manager = piTarget->GetExistingListenerManager();
+ if (manager && manager->HasMutationListeners()) {
+ return true;
+ }
+ }
+ }
+
+ // If we have a window, we know a mutation listener is registered, but it
+ // might not be in our chain. If we don't have a window, we might have a
+ // mutation listener. Check quickly to see.
+ while (aNode) {
+ EventListenerManager* manager = aNode->GetExistingListenerManager();
+ if (manager && manager->HasMutationListeners()) {
+ return true;
+ }
+
+ if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
+ nsIContent* content = static_cast<nsIContent*>(aNode);
+ nsIContent* insertionParent = content->GetXBLInsertionParent();
+ if (insertionParent) {
+ aNode = insertionParent;
+ continue;
+ }
+ }
+ aNode = aNode->GetParentNode();
+ }
+
+ return false;
+}
+
+/* static */
+bool
+nsContentUtils::HasMutationListeners(nsIDocument* aDocument,
+ uint32_t aType)
+{
+ nsPIDOMWindowInner* window = aDocument ?
+ aDocument->GetInnerWindow() : nullptr;
+
+ // This relies on EventListenerManager::AddEventListener, which sets
+ // all mutation bits when there is a listener for DOMSubtreeModified event.
+ return !window || window->HasMutationListeners(aType);
+}
+
+void
+nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent,
+ nsIDocument* aOwnerDoc)
+{
+ NS_PRECONDITION(aChild, "Missing child");
+ NS_PRECONDITION(aChild->GetParentNode() == aParent, "Wrong parent");
+ NS_PRECONDITION(aChild->OwnerDoc() == aOwnerDoc, "Wrong owner-doc");
+
+ // Having an explicit check here since it's an easy mistake to fall into,
+ // and there might be existing code with problems. We'd rather be safe
+ // than fire DOMNodeRemoved in all corner cases. We also rely on it for
+ // nsAutoScriptBlockerSuppressNodeRemoved.
+ if (!IsSafeToRunScript()) {
+ // This checks that IsSafeToRunScript is true since we don't want to fire
+ // events when that is false. We can't rely on EventDispatcher to assert
+ // this in this situation since most of the time there are no mutation
+ // event listeners, in which case we won't even attempt to dispatch events.
+ // However this also allows for two exceptions. First off, we don't assert
+ // if the mutation happens to native anonymous content since we never fire
+ // mutation events on such content anyway.
+ // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
+ // that is a know case when we'd normally fire a mutation event, but can't
+ // make that safe and so we suppress it at this time. Ideally this should
+ // go away eventually.
+ if (!(aChild->IsContent() && aChild->AsContent()->IsInNativeAnonymousSubtree()) &&
+ !sDOMNodeRemovedSuppressCount) {
+ NS_ERROR("Want to fire DOMNodeRemoved event, but it's not safe");
+ WarnScriptWasIgnored(aOwnerDoc);
+ }
+ return;
+ }
+
+ if (HasMutationListeners(aChild,
+ NS_EVENT_BITS_MUTATION_NODEREMOVED, aParent)) {
+ InternalMutationEvent mutation(true, eLegacyNodeRemoved);
+ mutation.mRelatedNode = do_QueryInterface(aParent);
+
+ mozAutoSubtreeModified subtree(aOwnerDoc, aParent);
+ EventDispatcher::Dispatch(aChild, nullptr, &mutation);
+ }
+}
+
+void
+nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments()
+{
+ if (!sEventListenerManagersHash) {
+ return;
+ }
+
+ for (auto i = sEventListenerManagersHash->Iter(); !i.Done(); i.Next()) {
+ auto entry = static_cast<EventListenerManagerMapEntry*>(i.Get());
+ nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
+ if (n && n->IsInUncomposedDoc() &&
+ nsCCUncollectableMarker::InGeneration(n->OwnerDoc()->GetMarkedCCGeneration())) {
+ entry->mListenerManager->MarkForCC();
+ }
+ }
+}
+
+/* static */
+void
+nsContentUtils::TraverseListenerManager(nsINode *aNode,
+ nsCycleCollectionTraversalCallback &cb)
+{
+ if (!sEventListenerManagersHash) {
+ // We're already shut down, just return.
+ return;
+ }
+
+ auto entry = static_cast<EventListenerManagerMapEntry*>
+ (sEventListenerManagersHash->Search(aNode));
+ if (entry) {
+ CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
+ "[via hash] mListenerManager");
+ }
+}
+
+EventListenerManager*
+nsContentUtils::GetListenerManagerForNode(nsINode *aNode)
+{
+ if (!sEventListenerManagersHash) {
+ // We're already shut down, don't bother creating an event listener
+ // manager.
+
+ return nullptr;
+ }
+
+ auto entry =
+ static_cast<EventListenerManagerMapEntry*>
+ (sEventListenerManagersHash->Add(aNode, fallible));
+
+ if (!entry) {
+ return nullptr;
+ }
+
+ if (!entry->mListenerManager) {
+ entry->mListenerManager = new EventListenerManager(aNode);
+
+ aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
+ }
+
+ return entry->mListenerManager;
+}
+
+EventListenerManager*
+nsContentUtils::GetExistingListenerManagerForNode(const nsINode *aNode)
+{
+ if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
+ return nullptr;
+ }
+
+ if (!sEventListenerManagersHash) {
+ // We're already shut down, don't bother creating an event listener
+ // manager.
+
+ return nullptr;
+ }
+
+ auto entry = static_cast<EventListenerManagerMapEntry*>
+ (sEventListenerManagersHash->Search(aNode));
+ if (entry) {
+ return entry->mListenerManager;
+ }
+
+ return nullptr;
+}
+
+/* static */
+void
+nsContentUtils::RemoveListenerManager(nsINode *aNode)
+{
+ if (sEventListenerManagersHash) {
+ auto entry = static_cast<EventListenerManagerMapEntry*>
+ (sEventListenerManagersHash->Search(aNode));
+ if (entry) {
+ RefPtr<EventListenerManager> listenerManager;
+ listenerManager.swap(entry->mListenerManager);
+ // Remove the entry and *then* do operations that could cause further
+ // modification of sEventListenerManagersHash. See bug 334177.
+ sEventListenerManagersHash->RawRemove(entry);
+ if (listenerManager) {
+ listenerManager->Disconnect();
+ }
+ }
+ }
+}
+
+/* static */
+bool
+nsContentUtils::IsValidNodeName(nsIAtom *aLocalName, nsIAtom *aPrefix,
+ int32_t aNamespaceID)
+{
+ if (aNamespaceID == kNameSpaceID_Unknown) {
+ return false;
+ }
+
+ if (!aPrefix) {
+ // If the prefix is null, then either the QName must be xmlns or the
+ // namespace must not be XMLNS.
+ return (aLocalName == nsGkAtoms::xmlns) ==
+ (aNamespaceID == kNameSpaceID_XMLNS);
+ }
+
+ // If the prefix is non-null then the namespace must not be null.
+ if (aNamespaceID == kNameSpaceID_None) {
+ return false;
+ }
+
+ // If the namespace is the XMLNS namespace then the prefix must be xmlns,
+ // but the localname must not be xmlns.
+ if (aNamespaceID == kNameSpaceID_XMLNS) {
+ return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
+ }
+
+ // If the namespace is not the XMLNS namespace then the prefix must not be
+ // xmlns.
+ // If the namespace is the XML namespace then the prefix can be anything.
+ // If the namespace is not the XML namespace then the prefix must not be xml.
+ return aPrefix != nsGkAtoms::xmlns &&
+ (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
+}
+
+/* static */
+nsresult
+nsContentUtils::CreateContextualFragment(nsINode* aContextNode,
+ const nsAString& aFragment,
+ bool aPreventScriptExecution,
+ nsIDOMDocumentFragment** aReturn)
+{
+ ErrorResult rv;
+ *aReturn = CreateContextualFragment(aContextNode, aFragment,
+ aPreventScriptExecution, rv).take();
+ return rv.StealNSResult();
+}
+
+already_AddRefed<DocumentFragment>
+nsContentUtils::CreateContextualFragment(nsINode* aContextNode,
+ const nsAString& aFragment,
+ bool aPreventScriptExecution,
+ ErrorResult& aRv)
+{
+ if (!aContextNode) {
+ aRv.Throw(NS_ERROR_INVALID_ARG);
+ return nullptr;
+ }
+
+ // If we don't have a document here, we can't get the right security context
+ // for compiling event handlers... so just bail out.
+ nsCOMPtr<nsIDocument> document = aContextNode->OwnerDoc();
+ bool isHTML = document->IsHTMLDocument();
+#ifdef DEBUG
+ nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document);
+ NS_ASSERTION(!isHTML || htmlDoc, "Should have HTMLDocument here!");
+#endif
+
+ if (isHTML) {
+ RefPtr<DocumentFragment> frag =
+ new DocumentFragment(document->NodeInfoManager());
+
+ nsCOMPtr<nsIContent> contextAsContent = do_QueryInterface(aContextNode);
+ if (contextAsContent && !contextAsContent->IsElement()) {
+ contextAsContent = contextAsContent->GetParent();
+ if (contextAsContent && !contextAsContent->IsElement()) {
+ // can this even happen?
+ contextAsContent = nullptr;
+ }
+ }
+
+ if (contextAsContent && !contextAsContent->IsHTMLElement(nsGkAtoms::html)) {
+ aRv = ParseFragmentHTML(aFragment, frag,
+ contextAsContent->NodeInfo()->NameAtom(),
+ contextAsContent->GetNameSpaceID(),
+ (document->GetCompatibilityMode() ==
+ eCompatibility_NavQuirks),
+ aPreventScriptExecution);
+ } else {
+ aRv = ParseFragmentHTML(aFragment, frag,
+ nsGkAtoms::body,
+ kNameSpaceID_XHTML,
+ (document->GetCompatibilityMode() ==
+ eCompatibility_NavQuirks),
+ aPreventScriptExecution);
+ }
+
+ return frag.forget();
+ }
+
+ AutoTArray<nsString, 32> tagStack;
+ nsAutoString uriStr, nameStr;
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aContextNode);
+ // just in case we have a text node
+ if (content && !content->IsElement())
+ content = content->GetParent();
+
+ while (content && content->IsElement()) {
+ nsString& tagName = *tagStack.AppendElement();
+ tagName = content->NodeInfo()->QualifiedName();
+
+ // see if we need to add xmlns declarations
+ uint32_t count = content->GetAttrCount();
+ bool setDefaultNamespace = false;
+ if (count > 0) {
+ uint32_t index;
+
+ for (index = 0; index < count; index++) {
+ const BorrowedAttrInfo info = content->GetAttrInfoAt(index);
+ const nsAttrName* name = info.mName;
+ if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
+ info.mValue->ToString(uriStr);
+
+ // really want something like nsXMLContentSerializer::SerializeAttr
+ tagName.AppendLiteral(" xmlns"); // space important
+ if (name->GetPrefix()) {
+ tagName.Append(char16_t(':'));
+ name->LocalName()->ToString(nameStr);
+ tagName.Append(nameStr);
+ } else {
+ setDefaultNamespace = true;
+ }
+ tagName.AppendLiteral("=\"");
+ tagName.Append(uriStr);
+ tagName.Append('"');
+ }
+ }
+ }
+
+ if (!setDefaultNamespace) {
+ mozilla::dom::NodeInfo* info = content->NodeInfo();
+ if (!info->GetPrefixAtom() &&
+ info->NamespaceID() != kNameSpaceID_None) {
+ // We have no namespace prefix, but have a namespace ID. Push
+ // default namespace attr in, so that our kids will be in our
+ // namespace.
+ info->GetNamespaceURI(uriStr);
+ tagName.AppendLiteral(" xmlns=\"");
+ tagName.Append(uriStr);
+ tagName.Append('"');
+ }
+ }
+
+ content = content->GetParent();
+ }
+
+ nsCOMPtr<nsIDOMDocumentFragment> frag;
+ aRv = ParseFragmentXML(aFragment, document, tagStack,
+ aPreventScriptExecution, getter_AddRefs(frag));
+ return frag.forget().downcast<DocumentFragment>();
+}
+
+/* static */
+void
+nsContentUtils::DropFragmentParsers()
+{
+ NS_IF_RELEASE(sHTMLFragmentParser);
+ NS_IF_RELEASE(sXMLFragmentParser);
+ NS_IF_RELEASE(sXMLFragmentSink);
+}
+
+/* static */
+void
+nsContentUtils::XPCOMShutdown()
+{
+ nsContentUtils::DropFragmentParsers();
+}
+
+/* static */
+nsresult
+nsContentUtils::ParseFragmentHTML(const nsAString& aSourceBuffer,
+ nsIContent* aTargetNode,
+ nsIAtom* aContextLocalName,
+ int32_t aContextNamespace,
+ bool aQuirks,
+ bool aPreventScriptExecution)
+{
+ AutoTimelineMarker m(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");
+
+ if (nsContentUtils::sFragmentParsingActive) {
+ NS_NOTREACHED("Re-entrant fragment parsing attempted.");
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+ mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
+ nsContentUtils::sFragmentParsingActive = true;
+ if (!sHTMLFragmentParser) {
+ NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
+ // Now sHTMLFragmentParser owns the object
+ }
+ nsresult rv =
+ sHTMLFragmentParser->ParseFragment(aSourceBuffer,
+ aTargetNode,
+ aContextLocalName,
+ aContextNamespace,
+ aQuirks,
+ aPreventScriptExecution);
+ return rv;
+}
+
+/* static */
+nsresult
+nsContentUtils::ParseDocumentHTML(const nsAString& aSourceBuffer,
+ nsIDocument* aTargetDocument,
+ bool aScriptingEnabledForNoscriptParsing)
+{
+ AutoTimelineMarker m(aTargetDocument->GetDocShell(), "Parse HTML");
+
+ if (nsContentUtils::sFragmentParsingActive) {
+ NS_NOTREACHED("Re-entrant fragment parsing attempted.");
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+ mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
+ nsContentUtils::sFragmentParsingActive = true;
+ if (!sHTMLFragmentParser) {
+ NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
+ // Now sHTMLFragmentParser owns the object
+ }
+ nsresult rv =
+ sHTMLFragmentParser->ParseDocument(aSourceBuffer,
+ aTargetDocument,
+ aScriptingEnabledForNoscriptParsing);
+ return rv;
+}
+
+/* static */
+nsresult
+nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
+ nsIDocument* aDocument,
+ nsTArray<nsString>& aTagStack,
+ bool aPreventScriptExecution,
+ nsIDOMDocumentFragment** aReturn)
+{
+ AutoTimelineMarker m(aDocument->GetDocShell(), "Parse XML");
+
+ if (nsContentUtils::sFragmentParsingActive) {
+ NS_NOTREACHED("Re-entrant fragment parsing attempted.");
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+ mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
+ nsContentUtils::sFragmentParsingActive = true;
+ if (!sXMLFragmentParser) {
+ nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID);
+ parser.forget(&sXMLFragmentParser);
+ // sXMLFragmentParser now owns the parser
+ }
+ if (!sXMLFragmentSink) {
+ NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
+ // sXMLFragmentSink now owns the sink
+ }
+ nsCOMPtr<nsIContentSink> contentsink = do_QueryInterface(sXMLFragmentSink);
+ MOZ_ASSERT(contentsink, "Sink doesn't QI to nsIContentSink!");
+ sXMLFragmentParser->SetContentSink(contentsink);
+
+ sXMLFragmentSink->SetTargetDocument(aDocument);
+ sXMLFragmentSink->SetPreventScriptExecution(aPreventScriptExecution);
+
+ nsresult rv =
+ sXMLFragmentParser->ParseFragment(aSourceBuffer,
+ aTagStack);
+ if (NS_FAILED(rv)) {
+ // Drop the fragment parser and sink that might be in an inconsistent state
+ NS_IF_RELEASE(sXMLFragmentParser);
+ NS_IF_RELEASE(sXMLFragmentSink);
+ return rv;
+ }
+
+ rv = sXMLFragmentSink->FinishFragmentParsing(aReturn);
+
+ sXMLFragmentParser->Reset();
+
+ return rv;
+}
+
+/* static */
+nsresult
+nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
+ nsAString& aResultBuffer,
+ uint32_t aFlags,
+ uint32_t aWrapCol)
+{
+ nsCOMPtr<nsIURI> uri;
+ NS_NewURI(getter_AddRefs(uri), "about:blank");
+ nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::Create();
+ nsCOMPtr<nsIDOMDocument> domDocument;
+ nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument),
+ EmptyString(),
+ EmptyString(),
+ nullptr,
+ uri,
+ uri,
+ principal,
+ true,
+ nullptr,
+ DocumentFlavorHTML);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
+ rv = nsContentUtils::ParseDocumentHTML(aSourceBuffer, document,
+ !(aFlags & nsIDocumentEncoder::OutputNoScriptContent));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocumentEncoder> encoder = do_CreateInstance(
+ "@mozilla.org/layout/documentEncoder;1?type=text/plain");
+
+ rv = encoder->Init(domDocument, NS_LITERAL_STRING("text/plain"), aFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ encoder->SetWrapColumn(aWrapCol);
+
+ return encoder->EncodeToString(aResultBuffer);
+}
+
+/* static */
+nsresult
+nsContentUtils::SetNodeTextContent(nsIContent* aContent,
+ const nsAString& aValue,
+ bool aTryReuse)
+{
+ // Fire DOMNodeRemoved mutation events before we do anything else.
+ nsCOMPtr<nsIContent> owningContent;
+
+ // Batch possible DOMSubtreeModified events.
+ mozAutoSubtreeModified subtree(nullptr, nullptr);
+
+ // Scope firing mutation events so that we don't carry any state that
+ // might be stale
+ {
+ // We're relying on mozAutoSubtreeModified to keep a strong reference if
+ // needed.
+ nsIDocument* doc = aContent->OwnerDoc();
+
+ // Optimize the common case of there being no observers
+ if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
+ subtree.UpdateTarget(doc, nullptr);
+ owningContent = aContent;
+ nsCOMPtr<nsINode> child;
+ bool skipFirst = aTryReuse;
+ for (child = aContent->GetFirstChild();
+ child && child->GetParentNode() == aContent;
+ child = child->GetNextSibling()) {
+ if (skipFirst && child->IsNodeOfType(nsINode::eTEXT)) {
+ skipFirst = false;
+ continue;
+ }
+ nsContentUtils::MaybeFireNodeRemoved(child, aContent, doc);
+ }
+ }
+ }
+
+ // Might as well stick a batch around this since we're performing several
+ // mutations.
+ mozAutoDocUpdate updateBatch(aContent->GetComposedDoc(),
+ UPDATE_CONTENT_MODEL, true);
+ nsAutoMutationBatch mb;
+
+ uint32_t childCount = aContent->GetChildCount();
+
+ if (aTryReuse && !aValue.IsEmpty()) {
+ uint32_t removeIndex = 0;
+
+ for (uint32_t i = 0; i < childCount; ++i) {
+ nsIContent* child = aContent->GetChildAt(removeIndex);
+ if (removeIndex == 0 && child && child->IsNodeOfType(nsINode::eTEXT)) {
+ nsresult rv = child->SetText(aValue, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ removeIndex = 1;
+ }
+ else {
+ aContent->RemoveChildAt(removeIndex, true);
+ }
+ }
+
+ if (removeIndex == 1) {
+ return NS_OK;
+ }
+ }
+ else {
+ mb.Init(aContent, true, false);
+ for (uint32_t i = 0; i < childCount; ++i) {
+ aContent->RemoveChildAt(0, true);
+ }
+ }
+ mb.RemovalDone();
+
+ if (aValue.IsEmpty()) {
+ return NS_OK;
+ }
+
+ RefPtr<nsTextNode> textContent =
+ new nsTextNode(aContent->NodeInfo()->NodeInfoManager());
+
+ textContent->SetText(aValue, true);
+
+ nsresult rv = aContent->AppendChildTo(textContent, true);
+ mb.NodesAdded();
+ return rv;
+}
+
+static bool
+AppendNodeTextContentsRecurse(nsINode* aNode, nsAString& aResult,
+ const fallible_t& aFallible)
+{
+ for (nsIContent* child = aNode->GetFirstChild();
+ child;
+ child = child->GetNextSibling()) {
+ if (child->IsElement()) {
+ bool ok = AppendNodeTextContentsRecurse(child, aResult,
+ aFallible);
+ if (!ok) {
+ return false;
+ }
+ }
+ else if (child->IsNodeOfType(nsINode::eTEXT)) {
+ bool ok = child->AppendTextTo(aResult, aFallible);
+ if (!ok) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/* static */
+bool
+nsContentUtils::AppendNodeTextContent(nsINode* aNode, bool aDeep,
+ nsAString& aResult,
+ const fallible_t& aFallible)
+{
+ if (aNode->IsNodeOfType(nsINode::eTEXT)) {
+ return static_cast<nsIContent*>(aNode)->AppendTextTo(aResult,
+ aFallible);
+ }
+ else if (aDeep) {
+ return AppendNodeTextContentsRecurse(aNode, aResult, aFallible);
+ }
+ else {
+ for (nsIContent* child = aNode->GetFirstChild();
+ child;
+ child = child->GetNextSibling()) {
+ if (child->IsNodeOfType(nsINode::eTEXT)) {
+ bool ok = child->AppendTextTo(aResult, fallible);
+ if (!ok) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+nsContentUtils::HasNonEmptyTextContent(nsINode* aNode,
+ TextContentDiscoverMode aDiscoverMode)
+{
+ for (nsIContent* child = aNode->GetFirstChild();
+ child;
+ child = child->GetNextSibling()) {
+ if (child->IsNodeOfType(nsINode::eTEXT) &&
+ child->TextLength() > 0) {
+ return true;
+ }
+
+ if (aDiscoverMode == eRecurseIntoChildren &&
+ HasNonEmptyTextContent(child, aDiscoverMode)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* static */
+bool
+nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
+ const nsIContent* aContent)
+{
+ NS_PRECONDITION(aNode,
+ "Must have a node to work with");
+ NS_PRECONDITION(aContent,
+ "Must have a content to work with");
+
+ if (!aNode->IsNodeOfType(nsINode::eCONTENT)) {
+ /**
+ * The root isn't an nsIContent, so it's a document or attribute. The only
+ * nodes in the same anonymous subtree as it will have a null
+ * bindingParent.
+ *
+ * XXXbz strictly speaking, that's not true for attribute nodes.
+ */
+ return aContent->GetBindingParent() == nullptr;
+ }
+
+ const nsIContent* nodeAsContent = static_cast<const nsIContent*>(aNode);
+
+ // For nodes in a shadow tree, it is insufficient to simply compare
+ // the binding parent because a node may host multiple ShadowRoots,
+ // thus nodes in different shadow tree may have the same binding parent.
+ if (aNode->IsInShadowTree()) {
+ return nodeAsContent->GetContainingShadow() ==
+ aContent->GetContainingShadow();
+ }
+
+ return nodeAsContent->GetBindingParent() == aContent->GetBindingParent();
+}
+
+class AnonymousContentDestroyer : public Runnable {
+public:
+ explicit AnonymousContentDestroyer(nsCOMPtr<nsIContent>* aContent) {
+ mContent.swap(*aContent);
+ mParent = mContent->GetParent();
+ mDoc = mContent->OwnerDoc();
+ }
+ explicit AnonymousContentDestroyer(nsCOMPtr<Element>* aElement) {
+ mContent = aElement->forget();
+ mParent = mContent->GetParent();
+ mDoc = mContent->OwnerDoc();
+ }
+ NS_IMETHOD Run() override {
+ mContent->UnbindFromTree();
+ return NS_OK;
+ }
+private:
+ nsCOMPtr<nsIContent> mContent;
+ // Hold strong refs to the parent content and document so that they
+ // don't die unexpectedly
+ nsCOMPtr<nsIDocument> mDoc;
+ nsCOMPtr<nsIContent> mParent;
+};
+
+/* static */
+void
+nsContentUtils::DestroyAnonymousContent(nsCOMPtr<nsIContent>* aContent)
+{
+ if (*aContent) {
+ AddScriptRunner(new AnonymousContentDestroyer(aContent));
+ }
+}
+
+/* static */
+void
+nsContentUtils::DestroyAnonymousContent(nsCOMPtr<Element>* aElement)
+{
+ if (*aElement) {
+ AddScriptRunner(new AnonymousContentDestroyer(aElement));
+ }
+}
+
+/* static */
+void
+nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling)
+{
+ IMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
+}
+
+static bool SchemeIs(nsIURI* aURI, const char* aScheme)
+{
+ nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
+ NS_ENSURE_TRUE(baseURI, false);
+
+ bool isScheme = false;
+ return NS_SUCCEEDED(baseURI->SchemeIs(aScheme, &isScheme)) && isScheme;
+}
+
+bool
+nsContentUtils::IsSystemPrincipal(nsIPrincipal* aPrincipal)
+{
+ MOZ_ASSERT(IsInitialized());
+ return aPrincipal == sSystemPrincipal;
+}
+
+bool
+nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal)
+{
+ nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
+ return !!ep;
+}
+
+nsIPrincipal*
+nsContentUtils::GetSystemPrincipal()
+{
+ MOZ_ASSERT(IsInitialized());
+ return sSystemPrincipal;
+}
+
+bool
+nsContentUtils::CombineResourcePrincipals(nsCOMPtr<nsIPrincipal>* aResourcePrincipal,
+ nsIPrincipal* aExtraPrincipal)
+{
+ if (!aExtraPrincipal) {
+ return false;
+ }
+ if (!*aResourcePrincipal) {
+ *aResourcePrincipal = aExtraPrincipal;
+ return true;
+ }
+ if (*aResourcePrincipal == aExtraPrincipal) {
+ return false;
+ }
+ bool subsumes;
+ if (NS_SUCCEEDED((*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
+ subsumes) {
+ return false;
+ }
+ *aResourcePrincipal = sSystemPrincipal;
+ return true;
+}
+
+/* static */
+void
+nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
+ nsIURI *aLinkURI, const nsString &aTargetSpec,
+ bool aClick, bool aIsUserTriggered,
+ bool aIsTrusted)
+{
+ NS_ASSERTION(aPresContext, "Need a nsPresContext");
+ NS_PRECONDITION(aLinkURI, "No link URI");
+
+ if (aContent->IsEditable()) {
+ return;
+ }
+
+ nsILinkHandler *handler = aPresContext->GetLinkHandler();
+ if (!handler) {
+ return;
+ }
+
+ if (!aClick) {
+ handler->OnOverLink(aContent, aLinkURI, aTargetSpec.get());
+ return;
+ }
+
+ // Check that this page is allowed to load this URI.
+ nsresult proceed = NS_OK;
+
+ if (sSecurityManager) {
+ uint32_t flag =
+ aIsUserTriggered ?
+ (uint32_t)nsIScriptSecurityManager::STANDARD :
+ (uint32_t)nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT;
+ proceed =
+ sSecurityManager->CheckLoadURIWithPrincipal(aContent->NodePrincipal(),
+ aLinkURI, flag);
+ }
+
+ // Only pass off the click event if the script security manager says it's ok.
+ // We need to rest aTargetSpec for forced downloads.
+ if (NS_SUCCEEDED(proceed)) {
+
+ // A link/area element with a download attribute is allowed to set
+ // a pseudo Content-Disposition header.
+ // For security reasons we only allow websites to declare same-origin resources
+ // as downloadable. If this check fails we will just do the normal thing
+ // (i.e. navigate to the resource).
+ nsAutoString fileName;
+ if ((!aContent->IsHTMLElement(nsGkAtoms::a) &&
+ !aContent->IsHTMLElement(nsGkAtoms::area) &&
+ !aContent->IsSVGElement(nsGkAtoms::a)) ||
+ !aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::download, fileName) ||
+ NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, false, true))) {
+ fileName.SetIsVoid(true); // No actionable download attribute was found.
+ }
+
+ handler->OnLinkClick(aContent, aLinkURI,
+ fileName.IsVoid() ? aTargetSpec.get() : EmptyString().get(),
+ fileName, nullptr, nullptr, aIsTrusted);
+ }
+}
+
+/* static */
+void
+nsContentUtils::GetLinkLocation(Element* aElement, nsString& aLocationString)
+{
+ nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
+ if (hrefURI) {
+ nsAutoCString specUTF8;
+ nsresult rv = hrefURI->GetSpec(specUTF8);
+ if (NS_SUCCEEDED(rv))
+ CopyUTF8toUTF16(specUTF8, aLocationString);
+ }
+}
+
+/* static */
+nsIWidget*
+nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget)
+{
+ if (!aWidget)
+ return nullptr;
+
+ return aWidget->GetTopLevelWidget();
+}
+
+/* static */
+const nsDependentString
+nsContentUtils::GetLocalizedEllipsis()
+{
+ static char16_t sBuf[4] = { 0, 0, 0, 0 };
+ if (!sBuf[0]) {
+ nsAdoptingString tmp = Preferences::GetLocalizedString("intl.ellipsis");
+ uint32_t len = std::min(uint32_t(tmp.Length()),
+ uint32_t(ArrayLength(sBuf) - 1));
+ CopyUnicodeTo(tmp, 0, sBuf, len);
+ if (!sBuf[0])
+ sBuf[0] = char16_t(0x2026);
+ }
+ return nsDependentString(sBuf);
+}
+
+/* static */
+void
+nsContentUtils::AddScriptBlocker()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sScriptBlockerCount) {
+ MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
+ "Should not already have a count");
+ sRunnersCountAtFirstBlocker = sBlockedScriptRunners ? sBlockedScriptRunners->Length() : 0;
+ }
+ ++sScriptBlockerCount;
+}
+
+#ifdef DEBUG
+static bool sRemovingScriptBlockers = false;
+#endif
+
+/* static */
+void
+nsContentUtils::RemoveScriptBlocker()
+{
+ MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sRemovingScriptBlockers);
+ NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
+ --sScriptBlockerCount;
+ if (sScriptBlockerCount) {
+ return;
+ }
+
+ if (!sBlockedScriptRunners) {
+ return;
+ }
+
+ uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
+ uint32_t lastBlocker = sBlockedScriptRunners->Length();
+ uint32_t originalFirstBlocker = firstBlocker;
+ uint32_t blockersCount = lastBlocker - firstBlocker;
+ sRunnersCountAtFirstBlocker = 0;
+ NS_ASSERTION(firstBlocker <= lastBlocker,
+ "bad sRunnersCountAtFirstBlocker");
+
+ while (firstBlocker < lastBlocker) {
+ nsCOMPtr<nsIRunnable> runnable;
+ runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
+ ++firstBlocker;
+
+ // Calling the runnable can reenter us
+ runnable->Run();
+ // So can dropping the reference to the runnable
+ runnable = nullptr;
+
+ NS_ASSERTION(sRunnersCountAtFirstBlocker == 0,
+ "Bad count");
+ NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
+ }
+#ifdef DEBUG
+ AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
+ sRemovingScriptBlockers = true;
+#endif
+ sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
+}
+
+/* static */
+nsIWindowProvider*
+nsContentUtils::GetWindowProviderForContentProcess()
+{
+ MOZ_ASSERT(XRE_IsContentProcess());
+ return ContentChild::GetSingleton();
+}
+
+/* static */
+already_AddRefed<nsPIDOMWindowOuter>
+nsContentUtils::GetMostRecentNonPBWindow()
+{
+ nsCOMPtr<nsIWindowMediator> windowMediator =
+ do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
+ nsCOMPtr<nsIWindowMediator_44> wm = do_QueryInterface(windowMediator);
+
+ nsCOMPtr<mozIDOMWindowProxy> window;
+ wm->GetMostRecentNonPBWindow(u"navigator:browser",
+ getter_AddRefs(window));
+ nsCOMPtr<nsPIDOMWindowOuter> pwindow;
+ pwindow = do_QueryInterface(window);
+
+ return pwindow.forget();
+}
+
+/* static */
+void
+nsContentUtils::WarnScriptWasIgnored(nsIDocument* aDocument)
+{
+ nsAutoString msg;
+ if (aDocument) {
+ nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
+ if (uri) {
+ msg.Append(NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()));
+ msg.AppendLiteral(" : ");
+ }
+ }
+ msg.AppendLiteral("Unable to run script because scripts are blocked internally.");
+
+ LogSimpleConsoleError(msg, "DOM");
+}
+
+/* static */
+void
+nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable)
+{
+ nsCOMPtr<nsIRunnable> runnable = aRunnable;
+ if (!runnable) {
+ return;
+ }
+
+ if (sScriptBlockerCount) {
+ sBlockedScriptRunners->AppendElement(runnable.forget());
+ return;
+ }
+
+ runnable->Run();
+}
+
+/* static */
+void
+nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) {
+ nsCOMPtr<nsIRunnable> runnable = aRunnable;
+ AddScriptRunner(runnable.forget());
+}
+
+/* static */
+void
+nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable)
+{
+ MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
+ CycleCollectedJSContext::Get()->RunInStableState(Move(aRunnable));
+}
+
+/* static */
+void
+nsContentUtils::RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable)
+{
+ MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
+ CycleCollectedJSContext::Get()->RunInMetastableState(Move(aRunnable));
+}
+
+void
+nsContentUtils::EnterMicroTask()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ ++sMicroTaskLevel;
+}
+
+void
+nsContentUtils::LeaveMicroTask()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (--sMicroTaskLevel == 0) {
+ PerformMainThreadMicroTaskCheckpoint();
+ }
+}
+
+bool
+nsContentUtils::IsInMicroTask()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ return sMicroTaskLevel != 0;
+}
+
+uint32_t
+nsContentUtils::MicroTaskLevel()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ return sMicroTaskLevel;
+}
+
+void
+nsContentUtils::SetMicroTaskLevel(uint32_t aLevel)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ sMicroTaskLevel = aLevel;
+}
+
+void
+nsContentUtils::PerformMainThreadMicroTaskCheckpoint()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsDOMMutationObserver::HandleMutations();
+}
+
+/*
+ * Helper function for nsContentUtils::ProcessViewportInfo.
+ *
+ * Handles a single key=value pair. If it corresponds to a valid viewport
+ * attribute, add it to the document header data. No validation is done on the
+ * value itself (this is done at display time).
+ */
+static void ProcessViewportToken(nsIDocument *aDocument,
+ const nsAString &token) {
+
+ /* Iterators. */
+ nsAString::const_iterator tip, tail, end;
+ token.BeginReading(tip);
+ tail = tip;
+ token.EndReading(end);
+
+ /* Move tip to the '='. */
+ while ((tip != end) && (*tip != '='))
+ ++tip;
+
+ /* If we didn't find an '=', punt. */
+ if (tip == end)
+ return;
+
+ /* Extract the key and value. */
+ const nsAString &key =
+ nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(Substring(tail, tip),
+ true);
+ const nsAString &value =
+ nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(Substring(++tip, end),
+ true);
+
+ /* Check for known keys. If we find a match, insert the appropriate
+ * information into the document header. */
+ nsCOMPtr<nsIAtom> key_atom = NS_Atomize(key);
+ if (key_atom == nsGkAtoms::height)
+ aDocument->SetHeaderData(nsGkAtoms::viewport_height, value);
+ else if (key_atom == nsGkAtoms::width)
+ aDocument->SetHeaderData(nsGkAtoms::viewport_width, value);
+ else if (key_atom == nsGkAtoms::initial_scale)
+ aDocument->SetHeaderData(nsGkAtoms::viewport_initial_scale, value);
+ else if (key_atom == nsGkAtoms::minimum_scale)
+ aDocument->SetHeaderData(nsGkAtoms::viewport_minimum_scale, value);
+ else if (key_atom == nsGkAtoms::maximum_scale)
+ aDocument->SetHeaderData(nsGkAtoms::viewport_maximum_scale, value);
+ else if (key_atom == nsGkAtoms::user_scalable)
+ aDocument->SetHeaderData(nsGkAtoms::viewport_user_scalable, value);
+}
+
+#define IS_SEPARATOR(c) ((c == '=') || (c == ',') || (c == ';') || \
+ (c == '\t') || (c == '\n') || (c == '\r'))
+
+/* static */
+nsresult
+nsContentUtils::ProcessViewportInfo(nsIDocument *aDocument,
+ const nsAString &viewportInfo) {
+
+ /* We never fail. */
+ nsresult rv = NS_OK;
+
+ aDocument->SetHeaderData(nsGkAtoms::viewport, viewportInfo);
+
+ /* Iterators. */
+ nsAString::const_iterator tip, tail, end;
+ viewportInfo.BeginReading(tip);
+ tail = tip;
+ viewportInfo.EndReading(end);
+
+ /* Read the tip to the first non-separator character. */
+ while ((tip != end) && (IS_SEPARATOR(*tip) || nsCRT::IsAsciiSpace(*tip)))
+ ++tip;
+
+ /* Read through and find tokens separated by separators. */
+ while (tip != end) {
+
+ /* Synchronize tip and tail. */
+ tail = tip;
+
+ /* Advance tip past non-separator characters. */
+ while ((tip != end) && !IS_SEPARATOR(*tip))
+ ++tip;
+
+ /* Allow white spaces that surround the '=' character */
+ if ((tip != end) && (*tip == '=')) {
+ ++tip;
+
+ while ((tip != end) && nsCRT::IsAsciiSpace(*tip))
+ ++tip;
+
+ while ((tip != end) && !(IS_SEPARATOR(*tip) || nsCRT::IsAsciiSpace(*tip)))
+ ++tip;
+ }
+
+ /* Our token consists of the characters between tail and tip. */
+ ProcessViewportToken(aDocument, Substring(tail, tip));
+
+ /* Skip separators. */
+ while ((tip != end) && (IS_SEPARATOR(*tip) || nsCRT::IsAsciiSpace(*tip)))
+ ++tip;
+ }
+
+ return rv;
+
+}
+
+#undef IS_SEPARATOR
+
+/* static */
+void
+nsContentUtils::HidePopupsInDocument(nsIDocument* aDocument)
+{
+#ifdef MOZ_XUL
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ if (pm && aDocument) {
+ nsCOMPtr<nsIDocShellTreeItem> docShellToHide = aDocument->GetDocShell();
+ if (docShellToHide)
+ pm->HidePopupsInDocShell(docShellToHide);
+ }
+#endif
+}
+
+/* static */
+already_AddRefed<nsIDragSession>
+nsContentUtils::GetDragSession()
+{
+ nsCOMPtr<nsIDragSession> dragSession;
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (dragService)
+ dragService->GetCurrentSession(getter_AddRefs(dragSession));
+ return dragSession.forget();
+}
+
+/* static */
+nsresult
+nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent)
+{
+ if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) {
+ return NS_OK;
+ }
+
+ // For dragstart events, the data transfer object is
+ // created before the event fires, so it should already be set. For other
+ // drag events, get the object from the drag session.
+ NS_ASSERTION(aDragEvent->mMessage != eDragStart,
+ "draggesture event created without a dataTransfer");
+
+ nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
+ NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
+
+ nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
+ nsCOMPtr<DataTransfer> initialDataTransfer;
+ dragSession->GetDataTransfer(getter_AddRefs(dataTransfer));
+ if (dataTransfer) {
+ initialDataTransfer = do_QueryInterface(dataTransfer);
+ if (!initialDataTransfer) {
+ return NS_ERROR_FAILURE;
+ }
+ } else {
+ // A dataTransfer won't exist when a drag was started by some other
+ // means, for instance calling the drag service directly, or a drag
+ // from another application. In either case, a new dataTransfer should
+ // be created that reflects the data.
+ initialDataTransfer =
+ new DataTransfer(aDragEvent->mTarget, aDragEvent->mMessage, true, -1);
+
+ // now set it in the drag session so we don't need to create it again
+ dragSession->SetDataTransfer(initialDataTransfer);
+ }
+
+ bool isCrossDomainSubFrameDrop = false;
+ if (aDragEvent->mMessage == eDrop) {
+ isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
+ }
+
+ // each event should use a clone of the original dataTransfer.
+ initialDataTransfer->Clone(aDragEvent->mTarget, aDragEvent->mMessage,
+ aDragEvent->mUserCancelled,
+ isCrossDomainSubFrameDrop,
+ getter_AddRefs(aDragEvent->mDataTransfer));
+ if (NS_WARN_IF(!aDragEvent->mDataTransfer)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // for the dragenter and dragover events, initialize the drop effect
+ // from the drop action, which platform specific widget code sets before
+ // the event is fired based on the keyboard state.
+ if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
+ uint32_t action, effectAllowed;
+ dragSession->GetDragAction(&action);
+ aDragEvent->mDataTransfer->GetEffectAllowedInt(&effectAllowed);
+ aDragEvent->mDataTransfer->SetDropEffectInt(
+ FilterDropEffect(action, effectAllowed));
+ }
+ else if (aDragEvent->mMessage == eDrop ||
+ aDragEvent->mMessage == eDragEnd) {
+ // For the drop and dragend events, set the drop effect based on the
+ // last value that the dropEffect had. This will have been set in
+ // EventStateManager::PostHandleEvent for the last dragenter or
+ // dragover event.
+ uint32_t dropEffect;
+ initialDataTransfer->GetDropEffectInt(&dropEffect);
+ aDragEvent->mDataTransfer->SetDropEffectInt(dropEffect);
+ }
+
+ return NS_OK;
+}
+
+/* static */
+uint32_t
+nsContentUtils::FilterDropEffect(uint32_t aAction, uint32_t aEffectAllowed)
+{
+ // It is possible for the drag action to include more than one action, but
+ // the widget code which sets the action from the keyboard state should only
+ // be including one. If multiple actions were set, we just consider them in
+ // the following order:
+ // copy, link, move
+ if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
+ aAction = nsIDragService::DRAGDROP_ACTION_COPY;
+ else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
+ aAction = nsIDragService::DRAGDROP_ACTION_LINK;
+ else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
+ aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
+
+ // Filter the action based on the effectAllowed. If the effectAllowed
+ // doesn't include the action, then that action cannot be done, so adjust
+ // the action to something that is allowed. For a copy, adjust to move or
+ // link. For a move, adjust to copy or link. For a link, adjust to move or
+ // link. Otherwise, use none.
+ if (aAction & aEffectAllowed ||
+ aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
+ return aAction;
+ if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
+ return nsIDragService::DRAGDROP_ACTION_MOVE;
+ if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
+ return nsIDragService::DRAGDROP_ACTION_COPY;
+ if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
+ return nsIDragService::DRAGDROP_ACTION_LINK;
+ return nsIDragService::DRAGDROP_ACTION_NONE;
+}
+
+/* static */
+bool
+nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession,
+ WidgetDragEvent* aDropEvent)
+{
+ nsCOMPtr<nsIContent> target = do_QueryInterface(aDropEvent->mOriginalTarget);
+ if (!target) {
+ return true;
+ }
+
+ nsIDocument* targetDoc = target->OwnerDoc();
+ nsPIDOMWindowOuter* targetWin = targetDoc->GetWindow();
+ if (!targetWin) {
+ return true;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> tdsti = targetWin->GetDocShell();
+ if (!tdsti) {
+ return true;
+ }
+
+ // Always allow dropping onto chrome shells.
+ if (tdsti->ItemType() == nsIDocShellTreeItem::typeChrome) {
+ return false;
+ }
+
+ // If there is no source node, then this is a drag from another
+ // application, which should be allowed.
+ nsCOMPtr<nsIDOMDocument> sourceDocument;
+ aDragSession->GetSourceDocument(getter_AddRefs(sourceDocument));
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(sourceDocument);
+ if (doc) {
+ // Get each successive parent of the source document and compare it to
+ // the drop document. If they match, then this is a drag from a child frame.
+ do {
+ doc = doc->GetParentDocument();
+ if (doc == targetDoc) {
+ // The drag is from a child frame.
+ return true;
+ }
+ } while (doc);
+ }
+
+ return false;
+}
+
+/* static */
+bool
+nsContentUtils::URIIsLocalFile(nsIURI *aURI)
+{
+ bool isFile;
+ nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
+
+ // Important: we do NOT test the entire URI chain here!
+ return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
+ nsIProtocolHandler::URI_IS_LOCAL_FILE,
+ &isFile)) &&
+ isFile;
+}
+
+/* static */
+nsIScriptContext*
+nsContentUtils::GetContextForEventHandlers(nsINode* aNode,
+ nsresult* aRv)
+{
+ *aRv = NS_OK;
+ bool hasHadScriptObject = true;
+ nsIScriptGlobalObject* sgo =
+ aNode->OwnerDoc()->GetScriptHandlingObject(hasHadScriptObject);
+ // It is bad if the document doesn't have event handling context,
+ // but it used to have one.
+ if (!sgo && hasHadScriptObject) {
+ *aRv = NS_ERROR_UNEXPECTED;
+ return nullptr;
+ }
+
+ if (sgo) {
+ nsIScriptContext* scx = sgo->GetContext();
+ // Bad, no context from script global object!
+ if (!scx) {
+ *aRv = NS_ERROR_UNEXPECTED;
+ return nullptr;
+ }
+ return scx;
+ }
+
+ return nullptr;
+}
+
+/* static */
+JSContext *
+nsContentUtils::GetCurrentJSContext()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(IsInitialized());
+ if (!IsJSAPIActive()) {
+ return nullptr;
+ }
+ return danger::GetJSContext();
+}
+
+/* static */
+JSContext *
+nsContentUtils::GetCurrentJSContextForThread()
+{
+ MOZ_ASSERT(IsInitialized());
+ if (MOZ_LIKELY(NS_IsMainThread())) {
+ return GetCurrentJSContext();
+ } else {
+ return workers::GetCurrentThreadJSContext();
+ }
+}
+
+template<typename StringType, typename CharType>
+void
+_ASCIIToLowerInSitu(StringType& aStr)
+{
+ CharType* iter = aStr.BeginWriting();
+ CharType* end = aStr.EndWriting();
+ MOZ_ASSERT(iter && end);
+
+ while (iter != end) {
+ CharType c = *iter;
+ if (c >= 'A' && c <= 'Z') {
+ *iter = c + ('a' - 'A');
+ }
+ ++iter;
+ }
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToLower(nsAString& aStr)
+{
+ return _ASCIIToLowerInSitu<nsAString, char16_t>(aStr);
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToLower(nsACString& aStr)
+{
+ return _ASCIIToLowerInSitu<nsACString, char>(aStr);
+}
+
+template<typename StringType, typename CharType>
+void
+_ASCIIToLowerCopy(const StringType& aSource, StringType& aDest)
+{
+ uint32_t len = aSource.Length();
+ aDest.SetLength(len);
+ MOZ_ASSERT(aDest.Length() == len);
+
+ CharType* dest = aDest.BeginWriting();
+ MOZ_ASSERT(dest);
+
+ const CharType* iter = aSource.BeginReading();
+ const CharType* end = aSource.EndReading();
+ while (iter != end) {
+ CharType c = *iter;
+ *dest = (c >= 'A' && c <= 'Z') ?
+ c + ('a' - 'A') : c;
+ ++iter;
+ ++dest;
+ }
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest) {
+ return _ASCIIToLowerCopy<nsAString, char16_t>(aSource, aDest);
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToLower(const nsACString& aSource, nsACString& aDest) {
+ return _ASCIIToLowerCopy<nsACString, char>(aSource, aDest);
+}
+
+
+template<typename StringType, typename CharType>
+void
+_ASCIIToUpperInSitu(StringType& aStr)
+{
+ CharType* iter = aStr.BeginWriting();
+ CharType* end = aStr.EndWriting();
+ MOZ_ASSERT(iter && end);
+
+ while (iter != end) {
+ CharType c = *iter;
+ if (c >= 'a' && c <= 'z') {
+ *iter = c + ('A' - 'a');
+ }
+ ++iter;
+ }
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToUpper(nsAString& aStr)
+{
+ return _ASCIIToUpperInSitu<nsAString, char16_t>(aStr);
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToUpper(nsACString& aStr)
+{
+ return _ASCIIToUpperInSitu<nsACString, char>(aStr);
+}
+
+template<typename StringType, typename CharType>
+void
+_ASCIIToUpperCopy(const StringType& aSource, StringType& aDest)
+{
+ uint32_t len = aSource.Length();
+ aDest.SetLength(len);
+ MOZ_ASSERT(aDest.Length() == len);
+
+ CharType* dest = aDest.BeginWriting();
+ MOZ_ASSERT(dest);
+
+ const CharType* iter = aSource.BeginReading();
+ const CharType* end = aSource.EndReading();
+ while (iter != end) {
+ CharType c = *iter;
+ *dest = (c >= 'a' && c <= 'z') ?
+ c + ('A' - 'a') : c;
+ ++iter;
+ ++dest;
+ }
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest)
+{
+ return _ASCIIToUpperCopy<nsAString, char16_t>(aSource, aDest);
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToUpper(const nsACString& aSource, nsACString& aDest)
+{
+ return _ASCIIToUpperCopy<nsACString, char>(aSource, aDest);
+}
+
+/* static */
+bool
+nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
+ const nsAString& aStr2)
+{
+ uint32_t len = aStr1.Length();
+ if (len != aStr2.Length()) {
+ return false;
+ }
+
+ const char16_t* str1 = aStr1.BeginReading();
+ const char16_t* str2 = aStr2.BeginReading();
+ const char16_t* end = str1 + len;
+
+ while (str1 < end) {
+ char16_t c1 = *str1++;
+ char16_t c2 = *str2++;
+
+ // First check if any bits other than the 0x0020 differs
+ if ((c1 ^ c2) & 0xffdf) {
+ return false;
+ }
+
+ // We know they can only differ in the 0x0020 bit.
+ // Likely the two chars are the same, so check that first
+ if (c1 != c2) {
+ // They do differ, but since it's only in the 0x0020 bit, check if it's
+ // the same ascii char, but just differing in case
+ char16_t c1Upper = c1 & 0xffdf;
+ if (!('A' <= c1Upper && c1Upper <= 'Z')) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/* static */
+bool
+nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr)
+{
+ const char16_t* iter = aStr.BeginReading();
+ const char16_t* end = aStr.EndReading();
+ while (iter != end) {
+ char16_t c = *iter;
+ if (c >= 'A' && c <= 'Z') {
+ return true;
+ }
+ ++iter;
+ }
+
+ return false;
+}
+
+/* static */
+nsIInterfaceRequestor*
+nsContentUtils::SameOriginChecker()
+{
+ if (!sSameOriginChecker) {
+ sSameOriginChecker = new SameOriginCheckerImpl();
+ NS_ADDREF(sSameOriginChecker);
+ }
+ return sSameOriginChecker;
+}
+
+/* static */
+nsresult
+nsContentUtils::CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel)
+{
+ if (!nsContentUtils::GetSecurityManager())
+ return NS_ERROR_NOT_AVAILABLE;
+
+ nsCOMPtr<nsIPrincipal> oldPrincipal;
+ nsContentUtils::GetSecurityManager()->
+ GetChannelResultPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
+
+ nsCOMPtr<nsIURI> newURI;
+ aNewChannel->GetURI(getter_AddRefs(newURI));
+ nsCOMPtr<nsIURI> newOriginalURI;
+ aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
+
+ NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
+
+ nsresult rv = oldPrincipal->CheckMayLoad(newURI, false, false);
+ if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
+ rv = oldPrincipal->CheckMayLoad(newOriginalURI, false, false);
+ }
+
+ return rv;
+}
+
+NS_IMPL_ISUPPORTS(SameOriginCheckerImpl,
+ nsIChannelEventSink,
+ nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+SameOriginCheckerImpl::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
+ nsIChannel* aNewChannel,
+ uint32_t aFlags,
+ nsIAsyncVerifyRedirectCallback* cb)
+{
+ NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
+
+ nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
+ if (NS_SUCCEEDED(rv)) {
+ cb->OnRedirectVerifyCallback(NS_OK);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult)
+{
+ return QueryInterface(aIID, aResult);
+}
+
+/* static */
+nsresult
+nsContentUtils::GetASCIIOrigin(nsIPrincipal* aPrincipal, nsACString& aOrigin)
+{
+ NS_PRECONDITION(aPrincipal, "missing principal");
+
+ aOrigin.Truncate();
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (uri) {
+ return GetASCIIOrigin(uri, aOrigin);
+ }
+
+ aOrigin.AssignLiteral("null");
+
+ return NS_OK;
+}
+
+/* static */
+nsresult
+nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsACString& aOrigin)
+{
+ NS_PRECONDITION(aURI, "missing uri");
+
+ // For Blob URI we have to return the origin of page using its principal.
+ nsCOMPtr<nsIURIWithPrincipal> uriWithPrincipal = do_QueryInterface(aURI);
+ if (uriWithPrincipal) {
+ nsCOMPtr<nsIPrincipal> principal;
+ uriWithPrincipal->GetPrincipal(getter_AddRefs(principal));
+
+ if (principal) {
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = principal->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (uri && uri != aURI) {
+ return GetASCIIOrigin(uri, aOrigin);
+ }
+ }
+ }
+
+ aOrigin.Truncate();
+
+ nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
+ NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
+
+ nsCString host;
+ nsresult rv = uri->GetAsciiHost(host);
+
+ if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
+ nsCString scheme;
+ rv = uri->GetScheme(scheme);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t port = -1;
+ uri->GetPort(&port);
+ if (port != -1 && port == NS_GetDefaultPort(scheme.get()))
+ port = -1;
+
+ nsCString hostPort;
+ rv = NS_GenerateHostPort(host, port, hostPort);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort;
+ }
+ else {
+ aOrigin.AssignLiteral("null");
+ }
+
+ return NS_OK;
+}
+
+/* static */
+nsresult
+nsContentUtils::GetUTFOrigin(nsIPrincipal* aPrincipal, nsAString& aOrigin)
+{
+ NS_PRECONDITION(aPrincipal, "missing principal");
+
+ aOrigin.Truncate();
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (uri) {
+ return GetUTFOrigin(uri, aOrigin);
+ }
+
+ aOrigin.AssignLiteral("null");
+
+ return NS_OK;
+}
+
+/* static */
+nsresult
+nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsAString& aOrigin)
+{
+ NS_PRECONDITION(aURI, "missing uri");
+
+ // For Blob URI we have to return the origin of page using its principal.
+ nsCOMPtr<nsIURIWithPrincipal> uriWithPrincipal = do_QueryInterface(aURI);
+ if (uriWithPrincipal) {
+ nsCOMPtr<nsIPrincipal> principal;
+ uriWithPrincipal->GetPrincipal(getter_AddRefs(principal));
+
+ if (principal) {
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = principal->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (uri && uri != aURI) {
+ return GetUTFOrigin(uri, aOrigin);
+ }
+ } else {
+ // We are probably dealing with an unknown blob URL.
+ bool isBlobURL = false;
+ nsresult rv = aURI->SchemeIs(BLOBURI_SCHEME, &isBlobURL);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (isBlobURL) {
+ nsAutoCString path;
+ rv = aURI->GetPath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), path);
+ if (NS_FAILED(rv)) {
+ aOrigin.AssignLiteral("null");
+ return NS_OK;
+ }
+
+ return GetUTFOrigin(uri, aOrigin);
+ }
+ }
+ }
+
+ aOrigin.Truncate();
+
+ nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
+ NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
+
+ nsCString host;
+ nsresult rv = uri->GetHost(host);
+
+ if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
+ nsCString scheme;
+ rv = uri->GetScheme(scheme);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t port = -1;
+ uri->GetPort(&port);
+ if (port != -1 && port == NS_GetDefaultPort(scheme.get()))
+ port = -1;
+
+ nsCString hostPort;
+ rv = NS_GenerateHostPort(host, port, hostPort);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aOrigin = NS_ConvertUTF8toUTF16(
+ scheme + NS_LITERAL_CSTRING("://") + hostPort);
+ }
+ else {
+ aOrigin.AssignLiteral("null");
+ }
+
+ return NS_OK;
+}
+
+/* static */
+bool
+nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal, nsIChannel* aChannel, bool aAllowIfInheritsPrincipal)
+{
+ nsCOMPtr<nsIURI> channelURI;
+ nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return NS_SUCCEEDED(aPrincipal->CheckMayLoad(channelURI, false, aAllowIfInheritsPrincipal));
+}
+
+nsContentTypeParser::nsContentTypeParser(const nsAString& aString)
+ : mString(aString), mService(nullptr)
+{
+ CallGetService("@mozilla.org/network/mime-hdrparam;1", &mService);
+}
+
+nsContentTypeParser::~nsContentTypeParser()
+{
+ NS_IF_RELEASE(mService);
+}
+
+nsresult
+nsContentTypeParser::GetParameter(const char* aParameterName,
+ nsAString& aResult) const
+{
+ NS_ENSURE_TRUE(mService, NS_ERROR_FAILURE);
+ return mService->GetParameterHTTP(mString, aParameterName,
+ EmptyCString(), false, nullptr,
+ aResult);
+}
+
+nsresult
+nsContentTypeParser::GetType(nsAString& aResult) const
+{
+ nsresult rv = GetParameter(nullptr, aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsContentUtils::ASCIIToLower(aResult);
+ return NS_OK;
+}
+
+/* static */
+
+bool
+nsContentUtils::CanAccessNativeAnon()
+{
+ return LegacyIsCallerChromeOrNativeCode() || IsCallerContentXBL();
+}
+
+/* static */ nsresult
+nsContentUtils::DispatchXULCommand(nsIContent* aTarget,
+ bool aTrusted,
+ nsIDOMEvent* aSourceEvent,
+ nsIPresShell* aShell,
+ bool aCtrl,
+ bool aAlt,
+ bool aShift,
+ bool aMeta)
+{
+ NS_ENSURE_STATE(aTarget);
+ nsIDocument* doc = aTarget->OwnerDoc();
+ nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
+ NS_ENSURE_STATE(domDoc);
+ nsCOMPtr<nsIDOMEvent> event;
+ domDoc->CreateEvent(NS_LITERAL_STRING("xulcommandevent"),
+ getter_AddRefs(event));
+ nsCOMPtr<nsIDOMXULCommandEvent> xulCommand = do_QueryInterface(event);
+ nsresult rv = xulCommand->InitCommandEvent(NS_LITERAL_STRING("command"),
+ true, true, doc->GetInnerWindow(),
+ 0, aCtrl, aAlt, aShift, aMeta,
+ aSourceEvent);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aShell) {
+ nsEventStatus status = nsEventStatus_eIgnore;
+ nsCOMPtr<nsIPresShell> kungFuDeathGrip = aShell;
+ return aShell->HandleDOMEventWithTarget(aTarget, event, &status);
+ }
+
+ nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
+ NS_ENSURE_STATE(target);
+ bool dummy;
+ return target->DispatchEvent(event, &dummy);
+}
+
+// static
+nsresult
+nsContentUtils::WrapNative(JSContext *cx, nsISupports *native,
+ nsWrapperCache *cache, const nsIID* aIID,
+ JS::MutableHandle<JS::Value> vp, bool aAllowWrapping)
+{
+ MOZ_ASSERT(cx == GetCurrentJSContext());
+
+ if (!native) {
+ vp.setNull();
+
+ return NS_OK;
+ }
+
+ JSObject *wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
+ if (wrapper) {
+ return NS_OK;
+ }
+
+ NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
+
+ if (!NS_IsMainThread()) {
+ MOZ_CRASH();
+ }
+
+ nsresult rv = NS_OK;
+ JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
+ rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
+ aAllowWrapping, vp);
+ return rv;
+}
+
+nsresult
+nsContentUtils::CreateArrayBuffer(JSContext *aCx, const nsACString& aData,
+ JSObject** aResult)
+{
+ if (!aCx) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t dataLen = aData.Length();
+ *aResult = JS_NewArrayBuffer(aCx, dataLen);
+ if (!*aResult) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (dataLen > 0) {
+ NS_ASSERTION(JS_IsArrayBufferObject(*aResult), "What happened?");
+ JS::AutoCheckCannotGC nogc;
+ bool isShared;
+ memcpy(JS_GetArrayBufferData(*aResult, &isShared, nogc), aData.BeginReading(), dataLen);
+ MOZ_ASSERT(!isShared);
+ }
+
+ return NS_OK;
+}
+
+void
+nsContentUtils::StripNullChars(const nsAString& aInStr, nsAString& aOutStr)
+{
+ // In common cases where we don't have nulls in the
+ // string we can simple simply bypass the checking code.
+ int32_t firstNullPos = aInStr.FindChar('\0');
+ if (firstNullPos == kNotFound) {
+ aOutStr.Assign(aInStr);
+ return;
+ }
+
+ aOutStr.SetCapacity(aInStr.Length() - 1);
+ nsAString::const_iterator start, end;
+ aInStr.BeginReading(start);
+ aInStr.EndReading(end);
+ while (start != end) {
+ if (*start != '\0')
+ aOutStr.Append(*start);
+ ++start;
+ }
+}
+
+struct ClassMatchingInfo {
+ nsAttrValue::AtomArray mClasses;
+ nsCaseTreatment mCaseTreatment;
+};
+
+// static
+bool
+nsContentUtils::MatchClassNames(nsIContent* aContent, int32_t aNamespaceID,
+ nsIAtom* aAtom, void* aData)
+{
+ // We can't match if there are no class names
+ const nsAttrValue* classAttr = aContent->GetClasses();
+ if (!classAttr) {
+ return false;
+ }
+
+ // need to match *all* of the classes
+ ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
+ uint32_t length = info->mClasses.Length();
+ if (!length) {
+ // If we actually had no classes, don't match.
+ return false;
+ }
+ uint32_t i;
+ for (i = 0; i < length; ++i) {
+ if (!classAttr->Contains(info->mClasses[i],
+ info->mCaseTreatment)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// static
+void
+nsContentUtils::DestroyClassNameArray(void* aData)
+{
+ ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
+ delete info;
+}
+
+// static
+void*
+nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
+ const nsString* aClasses)
+{
+ nsAttrValue attrValue;
+ attrValue.ParseAtomArray(*aClasses);
+ // nsAttrValue::Equals is sensitive to order, so we'll send an array
+ ClassMatchingInfo* info = new ClassMatchingInfo;
+ if (attrValue.Type() == nsAttrValue::eAtomArray) {
+ info->mClasses.SwapElements(*(attrValue.GetAtomArrayValue()));
+ } else if (attrValue.Type() == nsAttrValue::eAtom) {
+ info->mClasses.AppendElement(attrValue.GetAtomValue());
+ }
+
+ info->mCaseTreatment =
+ aRootNode->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks ?
+ eIgnoreCase : eCaseMatters;
+ return info;
+}
+
+// static
+bool
+nsContentUtils::IsFocusedContent(const nsIContent* aContent)
+{
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+
+ return fm && fm->GetFocusedContent() == aContent;
+}
+
+bool
+nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent)
+{
+ //XXXsmaug Shadow DOM spec issue!
+ // We may need to change this to GetComposedDoc().
+ nsIDocument* doc = aContent->GetUncomposedDoc();
+ if (!doc) {
+ return false;
+ }
+
+ // If the subdocument lives in another process, the frame is
+ // tabbable.
+ if (EventStateManager::IsRemoteTarget(aContent)) {
+ return true;
+ }
+
+ // XXXbz should this use OwnerDoc() for GetSubDocumentFor?
+ // sXBL/XBL2 issue!
+ nsIDocument* subDoc = doc->GetSubDocumentFor(aContent);
+ if (!subDoc) {
+ return false;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = subDoc->GetDocShell();
+ if (!docShell) {
+ return false;
+ }
+
+ nsCOMPtr<nsIContentViewer> contentViewer;
+ docShell->GetContentViewer(getter_AddRefs(contentViewer));
+ if (!contentViewer) {
+ return false;
+ }
+
+ nsCOMPtr<nsIContentViewer> zombieViewer;
+ contentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
+
+ // If there are 2 viewers for the current docshell, that
+ // means the current document is a zombie document.
+ // Only navigate into the subdocument if it's not a zombie.
+ return !zombieViewer;
+}
+
+bool
+nsContentUtils::IsUserFocusIgnored(nsINode* aNode)
+{
+ if (!nsGenericHTMLFrameElement::BrowserFramesEnabled()) {
+ return false;
+ }
+
+ // Check if our mozbrowser iframe ancestors has ignoreuserfocus attribute.
+ while (aNode) {
+ nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aNode);
+ if (browserFrame &&
+ aNode->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::ignoreuserfocus) &&
+ browserFrame->GetReallyIsBrowserOrApp()) {
+ return true;
+ }
+ nsPIDOMWindowOuter* win = aNode->OwnerDoc()->GetWindow();
+ aNode = win ? win->GetFrameElementInternal() : nullptr;
+ }
+
+ return false;
+}
+
+bool
+nsContentUtils::HasScrollgrab(nsIContent* aContent)
+{
+ nsGenericHTMLElement* element = nsGenericHTMLElement::FromContentOrNull(aContent);
+ return element && element->Scrollgrab();
+}
+
+void
+nsContentUtils::FlushLayoutForTree(nsPIDOMWindowOuter* aWindow)
+{
+ if (!aWindow) {
+ return;
+ }
+
+ // Note that because FlushPendingNotifications flushes parents, this
+ // is O(N^2) in docshell tree depth. However, the docshell tree is
+ // usually pretty shallow.
+
+ if (nsCOMPtr<nsIDocument> doc = aWindow->GetDoc()) {
+ doc->FlushPendingNotifications(Flush_Layout);
+ }
+
+ if (nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell()) {
+ int32_t i = 0, i_end;
+ docShell->GetChildCount(&i_end);
+ for (; i < i_end; ++i) {
+ nsCOMPtr<nsIDocShellTreeItem> item;
+ docShell->GetChildAt(i, getter_AddRefs(item));
+ if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
+ FlushLayoutForTree(win);
+ }
+ }
+ }
+}
+
+void nsContentUtils::RemoveNewlines(nsString &aString)
+{
+ // strip CR/LF and null
+ static const char badChars[] = {'\r', '\n', 0};
+ aString.StripChars(badChars);
+}
+
+void
+nsContentUtils::PlatformToDOMLineBreaks(nsString &aString)
+{
+ if (!PlatformToDOMLineBreaks(aString, fallible)) {
+ aString.AllocFailed(aString.Length());
+ }
+}
+
+bool
+nsContentUtils::PlatformToDOMLineBreaks(nsString& aString, const fallible_t& aFallible)
+{
+ if (aString.FindChar(char16_t('\r')) != -1) {
+ // Windows linebreaks: Map CRLF to LF:
+ if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
+ return false;
+ }
+
+ // Mac linebreaks: Map any remaining CR to LF:
+ if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
+ nsAString& aResultString)
+{
+ MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
+
+ uint32_t stringLen = NS_strlen(static_cast<char16_t*>(aBuf->Data()));
+
+ // SANITY CHECK: In case the nsStringBuffer isn't correctly
+ // null-terminated, let's clamp its length using the allocated size, to be
+ // sure the resulting string doesn't sample past the end of the the buffer.
+ // (Note that StorageSize() is in units of bytes, so we have to convert that
+ // to units of PRUnichars, and subtract 1 for the null-terminator.)
+ uint32_t allocStringLen = (aBuf->StorageSize() / sizeof(char16_t)) - 1;
+ MOZ_ASSERT(stringLen <= allocStringLen,
+ "string buffer lacks null terminator!");
+ stringLen = std::min(stringLen, allocStringLen);
+
+ aBuf->ToString(stringLen, aResultString);
+}
+
+nsIPresShell*
+nsContentUtils::FindPresShellForDocument(const nsIDocument* aDoc)
+{
+ const nsIDocument* doc = aDoc;
+ nsIDocument* displayDoc = doc->GetDisplayDocument();
+ if (displayDoc) {
+ doc = displayDoc;
+ }
+
+ nsIPresShell* shell = doc->GetShell();
+ if (shell) {
+ return shell;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
+ while (docShellTreeItem) {
+ // We may be in a display:none subdocument, or we may not have a presshell
+ // created yet.
+ // Walk the docshell tree to find the nearest container that has a presshell,
+ // and return that.
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
+ nsIPresShell* presShell = docShell->GetPresShell();
+ if (presShell) {
+ return presShell;
+ }
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ docShellTreeItem->GetParent(getter_AddRefs(parent));
+ docShellTreeItem = parent;
+ }
+
+ return nullptr;
+}
+
+nsIWidget*
+nsContentUtils::WidgetForDocument(const nsIDocument* aDoc)
+{
+ nsIPresShell* shell = FindPresShellForDocument(aDoc);
+ if (shell) {
+ nsViewManager* VM = shell->GetViewManager();
+ if (VM) {
+ nsView* rootView = VM->GetRootView();
+ if (rootView) {
+ nsView* displayRoot = nsViewManager::GetDisplayRootFor(rootView);
+ if (displayRoot) {
+ return displayRoot->GetNearestWidget(nullptr);
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+static already_AddRefed<LayerManager>
+LayerManagerForDocumentInternal(const nsIDocument *aDoc, bool aRequirePersistent)
+{
+ nsIWidget *widget = nsContentUtils::WidgetForDocument(aDoc);
+ if (widget) {
+ RefPtr<LayerManager> manager =
+ widget->GetLayerManager(aRequirePersistent ? nsIWidget::LAYER_MANAGER_PERSISTENT :
+ nsIWidget::LAYER_MANAGER_CURRENT);
+ return manager.forget();
+ }
+
+ return nullptr;
+}
+
+already_AddRefed<LayerManager>
+nsContentUtils::LayerManagerForDocument(const nsIDocument *aDoc)
+{
+ return LayerManagerForDocumentInternal(aDoc, false);
+}
+
+already_AddRefed<LayerManager>
+nsContentUtils::PersistentLayerManagerForDocument(nsIDocument *aDoc)
+{
+ return LayerManagerForDocumentInternal(aDoc, true);
+}
+
+bool
+nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal)
+{
+ if (IsSystemPrincipal(aPrincipal)) {
+ return true;
+ }
+
+ nsCOMPtr<nsIURI> princURI;
+ aPrincipal->GetURI(getter_AddRefs(princURI));
+
+ return princURI &&
+ ((sAllowXULXBL_for_file && SchemeIs(princURI, "file")) ||
+ IsSitePermAllow(aPrincipal, "allowXULXBL"));
+}
+
+bool
+nsContentUtils::IsPDFJSEnabled()
+{
+ nsCOMPtr<nsIStreamConverterService> convServ =
+ do_GetService("@mozilla.org/streamConverters;1");
+ nsresult rv = NS_ERROR_FAILURE;
+ bool canConvert = false;
+ if (convServ) {
+ rv = convServ->CanConvert("application/pdf", "text/html", &canConvert);
+ }
+ return NS_SUCCEEDED(rv) && canConvert;
+}
+
+bool
+nsContentUtils::IsSWFPlayerEnabled()
+{
+ nsCOMPtr<nsIStreamConverterService> convServ =
+ do_GetService("@mozilla.org/streamConverters;1");
+ nsresult rv = NS_ERROR_FAILURE;
+ bool canConvert = false;
+ if (convServ) {
+ rv = convServ->CanConvert("application/x-shockwave-flash",
+ "text/html", &canConvert);
+ }
+ return NS_SUCCEEDED(rv) && canConvert;
+}
+
+already_AddRefed<nsIDocumentLoaderFactory>
+nsContentUtils::FindInternalContentViewer(const nsACString& aType,
+ ContentViewerType* aLoaderType)
+{
+ if (aLoaderType) {
+ *aLoaderType = TYPE_UNSUPPORTED;
+ }
+
+ // one helper factory, please
+ nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
+ if (!catMan)
+ return nullptr;
+
+ nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
+
+ nsXPIDLCString contractID;
+ nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
+ PromiseFlatCString(aType).get(),
+ getter_Copies(contractID));
+ if (NS_SUCCEEDED(rv)) {
+ docFactory = do_GetService(contractID);
+ if (docFactory && aLoaderType) {
+ if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
+ *aLoaderType = TYPE_CONTENT;
+ else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
+ *aLoaderType = TYPE_PLUGIN;
+ else
+ *aLoaderType = TYPE_UNKNOWN;
+ }
+ return docFactory.forget();
+ }
+
+ if (DecoderTraits::IsSupportedInVideoDocument(aType)) {
+ docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
+ if (docFactory && aLoaderType) {
+ *aLoaderType = TYPE_CONTENT;
+ }
+ return docFactory.forget();
+ }
+
+ return nullptr;
+}
+
+static void
+ReportPatternCompileFailure(nsAString& aPattern, nsIDocument* aDocument,
+ JSContext* cx)
+{
+ MOZ_ASSERT(JS_IsExceptionPending(cx));
+
+ JS::RootedValue exn(cx);
+ if (!JS_GetPendingException(cx, &exn)) {
+ return;
+ }
+ if (!exn.isObject()) {
+ // If pending exception is not an object, it should be OOM.
+ return;
+ }
+
+ JS::AutoSaveExceptionState savedExc(cx);
+ JS::RootedObject exnObj(cx, &exn.toObject());
+ JS::RootedValue messageVal(cx);
+ if (!JS_GetProperty(cx, exnObj, "message", &messageVal)) {
+ return;
+ }
+ MOZ_ASSERT(messageVal.isString());
+
+ JS::RootedString messageStr(cx, messageVal.toString());
+ MOZ_ASSERT(messageStr);
+
+ nsAutoString wideMessage;
+ if (!AssignJSString(cx, wideMessage, messageStr)) {
+ return;
+ }
+
+ const nsString& pattern = PromiseFlatString(aPattern);
+ const char16_t *strings[] = { pattern.get(), wideMessage.get() };
+ nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+ NS_LITERAL_CSTRING("DOM"),
+ aDocument,
+ nsContentUtils::eDOM_PROPERTIES,
+ "PatternAttributeCompileFailure",
+ strings, ArrayLength(strings));
+ savedExc.drop();
+}
+
+// static
+bool
+nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
+ nsIDocument* aDocument)
+{
+ NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
+
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+
+ // We can use the junk scope here, because we're just using it for
+ // regexp evaluation, not actual script execution.
+ JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope());
+
+ // The pattern has to match the entire value.
+ aPattern.Insert(NS_LITERAL_STRING("^(?:"), 0);
+ aPattern.AppendLiteral(")$");
+
+ JS::Rooted<JSObject*> re(cx,
+ JS_NewUCRegExpObject(cx,
+ static_cast<char16_t*>(aPattern.BeginWriting()),
+ aPattern.Length(), JSREG_UNICODE));
+ if (!re) {
+ // Remove extra patterns added above to report with the original pattern.
+ aPattern.Cut(0, 4);
+ aPattern.Cut(aPattern.Length() - 2, 2);
+ ReportPatternCompileFailure(aPattern, aDocument, cx);
+ return true;
+ }
+
+ JS::Rooted<JS::Value> rval(cx, JS::NullValue());
+ size_t idx = 0;
+ if (!JS_ExecuteRegExpNoStatics(cx, re,
+ static_cast<char16_t*>(aValue.BeginWriting()),
+ aValue.Length(), &idx, true, &rval)) {
+ return true;
+ }
+
+ return !rval.isNull();
+}
+
+// static
+nsresult
+nsContentUtils::URIInheritsSecurityContext(nsIURI *aURI, bool *aResult)
+{
+ // Note: about:blank URIs do NOT inherit the security context from the
+ // current document, which is what this function tests for...
+ return NS_URIChainHasFlags(aURI,
+ nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
+ aResult);
+}
+
+// static
+bool
+nsContentUtils::ChannelShouldInheritPrincipal(nsIPrincipal* aLoadingPrincipal,
+ nsIURI* aURI,
+ bool aInheritForAboutBlank,
+ bool aForceInherit)
+{
+ MOZ_ASSERT(aLoadingPrincipal, "Can not check inheritance without a principal");
+
+ // Only tell the channel to inherit if it can't provide its own security context.
+ //
+ // XXX: If this is ever changed, check all callers for what owners
+ // they're passing in. In particular, see the code and
+ // comments in nsDocShell::LoadURI where we fall back on
+ // inheriting the owner if called from chrome. That would be
+ // very wrong if this code changed anything but channels that
+ // can't provide their own security context!
+ //
+ // If aForceInherit is true, we will inherit, even for a channel that
+ // can provide its own security context. This is used for srcdoc loads.
+ bool inherit = aForceInherit;
+ if (!inherit) {
+ bool uriInherits;
+ // We expect URIInheritsSecurityContext to return success for an
+ // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
+ // This condition needs to match the one in nsDocShell::InternalLoad where
+ // we're checking for things that will use the owner.
+ inherit =
+ (NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &uriInherits)) &&
+ (uriInherits || (aInheritForAboutBlank && NS_IsAboutBlank(aURI)))) ||
+ //
+ // file: uri special-casing
+ //
+ // If this is a file: load opened from another file: then it may need
+ // to inherit the owner from the referrer so they can script each other.
+ // If we don't set the owner explicitly then each file: gets an owner
+ // based on its own codebase later.
+ //
+ (URIIsLocalFile(aURI) &&
+ NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, false, false)) &&
+ // One more check here. CheckMayLoad will always return true for the
+ // system principal, but we do NOT want to inherit in that case.
+ !IsSystemPrincipal(aLoadingPrincipal));
+ }
+ return inherit;
+}
+
+/* static */
+bool
+nsContentUtils::IsFullScreenApiEnabled()
+{
+ return sIsFullScreenApiEnabled;
+}
+
+/* static */
+bool
+nsContentUtils::IsRequestFullScreenAllowed()
+{
+ return !sTrustedFullScreenOnly ||
+ EventStateManager::IsHandlingUserInput() ||
+ IsCallerChrome();
+}
+
+/* static */
+bool
+nsContentUtils::IsCutCopyAllowed()
+{
+ if ((!IsCutCopyRestricted() && EventStateManager::IsHandlingUserInput()) ||
+ IsCallerChrome()) {
+ return true;
+ }
+
+ return BasePrincipal::Cast(SubjectPrincipal())->AddonHasPermission(NS_LITERAL_STRING("clipboardWrite"));
+}
+
+/* static */
+bool
+nsContentUtils::IsFrameTimingEnabled()
+{
+ return sIsFrameTimingPrefEnabled;
+}
+
+/* static */
+bool
+nsContentUtils::HaveEqualPrincipals(nsIDocument* aDoc1, nsIDocument* aDoc2)
+{
+ if (!aDoc1 || !aDoc2) {
+ return false;
+ }
+ bool principalsEqual = false;
+ aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
+ return principalsEqual;
+}
+
+/* static */
+bool
+nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIContent* aContent)
+{
+#ifdef XP_MACOSX
+ // We control dispatch to all mac plugins.
+ return false;
+#else
+ if (!aContent || !aContent->IsInUncomposedDoc()) {
+ return false;
+ }
+
+ nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(aContent);
+ if (!olc) {
+ return false;
+ }
+
+ RefPtr<nsNPAPIPluginInstance> plugin;
+ olc->GetPluginInstance(getter_AddRefs(plugin));
+ if (!plugin) {
+ return false;
+ }
+
+ bool isWindowless = false;
+ nsresult res = plugin->IsWindowless(&isWindowless);
+ if (NS_FAILED(res)) {
+ return false;
+ }
+
+ return !isWindowless;
+#endif
+}
+
+/* static */
+void
+nsContentUtils::FireMutationEventsForDirectParsing(nsIDocument* aDoc,
+ nsIContent* aDest,
+ int32_t aOldChildCount)
+{
+ // Fire mutation events. Optimize for the case when there are no listeners
+ int32_t newChildCount = aDest->GetChildCount();
+ if (newChildCount && nsContentUtils::
+ HasMutationListeners(aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
+ AutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
+ NS_ASSERTION(newChildCount - aOldChildCount >= 0,
+ "What, some unexpected dom mutation has happened?");
+ childNodes.SetCapacity(newChildCount - aOldChildCount);
+ for (nsIContent* child = aDest->GetFirstChild();
+ child;
+ child = child->GetNextSibling()) {
+ childNodes.AppendElement(child);
+ }
+ FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
+ }
+}
+
+/* static */
+nsIDocument*
+nsContentUtils::GetRootDocument(nsIDocument* aDoc)
+{
+ if (!aDoc) {
+ return nullptr;
+ }
+ nsIDocument* doc = aDoc;
+ while (doc->GetParentDocument()) {
+ doc = doc->GetParentDocument();
+ }
+ return doc;
+}
+
+/* static */
+bool
+nsContentUtils::IsInPointerLockContext(nsPIDOMWindowOuter* aWin)
+{
+ if (!aWin) {
+ return false;
+ }
+
+ nsCOMPtr<nsIDocument> pointerLockedDoc =
+ do_QueryReferent(EventStateManager::sPointerLockedDoc);
+ if (!pointerLockedDoc || !pointerLockedDoc->GetWindow()) {
+ return false;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> lockTop =
+ pointerLockedDoc->GetWindow()->GetScriptableTop();
+ nsCOMPtr<nsPIDOMWindowOuter> top = aWin->GetScriptableTop();
+
+ return top == lockTop;
+}
+
+// static
+int32_t
+nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
+ int32_t aOffset)
+{
+ // The structure of the anonymous frames within a text control frame is
+ // an optional block frame, followed by an optional br frame.
+
+ // If the offset frame has a child, then this frame is the block which
+ // has the text frames (containing the content) as its children. This will
+ // be the case if we click to the right of any of the text frames, or at the
+ // bottom of the text area.
+ nsIFrame* firstChild = aOffsetFrame->PrincipalChildList().FirstChild();
+ if (firstChild) {
+ // In this case, the passed-in offset is incorrect, and we want the length
+ // of the entire content in the text control frame.
+ return firstChild->GetContent()->Length();
+ }
+
+ if (aOffsetFrame->GetPrevSibling() &&
+ !aOffsetFrame->GetNextSibling()) {
+ // In this case, we're actually within the last frame, which is a br
+ // frame. Our offset should therefore be the length of the first child of
+ // our parent.
+ int32_t aOutOffset =
+ aOffsetFrame->GetParent()->PrincipalChildList().FirstChild()->GetContent()->Length();
+ return aOutOffset;
+ }
+
+ // Otherwise, we're within one of the text frames, in which case our offset
+ // has already been correctly calculated.
+ return aOffset;
+}
+
+// static
+void
+nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
+ Element* aRoot,
+ int32_t& aOutStartOffset,
+ int32_t& aOutEndOffset)
+{
+ MOZ_ASSERT(aSelection && aRoot);
+
+ if (!aSelection->RangeCount()) {
+ // Nothing selected
+ aOutStartOffset = aOutEndOffset = 0;
+ return;
+ }
+
+ nsCOMPtr<nsINode> anchorNode = aSelection->GetAnchorNode();
+ uint32_t anchorOffset = aSelection->AnchorOffset();
+ nsCOMPtr<nsINode> focusNode = aSelection->GetFocusNode();
+ uint32_t focusOffset = aSelection->FocusOffset();
+
+ // We have at most two children, consisting of an optional text node followed
+ // by an optional <br>.
+ NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
+ nsCOMPtr<nsIContent> firstChild = aRoot->GetFirstChild();
+#ifdef DEBUG
+ nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
+ NS_ASSERTION(anchorNode == aRoot || anchorNode == firstChild ||
+ anchorNode == lastChild, "Unexpected anchorNode");
+ NS_ASSERTION(focusNode == aRoot || focusNode == firstChild ||
+ focusNode == lastChild, "Unexpected focusNode");
+#endif
+ if (!firstChild || !firstChild->IsNodeOfType(nsINode::eTEXT)) {
+ // No text node, so everything is 0
+ anchorOffset = focusOffset = 0;
+ } else {
+ // First child is text. If the anchor/focus is already in the text node,
+ // or the start of the root node, no change needed. If it's in the root
+ // node but not the start, or in the trailing <br>, we need to set the
+ // offset to the end.
+ if ((anchorNode == aRoot && anchorOffset != 0) ||
+ (anchorNode != aRoot && anchorNode != firstChild)) {
+ anchorOffset = firstChild->Length();
+ }
+ if ((focusNode == aRoot && focusOffset != 0) ||
+ (focusNode != aRoot && focusNode != firstChild)) {
+ focusOffset = firstChild->Length();
+ }
+ }
+
+ // Make sure aOutStartOffset <= aOutEndOffset.
+ aOutStartOffset = std::min(anchorOffset, focusOffset);
+ aOutEndOffset = std::max(anchorOffset, focusOffset);
+}
+
+
+nsIEditor*
+nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext)
+{
+ nsCOMPtr<nsIDocShell> docShell(aPresContext->GetDocShell());
+ bool isEditable;
+ if (!docShell ||
+ NS_FAILED(docShell->GetEditable(&isEditable)) || !isEditable)
+ return nullptr;
+
+ nsCOMPtr<nsIEditor> editor;
+ docShell->GetEditor(getter_AddRefs(editor));
+ return editor;
+}
+
+bool
+nsContentUtils::IsContentInsertionPoint(nsIContent* aContent)
+{
+ // Check if the content is a XBL insertion point.
+ if (aContent->IsActiveChildrenElement()) {
+ return true;
+ }
+
+ // Check if the content is a web components content insertion point.
+ HTMLContentElement* contentElement =
+ HTMLContentElement::FromContent(aContent);
+ return contentElement && contentElement->IsInsertionPoint();
+}
+
+// static
+bool
+nsContentUtils::HasDistributedChildren(nsIContent* aContent)
+{
+ if (!aContent) {
+ return false;
+ }
+
+ if (aContent->GetShadowRoot()) {
+ // Children of a shadow root host are distributed
+ // to content insertion points in the shadow root.
+ return true;
+ }
+
+ ShadowRoot* shadow = ShadowRoot::FromNode(aContent);
+ if (shadow) {
+ // Children of a shadow root are distributed to
+ // the shadow insertion point of the younger shadow root.
+ return shadow->GetYoungerShadowRoot();
+ }
+
+ HTMLShadowElement* shadowEl = HTMLShadowElement::FromContent(aContent);
+ if (shadowEl && shadowEl->IsInsertionPoint()) {
+ // Children of a shadow insertion points are distributed
+ // to the insertion points in the older shadow root.
+ return shadowEl->GetOlderShadowRoot();
+ }
+
+ HTMLContentElement* contentEl = HTMLContentElement::FromContent(aContent);
+ if (contentEl && contentEl->IsInsertionPoint()) {
+ // Children of a content insertion point are distributed to the
+ // content insertion point if the content insertion point does
+ // not match any nodes (fallback content).
+ return contentEl->MatchedNodes().IsEmpty();
+ }
+
+ return false;
+}
+
+// static
+bool
+nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader)
+{
+ if (IsForbiddenSystemRequestHeader(aHeader)) {
+ return true;
+ }
+
+ return StringBeginsWith(aHeader, NS_LITERAL_CSTRING("proxy-"),
+ nsCaseInsensitiveCStringComparator()) ||
+ StringBeginsWith(aHeader, NS_LITERAL_CSTRING("sec-"),
+ nsCaseInsensitiveCStringComparator());
+}
+
+// static
+bool
+nsContentUtils::IsForbiddenSystemRequestHeader(const nsACString& aHeader)
+{
+ static const char *kInvalidHeaders[] = {
+ "accept-charset", "accept-encoding", "access-control-request-headers",
+ "access-control-request-method", "connection", "content-length",
+ "cookie", "cookie2", "date", "dnt", "expect", "host", "keep-alive",
+ "origin", "referer", "te", "trailer", "transfer-encoding", "upgrade", "via"
+ };
+ for (uint32_t i = 0; i < ArrayLength(kInvalidHeaders); ++i) {
+ if (aHeader.LowerCaseEqualsASCII(kInvalidHeaders[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// static
+bool
+nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader)
+{
+ return (aHeader.LowerCaseEqualsASCII("set-cookie") ||
+ aHeader.LowerCaseEqualsASCII("set-cookie2"));
+}
+
+// static
+bool
+nsContentUtils::IsAllowedNonCorsContentType(const nsACString& aHeaderValue)
+{
+ nsAutoCString contentType;
+ nsAutoCString unused;
+
+ nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ return contentType.LowerCaseEqualsLiteral("text/plain") ||
+ contentType.LowerCaseEqualsLiteral("application/x-www-form-urlencoded") ||
+ contentType.LowerCaseEqualsLiteral("multipart/form-data");
+}
+
+bool
+nsContentUtils::DOMWindowDumpEnabled()
+{
+#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
+ // In optimized builds we check a pref that controls if we should
+ // enable output from dump() or not, in debug builds it's always
+ // enabled.
+ return nsContentUtils::sDOMWindowDumpEnabled;
+#else
+ return true;
+#endif
+}
+
+bool
+nsContentUtils::DoNotTrackEnabled()
+{
+ return nsContentUtils::sDoNotTrackEnabled;
+}
+
+mozilla::LogModule*
+nsContentUtils::DOMDumpLog()
+{
+ return sDOMDumpLog;
+}
+
+bool
+nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep, nsAString& aResult,
+ const fallible_t& aFallible)
+{
+ aResult.Truncate();
+ return AppendNodeTextContent(aNode, aDeep, aResult, aFallible);
+}
+
+void
+nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep, nsAString& aResult)
+{
+ if (!GetNodeTextContent(aNode, aDeep, aResult, fallible)) {
+ NS_ABORT_OOM(0); // Unfortunately we don't know the allocation size
+ }
+}
+
+void
+nsContentUtils::DestroyMatchString(void* aData)
+{
+ if (aData) {
+ nsString* matchString = static_cast<nsString*>(aData);
+ delete matchString;
+ }
+}
+
+bool
+nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType)
+{
+ // Table ordered from most to least likely JS MIME types.
+ static const char* jsTypes[] = {
+ "text/javascript",
+ "text/ecmascript",
+ "application/javascript",
+ "application/ecmascript",
+ "application/x-javascript",
+ "application/x-ecmascript",
+ "text/javascript1.0",
+ "text/javascript1.1",
+ "text/javascript1.2",
+ "text/javascript1.3",
+ "text/javascript1.4",
+ "text/javascript1.5",
+ "text/jscript",
+ "text/livescript",
+ "text/x-ecmascript",
+ "text/x-javascript",
+ nullptr
+ };
+
+ for (uint32_t i = 0; jsTypes[i]; ++i) {
+ if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+nsresult
+nsContentUtils::GenerateUUIDInPlace(nsID& aUUID)
+{
+ MOZ_ASSERT(sUUIDGenerator);
+
+ nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&aUUID);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+bool
+nsContentUtils::PrefetchEnabled(nsIDocShell* aDocShell)
+{
+ //
+ // SECURITY CHECK: disable prefetching from mailnews!
+ //
+ // walk up the docshell tree to see if any containing
+ // docshell are of type MAIL.
+ //
+
+ if (!aDocShell) {
+ return false;
+ }
+
+ nsCOMPtr<nsIDocShell> docshell = aDocShell;
+ nsCOMPtr<nsIDocShellTreeItem> parentItem;
+
+ do {
+ uint32_t appType = 0;
+ nsresult rv = docshell->GetAppType(&appType);
+ if (NS_FAILED(rv) || appType == nsIDocShell::APP_TYPE_MAIL) {
+ return false; // do not prefetch, preconnect from mailnews
+ }
+
+ docshell->GetParent(getter_AddRefs(parentItem));
+ if (parentItem) {
+ docshell = do_QueryInterface(parentItem);
+ if (!docshell) {
+ NS_ERROR("cannot get a docshell from a treeItem!");
+ return false;
+ }
+ }
+ } while (parentItem);
+
+ return true;
+}
+
+uint64_t
+nsContentUtils::GetInnerWindowID(nsIRequest* aRequest)
+{
+ // can't do anything if there's no nsIRequest!
+ if (!aRequest) {
+ return 0;
+ }
+
+ nsCOMPtr<nsILoadGroup> loadGroup;
+ nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
+
+ if (NS_FAILED(rv) || !loadGroup) {
+ return 0;
+ }
+
+ nsCOMPtr<nsIInterfaceRequestor> callbacks;
+ rv = loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+ if (NS_FAILED(rv) || !callbacks) {
+ return 0;
+ }
+
+ nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
+ if (!loadContext) {
+ return 0;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> window;
+ rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
+ if (NS_FAILED(rv) || !window) {
+ return 0;
+ }
+
+ auto* pwindow = nsPIDOMWindowOuter::From(window);
+ if (!pwindow) {
+ return 0;
+ }
+
+ nsPIDOMWindowInner* inner = pwindow->GetCurrentInnerWindow();
+ return inner ? inner->WindowID() : 0;
+}
+
+nsresult
+nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost)
+{
+ aHost.Truncate();
+ nsresult rv = aURI->GetHost(aHost);
+ if (NS_FAILED(rv)) { // Some URIs do not have a host
+ return rv;
+ }
+
+ if (aHost.FindChar(':') != -1) { // Escape IPv6 address
+ MOZ_ASSERT(!aHost.Length() ||
+ (aHost[0] !='[' && aHost[aHost.Length() - 1] != ']'));
+ aHost.Insert('[', 0);
+ aHost.Append(']');
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost)
+{
+ nsAutoCString hostname;
+ nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ CopyUTF8toUTF16(hostname, aHost);
+ return NS_OK;
+}
+
+bool
+nsContentUtils::CallOnAllRemoteChildren(nsIMessageBroadcaster* aManager,
+ CallOnRemoteChildFunction aCallback,
+ void* aArg)
+{
+ uint32_t tabChildCount = 0;
+ aManager->GetChildCount(&tabChildCount);
+ for (uint32_t j = 0; j < tabChildCount; ++j) {
+ nsCOMPtr<nsIMessageListenerManager> childMM;
+ aManager->GetChildAt(j, getter_AddRefs(childMM));
+ if (!childMM) {
+ continue;
+ }
+
+ nsCOMPtr<nsIMessageBroadcaster> nonLeafMM = do_QueryInterface(childMM);
+ if (nonLeafMM) {
+ if (CallOnAllRemoteChildren(nonLeafMM, aCallback, aArg)) {
+ return true;
+ }
+ continue;
+ }
+
+ nsCOMPtr<nsIMessageSender> tabMM = do_QueryInterface(childMM);
+
+ mozilla::dom::ipc::MessageManagerCallback* cb =
+ static_cast<nsFrameMessageManager*>(tabMM.get())->GetCallback();
+ if (cb) {
+ nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
+ TabParent* remote = TabParent::GetFrom(fl);
+ if (remote && aCallback) {
+ if (aCallback(remote, aArg)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void
+nsContentUtils::CallOnAllRemoteChildren(nsPIDOMWindowOuter* aWindow,
+ CallOnRemoteChildFunction aCallback,
+ void* aArg)
+{
+ nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(aWindow));
+ if (chromeWindow) {
+ nsCOMPtr<nsIMessageBroadcaster> windowMM;
+ chromeWindow->GetMessageManager(getter_AddRefs(windowMM));
+ if (windowMM) {
+ CallOnAllRemoteChildren(windowMM, aCallback, aArg);
+ }
+ }
+}
+
+struct UIStateChangeInfo {
+ UIStateChangeType mShowAccelerators;
+ UIStateChangeType mShowFocusRings;
+
+ UIStateChangeInfo(UIStateChangeType aShowAccelerators,
+ UIStateChangeType aShowFocusRings)
+ : mShowAccelerators(aShowAccelerators),
+ mShowFocusRings(aShowFocusRings)
+ {}
+};
+
+bool
+SetKeyboardIndicatorsChild(TabParent* aParent, void* aArg)
+{
+ UIStateChangeInfo* stateInfo = static_cast<UIStateChangeInfo*>(aArg);
+ Unused << aParent->SendSetKeyboardIndicators(stateInfo->mShowAccelerators,
+ stateInfo->mShowFocusRings);
+ return false;
+}
+
+void
+nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(nsPIDOMWindowOuter* aWindow,
+ UIStateChangeType aShowAccelerators,
+ UIStateChangeType aShowFocusRings)
+{
+ UIStateChangeInfo stateInfo(aShowAccelerators, aShowFocusRings);
+ CallOnAllRemoteChildren(aWindow, SetKeyboardIndicatorsChild,
+ (void *)&stateInfo);
+}
+
+nsresult
+nsContentUtils::IPCTransferableToTransferable(const IPCDataTransfer& aDataTransfer,
+ const bool& aIsPrivateData,
+ nsIPrincipal* aRequestingPrincipal,
+ nsITransferable* aTransferable,
+ mozilla::dom::nsIContentParent* aContentParent,
+ mozilla::dom::TabChild* aTabChild)
+{
+ nsresult rv;
+
+ const nsTArray<IPCDataTransferItem>& items = aDataTransfer.items();
+ for (const auto& item : items) {
+ aTransferable->AddDataFlavor(item.flavor().get());
+
+ if (item.data().type() == IPCDataTransferData::TnsString) {
+ nsCOMPtr<nsISupportsString> dataWrapper =
+ do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const nsString& text = item.data().get_nsString();
+ rv = dataWrapper->SetData(text);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper,
+ text.Length() * sizeof(char16_t));
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (item.data().type() == IPCDataTransferData::TShmem) {
+ if (nsContentUtils::IsFlavorImage(item.flavor())) {
+ nsCOMPtr<imgIContainer> imageContainer;
+ rv = nsContentUtils::DataTransferItemToImage(item,
+ getter_AddRefs(imageContainer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsInterfacePointer> imgPtr =
+ do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
+ NS_ENSURE_TRUE(imgPtr, NS_ERROR_FAILURE);
+
+ rv = imgPtr->SetData(imageContainer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aTransferable->SetTransferData(item.flavor().get(), imgPtr, sizeof(nsISupports*));
+ } else {
+ nsCOMPtr<nsISupportsCString> dataWrapper =
+ do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // The buffer contains the terminating null.
+ Shmem itemData = item.data().get_Shmem();
+ const nsDependentCString text(itemData.get<char>(),
+ itemData.Size<char>());
+ rv = dataWrapper->SetData(text);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper, text.Length());
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (aContentParent) {
+ Unused << aContentParent->DeallocShmem(item.data().get_Shmem());
+ } else if (aTabChild) {
+ Unused << aTabChild->DeallocShmem(item.data().get_Shmem());
+ }
+ }
+ }
+
+ aTransferable->SetIsPrivateData(aIsPrivateData);
+ aTransferable->SetRequestingPrincipal(aRequestingPrincipal);
+ return NS_OK;
+}
+
+void
+nsContentUtils::TransferablesToIPCTransferables(nsIArray* aTransferables,
+ nsTArray<IPCDataTransfer>& aIPC,
+ bool aInSyncMessage,
+ mozilla::dom::nsIContentChild* aChild,
+ mozilla::dom::nsIContentParent* aParent)
+{
+ aIPC.Clear();
+ if (aTransferables) {
+ uint32_t transferableCount = 0;
+ aTransferables->GetLength(&transferableCount);
+ for (uint32_t i = 0; i < transferableCount; ++i) {
+ IPCDataTransfer* dt = aIPC.AppendElement();
+ nsCOMPtr<nsITransferable> transferable = do_QueryElementAt(aTransferables, i);
+ TransferableToIPCTransferable(transferable, dt, aInSyncMessage, aChild, aParent);
+ }
+ }
+}
+
+nsresult
+nsContentUtils::SlurpFileToString(nsIFile* aFile, nsACString& aString)
+{
+ aString.Truncate();
+
+ nsCOMPtr<nsIURI> fileURI;
+ nsresult rv = NS_NewFileURI(getter_AddRefs(fileURI), aFile);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ fileURI,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIInputStream> stream;
+ rv = channel->Open2(getter_AddRefs(stream));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = NS_ConsumeStream(stream, UINT32_MAX, aString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = stream->Close();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+bool
+nsContentUtils::IsFileImage(nsIFile* aFile, nsACString& aType)
+{
+ nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1");
+ if (!mime) {
+ return false;
+ }
+
+ nsresult rv = mime->GetTypeFromFile(aFile, aType);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ return StringBeginsWith(aType, NS_LITERAL_CSTRING("image/"));
+}
+
+nsresult
+nsContentUtils::DataTransferItemToImage(const IPCDataTransferItem& aItem,
+ imgIContainer** aContainer)
+{
+ MOZ_ASSERT(aItem.data().type() == IPCDataTransferData::TShmem);
+ MOZ_ASSERT(IsFlavorImage(aItem.flavor()));
+
+ const IPCDataTransferImage& imageDetails = aItem.imageDetails();
+ const IntSize size(imageDetails.width(), imageDetails.height());
+ if (!size.width || !size.height) {
+ return NS_ERROR_FAILURE;
+ }
+
+ Shmem data = aItem.data().get_Shmem();
+
+ RefPtr<DataSourceSurface> image =
+ CreateDataSourceSurfaceFromData(size,
+ static_cast<SurfaceFormat>(imageDetails.format()),
+ data.get<uint8_t>(),
+ imageDetails.stride());
+
+ RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size);
+ nsCOMPtr<imgIContainer> imageContainer =
+ image::ImageOps::CreateFromDrawable(drawable);
+ imageContainer.forget(aContainer);
+
+ return NS_OK;
+}
+
+bool
+nsContentUtils::IsFlavorImage(const nsACString& aFlavor)
+{
+ return aFlavor.EqualsLiteral(kNativeImageMime) ||
+ aFlavor.EqualsLiteral(kJPEGImageMime) ||
+ aFlavor.EqualsLiteral(kJPGImageMime) ||
+ aFlavor.EqualsLiteral(kPNGImageMime) ||
+ aFlavor.EqualsLiteral(kGIFImageMime);
+}
+
+static Shmem
+ConvertToShmem(mozilla::dom::nsIContentChild* aChild,
+ mozilla::dom::nsIContentParent* aParent,
+ const nsACString& aInput)
+{
+ MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
+
+ IShmemAllocator* allocator =
+ aChild ? static_cast<IShmemAllocator*>(aChild)
+ : static_cast<IShmemAllocator*>(aParent);
+
+ Shmem result;
+ if (!allocator->AllocShmem(aInput.Length() + 1,
+ SharedMemory::TYPE_BASIC,
+ &result)) {
+ return result;
+ }
+
+ memcpy(result.get<char>(),
+ aInput.BeginReading(),
+ aInput.Length() + 1);
+
+ return result;
+}
+
+void
+nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
+ IPCDataTransfer* aIPCDataTransfer,
+ bool aInSyncMessage,
+ mozilla::dom::nsIContentChild* aChild,
+ mozilla::dom::nsIContentParent* aParent)
+{
+ MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
+
+ if (aTransferable) {
+ nsCOMPtr<nsIArray> flavorList;
+ aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
+ if (flavorList) {
+ uint32_t flavorCount = 0;
+ flavorList->GetLength(&flavorCount);
+ for (uint32_t j = 0; j < flavorCount; ++j) {
+ nsCOMPtr<nsISupportsCString> flavor = do_QueryElementAt(flavorList, j);
+ if (!flavor) {
+ continue;
+ }
+
+ nsAutoCString flavorStr;
+ flavor->GetData(flavorStr);
+ if (!flavorStr.Length()) {
+ continue;
+ }
+
+ nsCOMPtr<nsISupports> data;
+ uint32_t dataLen = 0;
+ aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data), &dataLen);
+
+ nsCOMPtr<nsISupportsString> text = do_QueryInterface(data);
+ nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data);
+ if (text) {
+ nsAutoString dataAsString;
+ text->GetData(dataAsString);
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = flavorStr;
+ item->data() = dataAsString;
+ } else if (ctext) {
+ nsAutoCString dataAsString;
+ ctext->GetData(dataAsString);
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = flavorStr;
+
+ Shmem dataAsShmem = ConvertToShmem(aChild, aParent, dataAsString);
+ if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
+ continue;
+ }
+
+ item->data() = dataAsShmem;
+ } else {
+ nsCOMPtr<nsISupportsInterfacePointer> sip =
+ do_QueryInterface(data);
+ if (sip) {
+ sip->GetData(getter_AddRefs(data));
+ }
+
+ // Images to be pasted on the clipboard are nsIInputStreams
+ nsCOMPtr<nsIInputStream> stream(do_QueryInterface(data));
+ if (stream) {
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = flavorStr;
+
+ nsCString imageData;
+ NS_ConsumeStream(stream, UINT32_MAX, imageData);
+
+ Shmem imageDataShmem = ConvertToShmem(aChild, aParent, imageData);
+ if (!imageDataShmem.IsReadable() || !imageDataShmem.Size<char>()) {
+ continue;
+ }
+
+ item->data() = imageDataShmem;
+ continue;
+ }
+
+ // Images to be placed on the clipboard are imgIContainers.
+ nsCOMPtr<imgIContainer> image(do_QueryInterface(data));
+ if (image) {
+ RefPtr<mozilla::gfx::SourceSurface> surface =
+ image->GetFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_SYNC_DECODE);
+ if (!surface) {
+ continue;
+ }
+ RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
+ surface->GetDataSurface();
+ if (!dataSurface) {
+ continue;
+ }
+ size_t length;
+ int32_t stride;
+ Shmem surfaceData;
+ IShmemAllocator* allocator = aChild ? static_cast<IShmemAllocator*>(aChild)
+ : static_cast<IShmemAllocator*>(aParent);
+ GetSurfaceData(dataSurface, &length, &stride,
+ allocator,
+ &surfaceData);
+
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = flavorStr;
+ // Turn item->data() into an nsCString prior to accessing it.
+ item->data() = surfaceData;
+
+ IPCDataTransferImage& imageDetails = item->imageDetails();
+ mozilla::gfx::IntSize size = dataSurface->GetSize();
+ imageDetails.width() = size.width;
+ imageDetails.height() = size.height;
+ imageDetails.stride() = stride;
+ imageDetails.format() = static_cast<uint8_t>(dataSurface->GetFormat());
+
+ continue;
+ }
+
+ // Otherwise, handle this as a file.
+ nsCOMPtr<BlobImpl> blobImpl;
+ nsCOMPtr<nsIFile> file = do_QueryInterface(data);
+ if (file) {
+ // If we can send this over as a blob, do so. Otherwise, we're
+ // responding to a sync message and the child can't process the blob
+ // constructor before processing our response, which would crash. In
+ // that case, hope that the caller is nsClipboardProxy::GetData,
+ // called from editor and send over images as raw data.
+ if (aInSyncMessage) {
+ nsAutoCString type;
+ if (IsFileImage(file, type)) {
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = type;
+ nsAutoCString data;
+ SlurpFileToString(file, data);
+
+ Shmem dataAsShmem = ConvertToShmem(aChild, aParent, data);
+ item->data() = dataAsShmem;
+ }
+
+ continue;
+ }
+
+ if (aParent) {
+ bool isDir = false;
+ if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
+ nsAutoString path;
+ if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
+ continue;
+ }
+
+ RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
+ fss->GrantAccessToContentProcess(aParent->ChildID(), path);
+ }
+ }
+
+ blobImpl = new BlobImplFile(file, false);
+ ErrorResult rv;
+ // Ensure that file data is cached no that the content process
+ // has this data available to it when passed over:
+ blobImpl->GetSize(rv);
+ blobImpl->GetLastModified(rv);
+ } else {
+ if (aInSyncMessage) {
+ // Can't do anything.
+ continue;
+ }
+ blobImpl = do_QueryInterface(data);
+ }
+ if (blobImpl) {
+ IPCDataTransferData data;
+
+ // If we failed to create the blob actor, then this blob probably
+ // can't get the file size for the underlying file, ignore it for
+ // now. TODO pass this through anyway.
+ if (aChild) {
+ auto* child = mozilla::dom::BlobChild::GetOrCreate(aChild,
+ static_cast<BlobImpl*>(blobImpl.get()));
+ if (!child) {
+ continue;
+ }
+
+ data = child;
+ } else if (aParent) {
+ auto* parent = mozilla::dom::BlobParent::GetOrCreate(aParent,
+ static_cast<BlobImpl*>(blobImpl.get()));
+ if (!parent) {
+ continue;
+ }
+
+ data = parent;
+ }
+
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = flavorStr;
+ item->data() = data;
+ } else {
+ // This is a hack to support kFilePromiseMime.
+ // On Windows there just needs to be an entry for it,
+ // and for OSX we need to create
+ // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
+ if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = flavorStr;
+ item->data() = NS_ConvertUTF8toUTF16(flavorStr);
+ } else if (!data) {
+ // Empty element, transfer only the flavor
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = flavorStr;
+ item->data() = nsString();
+ continue;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+namespace {
+// The default type used for calling GetSurfaceData(). Gets surface data as
+// raw buffer.
+struct GetSurfaceDataRawBuffer
+{
+ using ReturnType = mozilla::UniquePtr<char[]>;
+ using BufferType = char*;
+
+ ReturnType Allocate(size_t aSize)
+ {
+ return ReturnType(new char[aSize]);
+ }
+
+ static BufferType
+ GetBuffer(const ReturnType& aReturnValue)
+ {
+ return aReturnValue.get();
+ }
+
+ static ReturnType
+ NullValue()
+ {
+ return ReturnType();
+ }
+};
+
+// The type used for calling GetSurfaceData() that allocates and writes to
+// a shared memory buffer.
+struct GetSurfaceDataShmem
+{
+ using ReturnType = Shmem;
+ using BufferType = char*;
+
+ explicit GetSurfaceDataShmem(IShmemAllocator* aAllocator)
+ : mAllocator(aAllocator)
+ { }
+
+ ReturnType Allocate(size_t aSize)
+ {
+ Shmem returnValue;
+ mAllocator->AllocShmem(aSize,
+ SharedMemory::TYPE_BASIC,
+ &returnValue);
+ return returnValue;
+ }
+
+ static BufferType
+ GetBuffer(ReturnType aReturnValue)
+ {
+ return aReturnValue.get<char>();
+ }
+
+ static ReturnType
+ NullValue()
+ {
+ return ReturnType();
+ }
+private:
+ IShmemAllocator* mAllocator;
+};
+
+/*
+ * Get the pixel data from the given source surface and return it as a buffer.
+ * The length and stride will be assigned from the surface.
+ */
+template <typename GetSurfaceDataContext = GetSurfaceDataRawBuffer>
+typename GetSurfaceDataContext::ReturnType
+GetSurfaceDataImpl(mozilla::gfx::DataSourceSurface* aSurface,
+ size_t* aLength, int32_t* aStride,
+ GetSurfaceDataContext aContext = GetSurfaceDataContext())
+{
+ mozilla::gfx::DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map)) {
+ return GetSurfaceDataContext::NullValue();
+ }
+
+ mozilla::gfx::IntSize size = aSurface->GetSize();
+ mozilla::CheckedInt32 requiredBytes =
+ mozilla::CheckedInt32(map.mStride) * mozilla::CheckedInt32(size.height);
+ if (!requiredBytes.isValid()) {
+ return GetSurfaceDataContext::NullValue();
+ }
+
+ size_t maxBufLen = requiredBytes.value();
+ mozilla::gfx::SurfaceFormat format = aSurface->GetFormat();
+
+ // Surface data handling is totally nuts. This is the magic one needs to
+ // know to access the data.
+ size_t bufLen = maxBufLen - map.mStride + (size.width * BytesPerPixel(format));
+
+ // nsDependentCString wants null-terminated string.
+ typename GetSurfaceDataContext::ReturnType surfaceData = aContext.Allocate(maxBufLen + 1);
+ if (GetSurfaceDataContext::GetBuffer(surfaceData)) {
+ memcpy(GetSurfaceDataContext::GetBuffer(surfaceData),
+ reinterpret_cast<char*>(map.mData),
+ bufLen);
+ memset(GetSurfaceDataContext::GetBuffer(surfaceData) + bufLen,
+ 0,
+ maxBufLen - bufLen + 1);
+ }
+
+ *aLength = maxBufLen;
+ *aStride = map.mStride;
+
+ aSurface->Unmap();
+ return surfaceData;
+}
+} // Anonymous namespace.
+
+mozilla::UniquePtr<char[]>
+nsContentUtils::GetSurfaceData(
+ NotNull<mozilla::gfx::DataSourceSurface*> aSurface,
+ size_t* aLength, int32_t* aStride)
+{
+ return GetSurfaceDataImpl(aSurface, aLength, aStride);
+}
+
+void
+nsContentUtils::GetSurfaceData(mozilla::gfx::DataSourceSurface* aSurface,
+ size_t* aLength, int32_t* aStride,
+ IShmemAllocator* aAllocator,
+ Shmem *aOutShmem)
+{
+ *aOutShmem = GetSurfaceDataImpl(aSurface, aLength, aStride,
+ GetSurfaceDataShmem(aAllocator));
+}
+
+mozilla::Modifiers
+nsContentUtils::GetWidgetModifiers(int32_t aModifiers)
+{
+ Modifiers result = 0;
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_SHIFT) {
+ result |= mozilla::MODIFIER_SHIFT;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_CONTROL) {
+ result |= mozilla::MODIFIER_CONTROL;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALT) {
+ result |= mozilla::MODIFIER_ALT;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_META) {
+ result |= mozilla::MODIFIER_META;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALTGRAPH) {
+ result |= mozilla::MODIFIER_ALTGRAPH;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_CAPSLOCK) {
+ result |= mozilla::MODIFIER_CAPSLOCK;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_FN) {
+ result |= mozilla::MODIFIER_FN;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_FNLOCK) {
+ result |= mozilla::MODIFIER_FNLOCK;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_NUMLOCK) {
+ result |= mozilla::MODIFIER_NUMLOCK;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_SCROLLLOCK) {
+ result |= mozilla::MODIFIER_SCROLLLOCK;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOL) {
+ result |= mozilla::MODIFIER_SYMBOL;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK) {
+ result |= mozilla::MODIFIER_SYMBOLLOCK;
+ }
+ if (aModifiers & nsIDOMWindowUtils::MODIFIER_OS) {
+ result |= mozilla::MODIFIER_OS;
+ }
+ return result;
+}
+
+nsIWidget*
+nsContentUtils::GetWidget(nsIPresShell* aPresShell, nsPoint* aOffset) {
+ if (aPresShell) {
+ nsIFrame* frame = aPresShell->GetRootFrame();
+ if (frame)
+ return frame->GetView()->GetNearestWidget(aOffset);
+ }
+ return nullptr;
+}
+
+int16_t
+nsContentUtils::GetButtonsFlagForButton(int32_t aButton)
+{
+ switch (aButton) {
+ case -1:
+ return WidgetMouseEvent::eNoButtonFlag;
+ case WidgetMouseEvent::eLeftButton:
+ return WidgetMouseEvent::eLeftButtonFlag;
+ case WidgetMouseEvent::eMiddleButton:
+ return WidgetMouseEvent::eMiddleButtonFlag;
+ case WidgetMouseEvent::eRightButton:
+ return WidgetMouseEvent::eRightButtonFlag;
+ case 4:
+ return WidgetMouseEvent::e4thButtonFlag;
+ case 5:
+ return WidgetMouseEvent::e5thButtonFlag;
+ default:
+ NS_ERROR("Button not known.");
+ return 0;
+ }
+}
+
+LayoutDeviceIntPoint
+nsContentUtils::ToWidgetPoint(const CSSPoint& aPoint,
+ const nsPoint& aOffset,
+ nsPresContext* aPresContext)
+{
+ return LayoutDeviceIntPoint::FromAppUnitsRounded(
+ (CSSPoint::ToAppUnits(aPoint) +
+ aOffset).ApplyResolution(nsLayoutUtils::GetCurrentAPZResolutionScale(aPresContext->PresShell())),
+ aPresContext->AppUnitsPerDevPixel());
+}
+
+nsView*
+nsContentUtils::GetViewToDispatchEvent(nsPresContext* presContext,
+ nsIPresShell** presShell)
+{
+ if (presContext && presShell) {
+ *presShell = presContext->PresShell();
+ if (*presShell) {
+ NS_ADDREF(*presShell);
+ if (nsViewManager* viewManager = (*presShell)->GetViewManager()) {
+ if (nsView* view = viewManager->GetRootView()) {
+ return view;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+nsresult
+nsContentUtils::SendKeyEvent(nsIWidget* aWidget,
+ const nsAString& aType,
+ int32_t aKeyCode,
+ int32_t aCharCode,
+ int32_t aModifiers,
+ uint32_t aAdditionalFlags,
+ bool* aDefaultActionTaken)
+{
+ // get the widget to send the event to
+ if (!aWidget)
+ return NS_ERROR_FAILURE;
+
+ EventMessage msg;
+ if (aType.EqualsLiteral("keydown"))
+ msg = eKeyDown;
+ else if (aType.EqualsLiteral("keyup"))
+ msg = eKeyUp;
+ else if (aType.EqualsLiteral("keypress"))
+ msg = eKeyPress;
+ else
+ return NS_ERROR_FAILURE;
+
+ WidgetKeyboardEvent event(true, msg, aWidget);
+ event.mModifiers = GetWidgetModifiers(aModifiers);
+
+ if (msg == eKeyPress) {
+ event.mKeyCode = aCharCode ? 0 : aKeyCode;
+ event.mCharCode = aCharCode;
+ } else {
+ event.mKeyCode = aKeyCode;
+ event.mCharCode = 0;
+ }
+
+ uint32_t locationFlag = (aAdditionalFlags &
+ (nsIDOMWindowUtils::KEY_FLAG_LOCATION_STANDARD | nsIDOMWindowUtils::KEY_FLAG_LOCATION_LEFT |
+ nsIDOMWindowUtils::KEY_FLAG_LOCATION_RIGHT | nsIDOMWindowUtils::KEY_FLAG_LOCATION_NUMPAD));
+ switch (locationFlag) {
+ case nsIDOMWindowUtils::KEY_FLAG_LOCATION_STANDARD:
+ event.mLocation = nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD;
+ break;
+ case nsIDOMWindowUtils::KEY_FLAG_LOCATION_LEFT:
+ event.mLocation = nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
+ break;
+ case nsIDOMWindowUtils::KEY_FLAG_LOCATION_RIGHT:
+ event.mLocation = nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT;
+ break;
+ case nsIDOMWindowUtils::KEY_FLAG_LOCATION_NUMPAD:
+ event.mLocation = nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
+ break;
+ default:
+ if (locationFlag != 0) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ // If location flag isn't set, choose the location from keycode.
+ switch (aKeyCode) {
+ case nsIDOMKeyEvent::DOM_VK_NUMPAD0:
+ case nsIDOMKeyEvent::DOM_VK_NUMPAD1:
+ case nsIDOMKeyEvent::DOM_VK_NUMPAD2:
+ case nsIDOMKeyEvent::DOM_VK_NUMPAD3:
+ case nsIDOMKeyEvent::DOM_VK_NUMPAD4:
+ case nsIDOMKeyEvent::DOM_VK_NUMPAD5:
+ case nsIDOMKeyEvent::DOM_VK_NUMPAD6:
+ case nsIDOMKeyEvent::DOM_VK_NUMPAD7:
+ case nsIDOMKeyEvent::DOM_VK_NUMPAD8:
+ case nsIDOMKeyEvent::DOM_VK_NUMPAD9:
+ case nsIDOMKeyEvent::DOM_VK_MULTIPLY:
+ case nsIDOMKeyEvent::DOM_VK_ADD:
+ case nsIDOMKeyEvent::DOM_VK_SEPARATOR:
+ case nsIDOMKeyEvent::DOM_VK_SUBTRACT:
+ case nsIDOMKeyEvent::DOM_VK_DECIMAL:
+ case nsIDOMKeyEvent::DOM_VK_DIVIDE:
+ event.mLocation = nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
+ break;
+ case nsIDOMKeyEvent::DOM_VK_SHIFT:
+ case nsIDOMKeyEvent::DOM_VK_CONTROL:
+ case nsIDOMKeyEvent::DOM_VK_ALT:
+ case nsIDOMKeyEvent::DOM_VK_META:
+ event.mLocation = nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
+ break;
+ default:
+ event.mLocation = nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD;
+ break;
+ }
+ break;
+ }
+
+ event.mRefPoint = LayoutDeviceIntPoint(0, 0);
+ event.mTime = PR_IntervalNow();
+ if (!(aAdditionalFlags & nsIDOMWindowUtils::KEY_FLAG_NOT_SYNTHESIZED_FOR_TESTS)) {
+ event.mFlags.mIsSynthesizedForTests = true;
+ }
+
+ if (aAdditionalFlags & nsIDOMWindowUtils::KEY_FLAG_PREVENT_DEFAULT) {
+ event.PreventDefaultBeforeDispatch();
+ }
+
+ nsEventStatus status;
+ nsresult rv = aWidget->DispatchEvent(&event, status);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aDefaultActionTaken = (status != nsEventStatus_eConsumeNoDefault);
+
+ return NS_OK;
+}
+
+nsresult
+nsContentUtils::SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
+ const nsAString& aType,
+ float aX,
+ float aY,
+ int32_t aButton,
+ int32_t aButtons,
+ int32_t aClickCount,
+ int32_t aModifiers,
+ bool aIgnoreRootScrollFrame,
+ float aPressure,
+ unsigned short aInputSourceArg,
+ bool aToWindow,
+ bool *aPreventDefault,
+ bool aIsDOMEventSynthesized,
+ bool aIsWidgetEventSynthesized)
+{
+ nsPoint offset;
+ nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
+ if (!widget)
+ return NS_ERROR_FAILURE;
+
+ EventMessage msg;
+ WidgetMouseEvent::ExitFrom exitFrom = WidgetMouseEvent::eChild;
+ bool contextMenuKey = false;
+ if (aType.EqualsLiteral("mousedown")) {
+ msg = eMouseDown;
+ } else if (aType.EqualsLiteral("mouseup")) {
+ msg = eMouseUp;
+ } else if (aType.EqualsLiteral("mousemove")) {
+ msg = eMouseMove;
+ } else if (aType.EqualsLiteral("mouseover")) {
+ msg = eMouseEnterIntoWidget;
+ } else if (aType.EqualsLiteral("mouseout")) {
+ msg = eMouseExitFromWidget;
+ } else if (aType.EqualsLiteral("mousecancel")) {
+ msg = eMouseExitFromWidget;
+ exitFrom = WidgetMouseEvent::eTopLevel;
+ } else if (aType.EqualsLiteral("mouselongtap")) {
+ msg = eMouseLongTap;
+ } else if (aType.EqualsLiteral("contextmenu")) {
+ msg = eContextMenu;
+ contextMenuKey = (aButton == 0);
+ } else if (aType.EqualsLiteral("MozMouseHittest")) {
+ msg = eMouseHitTest;
+ } else {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aInputSourceArg == nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN) {
+ aInputSourceArg = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
+ }
+
+ WidgetMouseEvent event(true, msg, widget,
+ aIsWidgetEventSynthesized ?
+ WidgetMouseEvent::eSynthesized :
+ WidgetMouseEvent::eReal,
+ contextMenuKey ? WidgetMouseEvent::eContextMenuKey :
+ WidgetMouseEvent::eNormal);
+ event.mModifiers = GetWidgetModifiers(aModifiers);
+ event.button = aButton;
+ event.buttons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED ?
+ aButtons :
+ msg == eMouseUp ? 0 : GetButtonsFlagForButton(aButton);
+ event.pressure = aPressure;
+ event.inputSource = aInputSourceArg;
+ event.mClickCount = aClickCount;
+ event.mTime = PR_IntervalNow();
+ event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
+ event.mExitFrom = exitFrom;
+
+ nsPresContext* presContext = aPresShell->GetPresContext();
+ if (!presContext)
+ return NS_ERROR_FAILURE;
+
+ event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
+ event.mIgnoreRootScrollFrame = aIgnoreRootScrollFrame;
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+ if (aToWindow) {
+ nsCOMPtr<nsIPresShell> presShell;
+ nsView* view = GetViewToDispatchEvent(presContext, getter_AddRefs(presShell));
+ if (!presShell || !view) {
+ return NS_ERROR_FAILURE;
+ }
+ return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
+ }
+ if (gfxPrefs::TestEventsAsyncEnabled()) {
+ status = widget->DispatchInputEvent(&event);
+ } else {
+ nsresult rv = widget->DispatchEvent(&event, status);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ if (aPreventDefault) {
+ *aPreventDefault = (status == nsEventStatus_eConsumeNoDefault);
+ }
+
+ return NS_OK;
+}
+
+/* static */
+void
+nsContentUtils::FirePageHideEvent(nsIDocShellTreeItem* aItem,
+ EventTarget* aChromeEventHandler)
+{
+ nsCOMPtr<nsIDocument> doc = aItem->GetDocument();
+ NS_ASSERTION(doc, "What happened here?");
+ doc->OnPageHide(true, aChromeEventHandler);
+
+ int32_t childCount = 0;
+ aItem->GetChildCount(&childCount);
+ AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
+ kids.AppendElements(childCount);
+ for (int32_t i = 0; i < childCount; ++i) {
+ aItem->GetChildAt(i, getter_AddRefs(kids[i]));
+ }
+
+ for (uint32_t i = 0; i < kids.Length(); ++i) {
+ if (kids[i]) {
+ FirePageHideEvent(kids[i], aChromeEventHandler);
+ }
+ }
+}
+
+// The pageshow event is fired for a given document only if IsShowing() returns
+// the same thing as aFireIfShowing. This gives us a way to fire pageshow only
+// on documents that are still loading or only on documents that are already
+// loaded.
+/* static */
+void
+nsContentUtils::FirePageShowEvent(nsIDocShellTreeItem* aItem,
+ EventTarget* aChromeEventHandler,
+ bool aFireIfShowing)
+{
+ int32_t childCount = 0;
+ aItem->GetChildCount(&childCount);
+ AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
+ kids.AppendElements(childCount);
+ for (int32_t i = 0; i < childCount; ++i) {
+ aItem->GetChildAt(i, getter_AddRefs(kids[i]));
+ }
+
+ for (uint32_t i = 0; i < kids.Length(); ++i) {
+ if (kids[i]) {
+ FirePageShowEvent(kids[i], aChromeEventHandler, aFireIfShowing);
+ }
+ }
+
+ nsCOMPtr<nsIDocument> doc = aItem->GetDocument();
+ NS_ASSERTION(doc, "What happened here?");
+ if (doc->IsShowing() == aFireIfShowing) {
+ doc->OnPageShow(true, aChromeEventHandler);
+ }
+}
+
+/* static */
+already_AddRefed<nsPIWindowRoot>
+nsContentUtils::GetWindowRoot(nsIDocument* aDoc)
+{
+ if (aDoc) {
+ if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
+ return win->GetTopWindowRoot();
+ }
+ }
+ return nullptr;
+}
+
+/* static */
+nsContentPolicyType
+nsContentUtils::InternalContentPolicyTypeToExternal(nsContentPolicyType aType)
+{
+ switch (aType) {
+ case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
+ case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
+ case nsIContentPolicy::TYPE_INTERNAL_WORKER:
+ case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
+ case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
+ return nsIContentPolicy::TYPE_SCRIPT;
+
+ case nsIContentPolicy::TYPE_INTERNAL_EMBED:
+ case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
+ return nsIContentPolicy::TYPE_OBJECT;
+
+ case nsIContentPolicy::TYPE_INTERNAL_FRAME:
+ case nsIContentPolicy::TYPE_INTERNAL_IFRAME:
+ return nsIContentPolicy::TYPE_SUBDOCUMENT;
+
+ case nsIContentPolicy::TYPE_INTERNAL_AUDIO:
+ case nsIContentPolicy::TYPE_INTERNAL_VIDEO:
+ case nsIContentPolicy::TYPE_INTERNAL_TRACK:
+ return nsIContentPolicy::TYPE_MEDIA;
+
+ case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST:
+ case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
+ return nsIContentPolicy::TYPE_XMLHTTPREQUEST;
+
+ case nsIContentPolicy::TYPE_INTERNAL_IMAGE:
+ case nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD:
+ case nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON:
+ return nsIContentPolicy::TYPE_IMAGE;
+
+ case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET:
+ case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD:
+ return nsIContentPolicy::TYPE_STYLESHEET;
+
+ default:
+ return aType;
+ }
+}
+
+/* static */
+nsContentPolicyType
+nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(nsContentPolicyType aType)
+{
+ switch (aType) {
+ case nsIContentPolicy::TYPE_INTERNAL_WORKER:
+ case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
+ case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
+ return aType;
+
+ default:
+ return InternalContentPolicyTypeToExternal(aType);
+ }
+}
+
+/* static */
+bool
+nsContentUtils::IsPreloadType(nsContentPolicyType aType)
+{
+ if (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
+ aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
+ aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD) {
+ return true;
+ }
+ return false;
+}
+
+nsresult
+nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
+ nsIDocument* aDoc,
+ nsIHttpChannel* aChannel,
+ mozilla::net::ReferrerPolicy aReferrerPolicy)
+{
+ NS_ENSURE_ARG_POINTER(aPrincipal);
+ NS_ENSURE_ARG_POINTER(aChannel);
+
+ nsCOMPtr<nsIURI> principalURI;
+
+ if (IsSystemPrincipal(aPrincipal)) {
+ return NS_OK;
+ }
+
+ aPrincipal->GetURI(getter_AddRefs(principalURI));
+
+ if (!aDoc) {
+ return aChannel->SetReferrerWithPolicy(principalURI, aReferrerPolicy);
+ }
+
+ // If it weren't for history.push/replaceState, we could just use the
+ // principal's URI here. But since we want changes to the URI effected
+ // by push/replaceState to be reflected in the XHR referrer, we have to
+ // be more clever.
+ //
+ // If the document's original URI (before any push/replaceStates) matches
+ // our principal, then we use the document's current URI (after
+ // push/replaceStates). Otherwise (if the document is, say, a data:
+ // URI), we just use the principal's URI.
+ nsCOMPtr<nsIURI> docCurURI = aDoc->GetDocumentURI();
+ nsCOMPtr<nsIURI> docOrigURI = aDoc->GetOriginalURI();
+
+ nsCOMPtr<nsIURI> referrerURI;
+
+ if (principalURI && docCurURI && docOrigURI) {
+ bool equal = false;
+ principalURI->Equals(docOrigURI, &equal);
+ if (equal) {
+ referrerURI = docCurURI;
+ }
+ }
+
+ if (!referrerURI) {
+ referrerURI = principalURI;
+ }
+
+ net::ReferrerPolicy referrerPolicy = aReferrerPolicy;
+ if (referrerPolicy == net::RP_Default) {
+ referrerPolicy = aDoc->GetReferrerPolicy();
+ }
+ return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
+}
+
+// static
+net::ReferrerPolicy
+nsContentUtils::GetReferrerPolicyFromHeader(const nsAString& aHeader)
+{
+ // Multiple headers could be concatenated into one comma-separated
+ // list of policies. Need to tokenize the multiple headers.
+ nsCharSeparatedTokenizer tokenizer(aHeader, ',');
+ nsAutoString token;
+ net::ReferrerPolicy referrerPolicy = mozilla::net::RP_Unset;
+ while (tokenizer.hasMoreTokens()) {
+ token = tokenizer.nextToken();
+ net::ReferrerPolicy policy = net::ReferrerPolicyFromString(token);
+ if (policy != net::RP_Unset) {
+ referrerPolicy = policy;
+ }
+ }
+ return referrerPolicy;
+}
+
+// static
+bool
+nsContentUtils::PushEnabled(JSContext* aCx, JSObject* aObj)
+{
+ if (NS_IsMainThread()) {
+ return Preferences::GetBool("dom.push.enabled", false);
+ }
+
+ using namespace workers;
+
+ // Otherwise, check the pref via the WorkerPrivate
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+ if (!workerPrivate) {
+ return false;
+ }
+
+ return workerPrivate->PushEnabled();
+}
+
+// static
+bool
+nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel)
+{
+ nsLoadFlags loadFlags = 0;
+ aChannel->GetLoadFlags(&loadFlags);
+ if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
+ return true;
+ }
+
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+ if (!loadInfo) {
+ return false;
+ }
+ nsContentPolicyType type = loadInfo->InternalContentPolicyType();
+ return type == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
+ type == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
+}
+
+// static, public
+nsContentUtils::StorageAccess
+nsContentUtils::StorageAllowedForWindow(nsPIDOMWindowInner* aWindow)
+{
+ MOZ_ASSERT(aWindow->IsInnerWindow());
+
+ if (nsIDocument* document = aWindow->GetExtantDoc()) {
+ nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
+ return InternalStorageAllowedForPrincipal(principal, aWindow);
+ }
+
+ return StorageAccess::eDeny;
+}
+
+// static, public
+nsContentUtils::StorageAccess
+nsContentUtils::StorageAllowedForPrincipal(nsIPrincipal* aPrincipal)
+{
+ return InternalStorageAllowedForPrincipal(aPrincipal, nullptr);
+}
+
+// static, private
+nsContentUtils::StorageAccess
+nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal,
+ nsPIDOMWindowInner* aWindow)
+{
+ MOZ_ASSERT(aPrincipal);
+ MOZ_ASSERT(!aWindow || aWindow->IsInnerWindow());
+
+ StorageAccess access = StorageAccess::eAllow;
+
+ // We don't allow storage on the null principal, in general. Even if the
+ // calling context is chrome.
+ if (aPrincipal->GetIsNullPrincipal()) {
+ return StorageAccess::eDeny;
+ }
+
+ if (aWindow) {
+ // If the document is sandboxed, then it is not permitted to use storage
+ nsIDocument* document = aWindow->GetExtantDoc();
+ if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
+ return StorageAccess::eDeny;
+ }
+
+ // Check if we are in private browsing, and record that fact
+ if (IsInPrivateBrowsing(document)) {
+ access = StorageAccess::ePrivateBrowsing;
+ }
+ }
+
+ nsCOMPtr<nsIPermissionManager> permissionManager =
+ services::GetPermissionManager();
+ if (!permissionManager) {
+ return StorageAccess::eDeny;
+ }
+
+ // check the permission manager for any allow or deny permissions
+ // for cookies for the window.
+ uint32_t perm;
+ permissionManager->TestPermissionFromPrincipal(aPrincipal, "cookie", &perm);
+ if (perm == nsIPermissionManager::DENY_ACTION) {
+ return StorageAccess::eDeny;
+ } else if (perm == nsICookiePermission::ACCESS_SESSION) {
+ return std::min(access, StorageAccess::eSessionScoped);
+ } else if (perm == nsIPermissionManager::ALLOW_ACTION) {
+ return access;
+ }
+
+ // Check if we should only allow storage for the session, and record that fact
+ if (sCookiesLifetimePolicy == nsICookieService::ACCEPT_SESSION) {
+ // Storage could be StorageAccess::ePrivateBrowsing or StorageAccess::eAllow
+ // so perform a std::min comparison to make sure we preserve ePrivateBrowsing
+ // if it has been set.
+ access = std::min(StorageAccess::eSessionScoped, access);
+ }
+
+ // About URIs are allowed to access storage, even if they don't have chrome
+ // privileges. If this is not desired, than the consumer will have to
+ // implement their own restriction functionality.
+ //
+ // This is due to backwards-compatibility and the state of storage access before
+ // the introducton of nsContentUtils::InternalStorageAllowedForPrincipal:
+ //
+ // BEFORE:
+ // localStorage, caches: allowed in 3rd-party iframes always
+ // IndexedDB: allowed in 3rd-party iframes only if 3rd party URI is an about:
+ // URI within a specific whitelist
+ //
+ // AFTER:
+ // localStorage, caches: allowed in 3rd-party iframes by default. Preference
+ // can be set to disable in 3rd-party, which will not disallow in about: URIs.
+ // IndexedDB: allowed in 3rd-party iframes by default. Preference can be set to
+ // disable in 3rd-party, which will disallow in about: URIs, unless they are
+ // within a specific whitelist.
+ //
+ // This means that behavior for storage with internal about: URIs should not be
+ // affected, which is desireable due to the lack of automated testing for about:
+ // URIs with these preferences set, and the importance of the correct functioning
+ // of these URIs even with custom preferences.
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+ if (NS_SUCCEEDED(rv) && uri) {
+ bool isAbout = false;
+ MOZ_ALWAYS_SUCCEEDS(uri->SchemeIs("about", &isAbout));
+ if (isAbout) {
+ return access;
+ }
+ }
+
+ // We don't want to prompt for every attempt to access permissions.
+ if (sCookiesBehavior == nsICookieService::BEHAVIOR_REJECT) {
+ return StorageAccess::eDeny;
+ }
+
+ // In the absense of a window, we assume that we are first-party.
+ if (aWindow && (sCookiesBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
+ sCookiesBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN)) {
+ nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
+ do_GetService(THIRDPARTYUTIL_CONTRACTID);
+ MOZ_ASSERT(thirdPartyUtil);
+
+ bool thirdPartyWindow = false;
+ if (NS_SUCCEEDED(thirdPartyUtil->IsThirdPartyWindow(
+ aWindow->GetOuterWindow(), nullptr, &thirdPartyWindow)) && thirdPartyWindow) {
+ // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
+ // simply rejecting the request to use the storage. In the future, if we
+ // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense
+ // for non-cookie storage types, this may change.
+
+ return StorageAccess::eDeny;
+ }
+ }
+
+ return access;
+}
+
+namespace {
+
+// We put StringBuilder in the anonymous namespace to prevent anything outside
+// this file from accidentally being linked against it.
+
+class StringBuilder
+{
+private:
+ // Try to keep the size of StringBuilder close to a jemalloc bucket size.
+ static const uint32_t STRING_BUFFER_UNITS = 1020;
+ class Unit
+ {
+ public:
+ Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
+ {
+ MOZ_COUNT_CTOR(StringBuilder::Unit);
+ }
+ ~Unit()
+ {
+ if (mType == eString || mType == eStringWithEncode) {
+ delete mString;
+ }
+ MOZ_COUNT_DTOR(StringBuilder::Unit);
+ }
+
+ enum Type
+ {
+ eUnknown,
+ eAtom,
+ eString,
+ eStringWithEncode,
+ eLiteral,
+ eTextFragment,
+ eTextFragmentWithEncode,
+ };
+
+ union
+ {
+ nsIAtom* mAtom;
+ const char* mLiteral;
+ nsAutoString* mString;
+ const nsTextFragment* mTextFragment;
+ };
+ Type mType;
+ uint32_t mLength;
+ };
+public:
+ StringBuilder() : mLast(this), mLength(0)
+ {
+ MOZ_COUNT_CTOR(StringBuilder);
+ }
+
+ ~StringBuilder()
+ {
+ MOZ_COUNT_DTOR(StringBuilder);
+ }
+
+ void Append(nsIAtom* aAtom)
+ {
+ Unit* u = AddUnit();
+ u->mAtom = aAtom;
+ u->mType = Unit::eAtom;
+ uint32_t len = aAtom->GetLength();
+ u->mLength = len;
+ mLength += len;
+ }
+
+ template<int N>
+ void Append(const char (&aLiteral)[N])
+ {
+ Unit* u = AddUnit();
+ u->mLiteral = aLiteral;
+ u->mType = Unit::eLiteral;
+ uint32_t len = N - 1;
+ u->mLength = len;
+ mLength += len;
+ }
+
+ template<int N>
+ void Append(char (&aLiteral)[N])
+ {
+ Unit* u = AddUnit();
+ u->mLiteral = aLiteral;
+ u->mType = Unit::eLiteral;
+ uint32_t len = N - 1;
+ u->mLength = len;
+ mLength += len;
+ }
+
+ void Append(const nsAString& aString)
+ {
+ Unit* u = AddUnit();
+ u->mString = new nsAutoString(aString);
+ u->mType = Unit::eString;
+ uint32_t len = aString.Length();
+ u->mLength = len;
+ mLength += len;
+ }
+
+ void Append(nsAutoString* aString)
+ {
+ Unit* u = AddUnit();
+ u->mString = aString;
+ u->mType = Unit::eString;
+ uint32_t len = aString->Length();
+ u->mLength = len;
+ mLength += len;
+ }
+
+ void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
+ {
+ Unit* u = AddUnit();
+ u->mString = aString;
+ u->mType = Unit::eStringWithEncode;
+ u->mLength = aLen;
+ mLength += aLen;
+ }
+
+ void Append(const nsTextFragment* aTextFragment)
+ {
+ Unit* u = AddUnit();
+ u->mTextFragment = aTextFragment;
+ u->mType = Unit::eTextFragment;
+ uint32_t len = aTextFragment->GetLength();
+ u->mLength = len;
+ mLength += len;
+ }
+
+ void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
+ {
+ Unit* u = AddUnit();
+ u->mTextFragment = aTextFragment;
+ u->mType = Unit::eTextFragmentWithEncode;
+ u->mLength = aLen;
+ mLength += aLen;
+ }
+
+ bool ToString(nsAString& aOut)
+ {
+ if (!aOut.SetCapacity(mLength, fallible)) {
+ return false;
+ }
+
+ for (StringBuilder* current = this; current; current = current->mNext) {
+ uint32_t len = current->mUnits.Length();
+ for (uint32_t i = 0; i < len; ++i) {
+ Unit& u = current->mUnits[i];
+ switch (u.mType) {
+ case Unit::eAtom:
+ aOut.Append(nsDependentAtomString(u.mAtom));
+ break;
+ case Unit::eString:
+ aOut.Append(*(u.mString));
+ break;
+ case Unit::eStringWithEncode:
+ EncodeAttrString(*(u.mString), aOut);
+ break;
+ case Unit::eLiteral:
+ aOut.AppendASCII(u.mLiteral, u.mLength);
+ break;
+ case Unit::eTextFragment:
+ u.mTextFragment->AppendTo(aOut);
+ break;
+ case Unit::eTextFragmentWithEncode:
+ EncodeTextFragment(u.mTextFragment, aOut);
+ break;
+ default:
+ MOZ_CRASH("Unknown unit type?");
+ }
+ }
+ }
+ return true;
+ }
+private:
+ Unit* AddUnit()
+ {
+ if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
+ new StringBuilder(this);
+ }
+ return mLast->mUnits.AppendElement();
+ }
+
+ explicit StringBuilder(StringBuilder* aFirst)
+ : mLast(nullptr), mLength(0)
+ {
+ MOZ_COUNT_CTOR(StringBuilder);
+ aFirst->mLast->mNext = this;
+ aFirst->mLast = this;
+ }
+
+ void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
+ {
+ const char16_t* c = aValue.BeginReading();
+ const char16_t* end = aValue.EndReading();
+ while (c < end) {
+ switch (*c) {
+ case '"':
+ aOut.AppendLiteral("&quot;");
+ break;
+ case '&':
+ aOut.AppendLiteral("&amp;");
+ break;
+ case 0x00A0:
+ aOut.AppendLiteral("&nbsp;");
+ break;
+ default:
+ aOut.Append(*c);
+ break;
+ }
+ ++c;
+ }
+ }
+
+ void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
+ {
+ uint32_t len = aValue->GetLength();
+ if (aValue->Is2b()) {
+ const char16_t* data = aValue->Get2b();
+ for (uint32_t i = 0; i < len; ++i) {
+ const char16_t c = data[i];
+ switch (c) {
+ case '<':
+ aOut.AppendLiteral("&lt;");
+ break;
+ case '>':
+ aOut.AppendLiteral("&gt;");
+ break;
+ case '&':
+ aOut.AppendLiteral("&amp;");
+ break;
+ case 0x00A0:
+ aOut.AppendLiteral("&nbsp;");
+ break;
+ default:
+ aOut.Append(c);
+ break;
+ }
+ }
+ } else {
+ const char* data = aValue->Get1b();
+ for (uint32_t i = 0; i < len; ++i) {
+ const unsigned char c = data[i];
+ switch (c) {
+ case '<':
+ aOut.AppendLiteral("&lt;");
+ break;
+ case '>':
+ aOut.AppendLiteral("&gt;");
+ break;
+ case '&':
+ aOut.AppendLiteral("&amp;");
+ break;
+ case 0x00A0:
+ aOut.AppendLiteral("&nbsp;");
+ break;
+ default:
+ aOut.Append(c);
+ break;
+ }
+ }
+ }
+ }
+
+ AutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
+ nsAutoPtr<StringBuilder> mNext;
+ StringBuilder* mLast;
+ // mLength is used only in the first StringBuilder object in the linked list.
+ uint32_t mLength;
+};
+
+} // namespace
+
+static void
+AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
+{
+ uint32_t extraSpaceNeeded = 0;
+ uint32_t len = aText->GetLength();
+ if (aText->Is2b()) {
+ const char16_t* data = aText->Get2b();
+ for (uint32_t i = 0; i < len; ++i) {
+ const char16_t c = data[i];
+ switch (c) {
+ case '<':
+ extraSpaceNeeded += ArrayLength("&lt;") - 2;
+ break;
+ case '>':
+ extraSpaceNeeded += ArrayLength("&gt;") - 2;
+ break;
+ case '&':
+ extraSpaceNeeded += ArrayLength("&amp;") - 2;
+ break;
+ case 0x00A0:
+ extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ const char* data = aText->Get1b();
+ for (uint32_t i = 0; i < len; ++i) {
+ const unsigned char c = data[i];
+ switch (c) {
+ case '<':
+ extraSpaceNeeded += ArrayLength("&lt;") - 2;
+ break;
+ case '>':
+ extraSpaceNeeded += ArrayLength("&gt;") - 2;
+ break;
+ case '&':
+ extraSpaceNeeded += ArrayLength("&amp;") - 2;
+ break;
+ case 0x00A0:
+ extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (extraSpaceNeeded) {
+ aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
+ } else {
+ aBuilder.Append(aText);
+ }
+}
+
+static void
+AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
+{
+ const char16_t* c = aValue->BeginReading();
+ const char16_t* end = aValue->EndReading();
+
+ uint32_t extraSpaceNeeded = 0;
+ while (c < end) {
+ switch (*c) {
+ case '"':
+ extraSpaceNeeded += ArrayLength("&quot;") - 2;
+ break;
+ case '&':
+ extraSpaceNeeded += ArrayLength("&amp;") - 2;
+ break;
+ case 0x00A0:
+ extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
+ break;
+ default:
+ break;
+ }
+ ++c;
+ }
+
+ if (extraSpaceNeeded) {
+ aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
+ } else {
+ aBuilder.Append(aValue);
+ }
+}
+
+static void
+StartElement(Element* aContent, StringBuilder& aBuilder)
+{
+ nsIAtom* localName = aContent->NodeInfo()->NameAtom();
+ int32_t tagNS = aContent->GetNameSpaceID();
+
+ aBuilder.Append("<");
+ if (aContent->IsHTMLElement() || aContent->IsSVGElement() ||
+ aContent->IsMathMLElement()) {
+ aBuilder.Append(localName);
+ } else {
+ aBuilder.Append(aContent->NodeName());
+ }
+
+ int32_t count = aContent->GetAttrCount();
+ for (int32_t i = 0; i < count; i++) {
+ const nsAttrName* name = aContent->GetAttrNameAt(i);
+ int32_t attNs = name->NamespaceID();
+ nsIAtom* attName = name->LocalName();
+
+ // Filter out any attribute starting with [-|_]moz
+ nsDependentAtomString attrNameStr(attName);
+ if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
+ StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
+ continue;
+ }
+
+ nsAutoString* attValue = new nsAutoString();
+ aContent->GetAttr(attNs, attName, *attValue);
+
+ // Filter out special case of <br type="_moz*"> used by the editor.
+ // Bug 16988. Yuck.
+ if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
+ attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
+ StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
+ delete attValue;
+ continue;
+ }
+
+ aBuilder.Append(" ");
+
+ if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
+ (attNs == kNameSpaceID_XMLNS &&
+ attName == nsGkAtoms::xmlns)) {
+ // Nothing else required
+ } else if (attNs == kNameSpaceID_XML) {
+ aBuilder.Append("xml:");
+ } else if (attNs == kNameSpaceID_XMLNS) {
+ aBuilder.Append("xmlns:");
+ } else if (attNs == kNameSpaceID_XLink) {
+ aBuilder.Append("xlink:");
+ } else {
+ nsIAtom* prefix = name->GetPrefix();
+ if (prefix) {
+ aBuilder.Append(prefix);
+ aBuilder.Append(":");
+ }
+ }
+
+ aBuilder.Append(attName);
+ aBuilder.Append("=\"");
+ AppendEncodedAttributeValue(attValue, aBuilder);
+ aBuilder.Append("\"");
+ }
+
+ aBuilder.Append(">");
+
+ /*
+ // Per HTML spec we should append one \n if the first child of
+ // pre/textarea/listing is a textnode and starts with a \n.
+ // But because browsers haven't traditionally had that behavior,
+ // we're not changing our behavior either - yet.
+ if (aContent->IsHTMLElement()) {
+ if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
+ localName == nsGkAtoms::listing) {
+ nsIContent* fc = aContent->GetFirstChild();
+ if (fc &&
+ (fc->NodeType() == nsIDOMNode::TEXT_NODE ||
+ fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
+ const nsTextFragment* text = fc->GetText();
+ if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
+ aBuilder.Append("\n");
+ }
+ }
+ }
+ }*/
+}
+
+static inline bool
+ShouldEscape(nsIContent* aParent)
+{
+ if (!aParent || !aParent->IsHTMLElement()) {
+ return true;
+ }
+
+ static const nsIAtom* nonEscapingElements[] = {
+ nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
+ nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
+ nsGkAtoms::plaintext,
+ // Per the current spec noscript should be escaped in case
+ // scripts are disabled or if document doesn't have
+ // browsing context. However the latter seems to be a spec bug
+ // and Gecko hasn't traditionally done the former.
+ nsGkAtoms::noscript
+ };
+ static mozilla::BloomFilter<12, nsIAtom> sFilter;
+ static bool sInitialized = false;
+ if (!sInitialized) {
+ sInitialized = true;
+ for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
+ sFilter.add(nonEscapingElements[i]);
+ }
+ }
+
+ nsIAtom* tag = aParent->NodeInfo()->NameAtom();
+ if (sFilter.mightContain(tag)) {
+ for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
+ if (tag == nonEscapingElements[i]) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+static inline bool
+IsVoidTag(Element* aElement)
+{
+ if (!aElement->IsHTMLElement()) {
+ return false;
+ }
+ return FragmentOrElement::IsHTMLVoid(aElement->NodeInfo()->NameAtom());
+}
+
+bool
+nsContentUtils::SerializeNodeToMarkup(nsINode* aRoot,
+ bool aDescendentsOnly,
+ nsAString& aOut)
+{
+ // If you pass in a DOCUMENT_NODE, you must pass aDescendentsOnly as true
+ MOZ_ASSERT(aDescendentsOnly ||
+ aRoot->NodeType() != nsIDOMNode::DOCUMENT_NODE);
+
+ nsINode* current = aDescendentsOnly ?
+ nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
+
+ if (!current) {
+ return true;
+ }
+
+ StringBuilder builder;
+ nsIContent* next;
+ while (true) {
+ bool isVoid = false;
+ switch (current->NodeType()) {
+ case nsIDOMNode::ELEMENT_NODE: {
+ Element* elem = current->AsElement();
+ StartElement(elem, builder);
+ isVoid = IsVoidTag(elem);
+ if (!isVoid &&
+ (next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
+ current = next;
+ continue;
+ }
+ break;
+ }
+
+ case nsIDOMNode::TEXT_NODE:
+ case nsIDOMNode::CDATA_SECTION_NODE: {
+ const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
+ nsIContent* parent = current->GetParent();
+ if (ShouldEscape(parent)) {
+ AppendEncodedCharacters(text, builder);
+ } else {
+ builder.Append(text);
+ }
+ break;
+ }
+
+ case nsIDOMNode::COMMENT_NODE: {
+ builder.Append("<!--");
+ builder.Append(static_cast<nsIContent*>(current)->GetText());
+ builder.Append("-->");
+ break;
+ }
+
+ case nsIDOMNode::DOCUMENT_TYPE_NODE: {
+ builder.Append("<!DOCTYPE ");
+ builder.Append(current->NodeName());
+ builder.Append(">");
+ break;
+ }
+
+ case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
+ builder.Append("<?");
+ builder.Append(current->NodeName());
+ builder.Append(" ");
+ builder.Append(static_cast<nsIContent*>(current)->GetText());
+ builder.Append(">");
+ break;
+ }
+ }
+
+ while (true) {
+ if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
+ builder.Append("</");
+ nsIContent* elem = static_cast<nsIContent*>(current);
+ if (elem->IsHTMLElement() || elem->IsSVGElement() ||
+ elem->IsMathMLElement()) {
+ builder.Append(elem->NodeInfo()->NameAtom());
+ } else {
+ builder.Append(current->NodeName());
+ }
+ builder.Append(">");
+ }
+ isVoid = false;
+
+ if (current == aRoot) {
+ return builder.ToString(aOut);
+ }
+
+ if ((next = current->GetNextSibling())) {
+ current = next;
+ break;
+ }
+
+ current = current->GetParentNode();
+
+ // Handle template element. If the parent is a template's content,
+ // then adjust the parent to be the template element.
+ if (current != aRoot &&
+ current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
+ DocumentFragment* frag = static_cast<DocumentFragment*>(current);
+ nsIContent* fragHost = frag->GetHost();
+ if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
+ current = fragHost;
+ }
+ }
+
+ if (aDescendentsOnly && current == aRoot) {
+ return builder.ToString(aOut);
+ }
+ }
+ }
+}
+
+bool
+nsContentUtils::IsSpecificAboutPage(JSObject* aGlobal, const char* aUri)
+{
+ // aUri must start with about: or this isn't the right function to be using.
+ MOZ_ASSERT(strncmp(aUri, "about:", 6) == 0);
+
+ // Make sure the global is a window
+ nsGlobalWindow* win = xpc::WindowGlobalOrNull(aGlobal);
+ if (!win) {
+ return false;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
+ NS_ENSURE_TRUE(principal, false);
+ nsCOMPtr<nsIURI> uri;
+ principal->GetURI(getter_AddRefs(uri));
+ if (!uri) {
+ return false;
+ }
+
+ // First check the scheme to avoid getting long specs in the common case.
+ bool isAbout = false;
+ uri->SchemeIs("about", &isAbout);
+ if (!isAbout) {
+ return false;
+ }
+
+ // Now check the spec itself
+ nsAutoCString spec;
+ uri->GetSpecIgnoringRef(spec);
+ return spec.EqualsASCII(aUri);
+}
+
+/* static */ void
+nsContentUtils::SetScrollbarsVisibility(nsIDocShell* aDocShell, bool aVisible)
+{
+ nsCOMPtr<nsIScrollable> scroller = do_QueryInterface(aDocShell);
+
+ if (scroller) {
+ int32_t prefValue;
+
+ if (aVisible) {
+ prefValue = nsIScrollable::Scrollbar_Auto;
+ } else {
+ prefValue = nsIScrollable::Scrollbar_Never;
+ }
+
+ scroller->SetDefaultScrollbarPreferences(
+ nsIScrollable::ScrollOrientation_Y, prefValue);
+ scroller->SetDefaultScrollbarPreferences(
+ nsIScrollable::ScrollOrientation_X, prefValue);
+ }
+}
+
+/* static */ void
+nsContentUtils::GetPresentationURL(nsIDocShell* aDocShell, nsAString& aPresentationUrl)
+{
+ MOZ_ASSERT(aDocShell);
+
+ // Simulate receiver context for web platform test
+ if (Preferences::GetBool("dom.presentation.testing.simulate-receiver")) {
+ nsCOMPtr<nsIDocument> doc;
+
+ nsCOMPtr<nsPIDOMWindowOuter> docShellWin =
+ do_QueryInterface(aDocShell->GetScriptGlobalObject());
+ if (docShellWin) {
+ doc = docShellWin->GetExtantDoc();
+ }
+
+ if (NS_WARN_IF(!doc)) {
+ return;
+ }
+
+ nsCOMPtr<nsIURI> uri = doc->GetDocumentURI();
+ if (NS_WARN_IF(!uri)) {
+ return;
+ }
+
+ nsAutoCString uriStr;
+ uri->GetSpec(uriStr);
+ aPresentationUrl = NS_ConvertUTF8toUTF16(uriStr);
+ return;
+ }
+
+ if (XRE_IsContentProcess()) {
+ nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
+ aDocShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ aDocShell->GetRootTreeItem(getter_AddRefs(root));
+ if (sameTypeRoot.get() == root.get()) {
+ // presentation URL is stored in TabChild for the top most
+ // <iframe mozbrowser> in content process.
+ TabChild* tabChild = TabChild::GetFrom(aDocShell);
+ if (tabChild) {
+ aPresentationUrl = tabChild->PresentationURL();
+ }
+ return;
+ }
+ }
+
+ nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(aDocShell));
+ nsCOMPtr<nsIDOMElement> topFrameElement;
+ loadContext->GetTopFrameElement(getter_AddRefs(topFrameElement));
+ if (!topFrameElement) {
+ return;
+ }
+
+ topFrameElement->GetAttribute(NS_LITERAL_STRING("mozpresentation"), aPresentationUrl);
+}
+
+/* static */ nsIDocShell*
+nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget)
+{
+ nsCOMPtr<nsPIDOMWindowInner> innerWindow;
+
+ if (nsCOMPtr<nsINode> node = do_QueryInterface(aTarget)) {
+ bool ignore;
+ innerWindow =
+ do_QueryInterface(node->OwnerDoc()->GetScriptHandlingObject(ignore));
+ } else if ((innerWindow = do_QueryInterface(aTarget))) {
+ // Nothing else to do
+ } else {
+ nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(aTarget);
+ if (helper) {
+ innerWindow = helper->GetOwner();
+ }
+ }
+
+ if (innerWindow) {
+ return innerWindow->GetDocShell();
+ }
+
+ return nullptr;
+}
+
+/*
+ * Note: this function only relates to figuring out HTTPS state, which is an
+ * input to the Secure Context algorithm. We are not actually implementing any
+ * part of the Secure Context algorithm itself here.
+ *
+ * This is a bit of a hack. Ideally we'd propagate HTTPS state through
+ * nsIChannel as described in the Fetch and HTML specs, but making channels
+ * know about whether they should inherit HTTPS state, propagating information
+ * about who the channel's "client" is, exposing GetHttpsState API on channels
+ * and modifying the various cache implementations to store and retrieve HTTPS
+ * state involves a huge amount of code (see bug 1220687). We avoid that for
+ * now using this function.
+ *
+ * This function takes advantage of the observation that we can return true if
+ * nsIContentSecurityManager::IsOriginPotentiallyTrustworthy returns true for
+ * the document's origin (e.g. the origin has a scheme of 'https' or host
+ * 'localhost' etc.). Since we generally propagate a creator document's origin
+ * onto data:, blob:, etc. documents, this works for them too.
+ *
+ * The scenario where this observation breaks down is sandboxing without the
+ * 'allow-same-origin' flag, since in this case a document is given a unique
+ * origin (IsOriginPotentiallyTrustworthy would return false). We handle that
+ * by using the origin that the document would have had had it not been
+ * sandboxed.
+ *
+ * DEFICIENCIES: Note that this function uses nsIScriptSecurityManager's
+ * getChannelResultPrincipalIfNotSandboxed, and that method's ignoring of
+ * sandboxing is limited to the immediate sandbox. In the case that aDocument
+ * should inherit its origin (e.g. data: URI) but its parent has ended up
+ * with a unique origin due to sandboxing further up the parent chain we may
+ * end up returning false when we would ideally return true (since we will
+ * examine the parent's origin for 'https' and not finding it.) This means
+ * that we may restrict the privileges of some pages unnecessarily in this
+ * edge case.
+ */
+/* static */ bool
+nsContentUtils::HttpsStateIsModern(nsIDocument* aDocument)
+{
+ if (!aDocument) {
+ return false;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
+
+ if (principal->GetIsSystemPrincipal()) {
+ return true;
+ }
+
+ // If aDocument is sandboxed, try and get the principal that it would have
+ // been given had it not been sandboxed:
+ if (principal->GetIsNullPrincipal() &&
+ (aDocument->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
+ nsIChannel* channel = aDocument->GetChannel();
+ if (channel) {
+ nsCOMPtr<nsIScriptSecurityManager> ssm =
+ nsContentUtils::GetSecurityManager();
+ nsresult rv =
+ ssm->GetChannelResultPrincipalIfNotSandboxed(channel,
+ getter_AddRefs(principal));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ if (principal->GetIsSystemPrincipal()) {
+ // If a document with the system principal is sandboxing a subdocument
+ // that would normally inherit the embedding element's principal (e.g.
+ // a srcdoc document) then the embedding document does not trust the
+ // content that is written to the embedded document. Unlike when the
+ // embedding document is https, in this case we have no indication as
+ // to whether the embedded document's contents are delivered securely
+ // or not, and the sandboxing would possibly indicate that they were
+ // not. To play it safe we return false here. (See bug 1162772
+ // comment 73-80.)
+ return false;
+ }
+ }
+ }
+
+ if (principal->GetIsNullPrincipal()) {
+ return false;
+ }
+
+ MOZ_ASSERT(principal->GetIsCodebasePrincipal());
+
+ nsCOMPtr<nsIContentSecurityManager> csm =
+ do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
+ NS_WARNING_ASSERTION(csm, "csm is null");
+ if (csm) {
+ bool isTrustworthyOrigin = false;
+ csm->IsOriginPotentiallyTrustworthy(principal, &isTrustworthyOrigin);
+ if (isTrustworthyOrigin) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* static */ CustomElementDefinition*
+nsContentUtils::LookupCustomElementDefinition(nsIDocument* aDoc,
+ const nsAString& aLocalName,
+ uint32_t aNameSpaceID,
+ const nsAString* aIs)
+{
+ MOZ_ASSERT(aDoc);
+
+ // To support imported document.
+ nsCOMPtr<nsIDocument> doc = aDoc->MasterDocument();
+
+ if (aNameSpaceID != kNameSpaceID_XHTML ||
+ !doc->GetDocShell()) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
+ if (!window) {
+ return nullptr;
+ }
+
+ RefPtr<CustomElementRegistry> registry(window->CustomElements());
+ if (!registry) {
+ return nullptr;
+ }
+
+ return registry->LookupCustomElementDefinition(aLocalName, aIs);
+}
+
+/* static */ void
+nsContentUtils::SetupCustomElement(Element* aElement,
+ const nsAString* aTypeExtension)
+{
+ MOZ_ASSERT(aElement);
+
+ nsCOMPtr<nsIDocument> doc = aElement->OwnerDoc();
+
+ if (!doc) {
+ return;
+ }
+
+ // To support imported document.
+ doc = doc->MasterDocument();
+
+ if (aElement->GetNameSpaceID() != kNameSpaceID_XHTML ||
+ !doc->GetDocShell()) {
+ return;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
+ if (!window) {
+ return;
+ }
+
+ RefPtr<CustomElementRegistry> registry(window->CustomElements());
+ if (!registry) {
+ return;
+ }
+
+ return registry->SetupCustomElement(aElement, aTypeExtension);
+}
+
+/* static */ void
+nsContentUtils::EnqueueLifecycleCallback(nsIDocument* aDoc,
+ nsIDocument::ElementCallbackType aType,
+ Element* aCustomElement,
+ LifecycleCallbackArgs* aArgs,
+ CustomElementDefinition* aDefinition)
+{
+ MOZ_ASSERT(aDoc);
+
+ // To support imported document.
+ nsCOMPtr<nsIDocument> doc = aDoc->MasterDocument();
+
+ if (!doc->GetDocShell()) {
+ return;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
+ if (!window) {
+ return;
+ }
+
+ RefPtr<CustomElementRegistry> registry(window->CustomElements());
+ if (!registry) {
+ return;
+ }
+
+ registry->EnqueueLifecycleCallback(aType, aCustomElement, aArgs, aDefinition);
+}
+
+/* static */ void
+nsContentUtils::GetCustomPrototype(nsIDocument* aDoc,
+ int32_t aNamespaceID,
+ nsIAtom* aAtom,
+ JS::MutableHandle<JSObject*> aPrototype)
+{
+ MOZ_ASSERT(aDoc);
+
+ // To support imported document.
+ nsCOMPtr<nsIDocument> doc = aDoc->MasterDocument();
+
+ if (aNamespaceID != kNameSpaceID_XHTML ||
+ !doc->GetDocShell()) {
+ return;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
+ if (!window) {
+ return;
+ }
+
+ RefPtr<CustomElementRegistry> registry(window->CustomElements());
+ if (!registry) {
+ return;
+ }
+
+ return registry->GetCustomPrototype(aAtom, aPrototype);
+}
+
+/* static */ bool
+nsContentUtils::AttemptLargeAllocationLoad(nsIHttpChannel* aChannel)
+{
+ MOZ_ASSERT(aChannel);
+
+ nsCOMPtr<nsILoadGroup> loadGroup;
+ nsresult rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+ if (NS_WARN_IF(NS_FAILED(rv) || !loadGroup)) {
+ return false;
+ }
+
+ nsCOMPtr<nsIInterfaceRequestor> callbacks;
+ rv = loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+ if (NS_WARN_IF(NS_FAILED(rv) || !callbacks)) {
+ return false;
+ }
+
+ nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
+ if (NS_WARN_IF(!loadContext)) {
+ return false;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> window;
+ rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
+ if (NS_WARN_IF(NS_FAILED(rv) || !window)) {
+ return false;
+ }
+
+ nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(window);
+ if (NS_WARN_IF(!outer)) {
+ return false;
+ }
+
+ nsIDocShell* docShell = outer->GetDocShell();
+ nsIDocument* doc = outer->GetExtantDoc();
+
+ // If the docshell is not allowed to change process, report an error based on
+ // the reason
+ const char* errorName = nullptr;
+ switch (docShell->GetProcessLockReason()) {
+ case nsIDocShell::PROCESS_LOCK_NON_CONTENT:
+ errorName = "LargeAllocationNonE10S";
+ break;
+ case nsIDocShell::PROCESS_LOCK_IFRAME:
+ errorName = "LargeAllocationIFrame";
+ break;
+ case nsIDocShell::PROCESS_LOCK_RELATED_CONTEXTS:
+ errorName = "LargeAllocationRelatedBrowsingContexts";
+ break;
+ case nsIDocShell::PROCESS_LOCK_NONE:
+ // Don't print a warning, we're allowed to change processes!
+ break;
+ default:
+ MOZ_ASSERT(false, "Should be unreachable!");
+ return false;
+ }
+
+ if (errorName) {
+ if (doc) {
+ nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("DOM"),
+ doc,
+ nsContentUtils::eDOM_PROPERTIES,
+ errorName);
+ }
+
+ return false;
+ }
+
+ // Get the request method, and check if it is a GET request. If it is not GET,
+ // then we cannot perform a large allocation load.
+ nsAutoCString requestMethod;
+ rv = aChannel->GetRequestMethod(requestMethod);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ if (NS_WARN_IF(!requestMethod.LowerCaseEqualsLiteral("get"))) {
+ if (doc) {
+ nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("DOM"),
+ doc,
+ nsContentUtils::eDOM_PROPERTIES,
+ "LargeAllocationNonGetRequest");
+ }
+ return false;
+ }
+
+ TabChild* tabChild = TabChild::GetFrom(outer);
+ NS_ENSURE_TRUE(tabChild, false);
+
+ if (tabChild->TakeIsFreshProcess()) {
+ NS_WARNING("Already in a fresh process, ignoring Large-Allocation header!");
+ if (doc) {
+ nsContentUtils::ReportToConsole(nsIScriptError::infoFlag,
+ NS_LITERAL_CSTRING("DOM"),
+ doc,
+ nsContentUtils::eDOM_PROPERTIES,
+ "LargeAllocationSuccess");
+ }
+ return false;
+ }
+
+ // At this point the fress process load should succeed! We just need to get
+ // ourselves a nsIWebBrowserChrome3 to ask to perform the reload. We should
+ // have one, as we have already confirmed that we are running in a content
+ // process.
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ docShell->GetTreeOwner(getter_AddRefs(treeOwner));
+ NS_ENSURE_TRUE(treeOwner, false);
+
+ nsCOMPtr<nsIWebBrowserChrome3> wbc3 = do_GetInterface(treeOwner);
+ NS_ENSURE_TRUE(wbc3, false);
+
+ nsCOMPtr<nsIURI> uri;
+ rv = aChannel->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, false);
+ NS_ENSURE_TRUE(uri, false);
+
+ nsCOMPtr<nsIURI> referrer;
+ rv = aChannel->GetReferrer(getter_AddRefs(referrer));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ // Actually perform the cross process load
+ bool reloadSucceeded = false;
+ rv = wbc3->ReloadInFreshProcess(docShell, uri, referrer, &reloadSucceeded);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return reloadSucceeded;
+}