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