content/base/src/nsContentUtils.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial