Bug 614578 - Removal of nsAutoGCRoot. r=mrbkap
[mozilla-central.git] / content / base / src / nsContentUtils.cpp
blob229787eea04b70d25d5b9b685b80926826569b12
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=2 sw=2 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
26 * Johnny Stenback <jst@netscape.com>
27 * Christopher A. Aillon <christopher@aillon.com>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either of the GNU General Public License Version 2 or later (the "GPL"),
31 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
43 /* A namespace class for static layout utilities. */
45 #include "jscntxt.h"
47 #include "nsJSUtils.h"
48 #include "nsCOMPtr.h"
49 #include "nsAString.h"
50 #include "nsPrintfCString.h"
51 #include "nsUnicharUtils.h"
52 #include "nsIPrefService.h"
53 #include "nsIPrefBranch2.h"
54 #include "nsIPrefLocalizedString.h"
55 #include "nsServiceManagerUtils.h"
56 #include "nsIScriptGlobalObject.h"
57 #include "nsIScriptContext.h"
58 #include "nsIDOMScriptObjectFactory.h"
59 #include "nsDOMCID.h"
60 #include "nsContentUtils.h"
61 #include "nsIContentUtils.h"
62 #include "nsIXPConnect.h"
63 #include "nsIContent.h"
64 #include "mozilla/dom/Element.h"
65 #include "nsIDocument.h"
66 #include "nsINodeInfo.h"
67 #include "nsReadableUtils.h"
68 #include "nsIDOMDocument.h"
69 #include "nsIDOMNodeList.h"
70 #include "nsIDOMNode.h"
71 #include "nsIDOM3Node.h"
72 #include "nsIIOService.h"
73 #include "nsNetCID.h"
74 #include "nsNetUtil.h"
75 #include "nsIScriptSecurityManager.h"
76 #include "nsDOMError.h"
77 #include "nsPIDOMWindow.h"
78 #include "nsIJSContextStack.h"
79 #include "nsIDocShell.h"
80 #include "nsIDocShellTreeItem.h"
81 #include "nsParserCIID.h"
82 #include "nsIParser.h"
83 #include "nsIFragmentContentSink.h"
84 #include "nsIContentSink.h"
85 #include "nsIHTMLContentSink.h"
86 #include "nsIXMLContentSink.h"
87 #include "nsHTMLParts.h"
88 #include "nsIParserService.h"
89 #include "nsIServiceManager.h"
90 #include "nsIAttribute.h"
91 #include "nsContentList.h"
92 #include "nsIHTMLDocument.h"
93 #include "nsIDOMHTMLDocument.h"
94 #include "nsIDOMHTMLCollection.h"
95 #include "nsIDOMHTMLFormElement.h"
96 #include "nsIDOMNSHTMLElement.h"
97 #include "nsIForm.h"
98 #include "nsIFormControl.h"
99 #include "nsGkAtoms.h"
100 #include "nsISupportsPrimitives.h"
101 #include "imgIDecoderObserver.h"
102 #include "imgIRequest.h"
103 #include "imgIContainer.h"
104 #include "imgILoader.h"
105 #include "mozilla/IHistory.h"
106 #include "nsDocShellCID.h"
107 #include "nsIImageLoadingContent.h"
108 #include "nsIInterfaceRequestor.h"
109 #include "nsIInterfaceRequestorUtils.h"
110 #include "nsILoadGroup.h"
111 #include "nsIObserver.h"
112 #include "nsIObserverService.h"
113 #include "nsContentPolicyUtils.h"
114 #include "nsNodeInfoManager.h"
115 #include "nsIXBLService.h"
116 #include "nsCRT.h"
117 #include "nsIDOMEvent.h"
118 #include "nsIDOMEventTarget.h"
119 #include "nsIPrivateDOMEvent.h"
120 #include "nsIDOMDocumentEvent.h"
121 #ifdef MOZ_XTF
122 #include "nsIXTFService.h"
123 static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
124 #endif
125 #include "nsIMIMEService.h"
126 #include "nsLWBrkCIID.h"
127 #include "nsILineBreaker.h"
128 #include "nsIWordBreaker.h"
129 #include "jsdbgapi.h"
130 #include "nsIJSRuntimeService.h"
131 #include "nsIDOMDocumentXBL.h"
132 #include "nsBindingManager.h"
133 #include "nsIURI.h"
134 #include "nsIURL.h"
135 #include "nsXBLBinding.h"
136 #include "nsXBLPrototypeBinding.h"
137 #include "nsEscape.h"
138 #include "nsICharsetConverterManager.h"
139 #include "nsIEventListenerManager.h"
140 #include "nsAttrName.h"
141 #include "nsIDOMUserDataHandler.h"
142 #include "nsContentCreatorFunctions.h"
143 #include "nsTPtrArray.h"
144 #include "nsGUIEvent.h"
145 #include "nsMutationEvent.h"
146 #include "nsIMEStateManager.h"
147 #include "nsContentErrors.h"
148 #include "nsUnicharUtilCIID.h"
149 #include "nsCompressedCharMap.h"
150 #include "nsINativeKeyBindings.h"
151 #include "nsIDOMNSUIEvent.h"
152 #include "nsIDOMNSEvent.h"
153 #include "nsIPrivateDOMEvent.h"
154 #include "nsXULPopupManager.h"
155 #include "nsIPermissionManager.h"
156 #include "nsIContentPrefService.h"
157 #include "nsIScriptObjectPrincipal.h"
158 #include "nsIRunnable.h"
159 #include "nsDOMJSUtils.h"
160 #include "nsGenericHTMLElement.h"
161 #include "nsAttrValue.h"
162 #include "nsReferencedElement.h"
163 #include "nsIUGenCategory.h"
164 #include "nsIDragService.h"
165 #include "nsIChannelEventSink.h"
166 #include "nsIAsyncVerifyRedirectCallback.h"
167 #include "nsIInterfaceRequestor.h"
168 #include "nsIOfflineCacheUpdate.h"
169 #include "nsCPrefetchService.h"
170 #include "nsIChromeRegistry.h"
171 #include "nsIMIMEHeaderParam.h"
172 #include "nsIDOMXULCommandEvent.h"
173 #include "nsIDOMAbstractView.h"
174 #include "nsIDOMDragEvent.h"
175 #include "nsDOMDataTransfer.h"
176 #include "nsHtml5Module.h"
177 #include "nsPresContext.h"
178 #include "nsLayoutStatics.h"
179 #include "nsLayoutUtils.h"
180 #include "nsFrameManager.h"
181 #include "BasicLayers.h"
182 #include "nsFocusManager.h"
183 #include "nsTextEditorState.h"
184 #include "nsIPluginHost.h"
185 #include "nsICategoryManager.h"
186 #include "nsAHtml5FragmentParser.h"
188 #ifdef IBMBIDI
189 #include "nsIBidiKeyboard.h"
190 #endif
191 #include "nsCycleCollectionParticipant.h"
193 // for ReportToConsole
194 #include "nsIStringBundle.h"
195 #include "nsIScriptError.h"
196 #include "nsIConsoleService.h"
198 #include "mozAutoDocUpdate.h"
199 #include "imgICache.h"
200 #include "jsinterp.h"
201 #include "jsarray.h"
202 #include "jsdate.h"
203 #include "jsregexp.h"
204 #include "jstypedarray.h"
205 #include "xpcprivate.h"
206 #include "nsScriptSecurityManager.h"
207 #include "nsIChannelPolicy.h"
208 #include "nsChannelPolicy.h"
209 #include "nsIContentSecurityPolicy.h"
210 #include "nsContentDLF.h"
211 #ifdef MOZ_MEDIA
212 #include "nsHTMLMediaElement.h"
213 #endif
215 using namespace mozilla::dom;
216 using namespace mozilla::layers;
218 const char kLoadAsData[] = "loadAsData";
220 static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
221 static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
222 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
224 nsIDOMScriptObjectFactory *nsContentUtils::sDOMScriptObjectFactory = nsnull;
225 nsIXPConnect *nsContentUtils::sXPConnect;
226 nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
227 nsIThreadJSContextStack *nsContentUtils::sThreadJSContextStack;
228 nsIParserService *nsContentUtils::sParserService = nsnull;
229 nsINameSpaceManager *nsContentUtils::sNameSpaceManager;
230 nsIIOService *nsContentUtils::sIOService;
231 #ifdef MOZ_XTF
232 nsIXTFService *nsContentUtils::sXTFService = nsnull;
233 #endif
234 nsIPrefBranch2 *nsContentUtils::sPrefBranch = nsnull;
235 imgILoader *nsContentUtils::sImgLoader;
236 imgICache *nsContentUtils::sImgCache;
237 mozilla::IHistory *nsContentUtils::sHistory;
238 nsIConsoleService *nsContentUtils::sConsoleService;
239 nsDataHashtable<nsISupportsHashKey, EventNameMapping>* nsContentUtils::sAtomEventTable = nsnull;
240 nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nsnull;
241 nsCOMArray<nsIAtom>* nsContentUtils::sUserDefinedEvents = nsnull;
242 nsIStringBundleService *nsContentUtils::sStringBundleService;
243 nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT];
244 nsIContentPolicy *nsContentUtils::sContentPolicyService;
245 PRBool nsContentUtils::sTriedToGetContentPolicy = PR_FALSE;
246 nsILineBreaker *nsContentUtils::sLineBreaker;
247 nsIWordBreaker *nsContentUtils::sWordBreaker;
248 nsIUGenCategory *nsContentUtils::sGenCat;
249 nsTArray<nsISupports**> *nsContentUtils::sPtrsToPtrsToRelease;
250 nsIScriptRuntime *nsContentUtils::sScriptRuntimes[NS_STID_ARRAY_UBOUND];
251 PRInt32 nsContentUtils::sScriptRootCount[NS_STID_ARRAY_UBOUND];
252 PRUint32 nsContentUtils::sJSGCThingRootCount;
253 #ifdef IBMBIDI
254 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nsnull;
255 #endif
256 PRUint32 nsContentUtils::sScriptBlockerCount = 0;
257 PRUint32 nsContentUtils::sRemovableScriptBlockerCount = 0;
258 nsCOMArray<nsIRunnable>* nsContentUtils::sBlockedScriptRunners = nsnull;
259 PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
260 PRUint32 nsContentUtils::sScriptBlockerCountWhereRunnersPrevented = 0;
261 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull;
263 PRBool nsContentUtils::sIsHandlingKeyBoardEvent = PR_FALSE;
264 PRBool nsContentUtils::sAllowXULXBL_for_file = PR_FALSE;
266 PRBool nsContentUtils::sInitialized = PR_FALSE;
268 nsRefPtrHashtable<nsPrefObserverHashKey, nsPrefOldCallback>
269 *nsContentUtils::sPrefCallbackTable = nsnull;
271 static PLDHashTable sEventListenerManagersHash;
273 class EventListenerManagerMapEntry : public PLDHashEntryHdr
275 public:
276 EventListenerManagerMapEntry(const void *aKey)
277 : mKey(aKey)
281 ~EventListenerManagerMapEntry()
283 NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
286 private:
287 const void *mKey; // must be first, to look like PLDHashEntryStub
289 public:
290 nsCOMPtr<nsIEventListenerManager> mListenerManager;
293 static PRBool
294 EventListenerManagerHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
295 const void *key)
297 // Initialize the entry with placement new
298 new (entry) EventListenerManagerMapEntry(key);
299 return PR_TRUE;
302 static void
303 EventListenerManagerHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
305 EventListenerManagerMapEntry *lm =
306 static_cast<EventListenerManagerMapEntry *>(entry);
308 // Let the EventListenerManagerMapEntry clean itself up...
309 lm->~EventListenerManagerMapEntry();
312 class nsSameOriginChecker : public nsIChannelEventSink,
313 public nsIInterfaceRequestor
315 NS_DECL_ISUPPORTS
316 NS_DECL_NSICHANNELEVENTSINK
317 NS_DECL_NSIINTERFACEREQUESTOR
320 class nsPrefObserverHashKey : public PLDHashEntryHdr {
321 public:
322 typedef nsPrefObserverHashKey* KeyType;
323 typedef const nsPrefObserverHashKey* KeyTypePointer;
325 static const nsPrefObserverHashKey* KeyToPointer(nsPrefObserverHashKey *aKey)
327 return aKey;
330 static PLDHashNumber HashKey(const nsPrefObserverHashKey *aKey)
332 PRUint32 strHash = nsCRT::HashCode(aKey->mPref.BeginReading(),
333 aKey->mPref.Length());
334 return PR_ROTATE_LEFT32(strHash, 4) ^
335 NS_PTR_TO_UINT32(aKey->mCallback);
338 nsPrefObserverHashKey(const char *aPref, PrefChangedFunc aCallback) :
339 mPref(aPref), mCallback(aCallback) { }
341 nsPrefObserverHashKey(const nsPrefObserverHashKey *aOther) :
342 mPref(aOther->mPref), mCallback(aOther->mCallback)
345 PRBool KeyEquals(const nsPrefObserverHashKey *aOther) const
347 return mCallback == aOther->mCallback &&
348 mPref.Equals(aOther->mPref);
351 nsPrefObserverHashKey *GetKey() const
353 return const_cast<nsPrefObserverHashKey*>(this);
356 enum { ALLOW_MEMMOVE = PR_TRUE };
358 public:
359 nsCString mPref;
360 PrefChangedFunc mCallback;
363 // For nsContentUtils::RegisterPrefCallback/UnregisterPrefCallback
364 class nsPrefOldCallback : public nsIObserver,
365 public nsPrefObserverHashKey
367 public:
368 NS_DECL_ISUPPORTS
369 NS_DECL_NSIOBSERVER
371 public:
372 nsPrefOldCallback(const char *aPref, PrefChangedFunc aCallback)
373 : nsPrefObserverHashKey(aPref, aCallback) { }
375 ~nsPrefOldCallback() {
376 nsIPrefBranch2 *prefBranch = nsContentUtils::GetPrefBranch();
377 if(prefBranch)
378 prefBranch->RemoveObserver(mPref.get(), this);
381 void AppendClosure(void *aClosure) {
382 mClosures.AppendElement(aClosure);
385 void RemoveClosure(void *aClosure) {
386 mClosures.RemoveElement(aClosure);
389 PRBool HasNoClosures() {
390 return mClosures.Length() == 0;
393 public:
394 nsTArray<void *> mClosures;
397 NS_IMPL_ISUPPORTS1(nsPrefOldCallback, nsIObserver)
399 NS_IMETHODIMP
400 nsPrefOldCallback::Observe(nsISupports *aSubject,
401 const char *aTopic,
402 const PRUnichar *aData)
404 NS_ASSERTION(!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
405 "invalid topic");
406 NS_LossyConvertUTF16toASCII data(aData);
407 for (PRUint32 i = 0; i < mClosures.Length(); i++) {
408 mCallback(data.get(), mClosures.ElementAt(i));
411 return NS_OK;
414 struct PrefCacheData {
415 void* cacheLocation;
416 union {
417 PRBool defaultValueBool;
418 PRInt32 defaultValueInt;
422 nsTArray<nsAutoPtr<PrefCacheData> >* sPrefCacheData = nsnull;
424 // static
425 nsresult
426 nsContentUtils::Init()
428 if (sInitialized) {
429 NS_WARNING("Init() called twice");
431 return NS_OK;
434 sPrefCacheData = new nsTArray<nsAutoPtr<PrefCacheData> >();
436 // It's ok to not have a pref service.
437 CallGetService(NS_PREFSERVICE_CONTRACTID, &sPrefBranch);
439 nsresult rv = NS_GetNameSpaceManager(&sNameSpaceManager);
440 NS_ENSURE_SUCCESS(rv, rv);
442 nsXPConnect* xpconnect = nsXPConnect::GetXPConnect();
443 NS_ENSURE_TRUE(xpconnect, NS_ERROR_FAILURE);
445 sXPConnect = xpconnect;
446 sThreadJSContextStack = xpconnect;
448 sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
449 if(!sSecurityManager)
450 return NS_ERROR_FAILURE;
451 NS_ADDREF(sSecurityManager);
453 rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
454 if (NS_FAILED(rv)) {
455 // This makes life easier, but we can live without it.
457 sIOService = nsnull;
460 rv = CallGetService(NS_LBRK_CONTRACTID, &sLineBreaker);
461 NS_ENSURE_SUCCESS(rv, rv);
463 rv = CallGetService(NS_WBRK_CONTRACTID, &sWordBreaker);
464 NS_ENSURE_SUCCESS(rv, rv);
466 rv = CallGetService(NS_UNICHARCATEGORY_CONTRACTID, &sGenCat);
467 NS_ENSURE_SUCCESS(rv, rv);
469 rv = CallGetService(NS_IHISTORY_CONTRACTID, &sHistory);
470 if (NS_FAILED(rv)) {
471 NS_RUNTIMEABORT("Cannot get the history service");
472 return rv;
475 sPtrsToPtrsToRelease = new nsTArray<nsISupports**>();
476 if (!sPtrsToPtrsToRelease) {
477 return NS_ERROR_OUT_OF_MEMORY;
480 if (!InitializeEventTable())
481 return NS_ERROR_FAILURE;
483 if (!sEventListenerManagersHash.ops) {
484 static PLDHashTableOps hash_table_ops =
486 PL_DHashAllocTable,
487 PL_DHashFreeTable,
488 PL_DHashVoidPtrKeyStub,
489 PL_DHashMatchEntryStub,
490 PL_DHashMoveEntryStub,
491 EventListenerManagerHashClearEntry,
492 PL_DHashFinalizeStub,
493 EventListenerManagerHashInitEntry
496 if (!PL_DHashTableInit(&sEventListenerManagersHash, &hash_table_ops,
497 nsnull, sizeof(EventListenerManagerMapEntry), 16)) {
498 sEventListenerManagersHash.ops = nsnull;
500 return NS_ERROR_OUT_OF_MEMORY;
504 sBlockedScriptRunners = new nsCOMArray<nsIRunnable>;
505 NS_ENSURE_TRUE(sBlockedScriptRunners, NS_ERROR_OUT_OF_MEMORY);
507 nsContentUtils::AddBoolPrefVarCache("dom.allow_XUL_XBL_for_file",
508 &sAllowXULXBL_for_file);
510 sInitialized = PR_TRUE;
512 return NS_OK;
515 bool nsContentUtils::sImgLoaderInitialized;
517 void
518 nsContentUtils::InitImgLoader()
520 sImgLoaderInitialized = true;
522 // Ignore failure and just don't load images
523 nsresult rv = CallGetService("@mozilla.org/image/loader;1", &sImgLoader);
524 if (NS_FAILED(rv)) {
525 // no image loading for us. Oh, well.
526 sImgLoader = nsnull;
527 sImgCache = nsnull;
528 } else {
529 if (NS_FAILED(CallGetService("@mozilla.org/image/cache;1", &sImgCache )))
530 sImgCache = nsnull;
534 PRBool
535 nsContentUtils::InitializeEventTable() {
536 NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
537 NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
539 static const EventNameMapping eventArray[] = {
540 { nsGkAtoms::onmousedown, NS_MOUSE_BUTTON_DOWN, EventNameType_All, NS_MOUSE_EVENT },
541 { nsGkAtoms::onmouseup, NS_MOUSE_BUTTON_UP, EventNameType_All, NS_MOUSE_EVENT },
542 { nsGkAtoms::onclick, NS_MOUSE_CLICK, EventNameType_All, NS_MOUSE_EVENT },
543 { nsGkAtoms::ondblclick, NS_MOUSE_DOUBLECLICK, EventNameType_HTMLXUL, NS_MOUSE_EVENT },
544 { nsGkAtoms::onmouseover, NS_MOUSE_ENTER_SYNTH, EventNameType_All, NS_MOUSE_EVENT },
545 { nsGkAtoms::onmouseout, NS_MOUSE_EXIT_SYNTH, EventNameType_All, NS_MOUSE_EVENT },
546 { nsGkAtoms::onMozMouseHittest, NS_MOUSE_MOZHITTEST, EventNameType_None, NS_MOUSE_EVENT },
547 { nsGkAtoms::onmousemove, NS_MOUSE_MOVE, EventNameType_All, NS_MOUSE_EVENT },
548 { nsGkAtoms::oncontextmenu, NS_CONTEXTMENU, EventNameType_HTMLXUL, NS_MOUSE_EVENT },
550 { nsGkAtoms::onkeydown, NS_KEY_DOWN, EventNameType_HTMLXUL, NS_KEY_EVENT },
551 { nsGkAtoms::onkeyup, NS_KEY_UP, EventNameType_HTMLXUL, NS_KEY_EVENT },
552 { nsGkAtoms::onkeypress, NS_KEY_PRESS, EventNameType_HTMLXUL, NS_KEY_EVENT },
554 { nsGkAtoms::onfocus, NS_FOCUS_CONTENT, EventNameType_HTMLXUL, NS_FOCUS_EVENT },
555 { nsGkAtoms::onblur, NS_BLUR_CONTENT, EventNameType_HTMLXUL, NS_FOCUS_EVENT },
557 { nsGkAtoms::onoffline, NS_OFFLINE, EventNameType_HTMLXUL, NS_EVENT },
558 { nsGkAtoms::ononline, NS_ONLINE, EventNameType_HTMLXUL, NS_EVENT },
559 { nsGkAtoms::onsubmit, NS_FORM_SUBMIT, EventNameType_HTMLXUL, NS_EVENT },
560 { nsGkAtoms::onreset, NS_FORM_RESET, EventNameType_HTMLXUL, NS_EVENT },
561 { nsGkAtoms::onchange, NS_FORM_CHANGE, EventNameType_HTMLXUL, NS_EVENT },
562 { nsGkAtoms::onselect, NS_FORM_SELECTED, EventNameType_HTMLXUL, NS_EVENT },
563 { nsGkAtoms::oninvalid, NS_FORM_INVALID, EventNameType_HTMLXUL, NS_EVENT },
564 { nsGkAtoms::onload, NS_LOAD, EventNameType_All, NS_EVENT },
565 { nsGkAtoms::onpopstate, NS_POPSTATE, EventNameType_HTMLXUL, NS_EVENT_NULL },
566 { nsGkAtoms::onunload, NS_PAGE_UNLOAD,
567 (EventNameType_HTMLXUL | EventNameType_SVGSVG), NS_EVENT },
568 { nsGkAtoms::onhashchange, NS_HASHCHANGE, EventNameType_HTMLXUL, NS_EVENT },
569 { nsGkAtoms::onreadystatechange, NS_READYSTATECHANGE, EventNameType_HTMLXUL },
570 { nsGkAtoms::onbeforeunload, NS_BEFORE_PAGE_UNLOAD, EventNameType_HTMLXUL, NS_EVENT },
571 { nsGkAtoms::onabort, NS_IMAGE_ABORT,
572 (EventNameType_HTMLXUL | EventNameType_SVGSVG), NS_EVENT },
573 { nsGkAtoms::onerror, NS_LOAD_ERROR,
574 (EventNameType_HTMLXUL | EventNameType_SVGSVG), NS_EVENT },
575 { nsGkAtoms::onbeforescriptexecute, NS_BEFORE_SCRIPT_EXECUTE, EventNameType_HTMLXUL, NS_EVENT },
576 { nsGkAtoms::onafterscriptexecute, NS_AFTER_SCRIPT_EXECUTE, EventNameType_HTMLXUL, NS_EVENT },
578 { nsGkAtoms::onDOMAttrModified, NS_MUTATION_ATTRMODIFIED, EventNameType_HTMLXUL, NS_MUTATION_EVENT },
579 { nsGkAtoms::onDOMCharacterDataModified, NS_MUTATION_CHARACTERDATAMODIFIED, EventNameType_HTMLXUL, NS_MUTATION_EVENT },
580 { nsGkAtoms::onDOMNodeInserted, NS_MUTATION_NODEINSERTED, EventNameType_HTMLXUL, NS_MUTATION_EVENT },
581 { nsGkAtoms::onDOMNodeRemoved, NS_MUTATION_NODEREMOVED, EventNameType_HTMLXUL, NS_MUTATION_EVENT },
582 { nsGkAtoms::onDOMNodeInsertedIntoDocument, NS_MUTATION_NODEINSERTEDINTODOCUMENT, EventNameType_HTMLXUL, NS_MUTATION_EVENT },
583 { nsGkAtoms::onDOMNodeRemovedFromDocument, NS_MUTATION_NODEREMOVEDFROMDOCUMENT, EventNameType_HTMLXUL, NS_MUTATION_EVENT },
584 { nsGkAtoms::onDOMSubtreeModified, NS_MUTATION_SUBTREEMODIFIED, EventNameType_HTMLXUL, NS_MUTATION_EVENT },
586 { nsGkAtoms::onDOMActivate, NS_UI_ACTIVATE, EventNameType_HTMLXUL, NS_UI_EVENT },
587 { nsGkAtoms::onDOMFocusIn, NS_UI_FOCUSIN, EventNameType_HTMLXUL, NS_UI_EVENT },
588 { nsGkAtoms::onDOMFocusOut, NS_UI_FOCUSOUT, EventNameType_HTMLXUL, NS_UI_EVENT },
589 { nsGkAtoms::oninput, NS_FORM_INPUT, EventNameType_HTMLXUL, NS_UI_EVENT },
591 { nsGkAtoms::onDOMMouseScroll, NS_MOUSE_SCROLL, EventNameType_HTMLXUL, NS_MOUSE_SCROLL_EVENT },
592 { nsGkAtoms::onMozMousePixelScroll, NS_MOUSE_PIXEL_SCROLL, EventNameType_HTMLXUL, NS_MOUSE_SCROLL_EVENT },
594 { nsGkAtoms::onpageshow, NS_PAGE_SHOW, EventNameType_HTML, NS_EVENT },
595 { nsGkAtoms::onpagehide, NS_PAGE_HIDE, EventNameType_HTML, NS_EVENT },
596 { nsGkAtoms::onMozBeforeResize, NS_BEFORERESIZE_EVENT, EventNameType_None, NS_EVENT },
597 { nsGkAtoms::onresize, NS_RESIZE_EVENT,
598 (EventNameType_HTMLXUL | EventNameType_SVGSVG), NS_EVENT },
599 { nsGkAtoms::onscroll, NS_SCROLL_EVENT,
600 (EventNameType_HTMLXUL | EventNameType_SVGSVG), NS_EVENT_NULL },
601 { nsGkAtoms::oncopy, NS_COPY, EventNameType_HTMLXUL, NS_EVENT },
602 { nsGkAtoms::oncut, NS_CUT, EventNameType_HTMLXUL, NS_EVENT },
603 { nsGkAtoms::onpaste, NS_PASTE, EventNameType_HTMLXUL, NS_EVENT },
604 // XUL specific events
605 { nsGkAtoms::ontext, NS_TEXT_TEXT, EventNameType_XUL, NS_EVENT_NULL },
607 { nsGkAtoms::oncompositionstart, NS_COMPOSITION_START, EventNameType_XUL, NS_COMPOSITION_EVENT },
608 { nsGkAtoms::oncompositionend, NS_COMPOSITION_END, EventNameType_XUL, NS_COMPOSITION_EVENT },
610 { nsGkAtoms::oncommand, NS_XUL_COMMAND, EventNameType_XUL, NS_INPUT_EVENT },
612 { nsGkAtoms::onclose, NS_XUL_CLOSE, EventNameType_XUL, NS_EVENT_NULL},
613 { nsGkAtoms::onpopupshowing, NS_XUL_POPUP_SHOWING, EventNameType_XUL, NS_EVENT_NULL},
614 { nsGkAtoms::onpopupshown, NS_XUL_POPUP_SHOWN, EventNameType_XUL, NS_EVENT_NULL},
615 { nsGkAtoms::onpopuphiding, NS_XUL_POPUP_HIDING, EventNameType_XUL, NS_EVENT_NULL},
616 { nsGkAtoms::onpopuphidden, NS_XUL_POPUP_HIDDEN, EventNameType_XUL, NS_EVENT_NULL},
617 { nsGkAtoms::onbroadcast, NS_XUL_BROADCAST, EventNameType_XUL, NS_EVENT_NULL},
618 { nsGkAtoms::oncommandupdate, NS_XUL_COMMAND_UPDATE, EventNameType_XUL, NS_EVENT_NULL},
620 { nsGkAtoms::ondragenter, NS_DRAGDROP_ENTER, EventNameType_HTMLXUL, NS_DRAG_EVENT },
621 { nsGkAtoms::ondragover, NS_DRAGDROP_OVER_SYNTH, EventNameType_HTMLXUL, NS_DRAG_EVENT },
622 { nsGkAtoms::ondragexit, NS_DRAGDROP_EXIT_SYNTH, EventNameType_XUL, NS_DRAG_EVENT },
623 { nsGkAtoms::ondragdrop, NS_DRAGDROP_DRAGDROP, EventNameType_XUL, NS_DRAG_EVENT },
624 { nsGkAtoms::ondraggesture, NS_DRAGDROP_GESTURE, EventNameType_XUL, NS_DRAG_EVENT },
625 { nsGkAtoms::ondrag, NS_DRAGDROP_DRAG, EventNameType_HTMLXUL, NS_DRAG_EVENT },
626 { nsGkAtoms::ondragend, NS_DRAGDROP_END, EventNameType_HTMLXUL, NS_DRAG_EVENT },
627 { nsGkAtoms::ondragstart, NS_DRAGDROP_START, EventNameType_HTMLXUL, NS_DRAG_EVENT },
628 { nsGkAtoms::ondragleave, NS_DRAGDROP_LEAVE_SYNTH, EventNameType_HTMLXUL, NS_DRAG_EVENT },
629 { nsGkAtoms::ondrop, NS_DRAGDROP_DROP, EventNameType_HTMLXUL, NS_DRAG_EVENT },
631 { nsGkAtoms::onoverflow, NS_SCROLLPORT_OVERFLOW, EventNameType_XUL, NS_EVENT_NULL},
632 { nsGkAtoms::onunderflow, NS_SCROLLPORT_UNDERFLOW, EventNameType_XUL, NS_EVENT_NULL},
633 #ifdef MOZ_SVG
634 { nsGkAtoms::onSVGLoad, NS_SVG_LOAD, EventNameType_None, NS_SVG_EVENT },
635 { nsGkAtoms::onSVGUnload, NS_SVG_UNLOAD, EventNameType_None, NS_SVG_EVENT },
636 { nsGkAtoms::onSVGAbort, NS_SVG_ABORT, EventNameType_None, NS_SVG_EVENT },
637 { nsGkAtoms::onSVGError, NS_SVG_ERROR, EventNameType_None, NS_SVG_EVENT },
638 { nsGkAtoms::onSVGResize, NS_SVG_RESIZE, EventNameType_None, NS_SVG_EVENT },
639 { nsGkAtoms::onSVGScroll, NS_SVG_SCROLL, EventNameType_None, NS_SVG_EVENT },
641 { nsGkAtoms::onSVGZoom, NS_SVG_ZOOM, EventNameType_None, NS_SVGZOOM_EVENT },
643 // This is a bit hackish, but SVG's event names are weird.
644 { nsGkAtoms::onzoom, NS_SVG_ZOOM, EventNameType_SVGSVG, NS_EVENT_NULL },
645 #endif // MOZ_SVG
646 #ifdef MOZ_SMIL
647 { nsGkAtoms::onbegin, NS_SMIL_BEGIN, EventNameType_SMIL, NS_EVENT_NULL },
648 { nsGkAtoms::onbeginEvent, NS_SMIL_BEGIN, EventNameType_None, NS_SMIL_TIME_EVENT },
649 { nsGkAtoms::onend, NS_SMIL_END, EventNameType_SMIL, NS_EVENT_NULL },
650 { nsGkAtoms::onendEvent, NS_SMIL_END, EventNameType_None, NS_SMIL_TIME_EVENT },
651 { nsGkAtoms::onrepeat, NS_SMIL_REPEAT, EventNameType_SMIL, NS_EVENT_NULL },
652 { nsGkAtoms::onrepeatEvent, NS_SMIL_REPEAT, EventNameType_None, NS_SMIL_TIME_EVENT },
653 #endif // MOZ_SMIL
654 #ifdef MOZ_MEDIA
655 { nsGkAtoms::onloadstart, NS_LOADSTART, EventNameType_HTML, NS_EVENT_NULL },
656 { nsGkAtoms::onprogress, NS_PROGRESS, EventNameType_HTML, NS_EVENT_NULL },
657 { nsGkAtoms::onsuspend, NS_SUSPEND, EventNameType_HTML, NS_EVENT_NULL },
658 { nsGkAtoms::onemptied, NS_EMPTIED, EventNameType_HTML, NS_EVENT_NULL },
659 { nsGkAtoms::onstalled, NS_STALLED, EventNameType_HTML, NS_EVENT_NULL },
660 { nsGkAtoms::onplay, NS_PLAY, EventNameType_HTML, NS_EVENT_NULL },
661 { nsGkAtoms::onpause, NS_PAUSE, EventNameType_HTML, NS_EVENT_NULL },
662 { nsGkAtoms::onloadedmetadata, NS_LOADEDMETADATA, EventNameType_HTML, NS_EVENT_NULL },
663 { nsGkAtoms::onloadeddata, NS_LOADEDDATA, EventNameType_HTML, NS_EVENT_NULL },
664 { nsGkAtoms::onwaiting, NS_WAITING, EventNameType_HTML, NS_EVENT_NULL },
665 { nsGkAtoms::onplaying, NS_PLAYING, EventNameType_HTML, NS_EVENT_NULL },
666 { nsGkAtoms::oncanplay, NS_CANPLAY, EventNameType_HTML, NS_EVENT_NULL },
667 { nsGkAtoms::oncanplaythrough, NS_CANPLAYTHROUGH, EventNameType_HTML, NS_EVENT_NULL },
668 { nsGkAtoms::onseeking, NS_SEEKING, EventNameType_HTML, NS_EVENT_NULL },
669 { nsGkAtoms::onseeked, NS_SEEKED, EventNameType_HTML, NS_EVENT_NULL },
670 { nsGkAtoms::ontimeupdate, NS_TIMEUPDATE, EventNameType_HTML, NS_EVENT_NULL },
671 { nsGkAtoms::onended, NS_ENDED, EventNameType_HTML, NS_EVENT_NULL },
672 { nsGkAtoms::onratechange, NS_RATECHANGE, EventNameType_HTML, NS_EVENT_NULL },
673 { nsGkAtoms::ondurationchange, NS_DURATIONCHANGE, EventNameType_HTML, NS_EVENT_NULL },
674 { nsGkAtoms::onvolumechange, NS_VOLUMECHANGE, EventNameType_HTML, NS_EVENT_NULL },
675 { nsGkAtoms::onMozAudioAvailable, NS_MOZAUDIOAVAILABLE, EventNameType_None, NS_EVENT_NULL },
676 #endif // MOZ_MEDIA
677 { nsGkAtoms::onMozAfterPaint, NS_AFTERPAINT, EventNameType_None, NS_EVENT },
678 { nsGkAtoms::onMozBeforePaint, NS_BEFOREPAINT, EventNameType_None, NS_EVENT_NULL },
680 { nsGkAtoms::onMozScrolledAreaChanged, NS_SCROLLEDAREACHANGED, EventNameType_None, NS_SCROLLAREA_EVENT },
682 // Simple gesture events
683 { nsGkAtoms::onMozSwipeGesture, NS_SIMPLE_GESTURE_SWIPE, EventNameType_None, NS_SIMPLE_GESTURE_EVENT },
684 { nsGkAtoms::onMozMagnifyGestureStart, NS_SIMPLE_GESTURE_MAGNIFY_START, EventNameType_None, NS_SIMPLE_GESTURE_EVENT },
685 { nsGkAtoms::onMozMagnifyGestureUpdate, NS_SIMPLE_GESTURE_MAGNIFY_UPDATE, EventNameType_None, NS_SIMPLE_GESTURE_EVENT },
686 { nsGkAtoms::onMozMagnifyGesture, NS_SIMPLE_GESTURE_MAGNIFY, EventNameType_None, NS_SIMPLE_GESTURE_EVENT },
687 { nsGkAtoms::onMozRotateGestureStart, NS_SIMPLE_GESTURE_ROTATE_START, EventNameType_None, NS_SIMPLE_GESTURE_EVENT },
688 { nsGkAtoms::onMozRotateGestureUpdate, NS_SIMPLE_GESTURE_ROTATE_UPDATE, EventNameType_None, NS_SIMPLE_GESTURE_EVENT },
689 { nsGkAtoms::onMozRotateGesture, NS_SIMPLE_GESTURE_ROTATE, EventNameType_None, NS_SIMPLE_GESTURE_EVENT },
690 { nsGkAtoms::onMozTapGesture, NS_SIMPLE_GESTURE_TAP, EventNameType_None, NS_SIMPLE_GESTURE_EVENT },
691 { nsGkAtoms::onMozPressTapGesture, NS_SIMPLE_GESTURE_PRESSTAP, EventNameType_None, NS_SIMPLE_GESTURE_EVENT },
693 { nsGkAtoms::onMozTouchDown, NS_MOZTOUCH_DOWN, EventNameType_None, NS_MOZTOUCH_EVENT },
694 { nsGkAtoms::onMozTouchMove, NS_MOZTOUCH_MOVE, EventNameType_None, NS_MOZTOUCH_EVENT },
695 { nsGkAtoms::onMozTouchUp, NS_MOZTOUCH_UP, EventNameType_None, NS_MOZTOUCH_EVENT },
697 { nsGkAtoms::ontransitionend, NS_TRANSITION_END, EventNameType_None, NS_TRANSITION_EVENT }
700 sAtomEventTable = new nsDataHashtable<nsISupportsHashKey, EventNameMapping>;
701 sStringEventTable = new nsDataHashtable<nsStringHashKey, EventNameMapping>;
702 sUserDefinedEvents = new nsCOMArray<nsIAtom>(64);
704 if (!sAtomEventTable || !sStringEventTable || !sUserDefinedEvents ||
705 !sAtomEventTable->Init(int(NS_ARRAY_LENGTH(eventArray) / 0.75) + 1) ||
706 !sStringEventTable->Init(int(NS_ARRAY_LENGTH(eventArray) / 0.75) + 1)) {
707 delete sAtomEventTable;
708 sAtomEventTable = nsnull;
709 delete sStringEventTable;
710 sStringEventTable = nsnull;
711 delete sUserDefinedEvents;
712 sUserDefinedEvents = nsnull;
713 return PR_FALSE;
716 for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(eventArray); ++i) {
717 if (!sAtomEventTable->Put(eventArray[i].mAtom, eventArray[i]) ||
718 !sStringEventTable->Put(Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
719 eventArray[i])) {
720 delete sAtomEventTable;
721 sAtomEventTable = nsnull;
722 delete sStringEventTable;
723 sStringEventTable = nsnull;
724 return PR_FALSE;
728 return PR_TRUE;
732 * Access a cached parser service. Don't addref. We need only one
733 * reference to it and this class has that one.
735 /* static */
736 nsIParserService*
737 nsContentUtils::GetParserService()
739 // XXX: This isn't accessed from several threads, is it?
740 if (!sParserService) {
741 // Lock, recheck sCachedParserService and aquire if this should be
742 // safe for multiple threads.
743 nsresult rv = CallGetService(kParserServiceCID, &sParserService);
744 if (NS_FAILED(rv)) {
745 sParserService = nsnull;
749 return sParserService;
752 #ifdef MOZ_XTF
753 nsIXTFService*
754 nsContentUtils::GetXTFService()
756 if (!sXTFService) {
757 nsresult rv = CallGetService(kXTFServiceCID, &sXTFService);
758 if (NS_FAILED(rv)) {
759 sXTFService = nsnull;
763 return sXTFService;
765 #endif
767 #ifdef IBMBIDI
768 nsIBidiKeyboard*
769 nsContentUtils::GetBidiKeyboard()
771 if (!sBidiKeyboard) {
772 nsresult rv = CallGetService("@mozilla.org/widget/bidikeyboard;1", &sBidiKeyboard);
773 if (NS_FAILED(rv)) {
774 sBidiKeyboard = nsnull;
777 return sBidiKeyboard;
779 #endif
781 template <class OutputIterator>
782 struct NormalizeNewlinesCharTraits {
783 public:
784 typedef typename OutputIterator::value_type value_type;
786 public:
787 NormalizeNewlinesCharTraits(OutputIterator& aIterator) : mIterator(aIterator) { }
788 void writechar(typename OutputIterator::value_type aChar) {
789 *mIterator++ = aChar;
792 private:
793 OutputIterator mIterator;
796 #ifdef HAVE_CPP_PARTIAL_SPECIALIZATION
798 template <class CharT>
799 struct NormalizeNewlinesCharTraits<CharT*> {
800 public:
801 typedef CharT value_type;
803 public:
804 NormalizeNewlinesCharTraits(CharT* aCharPtr) : mCharPtr(aCharPtr) { }
805 void writechar(CharT aChar) {
806 *mCharPtr++ = aChar;
809 private:
810 CharT* mCharPtr;
813 #else
815 NS_SPECIALIZE_TEMPLATE
816 struct NormalizeNewlinesCharTraits<char*> {
817 public:
818 typedef char value_type;
820 public:
821 NormalizeNewlinesCharTraits(char* aCharPtr) : mCharPtr(aCharPtr) { }
822 void writechar(char aChar) {
823 *mCharPtr++ = aChar;
826 private:
827 char* mCharPtr;
830 NS_SPECIALIZE_TEMPLATE
831 struct NormalizeNewlinesCharTraits<PRUnichar*> {
832 public:
833 typedef PRUnichar value_type;
835 public:
836 NormalizeNewlinesCharTraits(PRUnichar* aCharPtr) : mCharPtr(aCharPtr) { }
837 void writechar(PRUnichar aChar) {
838 *mCharPtr++ = aChar;
841 private:
842 PRUnichar* mCharPtr;
845 #endif
847 template <class OutputIterator>
848 class CopyNormalizeNewlines
850 public:
851 typedef typename OutputIterator::value_type value_type;
853 public:
854 CopyNormalizeNewlines(OutputIterator* aDestination,
855 PRBool aLastCharCR=PR_FALSE) :
856 mLastCharCR(aLastCharCR),
857 mDestination(aDestination),
858 mWritten(0)
861 PRUint32 GetCharsWritten() {
862 return mWritten;
865 PRBool IsLastCharCR() {
866 return mLastCharCR;
869 void write(const typename OutputIterator::value_type* aSource, PRUint32 aSourceLength) {
871 const typename OutputIterator::value_type* done_writing = aSource + aSourceLength;
873 // If the last source buffer ended with a CR...
874 if (mLastCharCR) {
875 // ..and if the next one is a LF, then skip it since
876 // we've already written out a newline
877 if (aSourceLength && (*aSource == value_type('\n'))) {
878 ++aSource;
880 mLastCharCR = PR_FALSE;
883 PRUint32 num_written = 0;
884 while ( aSource < done_writing ) {
885 if (*aSource == value_type('\r')) {
886 mDestination->writechar('\n');
887 ++aSource;
888 // If we've reached the end of the buffer, record
889 // that we wrote out a CR
890 if (aSource == done_writing) {
891 mLastCharCR = PR_TRUE;
893 // If the next character is a LF, skip it
894 else if (*aSource == value_type('\n')) {
895 ++aSource;
898 else {
899 mDestination->writechar(*aSource++);
901 ++num_written;
904 mWritten += num_written;
907 private:
908 PRBool mLastCharCR;
909 OutputIterator* mDestination;
910 PRUint32 mWritten;
913 // static
914 PRUint32
915 nsContentUtils::CopyNewlineNormalizedUnicodeTo(const nsAString& aSource,
916 PRUint32 aSrcOffset,
917 PRUnichar* aDest,
918 PRUint32 aLength,
919 PRBool& aLastCharCR)
921 typedef NormalizeNewlinesCharTraits<PRUnichar*> sink_traits;
923 sink_traits dest_traits(aDest);
924 CopyNormalizeNewlines<sink_traits> normalizer(&dest_traits,aLastCharCR);
925 nsReadingIterator<PRUnichar> fromBegin, fromEnd;
926 copy_string(aSource.BeginReading(fromBegin).advance( PRInt32(aSrcOffset) ),
927 aSource.BeginReading(fromEnd).advance( PRInt32(aSrcOffset+aLength) ),
928 normalizer);
929 aLastCharCR = normalizer.IsLastCharCR();
930 return normalizer.GetCharsWritten();
933 // static
934 PRUint32
935 nsContentUtils::CopyNewlineNormalizedUnicodeTo(nsReadingIterator<PRUnichar>& aSrcStart, const nsReadingIterator<PRUnichar>& aSrcEnd, nsAString& aDest)
937 typedef nsWritingIterator<PRUnichar> WritingIterator;
938 typedef NormalizeNewlinesCharTraits<WritingIterator> sink_traits;
940 WritingIterator iter;
941 aDest.BeginWriting(iter);
942 sink_traits dest_traits(iter);
943 CopyNormalizeNewlines<sink_traits> normalizer(&dest_traits);
944 copy_string(aSrcStart, aSrcEnd, normalizer);
945 return normalizer.GetCharsWritten();
948 // Replaced by precompiled CCMap (see bug 180266). To update the list
949 // of characters, see one of files included below. As for the way
950 // the original list of characters was obtained by Frank Tang, see bug 54467.
951 // Updated to fix the regression (bug 263411). The list contains
952 // characters of the following Unicode character classes : Ps, Pi, Po, Pf, Pe.
953 // (ref.: http://www.w3.org/TR/2004/CR-CSS21-20040225/selector.html#first-letter)
954 #include "punct_marks.x-ccmap"
955 DEFINE_X_CCMAP(gPuncCharsCCMapExt, const);
957 // static
958 PRBool
959 nsContentUtils::IsPunctuationMark(PRUint32 aChar)
961 return CCMAP_HAS_CHAR_EXT(gPuncCharsCCMapExt, aChar);
964 // static
965 PRBool
966 nsContentUtils::IsPunctuationMarkAt(const nsTextFragment* aFrag, PRUint32 aOffset)
968 PRUnichar h = aFrag->CharAt(aOffset);
969 if (!IS_SURROGATE(h)) {
970 return IsPunctuationMark(h);
972 if (NS_IS_HIGH_SURROGATE(h) && aOffset + 1 < aFrag->GetLength()) {
973 PRUnichar l = aFrag->CharAt(aOffset + 1);
974 if (NS_IS_LOW_SURROGATE(l)) {
975 return IsPunctuationMark(SURROGATE_TO_UCS4(h, l));
978 return PR_FALSE;
981 // static
982 PRBool nsContentUtils::IsAlphanumeric(PRUint32 aChar)
984 nsIUGenCategory::nsUGenCategory cat = sGenCat->Get(aChar);
986 return (cat == nsIUGenCategory::kLetter || cat == nsIUGenCategory::kNumber);
989 // static
990 PRBool nsContentUtils::IsAlphanumericAt(const nsTextFragment* aFrag, PRUint32 aOffset)
992 PRUnichar h = aFrag->CharAt(aOffset);
993 if (!IS_SURROGATE(h)) {
994 return IsAlphanumeric(h);
996 if (NS_IS_HIGH_SURROGATE(h) && aOffset + 1 < aFrag->GetLength()) {
997 PRUnichar l = aFrag->CharAt(aOffset + 1);
998 if (NS_IS_LOW_SURROGATE(l)) {
999 return IsAlphanumeric(SURROGATE_TO_UCS4(h, l));
1002 return PR_FALSE;
1005 /* static */
1006 PRBool
1007 nsContentUtils::IsHTMLWhitespace(PRUnichar aChar)
1009 return aChar == PRUnichar(0x0009) ||
1010 aChar == PRUnichar(0x000A) ||
1011 aChar == PRUnichar(0x000C) ||
1012 aChar == PRUnichar(0x000D) ||
1013 aChar == PRUnichar(0x0020);
1016 /* static */
1017 PRBool
1018 nsContentUtils::ParseIntMarginValue(const nsAString& aString, nsIntMargin& result)
1020 nsAutoString marginStr(aString);
1021 marginStr.CompressWhitespace(PR_TRUE, PR_TRUE);
1022 if (marginStr.IsEmpty()) {
1023 return PR_FALSE;
1026 PRInt32 start = 0, end = 0;
1027 for (int count = 0; count < 4; count++) {
1028 if ((PRUint32)end >= marginStr.Length())
1029 return PR_FALSE;
1031 // top, right, bottom, left
1032 if (count < 3)
1033 end = Substring(marginStr, start).FindChar(',');
1034 else
1035 end = Substring(marginStr, start).Length();
1037 if (end <= 0)
1038 return PR_FALSE;
1040 PRInt32 ec, val =
1041 nsString(Substring(marginStr, start, end)).ToInteger(&ec);
1042 if (NS_FAILED(ec))
1043 return PR_FALSE;
1045 switch(count) {
1046 case 0:
1047 result.top = val;
1048 break;
1049 case 1:
1050 result.right = val;
1051 break;
1052 case 2:
1053 result.bottom = val;
1054 break;
1055 case 3:
1056 result.left = val;
1057 break;
1059 start += end + 1;
1061 return PR_TRUE;
1064 /* static */
1065 void
1066 nsContentUtils::GetOfflineAppManifest(nsIDocument *aDocument, nsIURI **aURI)
1068 Element* docElement = aDocument->GetRootElement();
1069 if (!docElement) {
1070 return;
1073 nsAutoString manifestSpec;
1074 docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
1076 // Manifest URIs can't have fragment identifiers.
1077 if (manifestSpec.IsEmpty() ||
1078 manifestSpec.FindChar('#') != kNotFound) {
1079 return;
1082 nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec,
1083 aDocument,
1084 aDocument->GetDocBaseURI());
1087 /* static */
1088 PRBool
1089 nsContentUtils::OfflineAppAllowed(nsIURI *aURI)
1091 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1092 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
1093 if (!updateService) {
1094 return PR_FALSE;
1097 PRBool allowed;
1098 nsresult rv = updateService->OfflineAppAllowedForURI(aURI,
1099 sPrefBranch,
1100 &allowed);
1101 return NS_SUCCEEDED(rv) && allowed;
1104 /* static */
1105 PRBool
1106 nsContentUtils::OfflineAppAllowed(nsIPrincipal *aPrincipal)
1108 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1109 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
1110 if (!updateService) {
1111 return PR_FALSE;
1114 PRBool allowed;
1115 nsresult rv = updateService->OfflineAppAllowed(aPrincipal,
1116 sPrefBranch,
1117 &allowed);
1118 return NS_SUCCEEDED(rv) && allowed;
1121 // static
1122 void
1123 nsContentUtils::Shutdown()
1125 sInitialized = PR_FALSE;
1127 NS_HTMLParanoidFragmentSinkShutdown();
1128 NS_XHTMLParanoidFragmentSinkShutdown();
1130 NS_IF_RELEASE(sContentPolicyService);
1131 sTriedToGetContentPolicy = PR_FALSE;
1132 PRUint32 i;
1133 for (i = 0; i < PropertiesFile_COUNT; ++i)
1134 NS_IF_RELEASE(sStringBundles[i]);
1136 // Clean up c-style's observer
1137 if (sPrefCallbackTable) {
1138 delete sPrefCallbackTable;
1139 sPrefCallbackTable = nsnull;
1142 delete sPrefCacheData;
1143 sPrefCacheData = nsnull;
1145 NS_IF_RELEASE(sStringBundleService);
1146 NS_IF_RELEASE(sConsoleService);
1147 NS_IF_RELEASE(sDOMScriptObjectFactory);
1148 sXPConnect = nsnull;
1149 sThreadJSContextStack = nsnull;
1150 NS_IF_RELEASE(sSecurityManager);
1151 NS_IF_RELEASE(sNameSpaceManager);
1152 NS_IF_RELEASE(sParserService);
1153 NS_IF_RELEASE(sIOService);
1154 NS_IF_RELEASE(sLineBreaker);
1155 NS_IF_RELEASE(sWordBreaker);
1156 NS_IF_RELEASE(sGenCat);
1157 #ifdef MOZ_XTF
1158 NS_IF_RELEASE(sXTFService);
1159 #endif
1160 NS_IF_RELEASE(sImgLoader);
1161 NS_IF_RELEASE(sImgCache);
1162 NS_IF_RELEASE(sHistory);
1163 NS_IF_RELEASE(sPrefBranch);
1164 #ifdef IBMBIDI
1165 NS_IF_RELEASE(sBidiKeyboard);
1166 #endif
1168 delete sAtomEventTable;
1169 sAtomEventTable = nsnull;
1170 delete sStringEventTable;
1171 sStringEventTable = nsnull;
1172 delete sUserDefinedEvents;
1173 sUserDefinedEvents = nsnull;
1175 if (sPtrsToPtrsToRelease) {
1176 for (i = 0; i < sPtrsToPtrsToRelease->Length(); ++i) {
1177 nsISupports** ptrToPtr = sPtrsToPtrsToRelease->ElementAt(i);
1178 NS_RELEASE(*ptrToPtr);
1180 delete sPtrsToPtrsToRelease;
1181 sPtrsToPtrsToRelease = nsnull;
1184 if (sEventListenerManagersHash.ops) {
1185 NS_ASSERTION(sEventListenerManagersHash.entryCount == 0,
1186 "Event listener manager hash not empty at shutdown!");
1188 // See comment above.
1190 // However, we have to handle this table differently. If it still
1191 // has entries, we want to leak it too, so that we can keep it alive
1192 // in case any elements are destroyed. Because if they are, we need
1193 // their event listener managers to be destroyed too, or otherwise
1194 // it could leave dangling references in DOMClassInfo's preserved
1195 // wrapper table.
1197 if (sEventListenerManagersHash.entryCount == 0) {
1198 PL_DHashTableFinish(&sEventListenerManagersHash);
1199 sEventListenerManagersHash.ops = nsnull;
1203 NS_ASSERTION(!sBlockedScriptRunners ||
1204 sBlockedScriptRunners->Count() == 0,
1205 "How'd this happen?");
1206 delete sBlockedScriptRunners;
1207 sBlockedScriptRunners = nsnull;
1209 NS_IF_RELEASE(sSameOriginChecker);
1211 nsTextEditorState::ShutDown();
1214 // static
1215 PRBool
1216 nsContentUtils::IsCallerTrustedForCapability(const char* aCapability)
1218 // The secman really should handle UniversalXPConnect case, since that
1219 // should include UniversalBrowserRead... doesn't right now, though.
1220 PRBool hasCap;
1221 if (NS_FAILED(sSecurityManager->IsCapabilityEnabled(aCapability, &hasCap)))
1222 return PR_FALSE;
1223 if (hasCap)
1224 return PR_TRUE;
1226 if (NS_FAILED(sSecurityManager->IsCapabilityEnabled("UniversalXPConnect",
1227 &hasCap)))
1228 return PR_FALSE;
1229 return hasCap;
1233 * Checks whether two nodes come from the same origin. aTrustedNode is
1234 * considered 'safe' in that a user can operate on it and that it isn't
1235 * a js-object that implements nsIDOMNode.
1236 * Never call this function with the first node provided by script, it
1237 * must always be known to be a 'real' node!
1239 // static
1240 nsresult
1241 nsContentUtils::CheckSameOrigin(nsINode *aTrustedNode,
1242 nsIDOMNode *aUnTrustedNode)
1244 NS_PRECONDITION(aTrustedNode, "There must be a trusted node");
1246 PRBool isSystem = PR_FALSE;
1247 sSecurityManager->SubjectPrincipalIsSystem(&isSystem);
1248 if (isSystem) {
1249 // we're running as system, grant access to the node.
1251 return NS_OK;
1255 * Get hold of each node's principal
1257 nsCOMPtr<nsINode> unTrustedNode = do_QueryInterface(aUnTrustedNode);
1259 // Make sure these are both real nodes
1260 NS_ENSURE_TRUE(aTrustedNode && unTrustedNode, NS_ERROR_UNEXPECTED);
1262 nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
1263 nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
1265 if (trustedPrincipal == unTrustedPrincipal) {
1266 return NS_OK;
1269 PRBool equal;
1270 // XXXbz should we actually have a Subsumes() check here instead? Or perhaps
1271 // a separate method for that, with callers using one or the other?
1272 if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
1273 !equal) {
1274 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
1277 return NS_OK;
1280 // static
1281 PRBool
1282 nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
1283 nsIPrincipal* aPrincipal)
1285 PRBool subsumes;
1286 nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
1287 NS_ENSURE_SUCCESS(rv, PR_FALSE);
1289 if (subsumes) {
1290 return PR_TRUE;
1293 // The subject doesn't subsume aPrincipal. Allow access only if the subject
1294 // has either "UniversalXPConnect" (if aPrincipal is system principal) or
1295 // "UniversalBrowserRead" (in all other cases).
1296 PRBool isSystem;
1297 rv = sSecurityManager->IsSystemPrincipal(aPrincipal, &isSystem);
1298 isSystem = NS_FAILED(rv) || isSystem;
1299 const char* capability =
1300 NS_FAILED(rv) || isSystem ? "UniversalXPConnect" : "UniversalBrowserRead";
1302 return IsCallerTrustedForCapability(capability);
1305 // static
1306 PRBool
1307 nsContentUtils::CanCallerAccess(nsIDOMNode *aNode)
1309 // XXXbz why not check the IsCapabilityEnabled thing up front, and not bother
1310 // with the system principal games? But really, there should be a simpler
1311 // API here, dammit.
1312 nsCOMPtr<nsIPrincipal> subjectPrincipal;
1313 sSecurityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1315 if (!subjectPrincipal) {
1316 // we're running as system, grant access to the node.
1318 return PR_TRUE;
1321 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
1322 NS_ENSURE_TRUE(node, PR_FALSE);
1324 return CanCallerAccess(subjectPrincipal, node->NodePrincipal());
1327 // static
1328 PRBool
1329 nsContentUtils::CanCallerAccess(nsPIDOMWindow* aWindow)
1331 // XXXbz why not check the IsCapabilityEnabled thing up front, and not bother
1332 // with the system principal games? But really, there should be a simpler
1333 // API here, dammit.
1334 nsCOMPtr<nsIPrincipal> subjectPrincipal;
1335 sSecurityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1337 if (!subjectPrincipal) {
1338 // we're running as system, grant access to the node.
1340 return PR_TRUE;
1343 nsCOMPtr<nsIScriptObjectPrincipal> scriptObject =
1344 do_QueryInterface(aWindow->IsOuterWindow() ?
1345 aWindow->GetCurrentInnerWindow() : aWindow);
1346 NS_ENSURE_TRUE(scriptObject, PR_FALSE);
1348 return CanCallerAccess(subjectPrincipal, scriptObject->GetPrincipal());
1351 //static
1352 PRBool
1353 nsContentUtils::InProlog(nsINode *aNode)
1355 NS_PRECONDITION(aNode, "missing node to nsContentUtils::InProlog");
1357 nsINode* parent = aNode->GetNodeParent();
1358 if (!parent || !parent->IsNodeOfType(nsINode::eDOCUMENT)) {
1359 return PR_FALSE;
1362 nsIDocument* doc = static_cast<nsIDocument*>(parent);
1363 nsIContent* root = doc->GetRootElement();
1365 return !root || doc->IndexOf(aNode) < doc->IndexOf(root);
1368 static JSContext *
1369 GetContextFromDocument(nsIDocument *aDocument)
1371 nsIScriptGlobalObject *sgo = aDocument->GetScopeObject();
1372 if (!sgo) {
1373 // No script global, no context.
1374 return nsnull;
1377 nsIScriptContext *scx = sgo->GetContext();
1378 if (!scx) {
1379 // No context left in the scope...
1381 return nsnull;
1384 return (JSContext *)scx->GetNativeContext();
1387 // static
1388 nsresult
1389 nsContentUtils::GetContextAndScope(nsIDocument *aOldDocument,
1390 nsIDocument *aNewDocument, JSContext **aCx,
1391 JSObject **aNewScope)
1393 *aCx = nsnull;
1394 *aNewScope = nsnull;
1396 JSObject *newScope = aNewDocument->GetWrapper();
1397 JSObject *global;
1398 if (!newScope) {
1399 nsIScriptGlobalObject *newSGO = aNewDocument->GetScopeObject();
1400 if (!newSGO || !(global = newSGO->GetGlobalJSObject())) {
1401 return NS_OK;
1405 NS_ENSURE_TRUE(sXPConnect, NS_ERROR_NOT_INITIALIZED);
1407 JSContext *cx = aOldDocument ? GetContextFromDocument(aOldDocument) : nsnull;
1408 if (!cx) {
1409 cx = GetContextFromDocument(aNewDocument);
1411 if (!cx) {
1412 // No context reachable from the old or new document, use the
1413 // calling context, or the safe context if no caller can be
1414 // found.
1416 sThreadJSContextStack->Peek(&cx);
1418 if (!cx) {
1419 sThreadJSContextStack->GetSafeJSContext(&cx);
1421 if (!cx) {
1422 // No safe context reachable, bail.
1423 NS_WARNING("No context reachable in GetContextAndScopes()!");
1425 return NS_ERROR_NOT_AVAILABLE;
1431 if (!newScope && cx) {
1432 jsval v;
1433 nsresult rv = WrapNative(cx, global, aNewDocument, aNewDocument, &v);
1434 NS_ENSURE_SUCCESS(rv, rv);
1436 newScope = JSVAL_TO_OBJECT(v);
1439 *aCx = cx;
1440 *aNewScope = newScope;
1442 return NS_OK;
1445 nsresult
1446 nsContentUtils::ReparentContentWrappersInScope(nsIScriptGlobalObject *aOldScope,
1447 nsIScriptGlobalObject *aNewScope)
1449 JSContext *cx = nsnull;
1451 // Try really hard to find a context to work on.
1452 nsIScriptContext *context = aOldScope->GetContext();
1453 if (context) {
1454 cx = static_cast<JSContext *>(context->GetNativeContext());
1457 if (!cx) {
1458 context = aNewScope->GetContext();
1459 if (context) {
1460 cx = static_cast<JSContext *>(context->GetNativeContext());
1463 if (!cx) {
1464 sThreadJSContextStack->Peek(&cx);
1466 if (!cx) {
1467 sThreadJSContextStack->GetSafeJSContext(&cx);
1469 if (!cx) {
1470 // Wow, this is really bad!
1471 NS_WARNING("No context reachable in ReparentContentWrappers()!");
1473 return NS_ERROR_NOT_AVAILABLE;
1479 // Now that we have a context, let's get the global objects from the two
1480 // scopes and ask XPConnect to do the rest of the work.
1482 JSObject *oldScopeObj = aOldScope->GetGlobalJSObject();
1483 JSObject *newScopeObj = aNewScope->GetGlobalJSObject();
1485 if (!newScopeObj || !oldScopeObj) {
1486 // We can't really do anything without the JSObjects.
1488 return NS_ERROR_NOT_AVAILABLE;
1491 return sXPConnect->MoveWrappers(cx, oldScopeObj, newScopeObj);
1494 nsIDocShell *
1495 nsContentUtils::GetDocShellFromCaller()
1497 JSContext *cx = nsnull;
1498 sThreadJSContextStack->Peek(&cx);
1500 if (cx) {
1501 nsIScriptGlobalObject *sgo = nsJSUtils::GetDynamicScriptGlobal(cx);
1502 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(sgo));
1504 if (win) {
1505 return win->GetDocShell();
1509 return nsnull;
1512 nsPIDOMWindow *
1513 nsContentUtils::GetWindowFromCaller()
1515 JSContext *cx = nsnull;
1516 sThreadJSContextStack->Peek(&cx);
1518 if (cx) {
1519 nsCOMPtr<nsPIDOMWindow> win =
1520 do_QueryInterface(nsJSUtils::GetDynamicScriptGlobal(cx));
1521 return win;
1524 return nsnull;
1527 nsIDOMDocument *
1528 nsContentUtils::GetDocumentFromCaller()
1530 JSContext *cx = nsnull;
1531 JSObject *obj = nsnull;
1532 sXPConnect->GetCaller(&cx, &obj);
1533 NS_ASSERTION(cx && obj, "Caller ensures something is running");
1535 nsCOMPtr<nsPIDOMWindow> win =
1536 do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(cx, obj));
1537 if (!win) {
1538 return nsnull;
1541 return win->GetExtantDocument();
1544 nsIDOMDocument *
1545 nsContentUtils::GetDocumentFromContext()
1547 JSContext *cx = nsnull;
1548 sThreadJSContextStack->Peek(&cx);
1550 if (cx) {
1551 nsIScriptGlobalObject *sgo = nsJSUtils::GetDynamicScriptGlobal(cx);
1553 if (sgo) {
1554 nsCOMPtr<nsPIDOMWindow> pwin = do_QueryInterface(sgo);
1555 if (pwin) {
1556 return pwin->GetExtantDocument();
1561 return nsnull;
1564 PRBool
1565 nsContentUtils::IsCallerChrome()
1567 PRBool is_caller_chrome = PR_FALSE;
1568 nsresult rv = sSecurityManager->SubjectPrincipalIsSystem(&is_caller_chrome);
1569 if (NS_FAILED(rv)) {
1570 return PR_FALSE;
1573 return is_caller_chrome;
1576 PRBool
1577 nsContentUtils::IsCallerTrustedForRead()
1579 return IsCallerTrustedForCapability("UniversalBrowserRead");
1582 PRBool
1583 nsContentUtils::IsCallerTrustedForWrite()
1585 return IsCallerTrustedForCapability("UniversalBrowserWrite");
1588 // static
1589 nsINode*
1590 nsContentUtils::GetCrossDocParentNode(nsINode* aChild)
1592 NS_PRECONDITION(aChild, "The child is null!");
1594 nsINode* parent = aChild->GetNodeParent();
1595 if (parent || !aChild->IsNodeOfType(nsINode::eDOCUMENT))
1596 return parent;
1598 nsIDocument* doc = static_cast<nsIDocument*>(aChild);
1599 nsIDocument* parentDoc = doc->GetParentDocument();
1600 return parentDoc ? parentDoc->FindContentForSubDocument(doc) : nsnull;
1603 // static
1604 PRBool
1605 nsContentUtils::ContentIsDescendantOf(const nsINode* aPossibleDescendant,
1606 const nsINode* aPossibleAncestor)
1608 NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
1609 NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
1611 do {
1612 if (aPossibleDescendant == aPossibleAncestor)
1613 return PR_TRUE;
1614 aPossibleDescendant = aPossibleDescendant->GetNodeParent();
1615 } while (aPossibleDescendant);
1617 return PR_FALSE;
1620 // static
1621 PRBool
1622 nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
1623 nsINode* aPossibleAncestor)
1625 NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
1626 NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
1628 do {
1629 if (aPossibleDescendant == aPossibleAncestor)
1630 return PR_TRUE;
1631 aPossibleDescendant = GetCrossDocParentNode(aPossibleDescendant);
1632 } while (aPossibleDescendant);
1634 return PR_FALSE;
1638 // static
1639 nsresult
1640 nsContentUtils::GetAncestors(nsINode* aNode,
1641 nsTArray<nsINode*>& aArray)
1643 while (aNode) {
1644 aArray.AppendElement(aNode);
1645 aNode = aNode->GetNodeParent();
1647 return NS_OK;
1650 // static
1651 nsresult
1652 nsContentUtils::GetAncestorsAndOffsets(nsIDOMNode* aNode,
1653 PRInt32 aOffset,
1654 nsTArray<nsIContent*>* aAncestorNodes,
1655 nsTArray<PRInt32>* aAncestorOffsets)
1657 NS_ENSURE_ARG_POINTER(aNode);
1659 nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
1661 if (!content) {
1662 return NS_ERROR_FAILURE;
1665 if (!aAncestorNodes->IsEmpty()) {
1666 NS_WARNING("aAncestorNodes is not empty");
1667 aAncestorNodes->Clear();
1670 if (!aAncestorOffsets->IsEmpty()) {
1671 NS_WARNING("aAncestorOffsets is not empty");
1672 aAncestorOffsets->Clear();
1675 // insert the node itself
1676 aAncestorNodes->AppendElement(content.get());
1677 aAncestorOffsets->AppendElement(aOffset);
1679 // insert all the ancestors
1680 nsIContent* child = content;
1681 nsIContent* parent = child->GetParent();
1682 while (parent) {
1683 aAncestorNodes->AppendElement(parent);
1684 aAncestorOffsets->AppendElement(parent->IndexOf(child));
1685 child = parent;
1686 parent = parent->GetParent();
1689 return NS_OK;
1692 // static
1693 nsresult
1694 nsContentUtils::GetCommonAncestor(nsIDOMNode *aNode,
1695 nsIDOMNode *aOther,
1696 nsIDOMNode** aCommonAncestor)
1698 *aCommonAncestor = nsnull;
1700 nsCOMPtr<nsINode> node1 = do_QueryInterface(aNode);
1701 nsCOMPtr<nsINode> node2 = do_QueryInterface(aOther);
1703 NS_ENSURE_TRUE(node1 && node2, NS_ERROR_UNEXPECTED);
1705 nsINode* common = GetCommonAncestor(node1, node2);
1706 NS_ENSURE_TRUE(common, NS_ERROR_NOT_AVAILABLE);
1708 return CallQueryInterface(common, aCommonAncestor);
1711 // static
1712 nsINode*
1713 nsContentUtils::GetCommonAncestor(nsINode* aNode1,
1714 nsINode* aNode2)
1716 if (aNode1 == aNode2) {
1717 return aNode1;
1720 // Build the chain of parents
1721 nsAutoTPtrArray<nsINode, 30> parents1, parents2;
1722 do {
1723 parents1.AppendElement(aNode1);
1724 aNode1 = aNode1->GetNodeParent();
1725 } while (aNode1);
1726 do {
1727 parents2.AppendElement(aNode2);
1728 aNode2 = aNode2->GetNodeParent();
1729 } while (aNode2);
1731 // Find where the parent chain differs
1732 PRUint32 pos1 = parents1.Length();
1733 PRUint32 pos2 = parents2.Length();
1734 nsINode* parent = nsnull;
1735 PRUint32 len;
1736 for (len = NS_MIN(pos1, pos2); len > 0; --len) {
1737 nsINode* child1 = parents1.ElementAt(--pos1);
1738 nsINode* child2 = parents2.ElementAt(--pos2);
1739 if (child1 != child2) {
1740 break;
1742 parent = child1;
1745 return parent;
1748 /* static */
1749 PRInt32
1750 nsContentUtils::ComparePoints(nsINode* aParent1, PRInt32 aOffset1,
1751 nsINode* aParent2, PRInt32 aOffset2,
1752 PRBool* aDisconnected)
1754 if (aParent1 == aParent2) {
1755 return aOffset1 < aOffset2 ? -1 :
1756 aOffset1 > aOffset2 ? 1 :
1760 nsAutoTArray<nsINode*, 32> parents1, parents2;
1761 nsINode* node1 = aParent1;
1762 nsINode* node2 = aParent2;
1763 do {
1764 parents1.AppendElement(node1);
1765 node1 = node1->GetNodeParent();
1766 } while (node1);
1767 do {
1768 parents2.AppendElement(node2);
1769 node2 = node2->GetNodeParent();
1770 } while (node2);
1772 PRUint32 pos1 = parents1.Length() - 1;
1773 PRUint32 pos2 = parents2.Length() - 1;
1775 PRBool disconnected = parents1.ElementAt(pos1) != parents2.ElementAt(pos2);
1776 if (aDisconnected) {
1777 *aDisconnected = disconnected;
1779 if (disconnected) {
1780 NS_ASSERTION(aDisconnected, "unexpected disconnected nodes");
1781 return 1;
1784 // Find where the parent chains differ
1785 nsINode* parent = parents1.ElementAt(pos1);
1786 PRUint32 len;
1787 for (len = NS_MIN(pos1, pos2); len > 0; --len) {
1788 nsINode* child1 = parents1.ElementAt(--pos1);
1789 nsINode* child2 = parents2.ElementAt(--pos2);
1790 if (child1 != child2) {
1791 return parent->IndexOf(child1) < parent->IndexOf(child2) ? -1 : 1;
1793 parent = child1;
1797 // The parent chains never differed, so one of the nodes is an ancestor of
1798 // the other
1800 NS_ASSERTION(!pos1 || !pos2,
1801 "should have run out of parent chain for one of the nodes");
1803 if (!pos1) {
1804 nsINode* child2 = parents2.ElementAt(--pos2);
1805 return aOffset1 <= parent->IndexOf(child2) ? -1 : 1;
1808 nsINode* child1 = parents1.ElementAt(--pos1);
1809 return parent->IndexOf(child1) < aOffset2 ? -1 : 1;
1812 nsIContent*
1813 nsContentUtils::FindFirstChildWithResolvedTag(nsIContent* aParent,
1814 PRInt32 aNamespace,
1815 nsIAtom* aTag)
1817 nsIDocument* doc;
1818 if (!aParent || !(doc = aParent->GetOwnerDoc())) {
1819 return nsnull;
1822 nsBindingManager* bindingManager = doc->BindingManager();
1824 PRInt32 namespaceID;
1825 PRUint32 count = aParent->GetChildCount();
1827 PRUint32 i;
1829 for (i = 0; i < count; i++) {
1830 nsIContent *child = aParent->GetChildAt(i);
1831 nsIAtom* tag = bindingManager->ResolveTag(child, &namespaceID);
1832 if (tag == aTag && namespaceID == aNamespace) {
1833 return child;
1837 // now look for children in XBL
1838 nsCOMPtr<nsIDOMNodeList> children;
1839 bindingManager->GetXBLChildNodesFor(aParent, getter_AddRefs(children));
1840 if (!children) {
1841 return nsnull;
1844 PRUint32 length;
1845 children->GetLength(&length);
1846 for (i = 0; i < length; i++) {
1847 nsCOMPtr<nsIDOMNode> childNode;
1848 children->Item(i, getter_AddRefs(childNode));
1849 nsCOMPtr<nsIContent> childContent = do_QueryInterface(childNode);
1850 nsIAtom* tag = bindingManager->ResolveTag(childContent, &namespaceID);
1851 if (tag == aTag && namespaceID == aNamespace) {
1852 return childContent;
1856 return nsnull;
1859 inline PRBool
1860 IsCharInSet(const char* aSet,
1861 const PRUnichar aChar)
1863 PRUnichar ch;
1864 while ((ch = *aSet)) {
1865 if (aChar == PRUnichar(ch)) {
1866 return PR_TRUE;
1868 ++aSet;
1870 return PR_FALSE;
1874 * This method strips leading/trailing chars, in given set, from string.
1877 // static
1878 const nsDependentSubstring
1879 nsContentUtils::TrimCharsInSet(const char* aSet,
1880 const nsAString& aValue)
1882 nsAString::const_iterator valueCurrent, valueEnd;
1884 aValue.BeginReading(valueCurrent);
1885 aValue.EndReading(valueEnd);
1887 // Skip characters in the beginning
1888 while (valueCurrent != valueEnd) {
1889 if (!IsCharInSet(aSet, *valueCurrent)) {
1890 break;
1892 ++valueCurrent;
1895 if (valueCurrent != valueEnd) {
1896 for (;;) {
1897 --valueEnd;
1898 if (!IsCharInSet(aSet, *valueEnd)) {
1899 break;
1902 ++valueEnd; // Step beyond the last character we want in the value.
1905 // valueEnd should point to the char after the last to copy
1906 return Substring(valueCurrent, valueEnd);
1910 * This method strips leading and trailing whitespace from a string.
1913 // static
1914 template<PRBool IsWhitespace(PRUnichar)>
1915 const nsDependentSubstring
1916 nsContentUtils::TrimWhitespace(const nsAString& aStr, PRBool aTrimTrailing)
1918 nsAString::const_iterator start, end;
1920 aStr.BeginReading(start);
1921 aStr.EndReading(end);
1923 // Skip whitespace characters in the beginning
1924 while (start != end && IsWhitespace(*start)) {
1925 ++start;
1928 if (aTrimTrailing) {
1929 // Skip whitespace characters in the end.
1930 while (end != start) {
1931 --end;
1933 if (!IsWhitespace(*end)) {
1934 // Step back to the last non-whitespace character.
1935 ++end;
1937 break;
1942 // Return a substring for the string w/o leading and/or trailing
1943 // whitespace
1945 return Substring(start, end);
1948 // Declaring the templates we are going to use avoid linking issues without
1949 // inlining the method. Considering there is not so much spaces checking
1950 // methods we can consider this to be better than inlining.
1951 template
1952 const nsDependentSubstring
1953 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, PRBool);
1954 template
1955 const nsDependentSubstring
1956 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(const nsAString&, PRBool);
1958 static inline void KeyAppendSep(nsACString& aKey)
1960 if (!aKey.IsEmpty()) {
1961 aKey.Append('>');
1965 static inline void KeyAppendString(const nsAString& aString, nsACString& aKey)
1967 KeyAppendSep(aKey);
1969 // Could escape separator here if collisions happen. > is not a legal char
1970 // for a name or type attribute, so we should be safe avoiding that extra work.
1972 AppendUTF16toUTF8(aString, aKey);
1975 static inline void KeyAppendString(const nsACString& aString, nsACString& aKey)
1977 KeyAppendSep(aKey);
1979 // Could escape separator here if collisions happen. > is not a legal char
1980 // for a name or type attribute, so we should be safe avoiding that extra work.
1982 aKey.Append(aString);
1985 static inline void KeyAppendInt(PRInt32 aInt, nsACString& aKey)
1987 KeyAppendSep(aKey);
1989 aKey.Append(nsPrintfCString("%d", aInt));
1992 static inline void KeyAppendAtom(nsIAtom* aAtom, nsACString& aKey)
1994 NS_PRECONDITION(aAtom, "KeyAppendAtom: aAtom can not be null!\n");
1996 KeyAppendString(nsAtomCString(aAtom), aKey);
1999 static inline PRBool IsAutocompleteOff(const nsIContent* aElement)
2001 return aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocomplete,
2002 NS_LITERAL_STRING("off"), eIgnoreCase);
2005 /*static*/ nsresult
2006 nsContentUtils::GenerateStateKey(nsIContent* aContent,
2007 const nsIDocument* aDocument,
2008 nsIStatefulFrame::SpecialStateID aID,
2009 nsACString& aKey)
2011 aKey.Truncate();
2013 PRUint32 partID = aDocument ? aDocument->GetPartID() : 0;
2015 // SpecialStateID case - e.g. scrollbars around the content window
2016 // The key in this case is a special state id
2017 if (nsIStatefulFrame::eNoID != aID) {
2018 KeyAppendInt(partID, aKey); // first append a partID
2019 KeyAppendInt(aID, aKey);
2020 return NS_OK;
2023 // We must have content if we're not using a special state id
2024 NS_ENSURE_TRUE(aContent, NS_ERROR_FAILURE);
2026 // Don't capture state for anonymous content
2027 if (aContent->IsInAnonymousSubtree()) {
2028 return NS_OK;
2031 if (IsAutocompleteOff(aContent)) {
2032 return NS_OK;
2035 nsCOMPtr<nsIHTMLDocument> htmlDocument(do_QueryInterface(aContent->GetCurrentDoc()));
2037 KeyAppendInt(partID, aKey); // first append a partID
2038 // Make sure we can't possibly collide with an nsIStatefulFrame
2039 // special id of some sort
2040 KeyAppendInt(nsIStatefulFrame::eNoID, aKey);
2041 PRBool generatedUniqueKey = PR_FALSE;
2043 if (htmlDocument) {
2044 // Flush our content model so it'll be up to date
2045 // If this becomes unnecessary and the following line is removed,
2046 // please also remove the corresponding flush operation from
2047 // nsHtml5TreeBuilderCppSupplement.h. (Look for "See bug 497861." there.)
2048 aContent->GetCurrentDoc()->FlushPendingNotifications(Flush_Content);
2050 nsContentList *htmlForms = htmlDocument->GetForms();
2051 nsContentList *htmlFormControls = htmlDocument->GetFormControls();
2053 NS_ENSURE_TRUE(htmlForms && htmlFormControls, NS_ERROR_OUT_OF_MEMORY);
2055 // If we have a form control and can calculate form information, use that
2056 // as the key - it is more reliable than just recording position in the
2057 // DOM.
2058 // XXXbz Is it, really? We have bugs on this, I think...
2059 // Important to have a unique key, and tag/type/name may not be.
2061 // If the control has a form, the format of the key is:
2062 // f>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
2063 // else:
2064 // d>type>IndOfControlInDoc>name
2066 // XXX We don't need to use index if name is there
2067 // XXXbz We don't? Why not? I don't follow.
2069 nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
2070 if (control && htmlFormControls && htmlForms) {
2072 // Append the control type
2073 KeyAppendInt(control->GetType(), aKey);
2075 // If in a form, add form name / index of form / index in form
2076 PRInt32 index = -1;
2077 Element *formElement = control->GetFormElement();
2078 if (formElement) {
2079 if (IsAutocompleteOff(formElement)) {
2080 aKey.Truncate();
2081 return NS_OK;
2084 KeyAppendString(NS_LITERAL_CSTRING("f"), aKey);
2086 // Append the index of the form in the document
2087 index = htmlForms->IndexOf(formElement, PR_FALSE);
2088 if (index <= -1) {
2090 // XXX HACK this uses some state that was dumped into the document
2091 // specifically to fix bug 138892. What we are trying to do is *guess*
2092 // which form this control's state is found in, with the highly likely
2093 // guess that the highest form parsed so far is the one.
2094 // This code should not be on trunk, only branch.
2096 index = htmlDocument->GetNumFormsSynchronous() - 1;
2098 if (index > -1) {
2099 KeyAppendInt(index, aKey);
2101 // Append the index of the control in the form
2102 nsCOMPtr<nsIForm> form(do_QueryInterface(formElement));
2103 index = form->IndexOfControl(control);
2105 if (index > -1) {
2106 KeyAppendInt(index, aKey);
2107 generatedUniqueKey = PR_TRUE;
2111 // Append the form name
2112 nsAutoString formName;
2113 formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
2114 KeyAppendString(formName, aKey);
2116 } else {
2118 KeyAppendString(NS_LITERAL_CSTRING("d"), aKey);
2120 // If not in a form, add index of control in document
2121 // Less desirable than indexing by form info.
2123 // Hash by index of control in doc (we are not in a form)
2124 // These are important as they are unique, and type/name may not be.
2126 // We have to flush sink notifications at this point to make
2127 // sure that htmlFormControls is up to date.
2128 index = htmlFormControls->IndexOf(aContent, PR_TRUE);
2129 if (index > -1) {
2130 KeyAppendInt(index, aKey);
2131 generatedUniqueKey = PR_TRUE;
2135 // Append the control name
2136 nsAutoString name;
2137 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
2138 KeyAppendString(name, aKey);
2142 if (!generatedUniqueKey) {
2143 // Either we didn't have a form control or we aren't in an HTML document so
2144 // we can't figure out form info. First append a character that is not "d"
2145 // or "f" to disambiguate from the case when we were a form control in an
2146 // HTML document.
2147 KeyAppendString(NS_LITERAL_CSTRING("o"), aKey);
2149 // Now start at aContent and append the indices of it and all its ancestors
2150 // in their containers. That should at least pin down its position in the
2151 // DOM...
2152 nsINode* parent = aContent->GetNodeParent();
2153 nsINode* content = aContent;
2154 while (parent) {
2155 KeyAppendInt(parent->IndexOf(content), aKey);
2156 content = parent;
2157 parent = content->GetNodeParent();
2161 return NS_OK;
2164 // static
2165 nsresult
2166 nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
2167 const nsAString& aSpec,
2168 nsIDocument* aDocument,
2169 nsIURI* aBaseURI)
2171 return NS_NewURI(aResult, aSpec,
2172 aDocument ? aDocument->GetDocumentCharacterSet().get() : nsnull,
2173 aBaseURI, sIOService);
2176 // static
2177 PRBool
2178 nsContentUtils::BelongsInForm(nsIDOMHTMLFormElement *aForm,
2179 nsIContent *aContent)
2181 NS_PRECONDITION(aForm, "Must have a form");
2182 NS_PRECONDITION(aContent, "Must have a content node");
2184 nsCOMPtr<nsIContent> form(do_QueryInterface(aForm));
2186 if (!form) {
2187 NS_ERROR("This should not happen, form is not an nsIContent!");
2189 return PR_TRUE;
2192 if (form == aContent) {
2193 // A form does not belong inside itself, so we return false here
2195 return PR_FALSE;
2198 nsIContent* content = aContent->GetParent();
2200 while (content) {
2201 if (content == form) {
2202 // aContent is contained within the form so we return true.
2204 return PR_TRUE;
2207 if (content->Tag() == nsGkAtoms::form &&
2208 content->IsHTML()) {
2209 // The child is contained within a form, but not the right form
2210 // so we ignore it.
2212 return PR_FALSE;
2215 content = content->GetParent();
2218 if (form->GetChildCount() > 0) {
2219 // The form is a container but aContent wasn't inside the form,
2220 // return false
2222 return PR_FALSE;
2225 // The form is a leaf and aContent wasn't inside any other form so
2226 // we check whether the content comes after the form. If it does,
2227 // return true. If it does not, then it couldn't have been inside
2228 // the form in the HTML.
2229 if (PositionIsBefore(form, aContent)) {
2230 // We could be in this form!
2231 // In the future, we may want to get document.forms, look at the
2232 // form after aForm, and if aContent is after that form after
2233 // aForm return false here....
2234 return PR_TRUE;
2237 return PR_FALSE;
2240 // static
2241 nsresult
2242 nsContentUtils::CheckQName(const nsAString& aQualifiedName,
2243 PRBool aNamespaceAware)
2245 nsIParserService *parserService = GetParserService();
2246 NS_ENSURE_TRUE(parserService, NS_ERROR_FAILURE);
2248 const PRUnichar *colon;
2249 return parserService->CheckQName(PromiseFlatString(aQualifiedName),
2250 aNamespaceAware, &colon);
2253 //static
2254 nsresult
2255 nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
2256 const nsAFlatString& aQName,
2257 PRInt32 *aNamespace, nsIAtom **aLocalName)
2259 nsIParserService* parserService = GetParserService();
2260 NS_ENSURE_TRUE(parserService, NS_ERROR_FAILURE);
2262 const PRUnichar* colon;
2263 nsresult rv = parserService->CheckQName(aQName, PR_TRUE, &colon);
2264 NS_ENSURE_SUCCESS(rv, rv);
2266 if (colon) {
2267 const PRUnichar* end;
2268 aQName.EndReading(end);
2269 nsAutoString nameSpace;
2270 rv = aNamespaceResolver->LookupNamespaceURI(Substring(aQName.get(), colon),
2271 nameSpace);
2272 NS_ENSURE_SUCCESS(rv, rv);
2274 *aNamespace = NameSpaceManager()->GetNameSpaceID(nameSpace);
2275 if (*aNamespace == kNameSpaceID_Unknown)
2276 return NS_ERROR_FAILURE;
2278 *aLocalName = NS_NewAtom(Substring(colon + 1, end));
2280 else {
2281 *aNamespace = kNameSpaceID_None;
2282 *aLocalName = NS_NewAtom(aQName);
2284 NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
2285 return NS_OK;
2288 // static
2289 nsresult
2290 nsContentUtils::GetNodeInfoFromQName(const nsAString& aNamespaceURI,
2291 const nsAString& aQualifiedName,
2292 nsNodeInfoManager* aNodeInfoManager,
2293 nsINodeInfo** aNodeInfo)
2295 nsIParserService* parserService = GetParserService();
2296 NS_ENSURE_TRUE(parserService, NS_ERROR_FAILURE);
2298 const nsAFlatString& qName = PromiseFlatString(aQualifiedName);
2299 const PRUnichar* colon;
2300 nsresult rv = parserService->CheckQName(qName, PR_TRUE, &colon);
2301 NS_ENSURE_SUCCESS(rv, rv);
2303 PRInt32 nsID;
2304 sNameSpaceManager->RegisterNameSpace(aNamespaceURI, nsID);
2305 if (colon) {
2306 const PRUnichar* end;
2307 qName.EndReading(end);
2309 nsCOMPtr<nsIAtom> prefix = do_GetAtom(Substring(qName.get(), colon));
2311 rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix,
2312 nsID, aNodeInfo);
2314 else {
2315 rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nsnull, nsID,
2316 aNodeInfo);
2318 NS_ENSURE_SUCCESS(rv, rv);
2320 return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
2321 (*aNodeInfo)->GetPrefixAtom(),
2322 (*aNodeInfo)->NamespaceID()) ?
2323 NS_OK : NS_ERROR_DOM_NAMESPACE_ERR;
2326 // static
2327 void
2328 nsContentUtils::SplitExpatName(const PRUnichar *aExpatName, nsIAtom **aPrefix,
2329 nsIAtom **aLocalName, PRInt32* aNameSpaceID)
2332 * Expat can send the following:
2333 * localName
2334 * namespaceURI<separator>localName
2335 * namespaceURI<separator>localName<separator>prefix
2337 * and we use 0xFFFF for the <separator>.
2341 const PRUnichar *uriEnd = nsnull;
2342 const PRUnichar *nameEnd = nsnull;
2343 const PRUnichar *pos;
2344 for (pos = aExpatName; *pos; ++pos) {
2345 if (*pos == 0xFFFF) {
2346 if (uriEnd) {
2347 nameEnd = pos;
2349 else {
2350 uriEnd = pos;
2355 const PRUnichar *nameStart;
2356 if (uriEnd) {
2357 if (sNameSpaceManager) {
2358 sNameSpaceManager->RegisterNameSpace(nsDependentSubstring(aExpatName,
2359 uriEnd),
2360 *aNameSpaceID);
2362 else {
2363 *aNameSpaceID = kNameSpaceID_Unknown;
2366 nameStart = (uriEnd + 1);
2367 if (nameEnd) {
2368 const PRUnichar *prefixStart = nameEnd + 1;
2369 *aPrefix = NS_NewAtom(Substring(prefixStart, pos));
2371 else {
2372 nameEnd = pos;
2373 *aPrefix = nsnull;
2376 else {
2377 *aNameSpaceID = kNameSpaceID_None;
2378 nameStart = aExpatName;
2379 nameEnd = pos;
2380 *aPrefix = nsnull;
2382 *aLocalName = NS_NewAtom(Substring(nameStart, nameEnd));
2385 // static
2386 nsPresContext*
2387 nsContentUtils::GetContextForContent(const nsIContent* aContent)
2389 nsIDocument* doc = aContent->GetCurrentDoc();
2390 if (doc) {
2391 nsIPresShell *presShell = doc->GetShell();
2392 if (presShell) {
2393 return presShell->GetPresContext();
2396 return nsnull;
2399 // static
2400 PRBool
2401 nsContentUtils::CanLoadImage(nsIURI* aURI, nsISupports* aContext,
2402 nsIDocument* aLoadingDocument,
2403 nsIPrincipal* aLoadingPrincipal,
2404 PRInt16* aImageBlockingStatus)
2406 NS_PRECONDITION(aURI, "Must have a URI");
2407 NS_PRECONDITION(aLoadingDocument, "Must have a document");
2408 NS_PRECONDITION(aLoadingPrincipal, "Must have a loading principal");
2410 nsresult rv;
2412 PRUint32 appType = nsIDocShell::APP_TYPE_UNKNOWN;
2415 nsCOMPtr<nsISupports> container = aLoadingDocument->GetContainer();
2416 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
2417 do_QueryInterface(container);
2419 if (docShellTreeItem) {
2420 nsCOMPtr<nsIDocShellTreeItem> root;
2421 docShellTreeItem->GetRootTreeItem(getter_AddRefs(root));
2423 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
2425 if (!docShell || NS_FAILED(docShell->GetAppType(&appType))) {
2426 appType = nsIDocShell::APP_TYPE_UNKNOWN;
2431 if (appType != nsIDocShell::APP_TYPE_EDITOR) {
2432 // Editor apps get special treatment here, editors can load images
2433 // from anywhere. This allows editor to insert images from file://
2434 // into documents that are being edited.
2435 rv = sSecurityManager->
2436 CheckLoadURIWithPrincipal(aLoadingPrincipal, aURI,
2437 nsIScriptSecurityManager::ALLOW_CHROME);
2438 if (NS_FAILED(rv)) {
2439 if (aImageBlockingStatus) {
2440 // Reject the request itself, not all requests to the relevant
2441 // server...
2442 *aImageBlockingStatus = nsIContentPolicy::REJECT_REQUEST;
2444 return PR_FALSE;
2448 PRInt16 decision = nsIContentPolicy::ACCEPT;
2450 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_IMAGE,
2451 aURI,
2452 aLoadingPrincipal,
2453 aContext,
2454 EmptyCString(), //mime guess
2455 nsnull, //extra
2456 &decision,
2457 GetContentPolicy(),
2458 sSecurityManager);
2460 if (aImageBlockingStatus) {
2461 *aImageBlockingStatus =
2462 NS_FAILED(rv) ? nsIContentPolicy::REJECT_REQUEST : decision;
2464 return NS_FAILED(rv) ? PR_FALSE : NS_CP_ACCEPTED(decision);
2467 // static
2468 PRBool
2469 nsContentUtils::IsImageInCache(nsIURI* aURI)
2471 if (!sImgLoaderInitialized)
2472 InitImgLoader();
2474 if (!sImgCache) return PR_FALSE;
2476 // If something unexpected happened we return false, otherwise if props
2477 // is set, the image is cached and we return true
2478 nsCOMPtr<nsIProperties> props;
2479 nsresult rv = sImgCache->FindEntryProperties(aURI, getter_AddRefs(props));
2480 return (NS_SUCCEEDED(rv) && props);
2483 // static
2484 nsresult
2485 nsContentUtils::LoadImage(nsIURI* aURI, nsIDocument* aLoadingDocument,
2486 nsIPrincipal* aLoadingPrincipal, nsIURI* aReferrer,
2487 imgIDecoderObserver* aObserver, PRInt32 aLoadFlags,
2488 imgIRequest** aRequest)
2490 NS_PRECONDITION(aURI, "Must have a URI");
2491 NS_PRECONDITION(aLoadingDocument, "Must have a document");
2492 NS_PRECONDITION(aLoadingPrincipal, "Must have a principal");
2493 NS_PRECONDITION(aRequest, "Null out param");
2495 imgILoader* imgLoader = GetImgLoader();
2496 if (!imgLoader) {
2497 // nothing we can do here
2498 return NS_OK;
2501 nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
2502 NS_ASSERTION(loadGroup, "Could not get loadgroup; onload may fire too early");
2504 nsIURI *documentURI = aLoadingDocument->GetDocumentURI();
2506 // check for a Content Security Policy to pass down to the channel that
2507 // will get created to load the image
2508 nsCOMPtr<nsIChannelPolicy> channelPolicy;
2509 nsCOMPtr<nsIContentSecurityPolicy> csp;
2510 if (aLoadingPrincipal) {
2511 nsresult rv = aLoadingPrincipal->GetCsp(getter_AddRefs(csp));
2512 NS_ENSURE_SUCCESS(rv, rv);
2513 if (csp) {
2514 channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
2515 channelPolicy->SetContentSecurityPolicy(csp);
2516 channelPolicy->SetLoadType(nsIContentPolicy::TYPE_IMAGE);
2520 // Make the URI immutable so people won't change it under us
2521 NS_TryToSetImmutable(aURI);
2523 // XXXbz using "documentURI" for the initialDocumentURI is not quite
2524 // right, but the best we can do here...
2525 return imgLoader->LoadImage(aURI, /* uri to load */
2526 documentURI, /* initialDocumentURI */
2527 aReferrer, /* referrer */
2528 loadGroup, /* loadgroup */
2529 aObserver, /* imgIDecoderObserver */
2530 aLoadingDocument, /* uniquification key */
2531 aLoadFlags, /* load flags */
2532 nsnull, /* cache key */
2533 nsnull, /* existing request*/
2534 channelPolicy, /* CSP info */
2535 aRequest);
2538 // static
2539 already_AddRefed<imgIContainer>
2540 nsContentUtils::GetImageFromContent(nsIImageLoadingContent* aContent,
2541 imgIRequest **aRequest)
2543 if (aRequest) {
2544 *aRequest = nsnull;
2547 NS_ENSURE_TRUE(aContent, nsnull);
2549 nsCOMPtr<imgIRequest> imgRequest;
2550 aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
2551 getter_AddRefs(imgRequest));
2552 if (!imgRequest) {
2553 return nsnull;
2556 nsCOMPtr<imgIContainer> imgContainer;
2557 imgRequest->GetImage(getter_AddRefs(imgContainer));
2559 if (!imgContainer) {
2560 return nsnull;
2563 if (aRequest) {
2564 imgRequest.swap(*aRequest);
2567 return imgContainer.forget();
2570 //static
2571 already_AddRefed<imgIRequest>
2572 nsContentUtils::GetStaticRequest(imgIRequest* aRequest)
2574 NS_ENSURE_TRUE(aRequest, nsnull);
2575 nsCOMPtr<imgIRequest> retval;
2576 aRequest->GetStaticRequest(getter_AddRefs(retval));
2577 return retval.forget();
2580 // static
2581 PRBool
2582 nsContentUtils::ContentIsDraggable(nsIContent* aContent)
2584 nsCOMPtr<nsIDOMNSHTMLElement> htmlElement = do_QueryInterface(aContent);
2585 if (htmlElement) {
2586 PRBool draggable = PR_FALSE;
2587 htmlElement->GetDraggable(&draggable);
2588 if (draggable)
2589 return PR_TRUE;
2591 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
2592 nsGkAtoms::_false, eIgnoreCase))
2593 return PR_FALSE;
2596 // special handling for content area image and link dragging
2597 return IsDraggableImage(aContent) || IsDraggableLink(aContent);
2600 // static
2601 PRBool
2602 nsContentUtils::IsDraggableImage(nsIContent* aContent)
2604 NS_PRECONDITION(aContent, "Must have content node to test");
2606 nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(aContent));
2607 if (!imageContent) {
2608 return PR_FALSE;
2611 nsCOMPtr<imgIRequest> imgRequest;
2612 imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
2613 getter_AddRefs(imgRequest));
2615 // XXXbz It may be draggable even if the request resulted in an error. Why?
2616 // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
2617 return imgRequest != nsnull;
2620 // static
2621 PRBool
2622 nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
2623 nsCOMPtr<nsIURI> absURI;
2624 return aContent->IsLink(getter_AddRefs(absURI));
2627 // static
2628 nsAdoptingCString
2629 nsContentUtils::GetCharPref(const char *aPref)
2631 nsAdoptingCString result;
2633 if (sPrefBranch) {
2634 sPrefBranch->GetCharPref(aPref, getter_Copies(result));
2637 return result;
2640 // static
2641 PRPackedBool
2642 nsContentUtils::GetBoolPref(const char *aPref, PRBool aDefault)
2644 PRBool result;
2646 if (!sPrefBranch ||
2647 NS_FAILED(sPrefBranch->GetBoolPref(aPref, &result))) {
2648 result = aDefault;
2651 return (PRPackedBool)result;
2654 // static
2655 PRInt32
2656 nsContentUtils::GetIntPref(const char *aPref, PRInt32 aDefault)
2658 PRInt32 result;
2660 if (!sPrefBranch ||
2661 NS_FAILED(sPrefBranch->GetIntPref(aPref, &result))) {
2662 result = aDefault;
2665 return result;
2668 // static
2669 nsAdoptingString
2670 nsContentUtils::GetLocalizedStringPref(const char *aPref)
2672 nsAdoptingString result;
2674 if (sPrefBranch) {
2675 nsCOMPtr<nsIPrefLocalizedString> prefLocalString;
2676 sPrefBranch->GetComplexValue(aPref, NS_GET_IID(nsIPrefLocalizedString),
2677 getter_AddRefs(prefLocalString));
2678 if (prefLocalString) {
2679 prefLocalString->GetData(getter_Copies(result));
2683 return result;
2686 // static
2687 nsAdoptingString
2688 nsContentUtils::GetStringPref(const char *aPref)
2690 nsAdoptingString result;
2692 if (sPrefBranch) {
2693 nsCOMPtr<nsISupportsString> theString;
2694 sPrefBranch->GetComplexValue(aPref, NS_GET_IID(nsISupportsString),
2695 getter_AddRefs(theString));
2696 if (theString) {
2697 theString->ToString(getter_Copies(result));
2701 return result;
2704 // RegisterPrefCallback/UnregisterPrefCallback are for backward compatiblity
2705 // with c-style observers.
2707 // static
2708 void
2709 nsContentUtils::RegisterPrefCallback(const char *aPref,
2710 PrefChangedFunc aCallback,
2711 void * aClosure)
2713 if (sPrefBranch) {
2714 if (!sPrefCallbackTable) {
2715 sPrefCallbackTable =
2716 new nsRefPtrHashtable<nsPrefObserverHashKey, nsPrefOldCallback>();
2717 sPrefCallbackTable->Init();
2720 nsPrefObserverHashKey hashKey(aPref, aCallback);
2721 nsRefPtr<nsPrefOldCallback> callback;
2722 sPrefCallbackTable->Get(&hashKey, getter_AddRefs(callback));
2723 if (callback) {
2724 callback->AppendClosure(aClosure);
2725 return;
2728 callback = new nsPrefOldCallback(aPref, aCallback);
2729 callback->AppendClosure(aClosure);
2730 if (NS_SUCCEEDED(sPrefBranch->AddObserver(aPref, callback, PR_FALSE))) {
2731 sPrefCallbackTable->Put(callback, callback);
2736 // static
2737 void
2738 nsContentUtils::UnregisterPrefCallback(const char *aPref,
2739 PrefChangedFunc aCallback,
2740 void * aClosure)
2742 if (sPrefBranch) {
2743 if (!sPrefCallbackTable) {
2744 return;
2747 nsPrefObserverHashKey hashKey(aPref, aCallback);
2748 nsRefPtr<nsPrefOldCallback> callback;
2749 sPrefCallbackTable->Get(&hashKey, getter_AddRefs(callback));
2751 if (callback) {
2752 callback->RemoveClosure(aClosure);
2753 if (callback->HasNoClosures()) {
2754 // Delete the callback since its list of closures is empty.
2755 sPrefCallbackTable->Remove(callback);
2761 static int
2762 BoolVarChanged(const char *aPref, void *aClosure)
2764 PrefCacheData* cache = static_cast<PrefCacheData*>(aClosure);
2765 *((PRBool*)cache->cacheLocation) =
2766 nsContentUtils::GetBoolPref(aPref, cache->defaultValueBool);
2768 return 0;
2771 void
2772 nsContentUtils::AddBoolPrefVarCache(const char *aPref,
2773 PRBool* aCache,
2774 PRBool aDefault)
2776 *aCache = GetBoolPref(aPref, aDefault);
2777 PrefCacheData* data = new PrefCacheData;
2778 data->cacheLocation = aCache;
2779 data->defaultValueBool = aDefault;
2780 sPrefCacheData->AppendElement(data);
2781 RegisterPrefCallback(aPref, BoolVarChanged, data);
2784 static int
2785 IntVarChanged(const char *aPref, void *aClosure)
2787 PrefCacheData* cache = static_cast<PrefCacheData*>(aClosure);
2788 *((PRInt32*)cache->cacheLocation) =
2789 nsContentUtils::GetIntPref(aPref, cache->defaultValueInt);
2791 return 0;
2794 void
2795 nsContentUtils::AddIntPrefVarCache(const char *aPref,
2796 PRInt32* aCache,
2797 PRInt32 aDefault)
2799 *aCache = GetIntPref(aPref, aDefault);
2800 PrefCacheData* data = new PrefCacheData;
2801 data->cacheLocation = aCache;
2802 data->defaultValueInt = aDefault;
2803 sPrefCacheData->AppendElement(data);
2804 RegisterPrefCallback(aPref, IntVarChanged, data);
2807 PRBool
2808 nsContentUtils::IsSitePermAllow(nsIURI* aURI, const char* aType)
2810 nsCOMPtr<nsIPermissionManager> permMgr =
2811 do_GetService("@mozilla.org/permissionmanager;1");
2812 NS_ENSURE_TRUE(permMgr, PR_FALSE);
2814 PRUint32 perm;
2815 nsresult rv = permMgr->TestPermission(aURI, aType, &perm);
2816 NS_ENSURE_SUCCESS(rv, PR_FALSE);
2818 return perm == nsIPermissionManager::ALLOW_ACTION;
2821 static const char *gEventNames[] = {"event"};
2822 static const char *gSVGEventNames[] = {"evt"};
2823 // for b/w compat, the first name to onerror is still 'event', even though it
2824 // is actually the error message. (pre this code, the other 2 were not avail.)
2825 // XXXmarkh - a quick lxr shows no affected code - should we correct this?
2826 static const char *gOnErrorNames[] = {"event", "source", "lineno"};
2828 // static
2829 void
2830 nsContentUtils::GetEventArgNames(PRInt32 aNameSpaceID,
2831 nsIAtom *aEventName,
2832 PRUint32 *aArgCount,
2833 const char*** aArgArray)
2835 #define SET_EVENT_ARG_NAMES(names) \
2836 *aArgCount = sizeof(names)/sizeof(names[0]); \
2837 *aArgArray = names;
2839 // nsJSEventListener is what does the arg magic for onerror, and it does
2840 // not seem to take the namespace into account. So we let onerror in all
2841 // namespaces get the 3 arg names.
2842 if (aEventName == nsGkAtoms::onerror) {
2843 SET_EVENT_ARG_NAMES(gOnErrorNames);
2844 } else if (aNameSpaceID == kNameSpaceID_SVG) {
2845 SET_EVENT_ARG_NAMES(gSVGEventNames);
2846 } else {
2847 SET_EVENT_ARG_NAMES(gEventNames);
2851 nsCxPusher::nsCxPusher()
2852 : mScriptIsRunning(PR_FALSE),
2853 mPushedSomething(PR_FALSE)
2857 nsCxPusher::~nsCxPusher()
2859 Pop();
2862 static PRBool
2863 IsContextOnStack(nsIJSContextStack *aStack, JSContext *aContext)
2865 JSContext *ctx = nsnull;
2866 aStack->Peek(&ctx);
2867 if (!ctx)
2868 return PR_FALSE;
2869 if (ctx == aContext)
2870 return PR_TRUE;
2872 nsCOMPtr<nsIJSContextStackIterator>
2873 iterator(do_CreateInstance("@mozilla.org/js/xpc/ContextStackIterator;1"));
2874 NS_ENSURE_TRUE(iterator, PR_FALSE);
2876 nsresult rv = iterator->Reset(aStack);
2877 NS_ENSURE_SUCCESS(rv, PR_FALSE);
2879 PRBool done;
2880 while (NS_SUCCEEDED(iterator->Done(&done)) && !done) {
2881 rv = iterator->Prev(&ctx);
2882 NS_ASSERTION(NS_SUCCEEDED(rv), "Broken iterator implementation");
2884 if (!ctx) {
2885 continue;
2888 if (nsJSUtils::GetDynamicScriptContext(ctx) && ctx == aContext)
2889 return PR_TRUE;
2892 return PR_FALSE;
2895 PRBool
2896 nsCxPusher::Push(nsPIDOMEventTarget *aCurrentTarget)
2898 if (mPushedSomething) {
2899 NS_ERROR("Whaaa! No double pushing with nsCxPusher::Push()!");
2901 return PR_FALSE;
2904 NS_ENSURE_TRUE(aCurrentTarget, PR_FALSE);
2905 nsresult rv;
2906 nsIScriptContext* scx =
2907 aCurrentTarget->GetContextForEventHandlers(&rv);
2908 NS_ENSURE_SUCCESS(rv, PR_FALSE);
2910 if (!scx) {
2911 // The target may have a special JS context for event handlers.
2912 JSContext* cx = aCurrentTarget->GetJSContextForEventHandlers();
2913 if (cx) {
2914 DoPush(cx);
2917 // Nothing to do here, I guess. Have to return true so that event firing
2918 // will still work correctly even if there is no associated JSContext
2919 return PR_TRUE;
2922 JSContext* cx = nsnull;
2924 if (scx) {
2925 cx = static_cast<JSContext*>(scx->GetNativeContext());
2926 // Bad, no JSContext from script context!
2927 NS_ENSURE_TRUE(cx, PR_FALSE);
2930 // If there's no native context in the script context it must be
2931 // in the process or being torn down. We don't want to notify the
2932 // script context about scripts having been evaluated in such a
2933 // case, calling with a null cx is fine in that case.
2934 return Push(cx);
2937 PRBool
2938 nsCxPusher::RePush(nsPIDOMEventTarget *aCurrentTarget)
2940 if (!mPushedSomething) {
2941 return Push(aCurrentTarget);
2944 if (aCurrentTarget) {
2945 nsresult rv;
2946 nsIScriptContext* scx =
2947 aCurrentTarget->GetContextForEventHandlers(&rv);
2948 if (NS_FAILED(rv)) {
2949 Pop();
2950 return PR_FALSE;
2953 // If we have the same script context and native context is still
2954 // alive, no need to Pop/Push.
2955 if (scx && scx == mScx &&
2956 scx->GetNativeContext()) {
2957 return PR_TRUE;
2961 Pop();
2962 return Push(aCurrentTarget);
2965 PRBool
2966 nsCxPusher::Push(JSContext *cx, PRBool aRequiresScriptContext)
2968 if (mPushedSomething) {
2969 NS_ERROR("Whaaa! No double pushing with nsCxPusher::Push()!");
2971 return PR_FALSE;
2974 if (!cx) {
2975 return PR_FALSE;
2978 // Hold a strong ref to the nsIScriptContext, just in case
2979 // XXXbz do we really need to? If we don't get one of these in Pop(), is
2980 // that really a problem? Or do we need to do this to effectively root |cx|?
2981 mScx = GetScriptContextFromJSContext(cx);
2982 if (!mScx && aRequiresScriptContext) {
2983 // Should probably return PR_FALSE. See bug 416916.
2984 return PR_TRUE;
2987 return DoPush(cx);
2990 PRBool
2991 nsCxPusher::DoPush(JSContext* cx)
2993 nsIThreadJSContextStack* stack = nsContentUtils::ThreadJSContextStack();
2994 if (!stack) {
2995 return PR_TRUE;
2998 if (cx && IsContextOnStack(stack, cx)) {
2999 // If the context is on the stack, that means that a script
3000 // is running at the moment in the context.
3001 mScriptIsRunning = PR_TRUE;
3004 if (NS_FAILED(stack->Push(cx))) {
3005 mScriptIsRunning = PR_FALSE;
3006 mScx = nsnull;
3007 return PR_FALSE;
3010 mPushedSomething = PR_TRUE;
3011 #ifdef DEBUG
3012 mPushedContext = cx;
3013 #endif
3014 return PR_TRUE;
3017 PRBool
3018 nsCxPusher::PushNull()
3020 return DoPush(nsnull);
3023 void
3024 nsCxPusher::Pop()
3026 nsIThreadJSContextStack* stack = nsContentUtils::ThreadJSContextStack();
3027 if (!mPushedSomething || !stack) {
3028 mScx = nsnull;
3029 mPushedSomething = PR_FALSE;
3031 NS_ASSERTION(!mScriptIsRunning, "Huh, this can't be happening, "
3032 "mScriptIsRunning can't be set here!");
3034 return;
3037 JSContext *unused;
3038 stack->Pop(&unused);
3040 NS_ASSERTION(unused == mPushedContext, "Unexpected context popped");
3042 if (!mScriptIsRunning && mScx) {
3043 // No JS is running in the context, but executing the event handler might have
3044 // caused some JS to run. Tell the script context that it's done.
3046 mScx->ScriptEvaluated(PR_TRUE);
3049 mScx = nsnull;
3050 mScriptIsRunning = PR_FALSE;
3051 mPushedSomething = PR_FALSE;
3054 static const char gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT][56] = {
3055 // Must line up with the enum values in |PropertiesFile| enum.
3056 "chrome://global/locale/css.properties",
3057 "chrome://global/locale/xbl.properties",
3058 "chrome://global/locale/xul.properties",
3059 "chrome://global/locale/layout_errors.properties",
3060 "chrome://global/locale/layout/HtmlForm.properties",
3061 "chrome://global/locale/printing.properties",
3062 "chrome://global/locale/dom/dom.properties",
3063 #ifdef MOZ_SVG
3064 "chrome://global/locale/svg/svg.properties",
3065 #endif
3066 "chrome://branding/locale/brand.properties",
3067 "chrome://global/locale/commonDialogs.properties"
3070 /* static */ nsresult
3071 nsContentUtils::EnsureStringBundle(PropertiesFile aFile)
3073 if (!sStringBundles[aFile]) {
3074 if (!sStringBundleService) {
3075 nsresult rv =
3076 CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
3077 NS_ENSURE_SUCCESS(rv, rv);
3079 nsIStringBundle *bundle;
3080 nsresult rv =
3081 sStringBundleService->CreateBundle(gPropertiesFiles[aFile], &bundle);
3082 NS_ENSURE_SUCCESS(rv, rv);
3083 sStringBundles[aFile] = bundle; // transfer ownership
3085 return NS_OK;
3088 /* static */
3089 nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
3090 const char* aKey,
3091 nsXPIDLString& aResult)
3093 nsresult rv = EnsureStringBundle(aFile);
3094 NS_ENSURE_SUCCESS(rv, rv);
3095 nsIStringBundle *bundle = sStringBundles[aFile];
3097 return bundle->GetStringFromName(NS_ConvertASCIItoUTF16(aKey).get(),
3098 getter_Copies(aResult));
3101 /* static */
3102 nsresult nsContentUtils::FormatLocalizedString(PropertiesFile aFile,
3103 const char* aKey,
3104 const PRUnichar **aParams,
3105 PRUint32 aParamsLength,
3106 nsXPIDLString& aResult)
3108 nsresult rv = EnsureStringBundle(aFile);
3109 NS_ENSURE_SUCCESS(rv, rv);
3110 nsIStringBundle *bundle = sStringBundles[aFile];
3112 return bundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aKey).get(),
3113 aParams, aParamsLength,
3114 getter_Copies(aResult));
3117 /* static */ nsresult
3118 nsContentUtils::ReportToConsole(PropertiesFile aFile,
3119 const char *aMessageName,
3120 const PRUnichar **aParams,
3121 PRUint32 aParamsLength,
3122 nsIURI* aURI,
3123 const nsAFlatString& aSourceLine,
3124 PRUint32 aLineNumber,
3125 PRUint32 aColumnNumber,
3126 PRUint32 aErrorFlags,
3127 const char *aCategory)
3129 NS_ASSERTION((aParams && aParamsLength) || (!aParams && !aParamsLength),
3130 "Supply either both parameters and their number or no"
3131 "parameters and 0.");
3133 nsresult rv;
3134 if (!sConsoleService) { // only need to bother null-checking here
3135 rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
3136 NS_ENSURE_SUCCESS(rv, rv);
3139 nsXPIDLString errorText;
3140 if (aParams) {
3141 rv = FormatLocalizedString(aFile, aMessageName, aParams, aParamsLength,
3142 errorText);
3144 else {
3145 rv = GetLocalizedString(aFile, aMessageName, errorText);
3147 NS_ENSURE_SUCCESS(rv, rv);
3149 nsCAutoString spec;
3150 if (aURI)
3151 aURI->GetSpec(spec);
3153 nsCOMPtr<nsIScriptError> errorObject =
3154 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
3155 NS_ENSURE_SUCCESS(rv, rv);
3156 rv = errorObject->Init(errorText.get(),
3157 NS_ConvertUTF8toUTF16(spec).get(), // file name
3158 aSourceLine.get(),
3159 aLineNumber, aColumnNumber,
3160 aErrorFlags, aCategory);
3161 NS_ENSURE_SUCCESS(rv, rv);
3163 return sConsoleService->LogMessage(errorObject);
3166 PRBool
3167 nsContentUtils::IsChromeDoc(nsIDocument *aDocument)
3169 if (!aDocument) {
3170 return PR_FALSE;
3173 nsCOMPtr<nsIPrincipal> systemPrincipal;
3174 sSecurityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
3176 return aDocument->NodePrincipal() == systemPrincipal;
3179 PRBool
3180 nsContentUtils::IsChildOfSameType(nsIDocument* aDoc)
3182 nsCOMPtr<nsISupports> container = aDoc->GetContainer();
3183 nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(container));
3184 nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
3185 if (docShellAsItem) {
3186 docShellAsItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
3188 return sameTypeParent != nsnull;
3191 PRBool
3192 nsContentUtils::GetWrapperSafeScriptFilename(nsIDocument *aDocument,
3193 nsIURI *aURI,
3194 nsACString& aScriptURI)
3196 PRBool scriptFileNameModified = PR_FALSE;
3197 aURI->GetSpec(aScriptURI);
3199 if (IsChromeDoc(aDocument)) {
3200 nsCOMPtr<nsIChromeRegistry> chromeReg =
3201 mozilla::services::GetChromeRegistryService();
3203 if (!chromeReg) {
3204 // If we're running w/o a chrome registry we won't modify any
3205 // script file names.
3207 return scriptFileNameModified;
3210 PRBool docWrappersEnabled =
3211 chromeReg->WrappersEnabled(aDocument->GetDocumentURI());
3213 PRBool uriWrappersEnabled = chromeReg->WrappersEnabled(aURI);
3215 nsIURI *docURI = aDocument->GetDocumentURI();
3217 if (docURI && docWrappersEnabled && !uriWrappersEnabled) {
3218 // aURI is a script from a URL that doesn't get wrapper
3219 // automation. aDocument is a chrome document that does get
3220 // wrapper automation. Prepend the chrome document's URI
3221 // followed by the string " -> " to the URI of the script we're
3222 // loading here so that script in that URI gets the same wrapper
3223 // automation that the chrome document expects.
3224 nsCAutoString spec;
3225 docURI->GetSpec(spec);
3226 spec.AppendASCII(" -> ");
3227 spec.Append(aScriptURI);
3229 aScriptURI = spec;
3231 scriptFileNameModified = PR_TRUE;
3235 return scriptFileNameModified;
3238 // static
3239 PRBool
3240 nsContentUtils::IsInChromeDocshell(nsIDocument *aDocument)
3242 if (!aDocument) {
3243 return PR_FALSE;
3246 if (aDocument->GetDisplayDocument()) {
3247 return IsInChromeDocshell(aDocument->GetDisplayDocument());
3250 nsCOMPtr<nsISupports> docContainer = aDocument->GetContainer();
3251 nsCOMPtr<nsIDocShellTreeItem> docShell(do_QueryInterface(docContainer));
3252 PRInt32 itemType = nsIDocShellTreeItem::typeContent;
3253 if (docShell) {
3254 docShell->GetItemType(&itemType);
3257 return itemType == nsIDocShellTreeItem::typeChrome;
3260 // static
3261 nsIContentPolicy*
3262 nsContentUtils::GetContentPolicy()
3264 if (!sTriedToGetContentPolicy) {
3265 CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
3266 // It's OK to not have a content policy service
3267 sTriedToGetContentPolicy = PR_TRUE;
3270 return sContentPolicyService;
3273 // static
3274 PRBool
3275 nsContentUtils::IsEventAttributeName(nsIAtom* aName, PRInt32 aType)
3277 const PRUnichar* name = aName->GetUTF16String();
3278 if (name[0] != 'o' || name[1] != 'n')
3279 return PR_FALSE;
3281 EventNameMapping mapping;
3282 return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
3285 // static
3286 PRUint32
3287 nsContentUtils::GetEventId(nsIAtom* aName)
3289 EventNameMapping mapping;
3290 if (sAtomEventTable->Get(aName, &mapping))
3291 return mapping.mId;
3293 return NS_USER_DEFINED_EVENT;
3296 nsIAtom*
3297 nsContentUtils::GetEventIdAndAtom(const nsAString& aName,
3298 PRUint32 aEventStruct,
3299 PRUint32* aEventID)
3301 EventNameMapping mapping;
3302 if (sStringEventTable->Get(aName, &mapping)) {
3303 *aEventID =
3304 mapping.mStructType == aEventStruct ? mapping.mId : NS_USER_DEFINED_EVENT;
3305 return mapping.mAtom;
3308 // If we have cached lots of user defined event names, clear some of them.
3309 if (sUserDefinedEvents->Count() > 127) {
3310 while (sUserDefinedEvents->Count() > 64) {
3311 nsIAtom* first = sUserDefinedEvents->ObjectAt(0);
3312 sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
3313 sUserDefinedEvents->RemoveObjectAt(0);
3317 *aEventID = NS_USER_DEFINED_EVENT;
3318 nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aName);
3319 sUserDefinedEvents->AppendObject(atom);
3320 mapping.mAtom = atom;
3321 mapping.mId = NS_USER_DEFINED_EVENT;
3322 mapping.mType = EventNameType_None;
3323 mapping.mStructType = NS_EVENT_NULL;
3324 sStringEventTable->Put(aName, mapping);
3325 return mapping.mAtom;
3328 static
3329 nsresult GetEventAndTarget(nsIDocument* aDoc, nsISupports* aTarget,
3330 const nsAString& aEventName,
3331 PRBool aCanBubble, PRBool aCancelable,
3332 nsIDOMEvent** aEvent,
3333 nsIDOMEventTarget** aTargetOut)
3335 nsCOMPtr<nsIDOMDocumentEvent> docEvent(do_QueryInterface(aDoc));
3336 nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(aTarget));
3337 NS_ENSURE_TRUE(docEvent && target, NS_ERROR_INVALID_ARG);
3339 nsCOMPtr<nsIDOMEvent> event;
3340 nsresult rv =
3341 docEvent->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
3342 NS_ENSURE_SUCCESS(rv, rv);
3344 nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
3345 NS_ENSURE_TRUE(privateEvent, NS_ERROR_FAILURE);
3347 rv = event->InitEvent(aEventName, aCanBubble, aCancelable);
3348 NS_ENSURE_SUCCESS(rv, rv);
3350 rv = privateEvent->SetTrusted(PR_TRUE);
3351 NS_ENSURE_SUCCESS(rv, rv);
3353 rv = privateEvent->SetTarget(target);
3354 NS_ENSURE_SUCCESS(rv, rv);
3356 event.forget(aEvent);
3357 target.forget(aTargetOut);
3358 return NS_OK;
3361 // static
3362 nsresult
3363 nsContentUtils::DispatchTrustedEvent(nsIDocument* aDoc, nsISupports* aTarget,
3364 const nsAString& aEventName,
3365 PRBool aCanBubble, PRBool aCancelable,
3366 PRBool *aDefaultAction)
3368 nsCOMPtr<nsIDOMEvent> event;
3369 nsCOMPtr<nsIDOMEventTarget> target;
3370 nsresult rv = GetEventAndTarget(aDoc, aTarget, aEventName, aCanBubble,
3371 aCancelable, getter_AddRefs(event),
3372 getter_AddRefs(target));
3373 NS_ENSURE_SUCCESS(rv, rv);
3375 PRBool dummy;
3376 return target->DispatchEvent(event, aDefaultAction ? aDefaultAction : &dummy);
3379 nsresult
3380 nsContentUtils::DispatchChromeEvent(nsIDocument *aDoc,
3381 nsISupports *aTarget,
3382 const nsAString& aEventName,
3383 PRBool aCanBubble, PRBool aCancelable,
3384 PRBool *aDefaultAction)
3387 nsCOMPtr<nsIDOMEvent> event;
3388 nsCOMPtr<nsIDOMEventTarget> target;
3389 nsresult rv = GetEventAndTarget(aDoc, aTarget, aEventName, aCanBubble,
3390 aCancelable, getter_AddRefs(event),
3391 getter_AddRefs(target));
3392 NS_ENSURE_SUCCESS(rv, rv);
3394 NS_ASSERTION(aDoc, "GetEventAndTarget lied?");
3395 if (!aDoc->GetWindow())
3396 return NS_ERROR_INVALID_ARG;
3398 nsPIDOMEventTarget* piTarget = aDoc->GetWindow()->GetChromeEventHandler();
3399 if (!piTarget)
3400 return NS_ERROR_INVALID_ARG;
3402 nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(piTarget);
3403 if (flo) {
3404 nsRefPtr<nsFrameLoader> fl = flo->GetFrameLoader();
3405 if (fl) {
3406 nsPIDOMEventTarget* t = fl->GetTabChildGlobalAsEventTarget();
3407 piTarget = t ? t : piTarget;
3411 nsEventStatus status = nsEventStatus_eIgnore;
3412 rv = piTarget->DispatchDOMEvent(nsnull, event, nsnull, &status);
3413 if (aDefaultAction) {
3414 *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
3416 return rv;
3419 /* static */
3420 Element*
3421 nsContentUtils::MatchElementId(nsIContent *aContent, const nsIAtom* aId)
3423 for (nsIContent* cur = aContent;
3424 cur;
3425 cur = cur->GetNextNode(aContent)) {
3426 if (aId == cur->GetID()) {
3427 return cur->AsElement();
3431 return nsnull;
3434 /* static */
3435 Element *
3436 nsContentUtils::MatchElementId(nsIContent *aContent, const nsAString& aId)
3438 NS_PRECONDITION(!aId.IsEmpty(), "Will match random elements");
3440 // ID attrs are generally stored as atoms, so just atomize this up front
3441 nsCOMPtr<nsIAtom> id(do_GetAtom(aId));
3442 if (!id) {
3443 // OOM, so just bail
3444 return nsnull;
3447 return MatchElementId(aContent, id);
3450 // Convert the string from the given charset to Unicode.
3451 /* static */
3452 nsresult
3453 nsContentUtils::ConvertStringFromCharset(const nsACString& aCharset,
3454 const nsACString& aInput,
3455 nsAString& aOutput)
3457 if (aCharset.IsEmpty()) {
3458 // Treat the string as UTF8
3459 CopyUTF8toUTF16(aInput, aOutput);
3460 return NS_OK;
3463 nsresult rv;
3464 nsCOMPtr<nsICharsetConverterManager> ccm =
3465 do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
3466 if (NS_FAILED(rv))
3467 return rv;
3469 nsCOMPtr<nsIUnicodeDecoder> decoder;
3470 rv = ccm->GetUnicodeDecoder(PromiseFlatCString(aCharset).get(),
3471 getter_AddRefs(decoder));
3472 if (NS_FAILED(rv))
3473 return rv;
3475 nsPromiseFlatCString flatInput(aInput);
3476 PRInt32 srcLen = flatInput.Length();
3477 PRInt32 dstLen;
3478 rv = decoder->GetMaxLength(flatInput.get(), srcLen, &dstLen);
3479 if (NS_FAILED(rv))
3480 return rv;
3482 PRUnichar *ustr = (PRUnichar *)nsMemory::Alloc((dstLen + 1) *
3483 sizeof(PRUnichar));
3484 if (!ustr)
3485 return NS_ERROR_OUT_OF_MEMORY;
3487 rv = decoder->Convert(flatInput.get(), &srcLen, ustr, &dstLen);
3488 if (NS_SUCCEEDED(rv)) {
3489 ustr[dstLen] = 0;
3490 aOutput.Assign(ustr, dstLen);
3493 nsMemory::Free(ustr);
3494 return rv;
3497 /* static */
3498 PRBool
3499 nsContentUtils::CheckForBOM(const unsigned char* aBuffer, PRUint32 aLength,
3500 nsACString& aCharset, PRBool *bigEndian)
3502 PRBool found = PR_TRUE;
3503 aCharset.Truncate();
3504 if (aLength >= 3 &&
3505 aBuffer[0] == 0xEF &&
3506 aBuffer[1] == 0xBB &&
3507 aBuffer[2] == 0xBF) {
3508 aCharset = "UTF-8";
3510 else if (aLength >= 4 &&
3511 aBuffer[0] == 0x00 &&
3512 aBuffer[1] == 0x00 &&
3513 aBuffer[2] == 0xFE &&
3514 aBuffer[3] == 0xFF) {
3515 aCharset = "UTF-32";
3516 if (bigEndian)
3517 *bigEndian = PR_TRUE;
3519 else if (aLength >= 4 &&
3520 aBuffer[0] == 0xFF &&
3521 aBuffer[1] == 0xFE &&
3522 aBuffer[2] == 0x00 &&
3523 aBuffer[3] == 0x00) {
3524 aCharset = "UTF-32";
3525 if (bigEndian)
3526 *bigEndian = PR_FALSE;
3528 else if (aLength >= 2 &&
3529 aBuffer[0] == 0xFE && aBuffer[1] == 0xFF) {
3530 aCharset = "UTF-16";
3531 if (bigEndian)
3532 *bigEndian = PR_TRUE;
3534 else if (aLength >= 2 &&
3535 aBuffer[0] == 0xFF && aBuffer[1] == 0xFE) {
3536 aCharset = "UTF-16";
3537 if (bigEndian)
3538 *bigEndian = PR_FALSE;
3539 } else {
3540 found = PR_FALSE;
3543 return found;
3546 /* static */
3547 nsIContent*
3548 nsContentUtils::GetReferencedElement(nsIURI* aURI, nsIContent *aFromContent)
3550 nsReferencedElement ref;
3551 ref.Reset(aFromContent, aURI);
3552 return ref.get();
3555 /* static */
3556 void
3557 nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver)
3559 nsCOMPtr<nsIObserverService> observerService =
3560 mozilla::services::GetObserverService();
3561 if (observerService) {
3562 observerService->AddObserver(aObserver,
3563 NS_XPCOM_SHUTDOWN_OBSERVER_ID,
3564 PR_FALSE);
3568 /* static */
3569 void
3570 nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver)
3572 nsCOMPtr<nsIObserverService> observerService =
3573 mozilla::services::GetObserverService();
3574 if (observerService) {
3575 observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
3579 /* static */
3580 PRBool
3581 nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent, PRInt32 aNameSpaceID,
3582 nsIAtom* aName)
3584 static nsIContent::AttrValuesArray strings[] = {&nsGkAtoms::_empty, nsnull};
3585 return aContent->FindAttrValueIn(aNameSpaceID, aName, strings, eCaseMatters)
3586 == nsIContent::ATTR_VALUE_NO_MATCH;
3589 /* static */
3590 PRBool
3591 nsContentUtils::HasMutationListeners(nsINode* aNode,
3592 PRUint32 aType,
3593 nsINode* aTargetForSubtreeModified)
3595 nsIDocument* doc = aNode->GetOwnerDoc();
3596 if (!doc) {
3597 return PR_FALSE;
3600 NS_ASSERTION((aNode->IsNodeOfType(nsINode::eCONTENT) &&
3601 static_cast<nsIContent*>(aNode)->
3602 IsInNativeAnonymousSubtree()) ||
3603 sScriptBlockerCount == sRemovableScriptBlockerCount,
3604 "Want to fire mutation events, but it's not safe");
3606 // global object will be null for documents that don't have windows.
3607 nsPIDOMWindow* window = doc->GetInnerWindow();
3608 // This relies on nsEventListenerManager::AddEventListener, which sets
3609 // all mutation bits when there is a listener for DOMSubtreeModified event.
3610 if (window && !window->HasMutationListeners(aType)) {
3611 return PR_FALSE;
3614 if (aNode->IsNodeOfType(nsINode::eCONTENT) &&
3615 static_cast<nsIContent*>(aNode)->IsInNativeAnonymousSubtree()) {
3616 return PR_FALSE;
3619 doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
3621 // If we have a window, we can check it for mutation listeners now.
3622 if (aNode->IsInDoc()) {
3623 nsCOMPtr<nsPIDOMEventTarget> piTarget(do_QueryInterface(window));
3624 if (piTarget) {
3625 nsIEventListenerManager* manager = piTarget->GetListenerManager(PR_FALSE);
3626 if (manager) {
3627 PRBool hasListeners = PR_FALSE;
3628 manager->HasMutationListeners(&hasListeners);
3629 if (hasListeners) {
3630 return PR_TRUE;
3636 // If we have a window, we know a mutation listener is registered, but it
3637 // might not be in our chain. If we don't have a window, we might have a
3638 // mutation listener. Check quickly to see.
3639 while (aNode) {
3640 nsIEventListenerManager* manager = aNode->GetListenerManager(PR_FALSE);
3641 if (manager) {
3642 PRBool hasListeners = PR_FALSE;
3643 manager->HasMutationListeners(&hasListeners);
3644 if (hasListeners) {
3645 return PR_TRUE;
3649 if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
3650 nsIContent* content = static_cast<nsIContent*>(aNode);
3651 nsIContent* insertionParent =
3652 doc->BindingManager()->GetInsertionParent(content);
3653 if (insertionParent) {
3654 aNode = insertionParent;
3655 continue;
3658 aNode = aNode->GetNodeParent();
3661 return PR_FALSE;
3664 /* static */
3665 void
3666 nsContentUtils::TraverseListenerManager(nsINode *aNode,
3667 nsCycleCollectionTraversalCallback &cb)
3669 if (!sEventListenerManagersHash.ops) {
3670 // We're already shut down, just return.
3671 return;
3674 EventListenerManagerMapEntry *entry =
3675 static_cast<EventListenerManagerMapEntry *>
3676 (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
3677 PL_DHASH_LOOKUP));
3678 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3679 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via hash] mListenerManager");
3680 cb.NoteXPCOMChild(entry->mListenerManager);
3684 nsIEventListenerManager*
3685 nsContentUtils::GetListenerManager(nsINode *aNode,
3686 PRBool aCreateIfNotFound)
3688 if (!aCreateIfNotFound && !aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
3689 return nsnull;
3692 if (!sEventListenerManagersHash.ops) {
3693 // We're already shut down, don't bother creating an event listener
3694 // manager.
3696 return nsnull;
3699 if (!aCreateIfNotFound) {
3700 EventListenerManagerMapEntry *entry =
3701 static_cast<EventListenerManagerMapEntry *>
3702 (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
3703 PL_DHASH_LOOKUP));
3704 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3705 return entry->mListenerManager;
3707 return nsnull;
3710 EventListenerManagerMapEntry *entry =
3711 static_cast<EventListenerManagerMapEntry *>
3712 (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
3713 PL_DHASH_ADD));
3715 if (!entry) {
3716 return nsnull;
3719 if (!entry->mListenerManager) {
3720 nsresult rv =
3721 NS_NewEventListenerManager(getter_AddRefs(entry->mListenerManager));
3723 if (NS_FAILED(rv)) {
3724 PL_DHashTableRawRemove(&sEventListenerManagersHash, entry);
3726 return nsnull;
3729 entry->mListenerManager->SetListenerTarget(aNode);
3731 aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
3734 return entry->mListenerManager;
3737 /* static */
3738 void
3739 nsContentUtils::RemoveListenerManager(nsINode *aNode)
3741 if (sEventListenerManagersHash.ops) {
3742 EventListenerManagerMapEntry *entry =
3743 static_cast<EventListenerManagerMapEntry *>
3744 (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
3745 PL_DHASH_LOOKUP));
3746 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3747 nsCOMPtr<nsIEventListenerManager> listenerManager;
3748 listenerManager.swap(entry->mListenerManager);
3749 // Remove the entry and *then* do operations that could cause further
3750 // modification of sEventListenerManagersHash. See bug 334177.
3751 PL_DHashTableRawRemove(&sEventListenerManagersHash, entry);
3752 if (listenerManager) {
3753 listenerManager->Disconnect();
3759 /* static */
3760 PRBool
3761 nsContentUtils::IsValidNodeName(nsIAtom *aLocalName, nsIAtom *aPrefix,
3762 PRInt32 aNamespaceID)
3764 if (aNamespaceID == kNameSpaceID_Unknown) {
3765 return PR_FALSE;
3768 if (!aPrefix) {
3769 // If the prefix is null, then either the QName must be xmlns or the
3770 // namespace must not be XMLNS.
3771 return (aLocalName == nsGkAtoms::xmlns) ==
3772 (aNamespaceID == kNameSpaceID_XMLNS);
3775 // If the prefix is non-null then the namespace must not be null.
3776 if (aNamespaceID == kNameSpaceID_None) {
3777 return PR_FALSE;
3780 // If the namespace is the XMLNS namespace then the prefix must be xmlns,
3781 // but the localname must not be xmlns.
3782 if (aNamespaceID == kNameSpaceID_XMLNS) {
3783 return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
3786 // If the namespace is not the XMLNS namespace then the prefix must not be
3787 // xmlns.
3788 // If the namespace is the XML namespace then the prefix can be anything.
3789 // If the namespace is not the XML namespace then the prefix must not be xml.
3790 return aPrefix != nsGkAtoms::xmlns &&
3791 (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
3794 /* static */
3795 nsresult
3796 nsContentUtils::CreateContextualFragment(nsINode* aContextNode,
3797 const nsAString& aFragment,
3798 PRBool aWillOwnFragment,
3799 nsIDOMDocumentFragment** aReturn)
3801 *aReturn = nsnull;
3802 NS_ENSURE_ARG(aContextNode);
3804 nsresult rv;
3806 // If we don't have a document here, we can't get the right security context
3807 // for compiling event handlers... so just bail out.
3808 nsCOMPtr<nsIDocument> document = aContextNode->GetOwnerDoc();
3809 NS_ENSURE_TRUE(document, NS_ERROR_NOT_AVAILABLE);
3811 PRBool isHTML = document->IsHTML();
3812 #ifdef DEBUG
3813 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document);
3814 NS_ASSERTION(!isHTML || htmlDoc, "Should have HTMLDocument here!");
3815 #endif
3817 if (isHTML && nsHtml5Module::sEnabled) {
3818 // See if the document has a cached fragment parser. nsHTMLDocument is the
3819 // only one that should really have one at the moment.
3820 nsCOMPtr<nsIParser> parser = document->GetFragmentParser();
3821 if (parser) {
3822 // Get the parser ready to use.
3823 parser->Reset();
3825 else {
3826 // Create a new parser for this operation.
3827 parser = nsHtml5Module::NewHtml5Parser();
3828 if (!parser) {
3829 return NS_ERROR_OUT_OF_MEMORY;
3832 nsCOMPtr<nsIDOMDocumentFragment> frag;
3833 rv = NS_NewDocumentFragment(getter_AddRefs(frag), document->NodeInfoManager());
3834 NS_ENSURE_SUCCESS(rv, rv);
3836 nsCOMPtr<nsIContent> contextAsContent = do_QueryInterface(aContextNode);
3837 if (contextAsContent && !contextAsContent->IsElement()) {
3838 contextAsContent = contextAsContent->GetParent();
3839 if (contextAsContent && !contextAsContent->IsElement()) {
3840 // can this even happen?
3841 contextAsContent = nsnull;
3845 nsAHtml5FragmentParser* asFragmentParser =
3846 static_cast<nsAHtml5FragmentParser*> (parser.get());
3847 nsCOMPtr<nsIContent> fragment = do_QueryInterface(frag);
3848 if (contextAsContent &&
3849 !(nsGkAtoms::html == contextAsContent->Tag() &&
3850 contextAsContent->IsHTML())) {
3851 asFragmentParser->ParseHtml5Fragment(aFragment,
3852 fragment,
3853 contextAsContent->Tag(),
3854 contextAsContent->GetNameSpaceID(),
3855 (document->GetCompatibilityMode() ==
3856 eCompatibility_NavQuirks),
3857 PR_FALSE);
3858 } else {
3859 asFragmentParser->ParseHtml5Fragment(aFragment,
3860 fragment,
3861 nsGkAtoms::body,
3862 kNameSpaceID_XHTML,
3863 (document->GetCompatibilityMode() ==
3864 eCompatibility_NavQuirks),
3865 PR_FALSE);
3868 frag.swap(*aReturn);
3869 document->SetFragmentParser(parser);
3870 return NS_OK;
3873 nsAutoTArray<nsString, 32> tagStack;
3874 nsAutoString uriStr, nameStr;
3875 nsCOMPtr<nsIContent> content = do_QueryInterface(aContextNode);
3876 // just in case we have a text node
3877 if (content && !content->IsElement())
3878 content = content->GetParent();
3880 while (content && content->IsElement()) {
3881 nsString& tagName = *tagStack.AppendElement();
3882 NS_ENSURE_TRUE(&tagName, NS_ERROR_OUT_OF_MEMORY);
3884 content->NodeInfo()->GetQualifiedName(tagName);
3886 // see if we need to add xmlns declarations
3887 PRUint32 count = content->GetAttrCount();
3888 PRBool setDefaultNamespace = PR_FALSE;
3889 if (count > 0) {
3890 PRUint32 index;
3892 for (index = 0; index < count; index++) {
3893 const nsAttrName* name = content->GetAttrNameAt(index);
3894 if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
3895 content->GetAttr(kNameSpaceID_XMLNS, name->LocalName(), uriStr);
3897 // really want something like nsXMLContentSerializer::SerializeAttr
3898 tagName.Append(NS_LITERAL_STRING(" xmlns")); // space important
3899 if (name->GetPrefix()) {
3900 tagName.Append(PRUnichar(':'));
3901 name->LocalName()->ToString(nameStr);
3902 tagName.Append(nameStr);
3903 } else {
3904 setDefaultNamespace = PR_TRUE;
3906 tagName.Append(NS_LITERAL_STRING("=\"") + uriStr +
3907 NS_LITERAL_STRING("\""));
3912 if (!setDefaultNamespace) {
3913 nsINodeInfo* info = content->NodeInfo();
3914 if (!info->GetPrefixAtom() &&
3915 info->NamespaceID() != kNameSpaceID_None) {
3916 // We have no namespace prefix, but have a namespace ID. Push
3917 // default namespace attr in, so that our kids will be in our
3918 // namespace.
3919 info->GetNamespaceURI(uriStr);
3920 tagName.Append(NS_LITERAL_STRING(" xmlns=\"") + uriStr +
3921 NS_LITERAL_STRING("\""));
3925 content = content->GetParent();
3928 nsCAutoString contentType;
3929 nsAutoString buf;
3930 document->GetContentType(buf);
3931 LossyCopyUTF16toASCII(buf, contentType);
3933 // See if the document has a cached fragment parser. nsHTMLDocument is the
3934 // only one that should really have one at the moment.
3935 nsCOMPtr<nsIParser> parser = document->GetFragmentParser();
3936 if (parser) {
3937 // Get the parser ready to use.
3938 parser->Reset();
3940 else {
3941 // Create a new parser for this operation.
3942 parser = do_CreateInstance(kCParserCID, &rv);
3943 NS_ENSURE_SUCCESS(rv, rv);
3946 // See if the parser already has a content sink that we can reuse.
3947 nsCOMPtr<nsIFragmentContentSink> sink;
3948 nsCOMPtr<nsIContentSink> contentsink = parser->GetContentSink();
3949 if (contentsink) {
3950 // Make sure it's the correct type.
3951 if (isHTML) {
3952 nsCOMPtr<nsIHTMLContentSink> htmlsink = do_QueryInterface(contentsink);
3953 sink = do_QueryInterface(htmlsink);
3955 else {
3956 nsCOMPtr<nsIXMLContentSink> xmlsink = do_QueryInterface(contentsink);
3957 sink = do_QueryInterface(xmlsink);
3961 if (!sink) {
3962 // Either there was no cached content sink or it was the wrong type. Make a
3963 // new one.
3964 if (isHTML) {
3965 rv = NS_NewHTMLFragmentContentSink(getter_AddRefs(sink));
3966 } else {
3967 rv = NS_NewXMLFragmentContentSink(getter_AddRefs(sink));
3969 NS_ENSURE_SUCCESS(rv, rv);
3971 contentsink = do_QueryInterface(sink);
3972 NS_ASSERTION(contentsink, "Sink doesn't QI to nsIContentSink!");
3974 parser->SetContentSink(contentsink);
3977 sink->SetTargetDocument(document);
3979 nsDTDMode mode = eDTDMode_autodetect;
3980 switch (document->GetCompatibilityMode()) {
3981 case eCompatibility_NavQuirks:
3982 mode = eDTDMode_quirks;
3983 break;
3984 case eCompatibility_AlmostStandards:
3985 mode = eDTDMode_almost_standards;
3986 break;
3987 case eCompatibility_FullStandards:
3988 mode = eDTDMode_full_standards;
3989 break;
3990 default:
3991 NS_NOTREACHED("unknown mode");
3992 break;
3995 rv = parser->ParseFragment(aFragment, nsnull, tagStack,
3996 !isHTML, contentType, mode);
3997 if (NS_SUCCEEDED(rv)) {
3998 rv = sink->GetFragment(aWillOwnFragment, aReturn);
4001 document->SetFragmentParser(parser);
4003 return rv;
4006 /* static */
4007 nsresult
4008 nsContentUtils::CreateDocument(const nsAString& aNamespaceURI,
4009 const nsAString& aQualifiedName,
4010 nsIDOMDocumentType* aDoctype,
4011 nsIURI* aDocumentURI, nsIURI* aBaseURI,
4012 nsIPrincipal* aPrincipal,
4013 nsIScriptGlobalObject* aEventObject,
4014 nsIDOMDocument** aResult)
4016 nsresult rv = NS_NewDOMDocument(aResult, aNamespaceURI, aQualifiedName,
4017 aDoctype, aDocumentURI, aBaseURI, aPrincipal,
4018 PR_TRUE);
4019 NS_ENSURE_SUCCESS(rv, rv);
4021 nsCOMPtr<nsIDocument> document = do_QueryInterface(*aResult);
4022 document->SetScriptHandlingObject(aEventObject);
4024 // created documents are immediately "complete" (ready to use)
4025 document->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
4026 return NS_OK;
4029 /* static */
4030 nsresult
4031 nsContentUtils::SetNodeTextContent(nsIContent* aContent,
4032 const nsAString& aValue,
4033 PRBool aTryReuse)
4035 // Might as well stick a batch around this since we're performing several
4036 // mutations.
4037 mozAutoDocUpdate updateBatch(aContent->GetCurrentDoc(),
4038 UPDATE_CONTENT_MODEL, PR_TRUE);
4040 PRUint32 childCount = aContent->GetChildCount();
4042 if (aTryReuse && !aValue.IsEmpty()) {
4043 PRUint32 removeIndex = 0;
4045 // i is unsigned, so i >= is always true
4046 for (PRUint32 i = 0; i < childCount; ++i) {
4047 nsIContent* child = aContent->GetChildAt(removeIndex);
4048 if (removeIndex == 0 && child && child->IsNodeOfType(nsINode::eTEXT)) {
4049 nsresult rv = child->SetText(aValue, PR_TRUE);
4050 NS_ENSURE_SUCCESS(rv, rv);
4052 removeIndex = 1;
4054 else {
4055 aContent->RemoveChildAt(removeIndex, PR_TRUE);
4059 if (removeIndex == 1) {
4060 return NS_OK;
4063 else {
4064 // i is unsigned, so i >= is always true
4065 for (PRUint32 i = childCount; i-- != 0; ) {
4066 aContent->RemoveChildAt(i, PR_TRUE);
4070 if (aValue.IsEmpty()) {
4071 return NS_OK;
4074 nsCOMPtr<nsIContent> textContent;
4075 nsresult rv = NS_NewTextNode(getter_AddRefs(textContent),
4076 aContent->NodeInfo()->NodeInfoManager());
4077 NS_ENSURE_SUCCESS(rv, rv);
4079 textContent->SetText(aValue, PR_TRUE);
4081 return aContent->AppendChildTo(textContent, PR_TRUE);
4084 static void AppendNodeTextContentsRecurse(nsINode* aNode, nsAString& aResult)
4086 nsIContent* child;
4087 PRUint32 i;
4088 for (i = 0; (child = aNode->GetChildAt(i)); ++i) {
4089 if (child->IsElement()) {
4090 AppendNodeTextContentsRecurse(child, aResult);
4092 else if (child->IsNodeOfType(nsINode::eTEXT)) {
4093 child->AppendTextTo(aResult);
4098 /* static */
4099 void
4100 nsContentUtils::AppendNodeTextContent(nsINode* aNode, PRBool aDeep,
4101 nsAString& aResult)
4103 if (aNode->IsNodeOfType(nsINode::eTEXT)) {
4104 static_cast<nsIContent*>(aNode)->AppendTextTo(aResult);
4106 else if (aDeep) {
4107 AppendNodeTextContentsRecurse(aNode, aResult);
4109 else {
4110 nsIContent* child;
4111 PRUint32 i;
4112 for (i = 0; (child = aNode->GetChildAt(i)); ++i) {
4113 if (child->IsNodeOfType(nsINode::eTEXT)) {
4114 child->AppendTextTo(aResult);
4120 PRBool
4121 nsContentUtils::HasNonEmptyTextContent(nsINode* aNode)
4123 nsIContent* child;
4124 PRUint32 i;
4125 for (i = 0; (child = aNode->GetChildAt(i)); ++i) {
4126 if (child->IsNodeOfType(nsINode::eTEXT) &&
4127 child->TextLength() > 0) {
4128 return PR_TRUE;
4132 return PR_FALSE;
4135 /* static */
4136 PRBool
4137 nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
4138 const nsIContent* aContent)
4140 NS_PRECONDITION(aNode,
4141 "Must have a node to work with");
4142 NS_PRECONDITION(aContent,
4143 "Must have a content to work with");
4145 if (!aNode->IsNodeOfType(nsINode::eCONTENT)) {
4147 * The root isn't an nsIContent, so it's a document or attribute. The only
4148 * nodes in the same anonymous subtree as it will have a null
4149 * bindingParent.
4151 * XXXbz strictly speaking, that's not true for attribute nodes.
4153 return aContent->GetBindingParent() == nsnull;
4156 return static_cast<const nsIContent*>(aNode)->GetBindingParent() ==
4157 aContent->GetBindingParent();
4161 class AnonymousContentDestroyer : public nsRunnable {
4162 public:
4163 AnonymousContentDestroyer(nsCOMPtr<nsIContent>* aContent) {
4164 mContent.swap(*aContent);
4165 mParent = mContent->GetParent();
4166 mDoc = mContent->GetOwnerDoc();
4168 NS_IMETHOD Run() {
4169 mContent->UnbindFromTree();
4170 return NS_OK;
4172 private:
4173 nsCOMPtr<nsIContent> mContent;
4174 // Hold strong refs to the parent content and document so that they
4175 // don't die unexpectedly
4176 nsCOMPtr<nsIDocument> mDoc;
4177 nsCOMPtr<nsIContent> mParent;
4180 /* static */
4181 void
4182 nsContentUtils::DestroyAnonymousContent(nsCOMPtr<nsIContent>* aContent)
4184 if (*aContent) {
4185 AddScriptRunner(new AnonymousContentDestroyer(aContent));
4189 /* static */
4190 nsIDOMScriptObjectFactory*
4191 nsContentUtils::GetDOMScriptObjectFactory()
4193 if (!sDOMScriptObjectFactory) {
4194 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
4195 NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
4197 CallGetService(kDOMScriptObjectFactoryCID, &sDOMScriptObjectFactory);
4200 return sDOMScriptObjectFactory;
4203 /* static */
4204 nsresult
4205 nsContentUtils::HoldScriptObject(PRUint32 aLangID, void *aObject)
4207 NS_ASSERTION(aObject, "unexpected null object");
4208 NS_ASSERTION(aLangID != nsIProgrammingLanguage::JAVASCRIPT,
4209 "Should use HoldJSObjects.");
4210 nsresult rv;
4212 PRUint32 langIndex = NS_STID_INDEX(aLangID);
4213 nsIScriptRuntime *runtime = sScriptRuntimes[langIndex];
4214 if (!runtime) {
4215 nsIDOMScriptObjectFactory *factory = GetDOMScriptObjectFactory();
4216 NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
4218 rv = factory->GetScriptRuntimeByID(aLangID, &runtime);
4219 NS_ENSURE_SUCCESS(rv, rv);
4221 // This makes sScriptRuntimes hold a strong ref.
4222 sScriptRuntimes[langIndex] = runtime;
4225 rv = runtime->HoldScriptObject(aObject);
4226 NS_ENSURE_SUCCESS(rv, rv);
4228 ++sScriptRootCount[langIndex];
4229 NS_LOG_ADDREF(sScriptRuntimes[langIndex], sScriptRootCount[langIndex],
4230 "HoldScriptObject", sizeof(void*));
4232 return NS_OK;
4235 /* static */
4236 void
4237 nsContentUtils::DropScriptObject(PRUint32 aLangID, void *aObject,
4238 void *aClosure)
4240 NS_ASSERTION(aObject, "unexpected null object");
4241 NS_ASSERTION(aLangID != nsIProgrammingLanguage::JAVASCRIPT,
4242 "Should use DropJSObjects.");
4243 PRUint32 langIndex = NS_STID_INDEX(aLangID);
4244 NS_LOG_RELEASE(sScriptRuntimes[langIndex], sScriptRootCount[langIndex] - 1,
4245 "HoldScriptObject");
4246 sScriptRuntimes[langIndex]->DropScriptObject(aObject);
4247 if (--sScriptRootCount[langIndex] == 0) {
4248 NS_RELEASE(sScriptRuntimes[langIndex]);
4252 /* static */
4253 nsresult
4254 nsContentUtils::HoldJSObjects(void* aScriptObjectHolder,
4255 nsScriptObjectTracer* aTracer)
4257 NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
4259 nsresult rv = sXPConnect->AddJSHolder(aScriptObjectHolder, aTracer);
4260 NS_ENSURE_SUCCESS(rv, rv);
4262 if (sJSGCThingRootCount++ == 0) {
4263 nsLayoutStatics::AddRef();
4265 NS_LOG_ADDREF(sXPConnect, sJSGCThingRootCount, "HoldJSObjects",
4266 sizeof(void*));
4268 return NS_OK;
4271 /* static */
4272 nsresult
4273 nsContentUtils::DropJSObjects(void* aScriptObjectHolder)
4275 NS_LOG_RELEASE(sXPConnect, sJSGCThingRootCount - 1, "HoldJSObjects");
4276 nsresult rv = sXPConnect->RemoveJSHolder(aScriptObjectHolder);
4277 if (--sJSGCThingRootCount == 0) {
4278 nsLayoutStatics::Release();
4280 return rv;
4283 /* static */
4284 PRUint32
4285 nsContentUtils::GetWidgetStatusFromIMEStatus(PRUint32 aState)
4287 switch (aState & nsIContent::IME_STATUS_MASK_ENABLED) {
4288 case nsIContent::IME_STATUS_DISABLE:
4289 return nsIWidget::IME_STATUS_DISABLED;
4290 case nsIContent::IME_STATUS_ENABLE:
4291 return nsIWidget::IME_STATUS_ENABLED;
4292 case nsIContent::IME_STATUS_PASSWORD:
4293 return nsIWidget::IME_STATUS_PASSWORD;
4294 case nsIContent::IME_STATUS_PLUGIN:
4295 return nsIWidget::IME_STATUS_PLUGIN;
4296 default:
4297 NS_ERROR("The given state doesn't have valid enable state");
4298 return nsIWidget::IME_STATUS_ENABLED;
4302 /* static */
4303 void
4304 nsContentUtils::NotifyInstalledMenuKeyboardListener(PRBool aInstalling)
4306 nsIMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
4309 static PRBool SchemeIs(nsIURI* aURI, const char* aScheme)
4311 nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
4312 NS_ENSURE_TRUE(baseURI, PR_FALSE);
4314 PRBool isScheme = PR_FALSE;
4315 return NS_SUCCEEDED(baseURI->SchemeIs(aScheme, &isScheme)) && isScheme;
4318 /* static */
4319 nsresult
4320 nsContentUtils::CheckSecurityBeforeLoad(nsIURI* aURIToLoad,
4321 nsIPrincipal* aLoadingPrincipal,
4322 PRUint32 aCheckLoadFlags,
4323 PRBool aAllowData,
4324 PRUint32 aContentPolicyType,
4325 nsISupports* aContext,
4326 const nsACString& aMimeGuess,
4327 nsISupports* aExtra)
4329 NS_PRECONDITION(aLoadingPrincipal, "Must have a loading principal here");
4331 PRBool isSystemPrin = PR_FALSE;
4332 if (NS_SUCCEEDED(sSecurityManager->IsSystemPrincipal(aLoadingPrincipal,
4333 &isSystemPrin)) &&
4334 isSystemPrin) {
4335 return NS_OK;
4338 // XXXbz do we want to fast-path skin stylesheets loading XBL here somehow?
4339 // CheckLoadURIWithPrincipal
4340 nsresult rv = sSecurityManager->
4341 CheckLoadURIWithPrincipal(aLoadingPrincipal, aURIToLoad, aCheckLoadFlags);
4342 NS_ENSURE_SUCCESS(rv, rv);
4344 // Content Policy
4345 PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
4346 rv = NS_CheckContentLoadPolicy(aContentPolicyType,
4347 aURIToLoad,
4348 aLoadingPrincipal,
4349 aContext,
4350 aMimeGuess,
4351 aExtra,
4352 &shouldLoad,
4353 GetContentPolicy(),
4354 sSecurityManager);
4355 NS_ENSURE_SUCCESS(rv, rv);
4356 if (NS_CP_REJECTED(shouldLoad)) {
4357 return NS_ERROR_CONTENT_BLOCKED;
4360 // Same Origin
4361 if ((aAllowData && SchemeIs(aURIToLoad, "data")) ||
4362 ((aCheckLoadFlags & nsIScriptSecurityManager::ALLOW_CHROME) &&
4363 SchemeIs(aURIToLoad, "chrome"))) {
4364 return NS_OK;
4367 return aLoadingPrincipal->CheckMayLoad(aURIToLoad, PR_TRUE);
4370 PRBool
4371 nsContentUtils::IsSystemPrincipal(nsIPrincipal* aPrincipal)
4373 PRBool isSystem;
4374 nsresult rv = sSecurityManager->IsSystemPrincipal(aPrincipal, &isSystem);
4375 return NS_SUCCEEDED(rv) && isSystem;
4378 /* static */
4379 void
4380 nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
4381 nsIURI *aLinkURI, const nsString &aTargetSpec,
4382 PRBool aClick, PRBool aIsUserTriggered)
4384 NS_ASSERTION(aPresContext, "Need a nsPresContext");
4385 NS_PRECONDITION(aLinkURI, "No link URI");
4387 if (aContent->IsEditable()) {
4388 return;
4391 nsILinkHandler *handler = aPresContext->GetLinkHandler();
4392 if (!handler) {
4393 return;
4396 if (!aClick) {
4397 handler->OnOverLink(aContent, aLinkURI, aTargetSpec.get());
4399 return;
4402 // Check that this page is allowed to load this URI.
4403 nsresult proceed = NS_OK;
4405 if (sSecurityManager) {
4406 PRUint32 flag =
4407 aIsUserTriggered ?
4408 (PRUint32)nsIScriptSecurityManager::STANDARD :
4409 (PRUint32)nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT;
4410 proceed =
4411 sSecurityManager->CheckLoadURIWithPrincipal(aContent->NodePrincipal(),
4412 aLinkURI, flag);
4415 // Only pass off the click event if the script security manager says it's ok.
4416 if (NS_SUCCEEDED(proceed)) {
4417 handler->OnLinkClick(aContent, aLinkURI, aTargetSpec.get());
4421 /* static */
4422 nsIWidget*
4423 nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget)
4425 if (!aWidget)
4426 return nsnull;
4428 return aWidget->GetTopLevelWidget();
4431 /* static */
4432 const nsDependentString
4433 nsContentUtils::GetLocalizedEllipsis()
4435 static PRUnichar sBuf[4] = { 0, 0, 0, 0 };
4436 if (!sBuf[0]) {
4437 nsAutoString tmp(GetLocalizedStringPref("intl.ellipsis"));
4438 PRUint32 len = NS_MIN(PRUint32(tmp.Length()),
4439 PRUint32(NS_ARRAY_LENGTH(sBuf) - 1));
4440 CopyUnicodeTo(tmp, 0, sBuf, len);
4441 if (!sBuf[0])
4442 sBuf[0] = PRUnichar(0x2026);
4444 return nsDependentString(sBuf);
4447 //static
4448 nsEvent*
4449 nsContentUtils::GetNativeEvent(nsIDOMEvent* aDOMEvent)
4451 nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aDOMEvent));
4452 if (!privateEvent)
4453 return nsnull;
4454 return privateEvent->GetInternalNSEvent();
4457 //static
4458 PRBool
4459 nsContentUtils::DOMEventToNativeKeyEvent(nsIDOMKeyEvent* aKeyEvent,
4460 nsNativeKeyEvent* aNativeEvent,
4461 PRBool aGetCharCode)
4463 nsCOMPtr<nsIDOMNSUIEvent> uievent = do_QueryInterface(aKeyEvent);
4464 PRBool defaultPrevented;
4465 uievent->GetPreventDefault(&defaultPrevented);
4466 if (defaultPrevented)
4467 return PR_FALSE;
4469 nsCOMPtr<nsIDOMNSEvent> nsevent = do_QueryInterface(aKeyEvent);
4470 PRBool trusted = PR_FALSE;
4471 nsevent->GetIsTrusted(&trusted);
4472 if (!trusted)
4473 return PR_FALSE;
4475 if (aGetCharCode) {
4476 aKeyEvent->GetCharCode(&aNativeEvent->charCode);
4477 } else {
4478 aNativeEvent->charCode = 0;
4480 aKeyEvent->GetKeyCode(&aNativeEvent->keyCode);
4481 aKeyEvent->GetAltKey(&aNativeEvent->altKey);
4482 aKeyEvent->GetCtrlKey(&aNativeEvent->ctrlKey);
4483 aKeyEvent->GetShiftKey(&aNativeEvent->shiftKey);
4484 aKeyEvent->GetMetaKey(&aNativeEvent->metaKey);
4486 aNativeEvent->nativeEvent = GetNativeEvent(aKeyEvent);
4488 return PR_TRUE;
4491 static PRBool
4492 HasASCIIDigit(const nsTArray<nsShortcutCandidate>& aCandidates)
4494 for (PRUint32 i = 0; i < aCandidates.Length(); ++i) {
4495 PRUint32 ch = aCandidates[i].mCharCode;
4496 if (ch >= '0' && ch <= '9')
4497 return PR_TRUE;
4499 return PR_FALSE;
4502 static PRBool
4503 CharsCaseInsensitiveEqual(PRUint32 aChar1, PRUint32 aChar2)
4505 return aChar1 == aChar2 ||
4506 (IS_IN_BMP(aChar1) && IS_IN_BMP(aChar2) &&
4507 ToLowerCase(PRUnichar(aChar1)) == ToLowerCase(PRUnichar(aChar2)));
4510 static PRBool
4511 IsCaseChangeableChar(PRUint32 aChar)
4513 return IS_IN_BMP(aChar) &&
4514 ToLowerCase(PRUnichar(aChar)) != ToUpperCase(PRUnichar(aChar));
4517 /* static */
4518 void
4519 nsContentUtils::GetAccelKeyCandidates(nsIDOMKeyEvent* aDOMKeyEvent,
4520 nsTArray<nsShortcutCandidate>& aCandidates)
4522 NS_PRECONDITION(aCandidates.IsEmpty(), "aCandidates must be empty");
4524 nsAutoString eventType;
4525 aDOMKeyEvent->GetType(eventType);
4526 // Don't process if aDOMKeyEvent is not a keypress event.
4527 if (!eventType.EqualsLiteral("keypress"))
4528 return;
4530 nsKeyEvent* nativeKeyEvent =
4531 static_cast<nsKeyEvent*>(GetNativeEvent(aDOMKeyEvent));
4532 if (nativeKeyEvent) {
4533 NS_ASSERTION(nativeKeyEvent->eventStructType == NS_KEY_EVENT,
4534 "wrong type of native event");
4535 // nsShortcutCandidate::mCharCode is a candidate charCode.
4536 // nsShoftcutCandidate::mIgnoreShift means the mCharCode should be tried to
4537 // execute a command with/without shift key state. If this is TRUE, the
4538 // shifted key state should be ignored. Otherwise, don't ignore the state.
4539 // the priority of the charCodes are (shift key is not pressed):
4540 // 0: charCode/PR_FALSE,
4541 // 1: unshiftedCharCodes[0]/PR_FALSE, 2: unshiftedCharCodes[1]/PR_FALSE...
4542 // the priority of the charCodes are (shift key is pressed):
4543 // 0: charCode/PR_FALSE,
4544 // 1: shiftedCharCodes[0]/PR_FALSE, 2: shiftedCharCodes[0]/PR_TRUE,
4545 // 3: shiftedCharCodes[1]/PR_FALSE, 4: shiftedCharCodes[1]/PR_TRUE...
4546 if (nativeKeyEvent->charCode) {
4547 nsShortcutCandidate key(nativeKeyEvent->charCode, PR_FALSE);
4548 aCandidates.AppendElement(key);
4551 PRUint32 len = nativeKeyEvent->alternativeCharCodes.Length();
4552 if (!nativeKeyEvent->isShift) {
4553 for (PRUint32 i = 0; i < len; ++i) {
4554 PRUint32 ch =
4555 nativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode;
4556 if (!ch || ch == nativeKeyEvent->charCode)
4557 continue;
4559 nsShortcutCandidate key(ch, PR_FALSE);
4560 aCandidates.AppendElement(key);
4562 // If unshiftedCharCodes doesn't have numeric but shiftedCharCode has it,
4563 // this keyboard layout is AZERTY or similar layout, probably.
4564 // In this case, Accel+[0-9] should be accessible without shift key.
4565 // However, the priority should be lowest.
4566 if (!HasASCIIDigit(aCandidates)) {
4567 for (PRUint32 i = 0; i < len; ++i) {
4568 PRUint32 ch =
4569 nativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode;
4570 if (ch >= '0' && ch <= '9') {
4571 nsShortcutCandidate key(ch, PR_FALSE);
4572 aCandidates.AppendElement(key);
4573 break;
4577 } else {
4578 for (PRUint32 i = 0; i < len; ++i) {
4579 PRUint32 ch = nativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode;
4580 if (!ch)
4581 continue;
4583 if (ch != nativeKeyEvent->charCode) {
4584 nsShortcutCandidate key(ch, PR_FALSE);
4585 aCandidates.AppendElement(key);
4588 // If the char is an alphabet, the shift key state should not be
4589 // ignored. E.g., Ctrl+Shift+C should not execute Ctrl+C.
4591 // And checking the charCode is same as unshiftedCharCode too.
4592 // E.g., for Ctrl+Shift+(Plus of Numpad) should not run Ctrl+Plus.
4593 PRUint32 unshiftCh =
4594 nativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode;
4595 if (CharsCaseInsensitiveEqual(ch, unshiftCh))
4596 continue;
4598 // On the Hebrew keyboard layout on Windows, the unshifted char is a
4599 // localized character but the shifted char is a Latin alphabet,
4600 // then, we should not execute without the shift state. See bug 433192.
4601 if (IsCaseChangeableChar(ch))
4602 continue;
4604 // Setting the alternative charCode candidates for retry without shift
4605 // key state only when the shift key is pressed.
4606 nsShortcutCandidate key(ch, PR_TRUE);
4607 aCandidates.AppendElement(key);
4610 } else {
4611 PRUint32 charCode;
4612 aDOMKeyEvent->GetCharCode(&charCode);
4613 if (charCode) {
4614 nsShortcutCandidate key(charCode, PR_FALSE);
4615 aCandidates.AppendElement(key);
4620 /* static */
4621 void
4622 nsContentUtils::GetAccessKeyCandidates(nsKeyEvent* aNativeKeyEvent,
4623 nsTArray<PRUint32>& aCandidates)
4625 NS_PRECONDITION(aCandidates.IsEmpty(), "aCandidates must be empty");
4627 // return the lower cased charCode candidates for access keys.
4628 // the priority of the charCodes are:
4629 // 0: charCode, 1: unshiftedCharCodes[0], 2: shiftedCharCodes[0]
4630 // 3: unshiftedCharCodes[1], 4: shiftedCharCodes[1],...
4631 if (aNativeKeyEvent->charCode) {
4632 PRUint32 ch = aNativeKeyEvent->charCode;
4633 if (IS_IN_BMP(ch))
4634 ch = ToLowerCase(PRUnichar(ch));
4635 aCandidates.AppendElement(ch);
4637 for (PRUint32 i = 0;
4638 i < aNativeKeyEvent->alternativeCharCodes.Length(); ++i) {
4639 PRUint32 ch[2] =
4640 { aNativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode,
4641 aNativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode };
4642 for (PRUint32 j = 0; j < 2; ++j) {
4643 if (!ch[j])
4644 continue;
4645 if (IS_IN_BMP(ch[j]))
4646 ch[j] = ToLowerCase(PRUnichar(ch[j]));
4647 // Don't append the charCode that was already appended.
4648 if (aCandidates.IndexOf(ch[j]) == aCandidates.NoIndex)
4649 aCandidates.AppendElement(ch[j]);
4652 return;
4655 /* static */
4656 void
4657 nsContentUtils::AddScriptBlocker()
4659 if (!sScriptBlockerCount) {
4660 NS_ASSERTION(sRunnersCountAtFirstBlocker == 0,
4661 "Should not already have a count");
4662 sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Count();
4664 ++sScriptBlockerCount;
4667 /* static */
4668 void
4669 nsContentUtils::AddScriptBlockerAndPreventAddingRunners()
4671 AddScriptBlocker();
4672 if (sScriptBlockerCountWhereRunnersPrevented == 0) {
4673 sScriptBlockerCountWhereRunnersPrevented = sScriptBlockerCount;
4677 /* static */
4678 void
4679 nsContentUtils::RemoveScriptBlocker()
4681 NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
4682 --sScriptBlockerCount;
4683 if (sScriptBlockerCount < sScriptBlockerCountWhereRunnersPrevented) {
4684 sScriptBlockerCountWhereRunnersPrevented = 0;
4686 if (sScriptBlockerCount) {
4687 return;
4690 PRUint32 firstBlocker = sRunnersCountAtFirstBlocker;
4691 PRUint32 lastBlocker = (PRUint32)sBlockedScriptRunners->Count();
4692 sRunnersCountAtFirstBlocker = 0;
4693 NS_ASSERTION(firstBlocker <= lastBlocker,
4694 "bad sRunnersCountAtFirstBlocker");
4696 while (firstBlocker < lastBlocker) {
4697 nsCOMPtr<nsIRunnable> runnable = (*sBlockedScriptRunners)[firstBlocker];
4698 sBlockedScriptRunners->RemoveObjectAt(firstBlocker);
4699 --lastBlocker;
4701 runnable->Run();
4702 NS_ASSERTION(lastBlocker == (PRUint32)sBlockedScriptRunners->Count() &&
4703 sRunnersCountAtFirstBlocker == 0,
4704 "Bad count");
4705 NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
4709 /* static */
4710 PRBool
4711 nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
4713 if (!aRunnable) {
4714 return PR_FALSE;
4717 if (sScriptBlockerCount) {
4718 if (sScriptBlockerCountWhereRunnersPrevented > 0) {
4719 NS_ERROR("Adding a script runner when that is prevented!");
4720 return PR_FALSE;
4722 return sBlockedScriptRunners->AppendObject(aRunnable);
4725 nsCOMPtr<nsIRunnable> run = aRunnable;
4726 run->Run();
4728 return PR_TRUE;
4732 * Helper function for nsContentUtils::ProcessViewportInfo.
4734 * Handles a single key=value pair. If it corresponds to a valid viewport
4735 * attribute, add it to the document header data. No validation is done on the
4736 * value itself (this is done at display time).
4738 static void ProcessViewportToken(nsIDocument *aDocument,
4739 const nsAString &token) {
4741 /* Iterators. */
4742 nsAString::const_iterator tip, tail, end;
4743 token.BeginReading(tip);
4744 tail = tip;
4745 token.EndReading(end);
4747 /* Move tip to the '='. */
4748 while ((tip != end) && (*tip != '='))
4749 ++tip;
4751 /* If we didn't find an '=', punt. */
4752 if (tip == end)
4753 return;
4755 /* Extract the key and value. */
4756 const nsAString &key =
4757 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(Substring(tail, tip),
4758 PR_TRUE);
4759 const nsAString &value =
4760 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(Substring(++tip, end),
4761 PR_TRUE);
4763 /* Check for known keys. If we find a match, insert the appropriate
4764 * information into the document header. */
4765 nsCOMPtr<nsIAtom> key_atom = do_GetAtom(key);
4766 if (key_atom == nsGkAtoms::height)
4767 aDocument->SetHeaderData(nsGkAtoms::viewport_height, value);
4768 else if (key_atom == nsGkAtoms::width)
4769 aDocument->SetHeaderData(nsGkAtoms::viewport_width, value);
4770 else if (key_atom == nsGkAtoms::initial_scale)
4771 aDocument->SetHeaderData(nsGkAtoms::viewport_initial_scale, value);
4772 else if (key_atom == nsGkAtoms::minimum_scale)
4773 aDocument->SetHeaderData(nsGkAtoms::viewport_minimum_scale, value);
4774 else if (key_atom == nsGkAtoms::maximum_scale)
4775 aDocument->SetHeaderData(nsGkAtoms::viewport_maximum_scale, value);
4776 else if (key_atom == nsGkAtoms::user_scalable)
4777 aDocument->SetHeaderData(nsGkAtoms::viewport_user_scalable, value);
4780 #define IS_SEPARATOR(c) ((c == '=') || (c == ',') || (c == ';') || \
4781 (c == '\t') || (c == '\n') || (c == '\r'))
4783 /* static */
4784 nsresult
4785 nsContentUtils::ProcessViewportInfo(nsIDocument *aDocument,
4786 const nsAString &viewportInfo) {
4788 /* We never fail. */
4789 nsresult rv = NS_OK;
4791 /* Iterators. */
4792 nsAString::const_iterator tip, tail, end;
4793 viewportInfo.BeginReading(tip);
4794 tail = tip;
4795 viewportInfo.EndReading(end);
4797 /* Read the tip to the first non-separator character. */
4798 while ((tip != end) && (IS_SEPARATOR(*tip) || nsCRT::IsAsciiSpace(*tip)))
4799 ++tip;
4801 /* Read through and find tokens separated by separators. */
4802 while (tip != end) {
4804 /* Synchronize tip and tail. */
4805 tail = tip;
4807 /* Advance tip past non-separator characters. */
4808 while ((tip != end) && !IS_SEPARATOR(*tip))
4809 ++tip;
4811 /* Allow white spaces that surround the '=' character */
4812 if ((tip != end) && (*tip == '=')) {
4813 ++tip;
4815 while ((tip != end) && nsCRT::IsAsciiSpace(*tip))
4816 ++tip;
4818 while ((tip != end) && !(IS_SEPARATOR(*tip) || nsCRT::IsAsciiSpace(*tip)))
4819 ++tip;
4822 /* Our token consists of the characters between tail and tip. */
4823 ProcessViewportToken(aDocument, Substring(tail, tip));
4825 /* Skip separators. */
4826 while ((tip != end) && (IS_SEPARATOR(*tip) || nsCRT::IsAsciiSpace(*tip)))
4827 ++tip;
4830 return rv;
4834 #undef IS_SEPARATOR
4836 /* static */
4837 void
4838 nsContentUtils::HidePopupsInDocument(nsIDocument* aDocument)
4840 #ifdef MOZ_XUL
4841 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
4842 if (pm && aDocument) {
4843 nsCOMPtr<nsISupports> container = aDocument->GetContainer();
4844 nsCOMPtr<nsIDocShellTreeItem> docShellToHide = do_QueryInterface(container);
4845 if (docShellToHide)
4846 pm->HidePopupsInDocShell(docShellToHide);
4848 #endif
4851 /* static */
4852 already_AddRefed<nsIDragSession>
4853 nsContentUtils::GetDragSession()
4855 nsIDragSession* dragSession = nsnull;
4856 nsCOMPtr<nsIDragService> dragService =
4857 do_GetService("@mozilla.org/widget/dragservice;1");
4858 if (dragService)
4859 dragService->GetCurrentSession(&dragSession);
4860 return dragSession;
4863 /* static */
4864 nsresult
4865 nsContentUtils::SetDataTransferInEvent(nsDragEvent* aDragEvent)
4867 if (aDragEvent->dataTransfer || !NS_IS_TRUSTED_EVENT(aDragEvent))
4868 return NS_OK;
4870 // For draggesture and dragstart events, the data transfer object is
4871 // created before the event fires, so it should already be set. For other
4872 // drag events, get the object from the drag session.
4873 NS_ASSERTION(aDragEvent->message != NS_DRAGDROP_GESTURE &&
4874 aDragEvent->message != NS_DRAGDROP_START,
4875 "draggesture event created without a dataTransfer");
4877 nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
4878 NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
4880 nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
4881 dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
4882 if (!initialDataTransfer) {
4883 // A dataTransfer won't exist when a drag was started by some other
4884 // means, for instance calling the drag service directly, or a drag
4885 // from another application. In either case, a new dataTransfer should
4886 // be created that reflects the data.
4887 initialDataTransfer =
4888 new nsDOMDataTransfer(aDragEvent->message);
4889 NS_ENSURE_TRUE(initialDataTransfer, NS_ERROR_OUT_OF_MEMORY);
4891 // now set it in the drag session so we don't need to create it again
4892 dragSession->SetDataTransfer(initialDataTransfer);
4895 // each event should use a clone of the original dataTransfer.
4896 nsCOMPtr<nsIDOMNSDataTransfer> initialDataTransferNS =
4897 do_QueryInterface(initialDataTransfer);
4898 NS_ENSURE_TRUE(initialDataTransferNS, NS_ERROR_FAILURE);
4899 initialDataTransferNS->Clone(aDragEvent->message, aDragEvent->userCancelled,
4900 getter_AddRefs(aDragEvent->dataTransfer));
4901 NS_ENSURE_TRUE(aDragEvent->dataTransfer, NS_ERROR_OUT_OF_MEMORY);
4903 // for the dragenter and dragover events, initialize the drop effect
4904 // from the drop action, which platform specific widget code sets before
4905 // the event is fired based on the keyboard state.
4906 if (aDragEvent->message == NS_DRAGDROP_ENTER ||
4907 aDragEvent->message == NS_DRAGDROP_OVER) {
4908 nsCOMPtr<nsIDOMNSDataTransfer> newDataTransfer =
4909 do_QueryInterface(aDragEvent->dataTransfer);
4910 NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_FAILURE);
4912 PRUint32 action, effectAllowed;
4913 dragSession->GetDragAction(&action);
4914 newDataTransfer->GetEffectAllowedInt(&effectAllowed);
4915 newDataTransfer->SetDropEffectInt(FilterDropEffect(action, effectAllowed));
4917 else if (aDragEvent->message == NS_DRAGDROP_DROP ||
4918 aDragEvent->message == NS_DRAGDROP_DRAGDROP ||
4919 aDragEvent->message == NS_DRAGDROP_END) {
4920 // For the drop and dragend events, set the drop effect based on the
4921 // last value that the dropEffect had. This will have been set in
4922 // nsEventStateManager::PostHandleEvent for the last dragenter or
4923 // dragover event.
4924 nsCOMPtr<nsIDOMNSDataTransfer> newDataTransfer =
4925 do_QueryInterface(aDragEvent->dataTransfer);
4926 NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_FAILURE);
4928 PRUint32 dropEffect;
4929 initialDataTransferNS->GetDropEffectInt(&dropEffect);
4930 newDataTransfer->SetDropEffectInt(dropEffect);
4933 return NS_OK;
4936 /* static */
4937 PRUint32
4938 nsContentUtils::FilterDropEffect(PRUint32 aAction, PRUint32 aEffectAllowed)
4940 // It is possible for the drag action to include more than one action, but
4941 // the widget code which sets the action from the keyboard state should only
4942 // be including one. If multiple actions were set, we just consider them in
4943 // the following order:
4944 // copy, link, move
4945 if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
4946 aAction = nsIDragService::DRAGDROP_ACTION_COPY;
4947 else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
4948 aAction = nsIDragService::DRAGDROP_ACTION_LINK;
4949 else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
4950 aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
4952 // Filter the action based on the effectAllowed. If the effectAllowed
4953 // doesn't include the action, then that action cannot be done, so adjust
4954 // the action to something that is allowed. For a copy, adjust to move or
4955 // link. For a move, adjust to copy or link. For a link, adjust to move or
4956 // link. Otherwise, use none.
4957 if (aAction & aEffectAllowed ||
4958 aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
4959 return aAction;
4960 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
4961 return nsIDragService::DRAGDROP_ACTION_MOVE;
4962 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
4963 return nsIDragService::DRAGDROP_ACTION_COPY;
4964 if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
4965 return nsIDragService::DRAGDROP_ACTION_LINK;
4966 return nsIDragService::DRAGDROP_ACTION_NONE;
4969 /* static */
4970 PRBool
4971 nsContentUtils::URIIsLocalFile(nsIURI *aURI)
4973 PRBool isFile;
4974 nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
4976 return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
4977 nsIProtocolHandler::URI_IS_LOCAL_FILE,
4978 &isFile)) &&
4979 isFile;
4982 /* static */
4983 nsIScriptContext*
4984 nsContentUtils::GetContextForEventHandlers(nsINode* aNode,
4985 nsresult* aRv)
4987 *aRv = NS_OK;
4988 nsIDocument* ownerDoc = aNode->GetOwnerDoc();
4989 if (!ownerDoc) {
4990 *aRv = NS_ERROR_UNEXPECTED;
4991 return nsnull;
4994 PRBool hasHadScriptObject = PR_TRUE;
4995 nsIScriptGlobalObject* sgo =
4996 ownerDoc->GetScriptHandlingObject(hasHadScriptObject);
4997 // It is bad if the document doesn't have event handling context,
4998 // but it used to have one.
4999 if (!sgo && hasHadScriptObject) {
5000 *aRv = NS_ERROR_UNEXPECTED;
5001 return nsnull;
5004 if (sgo) {
5005 nsIScriptContext* scx = sgo->GetContext();
5006 // Bad, no context from script global object!
5007 if (!scx) {
5008 *aRv = NS_ERROR_UNEXPECTED;
5009 return nsnull;
5011 return scx;
5014 return nsnull;
5017 /* static */
5018 JSContext *
5019 nsContentUtils::GetCurrentJSContext()
5021 JSContext *cx = nsnull;
5023 sThreadJSContextStack->Peek(&cx);
5025 return cx;
5028 /* static */
5029 void
5030 nsContentUtils::ASCIIToLower(nsAString& aStr)
5032 PRUnichar* iter = aStr.BeginWriting();
5033 PRUnichar* end = aStr.EndWriting();
5034 while (iter != end) {
5035 PRUnichar c = *iter;
5036 if (c >= 'A' && c <= 'Z') {
5037 *iter = c + ('a' - 'A');
5039 ++iter;
5043 /* static */
5044 void
5045 nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest)
5047 PRUint32 len = aSource.Length();
5048 aDest.SetLength(len);
5049 if (aDest.Length() == len) {
5050 PRUnichar* dest = aDest.BeginWriting();
5051 const PRUnichar* iter = aSource.BeginReading();
5052 const PRUnichar* end = aSource.EndReading();
5053 while (iter != end) {
5054 PRUnichar c = *iter;
5055 *dest = (c >= 'A' && c <= 'Z') ?
5056 c + ('a' - 'A') : c;
5057 ++iter;
5058 ++dest;
5063 /* static */
5064 void
5065 nsContentUtils::ASCIIToUpper(nsAString& aStr)
5067 PRUnichar* iter = aStr.BeginWriting();
5068 PRUnichar* end = aStr.EndWriting();
5069 while (iter != end) {
5070 PRUnichar c = *iter;
5071 if (c >= 'a' && c <= 'z') {
5072 *iter = c + ('A' - 'a');
5074 ++iter;
5078 /* static */
5079 void
5080 nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest)
5082 PRUint32 len = aSource.Length();
5083 aDest.SetLength(len);
5084 if (aDest.Length() == len) {
5085 PRUnichar* dest = aDest.BeginWriting();
5086 const PRUnichar* iter = aSource.BeginReading();
5087 const PRUnichar* end = aSource.EndReading();
5088 while (iter != end) {
5089 PRUnichar c = *iter;
5090 *dest = (c >= 'a' && c <= 'z') ?
5091 c + ('A' - 'a') : c;
5092 ++iter;
5093 ++dest;
5098 PRBool
5099 nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
5100 const nsAString& aStr2)
5102 PRUint32 len = aStr1.Length();
5103 if (len != aStr2.Length()) {
5104 return PR_FALSE;
5107 const PRUnichar* str1 = aStr1.BeginReading();
5108 const PRUnichar* str2 = aStr2.BeginReading();
5109 const PRUnichar* end = str1 + len;
5111 while (str1 < end) {
5112 PRUnichar c1 = *str1++;
5113 PRUnichar c2 = *str2++;
5115 // First check if any bits other than the 0x0020 differs
5116 if ((c1 ^ c2) & 0xffdf) {
5117 return PR_FALSE;
5120 // We know they only differ in the 0x0020 bit.
5121 // Likely the two chars are the same, so check that first
5122 if (c1 != c2) {
5123 // They do differ, but since it's only in the 0x0020 bit, check if it's
5124 // the same ascii char, but just differing in case
5125 PRUnichar c1Upper = c1 & 0xffdf;
5126 if (!('A' <= c1Upper && c1Upper <= 'Z')) {
5127 return PR_FALSE;
5132 return PR_TRUE;
5135 /* static */
5136 nsIInterfaceRequestor*
5137 nsContentUtils::GetSameOriginChecker()
5139 if (!sSameOriginChecker) {
5140 sSameOriginChecker = new nsSameOriginChecker();
5141 NS_IF_ADDREF(sSameOriginChecker);
5143 return sSameOriginChecker;
5146 /* static */
5147 nsresult
5148 nsContentUtils::CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel)
5150 if (!nsContentUtils::GetSecurityManager())
5151 return NS_ERROR_NOT_AVAILABLE;
5153 nsCOMPtr<nsIPrincipal> oldPrincipal;
5154 nsContentUtils::GetSecurityManager()->
5155 GetChannelPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
5157 nsCOMPtr<nsIURI> newURI;
5158 aNewChannel->GetURI(getter_AddRefs(newURI));
5159 nsCOMPtr<nsIURI> newOriginalURI;
5160 aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
5162 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
5164 nsresult rv = oldPrincipal->CheckMayLoad(newURI, PR_FALSE);
5165 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
5166 rv = oldPrincipal->CheckMayLoad(newOriginalURI, PR_FALSE);
5169 return rv;
5172 NS_IMPL_ISUPPORTS2(nsSameOriginChecker,
5173 nsIChannelEventSink,
5174 nsIInterfaceRequestor)
5176 NS_IMETHODIMP
5177 nsSameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
5178 nsIChannel *aNewChannel,
5179 PRUint32 aFlags,
5180 nsIAsyncVerifyRedirectCallback *cb)
5182 NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
5184 nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
5185 if (NS_SUCCEEDED(rv)) {
5186 cb->OnRedirectVerifyCallback(NS_OK);
5189 return rv;
5192 NS_IMETHODIMP
5193 nsSameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
5195 return QueryInterface(aIID, aResult);
5198 /* static */
5199 nsresult
5200 nsContentUtils::GetASCIIOrigin(nsIPrincipal* aPrincipal, nsCString& aOrigin)
5202 NS_PRECONDITION(aPrincipal, "missing principal");
5204 aOrigin.Truncate();
5206 nsCOMPtr<nsIURI> uri;
5207 nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
5208 NS_ENSURE_SUCCESS(rv, rv);
5210 if (uri) {
5211 return GetASCIIOrigin(uri, aOrigin);
5214 aOrigin.AssignLiteral("null");
5216 return NS_OK;
5219 /* static */
5220 nsresult
5221 nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsCString& aOrigin)
5223 NS_PRECONDITION(aURI, "missing uri");
5225 aOrigin.Truncate();
5227 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
5228 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
5230 nsCString host;
5231 nsresult rv = uri->GetAsciiHost(host);
5233 if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
5234 nsCString scheme;
5235 rv = uri->GetScheme(scheme);
5236 NS_ENSURE_SUCCESS(rv, rv);
5238 aOrigin = scheme + NS_LITERAL_CSTRING("://") + host;
5240 // If needed, append the port
5241 PRInt32 port;
5242 uri->GetPort(&port);
5243 if (port != -1) {
5244 PRInt32 defaultPort = NS_GetDefaultPort(scheme.get());
5245 if (port != defaultPort) {
5246 aOrigin.Append(':');
5247 aOrigin.AppendInt(port);
5251 else {
5252 aOrigin.AssignLiteral("null");
5255 return NS_OK;
5258 /* static */
5259 nsresult
5260 nsContentUtils::GetUTFOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin)
5262 NS_PRECONDITION(aPrincipal, "missing principal");
5264 aOrigin.Truncate();
5266 nsCOMPtr<nsIURI> uri;
5267 nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
5268 NS_ENSURE_SUCCESS(rv, rv);
5270 if (uri) {
5271 return GetUTFOrigin(uri, aOrigin);
5274 aOrigin.AssignLiteral("null");
5276 return NS_OK;
5279 /* static */
5280 nsresult
5281 nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsString& aOrigin)
5283 NS_PRECONDITION(aURI, "missing uri");
5285 aOrigin.Truncate();
5287 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
5288 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
5290 nsCString host;
5291 nsresult rv = uri->GetHost(host);
5293 if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
5294 nsCString scheme;
5295 rv = uri->GetScheme(scheme);
5296 NS_ENSURE_SUCCESS(rv, rv);
5298 aOrigin = NS_ConvertUTF8toUTF16(scheme + NS_LITERAL_CSTRING("://") + host);
5300 // If needed, append the port
5301 PRInt32 port;
5302 uri->GetPort(&port);
5303 if (port != -1) {
5304 PRInt32 defaultPort = NS_GetDefaultPort(scheme.get());
5305 if (port != defaultPort) {
5306 aOrigin.Append(':');
5307 aOrigin.AppendInt(port);
5311 else {
5312 aOrigin.AssignLiteral("null");
5315 return NS_OK;
5318 /* static */
5319 already_AddRefed<nsIDocument>
5320 nsContentUtils::GetDocumentFromScriptContext(nsIScriptContext *aScriptContext)
5322 if (!aScriptContext)
5323 return nsnull;
5325 nsCOMPtr<nsIDOMWindow> window =
5326 do_QueryInterface(aScriptContext->GetGlobalObject());
5327 nsIDocument *doc = nsnull;
5328 if (window) {
5329 nsCOMPtr<nsIDOMDocument> domdoc;
5330 window->GetDocument(getter_AddRefs(domdoc));
5331 if (domdoc) {
5332 CallQueryInterface(domdoc, &doc);
5335 return doc;
5338 /* static */
5339 PRBool
5340 nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal, nsIChannel* aChannel)
5342 nsCOMPtr<nsIURI> channelURI;
5343 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
5344 NS_ENSURE_SUCCESS(rv, PR_FALSE);
5346 return NS_SUCCEEDED(aPrincipal->CheckMayLoad(channelURI, PR_FALSE));
5349 nsContentTypeParser::nsContentTypeParser(const nsAString& aString)
5350 : mString(aString), mService(nsnull)
5352 CallGetService("@mozilla.org/network/mime-hdrparam;1", &mService);
5355 nsContentTypeParser::~nsContentTypeParser()
5357 NS_IF_RELEASE(mService);
5360 nsresult
5361 nsContentTypeParser::GetParameter(const char* aParameterName, nsAString& aResult)
5363 NS_ENSURE_TRUE(mService, NS_ERROR_FAILURE);
5364 return mService->GetParameter(mString, aParameterName,
5365 EmptyCString(), PR_FALSE, nsnull,
5366 aResult);
5369 /* static */
5371 // If you change this code, change also AllowedToAct() in
5372 // XPCSystemOnlyWrapper.cpp!
5373 PRBool
5374 nsContentUtils::CanAccessNativeAnon()
5376 JSContext* cx = nsnull;
5377 sThreadJSContextStack->Peek(&cx);
5378 if (!cx) {
5379 return PR_TRUE;
5381 JSStackFrame* fp;
5382 nsIPrincipal* principal =
5383 sSecurityManager->GetCxSubjectPrincipalAndFrame(cx, &fp);
5384 NS_ENSURE_TRUE(principal, PR_FALSE);
5386 if (!fp) {
5387 if (!JS_FrameIterator(cx, &fp)) {
5388 // No code at all is running. So we must be arriving here as the result
5389 // of C++ code asking us to do something. Allow access.
5390 return PR_TRUE;
5393 // Some code is running, we can't make the assumption, as above, but we
5394 // can't use a native frame, so clear fp.
5395 fp = nsnull;
5396 } else if (!JS_IsScriptFrame(cx, fp)) {
5397 fp = nsnull;
5400 PRBool privileged;
5401 if (NS_SUCCEEDED(sSecurityManager->IsSystemPrincipal(principal, &privileged)) &&
5402 privileged) {
5403 // Chrome things are allowed to touch us.
5404 return PR_TRUE;
5407 // XXX HACK EWW! Allow chrome://global/ access to these things, even
5408 // if they've been cloned into less privileged contexts.
5409 static const char prefix[] = "chrome://global/";
5410 const char *filename;
5411 if (fp && JS_IsScriptFrame(cx, fp) &&
5412 (filename = JS_GetFrameScript(cx, fp)->filename) &&
5413 !strncmp(filename, prefix, NS_ARRAY_LENGTH(prefix) - 1)) {
5414 return PR_TRUE;
5417 // Before we throw, check for UniversalXPConnect.
5418 nsresult rv = sSecurityManager->IsCapabilityEnabled("UniversalXPConnect", &privileged);
5419 if (NS_SUCCEEDED(rv) && privileged) {
5420 return PR_TRUE;
5423 return PR_FALSE;
5426 /* static */ nsresult
5427 nsContentUtils::DispatchXULCommand(nsIContent* aTarget,
5428 PRBool aTrusted,
5429 nsIDOMEvent* aSourceEvent,
5430 nsIPresShell* aShell,
5431 PRBool aCtrl,
5432 PRBool aAlt,
5433 PRBool aShift,
5434 PRBool aMeta)
5436 NS_ENSURE_STATE(aTarget);
5437 nsIDocument* doc = aTarget->GetOwnerDoc();
5438 nsCOMPtr<nsIDOMDocumentEvent> docEvent = do_QueryInterface(doc);
5439 NS_ENSURE_STATE(docEvent);
5440 nsCOMPtr<nsIDOMEvent> event;
5441 docEvent->CreateEvent(NS_LITERAL_STRING("xulcommandevent"),
5442 getter_AddRefs(event));
5443 nsCOMPtr<nsIDOMXULCommandEvent> xulCommand = do_QueryInterface(event);
5444 nsCOMPtr<nsIPrivateDOMEvent> pEvent = do_QueryInterface(xulCommand);
5445 NS_ENSURE_STATE(pEvent);
5446 nsCOMPtr<nsIDOMAbstractView> view = do_QueryInterface(doc->GetWindow());
5447 nsresult rv = xulCommand->InitCommandEvent(NS_LITERAL_STRING("command"),
5448 PR_TRUE, PR_TRUE, view,
5449 0, aCtrl, aAlt, aShift, aMeta,
5450 aSourceEvent);
5451 NS_ENSURE_SUCCESS(rv, rv);
5453 if (aShell) {
5454 nsEventStatus status = nsEventStatus_eIgnore;
5455 nsCOMPtr<nsIPresShell> kungFuDeathGrip = aShell;
5456 return aShell->HandleDOMEventWithTarget(aTarget, event, &status);
5459 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(aTarget);
5460 NS_ENSURE_STATE(target);
5461 PRBool dummy;
5462 return target->DispatchEvent(event, &dummy);
5465 // static
5466 nsresult
5467 nsContentUtils::WrapNative(JSContext *cx, JSObject *scope, nsISupports *native,
5468 nsWrapperCache *cache, const nsIID* aIID, jsval *vp,
5469 nsIXPConnectJSObjectHolder **aHolder,
5470 PRBool aAllowWrapping)
5472 if (!native) {
5473 NS_ASSERTION(!aHolder || !*aHolder, "*aHolder should be null!");
5475 *vp = JSVAL_NULL;
5477 return NS_OK;
5480 JSObject *wrapper = xpc_GetCachedSlimWrapper(cache, scope, vp);
5481 if (wrapper) {
5482 return NS_OK;
5485 NS_ENSURE_TRUE(sXPConnect && sThreadJSContextStack, NS_ERROR_UNEXPECTED);
5487 // Keep sXPConnect and sThreadJSContextStack alive. If we're on the main
5488 // thread then this can be done simply and cheaply by adding a reference to
5489 // nsLayoutStatics. If we're not on the main thread then we need to add a
5490 // more expensive reference sXPConnect directly. We have to use manual
5491 // AddRef and Release calls so don't early-exit from this function after we've
5492 // added the reference!
5493 PRBool isMainThread = NS_IsMainThread();
5495 if (isMainThread) {
5496 nsLayoutStatics::AddRef();
5498 else {
5499 sXPConnect->AddRef();
5502 JSContext *topJSContext;
5503 nsresult rv = sThreadJSContextStack->Peek(&topJSContext);
5504 if (NS_SUCCEEDED(rv)) {
5505 PRBool push = topJSContext != cx;
5506 if (push) {
5507 rv = sThreadJSContextStack->Push(cx);
5509 if (NS_SUCCEEDED(rv)) {
5510 rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
5511 aAllowWrapping, vp, aHolder);
5512 if (push) {
5513 sThreadJSContextStack->Pop(nsnull);
5518 if (isMainThread) {
5519 nsLayoutStatics::Release();
5521 else {
5522 sXPConnect->Release();
5525 return rv;
5528 void
5529 nsContentUtils::StripNullChars(const nsAString& aInStr, nsAString& aOutStr)
5531 // In common cases where we don't have nulls in the
5532 // string we can simple simply bypass the checking code.
5533 PRInt32 firstNullPos = aInStr.FindChar('\0');
5534 if (firstNullPos == kNotFound) {
5535 aOutStr.Assign(aInStr);
5536 return;
5539 aOutStr.SetCapacity(aInStr.Length() - 1);
5540 nsAString::const_iterator start, end;
5541 aInStr.BeginReading(start);
5542 aInStr.EndReading(end);
5543 while (start != end) {
5544 if (*start != '\0')
5545 aOutStr.Append(*start);
5546 ++start;
5550 namespace {
5552 const unsigned int kCloneStackFrameStackSize = 20;
5554 class CloneStackFrame
5556 friend class CloneStack;
5558 public:
5559 // These three jsvals must all stick together as they're treated as a jsval
5560 // array!
5561 jsval source;
5562 jsval clone;
5563 jsval temp;
5564 js::AutoIdArray ids;
5565 jsuint index;
5567 private:
5568 // Only let CloneStack access these.
5569 CloneStackFrame(JSContext* aCx, jsval aSource, jsval aClone, JSIdArray* aIds)
5570 : source(aSource), clone(aClone), temp(JSVAL_NULL), ids(aCx, aIds), index(0),
5571 prevFrame(nsnull), tvrVals(aCx, 3, &source)
5573 MOZ_COUNT_CTOR(CloneStackFrame);
5576 ~CloneStackFrame()
5578 MOZ_COUNT_DTOR(CloneStackFrame);
5581 CloneStackFrame* prevFrame;
5582 js::AutoArrayRooter tvrVals;
5585 class CloneStack
5587 public:
5588 CloneStack(JSContext* cx)
5589 : mCx(cx), mLastFrame(nsnull) {
5590 mObjectSet.Init();
5593 ~CloneStack() {
5594 while (!IsEmpty()) {
5595 Pop();
5599 PRBool
5600 Push(jsval source, jsval clone, JSIdArray* ids) {
5601 NS_ASSERTION(!JSVAL_IS_PRIMITIVE(source) && !JSVAL_IS_PRIMITIVE(clone),
5602 "Must be an object!");
5603 if (!ids) {
5604 return PR_FALSE;
5607 CloneStackFrame* newFrame;
5608 if (mObjectSet.Count() < kCloneStackFrameStackSize) {
5609 // If the object can fit in our stack space then use that.
5610 CloneStackFrame* buf = reinterpret_cast<CloneStackFrame*>(mStackFrames);
5611 newFrame = new (buf + mObjectSet.Count())
5612 CloneStackFrame(mCx, source, clone, ids);
5614 else {
5615 // Use the heap.
5616 newFrame = new CloneStackFrame(mCx, source, clone, ids);
5619 mObjectSet.PutEntry(JSVAL_TO_OBJECT(source));
5621 newFrame->prevFrame = mLastFrame;
5622 mLastFrame = newFrame;
5624 return PR_TRUE;
5627 CloneStackFrame*
5628 Peek() {
5629 return mLastFrame;
5632 void
5633 Pop() {
5634 if (IsEmpty()) {
5635 NS_ERROR("Empty stack!");
5636 return;
5639 CloneStackFrame* lastFrame = mLastFrame;
5641 mObjectSet.RemoveEntry(JSVAL_TO_OBJECT(lastFrame->source));
5642 mLastFrame = lastFrame->prevFrame;
5644 if (mObjectSet.Count() >= kCloneStackFrameStackSize) {
5645 // Only delete if this was a heap object.
5646 delete lastFrame;
5648 else {
5649 // Otherwise just run the destructor.
5650 lastFrame->~CloneStackFrame();
5654 PRBool
5655 IsEmpty() {
5656 NS_ASSERTION((!mLastFrame && !mObjectSet.Count()) ||
5657 (mLastFrame && mObjectSet.Count()),
5658 "Hashset is out of sync!");
5659 return mObjectSet.Count() == 0;
5662 PRBool
5663 Search(JSObject* obj) {
5664 return !!mObjectSet.GetEntry(obj);
5667 private:
5668 JSContext* mCx;
5669 CloneStackFrame* mLastFrame;
5670 nsTHashtable<nsVoidPtrHashKey> mObjectSet;
5672 // Use a char array instead of CloneStackFrame array to prevent the JSAuto*
5673 // helpers from running until we're ready for them.
5674 char mStackFrames[kCloneStackFrameStackSize * sizeof(CloneStackFrame)];
5677 struct ReparentObjectData {
5678 ReparentObjectData(JSContext* cx, JSObject* obj)
5679 : cx(cx), obj(obj), ids(nsnull), index(0) { }
5681 ~ReparentObjectData() {
5682 if (ids) {
5683 JS_DestroyIdArray(cx, ids);
5687 JSContext* cx;
5688 JSObject* obj;
5689 JSIdArray* ids;
5690 jsint index;
5693 inline nsresult
5694 SetPropertyOnValueOrObject(JSContext* cx,
5695 jsval val,
5696 jsval* rval,
5697 JSObject* obj,
5698 jsid id)
5700 NS_ASSERTION((rval && !obj) || (!rval && obj), "Can only clone to one dest!");
5701 if (rval) {
5702 *rval = val;
5703 return NS_OK;
5705 if (!JS_DefinePropertyById(cx, obj, id, val, nsnull, nsnull,
5706 JSPROP_ENUMERATE)) {
5707 return NS_ERROR_FAILURE;
5709 return NS_OK;
5712 inline JSObject*
5713 CreateEmptyObjectOrArray(JSContext* cx,
5714 JSObject* obj)
5716 if (JS_IsArrayObject(cx, obj)) {
5717 jsuint length;
5718 if (!JS_GetArrayLength(cx, obj, &length)) {
5719 NS_ERROR("Failed to get array length?!");
5720 return nsnull;
5722 return JS_NewArrayObject(cx, length, NULL);
5724 return JS_NewObject(cx, NULL, NULL, NULL);
5727 nsresult
5728 CloneSimpleValues(JSContext* cx,
5729 jsval val,
5730 jsval* rval,
5731 PRBool* wasCloned,
5732 JSObject* robj = nsnull,
5733 jsid rid = INT_TO_JSID(0))
5735 *wasCloned = PR_TRUE;
5737 // No cloning necessary for these non-GC'd jsvals.
5738 if (!JSVAL_IS_GCTHING(val) || JSVAL_IS_NULL(val)) {
5739 return SetPropertyOnValueOrObject(cx, val, rval, robj, rid);
5742 // We'll use immutable strings to prevent copying if we can.
5743 if (JSVAL_IS_STRING(val)) {
5744 if (!JS_MakeStringImmutable(cx, JSVAL_TO_STRING(val))) {
5745 return NS_ERROR_FAILURE;
5747 return SetPropertyOnValueOrObject(cx, val, rval, robj, rid);
5750 NS_ASSERTION(!JSVAL_IS_PRIMITIVE(val), "Not an object!");
5751 JSObject* obj = JSVAL_TO_OBJECT(val);
5753 // Dense arrays of primitives can be cloned quickly.
5754 JSObject* newArray;
5755 if (!js_CloneDensePrimitiveArray(cx, obj, &newArray)) {
5756 return NS_ERROR_FAILURE;
5758 if (newArray) {
5759 return SetPropertyOnValueOrObject(cx, OBJECT_TO_JSVAL(newArray), rval, robj,
5760 rid);
5763 // Date objects.
5764 if (js_DateIsValid(cx, obj)) {
5765 jsdouble msec = js_DateGetMsecSinceEpoch(cx, obj);
5766 JSObject* newDate;
5767 if (!(msec && (newDate = js_NewDateObjectMsec(cx, msec)))) {
5768 return NS_ERROR_OUT_OF_MEMORY;
5770 return SetPropertyOnValueOrObject(cx, OBJECT_TO_JSVAL(newDate), rval, robj,
5771 rid);
5774 // RegExp objects.
5775 if (js_ObjectIsRegExp(obj)) {
5776 JSObject* proto;
5777 if (!js_GetClassPrototype(cx, JS_GetScopeChain(cx), JSProto_RegExp,
5778 &proto)) {
5779 return NS_ERROR_FAILURE;
5781 JSObject* newRegExp = js_CloneRegExpObject(cx, obj, proto);
5782 if (!newRegExp) {
5783 return NS_ERROR_FAILURE;
5785 return SetPropertyOnValueOrObject(cx, OBJECT_TO_JSVAL(newRegExp), rval,
5786 robj, rid);
5789 // Typed array objects.
5790 if (js_IsTypedArray(obj)) {
5791 js::TypedArray* src = js::TypedArray::fromJSObject(obj);
5792 JSObject* newTypedArray = js_CreateTypedArrayWithArray(cx, src->type, obj);
5793 if (!newTypedArray) {
5794 return NS_ERROR_FAILURE;
5796 return SetPropertyOnValueOrObject(cx, OBJECT_TO_JSVAL(newTypedArray), rval,
5797 robj, rid);
5800 // ArrayBuffer objects.
5801 if (js_IsArrayBuffer(obj)) {
5802 js::ArrayBuffer* src = js::ArrayBuffer::fromJSObject(obj);
5803 if (!src) {
5804 return NS_ERROR_FAILURE;
5807 JSObject* newBuffer = js_CreateArrayBuffer(cx, src->byteLength);
5808 if (!newBuffer) {
5809 return NS_ERROR_FAILURE;
5811 memcpy(js::ArrayBuffer::fromJSObject(newBuffer)->data, src->data,
5812 src->byteLength);
5813 return SetPropertyOnValueOrObject(cx, OBJECT_TO_JSVAL(newBuffer), rval,
5814 robj, rid);
5817 // Do we support File?
5818 // Do we support Blob?
5819 // Do we support FileList?
5821 // Function objects don't get cloned.
5822 if (JS_ObjectIsFunction(cx, obj)) {
5823 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
5826 // Security wrapped objects are not allowed either.
5827 if (obj->isWrapper() && !obj->getClass()->ext.innerObject)
5828 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
5830 // See if this JSObject is backed by some C++ object. If it is then we assume
5831 // that it is inappropriate to clone.
5832 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
5833 nsContentUtils::XPConnect()->
5834 GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrapper));
5835 if (wrapper) {
5836 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
5839 *wasCloned = PR_FALSE;
5840 return NS_OK;
5843 } // anonymous namespace
5845 // static
5846 nsresult
5847 nsContentUtils::CreateStructuredClone(JSContext* cx,
5848 jsval val,
5849 jsval* rval)
5851 JSAutoRequest ar(cx);
5853 nsCOMPtr<nsIXPConnect> xpconnect(sXPConnect);
5854 NS_ENSURE_STATE(xpconnect);
5856 PRBool wasCloned;
5857 nsresult rv = CloneSimpleValues(cx, val, rval, &wasCloned);
5858 if (NS_FAILED(rv)) {
5859 return rv;
5862 if (wasCloned) {
5863 return NS_OK;
5866 NS_ASSERTION(JSVAL_IS_OBJECT(val), "Not an object?!");
5867 JSObject* obj = CreateEmptyObjectOrArray(cx, JSVAL_TO_OBJECT(val));
5868 if (!obj) {
5869 return NS_ERROR_OUT_OF_MEMORY;
5872 jsval output = OBJECT_TO_JSVAL(obj);
5873 js::AutoValueRooter tvr(cx, output);
5875 CloneStack stack(cx);
5876 if (!stack.Push(val, OBJECT_TO_JSVAL(obj),
5877 JS_Enumerate(cx, JSVAL_TO_OBJECT(val)))) {
5878 return NS_ERROR_OUT_OF_MEMORY;
5881 while (!stack.IsEmpty()) {
5882 CloneStackFrame* frame = stack.Peek();
5884 NS_ASSERTION(!!frame->ids &&
5885 frame->ids.length() >= frame->index &&
5886 !JSVAL_IS_PRIMITIVE(frame->source) &&
5887 !JSVAL_IS_PRIMITIVE(frame->clone),
5888 "Bad frame state!");
5890 if (frame->index == frame->ids.length()) {
5891 // Done cloning this object, pop the frame.
5892 stack.Pop();
5893 continue;
5896 // Get the current id and increment the index.
5897 jsid id = frame->ids[frame->index++];
5899 if (!JS_GetPropertyById(cx, JSVAL_TO_OBJECT(frame->source), id,
5900 &frame->temp)) {
5901 return NS_ERROR_FAILURE;
5904 if (!JSVAL_IS_PRIMITIVE(frame->temp) &&
5905 stack.Search(JSVAL_TO_OBJECT(frame->temp))) {
5906 // Spec says to throw this particular exception for cyclical references.
5907 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
5910 JSObject* clone = JSVAL_TO_OBJECT(frame->clone);
5912 PRBool wasCloned;
5913 nsresult rv = CloneSimpleValues(cx, frame->temp, nsnull, &wasCloned, clone,
5914 id);
5915 if (NS_FAILED(rv)) {
5916 return rv;
5919 if (!wasCloned) {
5920 NS_ASSERTION(JSVAL_IS_OBJECT(frame->temp), "Not an object?!");
5921 obj = CreateEmptyObjectOrArray(cx, JSVAL_TO_OBJECT(frame->temp));
5922 if (!obj ||
5923 !stack.Push(frame->temp, OBJECT_TO_JSVAL(obj),
5924 JS_Enumerate(cx, JSVAL_TO_OBJECT(frame->temp)))) {
5925 return NS_ERROR_OUT_OF_MEMORY;
5927 // Set the new object as a property of the clone. We'll fill it on the
5928 // next iteration.
5929 if (!JS_DefinePropertyById(cx, clone, id, OBJECT_TO_JSVAL(obj), nsnull,
5930 nsnull, JSPROP_ENUMERATE)) {
5931 return NS_ERROR_FAILURE;
5936 *rval = output;
5937 return NS_OK;
5940 // static
5941 nsresult
5942 nsContentUtils::ReparentClonedObjectToScope(JSContext* cx,
5943 JSObject* obj,
5944 JSObject* scope)
5946 JSAutoRequest ar(cx);
5948 scope = JS_GetGlobalForObject(cx, scope);
5950 nsAutoTArray<ReparentObjectData, 20> objectData;
5951 objectData.AppendElement(ReparentObjectData(cx, obj));
5953 while (!objectData.IsEmpty()) {
5954 ReparentObjectData& data = objectData[objectData.Length() - 1];
5956 if (!data.ids) {
5957 NS_ASSERTION(!data.index, "Shouldn't have index here");
5959 // Typed arrays are special and don't need to be enumerated.
5960 if (js_IsTypedArray(data.obj)) {
5961 if (!js_ReparentTypedArrayToScope(cx, data.obj, scope)) {
5962 return NS_ERROR_FAILURE;
5965 // No need to enumerate anything here.
5966 objectData.RemoveElementAt(objectData.Length() - 1);
5967 continue;
5970 JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(JS_GET_CLASS(cx, data.obj));
5971 if (!key) {
5972 // We should never be reparenting an object that doesn't have a standard
5973 // proto key.
5974 return NS_ERROR_FAILURE;
5977 // Fix the prototype and parent first.
5978 JSObject* proto;
5979 if (!js_GetClassPrototype(cx, scope, key, &proto) ||
5980 !JS_SetPrototype(cx, data.obj, proto) ||
5981 !JS_SetParent(cx, data.obj, scope)) {
5982 return NS_ERROR_FAILURE;
5985 // Primitive arrays don't need to be enumerated either but the proto and
5986 // parent needed to be fixed above. Now we can just move on.
5987 if (js_IsDensePrimitiveArray(data.obj)) {
5988 objectData.RemoveElementAt(objectData.Length() - 1);
5989 continue;
5992 // And now enumerate the object's properties.
5993 if (!(data.ids = JS_Enumerate(cx, data.obj))) {
5994 return NS_ERROR_FAILURE;
5998 // If we've gone through all the object's properties then we're done with
5999 // this frame.
6000 if (data.index == data.ids->length) {
6001 objectData.RemoveElementAt(objectData.Length() - 1);
6002 continue;
6005 // Get the id and increment!
6006 jsid id = data.ids->vector[data.index++];
6008 jsval prop;
6009 if (!JS_GetPropertyById(cx, data.obj, id, &prop)) {
6010 return NS_ERROR_FAILURE;
6013 // Push a new frame if this property is an object.
6014 if (!JSVAL_IS_PRIMITIVE(prop)) {
6015 objectData.AppendElement(ReparentObjectData(cx, JSVAL_TO_OBJECT(prop)));
6019 return NS_OK;
6022 struct ClassMatchingInfo {
6023 nsAttrValue::AtomArray mClasses;
6024 nsCaseTreatment mCaseTreatment;
6027 static PRBool
6028 MatchClassNames(nsIContent* aContent, PRInt32 aNamespaceID, nsIAtom* aAtom,
6029 void* aData)
6031 // We can't match if there are no class names
6032 const nsAttrValue* classAttr = aContent->GetClasses();
6033 if (!classAttr) {
6034 return PR_FALSE;
6037 // need to match *all* of the classes
6038 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6039 PRUint32 length = info->mClasses.Length();
6040 if (!length) {
6041 // If we actually had no classes, don't match.
6042 return PR_FALSE;
6044 PRUint32 i;
6045 for (i = 0; i < length; ++i) {
6046 if (!classAttr->Contains(info->mClasses[i],
6047 info->mCaseTreatment)) {
6048 return PR_FALSE;
6052 return PR_TRUE;
6055 static void
6056 DestroyClassNameArray(void* aData)
6058 ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
6059 delete info;
6062 static void*
6063 AllocClassMatchingInfo(nsINode* aRootNode,
6064 const nsString* aClasses)
6066 nsAttrValue attrValue;
6067 attrValue.ParseAtomArray(*aClasses);
6068 // nsAttrValue::Equals is sensitive to order, so we'll send an array
6069 ClassMatchingInfo* info = new ClassMatchingInfo;
6070 NS_ENSURE_TRUE(info, nsnull);
6072 if (attrValue.Type() == nsAttrValue::eAtomArray) {
6073 info->mClasses.SwapElements(*(attrValue.GetAtomArrayValue()));
6074 } else if (attrValue.Type() == nsAttrValue::eAtom) {
6075 info->mClasses.AppendElement(attrValue.GetAtomValue());
6078 info->mCaseTreatment =
6079 aRootNode->GetOwnerDoc() &&
6080 aRootNode->GetOwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks ?
6081 eIgnoreCase : eCaseMatters;
6082 return info;
6085 // static
6087 nsresult
6088 nsContentUtils::GetElementsByClassName(nsINode* aRootNode,
6089 const nsAString& aClasses,
6090 nsIDOMNodeList** aReturn)
6092 NS_PRECONDITION(aRootNode, "Must have root node");
6094 nsContentList* elements =
6095 NS_GetFuncStringContentList(aRootNode, MatchClassNames,
6096 DestroyClassNameArray,
6097 AllocClassMatchingInfo,
6098 aClasses).get();
6099 NS_ENSURE_TRUE(elements, NS_ERROR_OUT_OF_MEMORY);
6101 // Transfer ownership
6102 *aReturn = elements;
6104 return NS_OK;
6107 #ifdef DEBUG
6108 class DebugWrapperTraversalCallback : public nsCycleCollectionTraversalCallback
6110 public:
6111 DebugWrapperTraversalCallback(void* aWrapper) : mFound(PR_FALSE),
6112 mWrapper(aWrapper)
6114 mFlags = WANT_ALL_TRACES;
6117 NS_IMETHOD_(void) DescribeNode(CCNodeType type,
6118 nsrefcnt refcount,
6119 size_t objsz,
6120 const char* objname)
6123 NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports *root)
6126 NS_IMETHOD_(void) NoteRoot(PRUint32 langID, void* root,
6127 nsCycleCollectionParticipant* helper)
6130 NS_IMETHOD_(void) NoteScriptChild(PRUint32 langID, void* child)
6132 if (langID == nsIProgrammingLanguage::JAVASCRIPT) {
6133 mFound = child == mWrapper;
6136 NS_IMETHOD_(void) NoteXPCOMChild(nsISupports *child)
6139 NS_IMETHOD_(void) NoteNativeChild(void* child,
6140 nsCycleCollectionParticipant* helper)
6144 NS_IMETHOD_(void) NoteNextEdgeName(const char* name)
6148 PRBool mFound;
6150 private:
6151 void* mWrapper;
6154 static void
6155 DebugWrapperTraceCallback(PRUint32 langID, void *p, void *closure)
6157 DebugWrapperTraversalCallback* callback =
6158 static_cast<DebugWrapperTraversalCallback*>(closure);
6159 callback->NoteScriptChild(langID, p);
6162 // static
6163 void
6164 nsContentUtils::CheckCCWrapperTraversal(nsISupports* aScriptObjectHolder,
6165 nsWrapperCache* aCache)
6167 nsXPCOMCycleCollectionParticipant* participant;
6168 CallQueryInterface(aScriptObjectHolder, &participant);
6170 DebugWrapperTraversalCallback callback(aCache->GetWrapper());
6172 participant->Traverse(aScriptObjectHolder, callback);
6173 NS_ASSERTION(callback.mFound,
6174 "Cycle collection participant didn't traverse to preserved "
6175 "wrapper! This will probably crash.");
6177 callback.mFound = PR_FALSE;
6178 participant->Trace(aScriptObjectHolder, DebugWrapperTraceCallback, &callback);
6179 NS_ASSERTION(callback.mFound,
6180 "Cycle collection participant didn't trace preserved wrapper! "
6181 "This will probably crash.");
6183 #endif
6185 mozAutoRemovableBlockerRemover::mozAutoRemovableBlockerRemover(nsIDocument* aDocument MOZILLA_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
6187 MOZILLA_GUARD_OBJECT_NOTIFIER_INIT;
6188 mNestingLevel = nsContentUtils::GetRemovableScriptBlockerLevel();
6189 mDocument = aDocument;
6190 nsISupports* sink = aDocument ? aDocument->GetCurrentContentSink() : nsnull;
6191 mObserver = do_QueryInterface(sink);
6192 for (PRUint32 i = 0; i < mNestingLevel; ++i) {
6193 if (mObserver) {
6194 mObserver->EndUpdate(mDocument, UPDATE_CONTENT_MODEL);
6196 nsContentUtils::RemoveRemovableScriptBlocker();
6199 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "killing mutation events");
6202 mozAutoRemovableBlockerRemover::~mozAutoRemovableBlockerRemover()
6204 NS_ASSERTION(nsContentUtils::GetRemovableScriptBlockerLevel() == 0,
6205 "Should have had none");
6206 for (PRUint32 i = 0; i < mNestingLevel; ++i) {
6207 nsContentUtils::AddRemovableScriptBlocker();
6208 if (mObserver) {
6209 mObserver->BeginUpdate(mDocument, UPDATE_CONTENT_MODEL);
6214 // static
6215 PRBool
6216 nsContentUtils::IsFocusedContent(const nsIContent* aContent)
6218 nsFocusManager* fm = nsFocusManager::GetFocusManager();
6220 return fm && fm->GetFocusedContent() == aContent;
6223 bool
6224 nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent)
6226 nsIDocument* doc = aContent->GetCurrentDoc();
6227 if (!doc) {
6228 return false;
6231 // XXXbz should this use GetOwnerDoc() for GetSubDocumentFor?
6232 // sXBL/XBL2 issue!
6233 nsIDocument* subDoc = doc->GetSubDocumentFor(aContent);
6234 if (!subDoc) {
6235 return false;
6238 nsCOMPtr<nsISupports> container = subDoc->GetContainer();
6239 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
6240 if (!docShell) {
6241 return false;
6244 nsCOMPtr<nsIContentViewer> contentViewer;
6245 docShell->GetContentViewer(getter_AddRefs(contentViewer));
6246 if (!contentViewer) {
6247 return false;
6250 nsCOMPtr<nsIContentViewer> zombieViewer;
6251 contentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
6253 // If there are 2 viewers for the current docshell, that
6254 // means the current document is a zombie document.
6255 // Only navigate into the subdocument if it's not a zombie.
6256 return !zombieViewer;
6259 void
6260 nsContentUtils::FlushLayoutForTree(nsIDOMWindow* aWindow)
6262 nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
6263 if (!piWin)
6264 return;
6266 // Note that because FlushPendingNotifications flushes parents, this
6267 // is O(N^2) in docshell tree depth. However, the docshell tree is
6268 // usually pretty shallow.
6270 nsCOMPtr<nsIDOMDocument> domDoc;
6271 aWindow->GetDocument(getter_AddRefs(domDoc));
6272 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
6273 if (doc) {
6274 doc->FlushPendingNotifications(Flush_Layout);
6277 nsCOMPtr<nsIDocShellTreeNode> node =
6278 do_QueryInterface(piWin->GetDocShell());
6279 if (node) {
6280 PRInt32 i = 0, i_end;
6281 node->GetChildCount(&i_end);
6282 for (; i < i_end; ++i) {
6283 nsCOMPtr<nsIDocShellTreeItem> item;
6284 node->GetChildAt(i, getter_AddRefs(item));
6285 nsCOMPtr<nsIDOMWindow> win = do_GetInterface(item);
6286 if (win) {
6287 FlushLayoutForTree(win);
6293 void nsContentUtils::RemoveNewlines(nsString &aString)
6295 // strip CR/LF and null
6296 static const char badChars[] = {'\r', '\n', 0};
6297 aString.StripChars(badChars);
6300 void
6301 nsContentUtils::PlatformToDOMLineBreaks(nsString &aString)
6303 if (aString.FindChar(PRUnichar('\r')) != -1) {
6304 // Windows linebreaks: Map CRLF to LF:
6305 aString.ReplaceSubstring(NS_LITERAL_STRING("\r\n").get(),
6306 NS_LITERAL_STRING("\n").get());
6308 // Mac linebreaks: Map any remaining CR to LF:
6309 aString.ReplaceSubstring(NS_LITERAL_STRING("\r").get(),
6310 NS_LITERAL_STRING("\n").get());
6314 already_AddRefed<LayerManager>
6315 nsContentUtils::LayerManagerForDocument(nsIDocument *aDoc)
6317 nsIDocument* doc = aDoc;
6318 nsIDocument* displayDoc = doc->GetDisplayDocument();
6319 if (displayDoc) {
6320 doc = displayDoc;
6323 nsIPresShell* shell = doc->GetShell();
6324 nsCOMPtr<nsISupports> container = doc->GetContainer();
6325 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = do_QueryInterface(container);
6326 while (!shell && docShellTreeItem) {
6327 // We may be in a display:none subdocument, or we may not have a presshell
6328 // created yet.
6329 // Walk the docshell tree to find the nearest container that has a presshell,
6330 // and find the root widget from that.
6331 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
6332 nsCOMPtr<nsIPresShell> presShell;
6333 docShell->GetPresShell(getter_AddRefs(presShell));
6334 if (presShell) {
6335 shell = presShell;
6336 } else {
6337 nsCOMPtr<nsIDocShellTreeItem> parent;
6338 docShellTreeItem->GetParent(getter_AddRefs(parent));
6339 docShellTreeItem = parent;
6343 if (shell) {
6344 nsIFrame* rootFrame = shell->FrameManager()->GetRootFrame();
6345 if (rootFrame) {
6346 nsIWidget* widget =
6347 nsLayoutUtils::GetDisplayRootFrame(rootFrame)->GetNearestWidget();
6348 if (widget) {
6349 nsRefPtr<LayerManager> manager = widget->GetLayerManager();
6350 return manager.forget();
6355 nsRefPtr<LayerManager> manager = new BasicLayerManager();
6356 return manager.forget();
6359 bool
6360 nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal)
6362 if (IsSystemPrincipal(aPrincipal)) {
6363 return true;
6366 nsCOMPtr<nsIURI> princURI;
6367 aPrincipal->GetURI(getter_AddRefs(princURI));
6369 return princURI &&
6370 ((sAllowXULXBL_for_file && SchemeIs(princURI, "file")) ||
6371 IsSitePermAllow(princURI, "allowXULXBL"));
6374 NS_IMPL_ISUPPORTS1(nsIContentUtils, nsIContentUtils)
6376 PRBool
6377 nsIContentUtils::IsSafeToRunScript()
6379 return nsContentUtils::IsSafeToRunScript();
6382 PRBool
6383 nsIContentUtils::ParseIntMarginValue(const nsAString& aString, nsIntMargin& result)
6385 return nsContentUtils::ParseIntMarginValue(aString, result);
6388 already_AddRefed<nsIDocumentLoaderFactory>
6389 nsIContentUtils::FindInternalContentViewer(const char* aType,
6390 ContentViewerType* aLoaderType)
6392 if (aLoaderType) {
6393 *aLoaderType = TYPE_UNSUPPORTED;
6396 // one helper factory, please
6397 nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
6398 if (!catMan)
6399 return NULL;
6401 nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
6403 nsXPIDLCString contractID;
6404 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, getter_Copies(contractID));
6405 if (NS_SUCCEEDED(rv)) {
6406 docFactory = do_GetService(contractID);
6407 if (docFactory && aLoaderType) {
6408 if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
6409 *aLoaderType = TYPE_CONTENT;
6410 else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
6411 *aLoaderType = TYPE_PLUGIN;
6412 else
6413 *aLoaderType = TYPE_UNKNOWN;
6415 return docFactory.forget();
6418 #ifdef MOZ_MEDIA
6419 #ifdef MOZ_OGG
6420 if (nsHTMLMediaElement::IsOggEnabled()) {
6421 for (unsigned int i = 0; i < NS_ARRAY_LENGTH(nsHTMLMediaElement::gOggTypes); ++i) {
6422 const char* type = nsHTMLMediaElement::gOggTypes[i];
6423 if (!strcmp(aType, type)) {
6424 docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
6425 if (docFactory && aLoaderType) {
6426 *aLoaderType = TYPE_CONTENT;
6428 return docFactory.forget();
6432 #endif
6434 #ifdef MOZ_WEBM
6435 if (nsHTMLMediaElement::IsWebMEnabled()) {
6436 for (unsigned int i = 0; i < NS_ARRAY_LENGTH(nsHTMLMediaElement::gWebMTypes); ++i) {
6437 const char* type = nsHTMLMediaElement::gWebMTypes[i];
6438 if (!strcmp(aType, type)) {
6439 docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
6440 if (docFactory && aLoaderType) {
6441 *aLoaderType = TYPE_CONTENT;
6443 return docFactory.forget();
6447 #endif
6448 #endif // MOZ_MEDIA
6450 return NULL;
6453 NS_IMPL_ISUPPORTS1(nsIContentUtils2, nsIContentUtils2)
6455 nsIInterfaceRequestor*
6456 nsIContentUtils2::GetSameOriginChecker()
6458 return nsContentUtils::GetSameOriginChecker();
6461 nsresult
6462 nsIContentUtils2::CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel)
6464 return nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);